Skip to main content

Crate bnb

Crate bnb 

Source
Expand description

bnb — an owned, bit-aware binary codec: ergonomic, fast bit/byte field types and the unified #[bin] whole-message codec.

It provides, designed to compose:

  • Arbitrary-width integers (u1..u127, via UInt) for sub-byte fields — a dependency-free replacement for arbitrary-int.
  • #[bitfield] — an attribute macro that packs typed fields into a single backing integer with explicit, independent control of bit order (MSB/LSB-first) and byte order (big/little). It generates getters, immutable with_* setters, raw access, and allocation-free *_bytes conversions.
  • #[derive(BitEnum)] — enum ⇄ integer at a chosen width, with an optional #[catch_all] variant that preserves unknown values (the dual-use convention).
  • #[bin] — the unified whole-message codec (see below): reads/writes a struct at arbitrary bit offsets, with a rich, binrw-inspired directive surface.

The aim is to collapse a whole stack of overlapping helpers — modular-bitfield(-msb), bitfield-struct, bitbybit, arbitrary-int, num_enum, and a binrw-style codec — into one fast, integer-backed (shift/mask, no bitvec) crate. See Inspiration.

§Example — a DNS-style 16-bit header field

use bnb::{bitfield, BitEnum, u4, u5, u7};

#[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
#[bit_enum(u4)]
enum RCode {
    NoError,   // 0
    FormErr,   // 1
    ServFail,  // 2
    #[catch_all]
    Other(u4), // any other 4-bit value
}

// MSB-first packing (network/RFC order), big-endian on the wire.
#[bitfield(u16, bits = msb, bytes = big)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct State {
    opcode: u5,   // first field -> high bits
    flags:  Flags,
    rcode:  RCode, // last field -> low bits
}

let s = State::new()
    .with_opcode(u5::new(2))
    .with_rcode(RCode::ServFail);
assert_eq!(s.rcode(), RCode::ServFail);
// opcode occupies bits 11..=15 (the high 5 of the u16).
assert_eq!(s.to_be_bytes()[0] >> 3, 2);

§Bit order vs. byte order

These are independent knobs, which is the whole point:

  • bits = msb | lsb — does the first declared field land in the high or low bits of the backing integer. Default: msb (matches RFC ASCII-art layouts).
  • bytes = big | le — endianness of the backing integer when serialized. Default: be.

§The #[bin] codec

Whole-message bit-aware codec: #[bin] (magic/count/ctx/map/if/calc·temp/reserved/ positioning/validate) over a Source/SeekSource/BufSource/SeekReader I/O ladder, with an opt-in bytes feature for async framing. Common field codecs — LEB128 varints, NUL-terminated and length-prefixed strings — ship ready-made in codecs (referenced via parse_with/write_with).

§Feature flags & no_std

bnb is no_std-compatible — it always needs alloc (the codec produces Vec<u8> and owns variable-length payloads), but not std. Build with default-features = false for an embedded target.

  • std (default) — the std::io ladder (StreamBitReader, BufSource, SeekReader, Source::as_read/Sink::as_write), the From<std::io::Error> bridge, and the encode(writer) convenience (EncodeExt). The #[br(dbg)] directive (which emits a tracing event) is also std-only.
  • bytes — the zero-copy bytes-crate adapters; implies std (async/tokio framing).
  • tokioBinCodec, a tokio_util::codec Decoder/Encoder for any #[bin] message: Framed::new(tcp, BinCodec::<T>::new()) (a stream) or UdpFramed::new(udp, …) (a datagram Stream + Sink of (T, addr)) — one codec, both async transports. Implies bytes. This is bnb’s async support: a native async Source/Sink family is deliberately out of scope (the codec is in-memory and fast; framing is the async boundary, and BinCodec covers it).
  • net — ergonomic std socket helpers: MessageStream (whole-message read/write over any Read + Write, e.g. a TcpStream, no try_clone) and MessageDatagram (send_message/ recv_message over a sealed DatagramSocketUdpSocket or UnixDatagram). Implies std.
  • mock — test-only in-memory transports for exercising net code without a real socket: MockDatagramSocket (a DatagramSocket) and MockStream (a Read + Write, with chunked delivery to drive the read-more path). Put it in your [dev-dependencies]. Implies net.

Without std you still get the full macro surface plus: decode from a &[u8] (BitReader, Type::decode/decode_all/decode_iter/decode_exact/peek), encode to a Vec<u8> (Type::to_bytes/to_canonical_bytes, or BitEncode::bit_encode over a Sink), and incremental framing with BitBuf — push bytes from any transport (a UART ISR, a radio, a channel), pull whole messages; BitBuf::bounded gives an alloc-once fixed footprint. You lose only the streaming std::io adapters and encode(&mut impl Write); on no_std, encode with to_bytes() and write the bytes to your transport yourself. That is the deliberate no_std boundary: streaming reader adapters without std are out of scope for 1.0 (if demanded later, they’d arrive as additive adapters over the ecosystem’s embedded-io traits — see ROADMAP.md).

§Inspiration

bnb stands on the shoulders of several excellent crates, collapsing their capabilities into one: the arbitrary-width integers of arbitrary-int; the bitfield packing of modular-bitfield, bitfield-struct, and bitbybit; the enum ⇄ integer mapping of num_enum; and — most of all — the declarative, bidirectional codec design of binrw, whose #[br]/#[bw] attribute vocabulary #[bin] deliberately echoes so the two feel like one toolkit. bnb shares no code with these crates; it is a from-scratch implementation, extended to do the one thing a byte-oriented Read + Seek codec cannot: read and write fields at arbitrary bit offsets. See ACKNOWLEDGMENTS.md for the full credit.

