pub struct BitReader<'a> {
data: &'a [u8],
pos: usize,
}
#[allow(dead_code)]
impl<'a> BitReader<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
pub fn remaining(&self) -> usize {
(self.data.len() * 8).saturating_sub(self.pos)
}
pub fn skip_bits(&mut self, n: usize) -> Option<()> {
let new = self.pos.checked_add(n)?;
if new > self.data.len() * 8 {
return None;
}
self.pos = new;
Some(())
}
pub fn align_to_byte(&mut self) {
let rem = self.pos % 8;
if rem != 0 {
self.pos += 8 - rem;
}
}
pub fn read_bit(&mut self) -> Option<u32> {
let byte = self.data.get(self.pos / 8)?;
let shift = 7 - (self.pos % 8);
self.pos += 1;
Some(((byte >> shift) & 1) as u32)
}
pub fn read_bits(&mut self, n: u32) -> Option<u32> {
let mut v = 0u32;
for _ in 0..n {
v = (v << 1) | self.read_bit()?;
}
Some(v)
}
pub fn read_ue(&mut self) -> Option<u32> {
let mut zeros = 0u32;
while self.read_bit()? == 0 {
zeros += 1;
if zeros > 31 {
return None; }
}
if zeros == 0 {
return Some(0);
}
let rest = self.read_bits(zeros)?;
Some((1u32 << zeros) - 1 + rest)
}
pub fn read_se(&mut self) -> Option<i32> {
let k = self.read_ue()?;
let val = k.div_ceil(2) as i32;
Some(if k % 2 == 1 { val } else { -val })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_bits_msb_first() {
let mut r = BitReader::new(&[0b1011_0010]);
assert_eq!(r.read_bit(), Some(1));
assert_eq!(r.read_bits(3), Some(0b011));
assert_eq!(r.read_bits(4), Some(0b0010));
assert_eq!(r.read_bit(), None);
}
#[test]
fn exp_golomb_ue_matches_spec_table() {
let bytes = [0b1010_0110, 0b0100_0011, 0b1000_0000];
let mut r = BitReader::new(&bytes);
assert_eq!(r.read_ue(), Some(0));
assert_eq!(r.read_ue(), Some(1));
assert_eq!(r.read_ue(), Some(2));
assert_eq!(r.read_ue(), Some(3));
assert_eq!(r.read_ue(), Some(6));
}
#[test]
fn exp_golomb_se_zigzag() {
let bytes = [0b1010_0110, 0b0100_0010, 0b1000_0000];
let mut r = BitReader::new(&bytes);
assert_eq!(r.read_se(), Some(0));
assert_eq!(r.read_se(), Some(1));
assert_eq!(r.read_se(), Some(-1));
assert_eq!(r.read_se(), Some(2));
assert_eq!(r.read_se(), Some(-2));
}
}