use crate::pumps::BitPump;
use alloc::{vec, vec::Vec};
const DECODE_CACHE_BITS: u32 = 13;
#[derive(Debug)]
pub(crate) struct HuffTable {
bits: [u32; 17],
huffval: [u32; 256],
nbits: u32,
hufftable: Vec<(u8, u8)>,
decodecache: [Option<(u8, i16)>; 1 << DECODE_CACHE_BITS],
}
struct MockPump {
bits: u64,
nbits: u32,
}
impl MockPump {
pub fn empty() -> Self {
MockPump { bits: 0, nbits: 0 }
}
pub fn set(&mut self, bits: u32, nbits: u32) {
self.bits = (bits as u64) << 32;
self.nbits = nbits + 32;
}
pub fn validbits(&self) -> i32 {
self.nbits as i32 - 32
}
}
impl BitPump for MockPump {
fn peek_bits(&mut self, num: u32) -> u32 {
(self.bits >> (self.nbits - num)) as u32
}
fn consume_bits(&mut self, num: u32) {
self.nbits -= num;
self.bits &= (1 << self.nbits) - 1;
}
}
impl HuffTable {
pub(crate) const fn empty() -> HuffTable {
HuffTable {
bits: [0; 17],
huffval: [0; 256],
nbits: 0,
hufftable: Vec::new(),
decodecache: [None; 1 << DECODE_CACHE_BITS],
}
}
pub(crate) fn new(bits: [u32; 17], huffval: [u32; 256]) -> HuffTable {
let mut tbl = HuffTable {
bits,
huffval,
nbits: 0,
hufftable: Vec::new(),
decodecache: [None; 1 << DECODE_CACHE_BITS],
};
tbl.initialize();
tbl
}
pub(crate) fn initialize(&mut self) {
self.nbits = 16;
for i in 0..16 {
if self.bits[16 - i] != 0 {
break;
}
self.nbits -= 1;
}
self.hufftable = vec![(0, 0); 1 << self.nbits];
let mut h = 0;
let mut pos = 0;
for len in 0..self.nbits {
for _ in 0..self.bits[len as usize + 1] {
for _ in 0..(1 << (self.nbits - len - 1)) {
self.hufftable[h] = (len as u8 + 1, self.huffval[pos] as u8);
h += 1;
}
pos += 1;
}
}
let mut pump = MockPump::empty();
for i in 0..(1 << DECODE_CACHE_BITS) {
pump.set(i, DECODE_CACHE_BITS);
let (mut bits, decode) = self.huff_decode_slow(&mut pump);
if pump.validbits() >= 0 {
if decode == -32768 {
debug_assert!(bits > 16);
bits -= 16;
}
self.decodecache[i as usize] = Some((bits, decode as i16));
}
}
}
#[inline]
pub(crate) fn huff_decode(&self, pump: &mut impl BitPump) -> i32 {
let code = pump.peek_bits(DECODE_CACHE_BITS) as usize;
if let Some((bits, decode)) = self.decodecache[code] {
pump.consume_bits(bits as u32);
decode as i32
} else {
let decode = self.huff_decode_slow(pump);
decode.1
}
}
#[inline]
pub(crate) fn huff_decode_slow(&self, pump: &mut impl BitPump) -> (u8, i32) {
let len = self.huff_len(pump);
(len.0 + len.1, self.huff_diff(pump, len))
}
#[inline]
pub(crate) fn huff_len(&self, pump: &mut impl BitPump) -> (u8, u8) {
let code = pump.peek_bits(self.nbits) as usize;
let (bits, len) = self.hufftable[code];
pump.consume_bits(bits as u32);
(bits, len)
}
#[inline]
pub(crate) fn huff_diff(&self, pump: &mut impl BitPump, input: (u8, u8)) -> i32 {
let (_, len) = input;
match len {
0 => 0,
16 => -32768,
len => {
let mut diff = pump.get_bits(len as u32) as i32;
if (diff & (1 << (len - 1))) == 0 {
diff -= (1 << len) - 1;
}
diff
}
}
}
}