#![cfg_attr(
not(test),
expect(dead_code, reason = "used by the partition and pivco encoders")
)]
use alloc::vec::Vec;
pub(crate) const WRITE_MAX_BITS: usize = usize::BITS as usize - 1;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct TooSmall;
#[derive(Debug)]
pub(crate) struct BitWriter {
container: usize,
nb_bits: usize,
buffer: Vec<u8>,
ptr: usize,
limit: usize,
end: usize,
}
impl BitWriter {
pub(crate) fn new(capacity: usize) -> Self {
let word = size_of::<usize>();
Self {
container: 0,
nb_bits: 0,
buffer: Vec::with_capacity(capacity),
ptr: 0,
limit: if capacity < word {
0
} else {
capacity - word + 1
},
end: capacity,
}
}
fn put_bytes(&mut self, bytes: &[u8]) {
let overlap = (self.buffer.len() - self.ptr).min(bytes.len());
self.buffer[self.ptr..self.ptr + overlap].copy_from_slice(&bytes[..overlap]);
self.buffer.extend_from_slice(&bytes[overlap..]);
}
pub(crate) fn write(&mut self, value: usize, nb_bits: usize) {
debug_assert!(self.nb_bits + nb_bits <= WRITE_MAX_BITS);
let mask = 1usize.wrapping_shl(nb_bits as u32).wrapping_sub(1);
self.container |= (value & mask).wrapping_shl(self.nb_bits as u32);
self.nb_bits += nb_bits;
}
pub(crate) fn flush(&mut self) {
if self.ptr >= self.limit {
return;
}
let nb_bytes = self.nb_bits >> 3;
let bytes = self.container.to_le_bytes();
self.put_bytes(&bytes[..nb_bytes]);
self.ptr += nb_bytes;
self.nb_bits &= 7;
self.container = self.container.wrapping_shr((nb_bytes << 3) as u32);
}
pub(crate) fn finish(mut self) -> Result<Vec<u8>, TooSmall> {
let to_write = self.nb_bits.div_ceil(8);
if self.end - self.ptr < to_write {
return Err(TooSmall);
}
let bytes = self.container.to_le_bytes();
self.put_bytes(&bytes[..to_write.min(bytes.len())]);
let total = self.ptr + to_write;
self.buffer.truncate(total);
Ok(self.buffer)
}
pub(crate) fn write_exp_golomb(&mut self, mut value: u32, order: usize) {
if order > 0 {
self.write(value as usize, order);
value >>= order;
}
let nb_bits = value.wrapping_add(1).checked_ilog2().unwrap_or(0) as usize;
self.write(1usize << nb_bits, nb_bits + 1);
self.write(value.wrapping_add(1) as usize, nb_bits);
}
pub(crate) fn reserve_aligned_bits(
&mut self,
nb_bits: usize,
) -> Option<core::ops::Range<usize>> {
if nb_bits > usize::MAX - 7 {
return None;
}
let buffered = self.nb_bits.div_ceil(8);
if buffered > self.end - self.ptr {
return None;
}
let nb_bytes = nb_bits.div_ceil(8);
if nb_bytes > self.end - self.ptr - buffered {
return None;
}
if buffered != 0 {
let bytes = self.container.to_le_bytes();
self.put_bytes(&bytes[..buffered]);
self.ptr += buffered;
self.container = 0;
self.nb_bits = 0;
}
let start = self.ptr;
let wanted = start + nb_bytes;
if self.buffer.len() < wanted {
self.buffer.resize(wanted, 0);
}
self.ptr = start + nb_bits / 8;
self.nb_bits = nb_bits & 7;
self.container = 0;
Some(start..wanted)
}
pub(crate) fn reserved_mut(&mut self, range: core::ops::Range<usize>) -> &mut [u8] {
&mut self.buffer[range]
}
pub(crate) fn commit_reserved_bits(&mut self) {
if self.nb_bits == 0 {
self.container = 0;
return;
}
let mask = (1usize << self.nb_bits) - 1;
self.container = usize::from(self.buffer[self.ptr]) & mask;
}
}