pub struct BitWriter {
data: Vec<u8>,
bit_buf: u8,
bits_in_buf: u8,
}
impl BitWriter {
#[must_use]
pub fn new() -> Self {
Self {
data: Vec::new(),
bit_buf: 0,
bits_in_buf: 0,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
bit_buf: 0,
bits_in_buf: 0,
}
}
#[must_use]
pub fn byte_len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn write_bit(&mut self, bit: u8) {
self.bit_buf = (self.bit_buf << 1) | (bit & 1);
self.bits_in_buf += 1;
if self.bits_in_buf == 8 {
let byte = self.bit_buf;
self.data.push(byte);
if byte == 0xFF {
self.data.push(0x00);
}
self.bit_buf = 0;
self.bits_in_buf = 0;
}
}
#[inline]
pub fn write_bits(&mut self, value: u32, n: u8) {
for i in (0..n).rev() {
self.write_bit(((value >> i) & 1) as u8);
}
}
#[must_use]
pub fn finish(mut self) -> Vec<u8> {
if self.bits_in_buf > 0 {
let byte = self.bit_buf << (8 - self.bits_in_buf);
self.data.push(byte);
if byte == 0xFF {
self.data.push(0x00);
}
self.bit_buf = 0;
self.bits_in_buf = 0;
}
self.data
}
}
impl Default for BitWriter {
fn default() -> Self {
Self::new()
}
}
pub fn encode_golomb_unsigned_limited(
writer: &mut BitWriter,
value: i32,
k: i32,
limit: i32,
qbpp: u8,
) {
let overflow_threshold = limit - k - 1;
let unary = value >> k;
if unary >= overflow_threshold {
for _ in 0..overflow_threshold {
writer.write_bit(0);
}
writer.write_bit(1);
writer.write_bits((value + 1) as u32, qbpp);
} else {
for _ in 0..unary {
writer.write_bit(0);
}
writer.write_bit(1);
let suffix = if k > 0 { value & ((1 << k) - 1) } else { 0 };
writer.write_bits(suffix as u32, k as u8);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jpegls::golomb::{
compute_limit, compute_qbpp, decode_golomb_unsigned_limited, BitReader,
};
#[test]
fn byte_stuffing_inserts_zero_after_ff() {
let mut w = BitWriter::new();
w.write_bits(0xFF, 8);
let out = w.finish();
assert_eq!(
out,
vec![0xFF, 0x00],
"0xFF data byte must be followed by 0x00"
);
}
#[test]
fn flush_pads_final_byte_with_zeros() {
let mut w = BitWriter::new();
w.write_bit(1);
w.write_bit(0);
w.write_bit(1);
let out = w.finish();
assert_eq!(out, vec![0xA0]);
}
#[test]
fn writer_reader_bit_roundtrip() {
let mut w = BitWriter::new();
let pattern = [1u8, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1];
for &b in &pattern {
w.write_bit(b);
}
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
for (i, &expected) in pattern.iter().enumerate() {
let got = r.read_bit().expect("bit should be readable");
assert_eq!(got as u8, expected, "bit {i} mismatch");
}
}
#[test]
fn golomb_normal_roundtrip_small_values() {
let max_val = 255i32;
let limit = compute_limit(max_val);
let qbpp = compute_qbpp(max_val);
for k in 0..=8i32 {
for value in 0..=64i32 {
let mut w = BitWriter::new();
encode_golomb_unsigned_limited(&mut w, value, k, limit, qbpp);
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
let decoded = decode_golomb_unsigned_limited(&mut r, k, limit, qbpp)
.expect("golomb value should decode");
assert_eq!(decoded, value, "k={k} value={value} round-trip failed");
}
}
}
#[test]
fn golomb_overflow_roundtrip_large_values() {
let max_val = 255i32;
let limit = compute_limit(max_val);
let qbpp = compute_qbpp(max_val);
for k in 0..=4i32 {
for value in [200i32, 255, 300, 400, 510] {
let mut w = BitWriter::new();
encode_golomb_unsigned_limited(&mut w, value, k, limit, qbpp);
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
let decoded = decode_golomb_unsigned_limited(&mut r, k, limit, qbpp)
.expect("overflow golomb value should decode");
assert_eq!(
decoded, value,
"overflow k={k} value={value} round-trip failed"
);
}
}
}
#[test]
fn golomb_roundtrip_through_stuffed_ff() {
let max_val = 255i32;
let limit = compute_limit(max_val);
let qbpp = compute_qbpp(max_val);
let values = [510i32, 0, 1, 255, 7, 300, 2, 4, 510, 510];
let k = 0;
let mut w = BitWriter::new();
for &v in &values {
encode_golomb_unsigned_limited(&mut w, v, k, limit, qbpp);
}
let bytes = w.finish();
let mut r = BitReader::new(&bytes);
for (i, &v) in values.iter().enumerate() {
let decoded = decode_golomb_unsigned_limited(&mut r, k, limit, qbpp)
.unwrap_or_else(|| panic!("value {i} should decode"));
assert_eq!(decoded, v, "value {i} mismatch through stuffed stream");
}
}
}