• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef LIBPANDABASE_UTILS_BIT_UTILS_H
17 #define LIBPANDABASE_UTILS_BIT_UTILS_H
18 
19 #include <cmath>
20 #include <cstdint>
21 #include <cstring>
22 #include <limits>
23 #include <type_traits>
24 #include <bitset>
25 
26 #include <securec.h>
27 
28 #include "globals.h"
29 #include "macros.h"
30 
31 #define panda_bit_utils_ctz __builtin_ctz      // NOLINT(cppcoreguidelines-macro-usage)
32 #define panda_bit_utils_ctzll __builtin_ctzll  // NOLINT(cppcoreguidelines-macro-usage)
33 
34 #define panda_bit_utils_clz __builtin_clz      // NOLINT(cppcoreguidelines-macro-usage)
35 #define panda_bit_utils_clzll __builtin_clzll  // NOLINT(cppcoreguidelines-macro-usage)
36 
37 #define panda_bit_utils_ffs __builtin_ffs      // NOLINT(cppcoreguidelines-macro-usage)
38 #define panda_bit_utils_ffsll __builtin_ffsll  // NOLINT(cppcoreguidelines-macro-usage)
39 
40 #define panda_bit_utils_popcount __builtin_popcount      // NOLINT(cppcoreguidelines-macro-usage)
41 #define panda_bit_utils_popcountll __builtin_popcountll  // NOLINT(cppcoreguidelines-macro-usage)
42 
43 namespace panda {
44 
45 template <typename T>
Clz(T x)46 constexpr int Clz(T x)
47 {
48     constexpr size_t RADIX = 2;
49     static_assert(std::is_integral<T>::value, "T must be integral");
50     static_assert(std::is_unsigned<T>::value, "T must be unsigned");
51     static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
52     static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
53     ASSERT(x != 0U);
54 
55     if (sizeof(T) == sizeof(uint64_t)) {
56         return panda_bit_utils_clzll(x);
57     }
58     return panda_bit_utils_clz(x) - (std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits);
59 }
60 
61 template <typename T>
Ctz(T x)62 constexpr int Ctz(T x)
63 {
64     constexpr size_t RADIX = 2;
65     static_assert(std::is_integral<T>::value, "T must be integral");
66     static_assert(std::is_unsigned<T>::value, "T must be unsigned");
67     static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
68     static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
69     ASSERT(x != 0U);
70 
71     if (sizeof(T) == sizeof(uint64_t)) {
72         return panda_bit_utils_ctzll(x);
73     }
74     return panda_bit_utils_ctz(x);
75 }
76 
77 template <typename T>
Popcount(T x)78 constexpr int Popcount(T x)
79 {
80     constexpr size_t RADIX = 2;
81     static_assert(std::is_integral<T>::value, "T must be integral");
82     static_assert(std::is_unsigned<T>::value, "T must be unsigned");
83     static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
84     static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
85 
86     if (sizeof(T) == sizeof(uint64_t)) {
87         return panda_bit_utils_popcountll(x);
88     }
89     return panda_bit_utils_popcount(x);
90 }
91 
92 // How many bits (minimally) does it take to store the constant 'value'? i.e. 1 for 1, 2 for 2 and 3, 3 for 4 and 5 etc.
93 template <typename T>
MinimumBitsToStore(T value)94 constexpr size_t MinimumBitsToStore(T value)
95 {
96     static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
97     if constexpr (std::is_enum_v<T>) {  // NOLINT
98         using UnderlyingType = std::make_unsigned_t<std::underlying_type_t<T>>;
99         auto uvalue = static_cast<UnderlyingType>(value);
100         if (uvalue == 0) {
101             uvalue = 1;
102         }
103         return std::numeric_limits<UnderlyingType>::digits - Clz(static_cast<UnderlyingType>(uvalue));
104     } else {  // NOLINT
105         constexpr size_t RADIX = 2;
106         static_assert(std::is_integral_v<T>, "T must be integral");
107         static_assert(std::is_unsigned_v<T>, "T must be unsigned");
108         static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
109         if (value == 0) {
110             return 0;
111         }
112         return std::numeric_limits<T>::digits - Clz(value);
113     }
114 }
115 
116 template <typename T>
Ffs(T x)117 constexpr int Ffs(T x)
118 {
119     constexpr size_t RADIX = 2;
120     static_assert(std::is_integral<T>::value, "T must be integral");
121     static_assert(std::is_unsigned<T>::value, "T must be unsigned");
122     static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
123     static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
124 
125     if (sizeof(T) == sizeof(uint64_t)) {
126         return panda_bit_utils_ffsll(x);
127     }
128     return panda_bit_utils_ffs(x);
129 }
130 
131 template <size_t n, typename T>
IsAligned(T value)132 constexpr bool IsAligned(T value)
133 {
134     static_assert(std::is_integral<T>::value, "T must be integral");
135     return value % n == 0;
136 }
137 
138 template <typename T>
IsAligned(T value,size_t n)139 constexpr bool IsAligned(T value, size_t n)
140 {
141     static_assert(std::is_integral<T>::value, "T must be integral");
142     return value % n == 0;
143 }
144 
145 template <typename T>
RoundUp(T x,size_t n)146 constexpr T RoundUp(T x, size_t n)
147 {
148     static_assert(std::is_integral<T>::value, "T must be integral");
149     return (static_cast<size_t>(x) + n - 1U) & (-n);
150 }
151 
BitsToBytesRoundUp(size_t num_bits)152 constexpr size_t BitsToBytesRoundUp(size_t num_bits)
153 {
154     return RoundUp(num_bits, BITS_PER_BYTE) / BITS_PER_BYTE;
155 }
156 
157 template <typename T>
RoundDown(T x,size_t n)158 constexpr T RoundDown(T x, size_t n)
159 {
160     static_assert(std::is_integral<T>::value, "T must be integral");
161     return x & static_cast<size_t>(-n);
162 }
163 
164 template <typename T>
SwapBits(T value,T mask,uint32_t offset)165 constexpr T SwapBits(T value, T mask, uint32_t offset)
166 {
167     return ((value >> offset) & mask) | ((value & mask) << offset);
168 }
169 
170 template <typename T>
GetByteFrom(T value,uint64_t index)171 inline uint8_t GetByteFrom(T value, uint64_t index)
172 {
173     static_assert(std::is_unsigned<T>::value, "T must be unsigned");
174     constexpr uint8_t OFFSET_BYTE = 3;
175     constexpr uint8_t MASK = 0xffU;
176     uint64_t shift = index << OFFSET_BYTE;
177     return static_cast<uint8_t>((value >> shift) & MASK);
178 }
179 
ReverseBytes(uint16_t value)180 inline uint16_t ReverseBytes(uint16_t value)
181 {
182     constexpr uint32_t OFFSET_0 = 8;
183     return static_cast<uint16_t>(value << OFFSET_0) | static_cast<uint16_t>(value >> OFFSET_0);
184 }
185 
ReverseBytes(uint32_t value)186 inline uint32_t ReverseBytes(uint32_t value)
187 {
188     constexpr uint32_t BYTES_MASK = 0xff00ffU;
189     constexpr uint32_t OFFSET_0 = 8;
190     constexpr uint32_t OFFSET_1 = 16;
191     value = SwapBits(value, BYTES_MASK, OFFSET_0);
192     return (value >> OFFSET_1) | (value << OFFSET_1);
193 }
194 
ReverseBytes(uint64_t value)195 inline uint64_t ReverseBytes(uint64_t value)
196 {
197     constexpr uint64_t BYTES_MASK = 0xff00ff00ff00ffLU;
198     constexpr uint64_t WORDS_MASK = 0xffff0000ffffLU;
199     constexpr uint32_t OFFSET_0 = 8;
200     constexpr uint32_t OFFSET_1 = 16;
201     constexpr uint32_t OFFSET_2 = 32;
202     value = SwapBits(value, BYTES_MASK, OFFSET_0);
203     value = SwapBits(value, WORDS_MASK, OFFSET_1);
204     return (value >> OFFSET_2) | (value << OFFSET_2);
205 }
206 
207 template <typename T>
BSWAP(T x)208 constexpr T BSWAP(T x)
209 {
210     if (sizeof(T) == sizeof(uint16_t)) {
211         return ReverseBytes(static_cast<uint16_t>(x));
212     }
213     if (sizeof(T) == sizeof(uint32_t)) {
214         return ReverseBytes(static_cast<uint32_t>(x));
215     }
216     return ReverseBytes(static_cast<uint64_t>(x));
217 }
218 
ReverseBits(uint32_t value)219 inline uint32_t ReverseBits(uint32_t value)
220 {
221     constexpr uint32_t BITS_MASK = 0x55555555U;
222     constexpr uint32_t TWO_BITS_MASK = 0x33333333U;
223     constexpr uint32_t HALF_BYTES_MASK = 0x0f0f0f0fU;
224     constexpr uint32_t OFFSET_0 = 1;
225     constexpr uint32_t OFFSET_1 = 2;
226     constexpr uint32_t OFFSET_2 = 4;
227     value = SwapBits(value, BITS_MASK, OFFSET_0);
228     value = SwapBits(value, TWO_BITS_MASK, OFFSET_1);
229     value = SwapBits(value, HALF_BYTES_MASK, OFFSET_2);
230     return ReverseBytes(value);
231 }
232 
ReverseBits(uint64_t value)233 inline uint64_t ReverseBits(uint64_t value)
234 {
235     constexpr uint64_t BITS_MASK = 0x5555555555555555LU;
236     constexpr uint64_t TWO_BITS_MASK = 0x3333333333333333LU;
237     constexpr uint64_t HALF_BYTES_MASK = 0x0f0f0f0f0f0f0f0fLU;
238     constexpr uint32_t OFFSET_0 = 1;
239     constexpr uint32_t OFFSET_1 = 2;
240     constexpr uint32_t OFFSET_2 = 4;
241     value = SwapBits(value, BITS_MASK, OFFSET_0);
242     value = SwapBits(value, TWO_BITS_MASK, OFFSET_1);
243     value = SwapBits(value, HALF_BYTES_MASK, OFFSET_2);
244     return ReverseBytes(value);
245 }
246 
BitCount(int32_t value)247 inline uint32_t BitCount(int32_t value)
248 {
249     constexpr size_t BIT_SIZE = sizeof(int32_t) * 8;
250     return std::bitset<BIT_SIZE>(value).count();
251 }
252 
BitCount(uint32_t value)253 inline uint32_t BitCount(uint32_t value)
254 {
255     constexpr size_t BIT_SIZE = sizeof(uint32_t) * 8;
256     return std::bitset<BIT_SIZE>(value).count();
257 }
258 
BitCount(int64_t value)259 inline uint32_t BitCount(int64_t value)
260 {
261     constexpr size_t BIT_SIZE = sizeof(int64_t) * 8;
262     return std::bitset<BIT_SIZE>(value).count();
263 }
264 
265 template <typename T>
BitNumbers()266 inline constexpr uint32_t BitNumbers()
267 {
268     constexpr int BIT_NUMBER_OF_CHAR = 8;
269     return sizeof(T) * BIT_NUMBER_OF_CHAR;
270 }
271 
272 template <typename T>
ExtractBits(T value,size_t offset,size_t count)273 inline constexpr T ExtractBits(T value, size_t offset, size_t count)
274 {
275     static_assert(std::is_integral<T>::value, "T must be integral");
276     static_assert(std::is_unsigned<T>::value, "T must be unsigned");
277     ASSERT(sizeof(value) * panda::BITS_PER_BYTE >= offset + count);
278     return (value >> offset) & ((1U << count) - 1);
279 }
280 
281 template <typename T>
Low32Bits(T value)282 inline constexpr uint32_t Low32Bits(T value)
283 {
284     return static_cast<uint32_t>(reinterpret_cast<uint64_t>(value));
285 }
286 
287 template <typename T>
High32Bits(T value)288 inline constexpr uint32_t High32Bits(T value)
289 {
290     if constexpr (sizeof(T) < sizeof(uint64_t)) {  // NOLINT
291         return 0;
292     }
293     return static_cast<uint32_t>(reinterpret_cast<uint64_t>(value) >> BITS_PER_UINT32);
294 }
295 
296 template <class To, class From>
bit_cast(const From & src)297 inline To bit_cast(const From &src) noexcept  // NOLINT(readability-identifier-naming)
298 {
299     static_assert(sizeof(To) == sizeof(From), "size of the types must be equal");
300     To dst;
301     errno_t res = memcpy_s(&dst, sizeof(To), &src, sizeof(To));
302     if (res != EOK) {
303         UNREACHABLE();
304     }
305     return dst;
306 }
307 
308 template <class To, class From>
down_cast(const From & src)309 inline To down_cast(const From &src) noexcept  // NOLINT(readability-identifier-naming)
310 {
311     static_assert(sizeof(To) <= sizeof(From), "size of the types must be lesser");
312     To dst;
313     errno_t res = memcpy_s(&dst, sizeof(To), &src, sizeof(To));
314     if (res != EOK) {
315         UNREACHABLE();
316     }
317     return dst;
318 }
319 
320 template <typename T>
BitsNumInValue(const T v)321 inline constexpr uint32_t BitsNumInValue(const T v)
322 {
323     return sizeof(v) * panda::BITS_PER_BYTE;
324 }
325 
326 template <typename T>
BitsNumInType()327 inline constexpr uint32_t BitsNumInType()
328 {
329     return sizeof(T) * panda::BITS_PER_BYTE;
330 }
331 
332 template <typename From, typename To>
CastFloatToInt(From value)333 inline constexpr To CastFloatToInt(From value)
334 {
335     static_assert(std::is_floating_point_v<From>);
336     static_assert(std::is_integral_v<To>);
337     To res;
338     constexpr To MIN_INT = std::numeric_limits<To>::min();
339     constexpr To MAX_INT = std::numeric_limits<To>::max();
340     constexpr auto FLOAT_MIN_INT = static_cast<From>(MIN_INT);
341     constexpr auto FLOAT_MAX_INT = static_cast<From>(MAX_INT);
342 
343     if (value > FLOAT_MIN_INT) {
344         if (value < FLOAT_MAX_INT) {
345             res = static_cast<To>(value);
346         } else {
347             res = MAX_INT;
348         }
349     } else if (std::isnan(value)) {
350         res = 0;
351     } else {
352         res = MIN_INT;
353     }
354     return res;
355 }
356 
357 }  // namespace panda
358 
359 #endif  // LIBPANDABASE_UTILS_BIT_UTILS_H
360