use super::*;
use crate::encoding::workspace::{Workspace, no_trailing};
use alloc::vec::Vec;
fn reset_in(
m: &mut FastKernelMatcher,
ws: &mut Workspace,
(window_log, hash_log, mls, step_size): (u8, u32, u32, usize),
carry: TableCarry,
) {
ws.begin_layout(
0,
no_trailing,
crate::encoding::workspace::IngestPlan::Stream,
);
ws.open(FastHashTable::workspace_bytes(hash_log), 0);
m.reset(
window_log,
hash_log,
mls,
step_size,
1usize << window_log,
0,
carry,
ws,
);
}
const LEVEL_1_SHAPE: (u8, u32, u32, usize) = (
FAST_LEVEL_1_WINDOW_LOG,
FAST_LEVEL_1_HASH_LOG,
FAST_LEVEL_1_MLS,
2,
);
#[test]
fn new_uses_level_1_defaults() {
let m = FastKernelMatcher::new();
assert_eq!(m.window_log, FAST_LEVEL_1_WINDOW_LOG);
assert_eq!(m.hash_table.hash_log(), FAST_LEVEL_1_HASH_LOG);
assert_eq!(m.hash_table.mls(), FAST_LEVEL_1_MLS);
assert_eq!(m.rep, FAST_INITIAL_REP);
assert_eq!(m.offset_hist, FAST_INITIAL_OFFSET_HIST);
assert_eq!(m.max_window_size, 1usize << FAST_LEVEL_1_WINDOW_LOG);
assert_eq!(m.history.len(), HISTORY_DRAIN_BASE);
assert_eq!(m.prefix_start_index, INITIAL_PREFIX_START_INDEX);
assert!(m.staged_block.is_none());
assert_eq!(m.uncommitted_len, 0);
}
#[test]
fn borrowed_window_reads_match_owned_then_restores() {
let mut m = FastKernelMatcher::new();
m.history = b"owned-history-bytes".to_vec().into();
assert_eq!(m.history_bytes(), b"owned-history-bytes");
let external = b"a-different-borrowed-window".to_vec();
unsafe { m.set_borrowed_window(&external) };
assert_eq!(m.history_bytes(), &external[..]);
assert_eq!(&m.history[..], b"owned-history-bytes");
m.clear_borrowed_window();
assert_eq!(m.history_bytes(), b"owned-history-bytes");
}
#[test]
fn reset_clears_borrowed_window() {
let mut ws = Workspace::new();
let mut m = FastKernelMatcher::new();
let external = b"borrowed".to_vec();
unsafe { m.set_borrowed_window(&external) };
reset_in(&mut m, &mut ws, LEVEL_1_SHAPE, TableCarry::Clear);
assert!(m.borrowed.is_none());
assert_eq!(m.history_bytes().len(), HISTORY_DRAIN_BASE);
}
#[test]
fn borrowed_window_matches_owned_sequence_stream() {
let mut whole = alloc::vec::Vec::with_capacity(320);
for i in 0..320usize {
whole.push((i % 64) as u8);
}
let split = 192usize;
let mut owned = FastKernelMatcher::with_params(15, 12, 5, 2);
let mut owned_seqs: Vec<Sequence> = Vec::new();
owned.commit_input(&whole[..split]);
owned.start_matching(|seq| owned_seqs.push(seq));
owned.commit_input(&whole[split..]);
owned.start_matching(|seq| owned_seqs.push(seq));
let mut borrowed = FastKernelMatcher::with_params(15, 12, 5, 2);
let mut borrowed_seqs: Vec<Sequence> = Vec::new();
unsafe { borrowed.set_borrowed_window(&whole) };
borrowed.start_matching_borrowed(0, split, |seq| borrowed_seqs.push(seq));
borrowed.start_matching_borrowed(split, whole.len(), |seq| borrowed_seqs.push(seq));
assert_eq!(
owned_seqs, borrowed_seqs,
"borrowed window must emit the identical sequence stream as the owned path",
);
assert_eq!(
owned.rep, borrowed.rep,
"rep state must match after both scans"
);
assert_eq!(
owned.offset_hist, borrowed.offset_hist,
"offset_hist must match after both scans",
);
assert!(
borrowed_seqs
.iter()
.any(|s| matches!(s, Sequence::Triple { .. })),
"pattern must yield at least one match to make the check meaningful",
);
}
#[test]
fn with_params_threads_through_each_field() {
let m = FastKernelMatcher::with_params(16, 12, 5, 2);
assert_eq!(m.window_log, 16);
assert_eq!(m.hash_table.hash_log(), 12);
assert_eq!(m.hash_table.mls(), 5);
assert_eq!(m.max_window_size, 1usize << 16);
}
#[test]
fn window_size_reports_one_shifted_window_log() {
let m = FastKernelMatcher::with_params(16, 12, 5, 2);
assert_eq!(m.window_size(), 1u64 << 16);
let m = FastKernelMatcher::with_params(22, 14, 7, 2);
assert_eq!(m.window_size(), 1u64 << 22);
}
#[test]
fn last_committed_space_empty_before_commit() {
let m = FastKernelMatcher::new();
assert!(m.last_committed_space().is_empty());
}
#[test]
fn reset_clears_history_and_state() {
let mut ws = Workspace::new();
let mut m = FastKernelMatcher::new();
m.history.extend_from_slice(&[1, 2, 3, 4]);
m.prefix_start_index = 7;
m.rep = [42, 99];
m.offset_hist = [10, 20, 30];
m.commit_input([5, 6, 7]);
m.fill_uncommitted(2, |history| {
history.extend_from_slice(&[8, 9]);
(2, false)
});
reset_in(&mut m, &mut ws, LEVEL_1_SHAPE, TableCarry::Clear);
assert_eq!(m.history.len(), HISTORY_DRAIN_BASE);
assert_eq!(m.prefix_start_index, INITIAL_PREFIX_START_INDEX);
assert_eq!(m.rep, FAST_INITIAL_REP);
assert_eq!(m.offset_hist, FAST_INITIAL_OFFSET_HIST);
assert!(m.staged_block.is_none());
assert_eq!(m.uncommitted_len, 0);
assert_eq!(m.hash_table.hash_log(), FAST_LEVEL_1_HASH_LOG);
assert_eq!(m.hash_table.mls(), FAST_LEVEL_1_MLS);
}
#[test]
fn reset_with_changed_params_rebuilds_hash_table() {
let mut ws = Workspace::new();
let mut m = FastKernelMatcher::new();
reset_in(&mut m, &mut ws, (16, 10, 4, 2), TableCarry::Clear);
assert_eq!(m.hash_table.hash_log(), 10);
assert_eq!(m.hash_table.mls(), 4);
assert_eq!(m.window_log, 16);
assert_eq!(m.max_window_size, 1usize << 16);
}
#[test]
fn reset_keeps_table_when_overwritten_by_restore() {
let mut ws = Workspace::new();
let mut m = FastKernelMatcher::new();
reset_in(&mut m, &mut ws, (16, 10, 4, 2), TableCarry::Clear);
let probe_hash = 7u32;
unsafe { m.hash_table.put(probe_hash, 0xCAFE) };
assert!(m.slots_tagged());
reset_in(
&mut m,
&mut ws,
(16, 10, 4, 2),
TableCarry::OverwrittenByRestore { tagged: true },
);
assert_eq!(
unsafe { m.hash_table.get(probe_hash) },
0xCAFE,
"restore-pending reset must not clear the table"
);
reset_in(
&mut m,
&mut ws,
(16, 10, 4, 2),
TableCarry::OverwrittenByRestore { tagged: false },
);
assert_eq!(
unsafe { m.hash_table.get(probe_hash) },
0,
"a restore in the other slot format must not leave the table uncleared"
);
unsafe { m.hash_table.put(probe_hash, 0xCAFE) };
reset_in(&mut m, &mut ws, (16, 10, 4, 2), TableCarry::Clear);
assert_eq!(
unsafe { m.hash_table.get(probe_hash) },
0,
"plain reset must clear the table"
);
unsafe { m.hash_table.put(probe_hash, 0xCAFE) };
reset_in(
&mut m,
&mut ws,
(16, 11, 4, 2),
TableCarry::OverwrittenByRestore { tagged: true },
);
assert_eq!(m.hash_table.hash_log(), 11);
assert_eq!(
unsafe { m.hash_table.get(probe_hash) },
0,
"shape change must rebuild the table regardless of the flag"
);
}
#[test]
fn accept_then_start_matching_emits_match_for_repeated_run() {
let mut data = alloc::vec::Vec::with_capacity(64);
for i in 0..32u8 {
data.push(i.wrapping_mul(7).wrapping_add(13));
}
data.extend_from_within(0..32);
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
m.commit_input(data.clone());
let mut emitted_match_lens: alloc::vec::Vec<usize> = alloc::vec::Vec::new();
let mut emitted_literal_byte_count: usize = 0;
let mut tail_byte_count: usize = 0;
m.start_matching(|seq| match seq {
Sequence::Triple {
literal_len,
offset: _,
match_len,
} => {
emitted_literal_byte_count += literal_len;
emitted_match_lens.push(match_len);
}
Sequence::Literals { len } => {
tail_byte_count += len;
}
});
let total_matched: usize = emitted_match_lens.iter().sum();
assert_eq!(
emitted_literal_byte_count + total_matched + tail_byte_count,
data.len(),
"all input bytes must be accounted for as literals/matches/tail",
);
assert!(
emitted_match_lens.iter().any(|&m| m >= 4),
"kernel must emit at least one Triple with match_len >= MIN_MATCH (got {emitted_match_lens:?})",
);
assert!(m.staged_block.is_none());
assert_eq!(m.history.len(), data.len() + HISTORY_DRAIN_BASE);
assert_eq!(m.last_committed_space(), data.as_slice());
}
#[test]
fn skip_matching_extends_history_without_emissions() {
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
let pre_rep = m.rep;
let pre_offset_hist = m.offset_hist;
let payload: alloc::vec::Vec<u8> = (0..40u8).collect();
m.commit_input(payload.clone());
assert_eq!(m.last_committed_space().len(), payload.len());
m.skip_matching_with_hint(None);
assert_eq!(
m.history.len(),
payload.len() + HISTORY_DRAIN_BASE,
"skip_matching must leave the committed block in history",
);
assert_eq!(m.rep, pre_rep, "skip must not touch rep state");
assert_eq!(
m.offset_hist, pre_offset_hist,
"skip must not touch offset_hist",
);
assert!(m.staged_block.is_none());
}
#[test]
fn cross_block_match_finds_first_block_payload() {
let mut block1 = alloc::vec::Vec::with_capacity(128);
for i in 0..128u8 {
block1.push(i.wrapping_mul(11).wrapping_add(5));
}
let mut block2 = alloc::vec::Vec::with_capacity(64);
block2.extend(0..16u8); block2.extend_from_slice(&block1[0..32]); block2.extend(200..216u8);
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
m.commit_input(block1.clone());
m.start_matching(|_seq| {});
m.commit_input(block2.clone());
let mut max_match: usize = 0;
let mut saw_cross_block = false;
m.start_matching(|seq| {
if let Sequence::Triple {
offset, match_len, ..
} = seq
{
max_match = max_match.max(match_len);
if offset >= block2.len() {
saw_cross_block = true;
}
}
});
assert!(
saw_cross_block,
"block 2's matcher must find at least one cross-block match \
(max_len={max_match})",
);
assert_eq!(
m.history.len(),
block1.len() + block2.len() + HISTORY_DRAIN_BASE,
"history must hold both blocks after two start_matching calls",
);
}
#[test]
fn skip_matching_with_false_hint_populates_hashes_for_dict_priming() {
let mut dict_block = alloc::vec::Vec::with_capacity(32);
for i in 0..32u8 {
dict_block.push(i.wrapping_mul(13).wrapping_add(7));
}
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
m.commit_input(dict_block.clone());
m.skip_matching_with_hint(Some(false));
assert_eq!(m.history.len(), dict_block.len() + HISTORY_DRAIN_BASE);
assert_eq!(m.prefix_start_index, INITIAL_PREFIX_START_INDEX);
let mut block2 = alloc::vec::Vec::with_capacity(48);
block2.extend(100..116u8);
block2.extend_from_slice(&dict_block[0..16]);
block2.extend(120..136u8);
m.commit_input(block2.clone());
let mut saw_cross_block = false;
m.start_matching(|seq| {
if let Sequence::Triple { offset, .. } = seq
&& offset >= block2.len()
{
saw_cross_block = true;
}
});
assert!(
saw_cross_block,
"skip_matching(Some(false)) must populate hashes so block 2 \
can match against the primed bytes",
);
}
#[test]
fn skip_matching_with_none_hint_skips_hash_population() {
let mut dict_block = alloc::vec::Vec::with_capacity(32);
for i in 0..32u8 {
dict_block.push(i.wrapping_mul(13).wrapping_add(7));
}
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
m.commit_input(dict_block.clone());
m.skip_matching_with_hint(None);
let mut block2 = alloc::vec::Vec::with_capacity(48);
block2.extend(100..116u8);
block2.extend_from_slice(&dict_block[0..16]);
block2.extend(120..136u8);
m.commit_input(block2.clone());
let mut saw_cross_block = false;
m.start_matching(|seq| {
if let Sequence::Triple { offset, .. } = seq
&& offset >= block2.len()
{
saw_cross_block = true;
}
});
assert!(
!saw_cross_block,
"skip_matching(None) must NOT populate hashes — the legacy \
skip cost-savings only hold when future blocks are willing \
to miss matches in the skipped region",
);
}
#[test]
fn extend_history_drains_old_prefix_past_two_window_sizes() {
let mut m = FastKernelMatcher::with_params(8, 6, 4, 2);
for round in 0..3 {
let block: alloc::vec::Vec<u8> = (0..200u8)
.map(|i| i.wrapping_add(round as u8 * 17))
.collect();
m.commit_input(block);
m.skip_matching_with_hint(None);
}
assert!(
m.history.len() <= m.max_window_size * 2 + HISTORY_DRAIN_BASE,
"after eviction, REAL history must be bounded by 2× \
max_window_size (got history.len()={}, max_window_size={})",
m.history.len(),
m.max_window_size,
);
assert!(
m.history.len() <= m.max_window_size + 200 + HISTORY_DRAIN_BASE,
"post-append history = retained prefix (≤ max_window_size) \
+ last block (200 bytes); got {}",
m.history.len(),
);
assert_eq!(
m.prefix_start_index, INITIAL_PREFIX_START_INDEX,
"drain must rebase prefix_start_index to the baseline (1)",
);
}
#[test]
fn skip_matching_dict_prime_handles_exactly_hash_read_size_bytes() {
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
let payload: alloc::vec::Vec<u8> = (0..8u8).collect();
m.commit_input(payload);
m.skip_matching_with_hint(Some(false));
assert_eq!(m.history.len(), 8 + HISTORY_DRAIN_BASE);
}
#[test]
fn copy_mode_dictionary_fill_keeps_the_upstream_occurrence_per_bucket() {
let dict: alloc::vec::Vec<u8> = b"abcd".iter().copied().cycle().take(96).collect();
let mut m = FastKernelMatcher::with_params(12, 12, 4, 2);
m.commit_input(dict);
m.skip_matching_for_dict_copy();
let base = m.history.as_ptr();
let last_hashable = m.history.len() - 8;
let phase_0 = unsafe {
let hash = m.hash_table.hash_ptr::<4>(base);
m.hash_table.get(hash)
};
let last_stride_of_phase = (0..=last_hashable)
.rfind(|p| p % 12 == 0)
.expect("the dictionary spans several stride groups");
let last_of_phase = (0..=last_hashable)
.rfind(|p| p % 4 == 0)
.expect("the dictionary spans several phase positions");
assert_ne!(
last_stride_of_phase, last_of_phase,
"the fixture must separate the two policies",
);
assert_eq!(
phase_0, last_stride_of_phase as u32,
"the bucket must hold the last stride position of its phase, not the \
last position ({last_of_phase}) a dense fill would leave",
);
}
#[test]
fn copy_mode_dictionary_fill_leaves_a_dictionary_too_short_to_hash() {
let mut m = FastKernelMatcher::with_params(12, 12, 4, 2);
m.commit_input(alloc::vec![0xABu8; 5]);
m.skip_matching_for_dict_copy();
assert_eq!(
m.dict_copy_fill_next, 0,
"nothing is hashable, so the stride has nowhere to resume from",
);
assert_eq!(m.loaded_dict_end, m.history.len());
}
#[test]
fn copy_mode_dictionary_fill_indexes_nothing_when_the_stride_is_already_past() {
let dict: alloc::vec::Vec<u8> = b"abcd".iter().copied().cycle().take(64).collect();
let mut m = FastKernelMatcher::with_params(12, 12, 4, 2);
m.commit_input(dict);
m.skip_matching_for_dict_copy();
let after_first = m.dict_copy_fill_next;
assert!(
after_first > m.history.len() - 8,
"the last stride group was partial, so the cursor sits past the last \
hashable position (at {after_first})",
);
m.commit_input(alloc::vec::Vec::new());
m.skip_matching_for_dict_copy();
assert_eq!(m.dict_copy_fill_next, after_first);
}
#[test]
fn copy_mode_dictionary_fill_runs_at_every_hash_width() {
for mls in 4u32..=8 {
let dict: alloc::vec::Vec<u8> = (0..96u8).map(|b| b.wrapping_mul(7)).collect();
let mut m = FastKernelMatcher::with_params(12, 14, mls, 2);
m.commit_input(dict);
m.skip_matching_for_dict_copy();
assert!(
m.dict_copy_fill_next > 0,
"mls {mls}: the fill must have advanced its stride",
);
assert_eq!(m.loaded_dict_end, m.history.len(), "mls {mls}");
}
}
#[test]
fn skip_matching_dict_prime_handles_below_hash_read_size_bytes() {
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
let payload: alloc::vec::Vec<u8> = (0..4u8).collect();
m.commit_input(payload);
m.skip_matching_with_hint(Some(false));
assert_eq!(m.history.len(), 4 + HISTORY_DRAIN_BASE);
}
#[test]
fn dict_prime_indexes_positions_across_chunk_seam() {
let mut m = FastKernelMatcher::with_params(20, 20, 4, 2);
let chunk1: alloc::vec::Vec<u8> = (0..16u8)
.map(|i| i.wrapping_mul(37).wrapping_add(13))
.collect();
m.commit_input(chunk1);
m.skip_matching_for_dict_prime(32);
let seam = m.history.len(); let chunk2: alloc::vec::Vec<u8> = (16..32u8)
.map(|i| i.wrapping_mul(37).wrapping_add(13))
.collect();
m.commit_input(chunk2);
m.skip_matching_for_dict_prime(32);
let p = seam - 4;
let dt = m.dict.table().expect("dict table primed");
let found = unsafe {
crate::encoding::simple::fast_kernel::kernel::dict_lookup::<4>(
dt.dict_slots(),
m.history.as_ptr().add(p),
dt.hash_log(),
)
};
assert_eq!(
found, p as u32,
"seam-spanning position {p} must be indexed in the dict table",
);
}
#[test]
fn dict_prime_in_slices_matches_single_pass_fill() {
let dict: alloc::vec::Vec<u8> = (0..96u8)
.map(|i| i.wrapping_mul(37).wrapping_add(13))
.collect();
let mut whole = FastKernelMatcher::with_params(16, 16, 4, 2);
whole.commit_input(dict.clone());
whole.skip_matching_for_dict_prime(dict.len());
let mut sliced = FastKernelMatcher::with_params(16, 16, 4, 2);
sliced.commit_input(&dict[..30]);
sliced.skip_matching_for_dict_prime(dict.len());
sliced.commit_input(&dict[30..]);
sliced.skip_matching_for_dict_prime(dict.len());
let a = whole.dict.table().expect("single-pass dict table");
let b = sliced.dict.table().expect("sliced dict table");
assert_eq!(a.hash_log(), b.hash_log(), "dict table width");
for slot in 0..(1u32 << a.hash_log()) {
let (x, y) = unsafe { (a.get(slot), b.get(slot)) };
assert_eq!(
x, y,
"dict table slot {slot} differs between sliced and single-pass fills"
);
}
}
#[test]
fn dict_prime_sizes_the_table_from_the_whole_dictionary() {
let dict: alloc::vec::Vec<u8> = (0..3000u32)
.map(|i| (i.wrapping_mul(37) ^ (i >> 5)) as u8)
.collect();
let mut m = FastKernelMatcher::with_params(20, 20, 4, 2);
m.commit_input(&dict[..30]);
m.skip_matching_for_dict_prime(dict.len());
m.commit_input(&dict[30..]);
m.skip_matching_for_dict_prime(dict.len());
let (expected, _) =
crate::encoding::cparams::create_cdict_table_logs(20, 20, 20, false, dict.len());
assert_eq!(
m.dict.table().expect("dict table primed").hash_log(),
expected,
"dict table width must follow the whole dictionary's CDict geometry",
);
}
#[test]
fn block_samples_match_dict_fires_only_on_extendable_dict_match() {
let dict: alloc::vec::Vec<u8> = (0..200u8)
.map(|i| i.wrapping_mul(37).wrapping_add(13))
.collect();
let mut m = FastKernelMatcher::with_params(20, 20, 4, 2);
m.commit_input(dict.clone());
m.skip_matching_for_dict_prime(dict.len());
assert!(m.dict_is_attached(), "dict must be attached after prime");
let hit = dict[8..40].to_vec();
assert!(
m.block_samples_match_dict(&hit),
"a long verbatim dict run must trip the dict probe",
);
let mut short = dict[8..16].to_vec();
short.extend((0..56u8).map(|i| i.wrapping_mul(53).wrapping_add(201)));
assert!(
!m.block_samples_match_dict(&short),
"a sub-16-byte dict match must not trip the probe",
);
let miss: alloc::vec::Vec<u8> = (0..64u8)
.map(|i| i.wrapping_mul(91).wrapping_add(7) ^ 0xA5)
.collect();
assert!(
!m.block_samples_match_dict(&miss),
"a non-dict block must not trip the probe",
);
assert!(
!m.block_samples_match_dict(&dict[..4]),
"a sub-8-byte block cannot be probed",
);
}
#[test]
fn block_samples_match_dict_is_false_without_a_dictionary() {
let m = FastKernelMatcher::with_params(20, 20, 4, 2);
let block: alloc::vec::Vec<u8> = (0..64u8)
.map(|i| i.wrapping_mul(37).wrapping_add(13))
.collect();
assert!(!m.dict_is_attached());
assert!(!m.block_samples_match_dict(&block));
}
#[test]
fn main_table_prime_indexes_positions_across_slice_seam() {
let mut m = FastKernelMatcher::with_params(20, 20, 4, 2);
let chunk1: alloc::vec::Vec<u8> = (0..16u8)
.map(|i| i.wrapping_mul(53).wrapping_add(7))
.collect();
m.commit_input(chunk1);
m.skip_matching_with_hint(Some(false));
let seam = m.history.len();
let chunk2: alloc::vec::Vec<u8> = (16..32u8)
.map(|i| i.wrapping_mul(53).wrapping_add(7))
.collect();
m.commit_input(chunk2);
m.skip_matching_with_hint(Some(false));
let p = seam - 4;
let h = unsafe { m.hash_table.hash_ptr::<4>(m.history.as_ptr().add(p)) };
assert_eq!(
unsafe { m.hash_table.get(h) },
p as u32,
"seam-spanning position {p} must be indexed in the main table",
);
}
#[test]
fn rep_tracks_last_explicit_offset_and_offset_hist_is_not_matched() {
let mut data = alloc::vec::Vec::with_capacity(96);
for i in 0..48u8 {
data.push(i.wrapping_mul(11).wrapping_add(3));
}
data.extend_from_within(0..48);
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
m.commit_input(data.clone());
let mut emitted_offsets: alloc::vec::Vec<usize> = alloc::vec::Vec::new();
m.start_matching(|seq| {
if let Sequence::Triple {
offset, match_len, ..
} = seq
&& match_len >= 4
{
emitted_offsets.push(offset);
}
});
assert!(
!emitted_offsets.is_empty(),
"test setup must produce at least one explicit match",
);
let last_explicit = emitted_offsets[emitted_offsets.len() - 1];
assert_eq!(
m.rep[0] as usize, last_explicit,
"kernel's rep[0] must reflect the last emitted explicit offset",
);
assert_eq!(
m.offset_hist, FAST_INITIAL_OFFSET_HIST,
"Fast matching must not mutate offset_hist (it is seeded only \
by reset / prime_offset_history; the wire offset coding runs \
downstream against the encode pipeline's own history)",
);
}
#[test]
fn eviction_during_dict_priming_drops_stale_prime_entries() {
let mut m = FastKernelMatcher::with_params(8, 6, 4, 2);
let block1: alloc::vec::Vec<u8> = (0..200u8).collect();
m.commit_input(block1);
m.skip_matching_with_hint(Some(false));
let block2: alloc::vec::Vec<u8> = (0..200u8).map(|i| i.wrapping_add(50)).collect();
m.commit_input(block2);
m.skip_matching_with_hint(Some(false));
let block3: alloc::vec::Vec<u8> = (0..200u8).map(|i| i.wrapping_add(100)).collect();
m.commit_input(block3);
m.skip_matching_with_hint(Some(false));
assert_eq!(
m.prefix_start_index, INITIAL_PREFIX_START_INDEX,
"drain must rebase prefix_start_index to the baseline (1)",
);
assert!(m.history.len() <= m.max_window_size * 2);
}
#[test]
fn commit_evicts_eagerly_and_reports_the_evicted_bytes() {
let mut m = FastKernelMatcher::with_params(8, 6, 4, 2);
m.commit_input((0..200u8).collect::<Vec<u8>>());
m.skip_matching_with_hint(None);
assert_eq!(m.history.len(), 200 + HISTORY_DRAIN_BASE);
assert_eq!(
m.prefix_start_index, INITIAL_PREFIX_START_INDEX,
"no eviction yet"
);
m.commit_input((0..200u8).map(|i| i.wrapping_add(50)).collect::<Vec<u8>>());
m.skip_matching_with_hint(None);
assert_eq!(m.history.len(), 400 + HISTORY_DRAIN_BASE);
assert_eq!(
m.prefix_start_index, INITIAL_PREFIX_START_INDEX,
"still no eviction (400 < 512)",
);
let evicted = m.commit_input((0..200u8).map(|i| i.wrapping_add(100)).collect::<Vec<u8>>());
assert_eq!(
evicted,
400 - 256,
"commit_block must evict at the threshold and report the bytes",
);
assert_eq!(
m.committed_len(),
256 + 200,
"post-eviction retained must equal max_window_size, then the block",
);
assert_eq!(
m.prefix_start_index, INITIAL_PREFIX_START_INDEX,
"drain rebases prefix_start_index to INITIAL_PREFIX_START_INDEX \
— eviction is proven by the history.len() shrink above",
);
}
#[test]
fn trim_to_window_keeps_last_committed_space_consistent() {
let mut m = FastKernelMatcher::with_params(8, 6, 4, 2);
let payload: alloc::vec::Vec<u8> = (0..200u8).collect();
m.commit_input(payload);
m.skip_matching_with_hint(None);
assert_eq!(m.last_block_start, HISTORY_DRAIN_BASE);
assert_eq!(m.history.len(), 200 + HISTORY_DRAIN_BASE);
let payload2: alloc::vec::Vec<u8> = (50..150u8).collect();
m.commit_input(payload2);
m.skip_matching_with_hint(None);
assert_eq!(m.last_block_start, HISTORY_DRAIN_BASE + 200);
assert_eq!(m.history.len(), 300 + HISTORY_DRAIN_BASE);
m.max_window_size = 64;
let drained = m.trim_to_window();
assert_eq!(
drained,
300 - 64,
"trim must drain REAL history down to max_window_size = 64",
);
assert_eq!(m.history.len(), 64 + HISTORY_DRAIN_BASE);
let last = m.last_committed_space();
assert!(
last.len() <= 64,
"last_committed_space must be in-bounds after trim \
(got len {})",
last.len(),
);
}
#[test]
fn prime_offset_history_keeps_rep_and_offset_hist_in_lockstep() {
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
assert_eq!(m.rep, FAST_INITIAL_REP);
assert_eq!(m.offset_hist, FAST_INITIAL_OFFSET_HIST);
let primed = [9u32, 4, 8];
m.prime_offset_history(primed);
assert_eq!(
m.offset_hist, primed,
"offset_hist must be updated by prime_offset_history",
);
assert_eq!(
m.rep,
[primed[0], primed[1]],
"rep[0..2] must mirror offset_hist[0..2] post-prime \
(kernel's repcode decisions must match the wire \
encoder's seeded history)",
);
}
#[test]
fn commit_evicts_more_aggressively_when_block_larger_than_window() {
let mut m = FastKernelMatcher::with_params(8, 6, 4, 2);
let preamble: alloc::vec::Vec<u8> = (0..256u32).map(|i| i as u8).collect();
m.commit_input(preamble);
m.skip_matching_with_hint(None);
assert_eq!(
m.committed_len(),
256,
"history pre-filled to one full window of real bytes",
);
let oversize: alloc::vec::Vec<u8> = (0..400u32)
.map(|i| (i as u8).wrapping_mul(7).wrapping_add(11))
.collect();
let evicted = m.commit_input(oversize);
m.start_matching(|_| {});
let real_total = m.committed_len();
assert!(
real_total <= m.max_window_size * 2,
"post-append history MUST stay within 2 × max_window_size \
(got real_total={real_total}, cap={})",
m.max_window_size * 2,
);
assert!(
evicted >= 144,
"the commit must have shed historical bytes (evicted {evicted})",
);
}
#[test]
fn start_matching_enforces_max_window_size_offset_bound() {
let mut m = FastKernelMatcher::with_params(7, 8, 4, 2);
let max_window = m.max_window_size;
assert_eq!(max_window, 128, "test assumes window_log=7");
let block1: alloc::vec::Vec<u8> = (0..200u8).map(|i| 0x30 + (i % 64)).collect();
m.commit_input(block1.clone());
m.start_matching(|_| {});
let block2 = block1.clone();
m.commit_input(block2);
let mut max_emitted_offset = 0usize;
let mut emitted_match_count = 0usize;
m.start_matching(|seq| {
if let Sequence::Triple {
offset, match_len, ..
} = seq
&& match_len > 0
{
emitted_match_count += 1;
if offset > max_emitted_offset {
max_emitted_offset = offset;
}
}
});
assert!(
emitted_match_count > 0,
"fixture must produce at least one Triple match — \
history.len()=~400, max_window=128, block2 is identical to block1",
);
assert!(
max_emitted_offset <= max_window,
"sliding floor MUST cap emitted offsets at max_window_size; \
got max emitted offset {} vs max_window_size {}",
max_emitted_offset,
max_window,
);
}
#[test]
fn start_matching_caps_offsets_at_window_log_not_inflated_max() {
let mut m = FastKernelMatcher::with_params(7, 8, 4, 2);
let advertised_window: usize = 1 << m.window_log;
assert_eq!(advertised_window, 128, "test assumes window_log=7");
let dict: alloc::vec::Vec<u8> = (0..200u8).map(|i| 0x40 + (i % 4)).collect();
m.commit_input(dict.clone());
m.start_matching(|_| {}); m.max_window_size = m.max_window_size.saturating_add(200);
let block: alloc::vec::Vec<u8> = (0..100u8).map(|i| 0x40 + (i % 4)).collect();
m.commit_input(block);
let mut max_emitted_offset = 0usize;
let mut emitted_match_count = 0usize;
m.start_matching(|seq| {
if let Sequence::Triple {
offset, match_len, ..
} = seq
&& match_len > 0
{
emitted_match_count += 1;
if offset > max_emitted_offset {
max_emitted_offset = offset;
}
}
});
assert!(
emitted_match_count > 0,
"fixture must produce at least one match — block content \
repeats dict, history.len() ≈ 300, scan should find at \
least one Triple",
);
assert!(
max_emitted_offset <= advertised_window,
"sliding floor MUST cap emitted offsets at the ADVERTISED \
frame window (1 << window_log = {}), NOT the inflated \
max_window_size; got max emitted offset {}",
advertised_window,
max_emitted_offset,
);
}
#[test]
fn block_zero_prologue_preserves_default_rep_offset_one() {
let mut data = alloc::vec::Vec::with_capacity(200);
data.push(0x01);
data.resize(200, 0x42);
let mut m = FastKernelMatcher::with_params(12, 8, 4, 2);
m.commit_input(data.clone());
let mut first_literals_len: Option<usize> = None;
let mut first_offset: Option<usize> = None;
m.start_matching(|seq| {
if first_literals_len.is_some() {
return;
}
if let Sequence::Triple {
literal_len,
offset,
..
} = seq
{
first_literals_len = Some(literal_len);
first_offset = Some(offset);
}
});
assert_eq!(
first_offset,
Some(1),
"first emit must reference offset=1 — upstream zstd's default \
rep_offset1=1 fires on rep-at-ip2 at iter 1, and the \
prologue MUST NOT zero it (max_rep computed against \
window_low=0 at block 0, NOT against the sentinel \
prefix=1)",
);
assert_eq!(
first_literals_len,
Some(2),
"first emit must have a 2-byte literal prefix \
([0x01, 0x42]) — the rep-at-ip2 probe lands at ip2=3, \
then the one-byte backward extension drops new_ip to 2, \
so literals = data[0..2]. A different prefix length \
would indicate the explicit-match catch-up fired instead",
);
}
#[test]
fn fast_tags_start_at_the_target_fill() {
let (input, dictionary) = if cfg!(target_arch = "x86") {
(5 * 8 * 4096 / 8, 3 * 5 * 4096 / 4)
} else {
(3 * 8 * 4096 / 4, 3 * 3 * 4096 / 2)
};
assert!(fast_slots_pay_for_tags(input, 0, 8, 12));
assert!(!fast_slots_pay_for_tags(input - 1, 0, 8, 12));
assert!(fast_slots_pay_for_tags(0, dictionary, 8, 12));
assert!(!fast_slots_pay_for_tags(0, dictionary - 1, 8, 12));
assert!(fast_slots_pay_for_tags(usize::MAX, usize::MAX, 2, 30));
assert!(!fast_slots_pay_for_tags(1, 0, usize::MAX, 30));
assert!(!fast_slots_pay_for_tags(0, 0, 2, 10));
}