§Guide

The guide module is a set of worked, runnable walkthroughs — start there for a tour of the crate and the rationale behind each piece. Reading order:

  1. guide::quick_start — a five-minute tour of every macro.
  2. guide::numbers — the arbitrary-width integers (u1..u127) and the Bits trait that lets everything compose.
  3. guide::bitfields#[bitfield]: bit order, byte order, widths and ranges, nesting.
  4. guide::enums#[derive(BitEnum)]: catch-all vs. closed, num_enum parity.
  5. guide::flags#[bitflags]: single-bit flag sets with set algebra.
  6. guide::builders#[derive(BitsBuilder)]: the required-by-default builder.
  7. guide::bin_codec#[bin]: a whole protocol header, end to end.
  8. guide::directives — the field-directive reference, one example each.
  9. guide::mapping#[bin(map/bw_map = …)]: a whole struct mapped to/from a wire type.
  10. guide::dispatch#[bin] on an enum: tagged-union dispatch by wire magic or off-wire tag.
  11. guide::io — the Source/Sink I/O ladder (slice, stream, socket, file, bytes).
  12. guide::errors — position-aware errors and the streaming Incomplete signal.
  13. guide::dual_use — the compliant-by-default-but-violatable philosophy.
  14. guide::composition — how the pieces nest and size each other.
  15. guide::migrating — coming from binrw / modular-bitfield / num_enum.

Re-exports§

pub use bitstream::BitAmount;
pub use bitstream::BitBuf;
pub use bitstream::BitDecode;
pub use bitstream::BitEncode;
pub use bitstream::BitError;
pub use bitstream::BitReader;
pub use bitstream::BitWriter;
pub use bitstream::CapacityError;
pub use bitstream::DecodeWith;
pub use bitstream::EncodeWith;
pub use bitstream::ErrorKind;
pub use bitstream::FixedBitLen;
pub use bitstream::Layout;
pub use bitstream::SeekSource;
pub use bitstream::Sink;
pub use bitstream::Source;
pub use bitstream::BufSource;std
pub use bitstream::EncodeExt;std
pub use bitstream::SeekReader;std
pub use bitstream::SinkWriter;std
pub use bitstream::SourceReader;std
pub use bitstream::StreamBitReader;std
pub use bitstream::BytesReader;bytes
pub use bitstream::BytesWriter;bytes
pub use framing::BinCodec;tokio
pub use net::DatagramSocket;net
pub use net::MessageDatagram;net
pub use net::MessageStream;net
pub use net::MockDatagramSocket;mock
pub use net::MockStream;mock
pub use builder::BuilderError;
pub use error::UnknownDiscriminant;
pub use error::WidthError;
pub use int::UInt;
pub use int::*;

Modules§

bitstream
A bit-level stream codec — read/write fields at arbitrary bit offsets, not just byte boundaries.
builder
Support types for #[derive(BitsBuilder)] and #[bin].
codecs
Ready-made field codecs for parse_with/write_with — LEB128 varints, NUL-terminated C strings, and length-prefixed strings.
error
The crate’s checked-construction error types.
framingtokio
Async framing — a tokio_util::codec adapter (the tokio feature). Async framing via tokio_util::codec — the tokio feature.
guide
The bnb guide — worked, runnable walkthroughs.
int
Arbitrary-width unsigned integers (u1..u127) — the substrate for sub-byte bitfield fields. Replaces the arbitrary-int dependency.
netnet
Ergonomic std socket helpers — MessageStream + MessageDatagram (the net feature). Ergonomic std socket helpers — the net feature.
prelude
Common imports for the codec — the typed positioning amounts (4.bits(), 3.bytes()) used by #[br(pad_before = …)] etc., plus the EncodeExt trait that carries encode(writer) (the std feature).

Macros§

impl_bits
Implements Bits for a hand-written field type from one pair of const fn conversion bodies — the supported way to make a custom type usable as a #[bitfield] field.

Enums§

BitOrder
Bit packing order within a bitfield: does the first declared field occupy the most-significant or least-significant bits of the backing integer.
ByteOrder
Byte order of a bitfield’s backing integer when it is serialized.
WireLen
A wire length/count field: Auto-derived by default, or an explicit Set override. See the module docs.

Traits§

BitEnum
Marker trait implemented by #[derive(BitEnum)] enums: a Bits value whose representation is an integer discriminant of a fixed width.
Bitfield
The seam every #[bitfield] struct implements — the stable interface the #[bin] codec builds on, independent of how the fields are accessed.
Bits
A value that occupies a fixed number of bits within a bitfield.

Attribute Macros§

bin
#[bin] — the unified whole-message bit codec. One attribute that folds the read codec (BitDecode), the write codec (BitEncode), and a required-by-default builder (BitsBuilder) over a struct of bnb::Bits fields, read and written at arbitrary bit offsets.
bitfield
Packs the annotated struct’s fields into a single backing integer.
bitflags
Packs named single-bit flags into one backing integer, with set algebra.

Derive Macros§

BitDecode
Derives a bnb::BitDecode impl that reads the struct’s named fields, in declaration order, from a bit cursor (a bnb::BitReader).
BitEncode
Derives a bnb::BitEncode impl — the dual of BitDecode, writing the struct’s named fields in order to a bnb::BitWriter bit cursor. Shares BitDecode’s right-tool guard and #[bit_stream(...)] override.
BitEnum
Derives an enum ⇄ integer mapping of a fixed bit width.
BitsBuilder
Generates a derive_builder-style builder for a struct (or, when listed in a #[bitfield]’s derives, for the bitfield — intercepted by #[bitfield]).