ZXFoundation™ 26h2
Loading...
Searching...
No Matches
bitfield.cxxm
Go to the documentation of this file.
1/// SPDX-License-Identifier: Apache-2.0
2/// @file bitfield.cxxm
3/// @brief C++26 unified field descriptor
4
5export module lib.bitfield;
6import lib.fixed_string;
7import lib.rwonce;
8import lib.pack_indexing;
9import lib.error;
10import zxfoundation.base.types;
11import zxfoundation.sys.syschk.barebones;
12import lib.stl;
13
14#define __zxmacro_want_assert
15#include macro_extensions
16
17export {
18
19using lib::literals::operator""_f;
20
21namespace lib {
22
23 /// @brief Compile-time bit slice descriptor (record domain).
24 /// @tparam Start First bit boundary
25 /// @tparam End Second bit boundary
26 template<usize Start, usize End = Start>
27 struct bit_slice {
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;
33 };
34
35 template<usize Start, usize End>
36 using bit_span = bit_slice<Start, End>;
37
38 /// @brief MSB0 bit numbering policy (Bit 0 = Most Significant Bit)
39 struct msb0_t {
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;
43 }
44
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};
53 }
54 };
55
56 /// @brief LSB0 bit numbering policy (Bit 0 = Least Significant Bit)
57 struct lsb0_t {
58 [[nodiscard]] static constexpr auto bit_shift(usize, usize n) noexcept -> usize {
59 return n;
60 }
61
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};
70 }
71 };
72
73 inline constexpr msb0_t msb0{};
74 inline constexpr lsb0_t lsb0{};
75
76 /// @brief Bit-window locator within a byte-addressed unit (structure domain).
77 template<usize ByteOffset, usize StartBit, usize EndBit, class Numbering = msb0_t>
78 struct slice {
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;
86 /// @brief Exclusive end byte offset physically touched by this window.
87 static constexpr usize end_byte = ByteOffset + (max_bit >> 3) + 1;
88 };
89
90 /// @brief Byte-offset anchor for hierarchical register addressing.
91 template<usize Offset>
92 struct addr {
93 static constexpr usize byte_offset = Offset;
94
95 template<usize K>
96 using at_byte = addr<Offset + K>;
97 template<usize K>
98 using at_halfword = addr<Offset + K * 2>;
99 template<usize K>
100 using at_word = addr<Offset + K * 4>;
101 template<usize K>
102 using at_dword = addr<Offset + K * 8>;
103 template<usize K>
104 using at_qword = addr<Offset + K * 16>;
105
106 template<usize A, usize B, class Numbering = msb0_t>
107 using at_bits = slice<Offset, A, B, Numbering>;
108
109 template<usize B, class Numbering = msb0_t>
110 using at_bit = slice<Offset, B, B, Numbering>;
111 };
112
113 /// @name Absolute byte anchors.
114 /// @{
115 template<usize N>
116 using at_byte = addr<N>;
117 template<usize N>
118 using at_halfword = addr<N * 2>;
119 template<usize N>
120 using at_word = addr<N * 4>;
121 template<usize N>
122 using at_dword = addr<N * 8>;
123 template<usize N>
124 using at_qword = addr<N * 16>;
125 /// @}
126
127 /// @brief Byte-range locator (structure domain, whole bytes).
128 template<usize Start, usize End>
129 struct byte_span {
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;
135 };
136
137 /// @brief Strided-array locator (structure domain).
138 template<usize Count, usize Stride>
139 struct bank {
140 static constexpr usize count = Count;
141 static constexpr usize stride = Stride;
142 };
143
144 /// @brief Automatic-layout locator (structure domain).
145 struct auto_layout {
146 };
147
148 namespace endian {
149 struct big {
150 };
151
152 struct little {
153 };
154
155 struct native {
156 };
157 }
158
159 /// @brief Field attribute tags (the closed modifier set for @c field).
160 namespace attr {
161 // -- access policy --
162 struct rw {
163 }; ///< Read-Write
164 struct ro {
165 }; ///< Read-Only
166 struct wo {
167 }; ///< Write-Only
168 struct w1c {
169 }; ///< Write-1-to-Clear
170 struct w1s {
171 }; ///< Write-1-to-Set
172 struct w1t {
173 }; ///< Write-1-to-Toggle
174 struct w0c {
175 }; ///< Write-0-to-Clear
176 struct rc {
177 }; ///< Read-to-Clear
178 struct rz {
179 }; ///< Reserved-Zero
180 struct raz {
181 }; ///< Read-As-Zero
182 struct rao {
183 }; ///< Read-As-One
184 struct wac {
185 }; ///< Write-Any-to-Clear
186 struct woo {
187 }; ///< Write-Once-Only
188
189 template<usize A>
190 struct aligned {
191 }; ///< address = raw << log2(A)
192 template<usize S>
193 struct shifted_by {
194 }; ///< value = raw << S
195 struct inverted {
196 }; ///< active-low
197
198 struct endian_big {
199 };
200
201 struct endian_little {
202 };
203
204 struct unaligned {
205 };
206
207 template<class W>
208 struct word {
209 }; ///< bus granularity (u8/u16/u32/u64)
210 struct overlay {
211 }; ///< intentional alias
212
213 template<auto V>
214 struct init {
215 static constexpr auto value = V;
216 }; ///< default/reset value
217 template<auto V>
218 struct fixed {
219 static constexpr auto value = V;
220 }; ///< architectural constant (always V)
221 }
222
223 /// @brief Backward-compatible access-tag namespace (aliases of @c attr).
224 namespace access {
225 using rw = attr::rw;
226 using ro = attr::ro;
227 using wo = attr::wo;
228 using w1c = attr::w1c;
229 using w1s = attr::w1s;
230 using w1t = attr::w1t;
231 using w0c = attr::w0c;
232 using rc = attr::rc;
233 using rz = attr::rz;
234 using raz = attr::raz;
235 using rao = attr::rao;
236 using wac = attr::wac;
237 using woo = attr::woo;
238 }
239
240 /// @brief Value transformer contract (carries caller bit width).
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);
245
246 template<usize = 0>
247 [[nodiscard]] static constexpr auto from_bits(u64 raw_bits) noexcept -> u64 {
248 return raw_bits << shift;
249 }
250
251 template<usize = 0>
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;
255 }
256 };
257
258 template<usize Shift>
259 struct shifted {
260 template<usize = 0>
261 [[nodiscard]] static constexpr auto from_bits(auto raw_bits) noexcept {
262 return raw_bits << Shift;
263 }
264
265 template<usize = 0>
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!");
270 return val >> Shift;
271 }
272 };
273
274 struct inverted {
275 private:
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));
280 }
281
282 public:
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>());
288 }
289
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>());
295 }
296 };
297
298 template<template <class> class... Mixins>
299 struct extensions {
300 };
301
302 template<class T>
303 struct is_byte_array : std::false_type {
304 };
305
306 template<usize N>
307 struct is_byte_array<std::array<u8, N> > : std::true_type {
308 };
309
310 /// @brief Exact-width unsigned integer type (compile-time bit width 1..128).
311 template<usize Bits>
312 struct exact_uint;
313
314 template<class T>
315 struct is_exact_uint : std::false_type {
316 };
317
318 template<usize Bits>
319 struct is_exact_uint<exact_uint<Bits> > : std::true_type {
320 };
321
322 template<class T>
323 inline constexpr bool is_exact_uint_v = is_exact_uint<std::remove_cvref_t<T> >::value;
324
325 namespace detail {
326 template<class T>
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>
332#endif
333 ;
334
335 template<fixed_string FullPath>
336 struct path_split {
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;
340 }
341 return static_cast<usize>(-1);
342 }();
343
344 static constexpr bool has_tail = (split_pos != static_cast<usize>(-1));
345
346 static constexpr auto head = []() consteval {
347 if constexpr (!has_tail) return FullPath;
348 else {
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);
353 }
354 }();
355
356 static constexpr auto tail = []() consteval {
357 if constexpr (!has_tail) return fixed_string("");
358 else {
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);
364 }
365 }();
366 };
367
368 template<class T, class... Ts>
369 inline constexpr bool is_one_of_v = (std::is_same_v<T, Ts> || ...);
370
371 template<class T>
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>;
375
376 template<class T>
377 struct is_transform_tag : std::false_type {
378 };
379
380 template<usize A>
381 struct is_transform_tag<attr::aligned<A> > : std::true_type {
382 };
383
384 template<usize S>
385 struct is_transform_tag<attr::shifted_by<S> > : std::true_type {
386 };
387
388 template<>
389 struct is_transform_tag<attr::inverted> : std::true_type {
390 };
391
392 template<class T>
393 inline constexpr bool is_transform_v = is_transform_tag<T>::value;
394
395 template<class T>
396 struct transform_of {
397 using type = void;
398 };
399
400 template<usize A>
401 struct transform_of<attr::aligned<A> > {
402 using type = aligned_address<A>;
403 };
404
405 template<usize S>
406 struct transform_of<attr::shifted_by<S> > {
407 using type = shifted<S>;
408 };
409
410 template<>
411 struct transform_of<attr::inverted> {
412 using type = inverted;
413 };
414
415 template<class T>
416 inline constexpr bool is_endian_v = is_one_of_v<T, attr::endian_big, attr::endian_little>;
417
418 template<class T>
419 struct is_word_tag : std::false_type {
420 };
421
422 template<class W>
423 struct is_word_tag<attr::word<W> > : std::true_type {
424 };
425
426 template<class T>
427 inline constexpr bool is_word_v = is_word_tag<T>::value;
428
429 template<class T>
430 struct word_type_of {
431 using type = void;
432 };
433
434 template<class W>
435 struct word_type_of<attr::word<W> > {
436 using type = W;
437 };
438
439 template<class T>
440 struct is_init_tag : std::false_type {
441 };
442
443 template<auto V>
444 struct is_init_tag<attr::init<V> > : std::true_type {
445 };
446
447 template<class T>
448 inline constexpr bool is_init_v = is_init_tag<T>::value;
449
450 template<class T>
451 struct is_fixed_tag : std::false_type {
452 };
453
454 template<auto V>
455 struct is_fixed_tag<attr::fixed<V> > : std::true_type {
456 };
457
458 template<class T>
459 inline constexpr bool is_fixed_v = is_fixed_tag<T>::value;
460
461 template<class T>
462 inline constexpr bool is_overlay_v = std::is_same_v<T, attr::overlay>;
463 template<class T>
464 inline constexpr bool is_unaligned_v = std::is_same_v<T, attr::unaligned>;
465
466 struct none_token {
467 };
468
469 template<template <class> class Pred, class... Ts>
470 struct first_of {
471 using type = none_token;
472 };
473
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>;
477 };
478
479 template<class... Attrs>
480 struct select_access {
481 using type = attr::rw;
482 };
483
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>;
487 };
488
489 template<class H, bool = is_transform_v<H> >
490 struct as_transform {
491 using type = void;
492 };
493
494 template<class H>
495 struct as_transform<H, true> {
496 using type = typename transform_of<H>::type;
497 };
498
499 template<class...>
500 struct select_transform {
501 using type = void;
502 };
503
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>;
510 };
511
512 template<class... Attrs>
513 struct select_endian {
514 using type = endian::native;
515 };
516
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>,
521 endian::big,
522 std::conditional_t<
523 std::is_same_v<H, attr::endian_little>,
524 endian::little,
525 typename select_endian<T...>::type> >;
526 };
527
528 template<class... Attrs>
529 struct select_word {
530 using type = void;
531 };
532
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
536 ...>::type>;
537 };
538
539 template<class... Attrs>
540 struct select_init_tag {
541 using type = none_token;
542 };
543
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>;
547 };
548
549 template<class... Attrs>
550 struct select_fixed_tag {
551 using type = none_token;
552 };
553
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>;
557 };
558
559 template<auto V>
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);
565 }
566
567 template<class Tag>
568 struct raw_tag_bits {
569 static constexpr u64 value = 0;
570 };
571
572 template<auto V>
573 struct raw_tag_bits<attr::init<V> > {
574 static constexpr u64 value = to_raw_bits<V>();
575 };
576
577 template<auto V>
578 struct raw_tag_bits<attr::fixed<V> > {
579 static constexpr u64 value = to_raw_bits<V>();
580 };
581
582 template<class L>
583 struct record_slices {
584 using type = std::tuple<>;
585 };
586
587 template<usize A, usize B>
588 struct record_slices<bit_slice<A, B> > {
589 using type = std::tuple<bit_slice<A, B> >;
590 };
591
592 template<class... Ss>
593 struct record_slices<std::tuple<Ss...> > {
594 using type = std::tuple<Ss...>;
595 };
596 } // namespace detail
597
598 template<class T>
599 concept bit_storage =
600 detail::unsigned_word<T> ||
601 is_byte_array<std::remove_cvref_t<T> >::value ||
602 is_exact_uint_v<T>;
603
604 template<class T>
605 struct storage_traits;
606
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);
612
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));
617 }
618 };
619
620 template<usize N>
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;
625
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;
630 }
631 };
632
633 namespace detail {
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));
639 }
640
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));
647 } else {
648 constexpr usize shift = in_bits - Width;
649 return static_cast<TargetType>(static_cast<std::make_signed_t<InT>>(val << shift) >> shift);
650 }
651 }
652
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;
659 }
660
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);
669 }
670
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);
674 OutT result = 0;
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));
681 }
682 } else {
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));
686 }
687 }
688 return result;
689 }
690
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);
700 }
701 } else {
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);
705 }
706 }
707 }
708
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{};
715 else {
716#if defined(__SIZEOF_INT128__)
717 static_assert(Count <= 128, "lib::bitfield: width exceeds 128 bits.");
718 return u128{};
719#else
720 static_assert(Count <= 64, "lib::bitfield: width exceeds 64 bits without __int128.");
721 return u64{};
722#endif
723 }
724 }
725
726 template<usize Count>
727 using optimal_uint_t = decltype(smallest_uint_for_bits<Count>());
728
729 template<class T>
730 struct is_extensions_pack : std::false_type {
731 };
732
733 template<template <class> class... Ms>
734 struct is_extensions_pack<extensions<Ms...> > : std::true_type {
735 };
736
737 template<class... Ts>
738 struct find_extensions {
739 using type = std::tuple<>;
740 };
741
742 template<class Head, class... Tail>
743 struct find_extensions<Head, Tail...> {
744 using type = std::conditional_t<
745 is_extensions_pack<Head>::value,
746 Head,
747 typename find_extensions<Tail...>::type>;
748 };
749
750 template<bool HasExt, class... Ts>
751 struct filter_extensions_dispatch {
752 using type = std::tuple<>;
753 };
754
755 template<class... Ts>
756 struct filter_extensions_dispatch<true, Ts...> {
757 using type = typename find_extensions<Ts...>::type;
758 };
759
760 template<class... Ts>
761 struct filter_extensions {
762 using type = typename filter_extensions_dispatch<(is_extensions_pack<Ts>::value || ...), Ts...>::type;
763 };
764
765 template<class FilteredTuple, class... Rest>
766 struct filter_fields_impl;
767
768 template<class... Collected>
769 struct filter_fields_impl<std::tuple<Collected...> > {
770 using type = std::tuple<Collected...>;
771 };
772
773 template<bool IsExt, class Acc, class Head, class... Tail>
774 struct filter_fields_step;
775
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;
779 };
780
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;
784 };
785
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;
789 };
790
791 template<bool AllFields, class... Ts>
792 struct filter_fields_dispatch {
793 using type = std::tuple<Ts...>;
794 };
795
796 template<class... Ts>
797 struct filter_fields_dispatch<false, Ts...> {
798 using type = typename filter_fields_impl<std::tuple<>, Ts...>::type;
799 };
800
801 template<class... Ts>
802 struct filter_fields {
803 using type = typename filter_fields_dispatch<(!is_extensions_pack<Ts>::value && ...), Ts...>::type;
804 };
805
806 template<class ExtensionsHolder, class Derived>
807 struct mixin_invoker;
808
809 template<template <class> class... Ms, class Derived>
810 struct mixin_invoker<extensions<Ms...>, Derived> : public Ms<Derived>... {
811 };
812
813 template<class Derived>
814 struct mixin_invoker<std::tuple<>, Derived> {
815 };
816
817 /// @brief Duck-typed detection of a bitfield record *value* type.
818 template<class T>
819 concept record_value_type = requires
820 {
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; };
826
827 template<class T>
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;
836 else return true;
837 }
838 } // namespace detail
839
840 /// @brief Exact-width unsigned integer (compile-time bit width 1..128).
841 template<usize Bits>
842 struct exact_uint {
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;
847
848 constexpr exact_uint() noexcept = default;
849
850 constexpr exact_uint(value_type v) noexcept : value(v) {
851 }
852
853 [[nodiscard]] constexpr operator value_type() const noexcept { return value; }
854
855 [[nodiscard]] friend constexpr auto operator==(exact_uint, exact_uint) noexcept -> bool = default;
856
857 [[nodiscard]] friend constexpr auto operator<=>(exact_uint, exact_uint) noexcept = default;
858
859 value_type value{};
860 };
861
862 template<usize Bits>
863 using uint_c = exact_uint<Bits>;
864
865 template<usize 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);
871
872 [[nodiscard]] static constexpr auto bitmask(usize count) noexcept -> value_type {
873 if (count >= Bits) {
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));
877 }
878 if (count == 0) return value_type{0};
879 return static_cast<value_type>((static_cast<value_type>(1) << (count - 1) << 1) - static_cast<
880 value_type>(1));
881 }
882 };
883
884 /// @brief Unified field descriptor
885 /// @tparam Name Structural compile-time field name.
886 /// @tparam Locator Position descriptor: @c bit_slice for records; @c slice,
887 /// @c byte_span, @c bank, @c auto_layout, or a @c std::tuple of
888 /// any of these (disjoint) for structures.
889 /// @tparam Type Value type (void = inferred from storage/width).
890 /// @tparam Attr Modifier tags: access policy, value transform, endianness,
891 /// alignment, bus granularity, overlay, init<>, fixed<>.
892 template<fixed_string Name, class Locator, class Type = void, class... Attr>
893 struct field {
894 static constexpr auto name = Name;
895 using locator = Locator;
896 using type = Type;
897
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;
902
903 using init_tag = typename detail::select_init_tag<Attr...>::type;
904 using fixed_tag = typename detail::select_fixed_tag<Attr...>::type;
905
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>;
910
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;
913 };
914
915 /// @name Prefab field aliases (generic over locator; variadic extra attrs).
916 /// @{
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...>;
943
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...>;
950
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...>;
957 /// @}
958
959 /// @name Locator shorthands.
960 /// @{
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>;
965 /// @}
966
967 namespace detail {
968 template<class Tuple>
969 struct fold_total_width {
970 static constexpr usize value = 0;
971 };
972
973 template<class... Ss>
974 struct fold_total_width<std::tuple<Ss...>> {
975 static constexpr usize value = (Ss::width + ...);
976 };
977
978 template<class Tuple>
979 struct fold_min_bit {
980 static constexpr usize value = 0;
981 };
982
983 template<class... Ss>
984 struct fold_min_bit<std::tuple<Ss...> > {
985 static constexpr usize value = std::min({Ss::min_bit...});
986 };
987
988 template<class Tuple>
989 struct fold_max_bit {
990 static constexpr usize value = 0;
991 };
992
993 template<class... Ss>
994 struct fold_max_bit<std::tuple<Ss...> > {
995 static constexpr usize value = std::max({Ss::max_bit...});
996 };
997
998 template<class Field>
999 struct record_field_view : Field {
1000 using slices = typename record_slices<typename Field::locator>::type;
1001
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;
1005 };
1006
1007 template<class Tuple>
1008 struct map_record_fields;
1009
1010 template<class... Fs>
1011 struct map_record_fields<std::tuple<Fs...> > {
1012 using type = std::tuple<record_field_view<Fs>...>;
1013 };
1014 } // namespace detail
1015
1016 template<class T>
1017 class bit_ref {
1018 public:
1019 constexpr bit_ref(T &storage, usize shift) noexcept
1020 : storage_(&storage), shift_(shift) {
1021 }
1022
1023 [[nodiscard]] constexpr operator bool() const noexcept {
1024 return ((*storage_ >> shift_) & T{1}) != T{0};
1025 }
1026
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_));
1030 return *this;
1031 }
1032
1033 constexpr auto operator=(const bit_ref &other) noexcept -> bit_ref & {
1034 return *this = static_cast<bool>(other);
1035 }
1036
1037 constexpr auto operator&=(bool v) noexcept -> bit_ref & {
1038 return *this = (static_cast<bool>(*this) && v);
1039 }
1040
1041 constexpr auto operator|=(bool v) noexcept -> bit_ref & {
1042 return *this = (static_cast<bool>(*this) || v);
1043 }
1044
1045 constexpr auto operator^=(bool v) noexcept -> bit_ref & {
1046 return *this = (static_cast<bool>(*this) ^ v);
1047 }
1048
1049 [[nodiscard]] constexpr auto operator~() const noexcept -> bool {
1050 return !static_cast<bool>(*this);
1051 }
1052
1053 constexpr auto flip() noexcept -> bit_ref & {
1054 *storage_ = static_cast<T>(*storage_ ^ (T{1} << shift_));
1055 return *this;
1056 }
1057
1058 private:
1059 T *storage_;
1060 usize shift_;
1061 };
1062
1063 template<class Numbering, usize TotalBits>
1064 class byte_bit_ref {
1065 public:
1066 constexpr byte_bit_ref(u8 *data, usize bit_idx) noexcept
1067 : data_(data), bit_idx_(bit_idx) {
1068 }
1069
1070 [[nodiscard]] constexpr operator bool() const noexcept {
1071 return detail::get_bit_from_bytes<Numbering, TotalBits>(data_, bit_idx_);
1072 }
1073
1074 constexpr auto operator=(bool v) noexcept -> byte_bit_ref & {
1075 detail::set_bit_in_bytes<Numbering, TotalBits>(data_, bit_idx_, v);
1076 return *this;
1077 }
1078
1079 constexpr auto operator=(const byte_bit_ref &other) noexcept -> byte_bit_ref & {
1080 return *this = static_cast<bool>(other);
1081 }
1082
1083 constexpr auto operator&=(bool v) noexcept -> byte_bit_ref & {
1084 return *this = (static_cast<bool>(*this) && v);
1085 }
1086
1087 constexpr auto operator|=(bool v) noexcept -> byte_bit_ref & {
1088 return *this = (static_cast<bool>(*this) || v);
1089 }
1090
1091 constexpr auto operator^=(bool v) noexcept -> byte_bit_ref & {
1092 return *this = (static_cast<bool>(*this) ^ v);
1093 }
1094
1095 [[nodiscard]] constexpr auto operator~() const noexcept -> bool {
1096 return !static_cast<bool>(*this);
1097 }
1098
1099 constexpr auto flip() noexcept -> byte_bit_ref & {
1100 return *this = !static_cast<bool>(*this);
1101 }
1102
1103 private:
1104 u8 *data_;
1105 usize bit_idx_;
1106 };
1107
1108 template<bit_storage T, class Numbering = msb0_t>
1109 class bit_field {
1110 public:
1111 using storage_type = T;
1112 using numbering = Numbering;
1113 using traits = storage_traits<T>;
1114 static constexpr usize extent = traits::total_bits;
1115
1116 constexpr bit_field() noexcept = default;
1117
1118 constexpr bit_field(T v) noexcept : value_(v) {
1119 }
1120
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};
1128 } else {
1129 return detail::get_bit_from_bytes<Numbering, extent>(self.value_.data(), bit);
1130 }
1131 } else {
1132 if constexpr (traits::is_scalar) {
1133 const usize shift = Numbering::bit_shift(extent, bit);
1134 return bit_ref<T>(self.value_, shift);
1135 } else {
1136 return byte_bit_ref<Numbering, extent>(self.value_.data(), bit);
1137 }
1138 }
1139 }
1140
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>;
1145 RawT raw_val{};
1146 if constexpr (traits::is_scalar) {
1147 raw_val = static_cast<RawT>((value_ >> sw.first) & traits::bitmask(sw.second));
1148 } else {
1149 raw_val = detail::extract_slice_from_bytes<RawT, Numbering, extent>(value_.data(), A, B);
1150 }
1151 if constexpr (!std::is_void_v<Transformer>) {
1152 return Transformer::template from_bits<sw.second>(raw_val);
1153 } else {
1154 return raw_val;
1155 }
1156 }
1157
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> >();
1161 }
1162
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));
1169 } else {
1170 return std::forward<ValT>(v);
1171 }
1172 }();
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));
1177 } else {
1178 detail::insert_slice_to_bytes<decltype(raw_val), Numbering, extent>(value_.data(), A, B, raw_val);
1179 }
1180 return *this;
1181 }
1182
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);
1186 }
1187
1188 [[nodiscard]] constexpr auto test(usize bit) const noexcept -> bool { return (*this)[bit]; }
1189
1190 constexpr auto set(usize bit, bool v = true) noexcept -> bit_field & {
1191 (*this)[bit] = v;
1192 return *this;
1193 }
1194
1195 constexpr auto clear(usize bit) noexcept -> bit_field & {
1196 (*this)[bit] = false;
1197 return *this;
1198 }
1199
1200 constexpr auto flip(usize bit) noexcept -> bit_field & {
1201 (*this)[bit].flip();
1202 return *this;
1203 }
1204
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];
1209 }
1210
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));
1214 (*this)[bit] = v;
1215 return {};
1216 }
1217
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;
1222 return {};
1223 }
1224
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();
1229 return {};
1230 }
1231
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_; }
1235
1236 constexpr operator T() const noexcept requires traits::is_scalar {
1237 return value_;
1238 }
1239
1240 [[nodiscard]] friend constexpr auto operator==(const bit_field &, const bit_field &) noexcept -> bool
1241 = default;
1242
1243 [[nodiscard]] friend constexpr auto operator<=>(const bit_field &, const bit_field &) noexcept = default;
1244
1245 private:
1246 T value_{};
1247 };
1248
1249 template<bit_storage T, class Numbering = msb0_t>
1250 class bitfield_ref {
1251 public:
1252 using storage_type = T;
1253 using numbering = Numbering;
1254 using traits = storage_traits<T>;
1255 static constexpr usize extent = traits::total_bits;
1256
1257 constexpr explicit bitfield_ref(T &storage) noexcept : ptr_(&storage) {
1258 }
1259
1260 constexpr explicit bitfield_ref(void *ptr) noexcept : ptr_(static_cast<T *>(ptr)) {
1261 }
1262
1263 constexpr explicit bitfield_ref(const void *ptr) noexcept : ptr_(
1264 const_cast<T *>(static_cast<const T *>(ptr))) {
1265 }
1266
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};
1274 } else {
1275 return detail::get_bit_from_bytes<Numbering, extent>(self.ptr_->data(), bit);
1276 }
1277 } else {
1278 if constexpr (traits::is_scalar) {
1279 const usize shift = Numbering::bit_shift(extent, bit);
1280 return bit_ref<T>(*self.ptr_, shift);
1281 } else {
1282 return byte_bit_ref<Numbering, extent>(self.ptr_->data(), bit);
1283 }
1284 }
1285 }
1286
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>;
1291 RawT raw_val{};
1292 if constexpr (traits::is_scalar) {
1293 raw_val = static_cast<RawT>((*ptr_ >> sw.first) & traits::bitmask(sw.second));
1294 } else {
1295 raw_val = detail::extract_slice_from_bytes<RawT, Numbering, extent>(ptr_->data(), A, B);
1296 }
1297 if constexpr (!std::is_void_v<Transformer>) {
1298 return Transformer::template from_bits<sw.second>(raw_val);
1299 } else {
1300 return raw_val;
1301 }
1302 }
1303
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> >();
1307 }
1308
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));
1315 } else {
1316 return std::forward<ValT>(v);
1317 }
1318 }();
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));
1323 } else {
1324 detail::insert_slice_to_bytes<decltype(raw_val), Numbering, extent>(ptr_->data(), A, B, raw_val);
1325 }
1326 return *this;
1327 }
1328
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);
1332 }
1333
1334 [[nodiscard]] constexpr auto test(usize bit) const noexcept -> bool { return (*this)[bit]; }
1335
1336 constexpr auto set(usize bit, bool v = true) noexcept -> bitfield_ref & {
1337 (*this)[bit] = v;
1338 return *this;
1339 }
1340
1341 constexpr auto clear(usize bit) noexcept -> bitfield_ref & {
1342 (*this)[bit] = false;
1343 return *this;
1344 }
1345
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];
1350 }
1351
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));
1355 (*this)[bit] = v;
1356 return {};
1357 }
1358
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;
1363 return {};
1364 }
1365
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_; }
1369
1370 constexpr operator T() const noexcept requires traits::is_scalar {
1371 return *ptr_;
1372 }
1373
1374 private:
1375 T *ptr_;
1376 };
1377
1378 namespace detail {
1379 template<class Tuple>
1380 struct record_layout_info;
1381
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())...
1387 };
1388
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;
1394 }
1395 return static_cast<usize>(-1);
1396 }
1397 };
1398
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).")
1403 ;
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>
1409 >;
1410 };
1411
1412 template<class Field, class Storage>
1413 using resolve_field_type = typename resolve_field_type_impl<Field, Storage>::type;
1414
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.");
1419
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!");
1424 }
1425
1426 using TargetType = resolve_field_type<Field, typename std::remove_cvref_t<BitFieldT>::storage_type>;
1427
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));
1435 } else {
1436 using Slices = typename Field::slices;
1437 constexpr usize slice_count = std::tuple_size_v<Slices>;
1438
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>();
1443 } else {
1444 using AccumT = optimal_uint_t<Field::total_width>;
1445 AccumT accum = 0;
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);
1453 }
1454 accum = static_cast<AccumT>(accum | static_cast<AccumT>(part));
1455 };
1456 (process_slice(pack_indexing_element_t<Is, Slices>{}), ...);
1457 };
1458 extract_all(std::make_index_sequence<slice_count>{});
1459 return accum;
1460 }
1461 };
1462
1463 auto raw_val = extract_val();
1464
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);
1469 } else {
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)), ...);
1473 };
1474 clear_all(std::make_index_sequence<slice_count>{});
1475 }
1476 }
1477
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);
1485 } else {
1486 return static_cast<TargetType>(raw_val);
1487 }
1488 }
1489 }
1490
1491 /// @brief Commit a raw value into a record field's slices (no access policy).
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);
1499 } else {
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);
1509 }
1510 };
1511 (process_slice_rev(pack_indexing_element_t<slice_count - 1 - Is, Slices>{}), ...);
1512 };
1513 write_all(std::make_index_sequence<slice_count>{});
1514 }
1515 }
1516
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.");
1521
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!");
1524 }
1525
1526 using Slices = typename Field::slices;
1527 constexpr usize slice_count = std::tuple_size_v<Slices>;
1528
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();
1536 } else {
1537 return val;
1538 }
1539 };
1540
1541 using RawT = optimal_uint_t<Field::total_width>;
1542 auto raw_val = static_cast<RawT>(get_raw_input());
1543
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>) {
1548 raw_val = RawT{0};
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>();
1554 chk_assert(
1555 cur == 0 &&
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);
1581 }
1582
1583 commit_raw<Field, Numbering>(bits, raw_val);
1584 }
1585
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));
1592 }
1593 }
1594
1595 template<class... Fs>
1596 consteval auto verify_field_overlaps() -> bool {
1597 constexpr usize N = sizeof...(Fs);
1598 if constexpr (N <= 1) {
1599 return true;
1600 } else {
1601 struct field_bounds {
1602 usize min_bit = 0;
1603 usize max_bit = 0;
1604 bool is_overlay = false;
1605 };
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}...
1608 };
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);
1615 chk_assert(
1616 !overlap &&
1617 "lib::record: Accidental bit overlap between fields! Use overlay_rw for intentional aliases.");
1618 }
1619 }
1620 return true;
1621 }
1622 }
1623 } // namespace detail
1624
1625 template<bit_storage T, class Numbering, class... FieldOrExts>
1626 class record_ref;
1627
1628 template<rwonce::Word T, class Numbering, class... FieldOrExts>
1629 class mmio_record_ref;
1630
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...>
1635 > {
1636 private:
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>;
1640
1641 template<class Tuple>
1642 struct overlap_checker;
1643
1644 template<class... Fs>
1645 struct overlap_checker<std::tuple<Fs...> > {
1646 static constexpr bool valid = detail::verify_field_overlaps<Fs...>();
1647 };
1648
1649 static_assert(overlap_checker<FieldTuple>::valid);
1650 static_assert(std::tuple_size_v<RawFieldTuple> == std::tuple_size_v<FieldTuple>);
1651
1652 public:
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;
1657
1658 constexpr record() noexcept : bits_{} {
1659 apply_defaults();
1660 }
1661
1662 constexpr record(T v) noexcept : bits_(v) {
1663 }
1664
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)), ...);
1669 }
1670
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!");
1675 record s{};
1676 (s.template set<FieldNames>(std::forward<Values>(vals)), ...);
1677 return s;
1678 }
1679
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_);
1686 } else {
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>();
1695 } else {
1696 return val;
1697 }
1698 }
1699 }
1700
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_);
1707 } else {
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>();
1716 } else {
1717 return val;
1718 }
1719 }
1720 }
1721
1722 template<usize I>
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_);
1727 }
1728
1729 template<usize I>
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_);
1734 }
1735
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);
1743 } else {
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);
1753 } else {
1754 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1755 }
1756 return std::forward<decltype(self)>(self);
1757 }
1758 }
1759
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);
1766 }
1767
1768 template<fixed_string Path>
1769 [[nodiscard]] constexpr auto operator[](field_tag<Path>) const noexcept {
1770 return get<Path>();
1771 }
1772
1773 template<fixed_string Path>
1774 [[nodiscard]] constexpr auto operator[](field_tag<Path>) noexcept {
1775 return get<Path>();
1776 }
1777
1778 [[nodiscard]] constexpr auto underlying() const noexcept -> T { return bits_.value(); }
1779
1780 [[nodiscard]] constexpr operator T() const noexcept requires storage_traits<T>::is_scalar {
1781 return bits_.value();
1782 }
1783
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_; }
1786
1787 using ref_type = record_ref<T, Numbering, FieldOrExts...>;
1788 using mmio_ref_type = mmio_record_ref<T, Numbering, FieldOrExts...>;
1789
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()); }
1792
1793 [[nodiscard]] friend constexpr auto operator==(const record &, const record &) noexcept -> bool = default;
1794
1795 [[nodiscard]] friend constexpr auto operator<=>(const record &, const record &) noexcept = default;
1796
1797 private:
1798 constexpr void apply_defaults() noexcept {
1799 apply_defaults_impl(static_cast<FieldTuple *>(nullptr));
1800 }
1801
1802 template<class... Fs>
1803 constexpr void apply_defaults_impl(std::tuple<Fs...> *) noexcept {
1804 (detail::apply_default<Fs, Numbering>(bits_), ...);
1805 }
1806
1807 bit_field<T, Numbering> bits_{};
1808 };
1809
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...>
1814 > {
1815 private:
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>;
1819
1820 public:
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;
1825
1826 constexpr explicit record_ref(T &storage) noexcept : bits_(storage) {
1827 }
1828
1829 constexpr explicit record_ref(void *ptr) noexcept : bits_(*static_cast<T *>(ptr)) {
1830 }
1831
1832 constexpr explicit record_ref(const void *ptr) noexcept : bits_(
1833 *const_cast<T *>(static_cast<const T *>(ptr))) {
1834 }
1835
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_);
1842 } else {
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>();
1851 } else {
1852 return val;
1853 }
1854 }
1855 }
1856
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_);
1863 } else {
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>();
1872 } else {
1873 return val;
1874 }
1875 }
1876 }
1877
1878 template<usize I>
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_);
1883 }
1884
1885 template<usize I>
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_);
1890 }
1891
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);
1899 } else {
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);
1909 } else {
1910 detail::write_field<F, Numbering>(self.bits_, std::forward<ValueType>(v));
1911 }
1912 return std::forward<decltype(self)>(self);
1913 }
1914 }
1915
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);
1922 }
1923
1924 template<fixed_string Path>
1925 [[nodiscard]] constexpr auto operator[](field_tag<Path>) const noexcept {
1926 return get<Path>();
1927 }
1928
1929 template<fixed_string Path>
1930 [[nodiscard]] constexpr auto operator[](field_tag<Path>) noexcept {
1931 return get<Path>();
1932 }
1933
1934 [[nodiscard]] constexpr auto underlying() const noexcept -> T { return bits_.value(); }
1935
1936 [[nodiscard]] constexpr operator T() const noexcept requires storage_traits<T>::is_scalar {
1937 return bits_.value();
1938 }
1939
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_; }
1942
1943 private:
1944 bitfield_ref<T, Numbering> bits_;
1945 };
1946
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...>
1951 > {
1952 public:
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;
1957
1958 constexpr explicit mmio_record_ref(T &storage) noexcept
1959 : ptr_(std::addressof(storage)) {
1960 }
1961
1962 constexpr explicit mmio_record_ref(volatile void *ptr) noexcept
1963 : ptr_(static_cast<T *>(const_cast<void *>(ptr))) {
1964 }
1965
1966 [[nodiscard]] constexpr auto load() const noexcept -> value_record {
1967 return value_record(rwonce::read_once(*ptr_));
1968 }
1969
1970 constexpr auto store(value_record val) const noexcept -> void {
1971 rwonce::write_once(*ptr_, val.underlying());
1972 }
1973
1974 constexpr auto store(T val) const noexcept -> void {
1975 rwonce::write_once(*ptr_, val);
1976 }
1977
1978 template<fixed_string Path>
1979 [[nodiscard]] constexpr auto get() const noexcept {
1980 return load().template get<Path>();
1981 }
1982
1983 template<fixed_string Path, class ValueType>
1984 constexpr auto set(ValueType &&v) const noexcept -> void {
1985 auto r = load();
1986 r.template set<Path>(std::forward<ValueType>(v));
1987 store(r);
1988 }
1989
1990 template<fixed_string Path>
1991 [[nodiscard]] constexpr auto operator[](field_tag<Path>) const noexcept {
1992 return get<Path>();
1993 }
1994
1995 [[nodiscard]] constexpr operator value_record() const noexcept {
1996 return load();
1997 }
1998
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));
2005 }
2006
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);
2013 }
2014
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);
2024 return ok;
2025 }
2026
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{};
2034 do {
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));
2039 }
2040
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);
2044 }
2045
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);
2049 }
2050
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{};
2058 do {
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));
2064 }
2065
2066 private:
2067 T *ptr_;
2068 };
2069
2070 template<class RecordT>
2071 struct record_traits;
2072
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;
2081 };
2082
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;
2091 };
2092
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;
2101 };
2102
2103 template<class RecordT>
2104 using record_as_ref_t = typename record_traits<RecordT>::ref_type;
2105
2106 template<class RecordT>
2107 using record_as_mmio_ref_t = typename record_traits<RecordT>::mmio_ref_type;
2108
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>;
2116#else
2117 using bf128 = bit_field<std::array<u8, 16>, msb0_t>;
2118 using bf128_ref = bitfield_ref<std::array<u8, 16>, msb0_t>;
2119#endif
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>;
2124
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>;
2132#else
2133 using lf128 = bit_field<std::array<u8, 16>, msb0_t>;
2134 using lf128_ref = bitfield_ref<std::array<u8, 16>, lsb0_t>;
2135#endif
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>;
2140
2141 // Compile-time self-checks.
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);
2161
2162} // namespace lib
2163
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> {
2167};
2168
2169template<usize I, class T, class Numbering, class... FieldOrExts>
2170struct std::tuple_element<I, lib::record<T, Numbering, FieldOrExts...> > {
2171private:
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>;
2175
2176public:
2177 using type = lib::detail::resolve_field_type<Field, T>;
2178};
2179
2180} // export