use super::tables::{
offset_slot, FIRST_LEN_SYM, LENGTH_SLOT, MIN_MATCH, NUM_LITLEN_SYMS, NUM_OFFSET_SYMS,
};
pub(super) const MIN_BLOCK_LENGTH: usize = 5000;
pub(super) const SOFT_MAX_BLOCK_LENGTH: usize = 1 << 16;
pub(super) const SEQ_STORE_LENGTH: usize = SOFT_MAX_BLOCK_LENGTH / 6;
pub(super) const MAX_BLOCK_LENGTH: usize = SOFT_MAX_BLOCK_LENGTH
+ if MIN_BLOCK_LENGTH > 3 + 258 {
MIN_BLOCK_LENGTH
} else {
3 + 258
};
const _: () = assert!(SEQ_STORE_LENGTH * MIN_MATCH as usize >= MIN_BLOCK_LENGTH);
const _: () = assert!(MAX_BLOCK_LENGTH < 1 << 22);
#[derive(Clone, Copy, Debug, Default)]
pub(super) struct Seq {
litrun_len: u32,
pub(super) offset: u16,
pub(super) slot: u16,
}
const LEN_SHIFT: u32 = 23;
pub(super) type Seqs = [[u16; 4]; SEQ_STORE_LENGTH + 1];
pub(super) const SEQS_LEN: usize = 4 * (SEQ_STORE_LENGTH + 1);
pub(super) fn carve_seqs(mem: &mut [u16]) -> &mut Seqs {
let (seqs, _) = mem.as_chunks_mut::<4>();
(&mut seqs[..=SEQ_STORE_LENGTH]).try_into().unwrap()
}
impl Seq {
pub(super) fn load(w: [u16; 4]) -> Self {
Seq {
litrun_len: u32::from(w[0]) | u32::from(w[1]) << 16,
offset: w[2],
slot: w[3],
}
}
fn store(self) -> [u16; 4] {
[
self.litrun_len as u16,
(self.litrun_len >> 16) as u16,
self.offset,
self.slot,
]
}
pub(super) fn litrun(self) -> u32 {
self.litrun_len & ((1 << LEN_SHIFT) - 1)
}
pub(super) fn len(self) -> u32 {
self.litrun_len >> LEN_SHIFT
}
}
#[derive(Clone, Debug)]
struct SplitStats {
new_observations: [u32; NUM_OBSERVATION_TYPES],
observations: [u32; NUM_OBSERVATION_TYPES],
num_new_observations: u32,
num_observations: u32,
}
const NUM_LITERAL_OBSERVATION_TYPES: usize = 8;
const NUM_OBSERVATION_TYPES: usize = NUM_LITERAL_OBSERVATION_TYPES + 2;
const NUM_OBSERVATIONS_PER_BLOCK_CHECK: u32 = 512;
impl SplitStats {
const NEW: SplitStats = SplitStats {
new_observations: [0; NUM_OBSERVATION_TYPES],
observations: [0; NUM_OBSERVATION_TYPES],
num_new_observations: 0,
num_observations: 0,
};
fn should_end(&mut self, len: usize) -> bool {
if self.num_observations > 0 {
let mut delta = 0u32;
for (&old, &new) in self.observations.iter().zip(&self.new_observations) {
let expected = old * self.num_new_observations;
let actual = new * self.num_observations;
delta += expected.abs_diff(actual);
}
let items = self.num_observations + self.num_new_observations;
let mut cutoff = self.num_new_observations * 200 / 512 * self.num_observations;
if len < 10000 && items < 8192 {
cutoff += (u64::from(cutoff) * u64::from(8192 - items) / 8192) as u32;
}
if delta + (len as u32 / 4096) * self.num_observations >= cutoff {
return true;
}
}
for (old, new) in self.observations.iter_mut().zip(&mut self.new_observations) {
*old += core::mem::take(new);
}
self.num_observations += core::mem::take(&mut self.num_new_observations);
false
}
}
pub(super) struct Block {
pub(super) start: usize,
pub(super) n: usize,
lits: u32,
pub(super) litlen_freqs: [u32; NUM_LITLEN_SYMS],
pub(super) offset_freqs: [u32; NUM_OFFSET_SYMS],
split: SplitStats,
}
impl Block {
pub(super) const fn new() -> Self {
Block {
start: 0,
n: 0,
lits: 0,
litlen_freqs: [0; NUM_LITLEN_SYMS],
offset_freqs: [0; NUM_OFFSET_SYMS],
split: SplitStats::NEW,
}
}
pub(super) fn begin(&mut self, start: usize) {
self.start = start;
self.n = 0;
self.lits = 0;
self.litlen_freqs = [0; NUM_LITLEN_SYMS];
self.offset_freqs = [0; NUM_OFFSET_SYMS];
self.split = SplitStats::NEW;
}
pub(super) fn close(&mut self, seqs: &mut Seqs) {
seqs[self.n] = Seq {
litrun_len: self.lits,
offset: 0,
slot: 0,
}
.store();
}
pub(super) fn seqs<'s>(&self, seqs: &'s Seqs) -> &'s [[u16; 4]] {
&seqs[..=self.n]
}
#[inline(always)]
pub(super) fn has_room(&self, limit: usize) -> bool {
self.n < limit
}
#[inline(always)]
pub(super) fn open_seq(&self) -> (usize, u32) {
(self.n, self.lits)
}
#[inline(always)]
pub(super) fn set_open_seq(&mut self, (n, lits): (usize, u32)) {
(self.n, self.lits) = (n, lits);
}
#[inline(always)]
pub(super) fn literal<const OBSERVE: bool>(&mut self, lit: u8) {
self.count_literal::<OBSERVE>(lit);
self.lits += 1;
}
#[inline(always)]
pub(super) fn count_literal<const OBSERVE: bool>(&mut self, lit: u8) {
self.litlen_freqs[lit as usize] += 1;
if OBSERVE {
self.split.new_observations[(((lit >> 5) & 6) | (lit & 1)) as usize] += 1;
self.split.num_new_observations += 1;
}
}
#[inline(always)]
pub(super) fn matched<const OBSERVE: bool>(&mut self, seqs: &mut Seqs, len: u32, offset: u32) {
self.store_match::<OBSERVE>(seqs, self.n, self.lits, len, offset);
self.n += 1;
self.lits = 0;
}
#[inline(always)]
pub(super) fn store_match<const OBSERVE: bool>(
&mut self,
seqs: &mut Seqs,
n: usize,
lits: u32,
len: u32,
offset: u32,
) {
let slot = offset_slot(offset);
self.litlen_freqs[FIRST_LEN_SYM + LENGTH_SLOT[len as usize] as usize] += 1;
self.offset_freqs[slot as usize] += 1;
if OBSERVE {
self.split.new_observations[NUM_LITERAL_OBSERVATION_TYPES + usize::from(len >= 9)] += 1;
self.split.num_new_observations += 1;
}
seqs[n] = Seq {
litrun_len: lits | len << LEN_SHIFT,
offset: offset as u16,
slot: slot as u16,
}
.store();
}
#[inline(always)]
pub(super) fn should_end(&mut self, pos: usize, rest: Option<usize>) -> bool {
self.split.num_new_observations >= NUM_OBSERVATIONS_PER_BLOCK_CHECK
&& pos - self.start >= MIN_BLOCK_LENGTH
&& rest.is_none_or(|r| r >= MIN_BLOCK_LENGTH)
&& self.split.should_end(pos - self.start)
}
pub(super) fn recalculate_min_len(&self, depth: u32) -> u32 {
let lits = &self.litlen_freqs[..256];
let total: u32 = lits.iter().sum();
let cutoff = total >> 10;
let used = lits.iter().filter(|&&f| f > cutoff).count();
choose_min_len(used, depth)
}
}
fn choose_min_len(used_literals: usize, depth: u32) -> u32 {
const MIN_LENS: [u8; 80] = [
9, 9, 9, 9, 9, 9, 8, 8, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
];
let Some(&min_len) = MIN_LENS.get(used_literals) else {
return MIN_MATCH;
};
let min_len = u32::from(min_len);
match depth {
0..5 => min_len.min(4),
5..10 => min_len.min(5),
10..16 => min_len.min(7),
_ => min_len,
}
}
pub(super) fn calculate_min_len(data: &[u8], len: usize, depth: u32) -> u32 {
if len < 512 {
return MIN_MATCH;
}
let mut used = [false; 256];
for &b in &data[..data.len().min(4096)] {
used[b as usize] = true;
}
choose_min_len(used.iter().filter(|&&u| u).count(), depth)
}