use crate::error::{Error, Result};
pub const MAX_BIT_WIDTH: u32 = 64;
#[derive(Debug, Clone)]
pub struct BitReader<'a> {
bytes: &'a [u8],
bit_pos: usize,
}
impl<'a> BitReader<'a> {
#[inline]
pub const fn new(bytes: &'a [u8]) -> Self {
Self { bytes, bit_pos: 0 }
}
#[inline]
pub const fn bits_consumed(&self) -> usize {
self.bit_pos
}
#[inline]
pub const fn bits_remaining(&self) -> usize {
bit_capacity(self.bytes.len()) - self.bit_pos
}
#[inline]
pub fn align_to_byte(&mut self) {
self.bit_pos = (self.bit_pos + 7) & !7;
}
pub fn read_bits(&mut self, n: u32) -> Result<u64> {
if n == 0 || n > MAX_BIT_WIDTH {
return Err(Error::BitOverflow);
}
if (n as usize) > self.bits_remaining() {
return Err(Error::UnexpectedEof);
}
let mut value: u64 = 0;
let mut bits_left = n;
while bits_left > 0 {
let byte_idx = self.bit_pos / 8;
let bit_off = (self.bit_pos % 8) as u32;
let avail = 8 - bit_off;
let take = if bits_left < avail { bits_left } else { avail };
let shift = avail - take;
let mask: u32 = (1u32 << take) - 1;
let chunk = (u32::from(self.bytes[byte_idx]) >> shift) & mask;
value = (value << take) | u64::from(chunk);
self.bit_pos += take as usize;
bits_left -= take;
}
Ok(value)
}
#[inline]
pub fn read_bool(&mut self) -> Result<bool> {
self.read_bits(1).map(|bit| bit != 0)
}
#[inline]
pub fn read_signed_bits(&mut self, n: u32) -> Result<i64> {
self.read_bits(n).map(|raw| sign_extend(raw, n))
}
}
#[inline]
pub(crate) const fn sign_extend(raw: u64, n: u32) -> i64 {
let shift = 64 - n;
((raw << shift) as i64) >> shift
}
#[inline]
pub(crate) fn signed_to_bits(value: i64, n: u32) -> Option<u64> {
if n == 0 || n > MAX_BIT_WIDTH {
return None;
}
let shift = 64 - n;
let raw = (value as u64) & (u64::MAX >> shift);
(sign_extend(raw, n) == value).then_some(raw)
}
#[derive(Debug)]
pub struct BitWriter<'a> {
bytes: &'a mut [u8],
bit_pos: usize,
}
impl<'a> BitWriter<'a> {
#[inline]
pub fn new(bytes: &'a mut [u8]) -> Self {
Self { bytes, bit_pos: 0 }
}
#[inline]
pub fn bits_written(&self) -> usize {
self.bit_pos
}
pub fn align_to_byte(&mut self) -> Result<()> {
self.zero_trailing_bits();
self.bit_pos = (self.bit_pos + 7) & !7;
Ok(())
}
#[inline]
fn zero_trailing_bits(&mut self) {
let bit_off = self.bit_pos % 8;
if bit_off != 0 {
let keep = 0xFFu8 << (8 - bit_off);
self.bytes[self.bit_pos / 8] &= keep;
}
}
pub fn write_bits(&mut self, value: u64, n: u32) -> Result<()> {
if n == 0 || n > MAX_BIT_WIDTH {
return Err(Error::BitOverflow);
}
if n < 64 && (value >> n) != 0 {
return Err(Error::BitOverflow);
}
if (n as usize) > bit_capacity(self.bytes.len()) - self.bit_pos {
return Err(Error::BufferFull);
}
let mut bits_left = n;
while bits_left > 0 {
let byte_idx = self.bit_pos / 8;
let bit_off = (self.bit_pos % 8) as u32;
let avail = 8 - bit_off;
let take = if bits_left < avail { bits_left } else { avail };
let shift = avail - take;
let chunk = ((value >> (bits_left - take)) & ((1u64 << take) - 1)) as u8;
let mask = (((1u32 << take) - 1) as u8) << shift;
self.bytes[byte_idx] = (self.bytes[byte_idx] & !mask) | (chunk << shift);
self.bit_pos += take as usize;
bits_left -= take;
}
Ok(())
}
#[inline]
pub fn write_bool(&mut self, value: bool) -> Result<()> {
self.write_bits(u64::from(value), 1)
}
#[inline]
pub fn write_signed_bits(&mut self, value: i64, n: u32) -> Result<()> {
let raw = signed_to_bits(value, n).ok_or(Error::BitOverflow)?;
self.write_bits(raw, n)
}
#[inline]
pub fn finish(mut self) -> usize {
self.zero_trailing_bits();
self.bit_pos.div_ceil(8)
}
}
#[inline]
const fn bit_capacity(len: usize) -> usize {
let max_bytes = usize::MAX / 8;
let bytes = if len < max_bytes { len } else { max_bytes };
bytes * 8
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pack_unpack_round_trip() {
let mut storage = [0u8; 4];
let mut w = BitWriter::new(&mut storage);
w.write_bits(0b1011, 4).unwrap();
w.write_bits(0b1100_1010, 8).unwrap();
w.write_bits(0b1111_0000, 8).unwrap();
let n = w.finish();
assert_eq!(n, 3);
let mut r = BitReader::new(&storage[..n]);
assert_eq!(r.read_bits(4).unwrap(), 0b1011);
assert_eq!(r.read_bits(8).unwrap(), 0b1100_1010);
assert_eq!(r.read_bits(8).unwrap(), 0b1111_0000);
}
#[test]
fn write_rejects_value_overflow() {
let mut storage = [0u8; 1];
let mut w = BitWriter::new(&mut storage);
assert_eq!(w.write_bits(0b1_0000, 4), Err(Error::BitOverflow));
}
#[test]
fn padding_bits_are_zeroed_on_reused_buffer() {
let mut storage = [0xFFu8; 2];
let mut w = BitWriter::new(&mut storage);
w.write_bits(0b1, 1).unwrap();
w.align_to_byte().unwrap();
w.write_bits(0b0, 1).unwrap();
assert_eq!(w.finish(), 2);
assert_eq!(storage, [0b1000_0000, 0b0000_0000]);
}
#[test]
fn finish_leaves_bytes_past_the_prefix_untouched() {
let mut storage = [0xAAu8; 3];
let mut w = BitWriter::new(&mut storage);
w.write_bits(0b11, 2).unwrap();
assert_eq!(w.finish(), 1);
assert_eq!(storage, [0b1100_0000, 0xAA, 0xAA]);
}
#[test]
fn align_when_already_aligned_is_a_no_op() {
let mut storage = [0xFFu8; 1];
let mut w = BitWriter::new(&mut storage);
w.align_to_byte().unwrap();
assert_eq!(w.bits_written(), 0);
w.write_bits(0xA5, 8).unwrap();
w.align_to_byte().unwrap();
assert_eq!(w.bits_written(), 8);
assert_eq!(w.finish(), 1);
assert_eq!(storage, [0xA5]);
}
#[test]
fn reader_align_to_byte() {
let mut r = BitReader::new(&[0b1010_0000, 0xCD]);
r.align_to_byte();
assert_eq!(r.bits_consumed(), 0, "aligned cursor does not move");
assert_eq!(r.read_bits(3).unwrap(), 0b101);
r.align_to_byte();
assert_eq!(r.bits_consumed(), 8);
assert_eq!(r.bits_remaining(), 8);
assert_eq!(r.read_bits(8).unwrap(), 0xCD);
r.align_to_byte();
assert_eq!(r.bits_consumed(), 16, "align at end of input stays put");
assert_eq!(r.read_bits(1), Err(Error::UnexpectedEof));
}
#[test]
fn width_validation() {
let mut r = BitReader::new(&[0xFF; 16]);
assert_eq!(r.read_bits(0), Err(Error::BitOverflow));
assert_eq!(r.read_bits(65), Err(Error::BitOverflow));
assert_eq!(r.read_signed_bits(0), Err(Error::BitOverflow));
assert_eq!(r.bits_consumed(), 0);
let mut storage = [0u8; 16];
let mut w = BitWriter::new(&mut storage);
assert_eq!(w.write_bits(0, 0), Err(Error::BitOverflow));
assert_eq!(w.write_bits(0, 65), Err(Error::BitOverflow));
assert_eq!(w.write_signed_bits(0, 0), Err(Error::BitOverflow));
assert_eq!(w.write_signed_bits(0, 65), Err(Error::BitOverflow));
assert_eq!(w.bits_written(), 0);
}
#[test]
fn full_width_fields_at_unaligned_offsets() {
for offset in 0..8u32 {
let mut storage = [0u8; 10];
let mut w = BitWriter::new(&mut storage);
if offset > 0 {
w.write_bits(0, offset).unwrap();
}
w.write_bits(0xDEAD_BEEF_0123_4567, 64).unwrap();
assert_eq!(w.write_bits(u64::MAX, 64), Err(Error::BufferFull));
let n = w.finish();
let mut r = BitReader::new(&storage[..n]);
if offset > 0 {
assert_eq!(r.read_bits(offset).unwrap(), 0);
}
assert_eq!(r.read_bits(64).unwrap(), 0xDEAD_BEEF_0123_4567);
}
}
#[test]
fn reads_and_writes_spanning_several_bytes() {
let mut storage = [0u8; 4];
let mut w = BitWriter::new(&mut storage);
w.write_bits(0b1, 1).unwrap();
w.write_bits(0x1_2345, 17).unwrap();
w.write_bits(0x3FFF, 14).unwrap();
assert_eq!(w.bits_written(), 32);
assert_eq!(w.write_bool(true), Err(Error::BufferFull));
assert_eq!(w.finish(), 4);
let mut r = BitReader::new(&storage);
assert_eq!(r.read_bits(1).unwrap(), 1);
assert_eq!(r.read_bits(17).unwrap(), 0x1_2345);
assert_eq!(r.read_bits(14).unwrap(), 0x3FFF);
assert_eq!(r.read_bool(), Err(Error::UnexpectedEof));
}
#[test]
fn signed_fields() {
for n in [1u32, 2, 7, 8, 13, 63, 64] {
let min = if n == 64 {
i64::MIN
} else {
-(1i64 << (n - 1))
};
let max = if n == 64 {
i64::MAX
} else {
(1i64 << (n - 1)) - 1
};
for v in [min, max, 0, -1] {
let mut storage = [0u8; 8];
let mut w = BitWriter::new(&mut storage);
w.write_signed_bits(v, n).unwrap();
let bytes = w.finish();
let mut r = BitReader::new(&storage[..bytes]);
assert_eq!(r.read_signed_bits(n).unwrap(), v, "n={n} v={v}");
}
if n < 64 {
let mut storage = [0u8; 8];
let mut w = BitWriter::new(&mut storage);
assert_eq!(w.write_signed_bits(max + 1, n), Err(Error::BitOverflow));
assert_eq!(w.write_signed_bits(min - 1, n), Err(Error::BitOverflow));
}
}
assert_eq!(sign_extend(0b1, 1), -1);
assert_eq!(signed_to_bits(-1, 1), Some(1));
assert_eq!(signed_to_bits(1, 1), None);
}
#[test]
fn bit_capacity_saturates() {
assert_eq!(bit_capacity(0), 0);
assert_eq!(bit_capacity(3), 24);
assert_eq!(bit_capacity(usize::MAX), (usize::MAX / 8) * 8);
assert_eq!(bit_capacity(usize::MAX) % 8, 0);
}
#[test]
fn align_rounds_up_to_byte() {
let mut storage = [0u8; 2];
let mut w = BitWriter::new(&mut storage);
w.write_bits(0b101, 3).unwrap();
w.align_to_byte().unwrap();
w.write_bits(0xFF, 8).unwrap();
assert_eq!(w.finish(), 2);
assert_eq!(storage, [0b1010_0000, 0xFF]);
}
}