#[derive(Debug, Clone, Copy, Default)]
pub struct BitReader {
acc: u64,
nbits: u32,
half: HalfWord,
}
#[derive(Debug, Clone, Copy, Default)]
struct HalfWord {
pending_low: Option<u8>,
}
impl BitReader {
pub const fn new() -> Self {
Self {
acc: 0,
nbits: 0,
half: HalfWord { pending_low: None },
}
}
pub fn feed(&mut self, byte: u8) {
match self.half.pending_low {
None => {
self.half.pending_low = Some(byte);
}
Some(low) => {
self.half.pending_low = None;
debug_assert!(self.nbits + 16 <= 64);
let word = ((byte as u64) << 8) | (low as u64);
self.acc |= word << (48 - self.nbits);
self.nbits += 16;
}
}
}
#[allow(dead_code)]
pub const fn has_partial_word(&self) -> bool {
self.half.pending_low.is_some()
}
pub const fn can_accept_word(&self) -> bool {
self.nbits + 16 <= 64
}
pub const fn bits_available(&self) -> u32 {
self.nbits
}
pub fn peek(&self, n: u32) -> u32 {
debug_assert!(n <= 32);
debug_assert!(n <= self.nbits);
if n == 0 {
return 0;
}
((self.acc >> (64 - n)) & ((1u64 << n) - 1)) as u32
}
pub fn drop_bits(&mut self, n: u32) {
debug_assert!(n <= self.nbits);
self.acc <<= n;
self.nbits -= n;
}
pub fn align_to_word(&mut self) {
let drop = self.nbits & 15;
self.drop_bits(drop);
}
#[allow(dead_code)]
pub fn reset(&mut self) {
self.acc = 0;
self.nbits = 0;
self.half = HalfWord { pending_low: None };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn msb_first_within_word() {
let mut r = BitReader::new();
r.feed(0xAB);
assert_eq!(r.bits_available(), 0); r.feed(0xCD);
assert_eq!(r.bits_available(), 16);
assert_eq!(r.peek(4), 0xC);
r.drop_bits(4);
assert_eq!(r.peek(4), 0xD);
r.drop_bits(4);
assert_eq!(r.peek(8), 0xAB);
}
#[test]
fn aligned_drops_fragment() {
let mut r = BitReader::new();
r.feed(0x00);
r.feed(0xFF);
r.drop_bits(3);
assert_eq!(r.bits_available(), 13);
r.align_to_word();
assert_eq!(r.bits_available(), 0);
}
}