#[derive(Debug, Default)]
pub struct BitWriter {
bytes: Vec<u8>,
current: u8,
nbits: u8,
}
impl BitWriter {
pub fn new() -> Self {
Self {
bytes: Vec::new(),
current: 0,
nbits: 0,
}
}
pub fn with_capacity(cap: usize) -> Self {
Self {
bytes: Vec::with_capacity(cap),
current: 0,
nbits: 0,
}
}
pub fn write_bit(&mut self, bit: u8) {
let shift = 7 - self.nbits;
if bit & 1 != 0 {
self.current |= 1u8 << shift;
}
self.nbits += 1;
if self.nbits == 8 {
self.bytes.push(self.current);
self.current = 0;
self.nbits = 0;
}
}
pub fn write_bits_u32(&mut self, val: u32, n: u8) {
let count = n.min(32);
for i in (0..count).rev() {
let bit = ((val >> i) & 1) as u8;
self.write_bit(bit);
}
}
pub fn write_u8(&mut self, val: u8) {
self.write_bits_u32(u32::from(val), 8);
}
pub fn write_u16_be(&mut self, val: u16) {
self.write_bits_u32(u32::from(val), 16);
}
pub fn byte_len(&self) -> usize {
self.bytes.len()
}
pub fn is_empty(&self) -> bool {
self.bytes.is_empty() && self.nbits == 0
}
pub fn is_byte_aligned(&self) -> bool {
self.nbits == 0
}
pub fn byte_align(&mut self) {
if self.nbits != 0 {
self.bytes.push(self.current);
self.current = 0;
self.nbits = 0;
}
}
pub fn finish(mut self) -> Vec<u8> {
self.byte_align();
self.bytes
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jpegxs::bitreader::BitReader;
#[test]
fn write_bit_msb_first() {
let mut w = BitWriter::new();
for &b in &[1u8, 0, 1, 0, 0, 0, 1, 1] {
w.write_bit(b);
}
let out = w.finish();
assert_eq!(out, vec![0b1010_0011]);
}
#[test]
fn write_bits_u32_full_byte() {
let mut w = BitWriter::new();
w.write_bits_u32(0xAB, 8);
assert_eq!(w.finish(), vec![0xAB]);
}
#[test]
fn write_bits_crosses_byte_boundary() {
let mut w = BitWriter::new();
w.write_bits_u32(0xABC, 12);
assert_eq!(w.finish(), vec![0xAB, 0xC0]);
}
#[test]
fn write_u16_be_roundtrip_with_reader() {
let mut w = BitWriter::new();
w.write_u16_be(0x1234);
let out = w.finish();
let mut r = BitReader::new(&out);
assert_eq!(r.read_u16_be().unwrap(), 0x1234);
}
#[test]
fn roundtrip_mixed_widths_via_reader() {
let mut w = BitWriter::new();
w.write_bit(1);
w.write_bits_u32(0b101, 3);
w.write_u8(0xF0);
w.write_bits_u32(0x3FF, 10);
w.write_bit(0);
let out = w.finish();
let mut r = BitReader::new(&out);
assert_eq!(r.read_bit().unwrap(), 1);
assert_eq!(r.read_bits_u32(3).unwrap(), 0b101);
assert_eq!(r.read_u8().unwrap(), 0xF0);
assert_eq!(r.read_bits_u32(10).unwrap(), 0x3FF);
assert_eq!(r.read_bit().unwrap(), 0);
}
#[test]
fn byte_align_pads_with_zeros() {
let mut w = BitWriter::new();
w.write_bits_u32(0b111, 3);
w.byte_align();
assert_eq!(w.byte_len(), 1);
w.write_u8(0xAA);
assert_eq!(w.finish(), vec![0b1110_0000, 0xAA]);
}
#[test]
fn empty_writer_finishes_empty() {
let w = BitWriter::new();
assert!(w.is_empty());
assert!(w.finish().is_empty());
}
#[test]
fn write_bits_u32_zero_width_is_noop() {
let mut w = BitWriter::new();
w.write_bits_u32(0xFFFF_FFFF, 0);
assert!(w.is_empty());
}
#[test]
fn long_run_of_ones_roundtrips() {
let mut w = BitWriter::new();
for _ in 0..8 {
w.write_bit(1);
}
let out = w.finish();
assert_eq!(out, vec![0xFF]);
let mut r = BitReader::new(&out);
for _ in 0..8 {
assert_eq!(r.read_bit().unwrap(), 1);
}
}
#[test]
fn alignment_flag_tracks_state() {
let mut w = BitWriter::new();
assert!(w.is_byte_aligned());
w.write_bit(1);
assert!(!w.is_byte_aligned());
w.write_bits_u32(0, 7);
assert!(w.is_byte_aligned());
}
}