bnb/guide/numbers.rs
1//! Arbitrary-width integers and the [`Bits`](crate::Bits) trait.
2//!
3//! # `u1`..`u127`
4//!
5//! Sub-byte fields need integers narrower than `u8`, and odd widths like 12 or 108
6//! bits. `bnb` provides them as type aliases over [`UInt`](crate::UInt): `u4` is
7//! `UInt<u8, 4>`, `u12` is `UInt<u16, 12>`, `u108` is `UInt<u128, 108>`, and so on —
8//! each backed by the smallest primitive that holds it. The native widths (`u8`,
9//! `u16`, `u32`, `u64`, `u128`) are the standard library's and need no wrapper.
10//!
11//! A value is always in range `0..=MAX`:
12//!
13//! ```
14//! use bnb::u5;
15//!
16//! assert_eq!(u5::MAX.value(), 31); // 2^5 - 1
17//! assert_eq!(u5::MIN.value(), 0);
18//! let x = u5::new(17); // checked: panics if > 31
19//! assert_eq!(x.value(), 17);
20//! ```
21//!
22//! ## Constructing values
23//!
24//! Pick the constructor by how you want out-of-range input handled:
25//!
26//! ```
27//! use bnb::u4;
28//!
29//! let a = u4::new(0xA); // panics on overflow — for known-good consts
30//! let b = u4::try_new(0x10); // Result — for untrusted input
31//! assert!(b.is_err());
32//! let c = u4::from_raw(0xFF); // masks to the low 4 bits — never fails
33//! assert_eq!(c.value(), 0xF);
34//! assert_eq!(u4::default().value(), 0); // zero
35//! ```
36//!
37//! ## Converting to and from the backing primitive
38//!
39//! ```
40//! use bnb::u12;
41//!
42//! let v = u12::new(0xABC);
43//! let raw: u16 = v.into(); // widening is infallible
44//! assert_eq!(raw, 0xABC);
45//! assert_eq!(u12::try_from(0xABCu16).unwrap(), v); // narrowing is checked
46//! assert!(u12::try_from(0x1000u16).is_err());
47//! ```
48//!
49//! # The `Bits` trait — why everything composes
50//!
51//! [`Bits`](crate::Bits) is the one interface the whole crate is built on: a value
52//! that occupies a fixed number of bits. Its contract is just two methods around a
53//! `u128` carrier — `into_bits` (the value in the low `BITS` bits) and `from_bits`
54//! (reconstruct from the low `BITS` bits) — plus the `BITS` width:
55//!
56//! ```
57//! use bnb::{Bits, u4};
58//!
59//! assert_eq!(<u4 as Bits>::BITS, 4);
60//! assert_eq!(<bool as Bits>::BITS, 1);
61//! assert_eq!(<u32 as Bits>::BITS, 32);
62//!
63//! // The round-trip law every impl upholds: from_bits(x.into_bits()) == x.
64//! let x = u4::new(0xD);
65//! assert_eq!(u4::from_bits(x.into_bits()), x);
66//!
67//! // from_bits reads only the low BITS bits; higher bits are ignored.
68//! assert_eq!(u8::from_bits(0x1FF), 0xFF);
69//! assert!(!bool::from_bits(0b10));
70//! ```
71//!
72//! `bool`, the primitive integers, and every `UInt` implement `Bits` out of the box;
73//! `#[bitfield]`, `#[derive(BitEnum)]`, and `#[bitflags]` generate impls so those
74//! types nest as fields too. That single abstraction is what lets a `u5` enum sit
75//! inside a `u16` bitfield inside a byte-aligned `#[bin]` message — see
76//! [`composition`](super::composition).