Skip to main content

bnb/
field.rs

1//! The field-value abstraction shared by every bit-packable type.
2//!
3//! [`Bits`] is the universal trait: a value that occupies a fixed number of bits
4//! inside a bitfield. `bool`, the primitive unsigned integers, the
5//! [`UInt`](crate::int::UInt) arbitrary-width integers, nested `#[bitfield]`
6//! structs, and `#[derive(BitEnum)]` enums all implement it, so they compose as
7//! fields. `u128` is the universal carrier — wide enough for any field this
8//! crate supports (the maximum width is 128 bits).
9
10/// Byte order of a bitfield's backing integer when it is serialized.
11///
12/// # Examples
13///
14/// ```
15/// use bnb::ByteOrder;
16/// assert_eq!(ByteOrder::default(), ByteOrder::Big); // network order is the default
17/// ```
18#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
19pub enum ByteOrder {
20    /// Most-significant byte first (network order). The default.
21    #[default]
22    Big,
23    /// Least-significant byte first.
24    Little,
25}
26
27/// Bit packing order within a bitfield: does the first declared field occupy the
28/// most-significant or least-significant bits of the backing integer.
29///
30/// Most network protocols (and the ASCII-art layouts in their RFCs) are
31/// most-significant-first, so [`Msb`](BitOrder::Msb) is the crate default.
32///
33/// # Examples
34///
35/// ```
36/// use bnb::BitOrder;
37/// assert_eq!(BitOrder::default(), BitOrder::Msb); // first field in the high bits
38/// ```
39#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
40pub enum BitOrder {
41    /// First field in the high bits (network / RFC-diagram order). The default.
42    #[default]
43    Msb,
44    /// First field in the low bits.
45    Lsb,
46}
47
48/// A value that occupies a fixed number of bits within a bitfield.
49///
50/// The contract: [`into_bits`](Bits::into_bits) yields the value in the low
51/// [`BITS`](Bits::BITS) bits of a `u128` (higher bits zero), and
52/// [`from_bits`](Bits::from_bits) reconstructs from the low `BITS` bits of its
53/// argument (higher bits ignored). Implementations must round-trip:
54/// `T::from_bits(x.into_bits()) == x` for every representable `x`.
55///
56/// `bool`, the primitive unsigned integers, and the [`UInt`](crate::UInt) types
57/// implement it out of the box; `#[bitfield]` and `#[derive(BitEnum)]` generate
58/// impls so those types nest as fields too.
59///
60/// ```
61/// use bnb::Bits;
62///
63/// assert_eq!(<u8 as Bits>::BITS, 8);
64/// assert_eq!(0xABu8.into_bits(), 0xAB);
65/// assert_eq!(u8::from_bits(0x1FF), 0xFF); // from_bits truncates to the width
66/// assert!(!bool::from_bits(0b10)); // only the low bit is read
67/// ```
68pub trait Bits: Copy {
69    /// The number of bits this value occupies on the wire.
70    const BITS: u32;
71
72    /// This value as the low [`BITS`](Bits::BITS) bits of a `u128`.
73    fn into_bits(self) -> u128;
74
75    /// Reconstruct from the low [`BITS`](Bits::BITS) bits of `raw`; any higher
76    /// bits are ignored.
77    fn from_bits(raw: u128) -> Self;
78}
79
80impl Bits for bool {
81    const BITS: u32 = 1;
82
83    #[inline]
84    fn into_bits(self) -> u128 {
85        self as u128
86    }
87
88    #[inline]
89    fn from_bits(raw: u128) -> Self {
90        (raw & 1) != 0
91    }
92}
93
94/// Implements [`Bits`] for the primitive unsigned integers (full width).
95macro_rules! impl_bits_for_primitive {
96    ($($t:ty),* $(,)?) => {
97        $(
98            impl Bits for $t {
99                const BITS: u32 = <$t>::BITS;
100
101                #[inline]
102                fn into_bits(self) -> u128 {
103                    self as u128
104                }
105
106                #[inline]
107                fn from_bits(raw: u128) -> Self {
108                    // `as` truncates to the low `BITS` bits, which is exactly the
109                    // masking the contract requires.
110                    raw as $t
111                }
112            }
113        )*
114    };
115}
116
117impl_bits_for_primitive!(u8, u16, u32, u64, u128);
118
119/// The seam every `#[bitfield]` struct implements — the stable interface the
120/// `#[bin]` codec builds on, independent of how the fields are accessed.
121///
122/// A bitfield is a thin wrapper over a single backing unsigned integer; this
123/// trait exposes that backing plus the declared layout metadata. The generated
124/// type also provides inherent `to_be_bytes`/`to_le_bytes`/`from_be_bytes`/
125/// `from_le_bytes` for allocation-free (de)serialization.
126///
127/// # Examples
128///
129/// ```
130/// use bnb::{bitfield, u4, Bitfield, BitOrder, ByteOrder};
131///
132/// #[bitfield(u8, bits = msb, bytes = be)]
133/// #[derive(Clone, Copy)]
134/// struct Byte { hi: u4, lo: u4 }
135///
136/// let b = Byte::new().with_hi(u4::new(0xA)).with_lo(u4::new(0xB));
137/// assert_eq!(b.to_raw(), 0xAB);              // the backing integer
138/// assert_eq!(Byte::from_raw(0xCD).hi().value(), 0xC);
139/// assert_eq!(Byte::WIDTH, 8);                // declared layout metadata
140/// assert_eq!(Byte::BYTE_ORDER, ByteOrder::Big);
141/// assert_eq!(Byte::BIT_ORDER, BitOrder::Msb);
142/// ```
143pub trait Bitfield: Bits + Sized {
144    /// The backing primitive integer (`u8`, `u16`, `u32`, `u64`, or `u128`).
145    type Backing: Copy;
146
147    /// The declared total bit width (may be less than the backing's width, the
148    /// remainder being reserved/padding).
149    const WIDTH: u32;
150
151    /// The byte order in which the backing integer is serialized.
152    const BYTE_ORDER: ByteOrder;
153
154    /// The bit packing order of the declared fields.
155    const BIT_ORDER: BitOrder;
156
157    /// The raw backing integer.
158    fn to_raw(self) -> Self::Backing;
159
160    /// Construct directly from a raw backing integer (no validation — the
161    /// dual-use escape hatch).
162    fn from_raw(raw: Self::Backing) -> Self;
163}
164
165#[cfg(test)]
166mod unit {
167    use super::*;
168
169    #[test]
170    fn bool_occupies_one_bit() {
171        assert_eq!(<bool as Bits>::BITS, 1);
172        assert_eq!(true.into_bits(), 1);
173        assert_eq!(false.into_bits(), 0);
174        assert!(bool::from_bits(1));
175        assert!(!bool::from_bits(0));
176        // Only the low bit is consulted.
177        assert!(!bool::from_bits(0b1110));
178        assert!(bool::from_bits(0b1111));
179    }
180
181    #[test]
182    fn primitives_round_trip_and_truncate() {
183        assert_eq!(<u8 as Bits>::BITS, 8);
184        assert_eq!(<u32 as Bits>::BITS, 32);
185        assert_eq!(0xABu8.into_bits(), 0xAB);
186        // from_bits truncates to the type's width.
187        assert_eq!(u8::from_bits(0x1FF), 0xFF);
188        assert_eq!(u16::from_bits(0x3_1234), 0x1234);
189        let v: u128 = 0xDEAD_BEEF_DEAD_BEEF_DEAD_BEEF_DEAD_BEEF;
190        assert_eq!(u128::from_bits(v.into_bits()), v);
191    }
192}