use crate::once::OnceBox;
use alloc::boxed::Box;
use alloc::vec::Vec;
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Bc1MatchEntry {
pub m_hi: u8,
pub m_lo: u8,
}
pub struct Bc1MatchTables {
pub match5_equals_1: [Bc1MatchEntry; 256],
pub match6_equals_1: [Bc1MatchEntry; 256],
pub match5_equals_0: [Bc1MatchEntry; 256],
pub match6_equals_0: [Bc1MatchEntry; 256],
}
#[inline]
fn iabs(x: i32) -> i32 {
x.abs()
}
fn prepare(table: &mut [Bc1MatchEntry; 256], expand: &[u8], size0: i32, size1: i32, sel: i32) {
for i in 0..256i32 {
let mut lowest_e = 256i32;
for lo in 0..size0 {
for hi in 0..size1 {
let lo_e = expand[lo as usize] as i32;
let hi_e = expand[hi as usize] as i32;
let e = if sel == 1 {
let mut e = iabs(((hi_e * 2 + lo_e) / 3) - i);
e += (iabs(hi_e - lo_e) * 3) / 100;
e
} else {
debug_assert_eq!(sel, 0);
iabs(hi_e - i)
};
if e < lowest_e {
table[i as usize].m_hi = hi as u8;
table[i as usize].m_lo = lo as u8;
lowest_e = e;
}
}
}
}
}
pub fn build() -> Bc1MatchTables {
let mut bc1_expand5 = [0u8; 32];
for (i, e) in bc1_expand5.iter_mut().enumerate() {
*e = ((i << 3) | (i >> 2)) as u8;
}
let mut bc1_expand6 = [0u8; 64];
for (i, e) in bc1_expand6.iter_mut().enumerate() {
*e = ((i << 2) | (i >> 4)) as u8;
}
let mut t = Bc1MatchTables {
match5_equals_1: [Bc1MatchEntry::default(); 256],
match6_equals_1: [Bc1MatchEntry::default(); 256],
match5_equals_0: [Bc1MatchEntry::default(); 256],
match6_equals_0: [Bc1MatchEntry::default(); 256],
};
prepare(&mut t.match5_equals_1, &bc1_expand5, 32, 32, 1);
prepare(&mut t.match5_equals_0, &bc1_expand5, 1, 32, 0);
prepare(&mut t.match6_equals_1, &bc1_expand6, 64, 64, 1);
prepare(&mut t.match6_equals_0, &bc1_expand6, 1, 64, 0);
t
}
pub fn tables() -> &'static Bc1MatchTables {
static T: OnceBox<Bc1MatchTables> = OnceBox::new();
T.get_or_init(|| Box::new(build()))
}
impl Bc1MatchTables {
pub fn table_bytes(table: &[Bc1MatchEntry; 256]) -> Vec<u8> {
let mut out = Vec::with_capacity(512);
for e in table {
out.push(e.m_hi);
out.push(e.m_lo);
}
out
}
}