mod fse_decoder;
pub use fse_decoder::*;
pub mod fse_encoder;
#[cfg(any(test, feature = "fuzz_exports"))]
pub fn round_trip(data: &[u8]) {
use crate::bit_io::{BitReaderReversed, BitWriter};
use fse_encoder::FSEEncoder;
let mapped: alloc::vec::Vec<u8> = data.iter().map(|b| b % 32).collect();
let data = mapped.as_slice();
if data.len() < 2 {
return;
}
if data.iter().all(|x| *x == data[0]) {
return;
}
if data.len() < 64 {
return;
}
let mut writer = BitWriter::new();
let mut encoder = FSEEncoder::new(
fse_encoder::build_table_from_data(data.iter().copied(), 6, false),
&mut writer,
);
let mut dec_table = FSETable::new(255);
encoder.encode(data);
let acc_log = encoder.acc_log();
let enc_table = encoder.into_table();
let encoded = writer.dump();
let table_bytes = dec_table.build_decoder(&encoded, acc_log).unwrap();
let encoded = &encoded[table_bytes..];
let mut decoder = FSEDecoder::new(&dec_table);
check_tables(&dec_table, &enc_table);
let mut br = BitReaderReversed::<crate::cpu_kernel::ScalarKernel>::new(encoded);
let mut skipped_bits = 0;
loop {
let val = br.get_bits(1);
skipped_bits += 1;
if val == 1 || skipped_bits > 8 {
break;
}
}
if skipped_bits > 8 {
panic!("Corrupted end marker");
}
decoder.init_state(&mut br).unwrap();
let mut decoded = alloc::vec::Vec::new();
for x in data {
let w = decoder.decode_symbol();
assert_eq!(w, *x);
decoded.push(w);
if decoded.len() < data.len() {
decoder.update_state(&mut br);
}
}
assert_eq!(&decoded, data);
assert_eq!(br.bits_remaining(), 0);
}
#[cfg(any(test, feature = "fuzz_exports"))]
fn check_tables(dec_table: &fse_decoder::FSETable, enc_table: &fse_encoder::FSETable) {
let table_size = enc_table.table_size;
let mut hit = alloc::vec![false; table_size];
for symbol_u in 0..=255u16 {
let symbol = symbol_u as u8;
if enc_table.symbol_probability(symbol) == 0 {
continue;
}
for input_state in 0..table_size {
let next = enc_table.next_state(symbol, input_state);
let dec_state = &dec_table.decode()[next.index];
assert_eq!(
dec_state.symbol, symbol,
"encoder transition for symbol {symbol} from state {input_state} \
lands on decoder slot {} which decodes to symbol {} \
(encoder/decoder routing mismatch)",
next.index, dec_state.symbol
);
assert_eq!(
next.baseline, dec_state.new_state as usize,
"decode/encode baseline mismatch at slot {} (symbol {symbol})",
next.index
);
assert_eq!(
next.num_bits, dec_state.num_bits,
"decode/encode num_bits mismatch at slot {} (symbol {symbol})",
next.index
);
hit[next.index] = true;
}
}
for (idx, was_hit) in hit.iter().enumerate() {
assert!(
*was_hit,
"decoder slot {idx} not reached by any (symbol, prev_state) encoder transition"
);
}
}
#[cfg(test)]
mod tests;