use alloc::vec;
use super::*;
fn new_table(window: usize) -> MatchTable {
let mut t = MatchTable::new(window);
t.hash_log = 8;
t.chain_log = 8;
t.hash3_log = 0;
t
}
#[test]
fn set_dictionary_limit_from_primed_bytes_zero_clears_limit() {
let mut t = new_table(64);
t.history_abs_start = 100;
t.dictionary_limit_abs = Some(123);
t.set_dictionary_limit_from_primed_bytes(0);
assert_eq!(t.dictionary_limit_abs, None);
}
#[test]
fn set_dictionary_limit_from_primed_bytes_offsets_from_history_start() {
let mut t = new_table(64);
t.history_abs_start = 100;
t.set_dictionary_limit_from_primed_bytes(40);
assert_eq!(t.dictionary_limit_abs, Some(140));
}
#[test]
fn dms_cache_rebuilds_across_hc_bt_layout_switch() {
let mut t = new_table(64);
t.hash_log = 12;
t.search_mls = 4;
t.push_test_chunk(vec![7u8; 48]);
t.ensure_tables();
let region = 48;
t.prime_dms_hc(region);
assert_eq!(t.dms.table().unwrap().layout, DmsDictLayout::Hc);
assert_eq!(t.dms.table().unwrap().chain_table.len(), region);
t.prime_dms_bt(region);
assert_eq!(t.dms.table().unwrap().layout, DmsDictLayout::Bt);
assert_eq!(t.dms.table().unwrap().chain_table.len(), 2 * region);
t.prime_dms_hc(region);
assert_eq!(t.dms.table().unwrap().layout, DmsDictLayout::Hc);
assert_eq!(t.dms.table().unwrap().chain_table.len(), region);
}
#[test]
fn skip_matching_bt_incompressible_routes_through_sparse_block() {
let mut t = new_table(32);
t.push_test_chunk(vec![0u8; 32]);
t.ensure_tables();
t.uses_bt = true;
t.is_btultra2 = false;
t.search_depth = 4;
let before_skip_until = t.skip_insert_until_abs;
t.skip_matching(Some(true));
assert!(t.skip_insert_until_abs >= t.window_size);
assert!(t.skip_insert_until_abs > before_skip_until);
}
#[test]
fn skip_matching_bt_dense_routes_through_bt_update_tree() {
let mut t = new_table(32);
t.push_test_chunk(vec![1u8; 32]);
t.ensure_tables();
t.uses_bt = true;
t.is_btultra2 = false;
t.search_depth = 4;
t.skip_matching(None);
assert_eq!(t.skip_insert_until_abs, t.history_abs_start + t.window_size);
}
#[test]
fn replay_history_for_rebase_bt_walks_inserted_prefix() {
let mut t = new_table(64);
t.history = vec![0u8; 64];
for (i, slot) in t.history.iter_mut().enumerate() {
*slot = (i % 17) as u8;
}
t.history_start = 0;
t.history_abs_start = 0;
t.window_size = 64;
t.position_base = 0;
t.search_depth = 4;
t.uses_bt = true;
t.ensure_tables();
assert!(t.hash_table.iter().all(|&v| v == HC_EMPTY));
t.replay_history_for_rebase_bt(0, 32);
assert!(
t.hash_table.iter().any(|&v| v != HC_EMPTY),
"BT replay must populate hash table"
);
}
#[test]
fn begin_rebase_clears_index_tables_and_resets_base() {
let mut t = new_table(32);
t.hash_table = vec![7; 16];
t.chain_table = vec![9; 16];
t.hash3_table = vec![5; 16];
t.history_abs_start = 50;
t.position_base = 0;
t.index_shift = 4;
t.allow_zero_relative_position = false;
t.begin_rebase();
assert_eq!(t.position_base, 50);
assert_eq!(t.index_shift, 0);
assert!(t.allow_zero_relative_position);
assert!(t.hash_table.iter().all(|&v| v == HC_EMPTY));
assert!(t.chain_table.iter().all(|&v| v == HC_EMPTY));
assert!(t.hash3_table.iter().all(|&v| v == HC_EMPTY));
}
#[test]
fn rebase_positions_cold_rebuilds_hash3_for_btultra2() {
let mut t = new_table(64);
t.history = b"abcdef_abcdef_abcdef_abcdef_abcdef_abcdef".to_vec();
t.history_start = 0;
t.history_abs_start = 0;
t.window_size = t.history.len();
t.chunk_lens.push_back(t.history.len());
t.hash_log = 8;
t.chain_log = 8;
t.hash3_log = 6;
t.is_btultra2 = true;
t.search_depth = 4;
t.ensure_tables();
t.update_hash3_until(20);
assert!(
t.hash3_table.iter().any(|&v| v != HC_EMPTY),
"fixture precondition: hash3 must be non-empty before rebase"
);
t.rebase_positions_cold(20);
assert!(
t.hash3_table.iter().any(|&v| v != HC_EMPTY),
"rebase must repopulate the HC3 side table — \
btultra2 short-match selection depends on it"
);
}
#[test]
fn insert_positions_with_step_zero_step_is_noop() {
let mut t = new_table(32);
t.history = vec![0u8; 32];
t.push_test_chunk(vec![0u8; 32]);
t.ensure_tables();
let next_to_update3_before = t.next_to_update3;
t.insert_positions_with_step(0, 16, 0);
assert!(t.hash_table.iter().all(|&v| v == HC_EMPTY));
assert_eq!(t.next_to_update3, next_to_update3_before);
}
#[test]
fn insert_positions_with_step_saturating_step_breaks_loop() {
let mut t = new_table(32);
t.history = vec![1u8; 32];
t.push_test_chunk(vec![1u8; 32]);
t.ensure_tables();
t.insert_positions_with_step(0, 16, usize::MAX);
let non_empty = t.hash_table.iter().filter(|&&v| v != HC_EMPTY).count();
assert!(
non_empty <= 1,
"step=usize::MAX must break after the first insert"
);
}
#[test]
fn apply_limited_update_after_long_match_hc_mode_is_noop() {
let mut t = new_table(32);
t.uses_bt = false;
t.skip_insert_until_abs = 100;
t.apply_limited_update_after_long_match(1000);
assert_eq!(
t.skip_insert_until_abs, 100,
"HC mode must not adjust skip cursor"
);
}
#[test]
fn apply_limited_update_after_long_match_bt_mode_caps_gap_at_384() {
let mut t = new_table(32);
t.uses_bt = true;
t.skip_insert_until_abs = 0;
t.apply_limited_update_after_long_match(1000);
assert_eq!(t.skip_insert_until_abs, 808);
}
#[test]
fn apply_limited_update_after_long_match_small_gap_is_noop() {
let mut t = new_table(32);
t.uses_bt = true;
t.skip_insert_until_abs = 800;
t.apply_limited_update_after_long_match(1000);
assert_eq!(t.skip_insert_until_abs, 800);
}
#[test]
fn emit_optimal_plan_empty_plan_emits_full_literals() {
let mut t = new_table(8);
t.push_test_chunk(b"abcdefgh".to_vec());
let mut emitted: Vec<u8> = Vec::new();
t.emit_optimal_plan(8, &[], &mut |seq| {
if let Sequence::Literals { literals } = seq {
emitted.extend_from_slice(literals);
}
});
assert_eq!(emitted, b"abcdefgh");
}
#[test]
fn emit_optimal_plan_skips_oversized_plan_item_and_emits_trailing_literals() {
let mut t = new_table(8);
t.push_test_chunk(b"abcdefgh".to_vec());
let plan = [HcOptimalSequence {
offset: 1,
lit_len: 4,
match_len: 99, }];
let mut triples = 0usize;
let mut trailing: Vec<u8> = Vec::new();
t.emit_optimal_plan(8, &plan, &mut |seq| match seq {
Sequence::Triple { .. } => triples += 1,
Sequence::Literals { literals } => trailing.extend_from_slice(literals),
});
assert_eq!(triples, 0, "oversized plan item must be skipped");
assert_eq!(
trailing, b"abcdefgh",
"trailing-literals path must emit the full window when plan skipped everything"
);
}