use super::*;
use crate::decoding::legacy::v07::{LL_DEFAULT, LL_DEFAULT_LOG};
use alloc::vec;
#[test]
fn bitstreams_open_at_their_end_mark() {
assert!(open_bits(&[]).is_err());
assert!(open_bits(&[0x5A, 0x00]).is_err());
let bits = open_bits(&[0x01, 0x01]).unwrap();
assert_eq!(bits.bits_remaining(), 8);
let mut bits = open_bits(&[0b1010_0101]).unwrap();
assert_eq!(bits.bits_remaining(), 7);
assert_eq!(bits.get_bits(7), 0b010_0101);
assert!(!overflowed(&bits));
bits.get_bits(1);
assert!(overflowed(&bits));
}
#[test]
fn predefined_distributions_fill_their_table() {
let dist = Distribution::predefined(&LL_DEFAULT, LL_DEFAULT_LOG);
let table = FseTable::new(&dist).unwrap();
assert_eq!(table.cells.len(), 1 << LL_DEFAULT_LOG);
for symbol in 0..LL_DEFAULT.len() as u8 {
assert!(
table.cells.iter().any(|cell| cell.symbol == symbol),
"{symbol}"
);
}
for cell in &table.cells {
assert!(usize::from(cell.new_state) + (1 << cell.nb_bits) <= table.cells.len());
}
}
#[test]
fn a_table_rebuilt_in_place_is_a_fresh_one() {
let dist = Distribution::predefined(&LL_DEFAULT, LL_DEFAULT_LOG);
let fresh = FseTable::new(&dist).unwrap();
let mut reused = FseTable::empty();
reused.set_raw(9);
reused.build(&dist).unwrap();
assert_eq!(reused.log, fresh.log);
assert_eq!(reused.fast, fresh.fast);
assert_eq!(reused.cells.len(), fresh.cells.len());
for (a, b) in reused.cells.iter().zip(&fresh.cells) {
assert_eq!(
(a.symbol, a.nb_bits, a.new_state),
(b.symbol, b.nb_bits, b.new_state)
);
}
let (fresh, _) = HufTable::read(&[127 + 2, 0x11], StreamEnd::PastStart).unwrap();
let (mut reused, _) = HufTable::read(&[255], StreamEnd::PastStart).unwrap();
reused
.read_into(&[127 + 2, 0x11], StreamEnd::PastStart)
.unwrap();
assert_eq!((reused.log, &reused.cells), (fresh.log, &fresh.cells));
}
#[test]
fn a_distribution_that_does_not_fill_its_table_is_refused() {
let mut short = LL_DEFAULT;
short[0] -= 1;
assert!(FseTable::new(&Distribution::predefined(&short, LL_DEFAULT_LOG)).is_err());
let all_low = [-1i16; 40];
assert!(FseTable::new(&Distribution::predefined(&all_low, 5)).is_err());
}
#[test]
fn bad_descriptions_are_refused() {
assert_eq!(
read_distribution(&[0x00, 0x00], 35).err(),
Some(Error::Truncated)
);
assert!(read_distribution(&[0x0F, 0, 0, 0], 35).is_err());
}
#[test]
fn raw_and_rle_tables_read_as_described() {
let mut raw = FseTable::empty();
raw.set_raw(5);
let mut bits = open_bits(&[0b1110_1011]).unwrap();
let mut state = FseState::new(&raw, &mut bits);
assert_eq!(state.symbol(&raw), 0b11010);
state.update(&raw, &mut bits);
assert_eq!(state.symbol(&raw), 0b11000);
assert!(overflowed(&bits));
let mut rle = raw;
rle.set_rle(42);
let mut bits = open_bits(&[0x80]).unwrap();
let mut state = FseState::new(&rle, &mut bits);
for _ in 0..3 {
assert_eq!(state.symbol(&rle), 42);
state.update(&rle, &mut bits);
}
assert_eq!(bits.bits_remaining(), 7);
}
#[test]
fn a_weight_run_makes_a_flat_code() {
let (table, consumed) = HufTable::read(&[242], StreamEnd::PastStart).unwrap();
assert_eq!(consumed, 1);
assert_eq!(table.log, 1);
let mut out = [0u8; 3];
table.decode_one_stream(&[0b0000_1101], &mut out).unwrap();
assert_eq!(out, [1, 0, 1]);
let mut out = [0u8; 2];
assert!(table.decode_one_stream(&[0b0000_1101], &mut out).is_err());
}
#[test]
fn nibble_weights_build_their_code() {
let end = StreamEnd::PastStart;
let (table, consumed) = HufTable::read(&[127 + 2, 0x11], end).unwrap();
assert_eq!(consumed, 2);
assert_eq!(table.log, 2);
assert_eq!(table.cells, [(0, 2), (1, 2), (2, 1), (2, 1)]);
assert_eq!(
HufTable::read(&[127 + 4, 0x11], end).err(),
Some(Error::Truncated)
);
assert!(HufTable::read(&[127 + 2, 0x31], end).is_err());
assert!(HufTable::read(&[127 + 2, 0xF1], end).is_err());
}
#[test]
fn four_streams_fill_their_quarters() {
let (table, _) = HufTable::read(&[242], StreamEnd::PastStart).unwrap();
let stream = 0b0000_0101u8;
let mut src = vec![1, 0, 1, 0, 1, 0];
src.extend_from_slice(&[stream, stream, stream, stream]);
let mut out = [9u8; 8];
table.decode_four_streams(&src, &mut out).unwrap();
assert_eq!(out, [0, 1, 0, 1, 0, 1, 0, 1]);
let mut src = vec![1, 0, 1, 0, 1, 0];
src.extend_from_slice(&[stream, stream, stream, 0x01]);
let mut out = [9u8; 5];
table.decode_four_streams(&src, &mut out).unwrap();
assert_eq!(out, [0, 1, 0, 1, 0]);
let mut src = vec![200, 0, 1, 0, 1, 0];
src.extend_from_slice(&[stream, stream, stream, stream]);
assert!(table.decode_four_streams(&src, &mut out).is_err());
}
#[test]
fn arbitrary_descriptions_never_panic() {
let mut state = 0x9E37_79B9_7F4A_7C15u64;
let mut next = move || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
state
};
let mut table = FseTable::empty();
let mut huf = HufTable::empty();
for _ in 0..20_000 {
let len = (next() % 24) as usize + 1;
let src: vec::Vec<u8> = (0..len).map(|_| next() as u8).collect();
for max_symbol in [28, 35, 52, 255] {
if let Ok((dist, consumed)) = read_distribution(&src, max_symbol) {
assert!(consumed <= src.len());
let _built = table.build(&dist).is_ok();
}
}
for end in [StreamEnd::PastStart, StreamEnd::Exact] {
let Ok(consumed) = huf.read_into(&src, end) else {
continue;
};
assert!(consumed <= src.len());
let stream: vec::Vec<u8> = (0..(next() % 16) as usize + 1)
.map(|_| next() as u8)
.collect();
let mut out = vec![0u8; (next() % 32) as usize + 1];
let _one = huf.decode_one_stream(&stream, &mut out).is_ok();
let _four = huf.decode_four_streams(&stream, &mut out).is_ok();
}
}
}
#[test]
fn a_zero_run_at_the_end_shifts_the_held_bits() {
let src = [0x10, 0xFE, 0xFF, 0x01];
assert!(read_distribution(&src, 255).is_err());
}
#[test]
fn probabilities_past_the_field_are_refused() {
assert!(Distribution::from_probabilities(&[1; 40], 5, 31).is_err());
assert!(Distribution::from_probabilities(&[], 5, 31).is_err());
assert!(Distribution::from_probabilities(&[32], 5, 31).is_ok());
}
#[test]
fn a_distribution_that_overfills_its_table_is_refused() {
let mut counts = [-1i16; 33];
counts[32] = 1;
let dist = Distribution::predefined(&counts, 5);
assert!(FseTable::new(&dist).is_err());
}
#[test]
fn code_lengths_past_the_formats_limits_are_refused() {
let mut table = HufTable::empty();
assert!(table.fill_from_code_lengths(&[5, 1, 1], 3).is_err());
assert!(table.fill_from_code_lengths(&[1, 1, 0], 20).is_err());
let lengths: vec::Vec<u8> = (1..=13).chain([0]).collect();
assert!(table.fill_from_code_lengths(&lengths, 13).is_err());
let lengths: vec::Vec<u8> = (1..=12).chain([0]).collect();
assert!(table.fill_from_code_lengths(&lengths, 12).is_ok());
}