use super::bit_dst::BitDst;
use std::io;
use std::mem;
pub const ACCUM_MAX: isize = mem::size_of::<usize>() as isize * 8 - 1;
pub struct BitWriter<'a, T: BitDst> {
accum_data: usize,
accum_bits: isize,
inner: &'a mut T,
}
impl<'a, T: BitDst> BitWriter<'a, T> {
#[inline(always)]
pub fn new(inner: &'a mut T, len: usize) -> io::Result<Self> {
inner.allocate(len + mem::size_of::<usize>())?;
Ok(Self { accum_data: 0, accum_bits: 0, inner })
}
#[inline(always)]
pub fn flush(&mut self) {
debug_assert!(0 <= self.accum_bits);
debug_assert!(self.accum_bits <= ACCUM_MAX);
let n_bytes = self.accum_bits as usize / 8;
unsafe { self.inner.push_bytes_unchecked(self.accum_data, n_bytes) };
self.accum_data >>= n_bytes * 8;
self.accum_bits -= n_bytes as isize * 8;
debug_assert!(0 <= self.accum_bits);
debug_assert!(self.accum_bits <= 7);
debug_assert!(self.accum_data >> self.accum_bits == 0);
}
#[inline(always)]
pub unsafe fn push_unchecked(&mut self, bits: usize, n_bits: usize) {
debug_assert!(0 <= self.accum_bits + n_bits as isize);
debug_assert!(self.accum_bits + n_bits as isize <= ACCUM_MAX);
debug_assert!(bits >> n_bits == 0);
self.accum_data |= bits << self.accum_bits;
self.accum_bits += n_bits as isize;
}
#[inline(always)]
pub fn finalize(mut self) -> io::Result<usize> {
assert!(0 <= self.accum_bits);
assert!(self.accum_bits <= ACCUM_MAX);
let n_bytes = (self.accum_bits as usize + 7) / 8;
unsafe { self.inner.push_bytes_unchecked(self.accum_data, n_bytes) };
self.accum_bits -= n_bytes as isize * 8;
debug_assert!(-7 <= self.accum_bits);
debug_assert!(self.accum_bits <= 0);
self.inner.finalize()?;
Ok(-self.accum_bits as usize)
}
}
#[cfg(test)]
mod tests {
use test_kit::Fibonacci;
use super::*;
const FIB_32_BS: [u8; 41] = [
0x7B, 0xB1, 0xAB, 0x78, 0x67, 0x21, 0xD3, 0xF3, 0x8A, 0xB9, 0x7D, 0x8F, 0x31, 0xB4, 0x0A,
0xB6, 0x69, 0x61, 0xF5, 0xA5, 0x18, 0xFF, 0x06, 0xA9, 0x8D, 0x28, 0x19, 0xA3, 0x5D, 0xE8,
0xDF, 0xB9, 0x6C, 0xD6, 0x62, 0x1F, 0x45, 0x96, 0xBB, 0x15, 0x29,
];
const FIB_32_OFF: usize = 2;
#[test]
fn test_bit_writer_fibonacci() -> io::Result<()> {
let mut vec = Vec::new();
let mut wtr = BitWriter::new(&mut vec, FIB_32_BS.len())?;
let fib: Vec<u32> = Fibonacci::default().take(32).collect();
for &v in fib.iter() {
wtr.flush();
unsafe { wtr.push_unchecked(v as usize, 32 - v.leading_zeros() as usize) };
}
let off = wtr.finalize()?;
assert_eq!(FIB_32_BS.as_ref(), vec.as_slice());
assert_eq!(off, FIB_32_OFF);
Ok(())
}
}