use super::super::opt::types::MatchCandidate;
pub(crate) const MIN_MATCH_LEN: usize = 5;
pub(crate) const FAST_HASH_FILL_STEP: usize = 3;
pub(crate) const INCOMPRESSIBLE_SKIP_STEP: usize = 8;
#[inline(always)]
pub(crate) fn common_prefix_len(a: &[u8], b: &[u8]) -> usize {
let max = a.len().min(b.len());
unsafe {
crate::encoding::fastpath::dispatch_common_prefix_len_ptr(a.as_ptr(), b.as_ptr(), max)
}
}
pub(crate) fn best_len_offset_candidate(
lhs: Option<MatchCandidate>,
rhs: Option<MatchCandidate>,
) -> Option<MatchCandidate> {
match (lhs, rhs) {
(None, other) | (other, None) => other,
(Some(lhs), Some(rhs)) => {
if rhs.match_len > lhs.match_len
|| (rhs.match_len == lhs.match_len && rhs.offset < lhs.offset)
{
Some(rhs)
} else {
Some(lhs)
}
}
}
}
#[inline]
pub(crate) fn extend_backwards_shared(
concat: &[u8],
history_abs_start: usize,
mut candidate_pos: usize,
mut abs_pos: usize,
mut match_len: usize,
lit_len: usize,
) -> MatchCandidate {
let min_abs_pos = abs_pos - lit_len;
let concat_ptr = concat.as_ptr();
let concat_len = concat.len();
while abs_pos > min_abs_pos && candidate_pos > history_abs_start {
let cand_off = candidate_pos - history_abs_start - 1;
let cur_off = abs_pos - history_abs_start - 1;
debug_assert!(cand_off < concat_len && cur_off < concat_len);
let cand_byte = unsafe { *concat_ptr.add(cand_off) };
let cur_byte = unsafe { *concat_ptr.add(cur_off) };
if cand_byte != cur_byte {
break;
}
candidate_pos -= 1;
abs_pos -= 1;
match_len += 1;
}
MatchCandidate {
start: abs_pos,
offset: abs_pos - candidate_pos,
match_len,
}
}
#[inline]
pub(crate) fn repcode_candidate_shared(
concat: &[u8],
history_abs_start: usize,
offset_hist: [u32; 3],
abs_pos: usize,
lit_len: usize,
min_match_len: usize,
) -> Option<MatchCandidate> {
let current_idx = abs_pos - history_abs_start;
if current_idx + min_match_len > concat.len() {
return None;
}
let mut best: Option<MatchCandidate> = None;
let (rep0, rep1, rep2_opt) = if lit_len == 0 {
let r2 = if offset_hist[0] > 1 {
Some(offset_hist[0] as usize - 1)
} else {
None
};
(offset_hist[1] as usize, offset_hist[2] as usize, r2)
} else {
(
offset_hist[0] as usize,
offset_hist[1] as usize,
Some(offset_hist[2] as usize),
)
};
macro_rules! probe {
($rep:expr) => {{
let rep = $rep;
if rep != 0 && rep <= abs_pos {
let candidate_pos = abs_pos - rep;
if candidate_pos >= history_abs_start {
let candidate_idx = candidate_pos - history_abs_start;
let match_len =
common_prefix_len(&concat[candidate_idx..], &concat[current_idx..]);
if match_len >= min_match_len {
let candidate = extend_backwards_shared(
concat,
history_abs_start,
candidate_pos,
abs_pos,
match_len,
lit_len,
);
best = best_len_offset_candidate(best, Some(candidate));
}
}
}
}};
}
probe!(rep0);
probe!(rep1);
if let Some(rep2) = rep2_opt {
probe!(rep2);
}
best
}
#[derive(Copy, Clone)]
pub(crate) struct LazyMatchConfig {
pub(crate) target_len: usize,
pub(crate) min_match_len: usize,
pub(crate) lazy_depth: u8,
pub(crate) history_abs_end: usize,
}
pub(crate) fn pick_lazy_match_shared(
abs_pos: usize,
lit_len: usize,
best: Option<MatchCandidate>,
config: LazyMatchConfig,
mut best_match_at: impl FnMut(usize, usize) -> Option<MatchCandidate>,
) -> Option<MatchCandidate> {
let best = best?;
if best.match_len >= config.target_len
|| abs_pos + 1 + config.min_match_len > config.history_abs_end
{
return Some(best);
}
let next = best_match_at(abs_pos + 1, lit_len + 1);
if let Some(next) = next
&& (next.match_len > best.match_len
|| (next.match_len == best.match_len && next.offset < best.offset))
{
return None;
}
if config.lazy_depth >= 2 && abs_pos + 2 + config.min_match_len <= config.history_abs_end {
let next2 = best_match_at(abs_pos + 2, lit_len + 2);
if let Some(next2) = next2
&& next2.match_len > best.match_len + 1
{
return None;
}
}
Some(best)
}