crate::ix!();
#[derive(Getters, Setters, Builder)]
#[getset(get = "pub", set = "pub")]
pub struct BitStreamWriter<OStream: Default + Write> {
#[builder(default)]
ostream: Rc<RefCell<OStream>>,
#[builder(default)]
buffer: u8,
#[builder(default)]
offset: i32,
}
impl<OStream: Default + Write> Drop for BitStreamWriter<OStream> {
#[instrument(level = "trace", skip(self))]
fn drop(&mut self) {
info!("Dropping BitStreamWriter, flushing pending bits");
self.flush();
}
}
impl<OStream: Default + Write> BitStreamWriter<OStream> {
#[instrument(level = "trace", skip(ostream))]
pub fn new(ostream: Rc<RefCell<OStream>>) -> Self {
info!("Constructing BitStreamWriter");
Self {
ostream,
buffer: 0,
offset: 0,
}
}
#[instrument(level = "trace", skip(self))]
pub fn write(&mut self, data: u64, mut nbits: i32) {
info!("BitStreamWriter writing {} bits from data={}", nbits, data);
if nbits < 0 || nbits > 64 {
error!("Invalid nbits: {}", nbits);
panic!("nbits must be between 0 and 64");
}
while nbits > 0 {
let bits = (8 - self.offset).min(nbits);
let shift_amount = 64 - nbits;
let partial = ((data << shift_amount) >> (64 - 8 + self.offset)) as u8;
self.buffer |= partial;
self.offset += bits;
nbits -= bits;
if self.offset == 8 {
self.flush();
}
}
}
#[instrument(level = "trace", skip(self))]
pub fn flush(&mut self) {
use std::io::Write as _;
debug!("BitStreamWriter flush called, offset={}", self.offset);
if self.offset == 0 {
trace!("Nothing to flush (offset = 0)");
return;
}
let one_byte = [self.buffer];
self.ostream
.borrow_mut()
.write_all(&one_byte)
.expect("BitStreamWriter: underlying stream write failed");
self.buffer = 0;
self.offset = 0;
}
}
#[cfg(test)]
mod test_bitstream_writer {
use super::*;
use traced_test::traced_test;
#[derive(Default)]
struct MockOutput {
data: Vec<u8>,
}
impl MockOutput {
fn new() -> Self {
Self { data: vec![] }
}
}
impl Write for MockOutput {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.data.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[traced_test]
fn test_write_bits_simple() {
let mock_out = std::rc::Rc::new(std::cell::RefCell::new(MockOutput::new()));
let mut writer = BitStreamWriter::new(mock_out.clone());
writer.write(0b1010, 4);
writer.write(0b0101, 4);
assert_eq!(mock_out.borrow().data, vec![0xA5]);
writer.write(0b11110000, 8);
assert_eq!(mock_out.borrow().data, vec![0xA5, 0xF0]);
}
}