use alloc::vec::Vec;
use super::CompressionLevel;
use super::Matcher;
use super::Sequence;
use super::cost_model::HC_FORMAT_MINMATCH;
#[cfg(test)]
use super::cost_model::HC_MAX_LIT;
#[cfg(test)]
use super::cost_model::{
HC_BITCOST_MULTIPLIER, HC_OPT_NUM, HC_PREDEF_THRESHOLD, HcOptState, HcOptimalCostProfile,
};
#[cfg(test)]
use super::cost_model::{HC_BLOCKSIZE_MAX, HC_MAX_LL, HC_MAX_ML, HC_MAX_OFF, HcOptPriceType};
use super::dfast::DfastMatchGenerator;
use super::fastpath::EncodeTier;
#[cfg(test)]
use super::hc::HC_MIN_MATCH_LEN;
#[cfg(test)]
use super::match_table::storage::HC3_HASH_LOG;
use super::sequences::{ExternalSequence, SequenceProducer};
#[cfg(test)]
use super::match_table::helpers::INCOMPRESSIBLE_SKIP_STEP;
use super::match_table::helpers::MIN_MATCH_LEN;
#[cfg(test)]
use super::match_table::helpers::common_prefix_len;
#[cfg(test)]
use super::opt::ldm::HcRawSeq;
#[cfg(test)]
use super::opt::types::{HcCandidateQuery, MatchCandidate};
use super::row::RowMatchGenerator;
use super::simple::fast_matcher::{
FAST_LEVEL_1_HASH_LOG, FAST_LEVEL_1_MLS, FastKernelMatcher, TableCarry,
};
use super::workspace::HistoryBuf;
pub(crate) const DFAST_MIN_MATCH_LEN: usize = 5;
pub(crate) const DFAST_SHORT_HASH_LOOKAHEAD: usize = 5;
pub(crate) const ROW_MIN_MATCH_LEN: usize = 5;
pub(crate) const DFAST_HASH_BITS: usize = 17;
pub(crate) const DFAST_SHORT_HASH_BITS_DELTA: usize = 1;
pub(crate) const DFAST_EMPTY_SLOT: u32 = 0;
pub(crate) const DFAST_REBASE_GUARD_BAND: u32 = 1u32 << 30;
pub(crate) const DFAST_SKIP_SEARCH_STRENGTH: usize = 8;
pub(crate) const DFAST_SKIP_STEP_GROWTH_INTERVAL: usize = 1 << DFAST_SKIP_SEARCH_STRENGTH;
pub(crate) const DFAST_INCOMPRESSIBLE_SKIP_STEP: usize =
crate::encoding::incompressible::RAW_SKIP_INDEX_STEP;
pub(crate) const ROW_HASH_BITS: usize = 20;
pub(crate) const ROW_LOG: usize = 5;
pub(crate) const ROW_SEARCH_DEPTH: usize = 16;
pub(crate) const ROW_TARGET_LEN: usize = 48;
pub(crate) const ROW_TAG_BITS: usize = 8;
pub(crate) const ROW_EMPTY_SLOT: u32 = u32::MAX;
pub(crate) const ROW_HASH_KEY_LEN: usize = 4;
#[cfg(test)]
use super::match_table::storage::{HC_PRIME3BYTES, HC_PRIME4BYTES};
#[cfg(test)]
use super::match_table::storage::HC_EMPTY;
pub(crate) const HC_SEARCH_DEPTH: usize = 16;
pub(crate) const HC_OPT_MIN_MATCH_LEN: usize = HC_FORMAT_MINMATCH;
pub(crate) const HC_TARGET_LEN: usize = 48;
use super::levels::config::*;
use super::hc::generator::HcMatchGenerator;
mod dict_prime;
#[cfg(feature = "ldm")]
mod long_distance;
#[derive(Clone)]
enum MatcherStorage {
Simple(FastKernelMatcher),
Dfast(DfastMatchGenerator),
Row(RowMatchGenerator),
HashChain(HcMatchGenerator),
}
impl MatcherStorage {
fn heap_size(&self) -> usize {
match self {
Self::Simple(m) => m.heap_size(),
Self::Dfast(m) => m.heap_size(),
Self::Row(m) => m.heap_size(),
Self::HashChain(m) => m.heap_size(),
}
}
#[cfg(test)]
fn ingest_capacity(&self) -> usize {
match self {
Self::Simple(m) => m.history_capacity(),
Self::Dfast(m) => m.history.capacity(),
Self::Row(m) => m.history.capacity(),
Self::HashChain(m) => m.table.history.capacity(),
}
}
fn set_dictionary_index_floor(&mut self, floor: usize) {
match self {
Self::Simple(m) => m.set_dictionary_index_floor(floor),
Self::Dfast(m) => m.set_dictionary_index_floor(floor),
Self::Row(m) => m.set_dictionary_index_floor(floor),
Self::HashChain(m) => m.table.set_dictionary_index_floor(floor),
}
}
#[cfg(feature = "ldm")]
fn committed_window(&self) -> &[u8] {
match self {
Self::Simple(m) => m.committed_window(),
Self::Dfast(m) => m.live_history(),
Self::Row(m) => m.live_history(),
Self::HashChain(m) => m.table.live_history(),
}
}
#[cfg(feature = "ldm")]
fn borrowed_window(&self) -> Option<&[u8]> {
match self {
Self::Simple(m) => m.borrowed_window(),
Self::Dfast(_) | Self::Row(_) | Self::HashChain(_) => None,
}
}
#[cfg(feature = "ldm")]
fn window_with_pending(&self) -> &[u8] {
match self {
Self::Simple(m) => m.window_with_pending(),
Self::Dfast(m) => m.window_with_pending(),
Self::Row(m) => m.window_with_pending(),
Self::HashChain(m) => m.table.window_with_pending(),
}
}
#[cfg(feature = "ldm")]
fn last_block_len(&self) -> usize {
match self {
Self::Simple(m) => m.last_committed_space().len(),
Self::Dfast(m) => m.get_last_space().len(),
Self::Row(m) => m.get_last_space().len(),
Self::HashChain(m) => m.table.get_last_space().len(),
}
}
#[cfg(feature = "ldm")]
fn uncommit_block(&mut self, len: usize) {
match self {
Self::Simple(m) => m.uncommit_block(len),
Self::Dfast(m) => m.uncommit_block(len),
Self::Row(m) => m.uncommit_block(len),
Self::HashChain(m) => m.table.uncommit_block(len),
}
}
#[cfg(feature = "ldm")]
fn commit_piece(&mut self, len: usize) {
match self {
Self::Simple(m) => {
let evicted = m.commit_block(len);
debug_assert_eq!(evicted, 0, "a recommitted piece evicted window bytes");
}
Self::Dfast(m) => m.commit_block(len),
Self::Row(m) => m.commit_block(len),
Self::HashChain(m) => m.table.commit_block(len),
}
}
#[cfg(feature = "ldm")]
fn skip_long_match(&mut self) {
match self {
Self::Simple(m) => m.skip_long_match(),
Self::Dfast(m) => m.skip_long_match(),
Self::Row(m) => m.skip_long_match(),
Self::HashChain(m) => m.table.skip_long_match(),
}
}
#[cfg(feature = "ldm")]
fn note_long_match(&mut self, offset: usize, literal_len: usize) {
match self {
Self::Simple(m) => m.note_long_match(offset, literal_len),
Self::Dfast(m) => m.note_long_match(offset, literal_len),
Self::Row(m) => m.note_long_match(offset, literal_len),
Self::HashChain(m) => m.table.note_long_match(offset, literal_len),
}
}
fn backend(&self) -> super::strategy::BackendTag {
use super::strategy::BackendTag;
match self {
Self::Simple(_) => BackendTag::Simple,
Self::Dfast(_) => BackendTag::Dfast,
Self::Row(_) => BackendTag::Row,
Self::HashChain(_) => BackendTag::HashChain,
}
}
}
pub struct MatchGeneratorDriver {
storage: MatcherStorage,
strategy_tag: super::strategy::StrategyTag,
search: super::strategy::SearchMethod,
pub(crate) parse: super::strategy::ParseMode,
#[cfg(test)]
config_override: Option<(super::strategy::SearchMethod, super::strategy::ParseMode)>,
param_overrides: Option<super::parameters::ParamOverrides>,
slice_size: usize,
base_slice_size: usize,
reported_window_size: usize,
dictionary_retained_budget: usize,
source_size_hint: Option<u64>,
dictionary_size_hint: Option<super::DictionarySizes>,
reset_size_log: Option<u8>,
reset_dict_attach_ok: bool,
reset_shape: Option<(LevelParams, usize, bool, bool)>,
dict_reach_log: Option<u32>,
borrowed_pending: Option<(usize, usize)>,
primed: Option<(MatcherStorage, usize, PrimedKey)>,
primed_spare: Option<MatcherStorage>,
own_workspace: crate::encoding::workspace::Workspace,
frame_in_place: bool,
#[cfg(feature = "ldm")]
ldm: Option<super::ldm::LdmProducer>,
#[cfg(feature = "ldm")]
ldm_store: super::ldm::store::LdmSequenceStore,
#[cfg(feature = "ldm")]
ldm_found: Vec<super::opt::ldm::HcRawSeq>,
#[cfg(feature = "ldm")]
ldm_block: Vec<super::opt::ldm::HcRawSeq>,
#[cfg(feature = "ldm")]
ldm_committed_end: usize,
#[cfg(feature = "ldm")]
ldm_dict_end: usize,
#[cfg(feature = "ldm")]
ldm_split_block: Option<usize>,
producer: Option<ProducerSlot>,
tier: super::fastpath::FastpathKernel,
}
struct ProducerSlot {
producer: alloc::boxed::Box<dyn SequenceProducer>,
fallback: bool,
failed: bool,
parse: Vec<ExternalSequence>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
struct PrimedKey {
level: super::CompressionLevel,
params: LevelParams,
table_bits: usize,
fast_attach: bool,
fast_tagged: bool,
}
fn hc_attaches_dictionary(hc: &HcMatchGenerator, size_log: Option<u8>) -> bool {
let cutoff = match hc.strategy_tag {
super::strategy::StrategyTag::BtUltra | super::strategy::StrategyTag::BtUltra2 => {
BT_ULTRA_ATTACH_DICT_CUTOFF_LOG
}
_ => BT_OPT_ATTACH_DICT_CUTOFF_LOG,
};
size_log.is_none_or(|log| log <= cutoff)
}
pub(crate) fn history_ceiling(
backend: super::strategy::BackendTag,
window: usize,
dict_len: usize,
block: usize,
) -> usize {
use super::match_table::storage::MAX_PRIMED_WINDOW_SIZE;
let window = window
.checked_add(dict_len)
.map_or(MAX_PRIMED_WINDOW_SIZE, |w| w.min(MAX_PRIMED_WINDOW_SIZE));
let slack = match backend {
super::strategy::BackendTag::Simple => window,
_ => window >> 2,
};
window + slack + block
}
fn frame_history_bytes(
backend: super::strategy::BackendTag,
workspace: &crate::encoding::workspace::Workspace,
in_place: bool,
hint: Option<u64>,
dict_len: usize,
max_window_size: usize,
level: CompressionLevel,
) -> usize {
use crate::encoding::workspace::IngestPlan;
let block = workspace.block_for_window(max_window_size);
let ceiling = history_ceiling(backend, max_window_size, dict_len, block);
let bytes = hint.map(|bytes| usize::try_from(bytes).unwrap_or(usize::MAX));
let (input, read_slack) = match workspace.ingest() {
IngestPlan::Raw => return 0,
IngestPlan::Slice(_) if in_place => return dict_len.min(ceiling),
IngestPlan::Slice(len) | IngestPlan::PledgedStream(len) => (Some(len), 0),
IngestPlan::Stream => (
bytes.map(|bytes| {
let level_window_log =
crate::encoding::levels::config::resolve_level_params(level, hint).window_log;
bytes.min(1usize << level_window_log)
}),
block,
),
};
match input {
Some(bytes) if bytes < max_window_size => dict_len
.checked_add(bytes + read_slack)
.map_or(ceiling, |sum| sum.min(ceiling)),
_ => ceiling,
}
}
impl MatchGeneratorDriver {
#[cfg(test)]
pub(crate) fn ingest_capacity(&self) -> usize {
self.storage.ingest_capacity()
}
#[cfg(test)]
pub(crate) fn owned_table_and_history_bytes(&self) -> (usize, usize) {
match &self.storage {
MatcherStorage::Simple(_) => panic!("asked of a window backend only"),
MatcherStorage::Dfast(m) => (m.tables.owned_bytes(), m.history.owned_bytes()),
MatcherStorage::Row(m) => (m.tables.owned_bytes(), m.history.owned_bytes()),
MatcherStorage::HashChain(m) => {
(m.table.tables.owned_bytes(), m.table.history.owned_bytes())
}
}
}
#[cfg(any(test, feature = "bench-internals"))]
pub(crate) fn commit_input(&mut self, input: &[u8]) {
self.fill_in_place(input.len(), &mut |history| {
history.extend_from_slice(input);
(input.len(), false)
});
self.commit_filled(input.len());
}
pub(crate) fn new(slice_size: usize, max_slices_in_window: usize) -> Self {
assert!(
slice_size > 0,
"MatchGeneratorDriver::new requires slice_size > 0 (got 0)",
);
assert!(
max_slices_in_window > 0,
"MatchGeneratorDriver::new requires max_slices_in_window > 0 (got 0)",
);
let max_window_size = max_slices_in_window
.checked_mul(slice_size)
.expect("MatchGeneratorDriver::new: slice_size * max_slices_in_window overflows usize");
let next_pow2 = max_window_size.checked_next_power_of_two().expect(
"MatchGeneratorDriver::new: max_window_size too large for \
next_power_of_two without overflow",
);
let window_log_init = next_pow2.trailing_zeros() as u8;
Self {
storage: MatcherStorage::Simple(FastKernelMatcher::with_params_deferred(
window_log_init,
FAST_LEVEL_1_HASH_LOG,
FAST_LEVEL_1_MLS,
2, )),
strategy_tag: super::strategy::StrategyTag::Fast,
search: super::strategy::SearchMethod::Fast,
parse: super::strategy::ParseMode::Greedy,
#[cfg(test)]
config_override: None,
param_overrides: None,
slice_size,
base_slice_size: slice_size,
reported_window_size: next_pow2,
reset_size_log: None,
reset_dict_attach_ok: true,
reset_shape: None,
dict_reach_log: None,
dictionary_retained_budget: 0,
source_size_hint: None,
dictionary_size_hint: None,
borrowed_pending: None,
primed: None,
primed_spare: None,
own_workspace: crate::encoding::workspace::Workspace::new(),
frame_in_place: false,
#[cfg(feature = "ldm")]
ldm: None,
#[cfg(feature = "ldm")]
ldm_store: Default::default(),
#[cfg(feature = "ldm")]
ldm_found: Vec::new(),
#[cfg(feature = "ldm")]
ldm_block: Vec::new(),
#[cfg(feature = "ldm")]
ldm_committed_end: 0,
#[cfg(feature = "ldm")]
ldm_dict_end: 0,
#[cfg(feature = "ldm")]
ldm_split_block: None,
producer: None,
tier: super::fastpath::select_kernel(),
}
}
pub(crate) fn set_sequence_producer(
&mut self,
producer: Option<alloc::boxed::Box<dyn SequenceProducer>>,
fallback: bool,
) {
let parse = self
.producer
.take()
.map(|slot| slot.parse)
.unwrap_or_default();
self.producer = producer.map(|producer| ProducerSlot {
producer,
fallback,
failed: false,
parse,
});
}
pub(crate) fn take_producer_failure(&mut self) -> bool {
self.producer
.as_mut()
.is_some_and(|slot| core::mem::take(&mut slot.failed))
}
fn match_produced<K: EncodeTier>(
&mut self,
handle_sequence: &mut impl FnMut(Sequence),
) -> bool {
let mut slot = self
.producer
.take()
.expect("called with a producer installed");
slot.parse.clear();
let window = self.window_size();
let block = self.get_last_space();
let block_len = block.len();
let produced = slot
.producer
.produce(block, window, &mut slot.parse)
.is_ok()
&& crate::encoding::sequences::validate_parse(
&slot.parse,
block_len,
window,
0,
true,
block_len,
)
.is_ok()
&& crate::encoding::sequences::parse_reproduces(&slot.parse, block, &[]).is_ok();
if !produced && slot.fallback {
self.producer = Some(slot);
return false;
}
self.skip_backend::<K>(None);
if produced {
let mut literals = 0usize;
for sequence in &slot.parse {
literals += sequence.lit_len as usize;
if sequence.match_len == 0 {
continue;
}
handle_sequence(Sequence::Triple {
literal_len: literals,
offset: sequence.offset as usize,
match_len: sequence.match_len as usize,
});
literals = 0;
}
if literals > 0 {
handle_sequence(Sequence::Literals { len: literals });
}
} else {
slot.failed = true;
if block_len > 0 {
handle_sequence(Sequence::Literals { len: block_len });
}
}
self.producer = Some(slot);
true
}
fn level_params(level: CompressionLevel, source_size: Option<u64>) -> LevelParams {
resolve_level_params(level, source_size)
}
pub(crate) fn set_param_overrides(
&mut self,
overrides: Option<super::parameters::ParamOverrides>,
) {
self.param_overrides = overrides;
}
pub(crate) fn active_backend(&self) -> super::strategy::BackendTag {
self.storage.backend()
}
pub(crate) fn borrowed_supported(&self) -> bool {
use super::strategy::BackendTag;
if self.producer.is_some() {
return false;
}
match self.active_backend() {
BackendTag::Simple | BackendTag::Dfast | BackendTag::Row => true,
BackendTag::HashChain => false,
}
}
pub(crate) fn frame_scans_in_place(&self) -> bool {
self.frame_in_place
}
pub(crate) fn borrowed_frame_is_independent(&self) -> bool {
match &self.storage {
MatcherStorage::Dfast(d) => !d.tables_hold_earlier_frames,
_ => true,
}
}
pub(crate) fn forget_earlier_frames(&mut self) {
if let MatcherStorage::Dfast(d) = &mut self.storage {
d.forget_earlier_frames();
}
}
pub(crate) fn borrowed_dict_supported(&self) -> bool {
self.producer.is_none()
&& !self.matches_beyond_window_tables()
&& matches!(
&self.storage,
MatcherStorage::Simple(m) if m.dict_is_attached()
)
}
fn simple_mut(&mut self) -> &mut FastKernelMatcher {
match &mut self.storage {
MatcherStorage::Simple(m) => m,
_ => panic!("simple backend must be initialized by reset() before use"),
}
}
pub(crate) unsafe fn set_borrowed_window(&mut self, buffer: &[u8]) {
match self.active_backend() {
super::strategy::BackendTag::Simple => unsafe {
self.simple_mut().set_borrowed_window(buffer)
},
super::strategy::BackendTag::Dfast => unsafe {
self.dfast_matcher_mut().set_borrowed_window(buffer)
},
super::strategy::BackendTag::Row => unsafe {
self.row_matcher_mut().set_borrowed_window(buffer)
},
super::strategy::BackendTag::HashChain => {
unreachable!("the optimal parsers have no borrowed scan")
}
}
}
pub(crate) fn clear_borrowed_window(&mut self) {
match self.active_backend() {
super::strategy::BackendTag::Simple => self.simple_mut().clear_borrowed_window(),
super::strategy::BackendTag::Dfast => self.dfast_matcher_mut().clear_borrowed_window(),
super::strategy::BackendTag::Row => self.row_matcher_mut().clear_borrowed_window(),
super::strategy::BackendTag::HashChain => {}
}
self.borrowed_pending = None;
}
pub(crate) fn set_borrowed_block(&mut self, block_start: usize, block_end: usize) {
assert!(
self.borrowed_supported(),
"borrowed block staging is not supported for the active backend/search config",
);
assert!(
block_start <= block_end,
"borrowed block range must satisfy start <= end (start={block_start} end={block_end})",
);
self.borrowed_pending = Some((block_start, block_end));
#[cfg(feature = "ldm")]
if self.ldm.is_some() {
debug_assert_eq!(self.ldm_committed_end, block_start);
self.ldm_committed_end = block_end;
self.ldm_split_block = None;
}
match self.active_backend() {
super::strategy::BackendTag::Simple => self
.simple_mut()
.stage_borrowed_block(block_start, block_end),
super::strategy::BackendTag::Dfast => self
.dfast_matcher_mut()
.stage_borrowed_block(block_start, block_end),
super::strategy::BackendTag::Row => self
.row_matcher_mut()
.stage_borrowed_block(block_start, block_end),
super::strategy::BackendTag::HashChain => {
unreachable!("`borrowed_supported` refuses the optimal parsers")
}
}
}
#[cfg(test)]
fn dfast_matcher(&self) -> &DfastMatchGenerator {
match &self.storage {
MatcherStorage::Dfast(m) => m,
_ => panic!("dfast backend must be initialized by reset() before use"),
}
}
#[cfg(test)]
pub(crate) fn dfast_dict_table_builds(&self) -> (usize, usize) {
self.dfast_matcher().dict_table_builds
}
#[cfg(test)]
pub(crate) fn row_dict_index_builds(&self) -> (usize, usize) {
self.row_matcher().dict_index_builds
}
fn dfast_matcher_mut(&mut self) -> &mut DfastMatchGenerator {
match &mut self.storage {
MatcherStorage::Dfast(m) => m,
_ => panic!("dfast backend must be initialized by reset() before use"),
}
}
#[cfg(test)]
pub(crate) fn row_matcher(&self) -> &RowMatchGenerator {
match &self.storage {
MatcherStorage::Row(m) => m,
_ => panic!("row backend must be initialized by reset() before use"),
}
}
pub(crate) fn row_matcher_mut(&mut self) -> &mut RowMatchGenerator {
match &mut self.storage {
MatcherStorage::Row(m) => m,
_ => panic!("row backend must be initialized by reset() before use"),
}
}
#[cfg(test)]
fn hc_matcher(&self) -> &HcMatchGenerator {
match &self.storage {
MatcherStorage::HashChain(m) => m,
_ => panic!("hash chain backend must be initialized by reset() before use"),
}
}
fn hc_matcher_mut(&mut self) -> &mut HcMatchGenerator {
match &mut self.storage {
MatcherStorage::HashChain(m) => m,
_ => panic!("hash chain backend must be initialized by reset() before use"),
}
}
#[must_use]
fn retire_dictionary_budget(&mut self, evicted_bytes: usize) -> bool {
let reclaimed = evicted_bytes.min(self.dictionary_retained_budget);
if reclaimed == 0 {
return false;
}
self.dictionary_retained_budget -= reclaimed;
match self.active_backend() {
super::strategy::BackendTag::Simple => {
let matcher = self.simple_mut();
matcher.max_window_size = matcher.max_window_size.saturating_sub(reclaimed);
}
super::strategy::BackendTag::Dfast => {
let matcher = self.dfast_matcher_mut();
matcher.max_window_size = matcher.max_window_size.saturating_sub(reclaimed);
}
super::strategy::BackendTag::Row => {
let matcher = self.row_matcher_mut();
matcher.max_window_size = matcher.max_window_size.saturating_sub(reclaimed);
}
super::strategy::BackendTag::HashChain => {
let matcher = self.hc_matcher_mut();
matcher.table.max_window_size =
matcher.table.max_window_size.saturating_sub(reclaimed);
}
}
true
}
fn trim_after_budget_retire(&mut self) {
loop {
let mut evicted_bytes = 0usize;
match self.active_backend() {
super::strategy::BackendTag::Simple => {
let MatcherStorage::Simple(m) = &mut self.storage else {
unreachable!("active_backend() == Simple proven above");
};
evicted_bytes += m.trim_to_window();
}
super::strategy::BackendTag::Dfast => {
let dfast = self.dfast_matcher_mut();
let pre = dfast.window_size;
dfast.trim_to_window();
evicted_bytes += pre - dfast.window_size;
}
super::strategy::BackendTag::Row => {
let row = self.row_matcher_mut();
let pre = row.window_size;
row.trim_to_window();
evicted_bytes += pre - row.window_size;
}
super::strategy::BackendTag::HashChain => {
let table = &mut self.hc_matcher_mut().table;
let pre = table.window_size;
table.trim_to_window();
evicted_bytes += pre - table.window_size;
}
}
if evicted_bytes == 0 {
break;
}
let _ = self.retire_dictionary_budget(evicted_bytes);
}
}
fn hc_dict_attach_mode(&self) -> bool {
let MatcherStorage::HashChain(hc) = &self.storage else {
return true;
};
hc_attaches_dictionary(hc, self.reset_size_log)
}
fn dfast_attaches_dictionary(&self, dict_len: usize) -> bool {
dict_len <= crate::encoding::dfast::DFAST_ATTACH_DICT_MAX_LEN
&& self
.reset_size_log
.is_none_or(|log| log <= DFAST_ATTACH_DICT_CUTOFF_LOG)
}
fn skip_matching_for_dictionary_priming<K: EncodeTier>(&mut self, dict_len: usize) {
match self.active_backend() {
super::strategy::BackendTag::Simple => {
let attach = self.reset_dict_attach_ok
&& self
.reset_size_log
.is_none_or(|log| log <= FAST_ATTACH_DICT_CUTOFF_LOG);
if attach {
self.simple_mut().skip_matching_for_dict_prime(dict_len);
} else {
self.simple_mut().skip_matching_for_dict_copy();
}
}
super::strategy::BackendTag::Dfast => {
if self.dfast_attaches_dictionary(dict_len) {
self.dfast_matcher_mut().skip_matching_for_dict_attach();
} else {
self.dfast_matcher_mut().park_dict_cache();
self.dfast_matcher_mut().skip_matching_dense();
}
}
super::strategy::BackendTag::Row => {
self.row_matcher_mut().prime_dictionary_current_block();
}
super::strategy::BackendTag::HashChain => {
if self.hc_dict_attach_mode() {
self.hc_matcher_mut().table.skip_matching_dict_bt();
} else {
self.hc_matcher_mut().skip_matching::<K>(Some(false));
}
}
}
}
}
impl Matcher for MatchGeneratorDriver {
fn supports_dictionary_priming(&self) -> bool {
true
}
fn set_source_size_hint(&mut self, size: u64) {
self.source_size_hint = Some(size);
}
fn set_dictionary_size_hint(&mut self, sizes: super::DictionarySizes) {
self.dictionary_size_hint = Some(sizes);
}
fn block_samples_match_dict(&self, block: &[u8]) -> bool {
match &self.storage {
MatcherStorage::Simple(m) => m.block_samples_match_dict(block),
_ => true,
}
}
fn heap_size(&self) -> usize {
let snapshot = self
.primed
.as_ref()
.map(|(storage, _, _)| storage)
.into_iter()
.chain(&self.primed_spare)
.map(MatcherStorage::heap_size)
.sum::<usize>();
#[cfg(feature = "ldm")]
let ldm = self.ldm.as_ref().map_or(0, |p| p.heap_size())
+ self.ldm_store.heap_size()
+ (self.ldm_found.capacity() + self.ldm_block.capacity())
* core::mem::size_of::<super::opt::ldm::HcRawSeq>();
#[cfg(not(feature = "ldm"))]
let ldm = 0;
let produced = self.producer.as_ref().map_or(0, |slot| {
slot.parse.capacity() * core::mem::size_of::<ExternalSequence>()
});
self.storage.heap_size() + snapshot + self.own_workspace.heap_bytes() + ldm + produced
}
fn clear_param_overrides(&mut self) {
self.param_overrides = None;
}
fn reset(&mut self, level: CompressionLevel) {
let mut own = core::mem::take(&mut self.own_workspace);
own.begin_layout(
crate::common::MAX_BLOCK_SIZE as usize,
crate::encoding::workspace::no_trailing,
crate::encoding::workspace::IngestPlan::Stream,
);
self.reset_in_workspace(level, &mut own);
own.release_retired();
self.own_workspace = own;
}
fn reset_in_workspace(
&mut self,
level: CompressionLevel,
workspace: &mut crate::encoding::workspace::Workspace,
) {
let hint = self.source_size_hint.take();
let dict_hint = self
.dictionary_size_hint
.take()
.filter(|sizes| sizes.content > 0);
if dict_hint.is_none() {
self.primed_spare = None;
}
self.reset_size_log = hint.map(source_size_ceil_log);
self.reset_dict_attach_ok =
dict_hint.is_none_or(|sizes| sizes.content <= MAX_FAST_ATTACH_DICT_REGION);
let hinted = hint.is_some();
let overrides = self.param_overrides.unwrap_or_default();
self.dict_reach_log = dict_hint.map(|sizes| {
let cd = crate::encoding::cparams::get_cdict_cparams(
crate::encoding::levels::config::numeric_level(level),
sizes.serialized,
&overrides,
);
(cd.hash_log + 3).max(cd.chain_log + 1).min(31)
});
let (params, dict_plan) = match dict_hint {
Some(sizes) => crate::encoding::levels::config::resolve_level_params_with_dict(
level, hint, sizes, &overrides,
),
None => (Self::level_params(level, hint), None),
};
#[cfg_attr(not(test), allow(unused_mut))]
let mut params = params;
#[cfg(test)]
if let Some((search, parse)) = self.config_override.take() {
params.search = search;
params.lazy_depth = parse.lazy_depth();
use super::strategy::SearchMethod;
match search {
SearchMethod::Fast => {
params.fast.get_or_insert(FAST_L1);
}
SearchMethod::DoubleFast => {
params.dfast.get_or_insert(DFAST_L3);
}
SearchMethod::RowHash | SearchMethod::BinaryTreeLazy => {
let row = params.row.get_or_insert(ROW_CONFIG);
row.bt = matches!(search, SearchMethod::BinaryTreeLazy);
}
SearchMethod::BinaryTree => {
params.hc.get_or_insert(HC_CONFIG);
}
}
}
if let Some(ov) = self.param_overrides {
crate::encoding::levels::config::apply_frame_overrides(
&mut params,
&ov,
dict_hint.is_some(),
hint,
);
}
#[cfg(feature = "ldm")]
let frame_ldm = self
.param_overrides
.and_then(|ov| ov.ldm)
.map(|ldm_ov| crate::encoding::levels::config::frame_ldm_params(¶ms, &ldm_ov));
let next_backend = params.backend();
let max_window_size = 1usize << params.window_log;
self.dictionary_retained_budget = 0;
self.borrowed_pending = None;
if self.active_backend() != next_backend {
match &mut self.storage {
MatcherStorage::Simple(_m) => {
}
MatcherStorage::Dfast(m) => {
m.tables = crate::encoding::workspace::Table::empty();
m.reset();
}
MatcherStorage::Row(m) => {
m.release_tables();
m.reset();
}
MatcherStorage::HashChain(m) => {
m.table.tables = crate::encoding::workspace::Table::empty();
m.table.chain_off = 0;
m.table.hash3_off = 0;
m.reset();
}
}
self.storage = match next_backend {
super::strategy::BackendTag::Simple => {
let fast = params.fast.expect("Fast level row carries a FastConfig");
MatcherStorage::Simple(FastKernelMatcher::with_params_deferred(
params.window_log,
fast.hash_log,
fast.mls,
fast.step_size,
))
}
super::strategy::BackendTag::Dfast => {
MatcherStorage::Dfast(DfastMatchGenerator::new(max_window_size))
}
super::strategy::BackendTag::Row => {
MatcherStorage::Row(RowMatchGenerator::new(max_window_size))
}
super::strategy::BackendTag::HashChain => {
MatcherStorage::HashChain(HcMatchGenerator::new(max_window_size))
}
};
}
self.strategy_tag = params.strategy_tag;
self.search = params.search;
self.parse = params.parse();
self.slice_size = self.base_slice_size.min(max_window_size);
self.reported_window_size = max_window_size;
let strategy_tag = self.strategy_tag;
let table_window_size = match hint {
Some(h) => {
let raw_log = source_size_ceil_log(h);
let shift = raw_log.max(MIN_WINDOW_LOG).min(usize::BITS as u8 - 1);
(1usize << shift).min(max_window_size)
}
None => max_window_size,
};
let fast_attach = matches!(next_backend, super::strategy::BackendTag::Simple)
&& self.reset_dict_attach_ok
&& self
.reset_size_log
.is_none_or(|log| log <= FAST_ATTACH_DICT_CUTOFF_LOG);
let dict_len = dict_hint.map_or(0, |sizes| sizes.content);
#[cfg(feature = "ldm")]
let long_distance = self.param_overrides.is_some_and(|ov| ov.ldm.is_some());
#[cfg(not(feature = "ldm"))]
let long_distance = false;
let long_distance_in_place =
matches!(next_backend, super::strategy::BackendTag::Simple) && dict_hint.is_none();
self.frame_in_place = match workspace.ingest() {
crate::encoding::workspace::IngestPlan::Slice(len) => {
(dict_hint.is_none() || fast_attach)
&& (!long_distance || long_distance_in_place)
&& self.borrowed_supported()
&& len
.checked_add(dict_len)
.is_some_and(|len| len <= u32::MAX as usize)
}
_ => false,
};
let history_bytes = frame_history_bytes(
next_backend,
workspace,
self.frame_in_place,
hint,
dict_hint.map_or(0, |sizes| sizes.content),
max_window_size,
level,
);
let expected_input = hint.map_or(history_bytes, |bytes| {
usize::try_from(bytes).unwrap_or(usize::MAX)
});
let mut resolved_table_bits: usize = 0;
let dfast_attach_next =
dict_hint.is_some_and(|sizes| self.dfast_attaches_dictionary(sizes.content));
match &mut self.storage {
MatcherStorage::Simple(m) => {
let fast = params.fast.expect("Fast level row carries a FastConfig");
let dict_attach_epoch = dict_hint.is_some()
&& self.reset_dict_attach_ok
&& self
.reset_size_log
.is_none_or(|log| log <= FAST_ATTACH_DICT_CUTOFF_LOG);
let restored_slot_format = self
.primed
.as_ref()
.filter(|(_, _, captured)| {
dict_hint.is_some()
&& restores_primed_snapshot(
self.reset_size_log,
FAST_ATTACH_DICT_CUTOFF_LOG,
)
&& *captured
== PrimedKey {
level,
params,
table_bits: 0,
fast_attach: false,
fast_tagged: captured.fast_tagged,
}
})
.map(|(_, _, captured)| captured.fast_tagged);
let carry = if let Some(tagged) = restored_slot_format {
TableCarry::OverwrittenByRestore { tagged }
} else if dict_attach_epoch {
TableCarry::AdvanceEpoch
} else {
TableCarry::Clear
};
let hash_log = if dict_hint.is_some() {
fast.hash_log
} else {
fast.hash_log.min(params.window_log as u32 + 1)
};
m.set_dict_table_hash_log(dict_hint.map(|sizes| {
crate::encoding::cparams::get_cdict_cparams(
crate::encoding::levels::config::numeric_level(level),
sizes.serialized,
&overrides,
)
.hash_log
}));
let tables =
crate::encoding::simple::fast_kernel::hash_table::FastHashTable::workspace_bytes(
hash_log,
);
m.retire_history();
workspace.open_for_match_finder(
tables + m.history_workspace_bytes(history_bytes),
tables,
max_window_size,
expected_input,
);
m.bind_history(workspace, history_bytes);
m.reset(
params.window_log,
hash_log,
fast.mls,
fast.step_size,
expected_input,
dict_hint.map_or(0, |sizes| sizes.content),
carry,
workspace,
);
}
MatcherStorage::Dfast(dfast) => {
dfast.max_window_size = max_window_size;
let dcfg = params
.dfast
.expect("Dfast level row must carry a DfastConfig");
let long_bits = if hinted {
dfast_hash_bits_for_window(table_window_size).min(dcfg.long_hash_log as usize)
} else {
dcfg.long_hash_log as usize
};
let short_bits = if hinted {
dfast_hash_bits_for_window(table_window_size).min(dcfg.short_hash_log as usize)
} else {
dcfg.short_hash_log as usize
};
resolved_table_bits = long_bits;
dfast.set_hash_bits(long_bits, short_bits);
dfast.set_dict_table_bits(dict_hint.map(|sizes| {
let cd = crate::encoding::cparams::get_cdict_cparams(
crate::encoding::levels::config::numeric_level(level),
sizes.serialized,
&overrides,
);
(cd.hash_log as usize, cd.chain_log as usize)
}));
if dict_hint.is_some() && !dfast_attach_next {
dfast.park_dict_cache();
}
let tables = dfast.tables_workspace_bytes();
dfast.retire_history();
workspace.open_for_match_finder(
tables + dfast.history.workspace_bytes(history_bytes),
tables,
max_window_size,
expected_input,
);
dfast.history.bind(workspace, history_bytes);
let dict_len = dict_hint.map_or(0, |sizes| sizes.content);
let in_place_fits = !self.frame_in_place
|| matches!(
workspace.ingest(),
crate::encoding::workspace::IngestPlan::Slice(len)
if len <= crate::encoding::dfast::DFAST_TAGGED_MAX_REL
);
let tagged = !dfast_attach_next
&& in_place_fits
&& max_window_size.checked_add(dict_len).is_some_and(|window| {
(crate::encoding::dfast::DFAST_TAGGED_WINDOW_FLOOR
..=crate::encoding::dfast::DFAST_TAGGED_WINDOW_LIMIT)
.contains(&window)
});
dfast.bind_tables(workspace, tagged);
dfast.reset();
}
MatcherStorage::Row(row) => {
row.max_window_size = max_window_size;
row.lazy_depth = params.lazy_depth;
row.set_dict_plan(dict_plan);
let mut row_cfg = params.row.expect("Row level row carries a RowConfig");
if hinted && dict_plan.is_none() {
row_cfg.hash_bits = row_cfg
.hash_bits
.min(row_hash_bits_for_window(table_window_size));
}
row.configure(row_cfg);
resolved_table_bits = row.hash_bits();
row.retire_history();
workspace.open_for_match_finder(
row.tables_workspace_bytes() + row.history.workspace_bytes(history_bytes),
row.zero_table_bytes(),
max_window_size,
expected_input,
);
row.history.bind(workspace, history_bytes);
row.bind_tables(workspace);
row.reset();
}
MatcherStorage::HashChain(hc) => {
hc.table.max_window_size = max_window_size;
let mut hc_cfg = params.hc.expect("HashChain level row carries an HcConfig");
if hinted && dict_hint.is_none() {
let wlog = hc_hash_bits_for_window(table_window_size);
hc_cfg.hash_log = hc_cfg.hash_log.min(wlog + 1);
hc_cfg.chain_log = hc_cfg.chain_log.min(wlog + 1);
}
hc.configure(hc_cfg, strategy_tag, params.window_log);
if dict_hint.is_some() && !hc_attaches_dictionary(hc, self.reset_size_log) {
hc.table.dms.invalidate();
}
let tables = hc.table.tables_workspace_bytes();
hc.table.retire_history();
workspace.open_for_match_finder(
tables + hc.table.history.workspace_bytes(history_bytes),
tables,
max_window_size,
expected_input,
);
hc.table.history.bind(workspace, history_bytes);
hc.table.bind_tables(workspace);
hc.reset();
}
}
#[cfg(feature = "ldm")]
{
self.ldm = frame_ldm.map(|p| match self.ldm.take() {
Some(mut existing) if existing.params() == p => {
existing.clear();
existing
}
_ => super::ldm::LdmProducer::new(p),
});
self.ldm_committed_end = self.storage.committed_window().len();
self.ldm_dict_end = self.ldm_committed_end;
self.ldm_split_block = None;
}
let fast_tagged = matches!(&self.storage, MatcherStorage::Simple(m) if m.slots_tagged());
self.reset_shape = Some((params, resolved_table_bits, fast_attach, fast_tagged));
self.own_workspace = crate::encoding::workspace::Workspace::new();
}
#[inline]
fn dictionary_is_resident(&self) -> bool {
self.dictionary_is_resident_impl()
}
#[inline]
fn reapply_resident_dictionary(&mut self, offset_hist: [u32; 3]) {
self.reapply_resident_dictionary_impl(offset_hist)
}
#[inline]
fn prime_with_dictionary(&mut self, dict_content: &[u8], offset_hist: [u32; 3]) {
crate::encoding::fastpath::with_encode_tier!(
self.tier,
K => self.prime_with_dictionary_with::<K>(dict_content, offset_hist)
)
}
#[inline]
fn restore_primed_dictionary(&mut self, level: super::CompressionLevel) -> bool {
self.restore_primed_dictionary_impl(level)
}
#[inline]
fn capture_primed_dictionary(&mut self, level: super::CompressionLevel) {
self.capture_primed_dictionary_impl(level)
}
#[inline]
fn invalidate_primed_dictionary(&mut self) {
self.invalidate_primed_dictionary_impl()
}
#[inline]
fn seed_dictionary_entropy(
&mut self,
huff: Option<&crate::huff0::huff0_encoder::HuffmanTable>,
ll: Option<&crate::fse::fse_encoder::FSETable>,
ml: Option<&crate::fse::fse_encoder::FSETable>,
of: Option<&crate::fse::fse_encoder::FSETable>,
) {
self.seed_dictionary_entropy_impl(huff, ll, ml, of)
}
fn leave_workspace(&mut self) {
match &mut self.storage {
MatcherStorage::Simple(m) => m.leave_workspace(),
MatcherStorage::Dfast(m) => {
m.tables.leave_workspace();
m.history.leave_workspace();
}
MatcherStorage::Row(m) => {
m.tables.leave_workspace();
m.history.leave_workspace();
}
MatcherStorage::HashChain(m) => {
m.table.tables.leave_workspace();
m.table.history.leave_workspace();
}
}
}
fn window_size(&self) -> u64 {
self.reported_window_size as u64
}
fn matches_beyond_window_tables(&self) -> bool {
#[cfg(feature = "ldm")]
return self.ldm.is_some();
#[cfg(not(feature = "ldm"))]
false
}
fn settle_block_cut(&mut self, window: usize, cut: usize) -> usize {
crate::encoding::fastpath::with_encode_tier!(
self.tier,
K => self.settle_block_cut_with::<K>(window, cut)
)
}
fn get_last_space(&mut self) -> &[u8] {
#[cfg(feature = "ldm")]
if let Some(len) = self.ldm_split_block {
let window = self.storage.committed_window();
return &window[window.len() - len..];
}
match &self.storage {
MatcherStorage::Simple(m) => m.last_committed_space(),
MatcherStorage::Dfast(m) => m.get_last_space(),
MatcherStorage::Row(m) => m.get_last_space(),
MatcherStorage::HashChain(m) => m.table.get_last_space(),
}
}
fn fill_in_place(
&mut self,
capacity: usize,
fill: &mut dyn FnMut(&mut HistoryBuf) -> (usize, bool),
) -> (usize, bool) {
match &mut self.storage {
MatcherStorage::Simple(m) => m.fill_uncommitted(capacity, fill),
MatcherStorage::Dfast(m) => m.fill_uncommitted(capacity, fill),
MatcherStorage::Row(m) => m.fill_uncommitted(capacity, fill),
MatcherStorage::HashChain(m) => m.table.fill_uncommitted(capacity, fill),
}
}
fn uncommitted_input(&self) -> &[u8] {
match &self.storage {
MatcherStorage::Simple(m) => m.uncommitted(),
MatcherStorage::Dfast(m) => m.uncommitted(),
MatcherStorage::Row(m) => m.uncommitted(),
MatcherStorage::HashChain(m) => m.table.uncommitted(),
}
}
fn commit_filled(&mut self, len: usize) {
#[cfg(feature = "ldm")]
{
self.ldm_committed_end += len;
self.ldm_split_block = None;
}
let evicted_bytes = match &mut self.storage {
MatcherStorage::Simple(m) => m.commit_block(len),
MatcherStorage::Dfast(m) => {
let pre = m.window_size;
m.commit_block(len);
pre + len - m.window_size
}
MatcherStorage::Row(m) => {
let pre = m.window_size;
m.commit_block(len);
pre + len - m.window_size
}
MatcherStorage::HashChain(m) => {
let pre = m.table.window_size;
m.table.commit_block(len);
pre + len - m.table.window_size
}
};
if self.retire_dictionary_budget(evicted_bytes) {
self.trim_after_budget_retire();
}
}
fn start_matching(&mut self, mut handle_sequence: impl FnMut(Sequence)) {
let sink: &mut dyn FnMut(Sequence) = &mut handle_sequence;
crate::encoding::fastpath::with_encode_tier!(
self.tier,
K => self.start_matching_with::<K>(&mut *sink)
)
}
#[inline]
fn builtin_driver(&mut self) -> Option<&mut MatchGeneratorDriver> {
Some(self)
}
fn skip_matching(&mut self) {
self.skip_matching_with_hint(None);
}
fn skip_matching_with_hint(&mut self, incompressible_hint: Option<bool>) {
crate::encoding::fastpath::with_encode_tier!(
self.tier,
K => self.skip_matching_with_hint_with::<K>(incompressible_hint)
)
}
}
impl MatchGeneratorDriver {
pub(crate) fn skip_matching_with_hint_with<K: EncodeTier>(
&mut self,
incompressible_hint: Option<bool>,
) {
if let Some((block_start, block_end)) = self.borrowed_pending.take() {
#[cfg(feature = "ldm")]
if self.ldm.is_some() {
self.take_long_distance_block::<K>();
}
match self.active_backend() {
super::strategy::BackendTag::Simple => self.simple_mut().skip_matching_borrowed(
block_start,
block_end,
incompressible_hint,
),
super::strategy::BackendTag::Dfast => self
.dfast_matcher_mut()
.skip_matching_borrowed(block_start, block_end, incompressible_hint),
super::strategy::BackendTag::Row => self.row_matcher_mut().skip_matching_borrowed(
block_start,
block_end,
incompressible_hint,
),
super::strategy::BackendTag::HashChain => {
unreachable!("`borrowed_supported` refuses the optimal parsers")
}
}
return;
}
#[cfg(feature = "ldm")]
if self.ldm.is_some() {
self.take_long_distance_block::<K>();
}
self.skip_backend::<K>(incompressible_hint);
}
}
impl MatchGeneratorDriver {
fn skip_backend<K: EncodeTier>(&mut self, incompressible_hint: Option<bool>) {
match self.active_backend() {
super::strategy::BackendTag::Simple => {
self.simple_mut()
.skip_matching_with_hint(incompressible_hint);
}
super::strategy::BackendTag::Dfast => {
self.dfast_matcher_mut().skip_matching(incompressible_hint)
}
super::strategy::BackendTag::Row => self
.row_matcher_mut()
.skip_matching_with_hint(incompressible_hint),
super::strategy::BackendTag::HashChain => self
.hc_matcher_mut()
.skip_matching::<K>(incompressible_hint),
}
}
}
fn owned_sink<'a, F: FnMut(Sequence)>(
handle_sequence: &'a mut F,
gap_literals: &'a mut Option<usize>,
) -> impl FnMut(Sequence) + 'a {
move |sequence| match sequence {
Sequence::Triple {
literal_len,
offset,
match_len,
} => handle_sequence(Sequence::Triple {
literal_len,
offset,
match_len,
}),
Sequence::Literals { len } => match gap_literals {
Some(gap) => *gap = len,
None => handle_sequence(Sequence::Literals { len }),
},
}
}
impl MatchGeneratorDriver {
pub(crate) fn start_matching_with<K: EncodeTier>(
&mut self,
mut handle_sequence: impl FnMut(Sequence),
) {
if let Some((block_start, block_end)) = self.borrowed_pending.take() {
#[cfg(feature = "ldm")]
if self.ldm.is_some()
&& self.start_matching_long_distance_borrowed::<K>(
block_start,
block_end,
&mut handle_sequence,
)
{
return;
}
let mut forward = None;
let mut sink = owned_sink(&mut handle_sequence, &mut forward);
match self.active_backend() {
super::strategy::BackendTag::Simple => {
let m = self.simple_mut();
if m.dict_is_attached() {
m.start_matching_borrowed_dict::<K>(block_start, block_end, &mut sink);
} else {
m.start_matching_borrowed(block_start, block_end, &mut sink);
}
}
super::strategy::BackendTag::Dfast => self
.dfast_matcher_mut()
.start_matching_borrowed::<K>(block_start, block_end, &mut sink),
super::strategy::BackendTag::Row => {
let greedy = self.parse == super::strategy::ParseMode::Greedy;
self.row_matcher_mut().start_matching_borrowed::<K>(
block_start,
block_end,
greedy,
&mut sink,
);
}
super::strategy::BackendTag::HashChain => {
unreachable!("`borrowed_supported` refuses the optimal parsers")
}
}
return;
}
if self.producer.is_some() {
#[cfg(feature = "ldm")]
if self.ldm.is_some() {
if matches!(self.search, super::strategy::SearchMethod::BinaryTree) {
let MatcherStorage::HashChain(hc) = &mut self.storage else {
unreachable!("the optimal parser runs on the hash-chain backend");
};
hc.bt.ldm_sequences.clear();
}
if self.start_matching_long_distance::<K>(&mut handle_sequence, true) {
return;
}
if self.match_produced::<K>(&mut handle_sequence) {
return;
}
let mut gap_literals = None;
self.start_matching_owned::<K>(&mut owned_sink(
&mut handle_sequence,
&mut gap_literals,
));
return;
}
if self.match_produced::<K>(&mut handle_sequence) {
return;
}
}
#[cfg(feature = "ldm")]
if self.ldm.is_some() {
if matches!(self.search, super::strategy::SearchMethod::BinaryTree) {
self.take_long_distance_block::<K>();
let MatcherStorage::HashChain(hc) = &mut self.storage else {
unreachable!("the optimal parser runs on the hash-chain backend");
};
core::mem::swap(&mut self.ldm_block, &mut hc.bt.ldm_sequences);
} else if self.start_matching_long_distance::<K>(&mut handle_sequence, false) {
return;
}
}
let mut gap_literals = None;
self.start_matching_owned::<K>(&mut owned_sink(&mut handle_sequence, &mut gap_literals));
}
pub(crate) fn settle_block_cut_with<K: EncodeTier>(
&mut self,
window: usize,
cut: usize,
) -> usize {
#[cfg(feature = "ldm")]
return self.settle_long_distance_cut::<K>(window, cut);
#[cfg(not(feature = "ldm"))]
{
let _ = window;
cut
}
}
fn start_matching_owned<K: EncodeTier>(&mut self, handle_sequence: &mut impl FnMut(Sequence)) {
use super::strategy::{self, StrategyTag};
use super::strategy::SearchMethod;
match self.search {
SearchMethod::Fast => {
self.simple_mut().start_matching(handle_sequence);
}
SearchMethod::DoubleFast => {
self.dfast_matcher_mut()
.start_matching::<K>(handle_sequence);
}
SearchMethod::RowHash | SearchMethod::BinaryTreeLazy => {
self.row_matcher_mut().start_matching::<K>(handle_sequence);
}
SearchMethod::BinaryTree => match self.strategy_tag {
StrategyTag::BtOpt => self.compress_block::<strategy::BtOpt, K>(handle_sequence),
StrategyTag::BtUltra => {
self.compress_block::<strategy::BtUltra, K>(handle_sequence)
}
StrategyTag::BtUltra2 => {
self.compress_block::<strategy::BtUltra2, K>(handle_sequence)
}
_ => unreachable!(
"SearchMethod::BinaryTree requires an optimal strategy tag (BtOpt/BtUltra/BtUltra2)"
),
},
}
}
}
impl MatchGeneratorDriver {
fn compress_block<S: super::strategy::Strategy, K: EncodeTier>(
&mut self,
handle_sequence: &mut impl FnMut(Sequence),
) {
debug_assert_eq!(S::BACKEND, super::strategy::BackendTag::HashChain);
debug_assert!(
S::USE_BT,
"compress_block only handles the optimal (BT) path"
);
self.hc_matcher_mut()
.start_matching_optimal::<S, K>(handle_sequence);
}
}
#[cfg(feature = "bench-internals")]
pub(crate) fn level22_block_ranges(data: &[u8]) -> Vec<(usize, usize)> {
let mut ranges = Vec::new();
let mut cursor = 0usize;
let mut savings = 0i64;
while cursor < data.len() {
let remaining = data.len() - cursor;
let candidate_len = remaining.min(super::cost_model::HC_BLOCKSIZE_MAX);
let block_len = crate::encoding::frame_compressor::optimal_block_size(
CompressionLevel::Level(22),
&data[cursor..cursor + candidate_len],
remaining,
super::cost_model::HC_BLOCKSIZE_MAX,
savings,
)
.min(candidate_len)
.max(1);
ranges.push((cursor, block_len));
cursor += block_len;
if cursor >= super::cost_model::HC_BLOCKSIZE_MAX {
savings = 3;
}
}
ranges
}
#[cfg(feature = "bench-internals")]
fn merge_block_delimiters(sequences: Vec<(usize, usize, usize)>) -> Vec<(usize, usize, usize)> {
let mut out = Vec::with_capacity(sequences.len());
let mut pending_lits = 0usize;
for (lit_len, offset, match_len) in sequences {
if offset == 0 && match_len == 0 {
pending_lits = pending_lits.saturating_add(lit_len);
continue;
}
out.push((lit_len.saturating_add(pending_lits), offset, match_len));
pending_lits = 0;
}
if pending_lits > 0 {
out.push((pending_lits, 0, 0));
}
out
}
#[cfg(feature = "bench-internals")]
pub(crate) fn collect_level22_sequences(data: &[u8]) -> Vec<(usize, usize, usize)> {
merge_block_delimiters(collect_level22_sequences_with_delimiters(data))
.into_iter()
.filter(|(_, offset, match_len)| *offset != 0 || *match_len != 0)
.collect()
}
#[cfg(feature = "bench-internals")]
fn collect_level22_sequences_with_delimiters(data: &[u8]) -> Vec<(usize, usize, usize)> {
let mut driver = MatchGeneratorDriver::new(super::cost_model::HC_BLOCKSIZE_MAX, 1);
driver.set_source_size_hint(data.len() as u64);
driver.reset(CompressionLevel::Level(22));
let mut sequences = Vec::new();
for (chunk_start, chunk_len) in level22_block_ranges(data) {
driver.commit_input(&data[chunk_start..chunk_start + chunk_len]);
driver.start_matching(|seq| {
let entry = match seq {
Sequence::Literals { len } => (len, 0usize, 0usize),
Sequence::Triple {
literal_len,
offset,
match_len,
} => (literal_len, offset, match_len),
};
sequences.push(entry);
});
}
sequences
}
#[cfg(test)]
mod tests;