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 allocation-free byte conversions: inherent `to_bytes`/`from_bytes`
125/// (which use the declared [`BYTE_ORDER`](Bitfield::BYTE_ORDER)) plus the
126/// endianness-explicit `to_be_bytes`/`to_le_bytes`/`from_be_bytes`/`from_le_bytes`.
127///
128/// # Examples
129///
130/// ```
131/// use bnb::{bitfield, u4, Bitfield, BitOrder, ByteOrder};
132///
133/// #[bitfield(u8, bits = msb, bytes = be)]
134/// #[derive(Clone, Copy)]
135/// struct Byte { hi: u4, lo: u4 }
136///
137/// let b = Byte::new().with_hi(u4::new(0xA)).with_lo(u4::new(0xB));
138/// assert_eq!(b.to_raw(), 0xAB); // the backing integer
139/// assert_eq!(Byte::from_raw(0xCD).hi().value(), 0xC);
140/// assert_eq!(Byte::WIDTH, 8); // declared layout metadata
141/// assert_eq!(Byte::BYTE_ORDER, ByteOrder::Big);
142/// assert_eq!(Byte::BIT_ORDER, BitOrder::Msb);
143/// ```
144pub trait Bitfield: Bits + Sized {
145 /// The backing primitive integer (`u8`, `u16`, `u32`, `u64`, or `u128`).
146 type Backing: Copy;
147
148 /// The declared total bit width (may be less than the backing's width, the
149 /// remainder being reserved/padding).
150 const WIDTH: u32;
151
152 /// The byte order in which the backing integer is serialized.
153 const BYTE_ORDER: ByteOrder;
154
155 /// The bit packing order of the declared fields.
156 const BIT_ORDER: BitOrder;
157
158 /// The raw backing integer.
159 fn to_raw(self) -> Self::Backing;
160
161 /// Construct directly from a raw backing integer (no validation — the
162 /// dual-use escape hatch).
163 fn from_raw(raw: Self::Backing) -> Self;
164}
165
166#[cfg(test)]
167mod unit {
168 use super::*;
169
170 #[test]
171 fn bool_occupies_one_bit() {
172 assert_eq!(<bool as Bits>::BITS, 1);
173 assert_eq!(true.into_bits(), 1);
174 assert_eq!(false.into_bits(), 0);
175 assert!(bool::from_bits(1));
176 assert!(!bool::from_bits(0));
177 // Only the low bit is consulted.
178 assert!(!bool::from_bits(0b1110));
179 assert!(bool::from_bits(0b1111));
180 }
181
182 #[test]
183 fn primitives_round_trip_and_truncate() {
184 assert_eq!(<u8 as Bits>::BITS, 8);
185 assert_eq!(<u32 as Bits>::BITS, 32);
186 assert_eq!(0xABu8.into_bits(), 0xAB);
187 // from_bits truncates to the type's width.
188 assert_eq!(u8::from_bits(0x1FF), 0xFF);
189 assert_eq!(u16::from_bits(0x3_1234), 0x1234);
190 let v: u128 = 0xDEAD_BEEF_DEAD_BEEF_DEAD_BEEF_DEAD_BEEF;
191 assert_eq!(u128::from_bits(v.into_bits()), v);
192 }
193}