5export module lib.bitfield;
6import lib.fixed_string;
8import lib.pack_indexing;
10import zxfoundation.base.types;
11import zxfoundation.sys.syschk.barebones;
14#define __zxmacro_want_assert
15#include macro_extensions
19using lib::literals::operator
""_f;
26 template<usize Start, usize End = Start>
28 static constexpr usize start = Start;
29 static constexpr usize end = End;
30 static constexpr usize min_bit = (Start <= End) ? Start : End;
31 static constexpr usize max_bit = (Start <= End) ? End : Start;
32 static constexpr usize width = max_bit - min_bit + 1;
35 template<usize Start, usize End>
36 using bit_span = bit_slice<Start, End>;
40 [[nodiscard]]
static constexpr auto bit_shift(usize total_bits, usize n)
noexcept -> usize {
41 chk_assert(n < total_bits);
42 return total_bits - 1 - n;
45 [[nodiscard]]
static constexpr auto range_shift_width(usize total_bits, usize a,
46 usize b)
noexcept -> std::pair<usize, usize> {
47 const usize min_bit = (a <= b) ? a : b;
48 const usize max_bit = (a <= b) ? b : a;
49 chk_assert(max_bit < total_bits);
50 const usize width = max_bit - min_bit + 1;
51 const usize shift = total_bits - 1 - max_bit;
52 return {shift, width};
58 [[nodiscard]]
static constexpr auto bit_shift(usize, usize n)
noexcept -> usize {
62 [[nodiscard]]
static constexpr auto range_shift_width(usize total_bits, usize a,
63 usize b)
noexcept -> std::pair<usize, usize> {
64 const usize min_bit = (a <= b) ? a : b;
65 const usize max_bit = (a <= b) ? b : a;
66 chk_assert(max_bit < total_bits);
67 const usize width = max_bit - min_bit + 1;
68 const usize shift = min_bit;
69 return {shift, width};
73 inline constexpr msb0_t msb0{};
74 inline constexpr lsb0_t lsb0{};
77 template<usize ByteOffset, usize StartBit, usize EndBit,
class Numbering = msb0_t>
79 static constexpr usize byte_offset = ByteOffset;
80 static constexpr usize start = StartBit;
81 static constexpr usize end = EndBit;
82 using numbering = Numbering;
83 static constexpr usize min_bit = (StartBit <= EndBit) ? StartBit : EndBit;
84 static constexpr usize max_bit = (StartBit <= EndBit) ? EndBit : StartBit;
85 static constexpr usize width = max_bit - min_bit + 1;
87 static constexpr usize end_byte = ByteOffset + (max_bit >> 3) + 1;
91 template<usize Offset>
93 static constexpr usize byte_offset = Offset;
96 using at_byte = addr<Offset + K>;
98 using at_halfword = addr<Offset + K * 2>;
100 using at_word = addr<Offset + K * 4>;
102 using at_dword = addr<Offset + K * 8>;
104 using at_qword = addr<Offset + K * 16>;
106 template<usize A, usize B,
class Numbering = msb0_t>
107 using at_bits = slice<Offset, A, B, Numbering>;
109 template<usize B,
class Numbering = msb0_t>
110 using at_bit = slice<Offset, B, B, Numbering>;
116 using at_byte = addr<N>;
118 using at_halfword = addr<N * 2>;
120 using at_word = addr<N * 4>;
122 using at_dword = addr<N * 8>;
124 using at_qword = addr<N * 16>;
128 template<usize Start, usize End>
130 static constexpr usize start = Start;
131 static constexpr usize end = End;
132 static constexpr usize min_byte = (Start <= End) ? Start : End;
133 static constexpr usize max_byte = (Start <= End) ? End : Start;
134 static constexpr usize size = max_byte - min_byte + 1;
138 template<usize Count, usize Stride>
140 static constexpr usize count = Count;
141 static constexpr usize stride = Stride;
201 struct endian_little {
215 static constexpr auto value = V;
219 static constexpr auto value = V;
228 using w1c = attr::w1c;
229 using w1s = attr::w1s;
230 using w1t = attr::w1t;
231 using w0c = attr::w0c;
234 using raz = attr::raz;
235 using rao = attr::rao;
236 using wac = attr::wac;
237 using woo = attr::woo;
241 template<usize Alignment>
242 struct aligned_address {
243 static_assert(std::has_single_bit(Alignment),
"lib::bitfield: Alignment must be a power of two!");
244 static constexpr usize shift = std::countr_zero(Alignment);
247 [[nodiscard]]
static constexpr auto from_bits(u64 raw_bits)
noexcept -> u64 {
248 return raw_bits << shift;
252 [[nodiscard]]
static constexpr auto to_bits(u64 address)
noexcept -> u64 {
253 chk_assert((address & (Alignment - 1)) == 0 &&
"Hardware address violates alignment boundary!");
254 return address >> shift;
258 template<usize Shift>
261 [[nodiscard]]
static constexpr auto from_bits(
auto raw_bits)
noexcept {
262 return raw_bits << Shift;
266 [[nodiscard]]
static constexpr auto to_bits(
auto val)
noexcept {
267 using V =
decltype(val);
268 constexpr V low_mask =
static_cast<V>((
static_cast<V>(1) << Shift) -
static_cast<V>(1));
269 chk_assert((val & low_mask) == 0 &&
"lib::bitfield: shifted value violates 2**Shift granularity!");
276 template<
class T, usize Width>
277 [[nodiscard]]
static consteval auto field_mask()
noexcept -> T {
278 if constexpr (Width >=
sizeof(T) * 8)
return static_cast<T>(~T{0});
279 else return static_cast<T>((
static_cast<T>(1) << (Width - 1) << 1) -
static_cast<T>(1));
283 template<usize Width = 0>
284 [[nodiscard]]
static constexpr auto from_bits(
auto raw_bits)
noexcept {
285 using T =
decltype(raw_bits);
286 if constexpr (Width == 0)
return static_cast<T>(~raw_bits);
287 else return static_cast<T>(~raw_bits & field_mask<T, Width>());
290 template<usize Width = 0>
291 [[nodiscard]]
static constexpr auto to_bits(
auto val)
noexcept {
292 using T =
decltype(val);
293 if constexpr (Width == 0)
return static_cast<T>(~val);
294 else return static_cast<T>(~val & field_mask<T, Width>());
298 template<
template <
class>
class... Mixins>
303 struct is_byte_array : std::false_type {
307 struct is_byte_array<std::array<u8, N> > : std::true_type {
315 struct is_exact_uint : std::false_type {
319 struct is_exact_uint<exact_uint<Bits> > : std::true_type {
323 inline constexpr bool is_exact_uint_v = is_exact_uint<std::remove_cvref_t<T> >::value;
327 concept unsigned_word =
328 (std::unsigned_integral<T> && !std::same_as<std::remove_cv_t<T>,
bool>)
329#if defined(__SIZEOF_INT128__
)
330 || std::same_as<std::remove_cv_t<T>, u128>
331 || std::same_as<std::remove_cv_t<T>,
unsigned __int128>
335 template<fixed_string FullPath>
337 static constexpr usize split_pos = []()
consteval -> usize {
338 for (usize i = 0; i < FullPath.size(); ++i) {
339 if (FullPath.c_str()[i] ==
'\\')
return i;
341 return static_cast<usize>(-1);
344 static constexpr bool has_tail = (split_pos !=
static_cast<usize>(-1));
346 static constexpr auto head = []()
consteval {
347 if constexpr (!has_tail)
return FullPath;
349 char buf[split_pos + 1]{};
350 for (usize i = 0; i < split_pos; ++i) buf[i] = FullPath.c_str()[i];
351 buf[split_pos] =
'\0';
352 return fixed_string(buf);
356 static constexpr auto tail = []()
consteval {
357 if constexpr (!has_tail)
return fixed_string(
"");
359 constexpr usize tail_len = FullPath.size() - split_pos - 1;
360 char buf[tail_len + 1]{};
361 for (usize i = 0; i < tail_len; ++i) buf[i] = FullPath.c_str()[split_pos + 1 + i];
362 buf[tail_len] =
'\0';
363 return fixed_string(buf);
368 template<
class T,
class... Ts>
369 inline constexpr bool is_one_of_v = (std::is_same_v<T, Ts> || ...);
372 inline constexpr bool is_access_v = is_one_of_v<T,
373 attr::rw, attr::ro, attr::wo, attr::w1c, attr::w1s, attr::w1t, attr::w0c,
374 attr::rc, attr::rz, attr::raz, attr::rao, attr::wac, attr::woo>;
377 struct is_transform_tag : std::false_type {
381 struct is_transform_tag<attr::aligned<A> > : std::true_type {
385 struct is_transform_tag<attr::shifted_by<S> > : std::true_type {
389 struct is_transform_tag<attr::inverted> : std::true_type {
393 inline constexpr bool is_transform_v = is_transform_tag<T>::value;
396 struct transform_of {
401 struct transform_of<attr::aligned<A> > {
402 using type = aligned_address<A>;
406 struct transform_of<attr::shifted_by<S> > {
407 using type = shifted<S>;
411 struct transform_of<attr::inverted> {
412 using type = inverted;
416 inline constexpr bool is_endian_v = is_one_of_v<T, attr::endian_big, attr::endian_little>;
419 struct is_word_tag : std::false_type {
423 struct is_word_tag<attr::word<W> > : std::true_type {
427 inline constexpr bool is_word_v = is_word_tag<T>::value;
430 struct word_type_of {
435 struct word_type_of<attr::word<W> > {
440 struct is_init_tag : std::false_type {
444 struct is_init_tag<attr::init<V> > : std::true_type {
448 inline constexpr bool is_init_v = is_init_tag<T>::value;
451 struct is_fixed_tag : std::false_type {
455 struct is_fixed_tag<attr::fixed<V> > : std::true_type {
459 inline constexpr bool is_fixed_v = is_fixed_tag<T>::value;
462 inline constexpr bool is_overlay_v = std::is_same_v<T, attr::overlay>;
464 inline constexpr bool is_unaligned_v = std::is_same_v<T, attr::unaligned>;
469 template<
template <
class>
class Pred,
class... Ts>
471 using type = none_token;
474 template<
template <
class>
class Pred,
class H,
class... T>
475 struct first_of<Pred, H, T...> {
476 using type = std::conditional_t<Pred<H>::value, H,
typename first_of<Pred, T...>::type>;
479 template<
class... Attrs>
480 struct select_access {
481 using type = attr::rw;
484 template<
class H,
class... T>
485 struct select_access<H, T...> {
486 using type = std::conditional_t<is_access_v<H>, H,
typename select_access<T...>::type>;
489 template<
class H,
bool = is_transform_v<H> >
490 struct as_transform {
495 struct as_transform<H,
true> {
496 using type =
typename transform_of<H>::type;
500 struct select_transform {
504 template<
class H,
class... T>
505 struct select_transform<H, T...> {
506 using type = std::conditional_t<
507 !std::is_void_v<
typename as_transform<H>::type>,
508 typename as_transform<H>::type,
509 typename select_transform<T...>::type>;
512 template<
class... Attrs>
513 struct select_endian {
514 using type = endian::native;
517 template<
class H,
class... T>
518 struct select_endian<H, T...> {
519 using type = std::conditional_t<
520 std::is_same_v<H, attr::endian_big>,
523 std::is_same_v<H, attr::endian_little>,
525 typename select_endian<T...>::type> >;
528 template<
class... Attrs>
533 template<
class H,
class... T>
534 struct select_word<H, T...> {
535 using type = std::conditional_t<is_word_v<H>,
typename word_type_of<H>::type,
typename select_word<T
539 template<
class... Attrs>
540 struct select_init_tag {
541 using type = none_token;
544 template<
class H,
class... T>
545 struct select_init_tag<H, T...> {
546 using type = std::conditional_t<is_init_v<H>, H,
typename select_init_tag<T...>::type>;
549 template<
class... Attrs>
550 struct select_fixed_tag {
551 using type = none_token;
554 template<
class H,
class... T>
555 struct select_fixed_tag<H, T...> {
556 using type = std::conditional_t<is_fixed_v<H>, H,
typename select_fixed_tag<T...>::type>;
560 [[nodiscard]]
consteval auto to_raw_bits()
noexcept -> u64 {
561 using D =
decltype(V);
562 if constexpr (std::is_enum_v<D>)
return static_cast<u64>(
static_cast<std::underlying_type_t<D>>(V));
563 else if constexpr (std::is_same_v<D,
bool>)
return V ? 1ULL : 0ULL;
564 else return static_cast<u64>(V);
568 struct raw_tag_bits {
569 static constexpr u64 value = 0;
573 struct raw_tag_bits<attr::init<V> > {
574 static constexpr u64 value = to_raw_bits<V>();
578 struct raw_tag_bits<attr::fixed<V> > {
579 static constexpr u64 value = to_raw_bits<V>();
583 struct record_slices {
584 using type = std::tuple<>;
587 template<usize A, usize B>
588 struct record_slices<bit_slice<A, B> > {
589 using type = std::tuple<bit_slice<A, B> >;
592 template<
class... Ss>
593 struct record_slices<std::tuple<Ss...> > {
594 using type = std::tuple<Ss...>;
599 concept bit_storage =
600 detail::unsigned_word<T> ||
601 is_byte_array<std::remove_cvref_t<T> >::value ||
605 struct storage_traits;
607 template<detail::unsigned_word T>
608 struct storage_traits<T> {
609 static constexpr bool is_scalar =
true;
610 static constexpr usize total_bits =
sizeof(T) * 8;
611 static constexpr usize byte_size =
sizeof(T);
613 [[nodiscard]]
static constexpr auto bitmask(usize count)
noexcept -> T {
614 if (count >= total_bits)
return static_cast<T>(~T{0});
615 if (count == 0)
return T{0};
616 return static_cast<T>((
static_cast<T>(1) << (count - 1) << 1) -
static_cast<T>(1));
621 struct storage_traits<std::array<u8, N> > {
622 static constexpr bool is_scalar =
false;
623 static constexpr usize total_bits = N * 8;
624 static constexpr usize byte_size = N;
626 [[nodiscard]]
static constexpr auto bitmask(usize count)
noexcept -> u64 {
627 if (count >= 64)
return static_cast<u64>(~u64{0});
628 if (count == 0)
return u64{0};
629 return (1ULL << count) - 1ULL;
634 template<unsigned_word T>
635 [[nodiscard]]
constexpr auto safe_bitmask(usize count, usize total_bits)
noexcept -> T {
636 if (count >= total_bits)
return static_cast<T>(~T{0});
637 if (count == 0)
return T{0};
638 return static_cast<T>((
static_cast<T>(1) << (count - 1) << 1) -
static_cast<T>(1));
641 template<std::signed_integral TargetType, usize Width, unsigned_word InT>
642 [[nodiscard]]
constexpr auto sign_extend(InT val)
noexcept -> TargetType {
643 constexpr usize in_bits =
sizeof(InT) * 8;
644 static_assert(Width <= in_bits,
"sign_extend: Width exceeds input integer width");
645 if constexpr (Width == in_bits) {
646 return static_cast<TargetType>(
static_cast<std::make_signed_t<InT>>(val));
648 constexpr usize shift = in_bits - Width;
649 return static_cast<TargetType>(
static_cast<std::make_signed_t<InT>>(val << shift) >> shift);
653 template<
class Numbering, usize TotalBits>
654 [[nodiscard]]
constexpr auto get_bit_from_bytes(
const u8 *data, usize bit_idx)
noexcept ->
bool {
655 chk_assert(bit_idx < TotalBits);
656 const usize byte_idx = bit_idx >> 3;
657 const usize bit_in_byte = (std::is_same_v<Numbering, msb0_t>) ? (7 - (bit_idx & 7)) : (bit_idx & 7);
658 return ((data[byte_idx] >> bit_in_byte) & 1) != 0;
661 template<
class Numbering, usize TotalBits>
662 constexpr auto set_bit_in_bytes(u8 *data, usize bit_idx,
bool val)
noexcept ->
void {
663 chk_assert(bit_idx < TotalBits);
664 const usize byte_idx = bit_idx >> 3;
665 const usize bit_in_byte = (std::is_same_v<Numbering, msb0_t>) ? (7 - (bit_idx & 7)) : (bit_idx & 7);
666 const u8 mask =
static_cast<u8>(1U << bit_in_byte);
667 if (val) data[byte_idx] |= mask;
668 else data[byte_idx] &=
static_cast<u8>(~mask);
671 template<unsigned_word OutT,
class Numbering, usize TotalBits>
672 [[nodiscard]]
constexpr auto extract_slice_from_bytes(
const u8 *data, usize a, usize b)
noexcept -> OutT {
673 chk_assert((a <= b ? (b - a + 1) : (a - b + 1)) <=
sizeof(OutT) * 8);
675 const usize min_bit = (a <= b) ? a : b;
676 const usize width = (a <= b) ? (b - a + 1) : (a - b + 1);
677 if constexpr (std::is_same_v<Numbering, msb0_t>) {
678 for (usize i = 0; i < width; ++i) {
679 bool bit_val = get_bit_from_bytes<Numbering, TotalBits>(data, min_bit + i);
680 result =
static_cast<OutT>((result << 1) | (bit_val ? 1 : 0));
683 for (usize i = 0; i < width; ++i) {
684 bool bit_val = get_bit_from_bytes<Numbering, TotalBits>(data, min_bit + i);
685 if (bit_val) result =
static_cast<OutT>(result | (
static_cast<OutT>(1) << i));
691 template<unsigned_word InT,
class Numbering, usize TotalBits>
692 constexpr auto insert_slice_to_bytes(u8 *data, usize a, usize b, InT val)
noexcept ->
void {
693 const usize min_bit = (a <= b) ? a : b;
694 const usize width = (a <= b) ? (b - a + 1) : (a - b + 1);
695 chk_assert(width <=
sizeof(InT) * 8);
696 if constexpr (std::is_same_v<Numbering, msb0_t>) {
697 for (usize i = 0; i < width; ++i) {
698 bool bit_val = ((val >> (width - 1 - i)) & 1) != 0;
699 set_bit_in_bytes<Numbering, TotalBits>(data, min_bit + i, bit_val);
702 for (usize i = 0; i < width; ++i) {
703 bool bit_val = ((val >> i) & 1) != 0;
704 set_bit_in_bytes<Numbering, TotalBits>(data, min_bit + i, bit_val);
709 template<usize Count>
710 consteval auto smallest_uint_for_bits() {
711 if constexpr (Count <= 8)
return u8{};
712 else if constexpr (Count <= 16)
return u16{};
713 else if constexpr (Count <= 32)
return u32{};
714 else if constexpr (Count <= 64)
return u64{};
716#if defined(__SIZEOF_INT128__
)
717 static_assert(Count <= 128,
"lib::bitfield: width exceeds 128 bits.");
720 static_assert(Count <= 64,
"lib::bitfield: width exceeds 64 bits without __int128.");
726 template<usize Count>
727 using optimal_uint_t =
decltype(smallest_uint_for_bits<Count>());
730 struct is_extensions_pack : std::false_type {
733 template<
template <
class>
class... Ms>
734 struct is_extensions_pack<extensions<Ms...> > : std::true_type {
737 template<
class... Ts>
738 struct find_extensions {
739 using type = std::tuple<>;
742 template<
class Head,
class... Tail>
743 struct find_extensions<Head, Tail...> {
744 using type = std::conditional_t<
745 is_extensions_pack<Head>::value,
747 typename find_extensions<Tail...>::type>;
750 template<
bool HasExt,
class... Ts>
751 struct filter_extensions_dispatch {
752 using type = std::tuple<>;
755 template<
class... Ts>
756 struct filter_extensions_dispatch<
true, Ts...> {
757 using type =
typename find_extensions<Ts...>::type;
760 template<
class... Ts>
761 struct filter_extensions {
762 using type =
typename filter_extensions_dispatch<(is_extensions_pack<Ts>::value || ...), Ts...>::type;
765 template<
class FilteredTuple,
class... Rest>
766 struct filter_fields_impl;
768 template<
class... Collected>
769 struct filter_fields_impl<std::tuple<Collected...> > {
770 using type = std::tuple<Collected...>;
773 template<
bool IsExt,
class Acc,
class Head,
class... Tail>
774 struct filter_fields_step;
776 template<
class... Collected,
class Head,
class... Tail>
777 struct filter_fields_step<
true, std::tuple<Collected...>, Head, Tail...> {
778 using type =
typename filter_fields_impl<std::tuple<Collected...>, Tail...>::type;
781 template<
class... Collected,
class Head,
class... Tail>
782 struct filter_fields_step<
false, std::tuple<Collected...>, Head, Tail...> {
783 using type =
typename filter_fields_impl<std::tuple<Collected..., Head>, Tail...>::type;
786 template<
class Acc,
class Head,
class... Tail>
787 struct filter_fields_impl<Acc, Head, Tail...> {
788 using type =
typename filter_fields_step<is_extensions_pack<Head>::value, Acc, Head, Tail...>::type;
791 template<
bool AllFields,
class... Ts>
792 struct filter_fields_dispatch {
793 using type = std::tuple<Ts...>;
796 template<
class... Ts>
797 struct filter_fields_dispatch<
false, Ts...> {
798 using type =
typename filter_fields_impl<std::tuple<>, Ts...>::type;
801 template<
class... Ts>
802 struct filter_fields {
803 using type =
typename filter_fields_dispatch<(!is_extensions_pack<Ts>::value && ...), Ts...>::type;
806 template<
class ExtensionsHolder,
class Derived>
807 struct mixin_invoker;
809 template<
template <
class>
class... Ms,
class Derived>
810 struct mixin_invoker<extensions<Ms...>, Derived> :
public Ms<Derived>... {
813 template<
class Derived>
814 struct mixin_invoker<std::tuple<>, Derived> {
819 concept record_value_type =
requires
821 typename std::remove_cvref_t<T>::storage_type;
822 typename std::remove_cvref_t<T>::numbering;
823 typename std::remove_cvref_t<T>::ref_type;
824 typename std::remove_cvref_t<T>::mmio_ref_type;
825 } && !
requires {
typename std::remove_cvref_t<T>::value_record; };
828 [[nodiscard]]
consteval auto field_type_fits(usize width)
noexcept ->
bool {
829 using C = std::remove_cvref_t<T>;
830 if constexpr (std::is_void_v<C>)
return true;
831 else if constexpr (record_value_type<C>)
return width ==
sizeof(
typename C::storage_type) * 8;
832 else if constexpr (std::is_same_v<C,
bool>)
return width == 1;
833 else if constexpr (std::is_enum_v<C>)
return width <=
sizeof(std::underlying_type_t<C>) * 8;
834 else if constexpr (std::is_integral_v<C>)
return width <=
sizeof(C) * 8;
835 else if constexpr (
requires { C::width; })
return width <= C::width;
843 static_assert(Bits >= 1 && Bits <= 128,
844 "lib::bitfield: exact_uint width must be within [1, 128].");
845 using value_type = detail::optimal_uint_t<Bits>;
846 static constexpr usize width = Bits;
848 constexpr exact_uint()
noexcept =
default;
850 constexpr exact_uint(value_type v)
noexcept : value(v) {
853 [[nodiscard]]
constexpr operator value_type()
const noexcept {
return value; }
855 [[nodiscard]]
friend constexpr auto operator==(exact_uint, exact_uint)
noexcept ->
bool =
default;
857 [[nodiscard]]
friend constexpr auto operator<=>(exact_uint, exact_uint)
noexcept =
default;
863 using uint_c = exact_uint<Bits>;
866 struct storage_traits<exact_uint<Bits> > {
867 using value_type =
typename exact_uint<Bits>::value_type;
868 static constexpr bool is_scalar =
true;
869 static constexpr usize total_bits = Bits;
870 static constexpr usize byte_size =
sizeof(value_type);
872 [[nodiscard]]
static constexpr auto bitmask(usize count)
noexcept -> value_type {
874 if constexpr (Bits ==
sizeof(value_type) * 8)
return static_cast<value_type>(~value_type{0});
875 else return static_cast<value_type>(
876 (
static_cast<value_type>(1) << (Bits - 1) << 1) -
static_cast<value_type>(1));
878 if (count == 0)
return value_type{0};
879 return static_cast<value_type>((
static_cast<value_type>(1) << (count - 1) << 1) -
static_cast<
892 template<fixed_string Name,
class Locator,
class Type =
void,
class... Attr>
894 static constexpr auto name = Name;
895 using locator = Locator;
898 using access =
typename detail::select_access<Attr...>::type;
899 using transformer =
typename detail::select_transform<Attr...>::type;
900 using endian_mode =
typename detail::select_endian<Attr...>::type;
901 using granularity =
typename detail::select_word<Attr...>::type;
903 using init_tag =
typename detail::select_init_tag<Attr...>::type;
904 using fixed_tag =
typename detail::select_fixed_tag<Attr...>::type;
906 static constexpr bool is_overlay = (detail::is_overlay_v<Attr> || ...);
907 static constexpr bool is_unaligned = (detail::is_unaligned_v<Attr> || ...);
908 static constexpr bool has_init = !std::is_same_v<init_tag, detail::none_token>;
909 static constexpr bool has_fixed = !std::is_same_v<fixed_tag, detail::none_token>;
911 static constexpr u64 init_value = detail::raw_tag_bits<init_tag>::value;
912 static constexpr u64 fixed_value = detail::raw_tag_bits<fixed_tag>::value;
917 template<fixed_string N,
class L,
class T =
void,
class... Extra>
918 using rw = field<N, L, T, attr::rw, Extra...>;
919 template<fixed_string N,
class L,
class T =
void,
class... Extra>
920 using ro = field<N, L, T, attr::ro, Extra...>;
921 template<fixed_string N,
class L,
class T =
void,
class... Extra>
922 using wo = field<N, L, T, attr::wo, Extra...>;
923 template<fixed_string N,
class L,
class T =
void,
class... Extra>
924 using w1c = field<N, L, T, attr::w1c, Extra...>;
925 template<fixed_string N,
class L,
class T =
void,
class... Extra>
926 using w1s = field<N, L, T, attr::w1s, Extra...>;
927 template<fixed_string N,
class L,
class T =
void,
class... Extra>
928 using w1t = field<N, L, T, attr::w1t, Extra...>;
929 template<fixed_string N,
class L,
class T =
void,
class... Extra>
930 using w0c = field<N, L, T, attr::w0c, Extra...>;
931 template<fixed_string N,
class L,
class T =
void,
class... Extra>
932 using rc = field<N, L, T, attr::rc, Extra...>;
933 template<fixed_string N,
class L,
class T =
void,
class... Extra>
934 using rz = field<N, L, T, attr::rz, Extra...>;
935 template<fixed_string N,
class L,
class T =
void,
class... Extra>
936 using raz = field<N, L, T, attr::raz, Extra...>;
937 template<fixed_string N,
class L,
class T =
void,
class... Extra>
938 using rao = field<N, L, T, attr::rao, Extra...>;
939 template<fixed_string N,
class L,
class T =
void,
class... Extra>
940 using wac = field<N, L, T, attr::wac, Extra...>;
941 template<fixed_string N,
class L,
class T =
void,
class... Extra>
942 using woo = field<N, L, T, attr::woo, Extra...>;
944 template<fixed_string N,
class L,
class T =
void,
class... Extra>
945 using overlay_rw = field<N, L, T, attr::rw, attr::overlay, Extra...>;
946 template<fixed_string N,
class L,
class T =
void,
class... Extra>
947 using overlay_ro = field<N, L, T, attr::ro, attr::overlay, Extra...>;
948 template<fixed_string N,
class L,
class T =
void,
class... Extra>
949 using overlay_wo = field<N, L, T, attr::wo, attr::overlay, Extra...>;
951 template<fixed_string N,
class L, usize Aln,
class... Extra>
952 using aligned_rw = field<N, L, u64, attr::rw, attr::aligned<Aln>, Extra...>;
953 template<fixed_string N,
class L, usize Aln,
class... Extra>
954 using aligned_ro = field<N, L, u64, attr::ro, attr::aligned<Aln>, Extra...>;
955 template<fixed_string N,
class L, usize Aln,
class... Extra>
956 using aligned_wo = field<N, L, u64, attr::wo, attr::aligned<Aln>, Extra...>;
961 template<usize A, usize B = A>
962 using bits = bit_slice<A, B>;
963 template<usize S, usize E>
964 using bytes = byte_span<S, E>;
968 template<
class Tuple>
969 struct fold_total_width {
970 static constexpr usize value = 0;
973 template<
class... Ss>
974 struct fold_total_width<std::tuple<Ss...>> {
975 static constexpr usize value = (Ss::width + ...);
978 template<
class Tuple>
979 struct fold_min_bit {
980 static constexpr usize value = 0;
983 template<
class... Ss>
984 struct fold_min_bit<std::tuple<Ss...> > {
985 static constexpr usize value = std::min({Ss::min_bit...});
988 template<
class Tuple>
989 struct fold_max_bit {
990 static constexpr usize value = 0;
993 template<
class... Ss>
994 struct fold_max_bit<std::tuple<Ss...> > {
995 static constexpr usize value = std::max({Ss::max_bit...});
998 template<
class Field>
999 struct record_field_view : Field {
1000 using slices =
typename record_slices<
typename Field::locator>::type;
1002 static constexpr usize total_width = fold_total_width<slices>::value;
1003 static constexpr usize min_bit = fold_min_bit<slices>::value;
1004 static constexpr usize max_bit = fold_max_bit<slices>::value;
1007 template<
class Tuple>
1008 struct map_record_fields;
1010 template<
class... Fs>
1011 struct map_record_fields<std::tuple<Fs...> > {
1012 using type = std::tuple<record_field_view<Fs>...>;
1019 constexpr bit_ref(T &storage, usize shift)
noexcept
1020 : storage_(&storage), shift_(shift) {
1023 [[nodiscard]]
constexpr operator
bool()
const noexcept {
1024 return ((*storage_ >> shift_) & T{1}) != T{0};
1027 constexpr auto operator=(
bool v)
noexcept -> bit_ref & {
1028 if (v) *storage_ =
static_cast<T>(*storage_ | (T{1} << shift_));
1029 else *storage_ =
static_cast<T>(*storage_ & ~(T{1} << shift_));
1033 constexpr auto operator=(
const bit_ref &other)
noexcept -> bit_ref & {
1034 return *
this =
static_cast<
bool>(other);
1037 constexpr auto operator&=(
bool v)
noexcept -> bit_ref & {
1038 return *
this = (
static_cast<
bool>(*
this) && v);
1041 constexpr auto operator|=(
bool v)
noexcept -> bit_ref & {
1042 return *
this = (
static_cast<
bool>(*
this) || v);
1045 constexpr auto operator^=(
bool v)
noexcept -> bit_ref & {
1046 return *
this = (
static_cast<
bool>(*
this) ^ v);
1049 [[nodiscard]]
constexpr auto operator~()
const noexcept ->
bool {
1050 return !
static_cast<
bool>(*
this);
1053 constexpr auto flip()
noexcept -> bit_ref & {
1054 *storage_ =
static_cast<T>(*storage_ ^ (T{1} << shift_));
1063 template<
class Numbering, usize TotalBits>
1064 class byte_bit_ref {
1066 constexpr byte_bit_ref(u8 *data, usize bit_idx)
noexcept
1067 : data_(data), bit_idx_(bit_idx) {
1070 [[nodiscard]]
constexpr operator
bool()
const noexcept {
1071 return detail::get_bit_from_bytes<Numbering, TotalBits>(data_, bit_idx_);
1074 constexpr auto operator=(
bool v)
noexcept -> byte_bit_ref & {
1075 detail::set_bit_in_bytes<Numbering, TotalBits>(data_, bit_idx_, v);
1079 constexpr auto operator=(
const byte_bit_ref &other)
noexcept -> byte_bit_ref & {
1080 return *
this =
static_cast<
bool>(other);
1083 constexpr auto operator&=(
bool v)
noexcept -> byte_bit_ref & {
1084 return *
this = (
static_cast<
bool>(*
this) && v);
1087 constexpr auto operator|=(
bool v)
noexcept -> byte_bit_ref & {
1088 return *
this = (
static_cast<
bool>(*
this) || v);
1091 constexpr auto operator^=(
bool v)
noexcept -> byte_bit_ref & {
1092 return *
this = (
static_cast<
bool>(*
this) ^ v);
1095 [[nodiscard]]
constexpr auto operator~()
const noexcept ->
bool {
1096 return !
static_cast<
bool>(*
this);
1099 constexpr auto flip()
noexcept -> byte_bit_ref & {
1100 return *
this = !
static_cast<
bool>(*
this);
1108 template<bit_storage T,
class Numbering = msb0_t>
1111 using storage_type = T;
1112 using numbering = Numbering;
1113 using traits = storage_traits<T>;
1114 static constexpr usize extent = traits::total_bits;
1116 constexpr bit_field()
noexcept =
default;
1118 constexpr bit_field(T v)
noexcept : value_(v) {
1121 [[nodiscard]]
constexpr auto operator[](
this auto &&self, usize bit)
noexcept {
1122 chk_assert(bit < extent);
1123 using SelfType = std::remove_reference_t<
decltype(self)>;
1124 if constexpr (std::is_rvalue_reference_v<
decltype(self)> || std::is_const_v<SelfType>) {
1125 if constexpr (traits::is_scalar) {
1126 const usize shift = Numbering::bit_shift(extent, bit);
1127 return ((self.value_ >> shift) & T{1}) != T{0};
1129 return detail::get_bit_from_bytes<Numbering, extent>(self.value_.data(), bit);
1132 if constexpr (traits::is_scalar) {
1133 const usize shift = Numbering::bit_shift(extent, bit);
1134 return bit_ref<T>(self.value_, shift);
1136 return byte_bit_ref<Numbering, extent>(self.value_.data(), bit);
1141 template<usize A, usize B,
class Transformer =
void>
1142 [[nodiscard]]
constexpr auto extract()
const noexcept {
1143 constexpr auto sw = Numbering::range_shift_width(extent, A, B);
1144 using RawT = detail::optimal_uint_t<sw.second>;
1146 if constexpr (traits::is_scalar) {
1147 raw_val =
static_cast<RawT>((value_ >> sw.first) & traits::bitmask(sw.second));
1149 raw_val = detail::extract_slice_from_bytes<RawT, Numbering, extent>(value_.data(), A, B);
1151 if constexpr (!std::is_void_v<Transformer>) {
1152 return Transformer::
template from_bits<sw.second>(raw_val);
1158 template<usize A, usize B, usize Alignment>
1159 [[nodiscard]]
constexpr auto extract_aligned()
const noexcept -> u64 {
1160 return extract<A, B, aligned_address<Alignment> >();
1163 template<usize A, usize B,
class Transformer =
void,
class ValT>
1164 constexpr auto insert(ValT &&v)
noexcept -> bit_field & {
1165 constexpr auto sw = Numbering::range_shift_width(extent, A, B);
1166 auto raw_val = [&]() {
1167 if constexpr (!std::is_void_v<Transformer>) {
1168 return Transformer::
template to_bits<sw.second>(std::forward<ValT>(v));
1170 return std::forward<ValT>(v);
1173 if constexpr (traits::is_scalar) {
1174 const T m = traits::bitmask(sw.second);
1175 value_ =
static_cast<T>((value_ &
static_cast<T>(~(m << sw.first))) |
1176 static_cast<T>((
static_cast<T>(raw_val) & m) << sw.first));
1178 detail::insert_slice_to_bytes<
decltype(raw_val), Numbering, extent>(value_.data(), A, B, raw_val);
1183 template<usize A, usize B, usize Alignment>
1184 constexpr auto insert_aligned(u64 address)
noexcept -> bit_field & {
1185 return insert<A, B, aligned_address<Alignment> >(address);
1188 [[nodiscard]]
constexpr auto test(usize bit)
const noexcept ->
bool {
return (*
this)[bit]; }
1190 constexpr auto set(usize bit,
bool v =
true)
noexcept -> bit_field & {
1195 constexpr auto clear(usize bit)
noexcept -> bit_field & {
1196 (*
this)[bit] =
false;
1200 constexpr auto flip(usize bit)
noexcept -> bit_field & {
1201 (*
this)[bit].flip();
1205 [[nodiscard]]
constexpr auto try_test(usize bit)
const noexcept
1206 -> std::expected<
bool, kernel_error> {
1207 if (bit >= extent)
return std::unexpected(kernel_error::from_generic(generic_error::out_of_range));
1208 return (*
this)[bit];
1211 constexpr auto try_set(usize bit,
bool v =
true)
noexcept
1212 -> std::expected<
void, kernel_error> {
1213 if (bit >= extent)
return std::unexpected(kernel_error::from_generic(generic_error::out_of_range));
1218 constexpr auto try_clear(usize bit)
noexcept
1219 -> std::expected<
void, kernel_error> {
1220 if (bit >= extent)
return std::unexpected(kernel_error::from_generic(generic_error::out_of_range));
1221 (*
this)[bit] =
false;
1225 constexpr auto try_flip(usize bit)
noexcept
1226 -> std::expected<
void, kernel_error> {
1227 if (bit >= extent)
return std::unexpected(kernel_error::from_generic(generic_error::out_of_range));
1228 (*
this)[bit].flip();
1232 [[nodiscard]]
constexpr auto value()
const noexcept -> T {
return value_; }
1233 [[nodiscard]]
constexpr auto raw()
noexcept -> T & {
return value_; }
1234 [[nodiscard]]
constexpr auto raw()
const noexcept ->
const T & {
return value_; }
1236 constexpr operator T()
const noexcept requires traits::is_scalar {
1240 [[nodiscard]]
friend constexpr auto operator==(
const bit_field &,
const bit_field &)
noexcept ->
bool
1243 [[nodiscard]]
friend constexpr auto operator<=>(
const bit_field &,
const bit_field &)
noexcept =
default;
1249 template<bit_storage T,
class Numbering = msb0_t>
1250 class bitfield_ref {
1252 using storage_type = T;
1253 using numbering = Numbering;
1254 using traits = storage_traits<T>;
1255 static constexpr usize extent = traits::total_bits;
1257 constexpr explicit bitfield_ref(T &storage)
noexcept : ptr_(&storage) {
1260 constexpr explicit bitfield_ref(
void *ptr)
noexcept : ptr_(
static_cast<T *>(ptr)) {
1263 constexpr explicit bitfield_ref(
const void *ptr)
noexcept : ptr_(
1264 const_cast<T *>(
static_cast<
const T *>(ptr))) {
1267 [[nodiscard]]
constexpr auto operator[](
this auto &&self, usize bit)
noexcept {
1268 chk_assert(bit < extent);
1269 using SelfType = std::remove_reference_t<
decltype(self)>;
1270 if constexpr (std::is_const_v<SelfType>) {
1271 if constexpr (traits::is_scalar) {
1272 const usize shift = Numbering::bit_shift(extent, bit);
1273 return ((*self.ptr_ >> shift) & T{1}) != T{0};
1275 return detail::get_bit_from_bytes<Numbering, extent>(self.ptr_->data(), bit);
1278 if constexpr (traits::is_scalar) {
1279 const usize shift = Numbering::bit_shift(extent, bit);
1280 return bit_ref<T>(*self.ptr_, shift);
1282 return byte_bit_ref<Numbering, extent>(self.ptr_->data(), bit);
1287 template<usize A, usize B,
class Transformer =
void>
1288 [[nodiscard]]
constexpr auto extract()
const noexcept {
1289 constexpr auto sw = Numbering::range_shift_width(extent, A, B);
1290 using RawT = detail::optimal_uint_t<sw.second>;
1292 if constexpr (traits::is_scalar) {
1293 raw_val =
static_cast<RawT>((*ptr_ >> sw.first) & traits::bitmask(sw.second));
1295 raw_val = detail::extract_slice_from_bytes<RawT, Numbering, extent>(ptr_->data(), A, B);
1297 if constexpr (!std::is_void_v<Transformer>) {
1298 return Transformer::
template from_bits<sw.second>(raw_val);
1304 template<usize A, usize B, usize Alignment>
1305 [[nodiscard]]
constexpr auto extract_aligned()
const noexcept -> u64 {
1306 return extract<A, B, aligned_address<Alignment> >();
1309 template<usize A, usize B,
class Transformer =
void,
class ValT>
1310 constexpr auto insert(ValT &&v)
noexcept -> bitfield_ref & {
1311 constexpr auto sw = Numbering::range_shift_width(extent, A, B);
1312 auto raw_val = [&]() {
1313 if constexpr (!std::is_void_v<Transformer>) {
1314 return Transformer::
template to_bits<sw.second>(std::forward<ValT>(v));
1316 return std::forward<ValT>(v);
1319 if constexpr (traits::is_scalar) {
1320 const T m = traits::bitmask(sw.second);
1321 *ptr_ =
static_cast<T>((*ptr_ &
static_cast<T>(~(m << sw.first))) |
1322 static_cast<T>((
static_cast<T>(raw_val) & m) << sw.first));
1324 detail::insert_slice_to_bytes<
decltype(raw_val), Numbering, extent>(ptr_->data(), A, B, raw_val);
1329 template<usize A, usize B, usize Alignment>
1330 constexpr auto insert_aligned(u64 address)
noexcept -> bitfield_ref & {
1331 return insert<A, B, aligned_address<Alignment> >(address);
1334 [[nodiscard]]
constexpr auto test(usize bit)
const noexcept ->
bool {
return (*
this)[bit]; }
1336 constexpr auto set(usize bit,
bool v =
true)
noexcept -> bitfield_ref & {
1341 constexpr auto clear(usize bit)
noexcept -> bitfield_ref & {
1342 (*
this)[bit] =
false;
1346 [[nodiscard]]
constexpr auto try_test(usize bit)
const noexcept
1347 -> std::expected<
bool, kernel_error> {
1348 if (bit >= extent)
return std::unexpected(kernel_error::from_generic(generic_error::out_of_range));
1349 return (*
this)[bit];
1352 constexpr auto try_set(usize bit,
bool v =
true)
noexcept
1353 -> std::expected<
void, kernel_error> {
1354 if (bit >= extent)
return std::unexpected(kernel_error::from_generic(generic_error::out_of_range));
1359 constexpr auto try_clear(usize bit)
noexcept
1360 -> std::expected<
void, kernel_error> {
1361 if (bit >= extent)
return std::unexpected(kernel_error::from_generic(generic_error::out_of_range));
1362 (*
this)[bit] =
false;
1366 [[nodiscard]]
constexpr auto value()
const noexcept -> T {
return *ptr_; }
1367 [[nodiscard]]
constexpr auto raw()
noexcept -> T & {
return *ptr_; }
1368 [[nodiscard]]
constexpr auto raw()
const noexcept ->
const T & {
return *ptr_; }
1370 constexpr operator T()
const noexcept requires traits::is_scalar {
1379 template<
class Tuple>
1380 struct record_layout_info;
1382 template<
class... Fs>
1383 struct record_layout_info<std::tuple<Fs...> > {
1384 static constexpr usize count =
sizeof...(Fs);
1385 static constexpr std::array<std::string_view, count> names = {
1386 std::string_view(Fs::name.c_str(), Fs::name.size())...
1389 template<fixed_string Name>
1390 static consteval auto index_of()
noexcept -> usize {
1391 constexpr std::string_view target(Name.c_str(), Name.size());
1392 for (usize i = 0; i < count; ++i) {
1393 if (names[i] == target)
return i;
1395 return static_cast<usize>(-1);
1399 template<
class Field,
class Storage>
1400 struct resolve_field_type_impl {
1401 static_assert(detail::field_type_fits<
typename Field::type>(Field::total_width),
1402 "lib::bitfield: declared field Type is narrower than its bit span (use void for auto-width).")
1404 using raw_optimal = optimal_uint_t<Field::total_width>;
1405 using type = std::conditional_t<
1406 !std::is_void_v<
typename Field::type>,
1407 typename Field::type,
1408 std::conditional_t<storage_traits<Storage>::is_scalar, Storage, raw_optimal>
1412 template<
class Field,
class Storage>
1413 using resolve_field_type =
typename resolve_field_type_impl<Field, Storage>::type;
1415 template<
class Field,
class Numbering,
class BitFieldT>
1416 [[nodiscard]]
constexpr auto read_field(BitFieldT &bits)
noexcept {
1417 static_assert(!std::is_same_v<
typename Field::access, attr::wo>,
1418 "lib::bitfield: Illegal read from Write-Only (WO) field.");
1420 constexpr bool is_const_context = std::is_const_v<std::remove_reference_t<BitFieldT> >;
1421 if constexpr (std::is_same_v<
typename Field::access, attr::rc>) {
1422 static_assert(!is_const_context,
1423 "lib::bitfield: Cannot perform Read-to-Clear (RC) on a const record instance!");
1426 using TargetType = resolve_field_type<Field,
typename std::remove_cvref_t<BitFieldT>::storage_type>;
1428 if constexpr (Field::has_fixed) {
1429 return static_cast<TargetType>(Field::fixed_value);
1430 }
else if constexpr (std::is_same_v<
typename Field::access, attr::rz> ||
1431 std::is_same_v<
typename Field::access, attr::raz>) {
1432 return TargetType{0};
1433 }
else if constexpr (std::is_same_v<
typename Field::access, attr::rao>) {
1434 return static_cast<TargetType>(storage_traits<TargetType>::bitmask(Field::total_width));
1436 using Slices =
typename Field::slices;
1437 constexpr usize slice_count = std::tuple_size_v<Slices>;
1439 auto extract_val = [&]() {
1440 if constexpr (slice_count == 1) {
1441 using S = pack_indexing_element_t<0, Slices>;
1442 return bits.
template extract<S::start, S::end>();
1444 using AccumT = optimal_uint_t<Field::total_width>;
1446 auto extract_all = [&]<usize... Is>(std::index_sequence<Is...>) {
1447 auto process_slice = [&]<
class Sl>(Sl) {
1448 constexpr usize width = Sl::width;
1449 constexpr usize accum_bits =
sizeof(AccumT) * 8;
1450 auto part = bits.
template extract<Sl::start, Sl::end>();
1451 if constexpr (width < accum_bits) {
1452 accum =
static_cast<AccumT>(accum << width);
1454 accum =
static_cast<AccumT>(accum |
static_cast<AccumT>(part));
1456 (process_slice(pack_indexing_element_t<Is, Slices>{}), ...);
1458 extract_all(std::make_index_sequence<slice_count>{});
1463 auto raw_val = extract_val();
1465 if constexpr (std::is_same_v<
typename Field::access, attr::rc>) {
1466 if constexpr (slice_count == 1) {
1467 using S = pack_indexing_element_t<0, Slices>;
1468 bits.
template insert<S::start, S::end>(0);
1470 auto clear_all = [&]<usize... Is>(std::index_sequence<Is...>) {
1471 ((bits.
template insert<pack_indexing_element_t<Is, Slices>::start,
1472 pack_indexing_element_t<Is, Slices>::end>(0)), ...);
1474 clear_all(std::make_index_sequence<slice_count>{});
1478 if constexpr (!std::is_void_v<
typename Field::transformer>) {
1479 return Field::transformer::
template from_bits<Field::total_width>(raw_val);
1480 }
else if constexpr (record_value_type<TargetType>) {
1481 using RecStorage =
typename TargetType::storage_type;
1482 return TargetType{
static_cast<RecStorage>(raw_val)};
1483 }
else if constexpr (std::signed_integral<TargetType>) {
1484 return detail::sign_extend<TargetType, Field::total_width>(raw_val);
1486 return static_cast<TargetType>(raw_val);
1492 template<
class Field,
class Numbering,
class BitFieldT,
class RawT>
1493 constexpr auto commit_raw(BitFieldT &bits, RawT raw_val)
noexcept ->
void {
1494 using Slices =
typename Field::slices;
1495 constexpr usize slice_count = std::tuple_size_v<Slices>;
1496 if constexpr (slice_count == 1) {
1497 using S = pack_indexing_element_t<0, Slices>;
1498 bits.
template insert<S::start, S::end>(raw_val);
1500 auto write_all = [&]<usize... Is>(std::index_sequence<Is...>) {
1501 auto process_slice_rev = [&]<
class Sl>(Sl) {
1502 constexpr usize width = Sl::width;
1503 constexpr usize raw_bits =
sizeof(RawT) * 8;
1504 RawT part_mask = safe_bitmask<RawT>(width, raw_bits);
1505 RawT part_val =
static_cast<RawT>(raw_val & part_mask);
1506 bits.
template insert<Sl::start, Sl::end>(part_val);
1507 if constexpr (width < raw_bits) {
1508 raw_val =
static_cast<RawT>(raw_val >> width);
1511 (process_slice_rev(pack_indexing_element_t<slice_count - 1 - Is, Slices>{}), ...);
1513 write_all(std::make_index_sequence<slice_count>{});
1517 template<
class Field,
class Numbering,
class BitFieldT,
class ValT>
1518 constexpr auto write_field(BitFieldT &bits, ValT &&val)
noexcept ->
void {
1519 static_assert(!std::is_same_v<
typename Field::access, attr::ro>,
1520 "lib::bitfield: Illegal write to Read-Only (RO) field.");
1522 if constexpr (std::is_same_v<
typename Field::access, attr::rz>) {
1523 chk_assert(val == 0 &&
"lib::bitfield: Write non-zero to Reserved-Zero (RZ) field!");
1526 using Slices =
typename Field::slices;
1527 constexpr usize slice_count = std::tuple_size_v<Slices>;
1529 auto get_raw_input = [&]() {
1530 if constexpr (!std::is_void_v<
typename Field::transformer>) {
1531 return Field::transformer::
template to_bits<Field::total_width>(val);
1532 }
else if constexpr (std::is_enum_v<std::remove_cvref_t<ValT> >) {
1533 return static_cast<std::underlying_type_t<std::remove_cvref_t<ValT> >>(val);
1534 }
else if constexpr (
requires { val.underlying(); }) {
1535 return val.underlying();
1541 using RawT = optimal_uint_t<Field::total_width>;
1542 auto raw_val =
static_cast<RawT>(get_raw_input());
1544 if constexpr (Field::has_fixed) {
1545 chk_assert(raw_val ==
static_cast<RawT>(Field::fixed_value) &&
1546 "lib::bitfield: write to architectural constant field must equal its fixed value!");
1547 }
else if constexpr (std::is_same_v<
typename Field::access, attr::wac>) {
1549 }
else if constexpr (std::is_same_v<
typename Field::access, attr::woo>) {
1550 static_assert(slice_count == 1,
1551 "lib::bitfield: Write-Once-Only (woo) requires a contiguous field.");
1552 using S = pack_indexing_element_t<0, Slices>;
1553 auto cur = bits.
template extract<S::start, S::end>();
1556 "lib::bitfield: Write-Once-Only (woo) field already latched (must be written exactly once).");
1557 }
else if constexpr (std::is_same_v<
typename Field::access, attr::w1c>) {
1558 static_assert(slice_count == 1,
1559 "lib::bitfield: w1c access requires a contiguous (single-slice) field.");
1560 using S = pack_indexing_element_t<0, Slices>;
1561 auto cur = bits.
template extract<S::start, S::end>();
1562 raw_val =
static_cast<RawT>(cur & ~raw_val);
1563 }
else if constexpr (std::is_same_v<
typename Field::access, attr::w0c>) {
1564 static_assert(slice_count == 1,
1565 "lib::bitfield: w0c access requires a contiguous (single-slice) field.");
1566 using S = pack_indexing_element_t<0, Slices>;
1567 auto cur = bits.
template extract<S::start, S::end>();
1568 raw_val =
static_cast<RawT>(cur & raw_val);
1569 }
else if constexpr (std::is_same_v<
typename Field::access, attr::w1s>) {
1570 static_assert(slice_count == 1,
1571 "lib::bitfield: w1s access requires a contiguous (single-slice) field.");
1572 using S = pack_indexing_element_t<0, Slices>;
1573 auto cur = bits.
template extract<S::start, S::end>();
1574 raw_val =
static_cast<RawT>(cur | raw_val);
1575 }
else if constexpr (std::is_same_v<
typename Field::access, attr::w1t>) {
1576 static_assert(slice_count == 1,
1577 "lib::bitfield: w1t access requires a contiguous (single-slice) field.");
1578 using S = pack_indexing_element_t<0, Slices>;
1579 auto cur = bits.
template extract<S::start, S::end>();
1580 raw_val =
static_cast<RawT>(cur ^ raw_val);
1583 commit_raw<Field, Numbering>(bits, raw_val);
1586 template<
class Field,
class Numbering,
class BitFieldT>
1587 constexpr auto apply_default(BitFieldT &bits)
noexcept ->
void {
1588 if constexpr (Field::has_fixed || Field::has_init) {
1589 constexpr u64 c = Field::has_fixed ? Field::fixed_value : Field::init_value;
1590 using RawT = optimal_uint_t<Field::total_width>;
1591 commit_raw<Field, Numbering>(bits,
static_cast<RawT>(c));
1595 template<
class... Fs>
1596 consteval auto verify_field_overlaps() ->
bool {
1597 constexpr usize N =
sizeof...(Fs);
1598 if constexpr (N <= 1) {
1601 struct field_bounds {
1604 bool is_overlay =
false;
1606 const std::array<field_bounds, N> bounds = {
1607 field_bounds{.min_bit = Fs::min_bit, .max_bit = Fs::max_bit, .is_overlay = Fs::is_overlay}...
1609 for (usize i = 0; i < N; ++i) {
1610 if (bounds[i].is_overlay)
continue;
1611 for (usize j = i + 1; j < N; ++j) {
1612 if (bounds[j].is_overlay)
continue;
1613 const bool overlap = !(
1614 bounds[i].max_bit < bounds[j].min_bit || bounds[j].max_bit < bounds[i].min_bit);
1617 "lib::record: Accidental bit overlap between fields! Use overlay_rw for intentional aliases.");
1625 template<bit_storage T,
class Numbering,
class... FieldOrExts>
1628 template<rwonce::Word T,
class Numbering,
class... FieldOrExts>
1629 class mmio_record_ref;
1631 template<bit_storage T,
class Numbering,
class... FieldOrExts>
1632 class record :
public detail::mixin_invoker<
1633 typename detail::filter_extensions<FieldOrExts...>::type,
1634 record<T, Numbering, FieldOrExts...>
1637 using RawFieldTuple =
typename detail::filter_fields<FieldOrExts...>::type;
1638 using FieldTuple =
typename detail::map_record_fields<RawFieldTuple>::type;
1639 using Layout = detail::record_layout_info<FieldTuple>;
1641 template<
class Tuple>
1642 struct overlap_checker;
1644 template<
class... Fs>
1645 struct overlap_checker<std::tuple<Fs...> > {
1646 static constexpr bool valid = detail::verify_field_overlaps<Fs...>();
1649 static_assert(overlap_checker<FieldTuple>::valid);
1650 static_assert(std::tuple_size_v<RawFieldTuple> == std::tuple_size_v<FieldTuple>);
1653 using storage_type = T;
1654 using numbering = Numbering;
1655 static constexpr usize field_count = std::tuple_size_v<FieldTuple>;
1656 static constexpr usize extent = storage_traits<T>::total_bits;
1658 constexpr record()
noexcept : bits_{} {
1662 constexpr record(T v)
noexcept : bits_(v) {
1665 template<
class... Bindings>
1666 requires ((
requires { Bindings::name; }) && ...)
1667 constexpr explicit record(Bindings &&... bindings)
noexcept : bits_{} {
1668 (
this->
template set<Bindings::name>(std::forward<
decltype(bindings.value)>(bindings.value)), ...);
1671 template<fixed_string... FieldNames,
class... Values>
1672 [[nodiscard]]
static constexpr auto make(Values &&... vals)
noexcept -> record {
1673 static_assert(
sizeof...(FieldNames) ==
sizeof...(Values),
1674 "Number of field names must match the number of values!");
1676 (s.
template set<FieldNames>(std::forward<Values>(vals)), ...);
1680 template<fixed_string Path>
1681 [[nodiscard]]
constexpr auto get()
const noexcept {
1682 constexpr usize exact_idx = Layout::
template index_of<Path>();
1683 if constexpr (exact_idx !=
static_cast<usize>(-1)) {
1684 using F = pack_indexing_element_t<exact_idx, FieldTuple>;
1685 return detail::read_field<F, Numbering>(bits_);
1687 using Split = detail::path_split<Path>;
1688 constexpr auto head_name = Split::head;
1689 constexpr usize idx = Layout::
template index_of<head_name>();
1690 static_assert(idx !=
static_cast<usize>(-1),
"record: unknown field identifier");
1691 using F = pack_indexing_element_t<idx, FieldTuple>;
1692 auto val = detail::read_field<F, Numbering>(bits_);
1693 if constexpr (Split::has_tail) {
1694 return val.
template get<Split::tail>();
1701 template<fixed_string Path>
1702 [[nodiscard]]
constexpr auto get()
noexcept {
1703 constexpr usize exact_idx = Layout::
template index_of<Path>();
1704 if constexpr (exact_idx !=
static_cast<usize>(-1)) {
1705 using F = pack_indexing_element_t<exact_idx, FieldTuple>;
1706 return detail::read_field<F, Numbering>(bits_);
1708 using Split = detail::path_split<Path>;
1709 constexpr auto head_name = Split::head;
1710 constexpr usize idx = Layout::
template index_of<head_name>();
1711 static_assert(idx !=
static_cast<usize>(-1),
"record: unknown field identifier");
1712 using F = pack_indexing_element_t<idx, FieldTuple>;
1713 auto val = detail::read_field<F, Numbering>(bits_);
1714 if constexpr (Split::has_tail) {
1715 return val.
template get<Split::tail>();
1723 [[nodiscard]]
constexpr auto get()
const noexcept {
1724 static_assert(I < field_count,
"record: field index out of bounds");
1725 using F = pack_indexing_element_t<I, FieldTuple>;
1726 return detail::read_field<F, Numbering>(bits_);
1730 [[nodiscard]]
constexpr auto get()
noexcept {
1731 static_assert(I < field_count,
"record: field index out of bounds");
1732 using F = pack_indexing_element_t<I, FieldTuple>;
1733 return detail::read_field<F, Numbering>(bits_);
1736 template<fixed_string Path,
class ValueType>
1737 constexpr decltype(
auto) set(
this auto &&self, ValueType &&v)
noexcept {
1738 constexpr usize exact_idx = Layout::
template index_of<Path>();
1739 if constexpr (exact_idx !=
static_cast<usize>(-1)) {
1740 using F = pack_indexing_element_t<exact_idx, FieldTuple>;
1741 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1742 return std::forward<
decltype(self)>(self);
1744 using Split = detail::path_split<Path>;
1745 constexpr auto head_name = Split::head;
1746 constexpr usize idx = Layout::
template index_of<head_name>();
1747 static_assert(idx !=
static_cast<usize>(-1),
"record: unknown field identifier");
1748 using F = pack_indexing_element_t<idx, FieldTuple>;
1749 if constexpr (Split::has_tail) {
1750 auto sub = detail::read_field<F, Numbering>(self.bits_);
1751 sub.
template set<Split::tail>(std::forward<ValueType>(v));
1752 detail::write_field<F, Numbering>(self.bits_, sub);
1754 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1756 return std::forward<
decltype(self)>(self);
1760 template<usize I,
class ValueType>
1761 constexpr decltype(
auto) set(
this auto &&self, ValueType &&v)
noexcept {
1762 static_assert(I < field_count,
"record: field index out of bounds");
1763 using F = pack_indexing_element_t<I, FieldTuple>;
1764 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1765 return std::forward<
decltype(self)>(self);
1768 template<fixed_string Path>
1769 [[nodiscard]]
constexpr auto operator[](field_tag<Path>)
const noexcept {
1773 template<fixed_string Path>
1774 [[nodiscard]]
constexpr auto operator[](field_tag<Path>)
noexcept {
1778 [[nodiscard]]
constexpr auto underlying()
const noexcept -> T {
return bits_.value(); }
1780 [[nodiscard]]
constexpr operator T()
const noexcept requires storage_traits<T>::is_scalar {
1781 return bits_.value();
1784 [[nodiscard]]
constexpr auto bits()
noexcept -> bit_field<T, Numbering> & {
return bits_; }
1785 [[nodiscard]]
constexpr auto bits()
const noexcept ->
const bit_field<T, Numbering> & {
return bits_; }
1787 using ref_type = record_ref<T, Numbering, FieldOrExts...>;
1788 using mmio_ref_type = mmio_record_ref<T, Numbering, FieldOrExts...>;
1790 [[nodiscard]]
constexpr auto ref()
noexcept -> ref_type {
return ref_type(bits_.raw()); }
1791 [[nodiscard]]
constexpr auto mmio_ref()
noexcept -> mmio_ref_type {
return mmio_ref_type(bits_.raw()); }
1793 [[nodiscard]]
friend constexpr auto operator==(
const record &,
const record &)
noexcept ->
bool =
default;
1795 [[nodiscard]]
friend constexpr auto operator<=>(
const record &,
const record &)
noexcept =
default;
1798 constexpr void apply_defaults()
noexcept {
1799 apply_defaults_impl(
static_cast<FieldTuple *>(
nullptr));
1802 template<
class... Fs>
1803 constexpr void apply_defaults_impl(std::tuple<Fs...> *)
noexcept {
1804 (detail::apply_default<Fs, Numbering>(bits_), ...);
1807 bit_field<T, Numbering> bits_{};
1810 template<bit_storage T,
class Numbering,
class... FieldOrExts>
1811 class record_ref :
public detail::mixin_invoker<
1812 typename detail::filter_extensions<FieldOrExts...>::type,
1813 record_ref<T, Numbering, FieldOrExts...>
1816 using RawFieldTuple =
typename detail::filter_fields<FieldOrExts...>::type;
1817 using FieldTuple =
typename detail::map_record_fields<RawFieldTuple>::type;
1818 using Layout = detail::record_layout_info<FieldTuple>;
1821 using storage_type = T;
1822 using numbering = Numbering;
1823 static constexpr usize field_count = std::tuple_size_v<FieldTuple>;
1824 static constexpr usize extent = storage_traits<T>::total_bits;
1826 constexpr explicit record_ref(T &storage)
noexcept : bits_(storage) {
1829 constexpr explicit record_ref(
void *ptr)
noexcept : bits_(*
static_cast<T *>(ptr)) {
1832 constexpr explicit record_ref(
const void *ptr)
noexcept : bits_(
1833 *
const_cast<T *>(
static_cast<
const T *>(ptr))) {
1836 template<fixed_string Path>
1837 [[nodiscard]]
constexpr auto get()
const noexcept {
1838 constexpr usize exact_idx = Layout::
template index_of<Path>();
1839 if constexpr (exact_idx !=
static_cast<usize>(-1)) {
1840 using F = pack_indexing_element_t<exact_idx, FieldTuple>;
1841 return detail::read_field<F, Numbering>(bits_);
1843 using Split = detail::path_split<Path>;
1844 constexpr auto head_name = Split::head;
1845 constexpr usize idx = Layout::
template index_of<head_name>();
1846 static_assert(idx !=
static_cast<usize>(-1),
"record_ref: unknown field identifier");
1847 using F = pack_indexing_element_t<idx, FieldTuple>;
1848 auto val = detail::read_field<F, Numbering>(bits_);
1849 if constexpr (Split::has_tail) {
1850 return val.
template get<Split::tail>();
1857 template<fixed_string Path>
1858 [[nodiscard]]
constexpr auto get()
noexcept {
1859 constexpr usize exact_idx = Layout::
template index_of<Path>();
1860 if constexpr (exact_idx !=
static_cast<usize>(-1)) {
1861 using F = pack_indexing_element_t<exact_idx, FieldTuple>;
1862 return detail::read_field<F, Numbering>(bits_);
1864 using Split = detail::path_split<Path>;
1865 constexpr auto head_name = Split::head;
1866 constexpr usize idx = Layout::
template index_of<head_name>();
1867 static_assert(idx !=
static_cast<usize>(-1),
"record_ref: unknown field identifier");
1868 using F = pack_indexing_element_t<idx, FieldTuple>;
1869 auto val = detail::read_field<F, Numbering>(bits_);
1870 if constexpr (Split::has_tail) {
1871 return val.
template get<Split::tail>();
1879 [[nodiscard]]
constexpr auto get()
const noexcept {
1880 static_assert(I < field_count,
"record_ref: field index out of bounds");
1881 using F = pack_indexing_element_t<I, FieldTuple>;
1882 return detail::read_field<F, Numbering>(bits_);
1886 [[nodiscard]]
constexpr auto get()
noexcept {
1887 static_assert(I < field_count,
"record_ref: field index out of bounds");
1888 using F = pack_indexing_element_t<I, FieldTuple>;
1889 return detail::read_field<F, Numbering>(bits_);
1892 template<fixed_string Path,
class ValueType>
1893 constexpr decltype(
auto) set(
this auto &&self, ValueType &&v)
noexcept {
1894 constexpr usize exact_idx = Layout::
template index_of<Path>();
1895 if constexpr (exact_idx !=
static_cast<usize>(-1)) {
1896 using F = pack_indexing_element_t<exact_idx, FieldTuple>;
1897 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1898 return std::forward<
decltype(self)>(self);
1900 using Split = detail::path_split<Path>;
1901 constexpr auto head_name = Split::head;
1902 constexpr usize idx = Layout::
template index_of<head_name>();
1903 static_assert(idx !=
static_cast<usize>(-1),
"record_ref: unknown field identifier");
1904 using F = pack_indexing_element_t<idx, FieldTuple>;
1905 if constexpr (Split::has_tail) {
1906 auto sub = detail::read_field<F, Numbering>(self.bits_);
1907 sub.
template set<Split::tail>(std::forward<ValueType>(v));
1908 detail::write_field<F, Numbering>(self.bits_, sub);
1910 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1912 return std::forward<
decltype(self)>(self);
1916 template<usize I,
class ValueType>
1917 constexpr decltype(
auto) set(
this auto &&self, ValueType &&v)
noexcept {
1918 static_assert(I < field_count,
"record_ref: field index out of bounds");
1919 using F = pack_indexing_element_t<I, FieldTuple>;
1920 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1921 return std::forward<
decltype(self)>(self);
1924 template<fixed_string Path>
1925 [[nodiscard]]
constexpr auto operator[](field_tag<Path>)
const noexcept {
1929 template<fixed_string Path>
1930 [[nodiscard]]
constexpr auto operator[](field_tag<Path>)
noexcept {
1934 [[nodiscard]]
constexpr auto underlying()
const noexcept -> T {
return bits_.value(); }
1936 [[nodiscard]]
constexpr operator T()
const noexcept requires storage_traits<T>::is_scalar {
1937 return bits_.value();
1940 [[nodiscard]]
constexpr auto bits()
noexcept -> bitfield_ref<T, Numbering> & {
return bits_; }
1941 [[nodiscard]]
constexpr auto bits()
const noexcept ->
const bitfield_ref<T, Numbering> & {
return bits_; }
1944 bitfield_ref<T, Numbering> bits_;
1947 template<rwonce::Word T,
class Numbering,
class... FieldOrExts>
1948 class mmio_record_ref :
public detail::mixin_invoker<
1949 typename detail::filter_extensions<FieldOrExts...>::type,
1950 mmio_record_ref<T, Numbering, FieldOrExts...>
1953 using storage_type = T;
1954 using numbering = Numbering;
1955 using value_record = record<T, Numbering, FieldOrExts...>;
1956 static constexpr usize extent = storage_traits<T>::total_bits;
1958 constexpr explicit mmio_record_ref(T &storage)
noexcept
1959 : ptr_(std::addressof(storage)) {
1962 constexpr explicit mmio_record_ref(
volatile void *ptr)
noexcept
1963 : ptr_(
static_cast<T *>(
const_cast<
void *>(ptr))) {
1966 [[nodiscard]]
constexpr auto load()
const noexcept -> value_record {
1967 return value_record(rwonce::read_once(*ptr_));
1970 constexpr auto store(value_record val)
const noexcept ->
void {
1971 rwonce::write_once(*ptr_, val.underlying());
1974 constexpr auto store(T val)
const noexcept ->
void {
1975 rwonce::write_once(*ptr_, val);
1978 template<fixed_string Path>
1979 [[nodiscard]]
constexpr auto get()
const noexcept {
1980 return load().
template get<Path>();
1983 template<fixed_string Path,
class ValueType>
1984 constexpr auto set(ValueType &&v)
const noexcept ->
void {
1986 r.
template set<Path>(std::forward<ValueType>(v));
1990 template<fixed_string Path>
1991 [[nodiscard]]
constexpr auto operator[](field_tag<Path>)
const noexcept {
1995 [[nodiscard]]
constexpr operator value_record()
const noexcept {
1999 [[nodiscard]]
auto atomic_load(
2000 std::memory_order order = std::memory_order_seq_cst)
const noexcept -> value_record {
2001 static_assert(std::is_trivially_copyable_v<T>,
2002 "lib::bitfield: Storage must be trivially copyable for atomic operations.");
2003 std::atomic_ref<T> aref(*ptr_);
2004 return value_record(aref.load(order));
2007 auto atomic_store(value_record val,
2008 std::memory_order order = std::memory_order_seq_cst)
const noexcept ->
void {
2009 static_assert(std::is_trivially_copyable_v<T>,
2010 "lib::bitfield: Storage must be trivially copyable for atomic operations.");
2011 std::atomic_ref<T> aref(*ptr_);
2012 aref.store(val.underlying(), order);
2015 auto atomic_compare_exchange(value_record &expected, value_record desired,
2016 std::memory_order success = std::memory_order_seq_cst,
2017 std::memory_order failure = std::memory_order_seq_cst)
const noexcept ->
bool {
2018 static_assert(std::is_trivially_copyable_v<T>,
2019 "lib::bitfield: Storage must be trivially copyable for atomic operations.");
2020 std::atomic_ref<T> aref(*ptr_);
2021 T exp_raw = expected.underlying();
2022 bool ok = aref.compare_exchange_strong(exp_raw, desired.underlying(), success, failure);
2023 if (!ok) expected = value_record(exp_raw);
2027 template<fixed_string Path,
class ValT>
2028 auto atomic_set(ValT &&val, std::memory_order order = std::memory_order_seq_cst)
const noexcept ->
void {
2029 static_assert(std::is_trivially_copyable_v<T>,
2030 "lib::bitfield: Storage must be trivially copyable for atomic operations.");
2031 std::atomic_ref<T> aref(*ptr_);
2032 T exp_raw = aref.load(std::memory_order_relaxed);
2033 value_record desired_rec{};
2035 desired_rec = value_record(exp_raw);
2036 desired_rec.
template set<Path>(std::forward<ValT>(val));
2037 }
while (!aref.compare_exchange_weak(exp_raw, desired_rec.underlying(), order,
2038 std::memory_order_relaxed));
2041 template<fixed_string Path>
2042 auto atomic_bit_set(std::memory_order order = std::memory_order_seq_cst)
const noexcept ->
void {
2043 atomic_set<Path>(
true, order);
2046 template<fixed_string Path>
2047 auto atomic_bit_clear(std::memory_order order = std::memory_order_seq_cst)
const noexcept ->
void {
2048 atomic_set<Path>(
false, order);
2051 template<fixed_string Path>
2052 auto atomic_bit_toggle(std::memory_order order = std::memory_order_seq_cst)
const noexcept ->
void {
2053 static_assert(std::is_trivially_copyable_v<T>,
2054 "lib::bitfield: Storage must be trivially copyable for atomic operations.");
2055 std::atomic_ref<T> aref(*ptr_);
2056 T exp_raw = aref.load(std::memory_order_relaxed);
2057 value_record desired_rec{};
2059 desired_rec = value_record(exp_raw);
2060 bool cur = desired_rec.
template get<Path>();
2061 desired_rec.
template set<Path>(!cur);
2062 }
while (!aref.compare_exchange_weak(exp_raw, desired_rec.underlying(), order,
2063 std::memory_order_relaxed));
2070 template<
class RecordT>
2071 struct record_traits;
2073 template<bit_storage T,
class Numbering,
class... Fs>
2074 struct record_traits<record<T, Numbering, Fs...> > {
2075 using storage_type = T;
2076 using numbering = Numbering;
2077 using value_type = record<T, Numbering, Fs...>;
2078 using ref_type = record_ref<T, Numbering, Fs...>;
2079 using mmio_ref_type = mmio_record_ref<T, Numbering, Fs...>;
2080 static constexpr usize total_bits = storage_traits<T>::total_bits;
2083 template<bit_storage T,
class Numbering,
class... Fs>
2084 struct record_traits<record_ref<T, Numbering, Fs...> > {
2085 using storage_type = T;
2086 using numbering = Numbering;
2087 using value_type = record<T, Numbering, Fs...>;
2088 using ref_type = record_ref<T, Numbering, Fs...>;
2089 using mmio_ref_type = mmio_record_ref<T, Numbering, Fs...>;
2090 static constexpr usize total_bits = storage_traits<T>::total_bits;
2093 template<rwonce::Word T,
class Numbering,
class... Fs>
2094 struct record_traits<mmio_record_ref<T, Numbering, Fs...> > {
2095 using storage_type = T;
2096 using numbering = Numbering;
2097 using value_type = record<T, Numbering, Fs...>;
2098 using ref_type = record_ref<T, Numbering, Fs...>;
2099 using mmio_ref_type = mmio_record_ref<T, Numbering, Fs...>;
2100 static constexpr usize total_bits = storage_traits<T>::total_bits;
2103 template<
class RecordT>
2104 using record_as_ref_t =
typename record_traits<RecordT>::ref_type;
2106 template<
class RecordT>
2107 using record_as_mmio_ref_t =
typename record_traits<RecordT>::mmio_ref_type;
2109 using bf8 = bit_field<u8, msb0_t>;
2110 using bf16 = bit_field<u16, msb0_t>;
2111 using bf32 = bit_field<u32, msb0_t>;
2112 using bf64 = bit_field<u64, msb0_t>;
2113#if defined(__SIZEOF_INT128__
)
2114 using bf128 = bit_field<u128, msb0_t>;
2115 using bf128_ref = bitfield_ref<u128, msb0_t>;
2117 using bf128 = bit_field<std::array<u8, 16>, msb0_t>;
2118 using bf128_ref = bitfield_ref<std::array<u8, 16>, msb0_t>;
2120 using bf8_ref = bitfield_ref<u8, msb0_t>;
2121 using bf16_ref = bitfield_ref<u16, msb0_t>;
2122 using bf32_ref = bitfield_ref<u32, msb0_t>;
2123 using bf64_ref = bitfield_ref<u64, msb0_t>;
2125 using lf8 = bit_field<u8, lsb0_t>;
2126 using lf16 = bit_field<u16, lsb0_t>;
2127 using lf32 = bit_field<u32, lsb0_t>;
2128 using lf64 = bit_field<u64, lsb0_t>;
2129#if defined(__SIZEOF_INT128__
)
2130 using lf128 = bit_field<u128, lsb0_t>;
2131 using lf128_ref = bitfield_ref<u128, lsb0_t>;
2133 using lf128 = bit_field<std::array<u8, 16>, msb0_t>;
2134 using lf128_ref = bitfield_ref<std::array<u8, 16>, lsb0_t>;
2136 using lf8_ref = bitfield_ref<u8, lsb0_t>;
2137 using lf16_ref = bitfield_ref<u16, lsb0_t>;
2138 using lf32_ref = bitfield_ref<u32, lsb0_t>;
2139 using lf64_ref = bitfield_ref<u64, lsb0_t>;
2142 static_assert(at_byte<0>::byte_offset == 0);
2143 static_assert(at_halfword<3>::byte_offset == 6);
2144 static_assert(at_word<2>::byte_offset == 8);
2145 static_assert(at_dword<2>::byte_offset == 16);
2146 static_assert(at_qword<1>::byte_offset == 16);
2147 static_assert(at_dword<2>::at_byte<3>::byte_offset == 19);
2148 static_assert(at_dword<2>::at_byte<3>::at_bits<8, 15>::byte_offset == 19);
2149 static_assert(at_dword<2>::at_byte<3>::at_bits<8, 15>::width == 8);
2150 static_assert(at_dword<2>::at_byte<3>::at_bits<8, 15>::end_byte == 21);
2151 static_assert(slice<1, 0, 23>::end_byte == 4);
2152 static_assert(slice<1, 0, 23>::width == 24);
2153 static_assert(exact_uint<96>::width == 96);
2154 static_assert(storage_traits<exact_uint<96> >::total_bits == 96);
2155 static_assert(
sizeof(exact_uint<24>::value_type) == 4);
2156 static_assert(storage_traits<exact_uint<24> >::bitmask(24) == 0x00FF'FFFFu);
2157 static_assert(std::is_same_v<field<
"a", bit_slice<0, 0>,
void>::access, attr::rw>);
2158 static_assert(std::is_same_v<field<
"a", bit_slice<0, 0>,
void, attr::ro>::access, attr::ro>);
2159 static_assert(field<
"a", bit_slice<0, 0>,
void, attr::fixed<1> >::fixed_value == 1);
2160 static_assert(field<
"a", bit_slice<0, 3>,
void, attr::init<5> >::init_value == 5);
2164template<
class T,
class Numbering,
class... FieldOrExts>
2165struct std::tuple_size<lib::record<T, Numbering, FieldOrExts...> >
2166 : std::integral_constant<usize, lib::record<T, Numbering, FieldOrExts...>::field_count> {
2169template<usize I,
class T,
class Numbering,
class... FieldOrExts>
2170struct std::tuple_element<I, lib::record<T, Numbering, FieldOrExts...> > {
2172 using RawFieldTuple =
typename lib::detail::filter_fields<FieldOrExts...>::type;
2173 using FieldTuple =
typename lib::detail::map_record_fields<RawFieldTuple>::type;
2174 using Field = lib::pack_indexing_element_t<I, FieldTuple>;
2177 using type = lib::detail::resolve_field_type<Field, T>;