use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
pub struct Hcb {
pub len: &'static [u8],
pub cw: &'static [u32],
pub dim: u8,
pub cb_mod: u32,
pub cb_mod2: u32,
pub cb_mod3: u32,
pub cb_off: i32,
pub unsigned: bool,
}
pub fn huff_decode(br: &mut BitReader<'_>, lens: &[u8], cws: &[u32]) -> Result<u32> {
debug_assert_eq!(lens.len(), cws.len());
let mut code: u32 = 0;
let mut width: u8 = 0;
while width < 32 {
let b = br.read_u32(1)?;
code = (code << 1) | b;
width += 1;
for (i, &l) in lens.iter().enumerate() {
if l == width && cws[i] == code {
return Ok(i as u32);
}
}
}
Err(Error::invalid("ac4: no matching Huffman codeword"))
}
include!("huffman_tables.rs");
pub fn asf_hcb(cb_idx: u32) -> Option<&'static Hcb> {
match cb_idx {
1 => Some(&HCB1),
2 => Some(&HCB2),
3 => Some(&HCB3),
4 => Some(&HCB4),
5 => Some(&HCB5),
6 => Some(&HCB6),
7 => Some(&HCB7),
8 => Some(&HCB8),
9 => Some(&HCB9),
10 => Some(&HCB10),
11 => Some(&HCB11),
_ => None,
}
}
pub fn split_qspec(hcb: &Hcb, mut cb_idx: u32, out: &mut [i32]) {
if hcb.dim == 4 {
debug_assert!(out.len() >= 4);
let q1 = (cb_idx / hcb.cb_mod3) as i32 - hcb.cb_off;
cb_idx -= (q1 + hcb.cb_off) as u32 * hcb.cb_mod3;
let q2 = (cb_idx / hcb.cb_mod2) as i32 - hcb.cb_off;
cb_idx -= (q2 + hcb.cb_off) as u32 * hcb.cb_mod2;
let q3 = (cb_idx / hcb.cb_mod) as i32 - hcb.cb_off;
cb_idx -= (q3 + hcb.cb_off) as u32 * hcb.cb_mod;
let q4 = cb_idx as i32 - hcb.cb_off;
out[0] = q1;
out[1] = q2;
out[2] = q3;
out[3] = q4;
} else {
debug_assert!(out.len() >= 2);
let q1 = (cb_idx / hcb.cb_mod) as i32 - hcb.cb_off;
cb_idx -= (q1 + hcb.cb_off) as u32 * hcb.cb_mod;
let q2 = cb_idx as i32 - hcb.cb_off;
out[0] = q1;
out[1] = q2;
}
}
pub fn ext_decode(br: &mut BitReader<'_>) -> Result<u32> {
let mut n_ext: u32 = 0;
loop {
let b = br.read_u32(1)?;
if b == 0 {
break;
}
n_ext += 1;
}
let bits = n_ext + 4;
let ext_val = br.read_u32(bits)?;
Ok((1u32 << (n_ext + 4)) + ext_val)
}
#[cfg(test)]
mod tests {
use super::*;
use oxideav_core::bits::BitWriter;
#[test]
fn scalefac_roundtrip_index_60() {
let mut bw = BitWriter::new();
bw.write_u32(HCB_SCALEFAC_CW[60], HCB_SCALEFAC_LEN[60] as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let idx = huff_decode(&mut br, HCB_SCALEFAC_LEN, HCB_SCALEFAC_CW).unwrap();
assert_eq!(idx, 60);
}
#[test]
fn snf_short_codeword() {
let mut bw = BitWriter::new();
bw.write_u32(HCB_SNF_CW[13], HCB_SNF_LEN[13] as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let idx = huff_decode(&mut br, HCB_SNF_LEN, HCB_SNF_CW).unwrap();
assert_eq!(idx, 13);
}
#[test]
fn hcb1_known_entry() {
let mut bw = BitWriter::new();
bw.write_u32(HCB1.cw[40], HCB1.len[40] as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let idx = huff_decode(&mut br, HCB1.len, HCB1.cw).unwrap();
assert_eq!(idx, 40);
}
#[test]
fn split_qspec_dim2_unsigned() {
let mut out = [0i32; 4];
split_qspec(&HCB5, 40, &mut out);
assert_eq!(out[0], 0);
assert_eq!(out[1], 0);
}
#[test]
fn split_qspec_dim4_signed() {
let mut out = [0i32; 4];
split_qspec(&HCB1, 40, &mut out);
assert_eq!(out, [0, 0, 0, 0]);
}
#[test]
fn ext_decode_n0_v0() {
let mut bw = BitWriter::new();
bw.write_u32(0, 1); bw.write_u32(0, 4); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
assert_eq!(ext_decode(&mut br).unwrap(), 16);
}
#[test]
fn ext_decode_n1_v5() {
let mut bw = BitWriter::new();
bw.write_u32(1, 1);
bw.write_u32(0, 1);
bw.write_u32(5, 5);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
assert_eq!(ext_decode(&mut br).unwrap(), 37);
}
fn all_asf_tables() -> Vec<(&'static str, &'static [u8], &'static [u32])> {
vec![
("ASF_HCB_SCALEFAC", HCB_SCALEFAC_LEN, HCB_SCALEFAC_CW),
("ASF_HCB_SNF", HCB_SNF_LEN, HCB_SNF_CW),
("ASF_HCB_1", HCB1_LEN, HCB1_CW),
("ASF_HCB_2", HCB2_LEN, HCB2_CW),
("ASF_HCB_3", HCB3_LEN, HCB3_CW),
("ASF_HCB_4", HCB4_LEN, HCB4_CW),
("ASF_HCB_5", HCB5_LEN, HCB5_CW),
("ASF_HCB_6", HCB6_LEN, HCB6_CW),
("ASF_HCB_7", HCB7_LEN, HCB7_CW),
("ASF_HCB_8", HCB8_LEN, HCB8_CW),
("ASF_HCB_9", HCB9_LEN, HCB9_CW),
("ASF_HCB_10", HCB10_LEN, HCB10_CW),
("ASF_HCB_11", HCB11_LEN, HCB11_CW),
]
}
#[test]
fn all_asf_tables_decode_shortest_entry() {
for (name, lens, cws) in all_asf_tables() {
let (sym_idx, &min_len) = lens
.iter()
.enumerate()
.min_by_key(|(_, &l)| l)
.expect("non-empty codebook");
let cw = cws[sym_idx];
let mut bw = BitWriter::new();
bw.write_u32(cw, min_len as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let got = huff_decode(&mut br, lens, cws)
.unwrap_or_else(|e| panic!("{name}: decode failed at sym_idx={sym_idx}: {e:?}"));
assert_eq!(
got as usize, sym_idx,
"{name}: decoded {got}, expected {sym_idx} (cw=0x{cw:x}, len={min_len})"
);
}
}
#[test]
fn all_asf_tables_decode_last_entry() {
for (name, lens, cws) in all_asf_tables() {
let last = lens.len() - 1;
let l = lens[last];
let cw = cws[last];
let mut bw = BitWriter::new();
bw.write_u32(cw, l as u32);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let got = huff_decode(&mut br, lens, cws)
.unwrap_or_else(|e| panic!("{name}: decode failed for last entry: {e:?}"));
assert_eq!(
got as usize, last,
"{name}: decoded {got}, expected {last} (cw=0x{cw:x}, len={l})"
);
}
}
}