use super::super::match_table::helpers::common_prefix_len;
use super::table::LdmHashTable;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) struct LdmMatch {
pub(crate) match_pos: usize,
pub(crate) forward_len: usize,
pub(crate) backward_len: usize,
}
impl LdmMatch {
pub(crate) const fn total_len(&self) -> usize {
self.forward_len + self.backward_len
}
}
pub(crate) fn count_backwards_match(
history: &[u8],
p_in_idx: usize,
anchor_idx: usize,
p_match_idx: usize,
match_base_idx: usize,
) -> usize {
debug_assert!(p_in_idx <= history.len());
debug_assert!(p_match_idx <= history.len());
debug_assert!(anchor_idx <= p_in_idx);
debug_assert!(match_base_idx <= p_match_idx);
let mut p_in = p_in_idx;
let mut p_match = p_match_idx;
let mut len = 0usize;
while p_in > anchor_idx && p_match > match_base_idx && history[p_in - 1] == history[p_match - 1]
{
p_in -= 1;
p_match -= 1;
len += 1;
}
len
}
pub(crate) struct FindBestMatchInputs<'a> {
pub(crate) live_history: &'a [u8],
pub(crate) history_abs_start: usize,
pub(crate) split_abs: usize,
pub(crate) anchor_abs: usize,
pub(crate) lowest_index_abs: usize,
pub(crate) iend_abs: usize,
pub(crate) min_match_length: usize,
}
pub(crate) fn find_best_match(
table: &LdmHashTable,
hash_id: u32,
checksum: u32,
inputs: FindBestMatchInputs<'_>,
) -> Option<LdmMatch> {
let FindBestMatchInputs {
live_history,
history_abs_start,
split_abs,
anchor_abs,
lowest_index_abs,
iend_abs,
min_match_length,
} = inputs;
debug_assert!(history_abs_start <= split_abs);
debug_assert!(split_abs <= history_abs_start + live_history.len());
debug_assert!(anchor_abs <= split_abs);
debug_assert!(history_abs_start <= anchor_abs);
debug_assert!(split_abs <= iend_abs);
debug_assert!(iend_abs <= history_abs_start + live_history.len());
let bucket = table.bucket(hash_id);
let mut best: Option<LdmMatch> = None;
let history_abs_end = history_abs_start + live_history.len();
let split_idx = split_abs - history_abs_start;
let split_idx_end = iend_abs - history_abs_start;
for entry in bucket {
if entry.checksum != checksum {
continue;
}
let Some(match_abs) = table.resolve(entry) else {
continue;
};
if match_abs < lowest_index_abs {
continue;
}
if match_abs < history_abs_start || match_abs >= history_abs_end {
continue;
}
if match_abs >= split_abs {
continue;
}
let match_idx = match_abs - history_abs_start;
let forward_len = common_prefix_len(
&live_history[split_idx..split_idx_end],
&live_history[match_idx..],
);
if forward_len < min_match_length {
continue;
}
let backward_len = count_backwards_match(
live_history,
split_idx,
anchor_abs - history_abs_start,
match_idx,
0, );
let candidate = LdmMatch {
match_pos: match_abs,
forward_len,
backward_len,
};
match best {
Some(b) if candidate.total_len() <= b.total_len() => {}
_ => best = Some(candidate),
}
}
best
}
#[cfg(test)]
mod tests;