use super::TRUNCATED;
use crate::{Error, Result};
pub(super) struct BitReader<'a> {
input: &'a [u8],
pos: usize,
buf: u64,
count: u32,
}
impl<'a> BitReader<'a> {
pub(super) fn new(input: &'a [u8]) -> Self {
Self {
input,
pos: 0,
buf: 0,
count: 0,
}
}
fn refill(&mut self, n: u32) {
while self.count < n {
let Some(&byte) = self.input.get(self.pos) else {
return;
};
self.buf |= u64::from(byte) << self.count;
self.pos += 1;
self.count += 8;
}
}
pub(super) fn bits(&mut self, n: u32) -> Result<u32> {
debug_assert!(n <= 16);
self.refill(n);
if self.count < n {
return Err(Error::InvalidFormat(TRUNCATED));
}
let value = self.buf & ((1u64 << n) - 1);
self.buf >>= n;
self.count -= n;
Ok(value as u32)
}
pub(super) fn peek(&mut self, n: u32) -> (u32, u32) {
debug_assert!(n <= 16);
self.refill(n);
let value = self.buf & ((1u64 << n) - 1);
(value as u32, self.count.min(n))
}
pub(super) fn consume(&mut self, n: u32) {
debug_assert!(n <= self.count);
let n = n.min(self.count);
self.buf >>= n;
self.count -= n;
}
pub(super) fn align_to_byte(&mut self) {
let whole_bytes = (self.count / 8) as usize;
self.pos -= whole_bytes;
self.buf = 0;
self.count = 0;
}
pub(super) fn bytes(&mut self, n: usize) -> Result<&'a [u8]> {
debug_assert_eq!(self.count, 0);
let end = self
.pos
.checked_add(n)
.ok_or(Error::InvalidFormat(TRUNCATED))?;
let slice = self
.input
.get(self.pos..end)
.ok_or(Error::InvalidFormat(TRUNCATED))?;
self.pos = end;
Ok(slice)
}
}
pub(super) struct BitWriter<'a> {
out: &'a mut Vec<u8>,
buf: u64,
count: u32,
}
impl<'a> BitWriter<'a> {
pub(super) fn new(out: &'a mut Vec<u8>) -> Self {
Self {
out,
buf: 0,
count: 0,
}
}
pub(super) fn bits(&mut self, value: u32, n: u32) {
debug_assert!(n <= 16);
let mask = (1u64 << n) - 1;
self.buf |= (u64::from(value) & mask) << self.count;
self.count += n;
while self.count >= 8 {
self.out.push(self.buf as u8);
self.buf >>= 8;
self.count -= 8;
}
}
pub(super) fn code(&mut self, code: u32, n: u32) {
self.bits(reverse(code, n), n);
}
pub(super) fn align_to_byte(&mut self) {
if self.count > 0 {
self.bits(0, 8 - self.count);
}
}
pub(super) fn bytes(&mut self, bytes: &[u8]) {
debug_assert_eq!(self.count, 0);
self.out.extend_from_slice(bytes);
}
pub(super) fn bit_offset(&self) -> u32 {
self.count
}
pub(super) fn finish(mut self) {
self.align_to_byte();
}
}
pub(super) fn reverse(code: u32, n: u32) -> u32 {
if n == 0 {
return 0;
}
code.reverse_bits() >> (32 - n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads_fields_lsb_first_across_bytes() {
let mut r = BitReader::new(&[0b1010_1101, 0b0000_0011]);
assert_eq!(r.bits(1).unwrap(), 1);
assert_eq!(r.bits(2).unwrap(), 0b10);
assert_eq!(r.bits(7).unwrap(), 0b11_10101);
assert_eq!(r.bits(6).unwrap(), 0);
assert_eq!(r.bits(1), Err(Error::InvalidFormat(TRUNCATED)));
}
#[test]
fn zero_bit_read_is_zero_even_at_end() {
let mut r = BitReader::new(&[]);
assert_eq!(r.bits(0).unwrap(), 0);
assert_eq!(r.bits(1), Err(Error::InvalidFormat(TRUNCATED)));
}
#[test]
fn peek_reports_available_bits_at_end() {
let mut r = BitReader::new(&[0xFF]);
assert_eq!(r.peek(15), (0xFF, 8));
r.consume(3);
assert_eq!(r.peek(15), (0x1F, 5));
}
#[test]
fn align_returns_buffered_bytes_to_the_input() {
let mut r = BitReader::new(&[0b0000_0101, 0xAA, 0xBB, 0xCC]);
assert_eq!(r.bits(3).unwrap(), 0b101);
let _ = r.peek(16); r.align_to_byte();
assert_eq!(r.bytes(2).unwrap(), &[0xAA, 0xBB]);
assert_eq!(r.bytes(1).unwrap(), &[0xCC]);
assert_eq!(r.bytes(1), Err(Error::InvalidFormat(TRUNCATED)));
assert_eq!(r.bytes(usize::MAX), Err(Error::InvalidFormat(TRUNCATED)));
}
#[test]
fn writer_round_trips_with_reader() {
let mut out = Vec::new();
let mut w = BitWriter::new(&mut out);
w.bits(1, 1);
w.bits(0b10, 2);
w.code(0b0011_0000, 8);
assert_eq!(w.bit_offset(), 3);
w.align_to_byte();
w.bytes(&[0x12, 0x34]);
w.bits(0x1FF, 9);
w.finish();
let mut r = BitReader::new(&out);
assert_eq!(r.bits(1).unwrap(), 1);
assert_eq!(r.bits(2).unwrap(), 0b10);
assert_eq!(reverse(r.bits(8).unwrap(), 8), 0b0011_0000);
r.align_to_byte();
assert_eq!(r.bytes(2).unwrap(), &[0x12, 0x34]);
assert_eq!(r.bits(9).unwrap(), 0x1FF);
assert_eq!(r.bits(7).unwrap(), 0);
assert!(r.bits(1).is_err());
}
#[test]
fn reverse_reverses_low_bits() {
assert_eq!(reverse(0b001, 3), 0b100);
assert_eq!(reverse(0b1101, 4), 0b1011);
assert_eq!(reverse(0, 0), 0);
assert_eq!(reverse(0x7FFF, 15), 0x7FFF);
}
}