use crate::crc;
#[derive(Debug, Default, Clone)]
pub struct BitWriter {
buf: Vec<u8>,
accum: u64,
nbits: u32,
}
impl BitWriter {
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub fn len_bits(&self) -> usize {
self.buf.len() * 8 + self.nbits as usize
}
pub fn is_byte_aligned(&self) -> bool {
self.nbits == 0
}
pub fn as_bytes(&self) -> &[u8] {
debug_assert_eq!(self.nbits, 0, "as_bytes() requires byte alignment");
&self.buf
}
pub fn crc8(&self) -> u8 {
crc::crc8(self.as_bytes())
}
pub fn crc16(&self) -> u16 {
crc::crc16(self.as_bytes())
}
pub fn write_raw_u32(&mut self, val: u32, bits: u32) {
debug_assert!(bits <= 32);
if bits == 0 {
return;
}
let val = if bits < 32 {
val & ((1u32 << bits) - 1)
} else {
val
};
self.accum = (self.accum << bits) | val as u64;
self.nbits += bits;
while self.nbits >= 8 {
self.nbits -= 8;
self.buf.push((self.accum >> self.nbits) as u8);
}
}
pub fn write_raw_i32(&mut self, val: i32, bits: u32) {
self.write_raw_u32(val as u32, bits);
}
pub fn write_raw_u64(&mut self, val: u64, bits: u32) {
debug_assert!(bits <= 64);
if bits > 32 {
self.write_raw_u32((val >> 32) as u32, bits - 32);
self.write_raw_u32(val as u32, 32);
} else {
self.write_raw_u32(val as u32, bits);
}
}
pub fn write_zeroes(&mut self, mut bits: u32) {
while bits > 32 {
self.write_raw_u32(0, 32);
bits -= 32;
}
if bits > 0 {
self.write_raw_u32(0, bits);
}
}
pub fn write_raw_u32_little_endian(&mut self, val: u32) {
self.write_raw_u32(val & 0xff, 8);
self.write_raw_u32((val >> 8) & 0xff, 8);
self.write_raw_u32((val >> 16) & 0xff, 8);
self.write_raw_u32(val >> 24, 8);
}
pub fn write_byte_block(&mut self, vals: &[u8]) {
for &v in vals {
self.write_raw_u32(v as u32, 8);
}
}
pub fn write_unary_unsigned(&mut self, val: u32) {
self.write_zeroes(val);
self.write_raw_u32(1, 1);
}
pub fn write_raw_i64(&mut self, val: i64, bits: u32) {
self.write_raw_u64(val as u64, bits);
}
pub fn zero_pad_to_byte_boundary(&mut self) {
if self.nbits != 0 {
self.write_zeroes(8 - self.nbits);
}
}
pub fn write_utf8_u32(&mut self, val: u32) {
debug_assert_eq!(val & 0x8000_0000, 0, "write_utf8_u32 handles 31 bits");
if val < 0x80 {
self.write_raw_u32(val, 8);
} else if val < 0x800 {
self.write_raw_u32(0xC0 | (val >> 6), 8);
self.write_raw_u32(0x80 | (val & 0x3F), 8);
} else if val < 0x10000 {
self.write_raw_u32(0xE0 | (val >> 12), 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F), 8);
self.write_raw_u32(0x80 | (val & 0x3F), 8);
} else if val < 0x200000 {
self.write_raw_u32(0xF0 | (val >> 18), 8);
self.write_raw_u32(0x80 | ((val >> 12) & 0x3F), 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F), 8);
self.write_raw_u32(0x80 | (val & 0x3F), 8);
} else if val < 0x400_0000 {
self.write_raw_u32(0xF8 | (val >> 24), 8);
self.write_raw_u32(0x80 | ((val >> 18) & 0x3F), 8);
self.write_raw_u32(0x80 | ((val >> 12) & 0x3F), 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F), 8);
self.write_raw_u32(0x80 | (val & 0x3F), 8);
} else {
self.write_raw_u32(0xFC | (val >> 30), 8);
self.write_raw_u32(0x80 | ((val >> 24) & 0x3F), 8);
self.write_raw_u32(0x80 | ((val >> 18) & 0x3F), 8);
self.write_raw_u32(0x80 | ((val >> 12) & 0x3F), 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F), 8);
self.write_raw_u32(0x80 | (val & 0x3F), 8);
}
}
#[allow(dead_code)]
pub fn write_utf8_u64(&mut self, val: u64) {
debug_assert_eq!(
val & 0xFFFF_FFF0_0000_0000,
0,
"write_utf8_u64 handles 36 bits"
);
if val < 0x80 {
self.write_raw_u32(val as u32, 8);
} else if val < 0x800 {
self.write_raw_u32(0xC0 | (val >> 6) as u32, 8);
self.write_raw_u32(0x80 | (val & 0x3F) as u32, 8);
} else if val < 0x10000 {
self.write_raw_u32(0xE0 | (val >> 12) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | (val & 0x3F) as u32, 8);
} else if val < 0x200000 {
self.write_raw_u32(0xF0 | (val >> 18) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 12) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | (val & 0x3F) as u32, 8);
} else if val < 0x4000000 {
self.write_raw_u32(0xF8 | (val >> 24) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 18) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 12) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | (val & 0x3F) as u32, 8);
} else if val < 0x80000000 {
self.write_raw_u32(0xFC | (val >> 30) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 24) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 18) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 12) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | (val & 0x3F) as u32, 8);
} else {
self.write_raw_u32(0xFE, 8);
self.write_raw_u32(0x80 | ((val >> 30) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 24) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 18) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 12) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | ((val >> 6) & 0x3F) as u32, 8);
self.write_raw_u32(0x80 | (val & 0x3F) as u32, 8);
}
}
pub fn write_rice_signed_block(&mut self, vals: &[i32], parameter: u32) {
debug_assert!(parameter < 31);
let lsb_mask = (1u32 << parameter) - 1;
let stop = 1u32 << parameter;
for &val in vals {
let uval = ((val as u32) << 1) ^ ((val >> 31) as u32);
self.write_zeroes(uval >> parameter); self.write_raw_u32(stop | (uval & lsb_mask), 1 + parameter); }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_u32_msb_first_bytes() {
let mut bw = BitWriter::new();
bw.write_raw_u32(0xABCD, 16);
assert!(bw.is_byte_aligned());
assert_eq!(bw.as_bytes(), &[0xAB, 0xCD]);
assert_eq!(bw.len_bits(), 16);
}
#[test]
fn unaligned_then_aligned() {
let mut bw = BitWriter::new();
bw.write_raw_u32(0b101, 3);
bw.write_raw_u32(0b11110, 5);
assert!(bw.is_byte_aligned());
assert_eq!(bw.as_bytes(), &[0b10111110]);
}
#[test]
fn crosses_byte_and_word_boundaries() {
let mut bw = BitWriter::new();
bw.write_raw_u32(0xABC, 12);
bw.write_raw_u32(0xDEF, 12);
bw.write_raw_u32(0x42, 8);
assert_eq!(bw.as_bytes(), &[0xAB, 0xCD, 0xEF, 0x42]);
}
#[test]
fn raw_u64_splits_high_then_low() {
let mut bw = BitWriter::new();
bw.write_raw_u64(0x0123_4567_89AB_CDEF, 64);
assert_eq!(
bw.as_bytes(),
&[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]
);
}
#[test]
fn raw_u64_partial() {
let mut bw = BitWriter::new();
bw.write_raw_u64(0x3_FFFF_FFFF, 34); bw.write_zeroes(6); assert_eq!(bw.as_bytes(), &[0xFF, 0xFF, 0xFF, 0xFF, 0xC0]);
}
#[test]
fn int32_twos_complement_low_bits() {
let mut bw = BitWriter::new();
bw.write_raw_i32(-1, 4); bw.write_raw_i32(-2, 4); assert_eq!(bw.as_bytes(), &[0xFE]);
}
#[test]
fn unary_small_and_large() {
let mut bw = BitWriter::new();
bw.write_unary_unsigned(0); bw.write_unary_unsigned(3); bw.write_zeroes(3);
assert_eq!(bw.as_bytes(), &[0b10001000]);
let mut bw = BitWriter::new();
bw.write_unary_unsigned(10); bw.write_zeroes(5);
assert_eq!(bw.as_bytes(), &[0x00, 0b00100000]);
}
#[test]
fn zeroes_long_run() {
let mut bw = BitWriter::new();
bw.write_zeroes(80);
assert_eq!(bw.as_bytes(), &[0u8; 10]);
assert_eq!(bw.len_bits(), 80);
}
#[test]
fn byte_block_roundtrip() {
let mut bw = BitWriter::new();
bw.write_byte_block(b"fLaC");
assert_eq!(bw.as_bytes(), b"fLaC");
}
#[test]
fn little_endian_u32() {
let mut bw = BitWriter::new();
bw.write_raw_u32_little_endian(0x11223344);
assert_eq!(bw.as_bytes(), &[0x44, 0x33, 0x22, 0x11]);
}
}