Skip to main content

nexus_bits/
field.rs

1//! Bit field extraction and packing.
2
3use crate::error::Overflow;
4
5/// A field within a packed integer.
6///
7/// Defines a contiguous range of bits by start position and length.
8/// Precomputes mask for efficient get/set operations.
9///
10/// # Example
11///
12/// ```
13/// use nexus_bits::BitField;
14///
15/// const EXCHANGE: BitField<u64> = BitField::<u64>::new(4, 8);  // bits 4-11
16///
17/// let packed = EXCHANGE.set(0, 42).unwrap();
18/// assert_eq!(EXCHANGE.get(packed), 42);
19/// ```
20#[allow(clippy::len_without_is_empty)]
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct BitField<T> {
23    start: u32,
24    len: u32,
25    mask: T,
26}
27
28macro_rules! impl_bitfield {
29    ($($ty:ty),*) => {
30        $(
31            impl BitField<$ty> {
32                /// Creates a new field at bit position `start` with width `len`.
33                ///
34                /// # Panics
35                ///
36                /// Panics if `len` is 0 or `start + len` exceeds type's bit width.
37                #[inline]
38                pub const fn new(start: u32, len: u32) -> Self {
39                    assert!(len > 0, "field length must be > 0");
40                    assert!(start + len <= <$ty>::BITS, "field exceeds integer bounds");
41
42                    let unshifted = if len == <$ty>::BITS {
43                        !0
44                    } else {
45                        (1 << len) - 1
46                    };
47                    let mask = unshifted << start;
48
49                    Self { start, len, mask }
50                }
51
52                /// Start bit position.
53                #[inline]
54                pub const fn start(self) -> u32 {
55                    self.start
56                }
57
58                /// Field width in bits.
59                #[inline]
60                pub const fn len(self) -> u32 {
61                    self.len
62                }
63
64                /// Mask with 1s in field position.
65                #[inline]
66                pub const fn mask(self) -> $ty {
67                    self.mask
68                }
69
70                /// Maximum value this field can hold.
71                #[inline]
72                pub const fn max_value(self) -> $ty {
73                    self.mask >> self.start
74                }
75
76                /// Extract field value from packed integer.
77                #[inline]
78                pub const fn get(self, val: $ty) -> $ty {
79                    (val & self.mask) >> self.start
80                }
81
82                /// Set field value in packed integer.
83                ///
84                /// Clears existing bits in field, then sets new value.
85                /// Returns error if `field_val` exceeds `max_value()`.
86                #[inline]
87                pub const fn set(self, val: $ty, field_val: $ty) -> Result<$ty, Overflow<$ty>> {
88                    let max = self.max_value();
89                    if field_val > max {
90                        return Err(Overflow { value: field_val, max });
91                    }
92                    Ok(self.set_unchecked(val, field_val))
93                }
94
95                /// Set field value without bounds checking.
96                ///
97                /// # Safety
98                ///
99                /// Caller must ensure `field_val <= max_value()`.
100                #[inline]
101                pub const fn set_unchecked(self, val: $ty, field_val: $ty) -> $ty {
102                    let cleared = val & !self.mask;
103                    cleared | (field_val << self.start)
104                }
105
106                /// Clear field to zero.
107                #[inline]
108                pub const fn clear(self, val: $ty) -> $ty {
109                    val & !self.mask
110                }
111            }
112        )*
113    };
114}
115
116impl_bitfield!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);