pub struct BitStream<'a> {
bytes: &'a [u8],
byte_offset: usize,
buffer: u64,
bits_in_buffer: u32,
}
impl<'a> BitStream<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self {
bytes,
byte_offset: 0,
buffer: 0,
bits_in_buffer: 0,
}
}
pub fn read_bits(&mut self, n: u32) -> u64 {
debug_assert!(n <= 64, "cannot read more than 64 bits at once");
if n == 0 {
return 0;
}
while self.bits_in_buffer < n && self.byte_offset < self.bytes.len() {
self.buffer |= (self.bytes[self.byte_offset] as u64) << self.bits_in_buffer;
self.bits_in_buffer += 8;
self.byte_offset += 1;
}
let take = n.min(self.bits_in_buffer);
let mask = if take == 64 {
u64::MAX
} else {
(1u64 << take) - 1
};
let value = self.buffer & mask;
if take == 64 {
self.buffer = 0;
} else {
self.buffer >>= take;
}
self.bits_in_buffer -= take;
value
}
pub fn bits_remaining(&self) -> u64 {
self.bits_in_buffer as u64 + (self.bytes.len() - self.byte_offset) as u64 * 8
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_full_byte_lsb_first() {
let mut bs = BitStream::new(&[0b1010_0101]);
let mut bits = Vec::new();
for _ in 0..8 {
bits.push(bs.read_bits(1) as u8);
}
assert_eq!(bits, vec![1, 0, 1, 0, 0, 1, 0, 1]);
}
#[test]
fn unpacks_gear_change_data() {
let mut bs = BitStream::new(&[0x04, 0x03, 0x02, 0x01]);
assert_eq!(bs.read_bits(8), 0x04); assert_eq!(bs.read_bits(8), 0x03); assert_eq!(bs.read_bits(8), 0x02); assert_eq!(bs.read_bits(8), 0x01); assert_eq!(bs.bits_remaining(), 0);
}
#[test]
fn handles_non_byte_aligned_widths() {
let mut bs = BitStream::new(&[0xBC, 0x3A, 0x12]);
assert_eq!(bs.read_bits(12), 0xABC);
assert_eq!(bs.read_bits(12), 0x123);
}
#[test]
fn read_zero_bits_is_noop() {
let mut bs = BitStream::new(&[0xFF]);
assert_eq!(bs.read_bits(0), 0);
assert_eq!(bs.read_bits(8), 0xFF);
}
#[test]
fn exhausted_stream_returns_remaining_then_zeros() {
let mut bs = BitStream::new(&[0x05]); assert_eq!(bs.read_bits(4), 0x5); assert_eq!(bs.read_bits(8), 0); assert_eq!(bs.read_bits(8), 0); }
}