header_utils
Loading...
Searching...
No Matches
bits.h
1
4
5#pragma once
6
7#include <bit>
8#include <stdexcept>
9
10#include "cpp23.h"
11
12#if !defined(__cpp_concepts)
13#error "This library requires concepts"
14#endif
15
16namespace ghassanpl
17{
20
24
30 template<typename T>
31 concept bit_integral = std::integral<T> && !std::same_as<std::remove_cvref_t<T>, bool>;
32
35 template <typename T>
36 constexpr inline auto bit_count = sizeof(T) * CHAR_BIT;
37
39 constexpr inline uint64_t all_bits = ~uint64_t{ 0 };
40
42 template <size_t BEGIN, size_t END>
43 requires (END > BEGIN) && (END <= 64)
44 constexpr inline uint64_t bit_mask_v = (all_bits >> (64 - END)) << BEGIN;
45
47 template <bit_integral FOR>
48 constexpr inline uint64_t bit_mask_for_v = (all_bits >> (64 - bit_count<FOR>));
49
51 template <typename TO, typename FROM>
52 concept bit_castable = std::is_trivially_copyable_v<TO> && std::is_trivially_copyable_v<FROM> && sizeof(TO) == sizeof(FROM);
53
55 template <size_t BEGIN, size_t END, bit_integral T>
56 constexpr T extract_bits(T value) {
57 static_assert(END > BEGIN, "range size must be greater than zero");
58 static_assert(END <= bit_count<T>, "range must be within type bit size");
59 return T(value >> BEGIN) & T((T(1)<<(END-BEGIN))-1);
60 };
61
62 template <size_t... RANGES, bit_integral T>
63 constexpr auto split_bit_ranges(T value) {
64 static_assert((RANGES + ...) == bit_count<T>);
65 uint64_t bits = static_cast<uint64_t>(value);
66 auto extract = [&]<size_t N>(std::integral_constant<size_t, N>) {
67 auto result = extract_bits<0, N>(bits);
68 bits >>= N;
69 return result;
70 };
71 return std::array {
72 extract(std::integral_constant<size_t, RANGES>{})...
73 };
74 };
75
76 template <size_t N, bit_integral T>
77 constexpr bool bit_at(T bits) { return ((bits >> N) & 1) == 1; }
78
79 namespace detail
80 {
81 template <size_t N>
82 struct uintN_t_t;
83 template <> struct uintN_t_t<8> { using value = uint8_t; };
84 template <> struct uintN_t_t<16> { using value = uint16_t; };
85 template <> struct uintN_t_t<32> { using value = uint32_t; };
86 template <> struct uintN_t_t<64> { using value = uint64_t; };
87 template <size_t N>
88 requires (N < 8)
89 struct uintN_t_t<N>
90 {
91 using value = uint8_t;
92 };
93 }
94
96 template <size_t N>
97 using uintN_t = typename detail::uintN_t_t<N>::value;
98
99 template <typename T>
100 using uint_sizeof_t = uintN_t<sizeof(T) * CHAR_BIT>;
101
102 namespace detail
103 {
104 template <size_t N>
105 struct intN_t_t
106 {
107 using value = std::make_signed_t<uintN_t<N>>;
108 };
109 }
110
112 template <size_t N>
113 using intN_t = typename detail::intN_t_t<N>::value;
114
115 template <typename T>
116 using int_sizeof_t = intN_t<sizeof(T)* CHAR_BIT>;
117
118 namespace detail
119 {
120 template <bool SIGNED, size_t N>
121 struct sintN_t_t;
122 template <size_t N> struct sintN_t_t<false, N> { using value = uintN_t<N>; };
123 template <size_t N> struct sintN_t_t<true, N> { using value = intN_t<N>; };
124 }
125
127 template <bool SIGNED, size_t N>
128 using sintN_t = typename detail::sintN_t_t<SIGNED, N>::value;
129
131 template <bit_integral... BIT_TYPES>
132 constexpr auto cat_bits(BIT_TYPES... bits)
133 {
134 using sum_type = uintN_t<(bit_count<BIT_TYPES> + ...)>;
135 sum_type result = 0;
136 auto assign = [&]<typename T>(T value) {
137 result <<= bit_count<T>;
138 result |= value;
139 };
140 (assign(bits), ...);
141 return result;
142 }
143
145 template <size_t... BIT_COUNT, bit_integral... BIT_TYPES>
146 constexpr auto cat_bits_n(BIT_TYPES... values)
147 {
148 static_assert(requires { sizeof(uintN_t<(BIT_COUNT + ...)>); });
149 static_assert(sizeof...(BIT_COUNT) == sizeof...(BIT_TYPES), "The number of bit counts must be the same as the number of values");
150 using sum_type = uintN_t<(BIT_COUNT + ...)>;
151 sum_type result = 0;
152 auto assign = [&]<size_t N, typename T>(T value) {
153 result <<= N;
154 result |= n_least_significant<N>(value);
155 };
156 ((assign.template operator()<BIT_COUNT>(values)), ...);
157 return result;
158 }
159
160 template <size_t N, bit_integral T>
161 constexpr T n_most_significant(T value) { return (value >> T(bit_count<T> -N)) & T(bit_mask_v<0, N>); };
162
163 template <size_t N, bit_integral T>
164 constexpr T n_least_significant(T value) { return value & T(bit_mask_v<0, N>); };
165
167 template <bit_integral T>
168 [[nodiscard]] constexpr auto most_significant_half(T v) noexcept
169 {
170 constexpr auto bit_count = ghassanpl::bit_count<T>;
171 constexpr auto bit_count_half = bit_count / 2;
172 return static_cast<sintN_t<std::is_signed_v<T>, bit_count_half>>((v >> bit_count_half) & bit_mask_v<0, bit_count_half>);
173 }
174
176 template <bit_integral T>
177 [[nodiscard]] constexpr auto least_significant_half(T v) noexcept
178 {
179 constexpr auto bit_count = ghassanpl::bit_count<T>;
180 constexpr auto bit_count_half = bit_count / 2;
181 return static_cast<sintN_t<std::is_signed_v<T>, bit_count_half>>(v & bit_mask_v<0, bit_count_half>);
182 }
183
185 template <bit_integral T>
186 [[nodiscard]] constexpr auto split_bits(T v) noexcept
187 {
188 return std::make_pair(most_significant_half(v), least_significant_half(v));
189 }
190
192 template <std::unsigned_integral T>
193 constexpr T bit_reverse(T x) noexcept
194 {
195 T result = 0;
196 for (int i = 0; i < std::numeric_limits<T>::digits; ++i) {
197 result <<= 1;
198 result |= x & 1;
199 x >>= 1;
200 }
201 return result;
202 }
203
205 template <std::unsigned_integral T>
206 constexpr T bit_repeat(T x, int length) noexcept(false)
207 {
208 T result = 0;
209 for (int i = 0; i < std::numeric_limits<T>::digits; ++i) {
210 result |= ((x >> (i % length)) & 1) << i;
211 }
212 return result;
213 }
214
216 template <std::unsigned_integral T>
217 constexpr T bit_compress(T x, T m) noexcept
218 {
219 T result = 0;
220 for (int i = 0, j = 0; i < std::numeric_limits<T>::digits; ++i) {
221 bool mask_bit = (m >> i) & 1;
222 result |= (mask_bit & (x >> i)) << j;
223 j += mask_bit;
224 }
225 return result;
226 }
227
229 template <std::unsigned_integral T>
230 constexpr T bit_expand(T x, T m) noexcept
231 {
232 T result = 0;
233 for (int i = 0, j = 0; i < std::numeric_limits<T>::digits; ++i) {
234 bool mask_bit = (m >> i) & 1;
235 result |= (mask_bit & (x >> j)) << i;
236 j += mask_bit;
237 }
238 return result;
239 }
240
243
245 template <bit_integral B>
246 [[nodiscard]] constexpr B to_big_endian(B val) noexcept { if constexpr (std::endian::native == std::endian::big) return val; else return byteswap(val); }
247
249 template <bit_integral B>
250 [[nodiscard]] constexpr B to_little_endian(B val) noexcept { if constexpr (std::endian::native == std::endian::little) return val; else return byteswap(val); }
251
253 template <bit_integral B>
254 [[nodiscard]] constexpr B from_big_endian(B val) noexcept { if constexpr (std::endian::native == std::endian::big) return val; else return byteswap(val); }
255
257 template <bit_integral B>
258 [[nodiscard]] constexpr B from_little_endian(B val) noexcept { if constexpr (std::endian::native == std::endian::little) return val; else return byteswap(val); }
259
261 template <std::endian ENDIANNESS, bit_integral B>
262 [[nodiscard]] constexpr B to_endian(B val) noexcept { if constexpr (std::endian::native == ENDIANNESS) return val; else return byteswap(val); }
263
265 template <bit_integral B>
266 [[nodiscard]] constexpr B to_endian(B val, std::endian endianness) noexcept { if (std::endian::native == endianness) return val; else return byteswap(val); }
267
269 template <std::endian ENDIANNESS, bit_integral B>
270 [[nodiscard]] constexpr B from_endian(B val) noexcept { if constexpr (std::endian::native == ENDIANNESS) return val; else return byteswap(val); }
271
273 template <bit_integral B>
274 [[nodiscard]] constexpr B from_endian(B val, std::endian endianness) noexcept { if (std::endian::native == endianness) return val; else return byteswap(val); }
275
277
279 constexpr inline size_t dynamic_bit_number = size_t(-1);
280
281 namespace detail
282 {
283 template <size_t NUM>
284 using bit_num_t = std::integral_constant<size_t, NUM>;
285
286 template <size_t NUM>
287 constexpr inline auto bit_num = bit_num_t<NUM>{};
288
289 template <bit_integral VALUE_TYPE, size_t BIT_NUM>
290 struct bit_num_base
291 {
292 using unsigned_value_type = std::make_unsigned_t<VALUE_TYPE>;
293 protected:
294 static constexpr VALUE_TYPE m_bit_mask = std::bit_cast<VALUE_TYPE>(unsigned_value_type(unsigned_value_type(1) << BIT_NUM % bit_count<VALUE_TYPE>));
295 };
296
297 template <bit_integral VALUE_TYPE>
298 struct bit_num_base<VALUE_TYPE, dynamic_bit_number>
299 {
300 using unsigned_value_type = std::make_unsigned_t<VALUE_TYPE>;
301 protected:
302 constexpr bit_num_base(size_t bitnum) noexcept : m_bit_mask(std::bit_cast<VALUE_TYPE>(unsigned_value_type(unsigned_value_type(1) << bitnum % bit_count<VALUE_TYPE>))) {}
303 VALUE_TYPE m_bit_mask;
304 };
305 }
306
308 template <bit_integral VALUE_TYPE, size_t BIT_NUM = dynamic_bit_number>
309 struct bit_reference : public detail::bit_num_base<VALUE_TYPE, BIT_NUM>
310 {
311 using base_type = detail::bit_num_base<VALUE_TYPE, BIT_NUM>;
312 static constexpr size_t value_bit_count = bit_count<VALUE_TYPE>;
313
314 static constexpr bool is_static = BIT_NUM != dynamic_bit_number;
315 static constexpr bool is_const = std::is_const_v<VALUE_TYPE>;
316
317 constexpr bit_reference(VALUE_TYPE& ref, size_t bitnum) requires (!is_static)
318 : base_type(bitnum)
319 , m_value_ref(&ref)
320 {
321 if (bitnum >= value_bit_count)
322 throw std::invalid_argument("bit_num can't be greater than or equal to the number of bits in the value type");
323 }
324
325 constexpr bit_reference(VALUE_TYPE& ref, detail::bit_num_t<BIT_NUM> = {}) noexcept requires is_static
326 : m_value_ref(&ref)
327 {
328 static_assert(BIT_NUM < value_bit_count, "BIT_NUM template argument can't be greater than or equal to the number of bits in the value type");
329 }
330
331 constexpr bit_reference& operator=(bool val) noexcept requires (!is_const)
332 {
333 if (val)
334 *m_value_ref |= this->m_bit_mask;
335 else
336 *m_value_ref &= ~this->m_bit_mask;
337 return *this;
338 }
339
340 [[nodiscard]] explicit constexpr operator bool() const noexcept
341 {
342 return (*m_value_ref & this->m_bit_mask) != 0;
343 }
344
345 template <bit_integral OTHER_VALUE_TYPE, size_t OTHER_BIT_NUM>
346 [[nodiscard]] constexpr bool operator==(bit_reference<OTHER_VALUE_TYPE, OTHER_BIT_NUM> const& other) const noexcept
347 {
348 return this->operator bool() == other.operator bool();
349 }
350
351 template <bit_integral OTHER_VALUE_TYPE, size_t OTHER_BIT_NUM>
352 [[nodiscard]] constexpr auto operator<=>(bit_reference<OTHER_VALUE_TYPE, OTHER_BIT_NUM> const& other) const noexcept
353 {
354 return this->operator bool() <=> other.operator bool();
355 }
356
358 [[nodiscard]] constexpr auto& integer_value() const noexcept { return *m_value_ref; }
359
361 [[nodiscard]] constexpr size_t bit_number() const noexcept
362 {
363 if constexpr (is_static)
364 return BIT_NUM;
365 else
366 return std::countr_zero(std::bit_cast<std::make_unsigned_t<VALUE_TYPE>>(this->m_bit_mask));
367 }
368
369 private:
370
371 VALUE_TYPE* m_value_ref;
372 };
373
375 template <bit_integral VALUE_TYPE>
376 bit_reference(VALUE_TYPE&, size_t) -> bit_reference<VALUE_TYPE>;
377 template <bit_integral VALUE_TYPE, size_t BIT_NUM>
378 bit_reference(VALUE_TYPE&, detail::bit_num_t<BIT_NUM>) -> bit_reference<VALUE_TYPE, BIT_NUM>;
380
382}
Whether two types can be safely bit_cast between each other.
Definition bits.h:52
Whether a type is integral (but not a bool).
Definition bits.h:31
constexpr B from_little_endian(B val) noexcept
Returns val (in little-endian representation) converted into native representation.
Definition bits.h:258
typename detail::sintN_t_t< SIGNED, N >::value sintN_t
A signed integer type for the given PoT bit size.
Definition bits.h:128
constexpr uint64_t all_bits
A value of type uint64_t with all bits set.
Definition bits.h:39
constexpr auto split_bits(T v) noexcept
Returns a pair of integers with the N/2 most significant and least significant bits of the given N-bi...
Definition bits.h:186
constexpr size_t dynamic_bit_number
Used to specify that a ghassanpl::bit_reference references a bit number given at runtime.
Definition bits.h:279
constexpr B to_big_endian(B val) noexcept
Returns val in its big-endian representation.
Definition bits.h:246
typename detail::uintN_t_t< N >::value uintN_t
An unsigned integer type for the given PoT bit size.
Definition bits.h:97
typename detail::intN_t_t< N >::value intN_t
An integer type for the given PoT bit size.
Definition bits.h:113
constexpr uint64_t bit_mask_for_v
Value with all bits available for the FOR type set (e.g. first 8 bits for uint8_t will be set,...
Definition bits.h:48
constexpr auto least_significant_half(T v) noexcept
Returns an integer with the N/2 least significant bits of the given N-bit integer.
Definition bits.h:177
constexpr B to_little_endian(B val) noexcept
Returns val in its little-endian representation.
Definition bits.h:250
constexpr auto cat_bits_n(BIT_TYPES... values)
Creates a new integral value by concatenating the given bit values, each truncated to the given bit c...
Definition bits.h:146
constexpr T bit_repeat(T x, int length) noexcept(false)
Repeats a pattern stored in the least significant length bits of x, as many times as fits into x.
Definition bits.h:206
constexpr B to_endian(B val) noexcept
Returns val in its ENDIANNESS representation.
Definition bits.h:262
constexpr T bit_compress(T x, T m) noexcept
For each one-bit in m, the corresponding bit in x is taken and packed contiguously into the result,...
Definition bits.h:217
constexpr auto cat_bits(BIT_TYPES... bits)
Creates a new integral value by concatenating the given bit values.
Definition bits.h:132
constexpr B from_big_endian(B val) noexcept
Returns val (in big-endian representation) converted into native representation.
Definition bits.h:254
constexpr B from_endian(B val) noexcept
Returns val (in ENDIANNESS representation) converted into native representation.
Definition bits.h:270
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:36
constexpr auto most_significant_half(T v) noexcept
Returns an integer with the N/2 most significant bits of the given N-bit integer.
Definition bits.h:168
constexpr T extract_bits(T value)
Returns just the bits between BEGIN and END in value.
Definition bits.h:56
constexpr uint64_t bit_mask_v
Value with bits between BEGIN and END (exclusive) set.
Definition bits.h:44
constexpr T bit_reverse(T x) noexcept
Reverses the bits of x so that the least significant bit becomes the most significant.
Definition bits.h:193
constexpr T bit_expand(T x, T m) noexcept
For each one-bit in m, a bit from x, starting with the least significant bit is taken and shifted int...
Definition bits.h:230
The below code is based on Sun's libm library code, which is licensed under the following license:
Primary namespace for everything in this library.
Definition align+rec2.h:10
constexpr size_t bit_number() const noexcept
Returns the bit number of the referenced bit.
Definition bits.h:361
constexpr auto & integer_value() const noexcept
Return the value of the referenced variable.
Definition bits.h:358