12#if !defined(__cpp_concepts)
13#error "This library requires concepts"
31 concept bit_integral = std::integral<T> && !std::same_as<std::remove_cvref_t<T>,
bool>;
36 constexpr inline auto bit_count =
sizeof(T) * CHAR_BIT;
39 constexpr inline uint64_t
all_bits = ~uint64_t{ 0 };
42 template <
size_t BEGIN,
size_t END>
43 requires (END > BEGIN) && (END <= 64)
47 template <bit_
integral FOR>
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);
55 template <
size_t BEGIN,
size_t END, bit_
integral T>
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);
62 template <
size_t... RANGES, bit_integral T>
63 constexpr auto split_bit_ranges(T value) {
65 uint64_t bits =
static_cast<uint64_t
>(value);
66 auto extract = [&]<
size_t N>(std::integral_constant<size_t, N>) {
72 extract(std::integral_constant<size_t, RANGES>{})...
76 template <
size_t N, bit_
integral T>
77 constexpr bool bit_at(T bits) {
return ((bits >> N) & 1) == 1; }
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; };
91 using value = uint8_t;
97 using uintN_t =
typename detail::uintN_t_t<N>::value;
100 using uint_sizeof_t =
uintN_t<
sizeof(T) * CHAR_BIT>;
107 using value = std::make_signed_t<uintN_t<N>>;
113 using intN_t =
typename detail::intN_t_t<N>::value;
115 template <
typename T>
116 using int_sizeof_t =
intN_t<
sizeof(T)* CHAR_BIT>;
120 template <
bool SIGNED,
size_t N>
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>; };
127 template <
bool SIGNED,
size_t N>
128 using sintN_t =
typename detail::sintN_t_t<SIGNED, N>::value;
136 auto assign = [&]<
typename T>(T value) {
137 result <<= bit_count<T>;
145 template <
size_t... BIT_COUNT, bit_integral... BIT_TYPES>
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 + ...)>;
152 auto assign = [&]<
size_t N,
typename T>(T value) {
154 result |= n_least_significant<N>(value);
156 ((assign.template operator()<BIT_COUNT>(values)), ...);
160 template <
size_t N, bit_
integral T>
163 template <
size_t N, bit_
integral T>
164 constexpr T n_least_significant(T value) {
return value & T(
bit_mask_v<0, N>); };
167 template <bit_
integral T>
171 constexpr auto bit_count_half =
bit_count / 2;
176 template <bit_
integral T>
180 constexpr auto bit_count_half =
bit_count / 2;
185 template <bit_
integral T>
192 template <std::
unsigned_
integral T>
196 for (
int i = 0; i < std::numeric_limits<T>::digits; ++i) {
205 template <std::
unsigned_
integral T>
209 for (
int i = 0; i < std::numeric_limits<T>::digits; ++i) {
210 result |= ((x >> (i % length)) & 1) << i;
216 template <std::
unsigned_
integral T>
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;
229 template <std::
unsigned_
integral T>
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;
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
283 template <
size_t NUM>
284 using bit_num_t = std::integral_constant<size_t, NUM>;
286 template <
size_t NUM>
287 constexpr inline auto bit_num = bit_num_t<NUM>{};
289 template <bit_
integral VALUE_TYPE,
size_t BIT_NUM>
292 using unsigned_value_type = std::make_unsigned_t<VALUE_TYPE>;
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>));
297 template <bit_
integral VALUE_TYPE>
300 using unsigned_value_type = std::make_unsigned_t<VALUE_TYPE>;
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;
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>
311 using base_type = detail::bit_num_base<VALUE_TYPE, BIT_NUM>;
315 static constexpr bool is_const = std::is_const_v<VALUE_TYPE>;
317 constexpr bit_reference(VALUE_TYPE& ref,
size_t bitnum)
requires (!is_static)
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");
325 constexpr bit_reference(VALUE_TYPE& ref, detail::bit_num_t<BIT_NUM> = {})
noexcept requires is_static
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");
331 constexpr bit_reference& operator=(
bool val)
noexcept requires (!is_const)
334 *m_value_ref |= this->m_bit_mask;
336 *m_value_ref &= ~this->m_bit_mask;
340 [[nodiscard]]
explicit constexpr operator bool()
const noexcept
342 return (*m_value_ref & this->m_bit_mask) != 0;
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
348 return this->
operator bool() == other.operator bool();
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
354 return this->
operator bool() <=> other.operator bool();
358 [[nodiscard]]
constexpr auto&
integer_value() const noexcept {
return *m_value_ref; }
363 if constexpr (is_static)
366 return std::countr_zero(std::bit_cast<std::make_unsigned_t<VALUE_TYPE>>(this->m_bit_mask));
371 VALUE_TYPE* m_value_ref;
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>;
Whether two types can be safely bit_cast between each other.
Whether a type is integral (but not a bool).
constexpr B from_little_endian(B val) noexcept
Returns val (in little-endian representation) converted into native representation.
typename detail::sintN_t_t< SIGNED, N >::value sintN_t
A signed integer type for the given PoT bit size.
constexpr uint64_t all_bits
A value of type uint64_t with all bits set.
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...
constexpr size_t dynamic_bit_number
Used to specify that a ghassanpl::bit_reference references a bit number given at runtime.
constexpr B to_big_endian(B val) noexcept
Returns val in its big-endian representation.
typename detail::uintN_t_t< N >::value uintN_t
An unsigned integer type for the given PoT bit size.
typename detail::intN_t_t< N >::value intN_t
An integer type for the given PoT bit size.
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,...
constexpr auto least_significant_half(T v) noexcept
Returns an integer with the N/2 least significant bits of the given N-bit integer.
constexpr B to_little_endian(B val) noexcept
Returns val in its little-endian representation.
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...
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.
constexpr B to_endian(B val) noexcept
Returns val in its ENDIANNESS representation.
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,...
constexpr auto cat_bits(BIT_TYPES... bits)
Creates a new integral value by concatenating the given bit values.
constexpr B from_big_endian(B val) noexcept
Returns val (in big-endian representation) converted into native representation.
constexpr B from_endian(B val) noexcept
Returns val (in ENDIANNESS representation) converted into native representation.
constexpr auto bit_count
Equal to the number of bits in the type.
constexpr auto most_significant_half(T v) noexcept
Returns an integer with the N/2 most significant bits of the given N-bit integer.
constexpr T extract_bits(T value)
Returns just the bits between BEGIN and END in value.
constexpr uint64_t bit_mask_v
Value with bits between BEGIN and END (exclusive) set.
constexpr T bit_reverse(T x) noexcept
Reverses the bits of x so that the least significant bit becomes the most significant.
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...
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.
constexpr size_t bit_number() const noexcept
Returns the bit number of the referenced bit.
constexpr auto & integer_value() const noexcept
Return the value of the referenced variable.