1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! `#[bitfield]` — pack typed fields into one backing integer.
//!
//! A `#[bitfield]` collapses a struct of `Bits`-typed fields into a single unsigned
//! integer, generating accessors that shift and mask. It is the tool for a run of
//! sub-byte fields that together fill one word (a flags/opcode byte, a VLAN tag, an
//! IPv4 first byte).
//!
//! ```
//! use bnb::{bitfield, u4};
//!
//! #[bitfield(u8, bits = msb)]
//! #[derive(Clone, Copy)]
//! struct VersionIhl {
//! version: u4, // high nibble
//! ihl: u4, // low nibble
//! }
//!
//! let b = VersionIhl::new().with_version(u4::new(4)).with_ihl(u4::new(5));
//! assert_eq!(b.to_raw(), 0x45); // the classic IPv4 first byte
//! assert_eq!(b.version().value(), 4);
//! ```
//!
//! # Generated API
//!
//! For a field `f: T`, you get `f() -> T`, `with_f(T) -> Self` (consuming, chainable),
//! and `set_f(&mut self, T)`. Plus `new()` (all-zero), `to_raw()`/`from_raw()`, and
//! allocation-free byte conversions: `to_bytes`/`from_bytes` serialize in the **declared**
//! byte order (`bytes = big|little`), while `to_be_bytes`/`to_le_bytes`/`from_be_bytes`/`from_le_bytes`
//! force a specific endianness (the override). The type also implements [`Bits`](crate::Bits)
//! and [`Bitfield`](crate::Bitfield), so it nests in another bitfield or a `#[bin]` message.
//!
//! ```
//! use bnb::{bitfield, u4};
//! # #[bitfield(u8, bits = msb)] #[derive(Clone, Copy)] struct VersionIhl { version: u4, ihl: u4 }
//! let mut b = VersionIhl::new().with_version(u4::new(4)).with_ihl(u4::new(5));
//! b.set_ihl(u4::new(6));
//! assert_eq!(b.ihl().value(), 6);
//! assert_eq!(VersionIhl::from_be_bytes([0x45]).version().value(), 4);
//! ```
//!
//! # Bit order vs. byte order — two independent knobs
//!
//! - `bits = msb | lsb` (default `msb`): does the **first** declared field land in the
//! high or low bits of the backing integer. `msb` matches the ASCII-art layouts in
//! RFCs (first field drawn leftmost = most significant).
//! - `bytes = big | little` (default `big`): the byte order `to_bytes`/`from_bytes` use
//! when serializing the backing integer.
//!
//! They are orthogonal *here*: a bitfield packs its fields into the backing integer (bit
//! order), then serializes that whole integer with the declared byte order — two genuinely
//! independent steps. (At the `#[bin]` *message* layer the two instead compose by the
//! natural-layout rule — a byte-multiple value is byte-swapped only when the declared byte
//! order differs from the bit order's natural layout; see the
//! [`bin_codec`](super::bin_codec) guide.) The same fields, packed `msb`, declared with two
//! different byte orders — `to_bytes` honors each declaration:
//!
//! ```
//! use bnb::{bitfield, u4};
//!
//! #[bitfield(u16, bits = msb, bytes = big)]
//! #[derive(Clone, Copy)]
//! struct Be { hi: u4, mid: u8, lo: u4 }
//!
//! #[bitfield(u16, bits = msb, bytes = little)]
//! #[derive(Clone, Copy)]
//! struct Le { hi: u4, mid: u8, lo: u4 }
//!
//! let be = Be::new().with_hi(u4::new(0xA)).with_mid(0xBC).with_lo(u4::new(0xD));
//! let le = Le::new().with_hi(u4::new(0xA)).with_mid(0xBC).with_lo(u4::new(0xD));
//! assert_eq!(be.to_bytes(), [0xAB, 0xCD]); // declared `big` -> big-endian bytes
//! assert_eq!(le.to_bytes(), [0xCD, 0xAB]); // same logical value, declared `little`
//!
//! // `to_be_bytes`/`to_le_bytes` ignore the declaration — use them only to override it.
//! assert_eq!(le.to_be_bytes(), [0xAB, 0xCD]);
//! ```
//!
//! # Field widths: inferred, explicit, or ranged
//!
//! In order of precedence:
//!
//! 1. **Inferred** (no attribute): the field's width is `<T as Bits>::BITS`. Fields
//! pack adjacently in declaration order. This is the common case.
//! 2. **`#[bits(N)]`**: an explicit width, still auto-placed. Useful when a field's
//! type is wider than the bits it should occupy.
//! 3. **`#[bits(A..=B)]`**: an absolute, inclusive bit range — fully manual layout,
//! the equivalent of `bitbybit`'s `bits = A..=B`. Use ranges on **every** field, or
//! on none; the two styles can't be mixed in one struct.
//!
//! With inferred widths the declared total is the sum of the fields (gaps are not
//! transmitted); with ranges the width is the whole backing integer (gaps are real
//! reserved bits on the wire).
//!
//! ```
//! use bnb::bitfield;
//!
//! // Manual layout: two fields with a deliberate 3-bit gap inside a u32.
//! #[bitfield(u32, bits = msb)]
//! #[derive(Clone, Copy)]
//! struct Manual {
//! #[bits(20..=31)] tag: bnb::u12, // top 12 bits
//! #[bits(0..=16)] body: bnb::u17, // low 17 bits; bits 17..=19 are reserved
//! }
//!
//! let m = Manual::new().with_tag(bnb::u12::new(0xABC)).with_body(bnb::u17::new(1));
//! assert_eq!(m.tag().value(), 0xABC);
//! assert_eq!(m.body().value(), 1);
//! ```
//!
//! A reversed range (`#[bits(31..=20)]`) is a clear compile error, not a silent
//! overflow, and field widths that exceed the backing integer fail a const assert.
//!
//! # Nesting
//!
//! Because a `#[bitfield]` is itself a `Bits` value, bitfields nest. A 5-bit field of
//! a nested type contributes exactly 5 bits to its parent:
//!
//! ```
//! use bnb::{bitfield, BitEnum, u3, u5};
//!
//! #[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
//! #[bit_enum(u3)]
//! enum Op { Read, Write, #[catch_all] Other(u3) }
//!
//! #[bitfield(u8, bits = msb)]
//! #[derive(Clone, Copy)]
//! struct Cmd { op: Op, addr: u5 } // 3 + 5 = 8 bits exactly
//!
//! let c = Cmd::new().with_op(Op::Write).with_addr(u5::new(0x11));
//! assert_eq!(c.op(), Op::Write);
//! assert_eq!(c.addr().value(), 0x11);
//! ```
//!
//! See [`enums`](super::enums) and [`flags`](super::flags) for the field types that
//! nest here, and [`composition`](super::composition) for the full picture.