use alloc::vec;
use alloc::vec::Vec;
use super::*;
use crate::encoding::opt::ldm::{HcOptLdmState, HcRawSeq, HcRawSeqStore};
fn raw(lit: usize, mat: usize, off: usize) -> HcRawSeq {
HcRawSeq {
lit_length: lit,
offset: off,
match_length: mat,
}
}
fn bt_with_seqs(seqs: Vec<HcRawSeq>) -> BtMatcher {
let mut bt = BtMatcher::new();
bt.ldm_sequences = seqs;
bt
}
fn store(size: usize) -> HcRawSeqStore {
HcRawSeqStore {
pos: 0,
pos_in_sequence: 0,
size,
}
}
#[test]
#[cfg(feature = "hash")]
fn prepare_ldm_candidates_translates_absolute_positions_to_slice_indices() {
use crate::encoding::ldm::{LdmProducer, params::LdmParams};
let mut bt = BtMatcher::new();
bt.ldm_producer = Some(LdmProducer::new(LdmParams {
window_log: 27,
hash_log: 10,
hash_rate_log: 4,
min_match_length: 32,
bucket_size_log: 4,
}));
let history: alloc::vec::Vec<u8> = (0u8..=255).collect();
let history_abs_start = 1024usize;
let current_abs_start = 1024usize;
let current_len = history.len();
bt.prepare_ldm_candidates(&history, history_abs_start, current_abs_start, current_len);
}
#[test]
fn ldm_skip_raw_seq_store_bytes_walks_whole_sequences() {
let bt = bt_with_seqs(vec![raw(2, 3, 100), raw(4, 1, 200), raw(0, 5, 50)]);
let mut s = store(3);
bt.ldm_skip_raw_seq_store_bytes(&mut s, 5);
assert_eq!(s.pos, 1);
assert_eq!(s.pos_in_sequence, 0);
}
#[test]
fn ldm_skip_raw_seq_store_bytes_leaves_partial_offset() {
let bt = bt_with_seqs(vec![raw(2, 3, 100), raw(4, 1, 200)]);
let mut s = store(2);
bt.ldm_skip_raw_seq_store_bytes(&mut s, 4);
assert_eq!(s.pos, 0);
assert_eq!(s.pos_in_sequence, 4);
}
#[test]
fn ldm_skip_raw_seq_store_bytes_exhausts_store_when_distance_too_large() {
let bt = bt_with_seqs(vec![raw(1, 1, 1), raw(1, 1, 1)]);
let mut s = store(2);
bt.ldm_skip_raw_seq_store_bytes(&mut s, 10);
assert_eq!(s.pos, 2);
assert_eq!(s.pos_in_sequence, 0);
}
#[test]
fn ldm_get_next_match_empty_store_clears_window() {
let bt = bt_with_seqs(vec![]);
let mut opt_ldm = HcOptLdmState::default();
opt_ldm.seq_store.size = 0;
bt.ldm_get_next_match_and_update_seq_store(&mut opt_ldm, 10, 100);
assert_eq!(opt_ldm.start_pos_in_block, usize::MAX);
assert_eq!(opt_ldm.end_pos_in_block, usize::MAX);
}
#[test]
fn ldm_get_next_match_marks_window_inside_block() {
let bt = bt_with_seqs(vec![raw(2, 5, 17)]);
let mut opt_ldm = HcOptLdmState::default();
opt_ldm.seq_store.size = 1;
bt.ldm_get_next_match_and_update_seq_store(&mut opt_ldm, 10, 100);
assert_eq!(opt_ldm.start_pos_in_block, 12);
assert_eq!(opt_ldm.end_pos_in_block, 17);
assert_eq!(opt_ldm.offset, 17);
}
#[test]
fn ldm_get_next_match_skips_when_literals_exceed_remaining_block() {
let bt = bt_with_seqs(vec![raw(50, 5, 17)]);
let mut opt_ldm = HcOptLdmState::default();
opt_ldm.seq_store.size = 1;
bt.ldm_get_next_match_and_update_seq_store(&mut opt_ldm, 0, 30);
assert_eq!(opt_ldm.start_pos_in_block, usize::MAX);
assert_eq!(opt_ldm.end_pos_in_block, usize::MAX);
assert_eq!(opt_ldm.seq_store.pos_in_sequence, 30);
}
#[test]
fn ldm_maybe_add_match_returns_none_before_window() {
let bt = bt_with_seqs(vec![]);
let opt_ldm = HcOptLdmState {
seq_store: store(0),
start_pos_in_block: 20,
end_pos_in_block: 30,
offset: 7,
};
assert!(bt.ldm_maybe_add_match(&opt_ldm, 10, 4).is_none());
}
#[test]
fn ldm_maybe_add_match_returns_none_past_window() {
let bt = bt_with_seqs(vec![]);
let opt_ldm = HcOptLdmState {
seq_store: store(0),
start_pos_in_block: 20,
end_pos_in_block: 30,
offset: 7,
};
assert!(bt.ldm_maybe_add_match(&opt_ldm, 30, 4).is_none());
assert!(bt.ldm_maybe_add_match(&opt_ldm, 35, 4).is_none());
}
#[test]
fn ldm_maybe_add_match_returns_none_below_min_match() {
let bt = bt_with_seqs(vec![]);
let opt_ldm = HcOptLdmState {
seq_store: store(0),
start_pos_in_block: 20,
end_pos_in_block: 23,
offset: 7,
};
assert!(bt.ldm_maybe_add_match(&opt_ldm, 20, 4).is_none());
}
#[test]
fn ldm_maybe_add_match_emits_candidate_inside_window() {
let bt = bt_with_seqs(vec![]);
let opt_ldm = HcOptLdmState {
seq_store: store(0),
start_pos_in_block: 20,
end_pos_in_block: 30,
offset: 7,
};
let cand = bt
.ldm_maybe_add_match(&opt_ldm, 22, 4)
.expect("should emit");
assert_eq!(cand.start, 22);
assert_eq!(cand.offset, 7);
assert_eq!(cand.match_len, 8);
}
#[test]
fn ldm_process_match_candidate_returns_none_when_store_exhausted() {
let mut bt = bt_with_seqs(vec![raw(2, 5, 17)]);
bt.ldm_sequences.clear();
let mut opt_ldm = HcOptLdmState::default();
opt_ldm.seq_store.size = 0;
assert!(
bt.ldm_process_match_candidate(&mut opt_ldm, 0, 100, 4)
.is_none()
);
}
#[test]
fn push_candidate_ladder_rejects_below_min_match() {
let mut out: Vec<MatchCandidate> = vec![];
let mut best = 0usize;
let accepted = BtMatcher::push_candidate_ladder(
&mut out,
&mut best,
MatchCandidate {
start: 10,
offset: 5,
match_len: 2,
},
4,
);
assert!(!accepted);
assert!(out.is_empty());
assert_eq!(best, 0);
}
#[test]
fn push_candidate_ladder_rejects_when_not_strictly_better() {
let mut out: Vec<MatchCandidate> = vec![];
let mut best = 8usize;
let accepted = BtMatcher::push_candidate_ladder(
&mut out,
&mut best,
MatchCandidate {
start: 10,
offset: 5,
match_len: 8,
},
4,
);
assert!(!accepted);
assert!(out.is_empty());
assert_eq!(best, 8);
}
#[test]
fn ldm_get_next_match_clamps_window_to_block_end() {
let bt = bt_with_seqs(vec![raw(0, 100, 99)]);
let mut opt_ldm = HcOptLdmState::default();
opt_ldm.seq_store.size = 1;
bt.ldm_get_next_match_and_update_seq_store(&mut opt_ldm, 10, 30);
assert_eq!(opt_ldm.start_pos_in_block, 10);
assert_eq!(opt_ldm.end_pos_in_block, 40);
assert_eq!(opt_ldm.offset, 99);
}
#[test]
fn ldm_process_match_candidate_handles_overshoot_into_next_seq() {
let bt = bt_with_seqs(vec![raw(0, 5, 17), raw(0, 5, 33)]);
let mut opt_ldm = HcOptLdmState {
seq_store: HcRawSeqStore {
pos: 1,
pos_in_sequence: 0,
size: 2,
},
start_pos_in_block: 0,
end_pos_in_block: 5,
offset: 17,
};
let _ = bt.ldm_process_match_candidate(&mut opt_ldm, 12, 100, 4);
assert_eq!(opt_ldm.seq_store.pos, 2);
}
#[test]
fn ldm_process_match_candidate_reseeds_after_overshoot() {
let bt = bt_with_seqs(vec![raw(0, 5, 17), raw(0, 5, 33)]);
let mut opt_ldm = HcOptLdmState {
seq_store: HcRawSeqStore {
pos: 1,
pos_in_sequence: 0,
size: 2,
},
start_pos_in_block: 0,
end_pos_in_block: 5,
offset: 17,
};
let cand = bt
.ldm_process_match_candidate(&mut opt_ldm, 5, 100, 4)
.expect("should emit reseeded candidate");
assert_eq!(cand.start, 5);
assert_eq!(cand.offset, 33);
assert_eq!(cand.match_len, 5);
}