bnb/guide/bin_codec.rs
1//! `#[bin]` — the unified whole-message codec.
2//!
3//! `#[bin]` folds three things over one struct: the read codec, the write codec, and a
4//! required-by-default builder. Fields are read and written at arbitrary **bit**
5//! offsets, so the same attribute handles byte-aligned headers and sub-byte frames,
6//! and any [`Bits`](crate::Bits) type — including a `#[bitfield]`, `#[derive(BitEnum)]`,
7//! or `#[bitflags]` — drops in as one field with no glue.
8//!
9//! # A DNS header, end to end
10//!
11//! The 12-byte DNS message header (RFC 1035 §4.1.1) is a good tour: a 16-bit flags
12//! word (itself a bitfield of enums and bools) between five `u16` counts/ids.
13//!
14//! ```
15//! use bnb::{bin, bitfield, BitEnum, u3, u4};
16//!
17//! #[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
18//! #[bit_enum(u4)]
19//! enum OpCode { Query, IQuery, Status, #[catch_all] Other(u4) }
20//!
21//! #[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
22//! #[bit_enum(u4)]
23//! enum RCode { NoError, FormErr, ServFail, NxDomain, #[catch_all] Other(u4) }
24//!
25//! // The 16-bit flags word, MSB-first (RFC diagram order), big-endian.
26//! #[bitfield(u16, bits = msb, bytes = be)]
27//! #[derive(Clone, Copy, Debug, PartialEq, Eq)]
28//! struct Flags {
29//! qr: bool, opcode: OpCode, aa: bool, tc: bool,
30//! rd: bool, ra: bool, z: u3, rcode: RCode, // 1+4+1+1+1+1+3+4 = 16
31//! }
32//!
33//! #[bin(big)]
34//! #[derive(Debug, Clone, PartialEq)]
35//! struct Header {
36//! id: u16,
37//! flags: Flags, // a 16-bit bitfield, nested as one field
38//! qdcount: u16,
39//! ancount: u16,
40//! nscount: u16,
41//! arcount: u16,
42//! }
43//!
44//! // Build with the required-by-default builder.
45//! let flags = Flags::new().with_qr(true).with_rd(true).with_ra(true);
46//! let h = Header::builder()
47//! .id(0x1234).flags(flags)
48//! .qdcount(1).ancount(1).nscount(0).arcount(0)
49//! .build().unwrap();
50//!
51//! // Encode -> 12 bytes; decode is the exact inverse.
52//! let bytes = h.to_bytes().unwrap();
53//! assert_eq!(bytes.len(), 12);
54//! assert_eq!(&bytes[..4], &[0x12, 0x34, 0x81, 0x80]); // id, then flags 0x8180
55//! assert_eq!(Header::decode_exact(&bytes).unwrap(), h);
56//! ```
57//!
58//! # The generated API
59//!
60//! Every `#[bin]` type gets a consistent surface:
61//!
62//! | Direction | Method | Use |
63//! |---|---|---|
64//! | decode | `decode_exact(&[u8])` | one message that consumes every whole byte |
65//! | decode | `decode(&mut &[u8])` | one message from the front; advances the slice |
66//! | decode | `peek(&[u8])` | one message, tail-tolerant, no buffer mutation |
67//! | decode | `decode_from(&mut S)` | from an explicit [`Source`](crate::Source) (stream/socket/file) |
68//! | encode | `to_bytes() -> Vec<u8>` | encode to a fresh buffer |
69//! | encode | `encode(&mut W)` | encode to any [`std::io::Write`] |
70//! | encode | `encode_into(&mut K)` | encode into an explicit [`Sink`](crate::Sink) |
71//! | build | `builder()` | the required-by-default builder |
72//!
73//! `decode`/`peek`/`decode_exact`/`to_bytes` are the everyday slice/`Vec` path;
74//! `decode_from`/`encode_into` open the door to the [I/O ladder](super::io).
75//!
76//! # Struct-level options
77//!
78//! Inside `#[bin(...)]`:
79//!
80//! - `big` / `little` — byte order (default `big`).
81//! - `bit_order = msb | lsb` — bit order (default `msb`).
82//! - `magic = <expr>` — a leading constant verified on read, emitted on write.
83//! - `read_only` / `write_only` — generate only one direction.
84//! - `no_builder` — skip the builder.
85//! - `forward_only` — bound decoding to a forward `Source` (a seek directive is then a
86//! compile error).
87//! - `ctx(name: Ty, …)` — declare context the message needs from its parent.
88//! - `validate = <path>` — a soundness check run by `build()`.
89//!
90//! ## Each option, by example
91//!
92//! Byte and bit order:
93//!
94//! ```
95//! use bnb::{bin, u4};
96//!
97//! #[bin(little)] // little-endian byte order
98//! #[derive(Debug, PartialEq)]
99//! struct Le { v: u32 }
100//! assert_eq!(Le { v: 0x1234_5678 }.to_bytes().unwrap(), [0x78, 0x56, 0x34, 0x12]);
101//!
102//! #[bin(big, bit_order = lsb)] // the first field lands in the LOW bits of the byte
103//! #[derive(Debug, PartialEq)]
104//! struct Lsb { a: u4, b: u4 }
105//! assert_eq!(Lsb { a: u4::new(0xA), b: u4::new(0xB) }.to_bytes().unwrap(), [0xBA]);
106//! ```
107//!
108//! Directional codecs and the builder:
109//!
110//! ```
111//! use bnb::bin;
112//!
113//! #[bin(big, read_only)] // only decodes — no `to_bytes`/`encode`
114//! #[derive(Debug, PartialEq)]
115//! struct Ro { v: u16 }
116//! assert_eq!(Ro::decode_exact(&[0x12, 0x34]).unwrap(), Ro { v: 0x1234 });
117//!
118//! #[bin(big, write_only)] // only encodes — no `decode`/`peek`
119//! struct Wo { v: u16 }
120//! assert_eq!(Wo { v: 0x1234 }.to_bytes().unwrap(), [0x12, 0x34]);
121//!
122//! #[bin(big, no_builder)] // no `Nb::builder()` — construct directly
123//! #[derive(Debug, PartialEq)]
124//! struct Nb { v: u16 }
125//! assert_eq!(Nb { v: 5 }.to_bytes().unwrap(), [0x00, 0x05]);
126//! ```
127//!
128//! `forward_only` — decode from a non-seekable stream, with a compile-time no-seek
129//! guarantee (a `#[br(restore_position)]` field would then be a compile error):
130//!
131//! ```
132//! use bnb::{bin, StreamBitReader};
133//!
134//! #[bin(big, forward_only)]
135//! #[derive(Debug, PartialEq)]
136//! struct Hdr { magic: u16, len: u16 }
137//!
138//! let data: &[u8] = &[0xCA, 0xFE, 0x00, 0x10]; // `&[u8]` is `Read` but not `Seek`
139//! let mut s = StreamBitReader::new(data);
140//! assert_eq!(Hdr::decode_from(&mut s).unwrap(), Hdr { magic: 0xCAFE, len: 16 });
141//! ```
142//!
143//! `ctx(...)` — context a message needs from its parent **to decode**. The parent passes it
144//! with `#[br(ctx { … })]`; standalone, `decode_with`/`decode_with_exact` take a `…Ctx`
145//! (built positionally with `…Ctx::new`). `ctx` is **decode-only**: encode stays a plain
146//! `to_bytes()` unless the *write* side actually reads a ctx param (a keyed `bw(map)`,
147//! `calc`, or `write_with`), in which case the type gets `to_bytes_with`/`encode_with`:
148//!
149//! ```
150//! use bnb::bin;
151//!
152//! #[bin(big, ctx(len: u16))]
153//! #[derive(Debug, PartialEq)]
154//! struct Body {
155//! #[br(count = len)]
156//! data: Vec<u8>,
157//! }
158//!
159//! #[bin(big)]
160//! #[derive(Debug, PartialEq)]
161//! struct Packet {
162//! len: u16,
163//! #[br(ctx { len })] // pass `len` to `Body`
164//! body: Body,
165//! }
166//!
167//! let p = Packet { len: 3, body: Body { data: vec![1, 2, 3] } };
168//! assert_eq!(p.to_bytes().unwrap(), [0x00, 0x03, 1, 2, 3]);
169//! assert_eq!(Packet::decode_exact(&[0x00, 0x03, 1, 2, 3]).unwrap(), p);
170//!
171//! // Standalone: decode needs the context (build it with `BodyCtx::new`); encode is
172//! // plain — `ctx` is decode-only, and `Body`'s write side doesn't read `len`.
173//! let b = Body::decode_with_exact(&[0xAA, 0xBB], BodyCtx::new(2)).unwrap();
174//! assert_eq!(b.data, vec![0xAA, 0xBB]);
175//! assert_eq!(b.to_bytes().unwrap(), vec![0xAA, 0xBB]);
176//! ```
177//!
178//! (`magic` and `validate` are shown below.)
179//!
180//! # Dual-use: `validate` gates the builder, not the parser
181//!
182//! `validate = path` runs a `fn(&Self) -> Result<(), impl Display>` in `build()` only.
183//! The **parser stays permissive** — it never rejects representable input — so a
184//! deliberately malformed message is still decodable (for fuzzing / interop), even
185//! though it can't be *built*:
186//!
187//! ```
188//! use bnb::bin;
189//!
190//! #[bin(big, validate = check)]
191//! #[derive(Debug, PartialEq)]
192//! struct Msg { kind: u8, len: u8 }
193//!
194//! fn check(m: &Msg) -> Result<(), String> {
195//! if m.kind == 0 { return Err("kind 0 is reserved".into()); }
196//! Ok(())
197//! }
198//!
199//! assert!(Msg::builder().kind(0).len(4).build().is_err()); // builder: rejected
200//! assert!(Msg::decode_exact(&[0x00, 0x04]).is_ok()); // parser: permissive
201//! ```
202//!
203//! # Derived, never-drifting fields
204//!
205//! A length or count you don't want to store can be read into a temp local and
206//! recomputed on write, so it can never disagree with the data. Here `len` drives a
207//! `count`-bound `Vec` on read and is recomputed from `payload.len()` on write:
208//!
209//! ```
210//! use bnb::bin;
211//!
212//! #[bin(big, magic = 0xCAFEu16)]
213//! #[derive(Debug, PartialEq)]
214//! struct Frame {
215//! #[br(temp)]
216//! #[bw(calc = self.payload.len() as u8)]
217//! len: u8,
218//! #[br(count = len)]
219//! payload: Vec<u8>,
220//! }
221//!
222//! let f = Frame::builder().payload(vec![0xDE, 0xAD, 0xBE, 0xEF]).build().unwrap();
223//! assert_eq!(f.to_bytes().unwrap(), [0xCA, 0xFE, 0x04, 0xDE, 0xAD, 0xBE, 0xEF]);
224//! assert_eq!(Frame::decode_exact(&[0xCA, 0xFE, 0x02, 0x01, 0x02]).unwrap().payload, vec![1, 2]);
225//! ```
226//!
227//! See [`directives`](super::directives) for every field directive, and
228//! [`io`](super::io) for decoding from a socket or file rather than a slice. The
229//! `examples/bin_message.rs` example in the repository is a runnable version of the
230//! header + frame above.