1/**
2 * Copyright (c) 2021-2024 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
43namespace panda {
44
45template <typename T>
46constexpr 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
61template <typename T>
62constexpr 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
77template <typename T>
78constexpr 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.
93template <typename T>
94constexpr 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
116template <typename T>
117constexpr 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
131template <size_t n, typename T>
132constexpr bool IsAligned(T value)
133{
134    static_assert(std::is_integral<T>::value, "T must be integral");
135    return value % n == 0;
136}
137
138template <typename T>
139constexpr 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
145template <typename T>
146constexpr 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
152constexpr size_t BitsToBytesRoundUp(size_t num_bits)
153{
154    return RoundUp(num_bits, BITS_PER_BYTE) / BITS_PER_BYTE;
155}
156
157template <typename T>
158constexpr 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
164template <typename T>
165constexpr T SwapBits(T value, T mask, uint32_t offset)
166{
167    return ((value >> offset) & mask) | ((value & mask) << offset);
168}
169
170template <typename T>
171inline 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
180inline 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
186inline 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
195inline 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
207template <typename T>
208constexpr 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
219inline 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
233inline 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
247inline 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
253inline 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
259inline 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
265template <typename T>
266inline constexpr uint32_t BitNumbers()
267{
268    constexpr int BIT_NUMBER_OF_CHAR = 8;
269    return sizeof(T) * BIT_NUMBER_OF_CHAR;
270}
271
272template <typename T>
273inline 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) & ((1ULL << count) - 1);
279}
280
281template <typename T>
282inline constexpr uint32_t Low32Bits(T value)
283{
284    return static_cast<uint32_t>(reinterpret_cast<uint64_t>(value));
285}
286
287template <typename T>
288inline 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
296template <class To, class From>
297inline 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    static_assert(std::is_trivially_copyable_v<To> && std::is_trivially_copyable_v<From>,
301                  "source and destination types must be trivially copyable");
302    static_assert(std::is_trivially_constructible_v<To>, "destination type must be default constructible");
303
304    To dst;
305    errno_t res = memcpy_s(&dst, sizeof(To), &src, sizeof(To));
306    if (res != EOK) {
307        UNREACHABLE();
308    }
309    return dst;
310}
311
312template <class To, class From>
313inline To down_cast(const From &src) noexcept  // NOLINT(readability-identifier-naming)
314{
315    static_assert(sizeof(To) <= sizeof(From), "size of the types must be lesser");
316    To dst;
317    errno_t res = memcpy_s(&dst, sizeof(To), &src, sizeof(To));
318    if (res != EOK) {
319        UNREACHABLE();
320    }
321    return dst;
322}
323
324template <typename T>
325inline constexpr uint32_t BitsNumInValue(const T v)
326{
327    return sizeof(v) * panda::BITS_PER_BYTE;
328}
329
330template <typename T>
331inline constexpr uint32_t BitsNumInType()
332{
333    return sizeof(T) * panda::BITS_PER_BYTE;
334}
335
336template <typename From, typename To>
337inline constexpr To CastFloatToInt(From value)
338{
339    static_assert(std::is_floating_point_v<From>);
340    static_assert(std::is_integral_v<To>);
341    To res;
342    constexpr To MIN_INT = std::numeric_limits<To>::min();
343    constexpr To MAX_INT = std::numeric_limits<To>::max();
344    constexpr auto FLOAT_MIN_INT = static_cast<From>(MIN_INT);
345    constexpr auto FLOAT_MAX_INT = static_cast<From>(MAX_INT);
346
347    if (value > FLOAT_MIN_INT) {
348        if (value < FLOAT_MAX_INT) {
349            res = static_cast<To>(value);
350        } else {
351            res = MAX_INT;
352        }
353    } else if (std::isnan(value)) {
354        res = 0;
355    } else {
356        res = MIN_INT;
357    }
358    return res;
359}
360
361}  // namespace panda
362
363#endif  // LIBPANDABASE_UTILS_BIT_UTILS_H
364