bitsandbytes 0.1.0

An owned, bit-aware binary codec: fast bit/byte field types and the unified #[bin] macro.
Documentation
//! Field-directive reference — `#[br]` (read), `#[bw]` (write), and the standalone
//! field attributes, one runnable example each.
//!
//! These appear on the fields of a [`#[bin]`](super::bin_codec) struct (or a bare
//! `#[derive(BitDecode/BitEncode)]`). Struct-level options (`magic`, `ctx`, `validate`,
//! `big`/`little`, …) are covered in [`bin_codec`](super::bin_codec).
//!
//! # `count` — a length-driven `Vec`
//!
//! `#[br(count = <expr>)]` reads that many elements into a `Vec<T>`; the expression may
//! name an earlier field. On write, every element is emitted (the length is the
//! caller's to track — usually with `temp`+`calc`, below).
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct List { len: u8, #[br(count = len)] items: Vec<u16> }
//!
//! let v = List { len: 2, items: vec![0xAABB, 0xCCDD] };
//! assert_eq!(v.to_bytes().unwrap(), [0x02, 0xAA, 0xBB, 0xCC, 0xDD]);
//! assert_eq!(List::decode_exact(&[0x02, 0xAA, 0xBB, 0xCC, 0xDD]).unwrap(), v);
//! ```
//!
//! # `temp` + `calc` — derived, never stored
//!
//! `#[br(temp)]` reads a value into a local that later directives can use, but does not
//! store it; its `#[bw(calc = <expr>)]` recomputes it on write. Together they keep a
//! length/count from ever drifting from the data it describes.
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Msg {
//!     #[br(temp)]
//!     #[bw(calc = self.items.len() as u8)]
//!     n: u8,
//!     #[br(count = n)]
//!     items: Vec<u8>,
//! }
//!
//! let m = Msg { items: vec![10, 20, 30] };   // no `n` field to set
//! assert_eq!(m.to_bytes().unwrap(), [0x03, 10, 20, 30]);
//! assert_eq!(Msg::decode_exact(&[0x02, 5, 6]).unwrap().items, vec![5, 6]);
//! ```
//!
//! # `if` — a conditional `Option`
//!
//! `#[br(if(<cond>))]` on an `Option<T>` reads `Some` when the condition (over earlier
//! fields) holds, else `None`. On write, the `Option`'s presence drives whether it is
//! emitted.
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Opt { has_ext: u8, #[br(if(has_ext != 0))] ext: Option<u16> }
//!
//! let with = Opt { has_ext: 1, ext: Some(0xBEEF) };
//! let without = Opt { has_ext: 0, ext: None };
//! assert_eq!(with.to_bytes().unwrap(), [0x01, 0xBE, 0xEF]);
//! assert_eq!(without.to_bytes().unwrap(), [0x00]);
//! assert_eq!(Opt::decode_exact(&[0x01, 0xBE, 0xEF]).unwrap(), with);
//! assert_eq!(Opt::decode_exact(&[0x00]).unwrap(), without);
//! ```
//!
//! # `map` / `try_map` — transform the wire value
//!
//! `#[br(map = <f>)]` reads the wire value (its type inferred from `f`'s argument) and
//! maps it to the field type; `#[bw(map = <f>)]` is the inverse on write. Use them to
//! store a friendly type while the wire keeps a raw encoding.
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Reading {
//!     // wire: u8 biased by 40; stored: signed °C.
//!     #[br(map = |raw: u8| raw as i16 - 40)]
//!     #[bw(map = |c: &i16| (*c + 40) as u8)]
//!     celsius: i16,
//! }
//!
//! let r = Reading { celsius: 10 };
//! assert_eq!(r.to_bytes().unwrap(), [0x32]);              // 10 + 40 = 50
//! assert_eq!(Reading::decode_exact(&[0x32]).unwrap(), r);
//! ```
//!
//! `try_map` is the fallible form — the converter returns a `Result`, and an error
//! becomes a decode error (no panic):
//!
//! ```
//! use bnb::bin;
//! #[bin(big, read_only)]
//! #[derive(Debug, PartialEq)]
//! struct Checked {
//!     #[br(try_map = |raw: u8| if raw < 100 { Ok(raw) } else { Err("out of range") })]
//!     pct: u8,
//! }
//! assert_eq!(Checked::decode_exact(&[42]).unwrap().pct, 42);
//! assert!(Checked::decode_exact(&[200]).is_err());        // converter rejected it
//! ```
//!
//! # `parse_with` / `write_with` — a custom codec escape hatch
//!
//! When a field's shape needs arbitrary logic, supply your own functions. `parse_with`
//! takes `fn(&mut impl Source) -> Result<T, BitError>`; `write_with` takes
//! `fn(&T, &mut impl Sink) -> Result<(), BitError>`.
//!
//! ```
//! use bnb::{bin, BitError, Sink, Source};
//!
//! fn read_pascal<S: Source>(r: &mut S) -> Result<String, BitError> {
//!     let len: u8 = r.read()?;
//!     let mut s = String::new();
//!     for _ in 0..len { s.push(r.read::<u8>()? as char); }
//!     Ok(s)
//! }
//! fn write_pascal<K: Sink>(s: &String, w: &mut K) -> Result<(), BitError> {
//!     w.write(s.len() as u8)?;
//!     for &b in s.as_bytes() { w.write(b)?; }
//!     Ok(())
//! }
//!
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Named {
//!     #[br(parse_with = read_pascal)]
//!     #[bw(write_with = write_pascal)]
//!     name: String,
//! }
//!
//! let n = Named { name: "Hi".into() };
//! assert_eq!(n.to_bytes().unwrap(), [0x02, b'H', b'i']);
//! assert_eq!(Named::decode_exact(&[0x02, b'H', b'i']).unwrap(), n);
//! ```
//!
//! # `brw(ignore)` — a field neither read nor written
//!
//! `#[brw(ignore)]` consumes no wire bits: the field is `Default::default()` on read and
//! skipped on write. Use it for derived/scratch state you want on the struct but not on
//! the wire. It is spelled with `brw` (not `br`) because it applies to **both**
//! directions.
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Parsed { raw: u8, #[brw(ignore)] note: u32 }
//!
//! let p = Parsed { raw: 7, note: 999 };
//! assert_eq!(p.to_bytes().unwrap(), [0x07]);             // note not written
//! assert_eq!(Parsed::decode_exact(&[0x07]).unwrap(), Parsed { raw: 7, note: 0 });
//! ```
//!
//! # `reserved` / `reserved_with` — fixed wire bits with a spec value
//!
//! A reserved field is a normal **stored** field with a known *spec value*: the type's
//! zero for `#[reserved]`, the given expression for `#[reserved_with(<expr>)]` (e.g. a
//! must-be-one pattern). On the default path it reads and writes its *actual* value —
//! so you can observe a peer's reserved bits and override them — while the builder
//! defaults it to the spec value (so it isn't required) and the `spec_*` codecs use the
//! spec value instead.
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct R {
//!     a: u8,
//!     #[reserved] pad: u8,                 // spec value 0x00
//!     #[reserved_with(0xFFu8)] ones: u8,   // spec value 0xFF
//!     b: u8,
//! }
//!
//! // The builder makes the reserved fields optional, defaulting to their spec values.
//! let r = R::builder().a(1).b(2).build().unwrap();
//! assert_eq!(r.to_bytes().unwrap(), [0x01, 0x00, 0xFF, 0x02]);
//!
//! // Decode captures the actual reserved bits; spec_decode reports the expected ones.
//! let actual = R::decode_exact(&[0x01, 0x55, 0x55, 0x02]).unwrap();
//! assert_eq!((actual.pad, actual.ones), (0x55, 0x55));
//! let spec = R::spec_decode_exact(&[0x01, 0x55, 0x55, 0x02]).unwrap();
//! assert_eq!((spec.pad, spec.ones), (0x00, 0xFF));     // the spec values
//! assert_eq!(spec.to_spec_bytes().unwrap(), [0x01, 0x00, 0xFF, 0x02]);
//! ```
//!
//! # `pad_*` / `align_*` — forward positioning
//!
//! `#[br(pad_before = <bits>)]` / `pad_after` skip a bit count around a field;
//! `align_before` / `align_after` skip to the next byte boundary. Bit/byte amounts come
//! from the [`prelude`](crate::prelude) (`1.bytes()`, `4.bits()`).
//!
//! ```
//! use bnb::{bin, prelude::*};
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct P { a: u8, #[br(pad_before = 1u32.bytes())] b: u8 }
//!
//! let p = P { a: 1, b: 2 };
//! assert_eq!(p.to_bytes().unwrap(), [0x01, 0x00, 0x02]); // one zero pad byte
//! assert_eq!(P::decode_exact(&[0x01, 0x99, 0x02]).unwrap(), p); // pad skipped on read
//! ```
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct A { flag: bool, #[br(align_before)] val: u8 }  // val starts on a byte boundary
//!
//! let a = A { flag: true, val: 0x2A };
//! assert_eq!(a.to_bytes().unwrap(), [0x80, 0x2A]); // flag in the high bit, then val
//! assert_eq!(A::decode_exact(&[0x80, 0x2A]).unwrap(), a);
//! ```
//!
//! # `restore_position` — peek without consuming
//!
//! `#[br(restore_position)]` reads the field, then rewinds the cursor so later fields
//! re-read the same bytes (e.g. peek a discriminant, then read the full record). The
//! field is not re-emitted on write — the overlapping field owns those bytes. It needs
//! a seekable source, so `decode_from` on a forward-only stream is a compile error; the
//! slice paths (`decode`/`peek`/`decode_exact`) always qualify.
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Peeked {
//!     #[br(restore_position)] tag: u8, // peek the first byte...
//!     full: u16,                       // ...then read it as the high byte of a u16
//! }
//!
//! let p = Peeked::decode_exact(&[0xAB, 0xCD]).unwrap();
//! assert_eq!(p.tag, 0xAB);
//! assert_eq!(p.full, 0xABCD);
//! assert_eq!(p.to_bytes().unwrap(), [0xAB, 0xCD]); // `full` emits the bytes; `tag` does not
//! ```
//!
//! # `seek` — read at an absolute offset (pointer-following)
//!
//! `#[br(seek = <bits>)]` jumps the cursor to an **absolute** bit offset before reading
//! the field — the building block for offset tables and pointer chains. Bit/byte amounts
//! come from the [`prelude`](crate::prelude) (`ptr.bytes()`, `n.bits()`). It is read-side
//! (the writer is append-only); pair it with `restore_position` to read at the offset and
//! return so later fields continue in order. Like `restore_position` it seeks, so
//! `decode_from` on a forward-only stream is a compile error; the slice paths qualify.
//!
//! ```
//! use bnb::{bin, prelude::*};
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Ptr {
//!     ptr: u8,                                     // byte offset of `target`
//!     #[br(seek = ptr.bytes(), restore_position)]
//!     target: u8,                                  // read at `ptr`, then rewind
//!     next: u8,                                    // continues right after `ptr`
//! }
//!
//! // `peek` doesn't require full consumption (seek/restore leave the tail untouched).
//! let p = Ptr::peek(&[0x03, 0x11, 0x22, 0xAB]).unwrap();
//! assert_eq!((p.ptr, p.target, p.next), (3, 0xAB, 0x11));
//! ```
//!
//! On encode the seek is a no-op (the writer appends), so a *relocated* layout won't
//! round-trip through the default encoder — emit such formats with `write_with` /
//! `write_only`, where you control placement.
//!
//! # `dbg` — trace a field as it decodes
//!
//! `#[br(dbg)]` emits a [`tracing`](https://docs.rs/tracing) event as the field is read,
//! carrying its start bit offset and decoded value (the field type must be `Debug`). It
//! is a read-side diagnostic — no extra bits are consumed and encode is unaffected. The
//! event is at `TRACE` level under the `bnb::dbg` target, so you can surface just these
//! with `RUST_LOG=bnb::dbg=trace` (the application installs the subscriber; libraries
//! only emit).
//!
//! ```
//! use bnb::bin;
//! #[bin(big)]
//! #[derive(Debug, PartialEq)]
//! struct Framed { tag: u8, #[br(dbg)] len: u16 }
//!
//! // Decoding is identical with or without `dbg`; it just also traces `len`.
//! let f = Framed::decode_exact(&[0x01, 0x00, 0x2A]).unwrap();
//! assert_eq!(f, Framed { tag: 1, len: 42 });
//! ```