use crate::bits::{ForwardBitReader, ReverseBitReader};
use crate::error::Error;
#[derive(Clone, Copy, Debug)]
pub(crate) struct FseEntry {
pub symbol: u8,
pub nb_bits: u8,
pub base: u16,
}
#[derive(Clone)]
pub(crate) struct FseTable {
pub table_log: u32,
pub entries: Vec<FseEntry>,
}
impl FseTable {
pub(crate) fn rle(symbol: u8) -> Self {
FseTable {
table_log: 0,
entries: vec![FseEntry {
symbol,
nb_bits: 0,
base: 0,
}],
}
}
}
pub(crate) struct NCount {
pub counts: Vec<i16>,
pub table_log: u32,
pub bytes_consumed: usize,
}
pub(crate) fn read_ncount(
src: &[u8],
max_symbol: u32,
max_table_log: u32,
) -> Result<NCount, Error> {
let mut br = ForwardBitReader::new(src);
let table_log = br.read(4) + 5;
if table_log > max_table_log {
return Err(Error::Corrupted("FSE accuracy log too large"));
}
let table_size: i32 = 1 << table_log;
let mut remaining: i32 = table_size + 1;
let mut threshold: i32 = table_size;
let mut nb_bits: u32 = table_log + 1;
let mut counts: Vec<i16> = Vec::new();
let mut previous0 = false;
while remaining > 1 {
if previous0 {
loop {
let flag = br.read(2);
let zeros = if flag == 3 { 3 } else { flag };
counts.extend(std::iter::repeat_n(0, zeros as usize));
if counts.len() > max_symbol as usize + 1 {
return Err(Error::Corrupted("FSE table describes too many symbols"));
}
if flag != 3 {
break;
}
}
}
if counts.len() > max_symbol as usize {
return Err(Error::Corrupted("FSE table describes too many symbols"));
}
let max = (2 * threshold - 1) - remaining;
let low = br.peek(nb_bits - 1) as i32;
let mut count: i32;
if low < max {
count = low;
br.consume(nb_bits - 1);
} else {
let v = br.peek(nb_bits) as i32;
count = if v >= threshold { v - max } else { v };
br.consume(nb_bits);
}
count -= 1;
remaining -= count.abs();
counts.push(count as i16);
previous0 = count == 0;
if remaining < 1 {
return Err(Error::Corrupted("FSE counts exceed table size"));
}
while remaining < threshold && threshold > 1 {
nb_bits -= 1;
threshold >>= 1;
}
}
if br.bits_consumed() > src.len() * 8 {
return Err(Error::SrcSizeWrong);
}
Ok(NCount {
counts,
table_log,
bytes_consumed: br.bits_consumed().div_ceil(8),
})
}
pub(crate) fn build_dtable(counts: &[i16], table_log: u32) -> Result<FseTable, Error> {
let table_size = 1usize << table_log;
let mut symbols = vec![0u8; table_size];
let mut symbol_next = vec![0u32; counts.len()];
let mut high_threshold = table_size as i64 - 1;
for (s, &c) in counts.iter().enumerate() {
if c == -1 {
if high_threshold < 0 {
return Err(Error::Corrupted("FSE counts exceed table size"));
}
symbols[high_threshold as usize] = s as u8;
high_threshold -= 1;
symbol_next[s] = 1;
} else {
symbol_next[s] = c as u32;
}
}
let step = (table_size >> 1) + (table_size >> 3) + 3;
let mask = table_size - 1;
let mut pos = 0usize;
for (s, &c) in counts.iter().enumerate() {
for _ in 0..c.max(0) {
symbols[pos] = s as u8;
pos = (pos + step) & mask;
while pos as i64 > high_threshold {
pos = (pos + step) & mask;
}
}
}
if pos != 0 {
return Err(Error::Corrupted("FSE table spread did not complete"));
}
let mut entries = Vec::with_capacity(table_size);
for &symbol in &symbols {
let next = symbol_next[symbol as usize];
symbol_next[symbol as usize] += 1;
debug_assert!(next > 0);
let nb_bits = table_log - (31 - next.leading_zeros());
let base = (next << nb_bits) - table_size as u32;
entries.push(FseEntry {
symbol,
nb_bits: nb_bits as u8,
base: base as u16,
});
}
Ok(FseTable { table_log, entries })
}
pub(crate) struct FseState<'t> {
table: &'t FseTable,
state: usize,
}
impl<'t> FseState<'t> {
#[inline]
pub(crate) fn new(table: &'t FseTable, br: &mut ReverseBitReader<'_>) -> Self {
let state = br.read(table.table_log) as usize;
FseState { table, state }
}
#[inline]
pub(crate) fn symbol(&self) -> u8 {
let mask = self.table.entries.len() - 1;
self.table.entries[self.state & mask].symbol
}
#[inline]
pub(crate) fn advance(&mut self, br: &mut ReverseBitReader<'_>) {
let mask = self.table.entries.len() - 1;
let e = self.table.entries[self.state & mask];
self.state = e.base as usize + br.read(u32::from(e.nb_bits)) as usize;
}
}
pub(crate) fn decode_interleaved(
table: &FseTable,
src: &[u8],
max_out: usize,
) -> Result<Vec<u8>, Error> {
let mut br = ReverseBitReader::new(src)?;
let mut s1 = FseState::new(table, &mut br);
let mut s2 = FseState::new(table, &mut br);
let mut out = Vec::new();
loop {
if out.len() + 2 > max_out {
return Err(Error::Corrupted("FSE output exceeds limit"));
}
out.push(s1.symbol());
s1.advance(&mut br);
if br.bits_remaining() < 0 {
out.push(s2.symbol());
break;
}
if out.len() + 2 > max_out {
return Err(Error::Corrupted("FSE output exceeds limit"));
}
out.push(s2.symbol());
s2.advance(&mut br);
if br.bits_remaining() < 0 {
out.push(s1.symbol());
break;
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rle_table_always_yields_symbol() {
let t = FseTable::rle(42);
let data = [0x01u8]; let mut br = ReverseBitReader::new(&data).unwrap();
let mut s = FseState::new(&t, &mut br);
for _ in 0..10 {
assert_eq!(s.symbol(), 42);
s.advance(&mut br);
}
assert!(br.finished_exactly());
}
#[test]
fn build_dtable_uniform_two_symbols() {
let t = build_dtable(&[16, 16], 5).unwrap();
assert_eq!(t.entries.len(), 32);
assert_eq!(t.entries.iter().filter(|e| e.symbol == 0).count(), 16);
assert_eq!(t.entries.iter().filter(|e| e.symbol == 1).count(), 16);
assert!(t.entries.iter().all(|e| e.nb_bits == 1));
}
#[test]
fn ncount_rejects_oversized_accuracy_log() {
assert!(read_ncount(&[0xFF, 0xFF], 35, 9).is_err());
}
}