use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::any::Any;
use core::fmt;
use crate::field::{BitOrder, Bits, ByteOrder};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BitError {
pub kind: ErrorKind,
pub at: usize,
pub field: Option<&'static str>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorKind {
UnexpectedEof {
needed: usize,
remaining: usize,
},
Incomplete {
needed: Option<usize>,
},
TrailingBytes {
remaining: usize,
},
TooWide {
width: usize,
},
#[cfg(feature = "std")]
Io(std::io::ErrorKind),
BadMagic {
expected: u128,
found: u128,
},
Convert {
message: String,
},
NotSeekable,
BufferFull {
cap: usize,
},
}
impl BitError {
#[must_use]
pub fn new(kind: ErrorKind, at: usize) -> Self {
Self {
kind,
at,
field: None,
}
}
#[must_use]
pub fn bad_magic(expected: u128, found: u128, at: usize) -> Self {
Self::new(ErrorKind::BadMagic { expected, found }, at)
}
#[must_use]
pub fn convert(message: String, at: usize) -> Self {
Self::new(ErrorKind::Convert { message }, at)
}
#[must_use]
pub fn in_field(mut self, field: &'static str) -> Self {
if self.field.is_none() {
self.field = Some(field);
}
self
}
#[must_use]
pub fn is_incomplete(&self) -> bool {
matches!(self.kind, ErrorKind::Incomplete { .. })
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for BitError {
fn from(e: std::io::Error) -> Self {
BitError::new(ErrorKind::Io(e.kind()), 0)
}
}
impl fmt::Display for BitError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
ErrorKind::UnexpectedEof { needed, remaining } => write!(
f,
"unexpected end of input: needed {needed} bits, {remaining} remain"
)?,
ErrorKind::Incomplete { needed } => match needed {
Some(n) => write!(f, "incomplete: need ~{n} more bytes")?,
None => write!(f, "incomplete: need more bytes")?,
},
ErrorKind::TrailingBytes { remaining } => {
write!(f, "{remaining} trailing bytes after the message")?;
}
ErrorKind::TooWide { width } => {
write!(f, "field width {width} exceeds the 128-bit carrier")?;
}
#[cfg(feature = "std")]
ErrorKind::Io(kind) => write!(f, "I/O error: {kind:?}")?,
ErrorKind::BadMagic { expected, found } => {
write!(f, "bad magic: expected {expected:#x}, found {found:#x}")?;
}
ErrorKind::Convert { message } => {
write!(f, "conversion failed: {message}")?;
}
ErrorKind::NotSeekable => {
write!(f, "a position directive ran on a non-seekable source")?;
}
ErrorKind::BufferFull { cap } => {
write!(f, "buffered source exceeded its {cap}-byte cap")?;
}
}
write!(f, " at bit {}", self.at)?;
if let Some(field) = self.field {
write!(f, " (field `{field}`)")?;
}
Ok(())
}
}
impl core::error::Error for BitError {}
impl From<crate::error::Error> for BitError {
#[inline]
fn from(e: crate::error::Error) -> Self {
BitError::convert(e.to_string(), 0)
}
}
#[doc(hidden)]
#[must_use]
pub const fn bits_of<T: Bits>(_value: &T) -> u32 {
T::BITS
}
#[doc(hidden)]
pub fn verify_magic<T: Bits, S: Source>(r: &mut S, expected: T) -> Result<(), BitError> {
let at = r.bit_pos();
let found: T = r.read()?;
let (e, g) = (expected.into_bits(), found.into_bits());
if e != g {
return Err(BitError::bad_magic(e, g, at));
}
Ok(())
}
#[doc(hidden)]
pub fn read_mapped<W, T, S, F>(r: &mut S, f: F) -> Result<T, BitError>
where
W: Bits,
S: Source,
F: FnOnce(W) -> T,
{
let raw: W = r.read()?;
Ok(f(raw))
}
#[doc(hidden)]
pub fn read_try_mapped<W, T, E, S, F>(r: &mut S, f: F) -> Result<T, BitError>
where
W: Bits,
S: Source,
E: fmt::Display,
F: FnOnce(W) -> Result<T, E>,
{
let at = r.bit_pos();
let raw: W = r.read()?;
f(raw).map_err(|e| BitError::convert(e.to_string(), at))
}
#[doc(hidden)]
pub fn write_mapped<W, T, K, F>(w: &mut K, value: &T, f: F) -> Result<(), BitError>
where
W: Bits,
K: Sink,
F: FnOnce(&T) -> W,
{
w.write(f(value))
}
#[doc(hidden)]
pub fn decode_mapped_msg<W, T, S, F>(r: &mut S, f: F) -> Result<T, BitError>
where
W: BitDecode,
S: Source,
F: FnOnce(W) -> T,
{
Ok(f(W::bit_decode(r)?))
}
#[doc(hidden)]
pub fn decode_try_mapped_msg<W, T, E, S, F>(r: &mut S, f: F) -> Result<T, BitError>
where
W: BitDecode,
S: Source,
E: fmt::Display,
F: FnOnce(W) -> Result<T, E>,
{
let at = r.bit_pos();
let w = W::bit_decode(r)?;
f(w).map_err(|e| BitError::convert(e.to_string(), at))
}
#[doc(hidden)]
pub fn encode_mapped_msg<W, T, K, F>(w: &mut K, value: &T, f: F) -> Result<(), BitError>
where
W: BitEncode,
K: Sink,
F: FnOnce(&T) -> W,
{
f(value).bit_encode(w)
}
pub trait BitAmount: Copy {
fn bits(self) -> u32;
fn bytes(self) -> u32;
}
macro_rules! impl_bit_amount {
($($t:ty),*) => {$(
impl BitAmount for $t {
fn bits(self) -> u32 { self as u32 }
fn bytes(self) -> u32 { (self as u32) * 8 }
}
)*};
}
impl_bit_amount!(u8, u16, u32, u64, usize, i16, i32, i64, isize);
#[doc(hidden)]
pub fn skip_read<S: Source>(r: &mut S, bits: u32) -> Result<(), BitError> {
let mut left = bits;
while left > 0 {
let n = left.min(128);
r.read_bits(n)?;
left -= n;
}
Ok(())
}
#[doc(hidden)]
pub fn skip_write<K: Sink>(w: &mut K, bits: u32) -> Result<(), BitError> {
let mut left = bits;
while left > 0 {
let n = left.min(128);
w.write_bits(0, n)?;
left -= n;
}
Ok(())
}
#[doc(hidden)]
pub fn align_read<S: Source>(r: &mut S) -> Result<(), BitError> {
let pad = (8 - (r.bit_pos() % 8)) % 8;
skip_read(r, pad as u32)
}
#[doc(hidden)]
pub fn align_write<K: Sink>(w: &mut K) -> Result<(), BitError> {
let pad = (8 - (w.bit_pos() % 8)) % 8;
skip_write(w, pad as u32)
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Layout {
pub bit: BitOrder,
pub byte: ByteOrder,
}
#[inline]
fn apply_byte_order(raw: u128, bits: u32, bit: BitOrder, byte: ByteOrder) -> u128 {
let natural = match bit {
BitOrder::Msb => ByteOrder::Big,
BitOrder::Lsb => ByteOrder::Little,
};
if byte == natural || bits % 8 != 0 {
return raw;
}
let n = (bits / 8) as usize;
let le = raw.to_le_bytes();
let mut out = 0u128;
let mut i = 0;
while i < n {
out |= (le[i] as u128) << (8 * (n - 1 - i));
i += 1;
}
out
}
#[inline]
fn extract_bits(buf: &[u8], pos: usize, n: usize, order: BitOrder) -> u128 {
if pos % 8 == 0 && n % 8 == 0 {
let start = pos / 8;
let nbytes = n / 8;
let mut acc = 0u128;
match order {
BitOrder::Msb => {
for j in 0..nbytes {
acc = (acc << 8) | u128::from(buf[start + j]);
}
}
BitOrder::Lsb => {
for j in 0..nbytes {
acc |= u128::from(buf[start + j]) << (8 * j);
}
}
}
return acc;
}
let mut acc = 0u128;
match order {
BitOrder::Msb => {
for k in 0..n {
let p = pos + k;
acc = (acc << 1) | u128::from((buf[p >> 3] >> (7 - (p & 7))) & 1);
}
}
BitOrder::Lsb => {
for k in 0..n {
let p = pos + k;
acc |= u128::from((buf[p >> 3] >> (p & 7)) & 1) << k;
}
}
}
acc
}
#[inline]
fn emit_bits(out: &mut Vec<u8>, bit_pos: usize, value: u128, n: usize, order: BitOrder) {
if n % 8 == 0 && bit_pos % 8 == 0 && bit_pos / 8 == out.len() {
let nbytes = n / 8;
match order {
BitOrder::Msb => {
for j in 0..nbytes {
out.push((value >> (8 * (nbytes - 1 - j))) as u8);
}
}
BitOrder::Lsb => {
for j in 0..nbytes {
out.push((value >> (8 * j)) as u8);
}
}
}
return;
}
for k in 0..n {
let p = bit_pos + k;
let (i, shift) = match order {
BitOrder::Msb => (n - 1 - k, 7 - (p & 7)),
BitOrder::Lsb => (k, p & 7),
};
let byte_idx = p >> 3;
if byte_idx == out.len() {
out.push(0);
}
if (value >> i) & 1 != 0 {
out[byte_idx] |= 1 << shift;
}
}
}
#[derive(Clone, Debug)]
pub struct BitReader<'a> {
bytes: &'a [u8],
bit_pos: usize,
order: BitOrder,
byte: ByteOrder,
}
impl<'a> BitReader<'a> {
#[must_use]
pub fn new(bytes: &'a [u8]) -> Self {
Self::with_order(bytes, BitOrder::Msb)
}
#[must_use]
pub fn with_order(bytes: &'a [u8], order: BitOrder) -> Self {
Self::with_layout(
bytes,
Layout {
bit: order,
byte: ByteOrder::Big,
},
)
}
#[must_use]
pub fn with_layout(bytes: &'a [u8], layout: Layout) -> Self {
Self {
bytes,
bit_pos: 0,
order: layout.bit,
byte: layout.byte,
}
}
#[must_use]
pub fn bit_pos(&self) -> usize {
self.bit_pos
}
#[must_use]
pub fn remaining_bits(&self) -> usize {
self.bytes.len() * 8 - self.bit_pos
}
#[inline]
pub fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
let n = n as usize;
if n > 128 {
return Err(BitError::new(ErrorKind::TooWide { width: n }, self.bit_pos));
}
if n > self.remaining_bits() {
return Err(BitError::new(
ErrorKind::UnexpectedEof {
needed: n,
remaining: self.remaining_bits(),
},
self.bit_pos,
));
}
let acc = extract_bits(self.bytes, self.bit_pos, n, self.order);
self.bit_pos += n;
Ok(acc)
}
#[inline]
pub fn read<T: Bits>(&mut self) -> Result<T, BitError> {
let raw = self.read_bits(T::BITS)?;
Ok(T::from_bits(apply_byte_order(
raw,
T::BITS,
self.order,
self.byte,
)))
}
pub fn seek_to_bit(&mut self, pos: usize) -> Result<(), BitError> {
let end = self.bytes.len() * 8;
if pos > end {
return Err(BitError::new(
ErrorKind::UnexpectedEof {
needed: pos,
remaining: end,
},
self.bit_pos,
));
}
self.bit_pos = pos;
Ok(())
}
pub fn align_to_byte(&mut self) {
self.bit_pos = (self.bit_pos + 7) & !7;
}
}
#[derive(Default)]
pub struct BitWriter {
bytes: Vec<u8>,
bit_pos: usize,
order: BitOrder,
byte: ByteOrder,
scratch: Option<Box<dyn Any>>,
}
impl Clone for BitWriter {
fn clone(&self) -> Self {
Self {
bytes: self.bytes.clone(),
bit_pos: self.bit_pos,
order: self.order,
byte: self.byte,
scratch: None,
}
}
}
impl fmt::Debug for BitWriter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BitWriter")
.field("bytes", &self.bytes)
.field("bit_pos", &self.bit_pos)
.field("order", &self.order)
.field("byte", &self.byte)
.field("scratch", &self.scratch.is_some())
.finish()
}
}
impl BitWriter {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_order(order: BitOrder) -> Self {
Self::with_layout(Layout {
bit: order,
byte: ByteOrder::Big,
})
}
#[must_use]
pub fn with_layout(layout: Layout) -> Self {
Self {
bytes: Vec::new(),
bit_pos: 0,
order: layout.bit,
byte: layout.byte,
scratch: None,
}
}
#[must_use]
pub fn with_scratch(mut self, scratch: Box<dyn Any>) -> Self {
self.scratch = Some(scratch);
self
}
#[must_use]
pub fn bit_len(&self) -> usize {
self.bit_pos
}
#[inline]
pub fn write_bits(&mut self, value: u128, n: u32) -> Result<(), BitError> {
let n = n as usize;
if n > 128 {
return Err(BitError::new(ErrorKind::TooWide { width: n }, self.bit_pos));
}
emit_bits(&mut self.bytes, self.bit_pos, value, n, self.order);
self.bit_pos += n;
Ok(())
}
#[inline]
pub fn write<T: Bits>(&mut self, value: T) -> Result<(), BitError> {
let raw = apply_byte_order(value.into_bits(), T::BITS, self.order, self.byte);
self.write_bits(raw, T::BITS)
}
#[must_use]
pub fn into_bytes(self) -> Vec<u8> {
self.bytes
}
}
pub trait Source {
fn read_bits(&mut self, n: u32) -> Result<u128, BitError>;
fn bit_pos(&self) -> usize;
fn byte_order(&self) -> ByteOrder {
ByteOrder::Big
}
fn bit_order(&self) -> BitOrder {
BitOrder::Msb
}
fn seek_to_bit(&mut self, _pos: usize) -> Result<(), BitError> {
Err(BitError::new(ErrorKind::NotSeekable, self.bit_pos()))
}
#[inline]
fn read<T: Bits>(&mut self) -> Result<T, BitError> {
let raw = self.read_bits(T::BITS)?;
Ok(T::from_bits(apply_byte_order(
raw,
T::BITS,
self.bit_order(),
self.byte_order(),
)))
}
fn read_bytes(&mut self, n: usize) -> Result<alloc::vec::Vec<u8>, BitError> {
let mut v = alloc::vec::Vec::new();
for _ in 0..n {
v.push(self.read::<u8>()?);
}
Ok(v)
}
fn read_into(&mut self, buf: &mut [u8]) -> Result<(), BitError> {
for slot in buf.iter_mut() {
*slot = self.read::<u8>()?;
}
Ok(())
}
#[cfg(feature = "std")]
fn as_read(&mut self) -> SourceReader<'_, Self>
where
Self: Sized,
{
SourceReader(self)
}
}
#[cfg(feature = "std")]
pub struct SourceReader<'a, S: Source>(&'a mut S);
#[cfg(feature = "std")]
impl<S: Source> std::io::Read for SourceReader<'_, S> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
for (i, slot) in buf.iter_mut().enumerate() {
match self.0.read_bits(8) {
Ok(b) => *slot = b as u8,
Err(e) if i == 0 => {
let kind = match e.kind {
ErrorKind::UnexpectedEof { .. } | ErrorKind::Incomplete { .. } => {
std::io::ErrorKind::UnexpectedEof
}
_ => std::io::ErrorKind::InvalidData,
};
return Err(std::io::Error::new(kind, e.to_string()));
}
Err(_) => return Ok(i),
}
}
Ok(buf.len())
}
}
pub trait SeekSource: Source {}
impl SeekSource for BitReader<'_> {}
pub trait Sink {
fn write_bits(&mut self, value: u128, n: u32) -> Result<(), BitError>;
fn bit_pos(&self) -> usize;
fn byte_order(&self) -> ByteOrder {
ByteOrder::Big
}
fn bit_order(&self) -> BitOrder {
BitOrder::Msb
}
fn scratch(&mut self) -> Option<&mut dyn Any> {
None
}
#[inline]
fn write<T: Bits>(&mut self, value: T) -> Result<(), BitError> {
let raw = apply_byte_order(
value.into_bits(),
T::BITS,
self.bit_order(),
self.byte_order(),
);
self.write_bits(raw, T::BITS)
}
fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), BitError> {
for &b in bytes {
self.write(b)?;
}
Ok(())
}
#[cfg(feature = "std")]
fn as_write(&mut self) -> SinkWriter<'_, Self>
where
Self: Sized,
{
SinkWriter(self)
}
}
#[cfg(feature = "std")]
pub struct SinkWriter<'a, K: Sink>(&'a mut K);
#[cfg(feature = "std")]
impl<K: Sink> std::io::Write for SinkWriter<'_, K> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
for &b in buf {
self.0
.write_bits(u128::from(b), 8)
.map_err(|e| std::io::Error::other(e.to_string()))?;
}
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Source for BitReader<'_> {
#[inline]
fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
BitReader::read_bits(self, n)
}
#[inline]
fn bit_pos(&self) -> usize {
self.bit_pos
}
#[inline]
fn byte_order(&self) -> ByteOrder {
self.byte
}
#[inline]
fn bit_order(&self) -> BitOrder {
self.order
}
#[inline]
fn seek_to_bit(&mut self, pos: usize) -> Result<(), BitError> {
BitReader::seek_to_bit(self, pos)
}
}
impl Sink for BitWriter {
#[inline]
fn write_bits(&mut self, value: u128, n: u32) -> Result<(), BitError> {
BitWriter::write_bits(self, value, n)
}
#[inline]
fn bit_pos(&self) -> usize {
self.bit_pos
}
#[inline]
fn byte_order(&self) -> ByteOrder {
self.byte
}
#[inline]
fn bit_order(&self) -> BitOrder {
self.order
}
#[inline]
fn scratch(&mut self) -> Option<&mut dyn Any> {
self.scratch.as_deref_mut()
}
}
pub trait BitDecode: Sized {
fn bit_decode<S: Source>(r: &mut S) -> Result<Self, BitError>;
}
pub trait FixedBitLen {
const BIT_LEN: u32;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum EncodeMode {
#[default]
Verbatim,
Canonical,
}
pub trait BitEncode {
const LAYOUT: Layout = Layout {
bit: BitOrder::Msb,
byte: ByteOrder::Big,
};
fn bit_encode<K: Sink>(&self, w: &mut K) -> Result<(), BitError>;
fn canonical_bit_encode<K: Sink>(&self, w: &mut K) -> Result<(), BitError> {
self.bit_encode(w)
}
fn encode_mode(&self) -> EncodeMode {
EncodeMode::Verbatim
}
}
macro_rules! bits_leaf_codec {
($($t:ty),* $(,)?) => {$(
impl BitDecode for $t {
#[inline]
fn bit_decode<S: Source>(r: &mut S) -> Result<Self, BitError> {
r.read::<$t>()
}
}
impl BitEncode for $t {
#[inline]
fn bit_encode<K: Sink>(&self, w: &mut K) -> Result<(), BitError> {
w.write(*self)
}
}
impl FixedBitLen for $t {
const BIT_LEN: u32 = <$t as Bits>::BITS;
}
)*};
}
bits_leaf_codec!(u8, u16, u32, u64, u128, bool);
macro_rules! ip_addr_codec {
($($t:ty => $int:ty, $bits:expr);* $(;)?) => {$(
#[cfg(feature = "std")]
impl BitDecode for $t {
#[inline]
fn bit_decode<S: Source>(r: &mut S) -> Result<Self, BitError> {
Ok(<$t>::from_bits(r.read::<$int>()?))
}
}
#[cfg(feature = "std")]
impl BitEncode for $t {
#[inline]
fn bit_encode<K: Sink>(&self, w: &mut K) -> Result<(), BitError> {
w.write(self.to_bits())
}
}
#[cfg(feature = "std")]
impl FixedBitLen for $t {
const BIT_LEN: u32 = $bits;
}
)*};
}
ip_addr_codec!(
std::net::Ipv4Addr => u32, 32;
std::net::Ipv6Addr => u128, 128;
);
impl<T, const N: usize> BitDecode for crate::int::UInt<T, N>
where
crate::int::UInt<T, N>: Bits,
{
#[inline]
fn bit_decode<S: Source>(r: &mut S) -> Result<Self, BitError> {
r.read::<Self>()
}
}
impl<T, const N: usize> BitEncode for crate::int::UInt<T, N>
where
crate::int::UInt<T, N>: Bits,
{
#[inline]
fn bit_encode<K: Sink>(&self, w: &mut K) -> Result<(), BitError> {
w.write(*self)
}
}
impl<T, const N: usize> FixedBitLen for crate::int::UInt<T, N>
where
crate::int::UInt<T, N>: Bits,
{
const BIT_LEN: u32 = <Self as Bits>::BITS;
}
pub(crate) mod sealed {
pub trait Sealed {}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be a `count_prefix` type",
note = "supported prefix types: u8, u16, u32, u64, u128 and the arbitrary-width `uN` aliases (e.g. `u12`)",
note = "this trait is sealed — the supported prefix types are built in"
)]
pub trait CountPrefix: Bits + sealed::Sealed {
fn try_from_len(len: usize) -> crate::error::Result<Self>
where
Self: Sized;
fn to_count(self) -> usize;
}
macro_rules! count_prefix_prim {
($($t:ty),* $(,)?) => {$(
impl sealed::Sealed for $t {}
impl CountPrefix for $t {
#[inline]
fn try_from_len(len: usize) -> crate::error::Result<Self> {
<$t>::try_from(len).map_err(|_| crate::error::Error::ValueTooLarge {
value: len as u128,
bits: <$t>::BITS,
})
}
#[inline]
fn to_count(self) -> usize {
self as usize
}
}
)*};
}
count_prefix_prim!(u8, u16, u32, u64, u128);
macro_rules! count_prefix_uint {
($($t:ty),* $(,)?) => {$(
impl<const N: usize> sealed::Sealed for crate::int::UInt<$t, N> {}
impl<const N: usize> CountPrefix for crate::int::UInt<$t, N> {
#[inline]
fn try_from_len(len: usize) -> crate::error::Result<Self> {
let wide = len as u128;
if wide > Self::MASK as u128 {
return Err(crate::error::Error::ValueTooLarge {
value: wide,
bits: N as u32,
});
}
Ok(Self::from_raw(len as $t))
}
#[inline]
fn to_count(self) -> usize {
self.value() as usize
}
}
)*};
}
count_prefix_uint!(u8, u16, u32, u64, u128);
#[cfg(feature = "std")]
pub trait EncodeExt: BitEncode {
fn encode<W: std::io::Write>(&self, w: &mut W) -> Result<(), BitError>
where
Self: Sized,
{
match self.encode_mode() {
EncodeMode::Verbatim => {
encode_to_writer_with(w, Self::LAYOUT, |bw| self.bit_encode(bw))
}
EncodeMode::Canonical => {
encode_to_writer_with(w, Self::LAYOUT, |bw| self.canonical_bit_encode(bw))
}
}
}
}
#[cfg(feature = "std")]
impl<T: BitEncode> EncodeExt for T {}
pub trait DecodeWith<A>: Sized {
fn decode_with<S: Source>(r: &mut S, args: A) -> Result<Self, BitError>;
}
pub trait EncodeWith<A> {
fn encode_with<K: Sink>(&self, w: &mut K, args: A) -> Result<(), BitError>;
}
impl<T: BitDecode> DecodeWith<()> for T {
fn decode_with<S: Source>(r: &mut S, _args: ()) -> Result<Self, BitError> {
T::bit_decode(r)
}
}
impl<T: BitEncode> EncodeWith<()> for T {
fn encode_with<K: Sink>(&self, w: &mut K, _args: ()) -> Result<(), BitError> {
self.bit_encode(w)
}
}
#[doc(hidden)]
pub fn decode_all<T: BitDecode>(bytes: &[u8], layout: Layout) -> Result<Vec<T>, BitError> {
let mut r = BitReader::with_layout(bytes, layout);
let mut out = Vec::new();
while r.remaining_bits() > 0 {
let before = r.bit_pos();
let item = T::bit_decode(&mut r)?;
if r.bit_pos() == before {
break; }
out.push(item);
}
Ok(out)
}
#[doc(hidden)]
pub fn decode_iter<T: BitDecode>(
bytes: &[u8],
layout: Layout,
) -> impl Iterator<Item = Result<T, BitError>> + '_ {
let mut r = BitReader::with_layout(bytes, layout);
let mut stopped = false;
core::iter::from_fn(move || {
if stopped || r.remaining_bits() == 0 {
return None;
}
let before = r.bit_pos();
match T::bit_decode(&mut r) {
Ok(v) => {
stopped = r.bit_pos() == before; Some(Ok(v))
}
Err(e) => {
stopped = true;
Some(Err(e))
}
}
})
}
#[doc(hidden)]
pub fn decode_peek<T: BitDecode>(bytes: &[u8], layout: Layout) -> Result<T, BitError> {
T::bit_decode(&mut BitReader::with_layout(bytes, layout))
}
#[doc(hidden)]
pub fn decode_peek_with<T, F>(bytes: &[u8], layout: Layout, f: F) -> Result<T, BitError>
where
F: FnOnce(&mut BitReader) -> Result<T, BitError>,
{
f(&mut BitReader::with_layout(bytes, layout))
}
#[doc(hidden)]
pub fn decode_exact_with<T, F>(bytes: &[u8], layout: Layout, f: F) -> Result<T, BitError>
where
F: FnOnce(&mut BitReader) -> Result<T, BitError>,
{
let mut r = BitReader::with_layout(bytes, layout);
let v = f(&mut r)?;
let consumed = r.bit_pos().div_ceil(8);
if consumed < bytes.len() {
return Err(BitError::new(
ErrorKind::TrailingBytes {
remaining: bytes.len() - consumed,
},
r.bit_pos(),
));
}
Ok(v)
}
#[doc(hidden)]
pub fn encode_to_vec_with<F>(layout: Layout, f: F) -> Result<Vec<u8>, BitError>
where
F: FnOnce(&mut BitWriter) -> Result<(), BitError>,
{
let mut w = BitWriter::with_layout(layout);
f(&mut w)?;
Ok(w.into_bytes())
}
#[doc(hidden)]
pub fn decode_exact<T: BitDecode>(bytes: &[u8], layout: Layout) -> Result<T, BitError> {
let mut r = BitReader::with_layout(bytes, layout);
let v = T::bit_decode(&mut r)?;
let consumed = r.bit_pos().div_ceil(8);
if consumed < bytes.len() {
return Err(BitError::new(
ErrorKind::TrailingBytes {
remaining: bytes.len() - consumed,
},
r.bit_pos(),
));
}
Ok(v)
}
#[doc(hidden)]
pub fn encode_to_vec<T: BitEncode>(value: &T, layout: Layout) -> Result<Vec<u8>, BitError> {
let mut w = BitWriter::with_layout(layout);
value.bit_encode(&mut w)?;
Ok(w.into_bytes())
}
#[cfg(feature = "std")]
#[doc(hidden)]
pub fn encode_to_writer_with<W, F>(w: &mut W, layout: Layout, f: F) -> Result<(), BitError>
where
W: std::io::Write,
F: FnOnce(&mut BitWriter) -> Result<(), BitError>,
{
let mut bw = BitWriter::with_layout(layout);
f(&mut bw)?;
let at = bw.bit_len();
w.write_all(&bw.into_bytes())
.map_err(|e| BitError::new(ErrorKind::Io(e.kind()), at))
}
#[doc(hidden)]
pub fn read_byte_array<const N: usize, S: Source>(r: &mut S) -> Result<[u8; N], BitError> {
let mut arr = [0u8; N];
for b in &mut arr {
*b = r.read_bits(8)? as u8;
}
Ok(arr)
}
#[doc(hidden)]
pub fn peek_bytes<S: Source>(r: &mut S, max: usize) -> Result<Vec<u8>, BitError> {
let start = r.bit_pos();
let mut out = Vec::with_capacity(max);
for _ in 0..max {
match r.read_bits(8) {
Ok(b) => out.push(b as u8),
Err(_) => break, }
}
r.seek_to_bit(start)?;
Ok(out)
}
#[doc(hidden)]
pub fn write_byte_array<const N: usize, K: Sink>(arr: &[u8; N], w: &mut K) -> Result<(), BitError> {
for &b in arr {
w.write_bits(u128::from(b), 8)?;
}
Ok(())
}
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct StreamBitReader<R> {
inner: R,
lead: u32,
lead_bits: u32,
pos: usize,
layout: Layout,
}
#[cfg(feature = "std")]
impl<R: std::io::Read> StreamBitReader<R> {
pub fn new(inner: R) -> Self {
Self::with_layout(inner, Layout::default())
}
pub fn with_layout(inner: R, layout: Layout) -> Self {
Self {
inner,
lead: 0,
lead_bits: 0,
pos: 0,
layout,
}
}
#[must_use]
pub fn bit_pos(&self) -> usize {
self.pos
}
pub fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
if n > 128 {
return Err(BitError::new(
ErrorKind::TooWide { width: n as usize },
self.pos,
));
}
let at = self.pos;
let mut result: u128 = 0;
let mut need = n;
while need > 0 {
if self.lead_bits == 0 {
let mut b = [0u8; 1];
if self.inner.read_exact(&mut b).is_err() {
return Err(BitError::new(ErrorKind::Incomplete { needed: None }, at));
}
self.lead = u32::from(b[0]);
self.lead_bits = 8;
}
let take = need.min(self.lead_bits);
let shift = self.lead_bits - take;
let chunk = (self.lead >> shift) & ((1u32 << take) - 1);
result = (result << take) | u128::from(chunk);
self.lead_bits -= take;
self.lead &= (1u32 << self.lead_bits) - 1; need -= take;
}
self.pos += n as usize;
Ok(result)
}
pub fn read<T: Bits>(&mut self) -> Result<T, BitError> {
Ok(T::from_bits(self.read_bits(T::BITS)?))
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read> Source for StreamBitReader<R> {
fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
StreamBitReader::read_bits(self, n)
}
fn bit_pos(&self) -> usize {
self.pos
}
fn byte_order(&self) -> ByteOrder {
self.layout.byte
}
fn bit_order(&self) -> BitOrder {
self.layout.bit
}
}
#[cfg(feature = "std")]
#[derive(Clone, Debug)]
pub struct BufSource<R> {
inner: R,
buf: Vec<u8>,
bit_pos: usize,
cap: usize,
layout: Layout,
eof: bool,
}
#[cfg(feature = "std")]
impl<R: std::io::Read> BufSource<R> {
#[must_use]
pub fn new(inner: R) -> Self {
Self::with_cap(inner, 64 * 1024)
}
#[must_use]
pub fn with_cap(inner: R, cap: usize) -> Self {
Self::with_cap_and_layout(inner, cap, Layout::default())
}
#[must_use]
pub fn with_cap_and_layout(inner: R, cap: usize, layout: Layout) -> Self {
Self {
inner,
buf: Vec::new(),
bit_pos: 0,
cap,
layout,
eof: false,
}
}
fn fill_to(&mut self, byte_end: usize) -> Result<(), BitError> {
while self.buf.len() < byte_end && !self.eof {
if self.buf.len() >= self.cap {
return Err(BitError::new(
ErrorKind::BufferFull { cap: self.cap },
self.bit_pos,
));
}
let want = (byte_end - self.buf.len()).min(self.cap - self.buf.len());
let start = self.buf.len();
self.buf.resize(start + want, 0);
match self.inner.read(&mut self.buf[start..]) {
Ok(0) => {
self.buf.truncate(start);
self.eof = true;
}
Ok(got) => self.buf.truncate(start + got),
Err(e) => {
self.buf.truncate(start);
return Err(BitError::new(ErrorKind::Io(e.kind()), self.bit_pos));
}
}
}
Ok(())
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read> Source for BufSource<R> {
fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
if n > 128 {
return Err(BitError::new(
ErrorKind::TooWide { width: n as usize },
self.bit_pos,
));
}
let byte_end = (self.bit_pos + n as usize).div_ceil(8);
self.fill_to(byte_end)?;
if self.buf.len() < byte_end {
return Err(BitError::new(
ErrorKind::Incomplete {
needed: Some(byte_end - self.buf.len()),
},
self.bit_pos,
));
}
let acc = extract_bits(&self.buf, self.bit_pos, n as usize, self.layout.bit);
self.bit_pos += n as usize;
Ok(acc)
}
fn bit_pos(&self) -> usize {
self.bit_pos
}
fn byte_order(&self) -> ByteOrder {
self.layout.byte
}
fn bit_order(&self) -> BitOrder {
self.layout.bit
}
fn seek_to_bit(&mut self, pos: usize) -> Result<(), BitError> {
self.bit_pos = pos;
Ok(())
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read> SeekSource for BufSource<R> {}
#[derive(Debug, Default, Clone)]
pub struct BitBuf {
buf: Vec<u8>,
cursor: usize,
cap: Option<usize>,
layout: Layout,
}
impl BitBuf {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_capacity(cap: usize) -> Self {
Self {
buf: Vec::with_capacity(cap),
cursor: 0,
cap: None,
layout: Layout::default(),
}
}
#[must_use]
pub fn bounded(cap: usize) -> Self {
Self {
buf: Vec::with_capacity(cap),
cursor: 0,
cap: Some(cap),
layout: Layout::default(),
}
}
#[must_use]
pub fn with_layout(mut self, layout: Layout) -> Self {
self.layout = layout;
self
}
#[must_use]
pub fn capacity(&self) -> Option<usize> {
self.cap
}
fn make_room(&mut self, additional: usize) {
let dead = self.cursor / 8;
if dead == 0 {
return;
}
let live = self.buf.len() - dead;
let would_grow = self.buf.len() + additional > self.buf.capacity();
if would_grow || dead >= live {
self.buf.drain(..dead);
self.cursor -= dead * 8;
}
}
pub fn push(&mut self, bytes: &[u8]) {
self.make_room(bytes.len());
self.buf.extend_from_slice(bytes);
}
pub fn try_push(&mut self, bytes: &[u8]) -> Result<(), CapacityError> {
let live = self.buf.len() - self.cursor / 8;
if let Some(cap) = self.cap {
if live + bytes.len() > cap {
return Err(CapacityError {
cap,
requested: live + bytes.len(),
});
}
}
self.make_room(bytes.len());
self.buf.extend_from_slice(bytes);
Ok(())
}
pub fn grow(&mut self, additional: usize) {
if let Some(cap) = &mut self.cap {
*cap += additional;
}
self.buf.reserve(additional);
}
#[must_use]
pub fn bit_len(&self) -> usize {
self.buf.len() * 8 - self.cursor
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bit_len() == 0
}
pub fn clear(&mut self) {
self.buf.clear();
self.cursor = 0;
}
pub fn compact(&mut self) {
let whole = self.cursor / 8;
self.buf.drain(..whole);
self.cursor -= whole * 8;
}
pub fn pull<T: BitDecode + BitEncode>(&mut self) -> Result<Option<T>, BitError> {
if self.cursor >= self.buf.len() * 8 {
return Ok(None);
}
let mut r = BitReader::with_layout(&self.buf, <T as BitEncode>::LAYOUT);
r.seek_to_bit(self.cursor)?;
match T::bit_decode(&mut r) {
Ok(msg) => {
self.cursor = r.bit_pos(); Ok(Some(msg))
}
Err(e)
if matches!(
e.kind,
ErrorKind::UnexpectedEof { .. } | ErrorKind::Incomplete { .. }
) =>
{
Ok(None)
}
Err(e) => Err(e),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct CapacityError {
pub cap: usize,
pub requested: usize,
}
impl fmt::Display for CapacityError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"bitbuf is full: {} bytes needed exceeds the {}-byte capacity",
self.requested, self.cap
)
}
}
impl core::error::Error for CapacityError {}
impl Source for BitBuf {
fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
let mut r = BitReader::with_layout(&self.buf, self.layout);
r.seek_to_bit(self.cursor)?;
let v = r.read_bits(n)?;
self.cursor = r.bit_pos();
Ok(v)
}
fn bit_pos(&self) -> usize {
self.cursor
}
fn byte_order(&self) -> ByteOrder {
self.layout.byte
}
fn bit_order(&self) -> BitOrder {
self.layout.bit
}
fn seek_to_bit(&mut self, pos: usize) -> Result<(), BitError> {
let mut probe = BitReader::with_layout(&self.buf, self.layout);
probe.seek_to_bit(pos)?;
self.cursor = pos;
Ok(())
}
}
impl SeekSource for BitBuf {}
#[cfg(feature = "std")]
#[derive(Clone, Debug)]
pub struct SeekReader<R> {
inner: R,
bit_pos: usize,
layout: Layout,
}
#[cfg(feature = "std")]
impl<R: std::io::Read + std::io::Seek> SeekReader<R> {
#[must_use]
pub fn new(inner: R) -> Self {
Self::with_layout(inner, Layout::default())
}
#[must_use]
pub fn with_layout(inner: R, layout: Layout) -> Self {
Self {
inner,
bit_pos: 0,
layout,
}
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read + std::io::Seek> Source for SeekReader<R> {
fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
if n > 128 {
return Err(BitError::new(
ErrorKind::TooWide { width: n as usize },
self.bit_pos,
));
}
let bit_off = self.bit_pos % 8;
let byte_start = (self.bit_pos / 8) as u64;
let nbytes = (bit_off + n as usize).div_ceil(8);
self.inner
.seek(std::io::SeekFrom::Start(byte_start))
.map_err(|e| BitError::new(ErrorKind::Io(e.kind()), self.bit_pos))?;
let mut buf = vec![0u8; nbytes];
self.inner.read_exact(&mut buf).map_err(|e| {
let kind = if e.kind() == std::io::ErrorKind::UnexpectedEof {
ErrorKind::UnexpectedEof {
needed: n as usize,
remaining: 0,
}
} else {
ErrorKind::Io(e.kind())
};
BitError::new(kind, self.bit_pos)
})?;
let acc = extract_bits(&buf, bit_off, n as usize, self.layout.bit);
self.bit_pos += n as usize;
Ok(acc)
}
fn bit_pos(&self) -> usize {
self.bit_pos
}
fn byte_order(&self) -> ByteOrder {
self.layout.byte
}
fn bit_order(&self) -> BitOrder {
self.layout.bit
}
fn seek_to_bit(&mut self, pos: usize) -> Result<(), BitError> {
self.bit_pos = pos; Ok(())
}
}
#[cfg(feature = "std")]
impl<R: std::io::Read + std::io::Seek> SeekSource for SeekReader<R> {}
#[cfg(feature = "bytes")]
mod bytes_io {
use super::{
BitError, BitOrder, BitReader, BitWriter, ByteOrder, Layout, SeekSource, Sink, Source,
};
#[derive(Clone, Debug)]
pub struct BytesReader {
data: bytes::Bytes,
bit_pos: usize,
layout: Layout,
}
impl BytesReader {
#[must_use]
pub fn new(data: bytes::Bytes) -> Self {
Self::with_layout(data, Layout::default())
}
#[must_use]
pub fn with_layout(data: bytes::Bytes, layout: Layout) -> Self {
Self {
data,
bit_pos: 0,
layout,
}
}
}
impl Source for BytesReader {
fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
let mut br = BitReader::with_layout(&self.data, self.layout);
br.seek_to_bit(self.bit_pos)?;
let v = br.read_bits(n)?;
self.bit_pos = Source::bit_pos(&br);
Ok(v)
}
fn bit_pos(&self) -> usize {
self.bit_pos
}
fn byte_order(&self) -> ByteOrder {
self.layout.byte
}
fn bit_order(&self) -> BitOrder {
self.layout.bit
}
fn seek_to_bit(&mut self, pos: usize) -> Result<(), BitError> {
self.bit_pos = pos;
Ok(())
}
}
impl SeekSource for BytesReader {}
#[derive(Clone, Debug, Default)]
pub struct BytesWriter {
inner: BitWriter,
}
impl BytesWriter {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_layout(layout: Layout) -> Self {
Self {
inner: BitWriter::with_layout(layout),
}
}
#[must_use]
pub fn freeze(self) -> bytes::Bytes {
bytes::Bytes::from(self.inner.into_bytes())
}
}
impl Sink for BytesWriter {
fn write_bits(&mut self, value: u128, n: u32) -> Result<(), BitError> {
self.inner.write_bits(value, n)
}
fn bit_pos(&self) -> usize {
Sink::bit_pos(&self.inner)
}
fn byte_order(&self) -> ByteOrder {
Sink::byte_order(&self.inner)
}
fn bit_order(&self) -> BitOrder {
Sink::bit_order(&self.inner)
}
}
}
#[cfg(feature = "bytes")]
pub use bytes_io::{BytesReader, BytesWriter};
#[cfg(test)]
mod unit {
use super::*;
use crate::{u4, u12};
#[test]
fn unaligned_round_trip() {
let mut w = BitWriter::new();
w.write(u4::new(0xA)).unwrap();
w.write(u12::new(0xBCD)).unwrap();
assert_eq!(w.bit_len(), 16);
let bytes = w.into_bytes();
assert_eq!(bytes, [0xAB, 0xCD]);
let mut r = BitReader::new(&bytes);
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xA));
assert_eq!(r.read::<u12>().unwrap(), u12::new(0xBCD));
assert_eq!(r.remaining_bits(), 0);
}
#[test]
fn eof_is_an_error_not_a_panic() {
let mut r = BitReader::new(&[0xFF]);
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xF));
let err = r.read_bits(8).unwrap_err();
assert_eq!(
err.kind,
ErrorKind::UnexpectedEof {
needed: 8,
remaining: 4
}
);
assert_eq!(err.at, 4, "error records the bit offset");
assert!(err.field.is_none(), "no field context at the reader level");
}
#[test]
fn too_wide_is_rejected() {
let mut r = BitReader::new(&[0u8; 32]);
let err = r.read_bits(129).unwrap_err();
assert_eq!(err.kind, ErrorKind::TooWide { width: 129 });
}
#[test]
fn stream_reader_matches_slice_up_to_128_bits() {
let bytes: Vec<u8> = (0u8..16).collect();
let mut s = StreamBitReader::new(&bytes[..]);
let mut r = BitReader::new(&bytes);
assert_eq!(s.read_bits(128).unwrap(), r.read_bits(128).unwrap());
let mut s = StreamBitReader::new(&bytes[..]);
let mut r = BitReader::new(&bytes);
assert_eq!(s.read_bits(100).unwrap(), r.read_bits(100).unwrap());
assert_eq!(s.read_bits(28).unwrap(), r.read_bits(28).unwrap());
let mut s = StreamBitReader::new(&bytes[..]);
assert_eq!(
s.read_bits(65).unwrap(),
BitReader::new(&bytes).read_bits(65).unwrap(),
"a 65-bit read used to be rejected"
);
let mut s = StreamBitReader::new(&bytes[..]);
assert_eq!(
s.read_bits(129).unwrap_err().kind,
ErrorKind::TooWide { width: 129 }
);
}
use alloc::string::{String, ToString};
#[test]
fn display_unexpected_eof() {
let e = BitError::new(
ErrorKind::UnexpectedEof {
needed: 16,
remaining: 8,
},
0,
);
assert_eq!(
e.to_string(),
"unexpected end of input: needed 16 bits, 8 remain at bit 0"
);
}
#[test]
fn display_incomplete_with_and_without_hint() {
assert_eq!(
BitError::new(ErrorKind::Incomplete { needed: Some(3) }, 8).to_string(),
"incomplete: need ~3 more bytes at bit 8",
);
assert_eq!(
BitError::new(ErrorKind::Incomplete { needed: None }, 8).to_string(),
"incomplete: need more bytes at bit 8",
);
}
#[test]
fn display_trailing_too_wide_not_seekable_buffer_full() {
assert_eq!(
BitError::new(ErrorKind::TrailingBytes { remaining: 2 }, 16).to_string(),
"2 trailing bytes after the message at bit 16",
);
assert_eq!(
BitError::new(ErrorKind::TooWide { width: 129 }, 0).to_string(),
"field width 129 exceeds the 128-bit carrier at bit 0",
);
assert_eq!(
BitError::new(ErrorKind::NotSeekable, 4).to_string(),
"a position directive ran on a non-seekable source at bit 4",
);
assert_eq!(
BitError::new(ErrorKind::BufferFull { cap: 64 }, 0).to_string(),
"buffered source exceeded its 64-byte cap at bit 0",
);
}
#[test]
fn display_bad_magic_and_convert() {
assert_eq!(
BitError::bad_magic(0xCAFE, 0x0000, 0).to_string(),
"bad magic: expected 0xcafe, found 0x0 at bit 0",
);
assert_eq!(
BitError::convert(String::from("nope"), 8).to_string(),
"conversion failed: nope at bit 8",
);
}
#[test]
fn display_appends_field_span_when_set() {
let e = BitError::new(ErrorKind::TooWide { width: 200 }, 12).in_field("payload");
assert_eq!(
e.to_string(),
"field width 200 exceeds the 128-bit carrier at bit 12 (field `payload`)"
);
}
#[test]
fn display_io_kind() {
let e = BitError::new(ErrorKind::Io(std::io::ErrorKind::BrokenPipe), 0);
assert!(e.to_string().starts_with("I/O error:"));
}
#[test]
fn in_field_records_only_the_innermost() {
let e = BitError::new(ErrorKind::NotSeekable, 0)
.in_field("inner")
.in_field("outer"); assert_eq!(e.field, Some("inner"));
}
#[test]
fn is_incomplete_is_true_only_for_incomplete() {
assert!(BitError::new(ErrorKind::Incomplete { needed: None }, 0).is_incomplete());
assert!(!BitError::new(ErrorKind::NotSeekable, 0).is_incomplete());
}
#[test]
fn construction_error_bridges_to_a_convert_error() {
let e: BitError = crate::error::Error::ValueTooLarge { value: 99, bits: 4 }.into();
assert!(matches!(e.kind, ErrorKind::Convert { .. }));
assert_eq!(e.at, 0);
assert!(e.to_string().contains("does not fit in 4 bits"));
}
#[test]
fn io_error_bridges_to_an_io_kind() {
let e: BitError = std::io::Error::new(std::io::ErrorKind::TimedOut, "x").into();
assert_eq!(e.kind, ErrorKind::Io(std::io::ErrorKind::TimedOut));
assert_eq!(e.at, 0);
}
#[test]
fn writer_with_order_lsb_packs_first_field_in_the_low_bits() {
let mut w = BitWriter::with_order(BitOrder::Lsb);
w.write(u4::new(0xA)).unwrap(); w.write(u4::new(0xB)).unwrap(); assert_eq!(w.into_bytes(), [0xBA]);
}
#[test]
fn cursor_layout_matrix_bit_and_byte_order_are_independent() {
let combos = [
(BitOrder::Msb, ByteOrder::Big),
(BitOrder::Msb, ByteOrder::Little),
(BitOrder::Lsb, ByteOrder::Big),
(BitOrder::Lsb, ByteOrder::Little),
];
let mut encs = Vec::new();
for (bit, byte) in combos {
let layout = Layout { bit, byte };
let mut w = BitWriter::with_layout(layout);
w.write(u4::new(0xA)).unwrap();
w.write(u4::new(0xB)).unwrap();
w.write(0x1234u16).unwrap();
let bytes = w.into_bytes();
let mut r = BitReader::with_layout(&bytes, layout);
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xA));
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xB));
assert_eq!(r.read::<u16>().unwrap(), 0x1234);
encs.push(bytes);
}
assert_eq!(encs[0], [0xAB, 0x12, 0x34]); assert_eq!(encs[1], [0xAB, 0x34, 0x12]); assert_eq!(encs[2], [0xBA, 0x12, 0x34]); assert_eq!(encs[3], [0xBA, 0x34, 0x12]); assert_eq!(encs[0][0], encs[1][0]);
assert_ne!(encs[0][1..], encs[1][1..]);
assert_eq!(encs[2][0], encs[3][0]);
assert_ne!(encs[2][1..], encs[3][1..]);
for i in 0..encs.len() {
for j in (i + 1)..encs.len() {
assert_ne!(encs[i], encs[j], "layout corners {i} and {j} alias");
}
}
}
#[test]
fn write_bits_rejects_over_128() {
let mut w = BitWriter::new();
assert_eq!(
w.write_bits(0, 129).unwrap_err().kind,
ErrorKind::TooWide { width: 129 }
);
}
struct TinySource<'a> {
bytes: &'a [u8],
pos: usize,
}
impl Source for TinySource<'_> {
fn read_bits(&mut self, n: u32) -> Result<u128, BitError> {
let n = n as usize;
let total = self.bytes.len() * 8;
if self.pos + n > total {
return Err(BitError::new(
ErrorKind::UnexpectedEof {
needed: n,
remaining: total - self.pos,
},
self.pos,
));
}
let mut acc = 0u128;
for k in 0..n {
let p = self.pos + k;
acc = (acc << 1) | u128::from((self.bytes[p >> 3] >> (7 - (p & 7))) & 1);
}
self.pos += n;
Ok(acc)
}
fn bit_pos(&self) -> usize {
self.pos
}
}
#[test]
fn source_default_byte_order_is_big() {
let s = TinySource {
bytes: &[0],
pos: 0,
};
assert_eq!(s.byte_order(), ByteOrder::Big);
}
#[test]
fn source_default_seek_is_not_seekable() {
let mut s = TinySource {
bytes: &[0, 0],
pos: 0,
};
assert_eq!(s.seek_to_bit(8).unwrap_err().kind, ErrorKind::NotSeekable);
}
#[test]
fn source_default_read_dispatches_through_read_bits() {
let mut s = TinySource {
bytes: &[0xAB, 0xCD],
pos: 0,
};
assert_eq!(s.read::<u8>().unwrap(), 0xAB);
assert_eq!(s.read::<u8>().unwrap(), 0xCD);
}
struct TinySink {
out: Vec<u8>,
bit: usize,
}
impl Sink for TinySink {
fn write_bits(&mut self, value: u128, n: u32) -> Result<(), BitError> {
let n = n as usize;
for k in 0..n {
let p = self.bit + k;
if p >> 3 == self.out.len() {
self.out.push(0);
}
if (value >> (n - 1 - k)) & 1 != 0 {
self.out[p >> 3] |= 1 << (7 - (p & 7));
}
}
self.bit += n;
Ok(())
}
fn bit_pos(&self) -> usize {
self.bit
}
}
#[test]
fn sink_default_byte_order_is_big() {
let s = TinySink {
out: Vec::new(),
bit: 0,
};
assert_eq!(s.byte_order(), ByteOrder::Big);
}
#[test]
fn sink_default_write_dispatches_through_write_bits() {
let mut s = TinySink {
out: Vec::new(),
bit: 0,
};
s.write(0xABu8).unwrap();
s.write(0xCDu8).unwrap();
assert_eq!(s.out, [0xAB, 0xCD]);
}
#[test]
fn leaf_canonical_encode_defaults_to_verbatim() {
let mut a = BitWriter::new();
let mut b = BitWriter::new();
BitEncode::bit_encode(&0xABCDu16, &mut a).unwrap();
BitEncode::canonical_bit_encode(&0xABCDu16, &mut b).unwrap();
assert_eq!(a.into_bytes(), b.into_bytes());
}
#[test]
fn leaf_encode_mode_default_is_verbatim() {
assert_eq!(BitEncode::encode_mode(&0u16), EncodeMode::Verbatim);
}
#[test]
fn leaf_decode_with_and_encode_with_unit_args() {
let mut r = BitReader::new(&[0xAB, 0xCD]);
assert_eq!(
<u16 as DecodeWith<()>>::decode_with(&mut r, ()).unwrap(),
0xABCD
);
let mut w = BitWriter::new();
EncodeWith::encode_with(&0xABCDu16, &mut w, ()).unwrap();
assert_eq!(w.into_bytes(), [0xAB, 0xCD]);
}
#[test]
fn bitbuf_bounded_reports_its_capacity() {
assert_eq!(BitBuf::bounded(64).capacity(), Some(64));
assert_eq!(BitBuf::new().capacity(), None);
assert_eq!(BitBuf::with_capacity(64).capacity(), None); }
#[test]
fn capacity_error_display() {
let e = CapacityError {
cap: 4,
requested: 5,
};
assert_eq!(
e.to_string(),
"bitbuf is full: 5 bytes needed exceeds the 4-byte capacity"
);
}
#[test]
fn count_prefix_primitive_boundaries() {
assert_eq!(u8::try_from_len(255).unwrap(), 255u8);
assert_eq!(
u8::try_from_len(256).unwrap_err(),
crate::error::Error::ValueTooLarge {
value: 256,
bits: 8
}
);
assert_eq!(u16::try_from_len(65_535).unwrap(), 65_535u16);
assert_eq!(u8::to_count(200), 200);
assert_eq!(u32::to_count(70_000), 70_000);
}
#[test]
fn count_prefix_uint_boundaries() {
assert_eq!(u12::try_from_len(4095).unwrap(), u12::new(4095));
assert_eq!(
u12::try_from_len(4096).unwrap_err(),
crate::error::Error::ValueTooLarge {
value: 4096,
bits: 12
}
);
assert_eq!(u12::new(4095).to_count(), 4095);
}
#[test]
fn count_prefix_uint_never_truncates_before_the_check() {
assert_eq!(
u4::try_from_len(300).unwrap_err(),
crate::error::Error::ValueTooLarge {
value: 300,
bits: 4
}
);
}
#[test]
fn count_prefix_round_trips() {
for len in [0usize, 1, 15, 255, 4095] {
assert_eq!(u16::try_from_len(len).unwrap().to_count(), len);
assert_eq!(u12::try_from_len(len).unwrap().to_count(), len);
}
}
#[test]
fn bulk_bytes_round_trip() {
let mut w = BitWriter::new();
w.write_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]).unwrap();
let bytes = w.into_bytes();
assert_eq!(bytes, [0xDE, 0xAD, 0xBE, 0xEF]);
let mut r = BitReader::new(&bytes);
assert_eq!(r.read_bytes(4).unwrap(), [0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(r.remaining_bits(), 0);
}
#[test]
fn bulk_bytes_work_at_a_bit_offset() {
let mut w = BitWriter::new();
w.write(u4::new(0xF)).unwrap();
w.write_bytes(&[0xAB, 0xCD]).unwrap();
let bytes = w.into_bytes();
assert_eq!(bytes, [0xFA, 0xBC, 0xD0]);
let mut r = BitReader::new(&bytes);
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xF));
assert_eq!(r.read_bytes(2).unwrap(), [0xAB, 0xCD]);
}
#[test]
fn read_bytes_hostile_length_is_eof_not_alloc() {
let mut r = BitReader::new(&[0x01, 0x02]);
let err = r.read_bytes(usize::MAX).unwrap_err();
assert!(matches!(err.kind, ErrorKind::UnexpectedEof { .. }));
}
#[test]
fn read_into_fills_and_errors_short() {
let mut r = BitReader::new(&[0x0A, 0x0B, 0x0C]);
let mut buf = [0u8; 3];
r.read_into(&mut buf).unwrap();
assert_eq!(buf, [0x0A, 0x0B, 0x0C]);
let mut r = BitReader::new(&[0x0A]);
let mut buf = [0u8; 3];
let err = r.read_into(&mut buf).unwrap_err();
assert!(matches!(err.kind, ErrorKind::UnexpectedEof { .. }));
}
}
#[cfg(test)]
mod component {
mod source {
use bnb::{BitDecode, BitEncode, BitReader, BitWriter, StreamBitReader, u4, u12};
#[derive(BitDecode, BitEncode, Debug, PartialEq, Eq)]
struct Word {
a: u4,
b: u12, }
#[test]
fn decodes_over_slice_and_stream_identically() {
let word = Word {
a: u4::new(0xA),
b: u12::new(0xBCD),
};
let mut w = BitWriter::new();
word.bit_encode(&mut w).unwrap();
let bytes = w.into_bytes();
assert_eq!(bytes, [0xAB, 0xCD]);
let mut slice = BitReader::new(&bytes);
assert_eq!(Word::bit_decode(&mut slice).unwrap(), word);
let mut stream = StreamBitReader::new(&bytes[..]);
assert_eq!(Word::bit_decode(&mut stream).unwrap(), word);
}
#[test]
fn stream_reader_honors_a_little_endian_layout() {
use bnb::{Layout, bin};
#[bin(little)]
#[derive(Debug, PartialEq)]
struct Le {
v: u32,
}
let bytes = Le { v: 0x1122_3344 }.to_bytes().unwrap();
assert_eq!(bytes, [0x44, 0x33, 0x22, 0x11]);
let mut stream = StreamBitReader::with_layout(&bytes[..], <Le as BitEncode>::LAYOUT);
assert_eq!(Le::bit_decode(&mut stream).unwrap(), Le { v: 0x1122_3344 });
let mut msb = StreamBitReader::with_layout(&bytes[..], Layout::default());
assert_ne!(Le::bit_decode(&mut msb).unwrap().v, 0x1122_3344);
}
}
mod seek {
use bnb::{BitReader, StreamBitReader, u4};
#[test]
fn seek_and_align_need_no_seek_trait() {
let bytes = [0xABu8, 0xCD, 0xEF];
let mut r = BitReader::new(&bytes);
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xA));
r.seek_to_bit(16).unwrap(); assert_eq!(r.read_bits(8).unwrap(), 0xEF);
r.seek_to_bit(4).unwrap(); assert_eq!(r.read::<u4>().unwrap(), u4::new(0xB));
r.seek_to_bit(9).unwrap();
r.align_to_byte();
assert_eq!(r.bit_pos(), 16);
assert!(r.seek_to_bit(999).is_err());
}
#[test]
fn forward_only_stream_reader_requires_only_read() {
let data = [0xABu8, 0xCD];
let src: &[u8] = &data;
let mut r = StreamBitReader::new(src);
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xA));
assert_eq!(r.read::<u4>().unwrap(), u4::new(0xB));
assert_eq!(r.read_bits(8).unwrap(), 0xCD);
assert!(r.read_bits(1).is_err());
}
}
mod entry {
use bnb::{
BitDecode, BitEncode, BitReader, EncodeExt, ErrorKind, StreamBitReader, u4, u12,
};
use std::io::Cursor;
#[derive(BitDecode, BitEncode, Debug, PartialEq, Eq, Clone, Copy)]
struct Word {
a: u4,
b: u12,
}
fn sample() -> (Word, [u8; 2]) {
(
Word {
a: u4::new(0xA),
b: u12::new(0xBCD),
},
[0xAB, 0xCD],
)
}
#[test]
fn to_bytes_peek_and_tail_tolerance() {
let (w, bytes) = sample();
assert_eq!(w.to_bytes().unwrap(), bytes);
assert_eq!(Word::peek(&bytes).unwrap(), w);
let mut padded = bytes.to_vec();
padded.push(0xFF);
assert_eq!(Word::peek(&padded).unwrap(), w);
}
#[test]
fn decode_advances_a_cursor() {
let (w, bytes) = sample();
let mut both = bytes.to_vec();
both.extend_from_slice(&bytes);
let mut cur = BitReader::new(&both);
assert_eq!(Word::decode(&mut cur).unwrap(), w);
assert_eq!(cur.bit_pos(), 16, "advanced past the first message");
assert_eq!(Word::decode(&mut cur).unwrap(), w);
assert_eq!(cur.bit_pos(), both.len() * 8, "consumed both");
}
#[test]
fn decode_all_and_iter_collect_back_to_back() {
let (w, bytes) = sample();
let mut both = bytes.to_vec();
both.extend_from_slice(&bytes);
assert_eq!(Word::decode_all(&both).unwrap(), vec![w, w]);
let collected: Result<Vec<_>, _> = Word::decode_iter(&both).collect();
assert_eq!(collected.unwrap(), vec![w, w]);
}
#[test]
fn decode_all_of_a_zero_width_type_yields_nothing() {
use bnb::{BitDecode, BitError, Layout, Source};
#[derive(Debug, PartialEq)]
struct Zero;
impl BitDecode for Zero {
fn bit_decode<S: Source>(_r: &mut S) -> Result<Self, BitError> {
Ok(Zero)
}
}
let out: Vec<Zero> =
bnb::__private::decode_all(&[0xFF, 0xFF], Layout::default()).unwrap();
assert!(out.is_empty());
}
#[test]
fn decode_errors_on_short_cursor() {
let short = [0xABu8]; let mut cur = BitReader::new(&short);
let err = Word::decode(&mut cur).unwrap_err();
assert!(matches!(err.kind, ErrorKind::UnexpectedEof { .. }));
}
#[test]
fn decode_exact_rejects_trailing_bytes() {
let (w, bytes) = sample();
assert_eq!(Word::decode_exact(&bytes).unwrap(), w);
let mut padded = bytes.to_vec();
padded.push(0xFF);
let err = Word::decode_exact(&padded).unwrap_err();
assert_eq!(err.kind, ErrorKind::TrailingBytes { remaining: 1 });
}
#[test]
fn encode_to_any_write() {
let (w, bytes) = sample();
let mut sink = Cursor::new(Vec::new());
w.encode(&mut sink).unwrap();
assert_eq!(sink.into_inner(), bytes);
}
#[test]
fn encode_io_error_is_reported() {
struct Full;
impl std::io::Write for Full {
fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
Err(std::io::Error::new(std::io::ErrorKind::WriteZero, "full"))
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
let (w, _) = sample();
let err = w.encode(&mut Full).unwrap_err();
assert_eq!(err.kind, ErrorKind::Io(std::io::ErrorKind::WriteZero));
}
#[test]
fn decode_explicit_cursor() {
let (w, bytes) = sample();
let mut r = BitReader::new(&bytes);
assert_eq!(Word::decode(&mut r).unwrap(), w);
}
#[test]
fn streaming_shortfall_is_incomplete_not_eof() {
let (_, bytes) = sample();
let mut stream = StreamBitReader::new(&bytes[..1]);
let err = Word::decode(&mut stream).unwrap_err();
assert!(err.is_incomplete(), "stream shortfall is incomplete: {err}");
assert!(matches!(err.kind, ErrorKind::Incomplete { .. }));
assert_eq!(err.field, Some("b"), "still records the field span");
}
}
mod errors {
use bnb::{BitDecode, BitEncode, BitError, BitReader, BitWriter, ErrorKind, u4, u12};
#[derive(BitDecode, BitEncode, Debug, PartialEq, Eq)]
struct Header {
a: u4,
b: u12, }
#[test]
fn round_trips() {
let h = Header {
a: u4::new(0xA),
b: u12::new(0xBCD),
};
let mut w = BitWriter::new();
h.bit_encode(&mut w).unwrap();
let bytes = w.into_bytes();
assert_eq!(bytes, [0xAB, 0xCD]);
let mut r = BitReader::new(&bytes);
assert_eq!(Header::bit_decode(&mut r).unwrap(), h);
}
#[test]
fn decode_eof_reports_offset_and_field() {
let bytes = [0xAB];
let mut r = BitReader::new(&bytes);
let err: BitError = Header::bit_decode(&mut r).unwrap_err();
assert_eq!(err.field, Some("b"), "names the field that failed");
assert_eq!(err.at, 4, "records the bit offset where decoding stopped");
assert_eq!(
err.kind,
ErrorKind::UnexpectedEof {
needed: 12,
remaining: 4
}
);
let msg = err.to_string();
assert!(msg.contains("field `b`"), "message names the field: {msg}");
assert!(msg.contains("at bit 4"), "message names the offset: {msg}");
}
#[test]
fn innermost_field_wins_the_span() {
let mut r = BitReader::new(&[0xAB]);
let err = Header::bit_decode(&mut r).unwrap_err();
assert_eq!(err.field, Some("b"));
}
}
mod io_adapter {
use bnb::{BitError, Sink, Source, bin};
use std::io::{Read, Write};
fn read_blob<S: Source>(r: &mut S) -> Result<Vec<u8>, BitError> {
let len: u8 = r.read()?;
let mut buf = vec![0u8; len as usize];
r.as_read().read_exact(&mut buf)?; Ok(buf)
}
fn write_blob<K: Sink>(blob: &[u8], w: &mut K) -> Result<(), BitError> {
w.write(u8::try_from(blob.len()).unwrap())?;
w.as_write().write_all(blob)?;
Ok(())
}
#[bin(big)]
#[derive(Debug, PartialEq)]
struct Msg {
#[br(parse_with = read_blob)]
#[bw(write_with = write_blob)]
data: Vec<u8>,
}
#[test]
fn as_read_as_write_roundtrip_through_std_io() {
let m = Msg {
data: vec![0xDE, 0xAD, 0xBE, 0xEF],
};
let bytes = m.to_bytes().unwrap();
assert_eq!(bytes, [0x04, 0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(Msg::decode_exact(&bytes).unwrap(), m);
}
#[test]
fn as_read_short_read_reports_eof() {
use bnb::BitReader;
let mut r = BitReader::new(&[0x04, 0xAA, 0xBB]);
assert!(read_blob(&mut r).is_err());
}
}
mod buf_source {
use bnb::{BufSource, ErrorKind, Source, bin, u4};
struct Chunked {
data: Vec<u8>,
pos: usize,
}
impl std::io::Read for Chunked {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
if self.pos >= self.data.len() || buf.is_empty() {
return Ok(0);
}
buf[0] = self.data[self.pos];
self.pos += 1;
Ok(1)
}
}
#[bin]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Frame {
flags: u4,
#[br(restore_position)]
peek: u8,
value: u16,
}
#[test]
fn seek_using_message_over_a_nonseekable_stream() {
let wire = vec![0x5A, 0xBC, 0xD0];
let mut src = BufSource::new(Chunked { data: wire, pos: 0 });
let f = Frame::decode(&mut src).unwrap();
assert_eq!(f.value, 0xABCD);
assert_eq!(f.peek, 0xAB, "the rewind re-read retained bytes");
}
#[test]
fn retention_cap_bounds_the_buffer() {
let mut src = BufSource::with_cap(
Chunked {
data: vec![0xFF; 8],
pos: 0,
},
1,
);
let err = src.read_bits(16).unwrap_err();
assert!(matches!(err.kind, ErrorKind::BufferFull { cap: 1 }));
}
#[test]
fn over_wide_read_is_rejected() {
let mut src = BufSource::new(Chunked {
data: vec![0u8; 32],
pos: 0,
});
assert!(matches!(
src.read_bits(129).unwrap_err().kind,
ErrorKind::TooWide { width: 129 }
));
}
#[test]
fn running_out_mid_field_is_incomplete() {
let mut src = BufSource::new(Chunked {
data: vec![0xAB],
pos: 0,
});
assert!(matches!(
src.read_bits(16).unwrap_err().kind,
ErrorKind::Incomplete { .. }
));
}
#[test]
fn an_io_error_from_the_reader_propagates() {
struct Failing;
impl std::io::Read for Failing {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::ConnectionReset,
"boom",
))
}
}
let mut src = BufSource::new(Failing);
assert!(matches!(
src.read_bits(8).unwrap_err().kind,
ErrorKind::Io(_)
));
}
}
mod seek_reader {
use bnb::{SeekReader, bin, u4};
use std::io::Cursor;
#[bin]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Frame {
flags: u4,
#[br(restore_position)]
peek: u8,
value: u16,
}
#[test]
fn seek_reader_over_a_file_like_source() {
let wire = vec![0x5A, 0xBC, 0xD0]; let mut src = SeekReader::new(Cursor::new(wire));
let f = Frame::decode(&mut src).unwrap();
assert_eq!(f.value, 0xABCD);
assert_eq!(f.peek, 0xAB, "rewound and re-read via io::Seek");
}
#[test]
fn over_wide_read_is_rejected() {
use bnb::{ErrorKind, Source};
let mut src = SeekReader::new(Cursor::new(vec![0u8; 32]));
assert!(matches!(
src.read_bits(129).unwrap_err().kind,
ErrorKind::TooWide { width: 129 }
));
}
#[test]
fn reading_past_the_end_is_unexpected_eof() {
use bnb::ErrorKind;
#[bin(big)]
#[derive(Debug)]
struct Quad {
v: u32,
}
let mut src = SeekReader::new(Cursor::new(vec![0x12, 0x34]));
assert!(matches!(
Quad::decode(&mut src).unwrap_err().kind,
ErrorKind::UnexpectedEof { .. }
));
}
#[test]
fn little_endian_layout_is_honored() {
#[bin(little)]
#[derive(Debug, PartialEq)]
struct Le {
v: u32,
}
let mut src = SeekReader::with_layout(
Cursor::new(vec![0x78, 0x56, 0x34, 0x12]),
<Le as bnb::BitEncode>::LAYOUT,
);
assert_eq!(Le::decode(&mut src).unwrap(), Le { v: 0x1234_5678 });
}
}
mod bitbuf {
use bnb::{BitBuf, BitDecode, BitEncode, BitWriter, bin, u4};
#[bin(big)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct Frame {
tag: u4,
val: u8,
}
#[bin(little)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
struct LeMsg {
a: u16,
b: u32,
}
#[test]
fn pull_is_none_until_a_whole_message_arrives_then_reclaims() {
let m = LeMsg {
a: 0x1234,
b: 0xDEAD_BEEF,
};
let bytes = m.to_bytes().unwrap();
let mut bb = BitBuf::new();
bb.push(&bytes[..3]); assert_eq!(bb.pull::<LeMsg>().unwrap(), None); assert_eq!(bb.bit_len(), 24);
bb.push(&bytes[3..]); assert_eq!(bb.pull::<LeMsg>().unwrap(), Some(m)); assert!(bb.is_empty()); assert_eq!(bb.pull::<LeMsg>().unwrap(), None);
}
#[test]
fn reassembles_sub_byte_boundary_messages_across_pushes() {
let f1 = Frame {
tag: u4::new(0xA),
val: 0x12,
};
let f2 = Frame {
tag: u4::new(0xB),
val: 0x34,
};
let mut w = BitWriter::new();
f1.bit_encode(&mut w).unwrap();
f2.bit_encode(&mut w).unwrap();
let wire = w.into_bytes();
let mut bb = BitBuf::new();
let mut out = Vec::new();
for chunk in [&wire[0..1], &wire[1..3]] {
bb.push(chunk);
while let Some(f) = bb.pull::<Frame>().unwrap() {
out.push(f);
}
}
assert_eq!(out, vec![f1, f2]);
assert!(bb.is_empty());
}
#[test]
fn clear_and_capacity() {
let mut bb = BitBuf::with_capacity(64);
bb.push(&[1, 2, 3]);
assert_eq!(bb.bit_len(), 24);
bb.clear();
assert!(bb.is_empty());
}
#[test]
fn reads_as_a_source_respecting_layout() {
let f = Frame {
tag: u4::new(0xC),
val: 0x9A,
};
let mut bb = BitBuf::new();
bb.push(&f.to_bytes().unwrap());
assert_eq!(<Frame as BitDecode>::bit_decode(&mut bb).unwrap(), f);
let m = LeMsg {
a: 0x1234,
b: 0xDEAD_BEEF,
};
let mut bb = BitBuf::new().with_layout(<LeMsg as BitEncode>::LAYOUT);
bb.push(&m.to_bytes().unwrap());
let got = <LeMsg as BitDecode>::bit_decode(&mut bb).unwrap();
assert_eq!(got, m); bb.compact(); assert!(bb.is_empty());
}
#[test]
fn as_a_seek_source_a_restore_position_message_decodes() {
#[bin(big)]
#[derive(Debug, PartialEq, Eq)]
struct Peeked {
#[br(restore_position)]
tag: u8,
full: u16,
}
let mut bb = BitBuf::new();
bb.push(&[0xAB, 0xCD]);
let p = Peeked::decode(&mut bb).unwrap();
assert_eq!((p.tag, p.full), (0xAB, 0xABCD));
}
#[bin(big)]
#[derive(Debug, PartialEq, Eq)]
struct Two {
v: u16,
}
#[test]
fn bounded_try_push_respects_capacity_then_reclaims_in_place() {
use bnb::CapacityError;
let mut bb = BitBuf::bounded(4);
assert_eq!(bb.capacity(), Some(4));
bb.try_push(&[0x00, 0x01]).unwrap(); bb.try_push(&[0x00, 0x02]).unwrap(); assert!(matches!(
bb.try_push(&[0xFF]),
Err(CapacityError { cap: 4, .. })
));
assert_eq!(bb.pull::<Two>().unwrap(), Some(Two { v: 1 }));
bb.try_push(&[0x00, 0x03]).unwrap();
assert_eq!(bb.pull::<Two>().unwrap(), Some(Two { v: 2 }));
assert_eq!(bb.pull::<Two>().unwrap(), Some(Two { v: 3 }));
assert!(bb.is_empty());
}
#[test]
fn grow_raises_a_bounded_capacity() {
let mut bb = BitBuf::bounded(2);
bb.try_push(&[0x00, 0x01]).unwrap();
assert!(bb.try_push(&[0x02]).is_err()); bb.grow(2); assert_eq!(bb.capacity(), Some(4));
bb.try_push(&[0x02, 0x03]).unwrap();
assert_eq!(bb.bit_len(), 32);
}
#[test]
fn unbounded_try_push_never_fails() {
let mut bb = BitBuf::new();
assert_eq!(bb.capacity(), None);
bb.try_push(&[1, 2, 3]).unwrap(); assert_eq!(bb.bit_len(), 24);
}
#[test]
fn a_streaming_push_pull_loop_stays_within_a_tiny_cap() {
let mut bb = BitBuf::bounded(2);
for i in 0..100u16 {
bb.try_push(&i.to_be_bytes()).unwrap();
assert_eq!(bb.pull::<Two>().unwrap(), Some(Two { v: i }));
}
assert!(bb.is_empty());
}
}
#[cfg(feature = "bytes")]
mod bytes_adapters {
use bnb::{BitEncode, BytesReader, BytesWriter, bin, u4, u12};
#[bin]
#[derive(Debug, PartialEq, Eq, Clone)]
struct Frame {
a: u4,
b: u12,
}
#[test]
fn round_trip_through_bytes() {
let f = Frame {
a: u4::new(0xA),
b: u12::new(0x123),
};
let mut w = BytesWriter::new();
f.bit_encode(&mut w).unwrap();
let frozen = w.freeze();
assert_eq!(&frozen[..], &[0xA1, 0x23]);
let mut r = BytesReader::new(frozen.clone());
let decoded = Frame::decode(&mut r).unwrap();
assert_eq!(decoded, f);
}
#[test]
fn bytes_reader_seek_and_bit_pos() {
use bnb::{Sink, Source};
#[bin(big)]
#[derive(Debug, PartialEq, Eq)]
struct Peeked {
#[br(restore_position)]
tag: u8,
full: u16,
}
let mut w = BytesWriter::new();
w.write(0xABu8).unwrap();
w.write(0xCDu8).unwrap();
let mut r = BytesReader::new(w.freeze());
assert_eq!(r.bit_pos(), 0);
let p = Peeked::decode(&mut r).unwrap();
assert_eq!((p.tag, p.full), (0xAB, 0xABCD));
}
#[test]
fn bytes_writer_with_layout_and_bit_pos() {
use bnb::{BitOrder, ByteOrder, Layout, Sink};
let mut w = BytesWriter::with_layout(Layout {
bit: BitOrder::Lsb,
byte: ByteOrder::Big,
});
assert_eq!(w.bit_pos(), 0);
w.write(u4::new(0xA)).unwrap();
assert_eq!(w.bit_pos(), 4);
}
}
}