1212#ifndef ROOT_BitUtils
1313#define ROOT_BitUtils
1414
15+ #include < algorithm>
1516#include < cassert>
1617#include < cstddef>
18+ #include < type_traits>
19+
20+ #ifdef _MSC_VER
21+ #include < intrin.h> // for _BitScan*
22+ #endif
1723
1824namespace ROOT {
1925namespace Internal {
@@ -35,6 +41,113 @@ inline constexpr T AlignUp(T value, T align) noexcept
3541 return (value + align - 1 ) & ~(align - 1 );
3642}
3743
44+ // / Given an integer `x`, returns the number of leading 0-bits starting at the most significant bit position.
45+ // / If `x` is 0, it returns the size of `x` in bits.
46+ // /
47+ // / Example:
48+ // /
49+ // / if x is a std::uint32_t with value 42 (0b0...0101010), then LeadingZeroes(x) == 26
50+ template <typename T>
51+ inline std::size_t LeadingZeroes (T x)
52+ {
53+ constexpr std::size_t maxBits = sizeof (T) * 8 ;
54+ static_assert (std::is_integral_v<T> && (maxBits == 32 || maxBits == 64 ));
55+
56+ if (x == 0 )
57+ return maxBits;
58+
59+ #ifdef _MSC_VER
60+ unsigned long idx = 0 ;
61+ [[maybe_unused]] unsigned char nonZero;
62+ if constexpr (maxBits == 32 ) {
63+ nonZero = _BitScanReverse (&idx, x);
64+ } else {
65+ #ifdef _WIN64
66+ // 64-bit machine
67+ nonZero = _BitScanReverse64 (&idx, x);
68+ #else
69+ // 32-bit machine
70+ std::uint32_t low = (x & 0xFFFF'FFFF );
71+ std::uint32_t high = (x >> 32 ) & 0xFFFF'FFFF ;
72+ unsigned long lowIdx, highIdx;
73+ unsigned char lowNonZero = _BitScanReverse (&lowIdx, low);
74+ unsigned char highNonZero = _BitScanReverse (&highIdx, high);
75+ assert (lowNonZero | highNonZero);
76+ if (high == 0 )
77+ idx = 63 - lowIdx;
78+ else
79+ idx = 31 - highIdx;
80+ return static_cast <std::size_t >(idx);
81+
82+ #endif // _WIN64
83+ }
84+
85+ assert (nonZero);
86+ // NOTE: _BitScanReverse return the 0-based index of the leftmost non-zero bit.
87+ // To convert it to the number of zeroes we need to "flip" it from [0, maxBits) to [maxBits, 0)
88+ // (e.g. _BitScanReverse == 0 <=> LeadingZeroes == maxBits)
89+ return static_cast <std::size_t >(maxBits - 1 - idx);
90+ #else
91+ if constexpr (maxBits == 32 ) {
92+ return static_cast <std::size_t >(__builtin_clz (x));
93+ } else {
94+ return static_cast <std::size_t >(__builtin_clzl (x));
95+ }
96+ #endif // _MSC_VER
97+ }
98+
99+ // / Given an integer `x`, returns the number of trailing 0-bits starting at the least significant bit position.
100+ // / If `x` is 0, it returns the size of `x` in bits.
101+ // /
102+ // / Example:
103+ // /
104+ // / if x is a std::uint32_t with value 42 (0b0...0101010), then TrailingZeroes(x) == 1
105+ template <typename T>
106+ inline std::size_t TrailingZeroes (T x)
107+ {
108+ constexpr std::size_t maxBits = sizeof (T) * 8 ;
109+ static_assert (std::is_integral_v<T> && (maxBits == 32 || maxBits == 64 ));
110+
111+ if (x == 0 )
112+ return maxBits;
113+
114+ #ifdef _MSC_VER
115+ unsigned long idx = 0 ;
116+ [[maybe_unused]] unsigned char nonZero;
117+ if constexpr (maxBits == 32 ) {
118+ nonZero = _BitScanForward (&idx, x);
119+ } else {
120+ #ifdef _WIN64
121+ // 64-bit machine
122+ nonZero = _BitScanForward64 (&idx, x);
123+ #else
124+ // 32-bit machine
125+ std::uint32_t low = (x & 0xFFFF'FFFF );
126+ std::uint32_t high = (x >> 32 ) & 0xFFFF'FFFF ;
127+ unsigned long lowIdx, highIdx;
128+ unsigned char lowNonZero = _BitScanForward (&lowIdx, low);
129+ unsigned char highNonZero = _BitScanForward (&highIdx, high);
130+ nonZero = lowNonZero | highNonZero;
131+ if (low == 0 )
132+ idx = highIdx + 32 ;
133+ else
134+ idx = lowIdx;
135+
136+ #endif // _WIN64
137+ }
138+ assert (nonZero);
139+ // Differently from LeadingZeroes, in this case the bit index returned by _BitScanForward is
140+ // already equivalent to the number of trailing zeroes, so we don't need any transformation.
141+ return static_cast <std::size_t >(idx);
142+ #else
143+ if constexpr (maxBits == 32 ) {
144+ return static_cast <std::size_t >(__builtin_ctz (x));
145+ } else {
146+ return static_cast <std::size_t >(__builtin_ctzl (x));
147+ }
148+ #endif // _MSC_VER
149+ }
150+
38151} // namespace Internal
39152} // namespace ROOT
40153
0 commit comments