Skip to main content

bnb_macros/
lib.rs

1//! Procedural macros for the [`bnb`](https://docs.rs/bnb) crate.
2//!
3//! - [`macro@bitfield`] — pack typed fields into one backing integer with
4//!   explicit bit and byte order.
5//! - [`macro@bitflags`] — pack named single-bit flags into one integer, with set
6//!   algebra.
7//! - [`macro@BitEnum`] — derive enum ⇄ integer with an optional catch-all.
8//! - [`macro@BitDecode`] / [`macro@BitEncode`] — the low-level read/write codec
9//!   derives (fields at arbitrary bit offsets).
10//! - [`macro@bin`] — the unified whole-message codec attribute, folding the codec
11//!   derives plus a builder (also dispatches tagged-union enums).
12//! - [`macro@BitsBuilder`] — a required-by-default builder.
13//!
14//! These are re-exported from `bnb`; depend on that crate, not this one.
15
16#![forbid(unsafe_code)]
17#![deny(missing_docs)]
18
19mod bitenum;
20mod bitfield;
21mod bitflags;
22mod bitstream;
23mod builder;
24
25use proc_macro::TokenStream;
26
27/// The path to the runtime crate as the crate currently being compiled refers to
28/// it, for generated code to reference.
29///
30/// The runtime is **published as `bitsandbytes`** but its **library is named `bnb`**
31/// (`[lib] name`); downstream consumers import it as `bnb` via
32/// `bnb = { package = "bitsandbytes" }`. Cargo links the library by its lib name
33/// (`bnb`) for any *non-renamed* reference — the crate's own tests/doctests/examples,
34/// `trybuild`'s temp crates, and an un-aliased `bitsandbytes = "…"` consumer — and by
35/// the *dependency key* for a `package = "…"`-renamed one. So:
36///
37/// - [`proc_macro_crate`] reporting the package name `bitsandbytes` (or `Itself`, or
38///   not found) ⇒ a non-renamed reference ⇒ emit the lib name `::bnb`. `lib.rs` carries
39///   `extern crate self as bnb;` so `::bnb` resolves inside the lib itself too.
40/// - any other name ⇒ a `package = "…"` rename ⇒ emit that key (`::#name`).
41pub(crate) fn bnb_path() -> proc_macro2::TokenStream {
42    use proc_macro_crate::{FoundCrate, crate_name};
43    match crate_name("bitsandbytes") {
44        // Renamed dependency: the key is the import name.
45        Ok(FoundCrate::Name(name)) if name != "bitsandbytes" => {
46            let ident = proc_macro2::Ident::new(&name, proc_macro2::Span::call_site());
47            quote::quote!(::#ident)
48        }
49        // Non-renamed (`Name("bitsandbytes")`), the crate itself, or unresolved: all
50        // link the library by its lib name `bnb`.
51        _ => quote::quote!(::bnb),
52    }
53}
54
55/// The `BitDecode`/`BitEncode` impls a `Bits` type carries so it's usable as a `#[bin]` field
56/// without a `#[nested]` marker — thin delegations to the bit read/write. Emitted by every
57/// `Bits`-producing macro (`#[bitfield]`, `#[derive(BitEnum)]`, `#[bitflags]`) for the type it
58/// generates; coheres because each is a concrete impl (no blanket).
59pub(crate) fn bits_leaf_codec_impl(
60    name: &syn::Ident,
61    bnb: &proc_macro2::TokenStream,
62) -> proc_macro2::TokenStream {
63    quote::quote! {
64        impl #bnb::__private::BitDecode for #name {
65            #[inline]
66            fn bit_decode<__S: #bnb::__private::Source>(
67                __r: &mut __S,
68            ) -> ::core::result::Result<Self, #bnb::__private::BitError> {
69                __r.read::<Self>()
70            }
71        }
72        impl #bnb::__private::BitEncode for #name {
73            #[inline]
74            fn bit_encode<__K: #bnb::__private::Sink>(
75                &self,
76                __w: &mut __K,
77            ) -> ::core::result::Result<(), #bnb::__private::BitError> {
78                __w.write(*self)
79            }
80        }
81        impl #bnb::__private::FixedBitLen for #name {
82            const BIT_LEN: u32 = <Self as #bnb::__private::Bits>::BITS;
83        }
84    }
85}
86
87/// Whether `ty` is exactly the bare single-segment path `name` (no qualifier, no
88/// generics) — the only shape the const-dispatch helpers may convert inline.
89fn is_bare_ident(ty: &syn::Type, name: &str) -> bool {
90    if let syn::Type::Path(p) = ty {
91        p.qself.is_none()
92            && p.path.leading_colon.is_none()
93            && p.path.segments.len() == 1
94            && p.path.segments[0].arguments.is_none()
95            && p.path.segments[0].ident == name
96    } else {
97        false
98    }
99}
100
101/// The `Bits::from_bits` contract as a `const`-evaluable expression over `#raw: u128`.
102///
103/// A `const fn` cannot call trait methods on stable Rust, so generated accessors
104/// dispatch by type instead of through `Bits`: `bool` and the primitive unsigned
105/// integers convert inline, everything else through the hidden inherent
106/// `__bnb_from_bits` pair that every `Bits`-producing macro (and `UInt`) provides.
107/// The trait impls delegate to the same pair, so the two can never disagree.
108pub(crate) fn const_from_bits(
109    ty: &syn::Type,
110    raw: &proc_macro2::TokenStream,
111) -> proc_macro2::TokenStream {
112    if is_bare_ident(ty, "bool") {
113        return quote::quote!(((#raw & 1) != 0));
114    }
115    for prim in ["u8", "u16", "u32", "u64", "u128"] {
116        if is_bare_ident(ty, prim) {
117            return quote::quote!((#raw as #ty));
118        }
119    }
120    quote::quote!(<#ty>::__bnb_from_bits(#raw))
121}
122
123/// The `Bits::into_bits` contract as a `const`-evaluable expression over a `#value`
124/// of type `ty` — the write-side dual of [`const_from_bits`].
125pub(crate) fn const_into_bits(
126    ty: &syn::Type,
127    value: &proc_macro2::TokenStream,
128) -> proc_macro2::TokenStream {
129    if is_bare_ident(ty, "bool") {
130        return quote::quote!((#value as u128));
131    }
132    for prim in ["u8", "u16", "u32", "u64", "u128"] {
133        if is_bare_ident(ty, prim) {
134            return quote::quote!((#value as u128));
135        }
136    }
137    quote::quote!(<#ty>::__bnb_into_bits(#value))
138}
139
140/// Packs the annotated struct's fields into a single backing integer.
141///
142/// ```ignore
143/// #[bitfield(u16, bits = msb, bytes = big)]
144/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
145/// struct State {
146///     opcode: u5,    // first field -> high bits (msb)
147///     flags:  Flags, // a nested bitfield
148///     rcode:  RCode, // a BitEnum; last field -> low bits
149/// }
150/// ```
151///
152/// ## Attribute arguments
153///
154/// - **backing** (first, required): the storage primitive — `u8`, `u16`, `u32`,
155///   `u64`, or `u128`. Must be at least as wide as the fields.
156/// - `bits = msb | lsb` (default `msb`): whether the first declared field lands
157///   in the high or low bits.
158/// - `bytes = big | little` (default `big`): byte order of the backing integer when
159///   serialized.
160///
161/// ## Field widths
162///
163/// A field's width is, in order of precedence: an explicit `#[bits(N)]`; an
164/// explicit `#[bits(A..=B)]` range (which also fixes its absolute offset); or,
165/// by default, `<FieldType as bnb::Bits>::BITS`. Use widths/inference for
166/// automatic layout, or ranges on **every** field for fully manual layout — the
167/// two styles cannot be mixed in one struct.
168///
169/// ## Generated API
170///
171/// `new()` (all-zero; derive `Default` yourself if you want it),
172/// `with_<field>`/`set_<field>`, `<field>()`
173/// getters, `to_raw()`/`from_raw()`, `to_be_bytes()`/`to_le_bytes()`/
174/// `from_be_bytes()`/`from_le_bytes()`, and `bnb::{Bits, Bitfield}` impls.
175///
176/// Every accessor is a **`const fn`** (`#[view]` accessors too, when their raw
177/// type is annotated — the view's `const` argument asserts it, `dynamic` opts
178/// out; see `bnb::guide::bitfields`. A custom field type is implemented with
179/// `bnb::impl_bits!` — see `bnb::Bits`).
180///
181/// ## Layout
182///
183/// The emitted struct is **`#[repr(transparent)]`** over its backing integer:
184/// its size, alignment, and ABI are exactly the backing type's. (`transparent`,
185/// not `repr(C)`, because the struct wraps a single native integer — that is
186/// the honest guarantee; before 0.3.2 the layout was formally unspecified.)
187/// Writing your own `#[repr(...)]` on the struct suppresses the generated one
188/// — e.g. `repr(C)`, or `repr(align(N))`, neither of which can combine with
189/// `transparent`.
190///
191/// See the `bnb::guide::bitfields` page for runnable examples (bit/byte order,
192/// inferred vs. ranged widths, nesting).
193#[proc_macro_attribute]
194pub fn bitfield(attr: TokenStream, item: TokenStream) -> TokenStream {
195    bitfield::expand(attr, item)
196}
197
198/// Packs named single-bit flags into one backing integer, with set algebra.
199///
200/// ```ignore
201/// #[bitflags(u8)]
202/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
203/// struct TcpFlags {
204///     fin: bool,   // bit 0 (auto, LSB-first)
205///     syn: bool,   // bit 1
206///     #[flag(5)] ack: bool, // pinned to bit 5
207/// }
208/// ```
209///
210/// ## Attribute arguments
211///
212/// - **backing** (first, required): `u8`/`u16`/`u32`/`u64`/`u128`.
213/// - `bytes = big | little` (default `big`): byte order when serialized.
214///
215/// ## Generated API
216///
217/// A `const` per flag (upper-cased: `fin` → `TcpFlags::FIN`); `empty()`/`all()`/
218/// `bits()`/`from_bits` (retains unknown bits) / `from_bits_truncate`;
219/// `contains`/`intersects`/`is_empty`/`insert`/`remove`/`toggle`/`set`;
220/// `union`/`intersection`/`difference`/`complement` (for combination
221/// consts); per-flag `fin()`/`with_fin(bool)`/`set_fin(bool)`; `iter()`; the
222/// `| & ^ - !` (+ assign) operators; and `Bits`/`Bitfield` impls so a
223/// flag set nests in a `#[bitfield]` and serializes. Everything except `iter()`
224/// and the operator impls is a **`const fn`**, and the emitted struct is
225/// **`#[repr(transparent)]`** over its backing (suppressed by writing your own
226/// `#[repr(...)]`), as for `#[bitfield]`.
227///
228/// See the `bnb::guide::flags` page for runnable examples (set algebra, iteration,
229/// retain-vs-truncate, nesting).
230#[proc_macro_attribute]
231pub fn bitflags(attr: TokenStream, item: TokenStream) -> TokenStream {
232    bitflags::expand(attr, item)
233}
234
235/// Derives an enum ⇄ integer mapping of a fixed bit width.
236///
237/// ```ignore
238/// #[derive(BitEnum, Clone, Copy, Debug, PartialEq, Eq)]
239/// #[bit_enum(u4)]
240/// enum RCode {
241///     NoError = 0,
242///     FormErr = 1,
243///     #[catch_all]
244///     Other(u4),   // preserves unknown values (dual-use)
245/// }
246/// ```
247///
248/// `#[bit_enum(uN)]` sets the width. Exactly one `#[catch_all]` tuple variant
249/// (holding a `uN`/integer) may capture unknown discriminants. Without a catch-all
250/// the variants must cover the whole width, or the enum must be declared
251/// `#[bit_enum(uN, closed)]` to assert a closed set — otherwise it is a **compile
252/// error**, because `from_bits` (the infallible codec / `#[bitfield]`-getter path)
253/// would panic on an unknown discriminant. A `closed` enum still `unreachable!`s on
254/// that path; its checked `TryFrom` rejects unknowns instead.
255///
256/// ## Generated API
257///
258/// Always: the `bnb::{Bits, BitEnum}` impls (so the enum nests in a
259/// `#[bitfield]`). For a **primitive** width (`u8`/`u16`/`u32`/`u64`/`u128`)
260/// additionally — for `num_enum` parity:
261///
262/// - `From<Enum> for uN` (every variant maps to a value);
263/// - with `#[catch_all]`: `From<uN> for Enum` (total — unknowns absorbed);
264/// - without it: `TryFrom<uN> for Enum`, erroring with `bnb::UnknownDiscriminant` on an
265///   unknown value.
266///
267/// A non-primitive width (`u4`, or even a byte-aligned `u24`) gets none of these — such an
268/// enum is only meaningful nested in a `#[bitfield]`.
269///
270/// See the `bnb::guide::enums` page for runnable examples (catch-all, `closed`, the
271/// `num_enum` parity, and nesting).
272#[proc_macro_derive(BitEnum, attributes(bit_enum, catch_all))]
273pub fn bit_enum(item: TokenStream) -> TokenStream {
274    bitenum::expand(item)
275}
276
277/// Derives a `bnb::BitDecode` impl that reads the struct's named fields, in
278/// declaration order, from a **bit** cursor (a `bnb::BitReader`).
279///
280/// ```ignore
281/// #[derive(BitDecode, BitEncode, Copy, Clone, Debug, PartialEq, Eq)]
282/// struct GenericBurst {   // a 264-bit DMR burst, fields at bit offsets
283///     p1: u108,           // bits   0..108
284///     pattern: SyncPattern,// bits 108..156 (a 48-bit #[derive(BitEnum)])
285///     p2: u108,           // bits 156..264
286/// }
287/// ```
288///
289/// Each leaf field is read with `Source::read`, so any `bnb::Bits` type
290/// (`u1`..`u127`, `#[bitfield]`, `#[derive(BitEnum)]`) works as a field — no
291/// byte-alignment, seeks, or shift glue. A field marked **`#[nested]`** is itself
292/// a `BitDecode`/`BitEncode` message and is recursed into (a fixed one's
293/// `FixedBitLen::BIT_LEN` counts toward the parent's). `[u8; N]` payloads and
294/// `#[br(count = …)]` `Vec`s are supported; a `count`-bearing message is
295/// variable-length and so does not implement `bnb::FixedBitLen`.
296///
297/// ## Which codec? (the derive steers you)
298///
299/// | Your data | Use | Why |
300/// |---|---|---|
301/// | Fields straddle byte boundaries (e.g. `108 \| 48 \| 108` bits) | **`#[derive(BitDecode/BitEncode)]`** or **`#[bin]`** | reads at arbitrary bit offsets |
302/// | A whole message (magic/counts/`Vec`/nesting/ctx/…), bit- or byte-aligned | **`#[bin]`** | the unified codec |
303/// | A run of sub-byte fields that fills one integer | **`#[bitfield]`** | one packed word with named accessors |
304///
305/// To enforce that, the bare derive **rejects an all-byte-aligned struct** at
306/// compile time (every field a whole number of bytes ⇒ the cursor never leaves byte
307/// boundaries ⇒ `#[bin]` is the better tool). Override with the struct-level
308/// `#[bit_stream(allow_byte_aligned)]` when you really mean it.
309///
310/// See the `bnb::guide::directives` page for a runnable example of each `#[br]`/`#[bw]`
311/// directive, and `bnb::guide::bin_codec` for the `#[bin]` front-end.
312#[proc_macro_derive(
313    BitDecode,
314    attributes(bit_stream, nested, br, bw, brw, reserved, reserved_with)
315)]
316pub fn bit_decode(item: TokenStream) -> TokenStream {
317    bitstream::expand_decode(item)
318}
319
320/// Derives a `bnb::BitEncode` impl — the dual of [`macro@BitDecode`], writing
321/// the struct's named fields in order to a `bnb::BitWriter` bit cursor. Shares
322/// [`BitDecode`](macro@BitDecode)'s right-tool guard and `#[bit_stream(...)]`
323/// override.
324#[proc_macro_derive(
325    BitEncode,
326    attributes(bit_stream, nested, br, bw, brw, reserved, reserved_with)
327)]
328pub fn bit_encode(item: TokenStream) -> TokenStream {
329    bitstream::expand_encode(item)
330}
331
332/// `#[bin]` — the unified whole-message bit codec. One attribute that folds the read
333/// codec ([`BitDecode`](macro@BitDecode)), the write codec
334/// ([`BitEncode`](macro@BitEncode)), and a required-by-default builder
335/// ([`BitsBuilder`](macro@BitsBuilder)) over a struct of `bnb::Bits` fields, read and
336/// written at arbitrary bit offsets.
337///
338/// ```ignore
339/// #[bin(big, validate = Frame::check)]
340/// #[derive(Debug, PartialEq)]
341/// struct Frame {
342///     version: u4,
343///     #[builder(default)] flags: u4,
344///     #[br(temp)] #[bw(calc = self.payload.len() as u16)] len: u16,
345///     #[br(count = len)] payload: Vec<u8>,
346/// }
347/// // -> Frame::{decode, decode_all, decode_iter, peek, decode_exact},
348/// //    Frame::{to_bytes, encode}, Frame::new(..), and Frame::builder()
349/// ```
350///
351/// ## Struct-level options
352///
353/// `big` / `little` or `bytes = big|little` (byte order), `bits = msb|lsb`, `magic = <expr>` (a leading
354/// constant verified on read / emitted on write), `read_only` / `write_only`
355/// (directional), `no_builder`, `forward_only` (bound decoding to a forward `Source`),
356/// `ctx(name: Ty, …)` (context from the parent), `validate = <path>` (a soundness
357/// check run by `build()` — the parser stays permissive),
358/// `map`/`try_map`/`wire`/`try_wire` (map the whole struct to/from a wire type — see
359/// `bnb::guide::mapping`), `auto_len(<field>.<nested> = count|bytes(<source>), …)`
360/// (cross-struct `WireLen` derivation), and — on a **single-field
361/// tuple newtype** — `codec = <module>` / `codec(parse = <f>, write = <f>)` (the
362/// per-type field codec: the type's wire form is owned by the fn pair; use it as a
363/// plain field anywhere, with `#[brw(variable)]` in a fixed parent).
364///
365/// `bits = msb` and `bytes = big` are the defaults, and `big`+`msb` is the natural
366/// pairing (network byte order); the declared byte order only swaps a **byte-multiple**
367/// value that *differs* from the bit order's natural layout, never a sub-byte field. See
368/// `bnb::guide::bin_codec` § "Byte order × bit order" for the rule.
369///
370/// ## Field directives
371///
372/// `#[br]`/`#[bw]`: `count`, `ctx { … }`, `temp` + `calc`, `if(…)`,
373/// `assert(<expr>[, "fmt", args…])` (a decode-time guard over this and earlier fields —
374/// the explicit opt-in strictness escape hatch; read-only, no inverse), `map`/`try_map`
375/// (+ the inverse `bw(map)`), `parse_with`/`write_with`, `pad_before/after`,
376/// `align_before/after`, `seek = <bits>`, `restore_position`, `dbg` (trace a field as it
377/// decodes), `#[bw(auto_len = count(<field>)|bytes(<field>))]` (derive a `WireLen<T>`
378/// field from a sibling target — the primary `WireLen` workflow); `#[brw(ignore)]`
379/// (neither read nor written); `#[brw(variable)]` (the
380/// field's type is a variable-length custom codec — suppresses the parent's
381/// `FixedBitLen`); `#[brw(count_prefix = <Ty>)]`
382/// on a `Vec<_>` (the length-prefixed count sugar — generates the `temp`+`calc`+`count`
383/// triad: the prefix sizes the `Vec` on read and is recomputed, **checked**, from
384/// `len()` on write; any `Bits` prefix type incl. `uN`); `#[try_str]` (render a
385/// byte-buffer field as text in `Debug`); plus `#[reserved]` / `#[reserved_with(…)]`.
386///
387/// ## On an enum — tagged-union dispatch
388///
389/// `#[bin]` also applies to an enum, dispatching on two orthogonal concepts: a
390/// **`magic`** wire constant (a byte string or width-suffixed unsigned int, read *and*
391/// written — the discriminant, or a verified signature on a tag-variant) and a **`tag`**
392/// selector taken from `ctx` (read-only, never on the wire). With per-variant `magic`s
393/// the enum reads the discriminant and matches (a single `==` for uniform widths, or a
394/// peek-and-`starts_with` for variable-width byte strings). With `#[bin(tag = <ctx-param>)]`
395/// and variant `#[bin(tag = V)]` it dispatches on the selector and writes no discriminant;
396/// the two compose (and may even be mixed in one enum — tag priority, then magic). The
397/// "nothing matched" tail is a `#[catch_all]` (preserving the unknown discriminant) or a
398/// typed no-tag/no-magic fallback variant, not both; without either a magic enum is a
399/// closed set. An optional enum-level `magic` is a leading prefix. Variants may be unit,
400/// tuple, named, or `#[nested]`. The codec also generates `decode_as_<variant>`,
401/// `peek_variant`/`<Name>Kind`, `decode_tagged`, and a `magic()`/`tag()` accessor where
402/// the discriminant is single-valued. See the `bnb::guide::dispatch` page.
403///
404/// On a struct, `#[bin]` lowers to `#[derive(BitDecode, BitEncode, BitsBuilder)]`, which
405/// stay usable directly. See the `bnb::guide::bin_codec` page for a full walkthrough and
406/// `bnb::guide::directives` for one runnable example per directive.
407#[proc_macro_attribute]
408pub fn bin(attr: TokenStream, item: TokenStream) -> TokenStream {
409    bitstream::expand_bin(attr, item)
410}
411
412/// Generates a `derive_builder`-style builder for a struct (or, when listed in a
413/// `#[bitfield]`'s derives, for the bitfield — intercepted by `#[bitfield]`).
414///
415/// ```ignore
416/// #[bitfield(u16, bits = msb)]
417/// #[derive(BitsBuilder, Clone, Copy)]
418/// struct State {
419///     opcode: u4,            // required
420///     #[builder(default)]    // optional; 0 if unset
421///     flags: u8,
422///     rcode: RCode,          // required
423/// }
424///
425/// let s = State::builder().opcode(u4::new(2)).rcode(RCode::ServFail).build()?;
426/// ```
427///
428/// Generates `Foo::builder() -> FooBuilder`, an `Option`-tracked setter per
429/// field, and `build() -> Result<Foo, bnb::BuilderError>` that errors on the
430/// first unset **required** field. A field is optional only with
431/// `#[builder(default)]` (`Default::default()` if unset) or
432/// `#[builder(default = expr)]`. Coexists with the infallible infix `with_*`.
433///
434/// On a `#[bitfield]` struct, list `#[bitfield(...)]` **above** the `#[derive]`
435/// so it intercepts this marker.
436///
437/// See the `bnb::guide::builders` page for runnable examples (required-field errors,
438/// `default`/`default = expr`, the `#[bitfield]` intercept).
439#[proc_macro_derive(BitsBuilder, attributes(builder))]
440pub fn bits_builder(item: TokenStream) -> TokenStream {
441    builder::expand_derive(item)
442}