use super::{MATCHFINDER_WINDOW_SIZE, lz_extend, lz_hash, matchfinder_init, matchfinder_rebase};
const HC_MATCHFINDER_HASH3_ORDER: u32 = 15;
pub(crate) const HC_MATCHFINDER_HASH4_ORDER: u32 = 16;
const HC_HASH3_SIZE: usize = 1 << HC_MATCHFINDER_HASH3_ORDER;
const HC_HASH4_SIZE: usize = 1 << HC_MATCHFINDER_HASH4_ORDER;
const WINDOW_MASK: usize = MATCHFINDER_WINDOW_SIZE as usize - 1;
#[derive(Clone)]
pub(crate) struct HcMatchfinder {
hash3_tab: [i16; HC_HASH3_SIZE],
hash4_tab: [i16; HC_HASH4_SIZE],
next_tab: [i16; MATCHFINDER_WINDOW_SIZE as usize],
}
impl HcMatchfinder {
pub fn new() -> Self {
Self {
hash3_tab: [super::MATCHFINDER_INITVAL; HC_HASH3_SIZE],
hash4_tab: [super::MATCHFINDER_INITVAL; HC_HASH4_SIZE],
next_tab: [super::MATCHFINDER_INITVAL; MATCHFINDER_WINDOW_SIZE as usize],
}
}
pub fn init(&mut self) {
matchfinder_init(&mut self.hash3_tab);
matchfinder_init(&mut self.hash4_tab);
}
fn slide_window(&mut self) {
matchfinder_rebase(&mut self.hash3_tab);
matchfinder_rebase(&mut self.hash4_tab);
matchfinder_rebase(&mut self.next_tab);
}
#[inline(always)]
#[allow(clippy::too_many_arguments)]
pub fn longest_match(
&mut self,
input: &[u8],
in_base_offset: &mut usize,
in_next: usize,
best_len: u32,
max_len: u32,
nice_len: u32,
max_search_depth: u32,
good_match: u32,
next_hashes: &mut [u32; 2],
) -> (u32, u32) {
use crate::fast_bytes::load_u32_le;
let mut best_len = best_len;
let mut best_offset = 0u32;
let mut depth_remaining = max_search_depth;
let mut cur_pos = (in_next - *in_base_offset) as u32;
if cur_pos >= MATCHFINDER_WINDOW_SIZE {
self.slide_window();
*in_base_offset += MATCHFINDER_WINDOW_SIZE as usize;
cur_pos -= MATCHFINDER_WINDOW_SIZE;
}
let in_base = *in_base_offset;
let cutoff = cur_pos as i32 - MATCHFINDER_WINDOW_SIZE as i32;
let hash3 = next_hashes[0] as usize;
let hash4 = next_hashes[1] as usize;
let cur_node3 = self.hash3_tab[hash3] as i32;
let mut cur_node4 = self.hash4_tab[hash4] as i32;
self.hash3_tab[hash3] = cur_pos as i16;
self.hash4_tab[hash4] = cur_pos as i16;
self.next_tab[cur_pos as usize] = cur_node4 as i16;
if in_next + 5 <= input.len() {
let next_seq = load_u32_le(input, in_next + 1);
next_hashes[0] = lz_hash(next_seq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER);
next_hashes[1] = lz_hash(next_seq, HC_MATCHFINDER_HASH4_ORDER);
}
if max_len < 5 {
return (best_len, best_offset);
}
if best_len < 4 {
if cur_node3 <= cutoff {
return (best_len, best_offset);
}
let seq4 = load_u32_le(input, in_next);
if best_len < 3 {
let match_pos = (in_base as isize + cur_node3 as isize) as usize;
let match_seq = load_u32_le(input, match_pos);
if (match_seq & 0xFFFFFF) == (seq4 & 0xFFFFFF) {
best_len = 3;
best_offset = (in_next - match_pos) as u32;
}
}
if cur_node4 <= cutoff {
return (best_len, best_offset);
}
loop {
let match_pos = (in_base as isize + cur_node4 as isize) as usize;
let match_seq = load_u32_le(input, match_pos);
if match_seq == seq4 {
best_len = lz_extend(&input[in_next..], &input[match_pos..], 4, max_len);
best_offset = (in_next - match_pos) as u32;
if best_len >= nice_len {
return (best_len, best_offset);
}
if best_len >= good_match {
depth_remaining = (depth_remaining >> 2).max(1);
}
cur_node4 = self.next_tab[cur_node4 as usize & WINDOW_MASK] as i32;
if cur_node4 <= cutoff || {
depth_remaining -= 1;
depth_remaining == 0
} {
return (best_len, best_offset);
}
break; }
cur_node4 = self.next_tab[cur_node4 as usize & WINDOW_MASK] as i32;
if cur_node4 <= cutoff || {
depth_remaining -= 1;
depth_remaining == 0
} {
return (best_len, best_offset);
}
}
} else {
if cur_node4 <= cutoff || best_len >= nice_len {
return (best_len, best_offset);
}
}
loop {
let match_pos = (in_base as isize + cur_node4 as isize) as usize;
let tail_off = (best_len - 3) as usize;
let m_tail = load_u32_le(input, match_pos + tail_off);
let s_tail = load_u32_le(input, in_next + tail_off);
if m_tail == s_tail {
let m_head = load_u32_le(input, match_pos);
let s_head = load_u32_le(input, in_next);
if m_head == s_head {
let len = lz_extend(&input[in_next..], &input[match_pos..], 4, max_len);
if len > best_len {
best_len = len;
best_offset = (in_next - match_pos) as u32;
if best_len >= nice_len {
return (best_len, best_offset);
}
if best_len >= good_match {
depth_remaining = (depth_remaining >> 2).max(1);
}
}
}
}
cur_node4 = self.next_tab[cur_node4 as usize & WINDOW_MASK] as i32;
if cur_node4 <= cutoff || {
depth_remaining -= 1;
depth_remaining == 0
} {
return (best_len, best_offset);
}
}
}
#[inline(always)]
pub fn skip_bytes(
&mut self,
input: &[u8],
in_base_offset: &mut usize,
in_next: usize,
in_end: usize,
count: u32,
next_hashes: &mut [u32; 2],
) {
use crate::fast_bytes::load_u32_le;
if count as usize + 5 > in_end - in_next {
return;
}
let mut cur_pos = (in_next - *in_base_offset) as u32;
let mut hash3 = next_hashes[0] as usize;
let mut hash4 = next_hashes[1] as usize;
let mut pos = in_next;
let mut remaining = count;
loop {
if cur_pos >= MATCHFINDER_WINDOW_SIZE {
self.slide_window();
*in_base_offset += MATCHFINDER_WINDOW_SIZE as usize;
cur_pos -= MATCHFINDER_WINDOW_SIZE;
}
self.hash3_tab[hash3] = cur_pos as i16;
self.next_tab[cur_pos as usize] = self.hash4_tab[hash4];
self.hash4_tab[hash4] = cur_pos as i16;
pos += 1;
cur_pos += 1;
remaining -= 1;
let next_seq = load_u32_le(input, pos);
hash3 = lz_hash(next_seq & 0xFFFFFF, HC_MATCHFINDER_HASH3_ORDER) as usize;
hash4 = lz_hash(next_seq, HC_MATCHFINDER_HASH4_ORDER) as usize;
if remaining == 0 {
break;
}
}
next_hashes[0] = hash3 as u32;
next_hashes[1] = hash4 as u32;
}
}