#![allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
use super::DecompressError;
use alloc::vec::Vec;
pub(super) struct BitReader<'a> {
data: &'a [u8],
pos: usize,
bitbuf: u64,
bitcnt: u8,
}
impl<'a> BitReader<'a> {
pub(super) const fn new(data: &'a [u8]) -> Self {
Self {
data,
pos: 0,
bitbuf: 0,
bitcnt: 0,
}
}
#[inline(always)]
pub(super) const fn bitcnt(&self) -> u8 {
self.bitcnt
}
pub(super) fn byte_pos(&self) -> usize {
let buffered = usize::from(self.bitcnt >> 3);
self.pos.saturating_sub(buffered)
}
#[inline(always)]
#[allow(clippy::indexing_slicing, clippy::cast_possible_truncation)]
fn refill(&mut self) {
while self.bitcnt <= 32 {
let Some(chunk) = self.data.get(self.pos..self.pos + 4) else {
break;
};
let word = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
self.bitbuf |= u64::from(word) << self.bitcnt;
self.bitcnt += 32;
self.pos += 4;
}
while self.bitcnt <= 56 {
let Some(&byte) = self.data.get(self.pos) else {
break;
};
self.pos += 1;
self.bitbuf |= u64::from(byte) << self.bitcnt;
self.bitcnt += 8;
}
}
#[inline(always)]
fn ensure(
&mut self,
need: u8,
) -> Result<(), DecompressError> {
if self.bitcnt < need {
self.refill();
if self.bitcnt < need {
return Err(DecompressError::InvalidInput);
}
}
Ok(())
}
#[inline(always)]
pub(super) fn get_bits(
&mut self,
n: u8,
) -> Result<u32, DecompressError> {
if n == 0 {
return Ok(0);
}
if n > 24 {
return Err(DecompressError::InvalidInput);
}
self.ensure(n)?;
let mask = (1u32 << n) - 1;
let v = (self.bitbuf as u32) & mask;
self.bitbuf >>= n;
self.bitcnt -= n;
Ok(v)
}
#[inline(always)]
pub(super) const fn peek_bits(
&self,
n: u8,
) -> u32 {
let mask = (1u32 << n) - 1;
(self.bitbuf as u32) & mask
}
pub(super) fn peek_bits_available(
&mut self,
n: u8,
) -> Result<(u32, u8), DecompressError> {
if n == 0 {
return Ok((0, 0));
}
if n > 24 {
return Err(DecompressError::InvalidInput);
}
if self.bitcnt < n {
self.refill();
}
if self.bitcnt == 0 {
return Err(DecompressError::InvalidInput);
}
let have = if self.bitcnt < n {
self.bitcnt
} else {
n
};
let mask = (1u32 << have) - 1;
Ok(((self.bitbuf as u32) & mask, have))
}
#[inline(always)]
pub(super) const fn drop_bits(
&mut self,
n: u8,
) {
if n == 0 {
return;
}
self.bitbuf >>= n;
self.bitcnt -= n;
}
pub(super) const fn align_to_byte(&mut self) {
let rem = self.bitcnt & 7;
if rem != 0 {
self.bitbuf >>= rem;
self.bitcnt -= rem;
}
}
pub(super) fn get_aligned_byte(&mut self) -> Result<u8, DecompressError> {
self.align_to_byte();
if self.bitcnt >= 8 {
let b = self.bitbuf as u8;
self.bitbuf >>= 8;
self.bitcnt -= 8;
return Ok(b);
}
let b = self
.data
.get(self.pos)
.copied()
.ok_or(DecompressError::InvalidInput)?;
self.pos += 1;
Ok(b)
}
pub(super) fn copy_aligned_bytes(
&mut self,
out: &mut Vec<u8>,
len: usize,
max_out: usize,
) -> Result<(), DecompressError> {
self.align_to_byte();
if out.len().saturating_add(len) > max_out {
return Err(DecompressError::LimitExceeded);
}
out.reserve(len);
let mut left = len;
if left > 0 && self.bitcnt >= 8 {
let mut tmp = [0u8; 8];
let mut n = 0usize;
while left > 0 && self.bitcnt >= 8 && n < tmp.len() {
if let Some(slot) = tmp.get_mut(n) {
*slot = self.bitbuf as u8;
}
self.bitbuf >>= 8;
self.bitcnt -= 8;
n += 1;
left -= 1;
}
out.extend_from_slice(tmp.get(..n).unwrap_or(&[]));
}
if left == 0 {
return Ok(());
}
let end = self
.pos
.checked_add(left)
.ok_or(DecompressError::InvalidInput)?;
let slice = self
.data
.get(self.pos..end)
.ok_or(DecompressError::InvalidInput)?;
out.extend_from_slice(slice);
self.pos = end;
Ok(())
}
}