use super::bits::BitReader;
use super::error::{Error, Result};
use super::{huff_esc as esc, huff_pair as pair, huff_quad as quad};
use std::sync::LazyLock;
#[repr(C, align(32))]
#[derive(Clone, Copy)]
struct Sym {
q: [i32; 4],
mag: [f32; 4],
}
struct HuffTree {
left: Vec<u16>,
right: Vec<u16>,
leaf: Vec<i16>,
lut_sym: [i16; 4096],
lut_len: [u8; 4096],
syms: Vec<Sym>,
width: u8,
sign_bits: bool,
escape: bool,
}
impl HuffTree {
fn from_tables(
len: &[u8],
code: &[u16],
expand: fn(usize) -> [i32; 4],
width: u8,
sign_bits: bool,
escape: bool,
) -> Self {
let mut left = vec![0u16];
let mut right = vec![0u16];
let mut leaf = vec![-1i16];
let mut lut_sym = [-1i16; 4096];
let mut lut_len = [0u8; 4096];
for (idx, (&l, &c)) in len.iter().zip(code.iter()).enumerate() {
let mut node = 0u16;
for b in (0..l).rev() {
let bit = (c >> b) & 1;
let existing = if bit == 0 {
left[node as usize]
} else {
right[node as usize]
};
if existing == 0 {
let id = left.len() as u16;
left.push(0);
right.push(0);
leaf.push(-1);
if bit == 0 {
left[node as usize] = id;
} else {
right[node as usize] = id;
}
node = id;
} else {
node = existing;
}
}
leaf[node as usize] = idx as i16;
if l > 0 && l <= 12 {
let shift = 12 - l;
let base = (c as usize) << shift;
for extra in 0..(1usize << shift) {
lut_sym[base + extra] = idx as i16;
lut_len[base + extra] = l;
}
}
}
let mut syms = Vec::with_capacity(len.len());
for i in 0..len.len() {
let q = expand(i);
syms.push(Sym {
mag: [
super::spectrum::invquant(q[0]),
super::spectrum::invquant(q[1]),
super::spectrum::invquant(q[2]),
super::spectrum::invquant(q[3]),
],
q,
});
}
Self {
left,
right,
leaf,
lut_sym,
lut_len,
syms,
width,
sign_bits,
escape,
}
}
#[inline(always)]
fn decode(&self, br: &mut BitReader<'_>) -> Result<usize> {
if let Some(p) = br.try_peek12() {
let p = p as usize;
let n = self.lut_len[p];
if n != 0 {
br.eat(u32::from(n));
return Ok(self.lut_sym[p] as usize);
}
}
let mut node = 0u16;
loop {
let bit = br.read_bit()?;
let next = if bit {
self.right[node as usize]
} else {
self.left[node as usize]
};
if next == 0 {
return Err(Error::HuffmanInvalid);
}
node = next;
let sym = self.leaf[node as usize];
if sym >= 0 {
return Ok(sym as usize);
}
}
}
#[inline(always)]
fn load(&self, idx: usize) -> Sym {
debug_assert!(idx < self.syms.len());
unsafe { *self.syms.get_unchecked(idx) }
}
}
fn expand_signed_quad(idx: usize) -> [i32; 4] {
[
(idx / 27) as i32 - 1,
((idx / 9) % 3) as i32 - 1,
((idx / 3) % 3) as i32 - 1,
(idx % 3) as i32 - 1,
]
}
fn expand_unsigned_quad(idx: usize) -> [i32; 4] {
[
(idx / 27) as i32,
((idx / 9) % 3) as i32,
((idx / 3) % 3) as i32,
(idx % 3) as i32,
]
}
fn expand_signed_pair(idx: usize) -> [i32; 4] {
const DIM: usize = 9;
const LAV: i32 = 4;
[(idx / DIM) as i32 - LAV, (idx % DIM) as i32 - LAV, 0, 0]
}
fn expand_upair<const DIM: usize>(idx: usize) -> [i32; 4] {
[(idx / DIM) as i32, (idx % DIM) as i32, 0, 0]
}
static BOOKS: LazyLock<[HuffTree; 11]> = LazyLock::new(|| {
[
HuffTree::from_tables(
&quad::H1_LEN,
&quad::H1_CODE,
expand_signed_quad,
4,
false,
false,
),
HuffTree::from_tables(
&quad::H2_LEN,
&quad::H2_CODE,
expand_signed_quad,
4,
false,
false,
),
HuffTree::from_tables(
&quad::H3_LEN,
&quad::H3_CODE,
expand_unsigned_quad,
4,
true,
false,
),
HuffTree::from_tables(
&quad::H4_LEN,
&quad::H4_CODE,
expand_unsigned_quad,
4,
true,
false,
),
HuffTree::from_tables(
&pair::H5_LEN,
&pair::H5_CODE,
expand_signed_pair,
2,
false,
false,
),
HuffTree::from_tables(
&pair::H6_LEN,
&pair::H6_CODE,
expand_signed_pair,
2,
false,
false,
),
HuffTree::from_tables(
&pair::H7_LEN,
&pair::H7_CODE,
expand_upair::<8>,
2,
true,
false,
),
HuffTree::from_tables(
&pair::H8_LEN,
&pair::H8_CODE,
expand_upair::<8>,
2,
true,
false,
),
HuffTree::from_tables(
&pair::H9_LEN,
&pair::H9_CODE,
expand_upair::<13>,
2,
true,
false,
),
HuffTree::from_tables(
&pair::H10_LEN,
&pair::H10_CODE,
expand_upair::<13>,
2,
true,
false,
),
HuffTree::from_tables(
&esc::H11_LEN,
&esc::H11_CODE,
expand_upair::<17>,
2,
true,
true,
),
]
});
fn book(n: u8) -> Result<&'static HuffTree> {
let i = usize::from(n).wrapping_sub(1);
BOOKS.get(i).ok_or(Error::InvalidCodebook(n))
}
pub(crate) struct SpectralBook {
tree: &'static HuffTree,
}
impl SpectralBook {
pub(crate) fn open(cb: u8) -> Result<Self> {
Ok(Self { tree: book(cb)? })
}
#[inline(always)]
pub(crate) fn pull(&self, br: &mut BitReader<'_>) -> Result<(usize, [f32; 4])> {
let (_, mag) = read_tuple(self.tree, br)?;
Ok((usize::from(self.tree.width), mag))
}
pub(crate) fn fill(&self, br: &mut BitReader<'_>, gain: f32, dst: &mut [f32]) -> Result<()> {
let plain = !self.tree.sign_bits && !self.tree.escape;
if self.tree.width == 4 {
if plain {
fill_width::<4, true>(self.tree, br, gain, dst)
} else {
fill_width::<4, false>(self.tree, br, gain, dst)
}
} else if plain {
fill_width::<2, true>(self.tree, br, gain, dst)
} else {
fill_width::<2, false>(self.tree, br, gain, dst)
}
}
}
#[inline(always)]
fn read_tuple(tree: &HuffTree, br: &mut BitReader<'_>) -> Result<([i32; 4], [f32; 4])> {
let mut sym = tree.load(tree.decode(br)?);
let n = usize::from(tree.width);
if tree.sign_bits {
for i in 0..n {
if sym.q[i] != 0 && br.read_bit()? {
sym.q[i] = -sym.q[i];
sym.mag[i] = -sym.mag[i];
}
}
}
if tree.escape {
for i in 0..n {
if sym.q[i].abs() == 16 {
decode_esc(br, &mut sym.q[i])?;
sym.mag[i] = super::spectrum::invquant(sym.q[i]);
}
}
}
Ok((sym.q, sym.mag))
}
#[inline(always)]
fn next_mag<const PLAIN: bool>(tree: &HuffTree, br: &mut BitReader<'_>) -> Result<[f32; 4]> {
if PLAIN {
Ok(tree.load(tree.decode(br)?).mag)
} else {
Ok(read_tuple(tree, br)?.1)
}
}
fn fill_width<const W: usize, const PLAIN: bool>(
tree: &HuffTree,
br: &mut BitReader<'_>,
gain: f32,
dst: &mut [f32],
) -> Result<()> {
let mut i = 0usize;
while i + W <= dst.len() {
let mag = next_mag::<PLAIN>(tree, br)?;
for (slot, &m) in dst[i..i + W].iter_mut().zip(mag[..W].iter()) {
*slot = m * gain;
}
i += W;
}
if i < dst.len() {
let mag = next_mag::<PLAIN>(tree, br)?;
for (slot, &m) in dst[i..].iter_mut().zip(mag.iter()) {
*slot = m * gain;
}
}
Ok(())
}
pub fn decode_tuple(br: &mut BitReader<'_>, cb: u8, out: &mut [i32]) -> Result<usize> {
let tree = book(cb)?;
let (q, _) = read_tuple(tree, br)?;
let n = usize::from(tree.width);
out[..n].copy_from_slice(&q[..n]);
Ok(n)
}
fn decode_esc(br: &mut BitReader<'_>, v: &mut i32) -> Result<()> {
if v.abs() != 16 {
return Ok(());
}
let neg = *v < 0;
let mut n = 4u32;
while br.read_bit()? {
n += 1;
if n > 21 {
return Err(Error::HuffmanInvalid);
}
}
let off = br.read(n)?;
let mag = (1i32 << n) + off as i32;
*v = if neg { -mag } else { mag };
Ok(())
}