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/// # Const dispatch (`#[bitfield]` field types)
61///
62/// The accessors `#[bitfield]` generates are `const fn`. A `const fn` cannot call
63/// trait methods on stable Rust, so the generated code does not go through this
64/// trait: `bool` and the primitive unsigned integers are converted inline, and
65/// every other field type is called through a pair of inherent `const fn`s with
66/// the same contract as `into_bits`/`from_bits`. [`UInt`](crate::UInt) and every
67/// `#[bitfield]`/`#[derive(BitEnum)]`/`#[bitflags]` type provides the pair
68/// automatically (their `Bits` impls delegate to it, so the two can never
69/// disagree). **Implement a hand-written field type with
70/// [`impl_bits!`](macro@crate::impl_bits)**, which emits the trait impl and the
71/// inherent pair from one definition — never write the pair by hand. The trait
72/// alone still suffices everywhere else (the `#[bin]` codec and the bitstream
73/// derives). Field types must be named directly — a `type` alias of a primitive
74/// is not recognized by the inline conversion.
75///
76/// ```
77/// use bnb::Bits;
78///
79/// assert_eq!(<u8 as Bits>::BITS, 8);
80/// assert_eq!(0xABu8.into_bits(), 0xAB);
81/// assert_eq!(u8::from_bits(0x1FF), 0xFF); // from_bits truncates to the width
82/// assert!(!bool::from_bits(0b10)); // only the low bit is read
83/// ```
84pub trait Bits: Copy {
85    /// The number of bits this value occupies on the wire.
86    const BITS: u32;
87
88    /// This value as the low [`BITS`](Bits::BITS) bits of a `u128`.
89    fn into_bits(self) -> u128;
90
91    /// Reconstruct from the low [`BITS`](Bits::BITS) bits of `raw`; any higher
92    /// bits are ignored.
93    fn from_bits(raw: u128) -> Self;
94}
95
96impl Bits for bool {
97    const BITS: u32 = 1;
98
99    #[inline]
100    fn into_bits(self) -> u128 {
101        self as u128
102    }
103
104    #[inline]
105    fn from_bits(raw: u128) -> Self {
106        (raw & 1) != 0
107    }
108}
109
110/// Implements [`Bits`] for the primitive unsigned integers (full width).
111macro_rules! impl_bits_for_primitive {
112    ($($t:ty),* $(,)?) => {
113        $(
114            impl Bits for $t {
115                const BITS: u32 = <$t>::BITS;
116
117                #[inline]
118                fn into_bits(self) -> u128 {
119                    self as u128
120                }
121
122                #[inline]
123                fn from_bits(raw: u128) -> Self {
124                    // `as` truncates to the low `BITS` bits, which is exactly the
125                    // masking the contract requires.
126                    raw as $t
127                }
128            }
129        )*
130    };
131}
132
133impl_bits_for_primitive!(u8, u16, u32, u64, u128);
134
135/// Implements [`Bits`] for a hand-written field type from one pair of `const fn`
136/// conversion bodies — the supported way to make a custom type usable as a
137/// `#[bitfield]` field.
138///
139/// The accessors `#[bitfield]` generates are `const fn`, and a `const fn` cannot
140/// call trait methods on stable Rust — so alongside its [`Bits`] impl, a field
141/// type needs the same conversions reachable as inherent `const fn`s. This macro
142/// emits **both from one definition**: the trait impl delegates to the inherent
143/// pair, so the two can never disagree, and the pair's naming stays an
144/// implementation detail of the crate.
145///
146/// The bodies must satisfy the [`Bits`] contract: `into_bits` yields the value in
147/// the low `BITS` bits of a `u128`, `from_bits` reconstructs from the low `BITS`
148/// bits (higher bits ignored), and the two round-trip.
149///
150/// ```
151/// use bnb::{bitfield, u7};
152///
153/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
154/// struct Percent(u7);
155///
156/// bnb::impl_bits! {
157///     impl Bits for Percent {
158///         const BITS: u32 = 7;
159///         const fn into_bits(self) -> u128 { self.0.value() as u128 }
160///         const fn from_bits(raw: u128) -> Self { Percent(u7::from_raw(raw as u8)) }
161///     }
162/// }
163///
164/// // `Percent` now nests as a `#[bitfield]` field like any built-in `Bits` type.
165/// #[bitfield(u8, bits = msb)]
166/// #[derive(Clone, Copy)]
167/// struct Meter { pct: Percent, on: bool }
168///
169/// let m = Meter::new().with_pct(Percent(u7::new(42))).with_on(true);
170/// assert_eq!(m.pct(), Percent(u7::new(42)));
171/// assert!(m.on());
172/// ```
173#[macro_export]
174macro_rules! impl_bits {
175    (
176        impl Bits for $t:ty {
177            const BITS: u32 = $bits:expr;
178            const fn into_bits($self_:ident) -> u128 $into:block
179            const fn from_bits($raw:ident: u128) -> Self $from:block
180        }
181    ) => {
182        impl $t {
183            // The const-dispatch pair `#[bitfield]` accessors call (see `Bits`'
184            // docs). `pub` because a bitfield in any other module/crate must be
185            // able to reach it; `allow(unreachable_pub)` for crate-private types.
186            #[doc(hidden)]
187            #[allow(unreachable_pub)]
188            #[inline]
189            pub const fn __bnb_into_bits($self_) -> u128 $into
190
191            #[doc(hidden)]
192            #[allow(unreachable_pub)]
193            #[inline]
194            pub const fn __bnb_from_bits($raw: u128) -> Self $from
195        }
196
197        impl $crate::Bits for $t {
198            const BITS: u32 = $bits;
199
200            #[inline]
201            fn into_bits(self) -> u128 {
202                self.__bnb_into_bits()
203            }
204
205            #[inline]
206            fn from_bits(raw: u128) -> Self {
207                Self::__bnb_from_bits(raw)
208            }
209        }
210    };
211}
212
213/// The seam every `#[bitfield]` struct implements — the stable interface the
214/// `#[bin]` codec builds on, independent of how the fields are accessed.
215///
216/// A bitfield is a thin wrapper over a single backing unsigned integer; this
217/// trait exposes that backing plus the declared layout metadata. The generated
218/// type also provides allocation-free byte conversions: inherent `to_bytes`/`from_bytes`
219/// (which use the declared [`BYTE_ORDER`](Bitfield::BYTE_ORDER)) plus the
220/// endianness-explicit `to_be_bytes`/`to_le_bytes`/`from_be_bytes`/`from_le_bytes`.
221///
222/// # Examples
223///
224/// ```
225/// use bnb::{bitfield, u4, Bitfield, BitOrder, ByteOrder};
226///
227/// #[bitfield(u8, bits = msb, bytes = big)]
228/// #[derive(Clone, Copy)]
229/// struct Byte { hi: u4, lo: u4 }
230///
231/// let b = Byte::new().with_hi(u4::new(0xA)).with_lo(u4::new(0xB));
232/// assert_eq!(b.to_raw(), 0xAB);              // the backing integer
233/// assert_eq!(Byte::from_raw(0xCD).hi().value(), 0xC);
234/// assert_eq!(Byte::WIDTH, 8);                // declared layout metadata
235/// assert_eq!(Byte::BYTE_ORDER, ByteOrder::Big);
236/// assert_eq!(Byte::BIT_ORDER, BitOrder::Msb);
237/// ```
238pub trait Bitfield: Bits + Sized {
239    /// The backing primitive integer (`u8`, `u16`, `u32`, `u64`, or `u128`).
240    type Backing: Copy;
241
242    /// The declared total bit width (may be less than the backing's width, the
243    /// remainder being reserved/padding).
244    const WIDTH: u32;
245
246    /// The byte order in which the backing integer is serialized.
247    const BYTE_ORDER: ByteOrder;
248
249    /// The bit packing order of the declared fields.
250    const BIT_ORDER: BitOrder;
251
252    /// The raw backing integer.
253    fn to_raw(self) -> Self::Backing;
254
255    /// Construct directly from a raw backing integer (no validation — the
256    /// dual-use escape hatch).
257    fn from_raw(raw: Self::Backing) -> Self;
258}
259
260#[cfg(test)]
261mod unit {
262    use super::*;
263
264    #[test]
265    fn bool_occupies_one_bit() {
266        assert_eq!(<bool as Bits>::BITS, 1);
267        assert_eq!(true.into_bits(), 1);
268        assert_eq!(false.into_bits(), 0);
269        assert!(bool::from_bits(1));
270        assert!(!bool::from_bits(0));
271        // Only the low bit is consulted.
272        assert!(!bool::from_bits(0b1110));
273        assert!(bool::from_bits(0b1111));
274    }
275
276    #[test]
277    fn primitives_round_trip_and_truncate() {
278        assert_eq!(<u8 as Bits>::BITS, 8);
279        assert_eq!(<u32 as Bits>::BITS, 32);
280        assert_eq!(0xABu8.into_bits(), 0xAB);
281        // from_bits truncates to the type's width.
282        assert_eq!(u8::from_bits(0x1FF), 0xFF);
283        assert_eq!(u16::from_bits(0x3_1234), 0x1234);
284        let v: u128 = 0xDEAD_BEEF_DEAD_BEEF_DEAD_BEEF_DEAD_BEEF;
285        assert_eq!(u128::from_bits(v.into_bits()), v);
286    }
287
288    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
289    struct Nibble(u8);
290
291    crate::impl_bits! {
292        impl Bits for Nibble {
293            const BITS: u32 = 4;
294            const fn into_bits(self) -> u128 {
295                self.0 as u128
296            }
297            const fn from_bits(raw: u128) -> Self {
298                Nibble((raw & 0xF) as u8)
299            }
300        }
301    }
302
303    #[test]
304    fn impl_bits_emits_a_delegating_trait_impl_and_a_const_pair() {
305        assert_eq!(<Nibble as Bits>::BITS, 4);
306        // The trait path routes through the user's bodies (masking preserved).
307        assert_eq!(Nibble::from_bits(0xAB), Nibble(0xB));
308        assert_eq!(Nibble(0x7).into_bits(), 0x7);
309        // The inherent pair is `const` — usable where the accessors need it.
310        const N: Nibble = Nibble::__bnb_from_bits(0x1C);
311        assert_eq!(N, Nibble(0xC));
312        const _: () = assert!(Nibble(0x3).__bnb_into_bits() == 0x3);
313    }
314}