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