summaryrefslogtreecommitdiff
path: root/lib/count-leading-zeros.h
blob: 9fe2a03ee0979336c4f5fd730f78e7791bae0836 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* count-leading-zeros.h -- counts the number of leading 0 bits in a word.
   Copyright (C) 2012-2023 Free Software Foundation, Inc.

   This file is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.

   This file is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

/* Written by Eric Blake.  */

#ifndef COUNT_LEADING_ZEROS_H
#define COUNT_LEADING_ZEROS_H 1

/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE.  */
#if !_GL_CONFIG_H_INCLUDED
 #error "Please include config.h first."
#endif

#include <limits.h>
#include <stdlib.h>

_GL_INLINE_HEADER_BEGIN
#ifndef COUNT_LEADING_ZEROS_INLINE
# define COUNT_LEADING_ZEROS_INLINE _GL_INLINE
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* Assuming the GCC builtin is BUILTIN and the MSC builtin is MSC_BUILTIN,
   expand to code that computes the number of leading zeros of the local
   variable 'x' of type TYPE (an unsigned integer type) and return it
   from the current function.  */
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) \
    || (__clang_major__ >= 4)
# define COUNT_LEADING_ZEROS(BUILTIN, MSC_BUILTIN, TYPE)                \
  return x ? BUILTIN (x) : CHAR_BIT * sizeof x;
#elif _MSC_VER
# pragma intrinsic (_BitScanReverse)
# if defined _M_X64
#  pragma intrinsic (_BitScanReverse64)
# endif
# define COUNT_LEADING_ZEROS(BUILTIN, MSC_BUILTIN, TYPE)                \
    do                                                                  \
      {                                                                 \
        unsigned long result;                                           \
        if (MSC_BUILTIN (&result, x))                                   \
          return CHAR_BIT * sizeof x - 1 - result;                      \
        return CHAR_BIT * sizeof x;                                     \
      }                                                                 \
    while (0)
#else
# define COUNT_LEADING_ZEROS(BUILTIN, MSC_BUILTIN, TYPE)                \
    do                                                                  \
      {                                                                 \
        int count;                                                      \
        unsigned int leading_32;                                        \
        if (! x)                                                        \
          return CHAR_BIT * sizeof x;                                   \
        for (count = 0;                                                 \
             (leading_32 = ((x >> (sizeof (TYPE) * CHAR_BIT - 32))      \
                            & 0xffffffffU),                             \
              count < CHAR_BIT * sizeof x - 32 && !leading_32);         \
             count += 32)                                               \
          x = x << 31 << 1;                                             \
        return count + count_leading_zeros_32 (leading_32);             \
      }                                                                 \
    while (0)

/* Compute and return the number of leading zeros in X,
   where 0 < X < 2**32.  */
COUNT_LEADING_ZEROS_INLINE int
count_leading_zeros_32 (unsigned int x)
{
  /* <https://github.com/gibsjose/BitHacks>
     <https://www.fit.vutbr.cz/~ibarina/pub/bithacks.pdf> */
  static const char de_Bruijn_lookup[32] = {
    31, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, 13, 9, 6, 28, 1,
    23, 19, 11, 3, 16, 14, 7, 24, 12, 4, 8, 25, 5, 26, 27, 0
  };

  x |= x >> 1;
  x |= x >> 2;
  x |= x >> 4;
  x |= x >> 8;
  x |= x >> 16;
  return de_Bruijn_lookup[((x * 0x07c4acddU) & 0xffffffffU) >> 27];
}
#endif

/* Compute and return the number of leading zeros in X. */
COUNT_LEADING_ZEROS_INLINE int
count_leading_zeros (unsigned int x)
{
  COUNT_LEADING_ZEROS (__builtin_clz, _BitScanReverse, unsigned int);
}

/* Compute and return the number of leading zeros in X. */
COUNT_LEADING_ZEROS_INLINE int
count_leading_zeros_l (unsigned long int x)
{
  COUNT_LEADING_ZEROS (__builtin_clzl, _BitScanReverse, unsigned long int);
}

/* Compute and return the number of leading zeros in X. */
COUNT_LEADING_ZEROS_INLINE int
count_leading_zeros_ll (unsigned long long int x)
{
#if (defined _MSC_VER && !defined __clang__) && !defined _M_X64
  /* 32-bit MSVC does not have _BitScanReverse64, only _BitScanReverse.  */
  unsigned long result;
  if (_BitScanReverse (&result, (unsigned long) (x >> 32)))
    return CHAR_BIT * sizeof x - 1 - 32 - result;
  if (_BitScanReverse (&result, (unsigned long) x))
    return CHAR_BIT * sizeof x - 1 - result;
  return CHAR_BIT * sizeof x;
#else
  COUNT_LEADING_ZEROS (__builtin_clzll, _BitScanReverse64,
                       unsigned long long int);
#endif
}

#ifdef __cplusplus
}
#endif

_GL_INLINE_HEADER_END

#endif /* COUNT_LEADING_ZEROS_H */