use alloc::vec::Vec;
use crate::encoding::Sequence;
use crate::encoding::dict_attach::DictAttach;
use super::fast_kernel::hash_table::{FastHashTable, hash_ptr_raw};
use super::fast_kernel::kernel::compress_block_fast;
use super::fast_kernel::kernel::{DICT_TAG_BITS, DICT_TAG_MASK};
pub(crate) const FAST_LEVEL_1_HASH_LOG: u32 = 14;
pub(crate) const FAST_LEVEL_1_MLS: u32 = 7;
#[cfg(test)]
pub(crate) const FAST_LEVEL_1_WINDOW_LOG: u8 = 19;
pub(crate) const FAST_INITIAL_REP: [u32; 2] = [1, 4];
pub(crate) const FAST_INITIAL_OFFSET_HIST: [u32; 3] = [1, 4, 8];
pub(crate) const HISTORY_DRAIN_BASE: usize = 0;
const INITIAL_PREFIX_START_INDEX: u32 = 1;
pub(crate) struct FastKernelMatcher {
history: Vec<u8>,
prefix_start_index: u32,
rep: [u32; 2],
pub(crate) offset_hist: [u32; 3],
hash_table: FastHashTable,
pub(crate) max_window_size: usize,
window_log: u8,
use_cmov: bool,
kernel: crate::encoding::fastpath::FastpathKernel,
step_size: usize,
pending: Option<Vec<u8>>,
last_block_start: usize,
recycled_space: Option<Vec<u8>>,
borrowed: Option<(*const u8, usize)>,
last_borrowed_block: Option<(usize, usize)>,
dict: DictAttach<FastHashTable>,
table_pos_high_water: usize,
dict_resident: bool,
loaded_dict_end: usize,
}
impl Clone for FastKernelMatcher {
fn clone(&self) -> Self {
Self {
history: self.history.clone(),
prefix_start_index: self.prefix_start_index,
rep: self.rep,
offset_hist: self.offset_hist,
hash_table: self.hash_table.clone(),
max_window_size: self.max_window_size,
window_log: self.window_log,
use_cmov: self.use_cmov,
kernel: self.kernel,
step_size: self.step_size,
pending: self.pending.clone(),
last_block_start: self.last_block_start,
recycled_space: self.recycled_space.clone(),
borrowed: self.borrowed,
last_borrowed_block: self.last_borrowed_block,
dict: self.dict.clone(),
table_pos_high_water: self.table_pos_high_water,
dict_resident: self.dict_resident,
loaded_dict_end: self.loaded_dict_end,
}
}
fn clone_from(&mut self, source: &Self) {
self.history.clone_from(&source.history);
self.prefix_start_index = source.prefix_start_index;
self.rep = source.rep;
self.offset_hist = source.offset_hist;
self.hash_table.clone_from(&source.hash_table);
self.max_window_size = source.max_window_size;
self.window_log = source.window_log;
self.use_cmov = source.use_cmov;
self.kernel = source.kernel;
self.step_size = source.step_size;
self.pending.clone_from(&source.pending);
self.last_block_start = source.last_block_start;
self.recycled_space.clone_from(&source.recycled_space);
self.borrowed = source.borrowed;
self.last_borrowed_block = source.last_borrowed_block;
self.dict.clone_from(&source.dict);
self.table_pos_high_water = source.table_pos_high_water;
self.dict_resident = source.dict_resident;
self.loaded_dict_end = source.loaded_dict_end;
}
}
impl FastKernelMatcher {
#[cfg(test)]
pub(crate) fn new() -> Self {
Self::with_params(
FAST_LEVEL_1_WINDOW_LOG,
FAST_LEVEL_1_HASH_LOG,
FAST_LEVEL_1_MLS,
2,
)
}
#[cfg(test)]
pub(crate) fn step_size(&self) -> usize {
self.step_size
}
#[cfg(test)]
pub(crate) fn hash_log(&self) -> u32 {
self.hash_table.hash_log()
}
pub(crate) fn dict_is_attached(&self) -> bool {
self.dict.is_attached()
}
pub(crate) fn block_samples_match_dict(&self, block: &[u8]) -> bool {
use super::fast_kernel::kernel::dict_lookup;
const HASH_READ_SIZE: usize = 8;
if !self.dict.is_attached() || block.len() < HASH_READ_SIZE {
return false;
}
let Some(dict_tab) = self.dict.table() else {
return false;
};
let dict_end = self.dict.region_len();
if dict_end > self.history.len() || dict_end < HASH_READ_SIZE {
return false;
}
let dict_bytes = &self.history[..dict_end];
let dhl = dict_tab.hash_log();
let mls = self.hash_table.mls();
let last = block.len() - HASH_READ_SIZE;
let step = (block.len() / 32).max(1);
let base = block.as_ptr();
let mut pos = 0;
while pos <= last {
let dpos = unsafe {
match mls {
4 => dict_lookup::<4>(dict_tab, base.add(pos), dhl),
5 => dict_lookup::<5>(dict_tab, base.add(pos), dhl),
6 => dict_lookup::<6>(dict_tab, base.add(pos), dhl),
7 => dict_lookup::<7>(dict_tab, base.add(pos), dhl),
_ => dict_lookup::<8>(dict_tab, base.add(pos), dhl),
}
} as usize;
const USEFUL_DICT_MATCH: usize = 16;
if dpos >= 1
&& dpos + 4 <= dict_end
&& block[pos..pos + 4] == dict_bytes[dpos..dpos + 4]
{
let cap = (block.len() - pos).min(dict_end - dpos);
let mut len = 4;
while len < cap && block[pos + len] == dict_bytes[dpos + len] {
len += 1;
}
if len >= USEFUL_DICT_MATCH {
return true;
}
}
pos += step;
}
false
}
#[cfg(test)]
pub(crate) fn mls(&self) -> u32 {
self.hash_table.mls()
}
pub(crate) fn with_params(window_log: u8, hash_log: u32, mls: u32, step_size: usize) -> Self {
Self::with_params_table(
window_log,
hash_log,
mls,
step_size,
FastHashTable::new(hash_log, mls),
)
}
pub(crate) fn with_params_deferred(
window_log: u8,
hash_log: u32,
mls: u32,
step_size: usize,
) -> Self {
Self::with_params_table(
window_log,
hash_log,
mls,
step_size,
FastHashTable::new_deferred(hash_log, mls),
)
}
fn with_params_table(
window_log: u8,
_hash_log: u32,
_mls: u32,
step_size: usize,
hash_table: FastHashTable,
) -> Self {
assert!(
step_size >= 2,
"FastKernelMatcher requires step_size >= 2 (got {step_size})"
);
assert!(
window_log <= 30,
"FastKernelMatcher requires window_log <= 30 (got {window_log}); \
2 * (1 << 30) is the eviction-band ceiling that keeps history \
length below the kernel's u32::MAX input bound"
);
let history = alloc::vec![0u8; HISTORY_DRAIN_BASE];
Self {
last_block_start: HISTORY_DRAIN_BASE,
recycled_space: None,
history,
prefix_start_index: INITIAL_PREFIX_START_INDEX,
rep: FAST_INITIAL_REP,
offset_hist: FAST_INITIAL_OFFSET_HIST,
hash_table,
max_window_size: 1usize << window_log,
window_log,
use_cmov: window_log < 19,
kernel: crate::encoding::fastpath::select_kernel(),
step_size,
pending: None,
borrowed: None,
last_borrowed_block: None,
dict: DictAttach::new(),
table_pos_high_water: 0,
dict_resident: false,
loaded_dict_end: 0,
}
}
pub(crate) fn reset(
&mut self,
window_log: u8,
hash_log: u32,
mls: u32,
step_size: usize,
dict_attach_epoch: bool,
table_overwritten_by_restore: bool,
) {
assert!(
step_size >= 2,
"FastKernelMatcher requires step_size >= 2 (got {step_size})"
);
assert!(
window_log <= 30,
"FastKernelMatcher requires window_log <= 30 (got {window_log})"
);
let mut reborrow_region: Option<usize> = None;
if !self.hash_table.is_allocated() {
self.hash_table = FastHashTable::new(hash_log, mls);
self.dict.invalidate();
} else if table_overwritten_by_restore
&& self.hash_table.hash_log() == hash_log
&& self.hash_table.mls() == mls
{
} else if self.hash_table.hash_log() != hash_log || self.hash_table.mls() != mls {
self.hash_table = FastHashTable::new(hash_log, mls);
self.dict.invalidate();
} else if dict_attach_epoch && self.dict.is_primed() {
let span = u32::try_from(self.table_pos_high_water).unwrap_or(u32::MAX);
self.hash_table.advance_epoch(span);
let region = self.dict.region_len();
if region > 0 && self.history.len() >= region {
reborrow_region = Some(region);
}
} else {
self.hash_table.clear();
}
self.table_pos_high_water = 0;
self.loaded_dict_end = 0;
if let Some(region) = reborrow_region {
self.history.truncate(region);
self.dict_resident = true;
} else {
self.history.clear();
self.history.resize(HISTORY_DRAIN_BASE, 0);
self.dict_resident = false;
}
self.prefix_start_index = INITIAL_PREFIX_START_INDEX;
self.rep = FAST_INITIAL_REP;
self.offset_hist = FAST_INITIAL_OFFSET_HIST;
self.window_log = window_log;
self.use_cmov = window_log < 19;
self.step_size = step_size;
self.max_window_size = 1usize << window_log;
if let Some(region) = reborrow_region {
let headroom = crate::encoding::match_table::storage::MAX_PRIMED_WINDOW_SIZE
- self.max_window_size;
self.max_window_size += region.min(headroom);
}
self.pending = None;
self.last_block_start = reborrow_region.unwrap_or(HISTORY_DRAIN_BASE);
self.recycled_space = None;
self.borrowed = None;
self.last_borrowed_block = None;
}
#[cfg(test)]
pub(crate) fn window_size(&self) -> u64 {
1u64 << self.window_log
}
pub(crate) fn heap_size(&self) -> usize {
self.history.capacity()
+ self.hash_table.heap_size()
+ self.pending.as_ref().map_or(0, |v| v.capacity())
+ self.recycled_space.as_ref().map_or(0, |v| v.capacity())
+ self.dict.table().map_or(0, |t| t.heap_size())
}
#[inline(always)]
fn history_bytes(&self) -> &[u8] {
match self.borrowed {
Some((ptr, len)) => unsafe { core::slice::from_raw_parts(ptr, len) },
None => &self.history,
}
}
pub(crate) unsafe fn set_borrowed_window(&mut self, buffer: &[u8]) {
assert!(
self.pending.is_none(),
"set_borrowed_window requires no staged owned block; reset before switching to a borrowed window",
);
debug_assert!(
self.borrowed.is_none(),
"set_borrowed_window called without a preceding reset()/clear_borrowed_window",
);
self.borrowed = Some((buffer.as_ptr(), buffer.len()));
self.last_borrowed_block = None;
self.prefix_start_index = INITIAL_PREFIX_START_INDEX;
}
pub(crate) fn clear_borrowed_window(&mut self) {
self.borrowed = None;
self.last_borrowed_block = None;
self.prefix_start_index = INITIAL_PREFIX_START_INDEX;
}
pub(crate) fn last_committed_space(&self) -> &[u8] {
if let Some(slice) = self.pending.as_deref() {
return slice;
}
if let Some((start, end)) = self.last_borrowed_block {
return &self.history_bytes()[start..end];
}
&self.history_bytes()[self.last_block_start..]
}
pub(crate) fn accept_data(&mut self, space: Vec<u8>) {
assert!(
self.pending.is_none(),
"FastKernelMatcher: accept_data called with a still-pending buffer; \
the driver must run start_matching / skip_matching between commits",
);
let real_len = self.history.len().saturating_sub(HISTORY_DRAIN_BASE);
let cap = self.max_window_size * 2;
assert!(
space.len() <= cap,
"FastKernelMatcher requires block_size <= 2 × max_window_size \
(block={}, cap={})",
space.len(),
cap,
);
if real_len > cap - space.len() {
let retain_real = cap.saturating_sub(space.len()).min(self.max_window_size);
let drop_n = real_len.saturating_sub(retain_real);
if drop_n > 0 {
self.drain_real_prefix(drop_n);
}
}
self.pending = Some(space);
}
fn drain_real_prefix(&mut self, drop_n: usize) {
let drain_end = HISTORY_DRAIN_BASE + drop_n;
self.history.drain(HISTORY_DRAIN_BASE..drain_end);
self.prefix_start_index = INITIAL_PREFIX_START_INDEX;
self.dict.invalidate();
self.loaded_dict_end = 0;
self.hash_table.clear();
self.last_block_start = self.last_block_start.saturating_sub(drop_n);
self.prime_hash_table_for_range(INITIAL_PREFIX_START_INDEX as usize);
}
fn extend_history_with_pending(&mut self) -> usize {
let mut space = self
.pending
.take()
.expect("extend_history_with_pending without a pending buffer");
let block_start = self.history.len();
self.history.extend_from_slice(&space);
self.table_pos_high_water = self.table_pos_high_water.max(self.history.len());
self.last_block_start = block_start;
space.clear();
self.recycled_space = Some(space);
block_start
}
pub(crate) fn take_recycled_space(&mut self) -> Option<Vec<u8>> {
self.recycled_space.take()
}
pub(crate) fn start_matching(&mut self, handle_sequence: impl for<'a> FnMut(Sequence<'a>)) {
assert!(
self.borrowed.is_none(),
"start_matching is the owned path; clear the borrowed window first (use start_matching_borrowed)",
);
let block_start = self.extend_history_with_pending();
let advertised_window = 1usize << self.window_log;
let block_end = self.history_bytes().len();
let windowed_low = block_end.saturating_sub(advertised_window) as u32;
let effective_dict_end = if self.loaded_dict_end != 0 {
self.loaded_dict_end
} else if self.dict.is_attached() {
self.dict.region_len()
} else {
0
};
let dict_in_window =
effective_dict_end != 0 && block_end <= advertised_window + effective_dict_end;
let window_low = if dict_in_window { 0 } else { windowed_low };
let prefix_start_index = window_low.max(self.prefix_start_index);
let rep_in = self.rep;
let mls = self.hash_table.mls();
let step_size = self.step_size;
let use_cmov = self.use_cmov;
let use_dict = self.dict.is_attached();
let history: &[u8] = &self.history;
let rep_out = if use_dict {
use super::fast_kernel::kernel::PrefixBounds;
let dict_end = self.dict.region_len() as u32;
let bounds = PrefixBounds {
prefix_start_index,
window_low,
};
let main_table = &mut self.hash_table;
let dict_table = self
.dict
.table()
.expect("use_dict implies dict_table is Some");
run_fast_kernel_block_dict(
history,
block_start,
bounds,
dict_end,
main_table,
dict_table,
rep_in,
step_size,
mls,
use_cmov,
handle_sequence,
)
} else {
run_fast_kernel_block(
history,
block_start,
prefix_start_index,
window_low,
&mut self.hash_table,
rep_in,
step_size,
mls,
use_cmov,
handle_sequence,
)
};
self.rep = rep_out;
}
pub(crate) fn start_matching_borrowed(
&mut self,
block_start: usize,
block_end: usize,
handle_sequence: impl for<'a> FnMut(Sequence<'a>),
) {
let (ptr, total_len) = self
.borrowed
.expect("start_matching_borrowed requires a registered borrowed window");
assert!(
block_start <= block_end && block_end <= total_len,
"borrowed block bounds out of range: start={block_start} end={block_end} total={total_len}",
);
self.table_pos_high_water = self.table_pos_high_water.max(block_end);
let advertised_window = 1usize << self.window_log;
let window_low = block_end.saturating_sub(advertised_window) as u32;
let prefix_start_index = window_low.max(self.prefix_start_index);
let rep_in = self.rep;
let mls = self.hash_table.mls();
let history: &[u8] = unsafe { core::slice::from_raw_parts(ptr, block_end) };
let rep_out = run_fast_kernel_block(
history,
block_start,
prefix_start_index,
window_low,
&mut self.hash_table,
rep_in,
self.step_size,
mls,
self.use_cmov,
handle_sequence,
);
self.rep = rep_out;
self.last_borrowed_block = Some((block_start, block_end));
}
pub(crate) fn start_matching_borrowed_dict(
&mut self,
block_start: usize,
block_end: usize,
mut handle_sequence: impl for<'a> FnMut(Sequence<'a>),
) {
use super::fast_kernel::kernel::{PrefixBounds, compress_block_fast_dict_borrowed};
let (ptr, total_len) = self
.borrowed
.expect("start_matching_borrowed_dict requires a registered borrowed window");
assert!(
block_start <= block_end && block_end <= total_len,
"borrowed block bounds out of range: start={block_start} end={block_end} total={total_len}",
);
self.last_borrowed_block = Some((block_start, block_end));
let dict_end = self.dict.region_len();
let virtual_len = dict_end
.checked_add(block_end)
.filter(|&v| v <= u32::MAX as usize)
.expect("dict_end + block_end exceeds the u32 FastKernel position space");
self.table_pos_high_water = self.table_pos_high_water.max(virtual_len);
let advertised_window = 1usize << self.window_log;
let mls = self.hash_table.mls();
let use_cmov = self.use_cmov;
let step_size = self.step_size;
let rep_in = self.rep;
let kernel = self.kernel;
let windowed_low = virtual_len.saturating_sub(advertised_window);
let dict_in_window = dict_end != 0 && virtual_len <= advertised_window + dict_end;
let window_low = if dict_in_window { 0 } else { windowed_low };
let prefix_start_index = window_low.max(self.prefix_start_index as usize) as u32;
let bounds = PrefixBounds {
prefix_start_index,
window_low: window_low as u32,
};
let inp: &[u8] = unsafe { core::slice::from_raw_parts(ptr, block_end) };
debug_assert!(
dict_end <= self.history.len(),
"dict region_len ({dict_end}) exceeds history.len() ({}) — \
dictionary bytes must be committed before borrowed-dict scan",
self.history.len(),
);
let dict: &[u8] = unsafe { core::slice::from_raw_parts(self.history.as_ptr(), dict_end) };
let dict_tab_ptr: *const FastHashTable = self
.dict
.table()
.expect("start_matching_borrowed_dict requires an attached dict table");
let dict_tab: &FastHashTable = unsafe { &*dict_tab_ptr };
let main_table = &mut self.hash_table;
macro_rules! run {
($mls:literal, $cmov:literal) => {
compress_block_fast_dict_borrowed::<$mls, $cmov>(
inp,
dict,
block_start,
block_end,
main_table,
dict_tab,
bounds,
rep_in,
step_size,
&mut handle_sequence,
kernel,
)
};
}
let result = match (mls, use_cmov) {
(4, false) => run!(4, false),
(4, true) => run!(4, true),
(5, false) => run!(5, false),
(5, true) => run!(5, true),
(6, false) => run!(6, false),
(6, true) => run!(6, true),
(7, false) => run!(7, false),
(7, true) => run!(7, true),
(8, false) => run!(8, false),
(8, true) => run!(8, true),
_ => {
unreachable!("FastHashTable construction rejects mls outside 4..=8 — got mls={mls}")
}
};
self.rep = result.rep;
if result.tail_literals_len > 0 {
let tail_start = block_end - result.tail_literals_len;
handle_sequence(Sequence::Literals {
literals: &inp[tail_start..block_end],
});
}
}
pub(crate) fn stage_borrowed_block(&mut self, block_start: usize, block_end: usize) {
let (_ptr, total_len) = self
.borrowed
.expect("stage_borrowed_block requires a registered borrowed window");
assert!(
block_start <= block_end && block_end <= total_len,
"staged borrowed block bounds out of range: start={block_start} end={block_end} total={total_len}",
);
self.last_borrowed_block = Some((block_start, block_end));
}
pub(crate) fn skip_matching_with_hint(&mut self, incompressible_hint: Option<bool>) {
let block_start = self.extend_history_with_pending();
if incompressible_hint == Some(false) {
self.prime_hash_table_for_range(block_start);
self.loaded_dict_end = self.history.len();
}
}
pub(crate) fn skip_matching_borrowed(
&mut self,
block_start: usize,
block_end: usize,
incompressible_hint: Option<bool>,
) {
let (_ptr, total_len) = self
.borrowed
.expect("skip_matching_borrowed requires a registered borrowed window");
assert!(
block_start <= block_end && block_end <= total_len,
"borrowed block bounds out of range: start={block_start} end={block_end} total={total_len}",
);
self.last_borrowed_block = Some((block_start, block_end));
if incompressible_hint == Some(false) {
self.prime_hash_table_for_range_borrowed(block_start, block_end);
}
}
fn prime_hash_table_for_range_borrowed(&mut self, range_start: usize, block_end: usize) {
const HASH_READ_SIZE: usize = 8;
if block_end < HASH_READ_SIZE {
return;
}
let last_hashable = block_end - HASH_READ_SIZE;
let backfill_start = range_start.saturating_sub(HASH_READ_SIZE - 1);
if backfill_start > last_hashable {
return;
}
let (base, _len) = self
.borrowed
.expect("prime_hash_table_for_range_borrowed requires a registered borrowed window");
let base_offset = self.dict.region_len();
debug_assert!(
base_offset
.checked_add(block_end)
.is_some_and(|v| v <= u32::MAX as usize),
"virtual position overflow: dict.region_len()={base_offset} + block_end={block_end} exceeds u32",
);
match self.hash_table.mls() {
4 => self.prime_hash_table_impl::<4>(base, backfill_start, last_hashable, base_offset),
5 => self.prime_hash_table_impl::<5>(base, backfill_start, last_hashable, base_offset),
6 => self.prime_hash_table_impl::<6>(base, backfill_start, last_hashable, base_offset),
7 => self.prime_hash_table_impl::<7>(base, backfill_start, last_hashable, base_offset),
8 => self.prime_hash_table_impl::<8>(base, backfill_start, last_hashable, base_offset),
_ => unreachable!("FastHashTable construction rejects mls outside 4..=8"),
}
}
pub(crate) fn prime_offset_history(&mut self, offset_hist: [u32; 3]) {
self.offset_hist = offset_hist;
self.rep = [offset_hist[0], offset_hist[1]];
}
pub(crate) fn history_len_for_eviction_accounting(&self) -> usize {
self.history.len().saturating_sub(HISTORY_DRAIN_BASE)
}
pub(crate) fn trim_to_window(&mut self) -> usize {
let real_len = self.history.len().saturating_sub(HISTORY_DRAIN_BASE);
if real_len <= self.max_window_size {
return 0;
}
let drop_n = real_len - self.max_window_size;
self.drain_real_prefix(drop_n);
drop_n
}
fn prime_hash_table_for_range(&mut self, range_start: usize) {
let history_len = self.history.len();
const HASH_READ_SIZE: usize = 8;
if history_len < HASH_READ_SIZE {
return;
}
let last_hashable = history_len - HASH_READ_SIZE;
let backfill_start = range_start.saturating_sub(HASH_READ_SIZE - 1);
if backfill_start > last_hashable {
return;
}
let base = self.history.as_ptr();
match self.hash_table.mls() {
4 => self.prime_hash_table_impl::<4>(base, backfill_start, last_hashable, 0),
5 => self.prime_hash_table_impl::<5>(base, backfill_start, last_hashable, 0),
6 => self.prime_hash_table_impl::<6>(base, backfill_start, last_hashable, 0),
7 => self.prime_hash_table_impl::<7>(base, backfill_start, last_hashable, 0),
8 => self.prime_hash_table_impl::<8>(base, backfill_start, last_hashable, 0),
_ => unreachable!("FastHashTable construction rejects mls outside 4..=8"),
}
}
fn prime_hash_table_impl<const MLS: u32>(
&mut self,
base: *const u8,
range_start: usize,
last_hashable: usize,
base_offset: usize,
) {
for pos in range_start..=last_hashable {
let ptr = unsafe { base.add(pos) };
let hash = unsafe { self.hash_table.hash_ptr::<MLS>(ptr) };
unsafe { self.hash_table.put(hash, (base_offset + pos) as u32) };
}
}
pub(crate) fn skip_matching_for_dict_prime(&mut self) {
let block_start = self.extend_history_with_pending();
self.prime_dict_table_for_range(block_start);
}
pub(crate) fn mark_dict_primed(&mut self) {
self.dict.mark_primed();
}
pub(crate) fn dict_resident(&self) -> bool {
self.dict_resident
}
pub(crate) fn invalidate_dict_cache(&mut self) {
self.dict.invalidate();
}
fn prime_dict_table_for_range(&mut self, range_start: usize) {
const HASH_READ_SIZE: usize = 8;
let history_len = self.history.len();
self.dict.set_region_len(history_len);
if self.dict.is_primed() {
return;
}
if history_len < HASH_READ_SIZE {
return;
}
let last_hashable = history_len - HASH_READ_SIZE;
debug_assert!(
self.dict.next_to_update() <= range_start,
"dict fill origin {} ran ahead of slice start {range_start}",
self.dict.next_to_update(),
);
let fill_start = self.dict.next_to_update();
if fill_start > last_hashable {
return;
}
let hash_log = self.hash_table.hash_log();
let mls = self.hash_table.mls();
self.dict
.table_mut_or_init(|| FastHashTable::new(hash_log, mls));
let base = self.history.as_ptr();
let next = match self.hash_table.mls() {
4 => self.prime_dict_table_impl::<4>(base, fill_start, last_hashable),
5 => self.prime_dict_table_impl::<5>(base, fill_start, last_hashable),
6 => self.prime_dict_table_impl::<6>(base, fill_start, last_hashable),
7 => self.prime_dict_table_impl::<7>(base, fill_start, last_hashable),
8 => self.prime_dict_table_impl::<8>(base, fill_start, last_hashable),
_ => unreachable!("FastHashTable construction rejects mls outside 4..=8"),
};
self.dict.set_next_to_update(next);
}
fn prime_dict_table_impl<const MLS: u32>(
&mut self,
base: *const u8,
range_start: usize,
last_hashable: usize,
) -> usize {
let dict_table = self
.dict
.table_mut()
.expect("prime_dict_table_for_range creates the table before this call");
let dict_hash_log = dict_table.hash_log();
assert!(
dict_hash_log + DICT_TAG_BITS <= 32,
"tagged Fast dict fill requires dict hash_log <= {} (got {dict_hash_log})",
32 - DICT_TAG_BITS,
);
assert!(
last_hashable >> (32 - DICT_TAG_BITS) == 0,
"dict region too large for the tagged fast-table position field \
(last_hashable={last_hashable}, max={})",
(1usize << (32 - DICT_TAG_BITS)) - 1,
);
for pos in range_start..=last_hashable {
let hat = unsafe { hash_ptr_raw::<MLS>(base.add(pos), dict_hash_log + DICT_TAG_BITS) };
unsafe {
dict_table.put(
hat >> DICT_TAG_BITS,
((pos as u32) << DICT_TAG_BITS) | (hat & DICT_TAG_MASK),
)
};
}
last_hashable + 1
}
}
#[allow(clippy::too_many_arguments)]
fn run_fast_kernel_block(
history: &[u8],
block_start: usize,
prefix_start_index: u32,
window_low: u32,
hash_table: &mut FastHashTable,
rep_in: [u32; 2],
step_size: usize,
mls: u32,
use_cmov: bool,
mut handle_sequence: impl for<'a> FnMut(Sequence<'a>),
) -> [u32; 2] {
use super::fast_kernel::kernel::PrefixBounds;
let bounds = PrefixBounds {
prefix_start_index,
window_low,
};
let result = match (mls, use_cmov) {
(4, false) => compress_block_fast::<4, false>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(4, true) => compress_block_fast::<4, true>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(5, false) => compress_block_fast::<5, false>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(5, true) => compress_block_fast::<5, true>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(6, false) => compress_block_fast::<6, false>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(6, true) => compress_block_fast::<6, true>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(7, false) => compress_block_fast::<7, false>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(7, true) => compress_block_fast::<7, true>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(8, false) => compress_block_fast::<8, false>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
(8, true) => compress_block_fast::<8, true>(
history,
block_start,
bounds,
hash_table,
rep_in,
step_size,
&mut handle_sequence,
),
_ => unreachable!(
"FastHashTable construction rejects mls outside 4..=8 — \
got mls={mls} which means the table was bypassed",
),
};
if result.tail_literals_len > 0 {
let tail_start = history.len() - result.tail_literals_len;
handle_sequence(Sequence::Literals {
literals: &history[tail_start..],
});
}
result.rep
}
#[allow(clippy::too_many_arguments)]
fn run_fast_kernel_block_dict(
history: &[u8],
block_start: usize,
bounds: super::fast_kernel::kernel::PrefixBounds,
dict_end: u32,
main_table: &mut FastHashTable,
dict_table: &FastHashTable,
rep_in: [u32; 2],
step_size: usize,
mls: u32,
use_cmov: bool,
mut handle_sequence: impl for<'a> FnMut(Sequence<'a>),
) -> [u32; 2] {
use super::fast_kernel::kernel::compress_block_fast_dict;
macro_rules! run {
($mls:literal, $cmov:literal) => {
compress_block_fast_dict::<$mls, $cmov>(
history,
block_start,
bounds,
main_table,
dict_table,
dict_end,
rep_in,
step_size,
&mut handle_sequence,
)
};
}
let result = match (mls, use_cmov) {
(4, false) => run!(4, false),
(4, true) => run!(4, true),
(5, false) => run!(5, false),
(5, true) => run!(5, true),
(6, false) => run!(6, false),
(6, true) => run!(6, true),
(7, false) => run!(7, false),
(7, true) => run!(7, true),
(8, false) => run!(8, false),
(8, true) => run!(8, true),
_ => unreachable!("FastHashTable construction rejects mls outside 4..=8 — got mls={mls}",),
};
if result.tail_literals_len > 0 {
let tail_start = history.len() - result.tail_literals_len;
handle_sequence(Sequence::Literals {
literals: &history[tail_start..],
});
}
result.rep
}
#[cfg(test)]
mod tests;