#![allow(unused_imports)]
use super::generator::HcMatchGenerator;
use crate::encoding::Sequence;
use crate::encoding::blocks::encode_offset_with_history;
use crate::encoding::hc::HC_OPT_CANDIDATE_CAPACITY;
use crate::encoding::hc::priceset::{
priceset_avx2, priceset_neon, priceset_scalar, priceset_simd128, priceset_sse2, priceset_sse41,
};
use alloc::vec::Vec;
#[cfg(test)]
use crate::encoding::opt::types::HcCandidateQuery;
#[allow(unused_imports)]
use crate::encoding::{
bt::BtMatcher,
cost_model::{
HC_BITCOST_MULTIPLIER, HC_OPT_NODE_LEN, HC_OPT_NUM, HC_OPT_PRICE_ARENA_LEN,
HC_OPT_PRICE_STRIDE, HC_PREDEF_THRESHOLD, HcOptState, HcOptimalCostProfile,
},
hc::HC_MIN_MATCH_LEN,
levels::config::HcConfig,
match_generator::{HC_OPT_MIN_MATCH_LEN, HC_SEARCH_DEPTH, HC_TARGET_LEN, HcBackend},
match_table::storage::HC3_HASH_LOG,
opt::ldm::{HcOptLdmState, HcRawSeqStore},
opt::types::{
HcBlockPass, HcOptimalNode, HcOptimalPlanBuffers, HcOptimalPlanState, HcOptimalSequence,
MatchCandidate,
},
};
type OptimalCollectFn = unsafe fn(
&mut HcMatchGenerator,
usize,
usize,
usize,
&[u32; 3],
bool,
&mut Vec<MatchCandidate>,
);
macro_rules! build_optimal_plan_impl_body {
(
$self:expr,
$strategy_ty:ty,
$current:ident,
$current_abs_start:ident,
$current_len:ident,
$initial_state:ident,
$stats:ident,
$out:ident,
$buffers:expr,
$collect:ident,
// The tier's price-set macro (`priceset::priceset_<tier>`).
$priceset:ident,
$block:ident,
$opt_state:ident,
$keep_plan:expr,
$seg:lifetime $(,)?
) => {{
let current_abs_end = $current_abs_start + $current_len;
let min_match_len = HC_OPT_MIN_MATCH_LEN;
let frontier_limit = $current_len.min(HC_OPT_NUM - 1);
let initial_reps = $initial_state.reps;
let initial_litlen = $initial_state.litlen;
let ldm_block_offset = $initial_state.block_offset;
let profile = $initial_state.profile;
debug_assert_eq!(
profile.sufficient_match_len,
$self.hc.sufficient_match_len_for_pass(profile),
"the caller must clamp sufficient_match_len for the pass",
);
debug_assert!(
<$strategy_ty as crate::encoding::strategy::Strategy>::USE_BT,
"build_optimal_plan_impl_body called on non-BT strategy"
);
let abort_on_worse_match: bool =
<$strategy_ty as crate::encoding::strategy::Strategy>::OPT_LEVEL == 0;
let opt_level: bool = <$strategy_ty as crate::encoding::strategy::Strategy>::OPT_LEVEL >= 2;
let frontier_buffer_size = frontier_limit + 2;
let HcOptimalPlanBuffers {
nodes,
node_prices,
candidates,
store,
price_arena,
candidates_searched_at: searched_at,
pass,
} = &mut *$buffers;
debug_assert!(
nodes.len() >= frontier_buffer_size && node_prices.len() >= frontier_buffer_size
);
let nodes: *mut HcOptimalNode = nodes.as_mut_ptr().cast();
let node_prices: *mut u32 = node_prices.as_mut_ptr().cast();
let carried_candidates = *searched_at == Some(($current_abs_start, initial_litlen));
*searched_at = None;
if !carried_candidates {
candidates.clear();
}
store.clear();
let mut ll_cache: &mut [[u32; 2]] = &mut [];
let mut ml_cache: &mut [[u32; 2]] = &mut [];
let mut ll_price_stamp = 0u32;
let mut lit_price_stamp = 0u32;
let mut ml_price_stamp = 0u32;
let sufficient_len = profile.sufficient_match_len;
let mut ll0_price = 0u32;
let mut ll1_price = 0u32;
let mut skipped_literals = 0usize;
let mut seed_candidates_ready = carried_candidates;
if !HAS_LDM && !seed_candidates_ready {
while $current_len - skipped_literals > 8 {
candidates.clear();
unsafe {
$collect(
&mut *$self,
$current_abs_start + skipped_literals,
current_abs_end,
profile.sufficient_match_len,
&initial_reps,
initial_litlen + skipped_literals == 0,
&mut *candidates,
)
};
if !candidates.is_empty() {
if skipped_literals > 0 {
*searched_at = Some((
$current_abs_start + skipped_literals,
initial_litlen + skipped_literals,
));
} else {
seed_candidates_ready = true;
}
break;
}
skipped_literals += 1;
}
if skipped_literals > 0 {
break $seg (
0u32,
initial_reps,
initial_litlen + skipped_literals,
skipped_literals,
);
}
}
let mut pos = 1usize;
let mut last_pos = 0usize;
let mut forced_end: Option<usize> = None;
let mut forced_end_state: Option<HcOptimalNode> = None;
let mut forced_end_price: Option<u32> = None;
let mut seed_forced_shortest_path = false;
let mut opt_ldm = if HAS_LDM {
HcOptLdmState {
seq_store: HcRawSeqStore {
pos: 0,
pos_in_sequence: 0,
size: $self.backend.bt_mut().ldm_sequences.len(),
},
..HcOptLdmState::default()
}
} else {
HcOptLdmState::default()
};
if HAS_LDM {
if ldm_block_offset > 0 {
$self
.backend
.bt_mut()
.ldm_skip_raw_seq_store_bytes(&mut opt_ldm.seq_store, ldm_block_offset);
}
$self
.backend
.bt_mut()
.ldm_get_next_match_and_update_seq_store(&mut opt_ldm, 0, $current_len);
}
if $current_len >= min_match_len {
let seed_ldm = if HAS_LDM {
$self.backend.bt_mut().ldm_process_match_candidate(
&mut opt_ldm,
0,
$current_len,
min_match_len,
)
} else {
None
};
if !seed_candidates_ready {
candidates.clear();
unsafe {
$collect(
&mut *$self,
$current_abs_start,
current_abs_end,
profile.sufficient_match_len,
&initial_reps,
initial_litlen == 0,
&mut *candidates,
)
};
if let Some(ldm) = seed_ldm {
BtMatcher::push_ldm_candidate(&mut *candidates, ldm, min_match_len);
}
}
if !candidates.is_empty() {
let arena_base = price_arena.as_mut_ptr();
ll_cache =
unsafe { core::slice::from_raw_parts_mut(arena_base, HC_OPT_PRICE_STRIDE) };
ml_cache = unsafe {
core::slice::from_raw_parts_mut(
arena_base.add(HC_OPT_PRICE_STRIDE),
HC_OPT_PRICE_STRIDE,
)
};
$self.backend.bt_mut().opt_ll_price_stamp =
$self.backend.bt_mut().opt_ll_price_stamp.wrapping_add(1).max(1);
ll_price_stamp = $self.backend.bt_mut().opt_ll_price_stamp;
$self.backend.bt_mut().opt_lit_price_stamp =
$self.backend.bt_mut().opt_lit_price_stamp.wrapping_add(1).max(1);
lit_price_stamp = $self.backend.bt_mut().opt_lit_price_stamp;
$self.backend.bt_mut().opt_ml_price_stamp =
$self.backend.bt_mut().opt_ml_price_stamp.wrapping_add(1).max(1);
ml_price_stamp = $self.backend.bt_mut().opt_ml_price_stamp;
let node0_price = BtMatcher::cached_lit_length_price::<ACCURATE_PRICE>(
profile,
$stats,
initial_litlen,
&mut ll_cache,
ll_price_stamp,
);
unsafe {
node_prices.write(node0_price);
nodes.write(HcOptimalNode {
litlen: initial_litlen as u32,
reps: initial_reps,
..HcOptimalNode::default()
});
}
ll0_price = BtMatcher::cached_lit_length_price::<ACCURATE_PRICE>(
profile,
$stats,
0,
&mut ll_cache,
ll_price_stamp,
);
ll1_price = BtMatcher::cached_lit_length_price::<ACCURATE_PRICE>(
profile,
$stats,
1,
&mut ll_cache,
ll_price_stamp,
);
last_pos = (min_match_len - 1).min(frontier_limit);
let seed_end = min_match_len.min(frontier_buffer_size);
if seed_end > 1 {
unsafe { BtMatcher::reset_opt_node_prices(node_prices, 1, seed_end - 1) };
}
}
if let Some(candidate) = candidates.last() {
let longest_len = candidate.match_len.min($current_len);
if longest_len > sufficient_len {
let off_base = BtMatcher::encode_offset_base_with_reps(
candidate.offset as u32,
initial_litlen,
initial_reps,
);
let off_price = profile
.offset_price_for::<ACCURATE_PRICE, FAVOR_SMALL_OFFSETS>($stats, off_base);
let ml_price = BtMatcher::cached_match_length_price::<ACCURATE_PRICE>(
profile,
$stats,
longest_len,
&mut ml_cache,
ml_price_stamp,
);
let seq_cost = BtMatcher::add_prices(
ll0_price,
profile.match_price_from_parts(off_price, ml_price, $stats),
);
let forced_price = BtMatcher::add_prices(unsafe { *node_prices }, seq_cost);
let forced_state = HcOptimalNode {
off: candidate.offset as u32,
mlen: longest_len as u32,
litlen: 0,
reps: initial_reps,
};
forced_end = Some(longest_len);
forced_end_state = Some(forced_state);
forced_end_price = Some(forced_price);
seed_forced_shortest_path = true;
}
}
if !seed_forced_shortest_path {
let mut prev_max_len = min_match_len - 1;
for candidate in candidates.iter() {
let max_match_len = candidate.match_len.min(frontier_limit);
if max_match_len < min_match_len {
continue;
}
let start_len = (prev_max_len + 1).max(min_match_len);
if start_len > max_match_len {
prev_max_len = prev_max_len.max(max_match_len);
continue;
}
debug_assert!(max_match_len < frontier_buffer_size);
if max_match_len > last_pos {
unsafe {
BtMatcher::reset_opt_node_prices(
node_prices,
last_pos + 1,
max_match_len,
)
};
}
let off_base = BtMatcher::encode_offset_base_with_reps(
candidate.offset as u32,
initial_litlen,
initial_reps,
);
let off_price = profile
.offset_price_for::<ACCURATE_PRICE, FAVOR_SMALL_OFFSETS>($stats, off_base);
let nodes0_price = unsafe { *node_prices };
for match_len in (start_len..=max_match_len).rev() {
let ml_price = BtMatcher::cached_match_length_price::<ACCURATE_PRICE>(
profile,
$stats,
match_len,
&mut ml_cache,
ml_price_stamp,
);
let seq_cost = BtMatcher::add_prices(
ll0_price,
profile.match_price_from_parts(off_price, ml_price, $stats),
);
let next_cost = BtMatcher::add_prices(nodes0_price, seq_cost);
let node_price = unsafe { *node_prices.add(match_len) };
if match_len > last_pos || next_cost < node_price {
unsafe {
HcOptimalNode::write_match_end(
nodes.add(match_len),
candidate.offset as u32,
match_len as u32,
);
*node_prices.add(match_len) = next_cost;
}
if match_len > last_pos {
last_pos = match_len;
}
} else if abort_on_worse_match {
break;
}
}
prev_max_len = prev_max_len.max(max_match_len);
}
if last_pos + 1 < frontier_buffer_size {
unsafe { *node_prices.add(last_pos + 1) = u32::MAX };
}
}
}
while !seed_forced_shortest_path && pos <= last_pos && pos <= frontier_limit {
debug_assert!(pos + 1 < frontier_buffer_size);
let prev_node_price = unsafe { *node_prices.add(pos - 1) };
if prev_node_price != u32::MAX {
let prev_node = unsafe { *nodes.add(pos - 1) };
let lit_len = prev_node.litlen as usize + 1;
let lit_price = {
let bt = $self.backend.bt_mut();
BtMatcher::cached_literal_price::<ACCURATE_PRICE>(
profile,
$stats,
$current[pos - 1],
&mut bt.opt_lit_price_scratch,
&mut bt.opt_lit_price_generation,
lit_price_stamp,
)
};
let ll_delta = BtMatcher::cached_lit_length_delta_price::<ACCURATE_PRICE>(
profile,
$stats,
lit_len,
&mut ll_cache,
ll_price_stamp,
);
let lit_cost = BtMatcher::add_price_delta(prev_node_price, lit_price, ll_delta);
let node_pos_price = unsafe { *node_prices.add(pos) };
if lit_cost <= node_pos_price {
let (prev_match_off, prev_match_mlen, prev_match_litlen) =
if node_pos_price != u32::MAX {
unsafe {
let cell = nodes.add(pos);
((*cell).off, (*cell).mlen, (*cell).litlen)
}
} else {
(0, 0, u32::MAX)
};
unsafe {
*nodes.add(pos) = HcOptimalNode {
litlen: lit_len as u32,
..prev_node
};
*node_prices.add(pos) = lit_cost;
}
#[allow(clippy::collapsible_if)]
if opt_level
&& prev_match_mlen > 0
&& prev_match_litlen == 0
&& pos < $current_len
{
if ll1_price < ll0_price {
let next_lit_price = {
let bt = $self.backend.bt_mut();
BtMatcher::cached_literal_price::<ACCURATE_PRICE>(
profile,
$stats,
$current[pos],
&mut bt.opt_lit_price_scratch,
&mut bt.opt_lit_price_generation,
lit_price_stamp,
)
};
let with1literal = BtMatcher::add_price_delta(
node_pos_price,
next_lit_price,
ll1_price as i32 - ll0_price as i32,
);
let ll_delta_next =
BtMatcher::cached_lit_length_delta_price::<ACCURATE_PRICE>(
profile,
$stats,
lit_len + 1,
&mut ll_cache,
ll_price_stamp,
);
let with_more_literals =
BtMatcher::add_price_delta(lit_cost, next_lit_price, ll_delta_next);
let next = pos + 1;
let next_price = unsafe { *node_prices.add(next) };
if with1literal < with_more_literals && with1literal < next_price {
debug_assert!(pos >= prev_match_mlen as usize);
let prev_pos = pos - prev_match_mlen as usize;
{
debug_assert!(unsafe { *node_prices.add(prev_pos) } != u32::MAX);
let prev_state = unsafe { *nodes.add(prev_pos) };
let (_, reps_after_match) = BtMatcher::encode_offset_with_reps(
prev_match_off,
prev_state.litlen as usize,
prev_state.reps,
);
unsafe {
*nodes.add(next) = HcOptimalNode {
off: prev_match_off,
mlen: prev_match_mlen,
litlen: 1,
reps: reps_after_match,
};
*node_prices.add(next) = with1literal;
}
if next > last_pos {
last_pos = next;
if next + 1 < frontier_buffer_size {
unsafe { *node_prices.add(next + 1) = u32::MAX };
}
}
}
}
}
}
}
}
let base_cost = unsafe { *node_prices.add(pos) };
if base_cost == u32::MAX {
pos += 1;
continue;
}
{
let (base_off, base_mlen, base_litlen) = unsafe {
let cell = nodes.add(pos);
((*cell).off, (*cell).mlen, (*cell).litlen)
};
if base_mlen > 0 && base_litlen == 0 {
debug_assert!(pos >= base_mlen as usize);
let prev_pos = pos - base_mlen as usize;
debug_assert!(unsafe { *node_prices.add(prev_pos) } != u32::MAX);
let prev_state = unsafe { *nodes.add(prev_pos) };
let (_, reps_after_match) = BtMatcher::encode_offset_with_reps(
base_off,
prev_state.litlen as usize,
prev_state.reps,
);
unsafe { (*nodes.add(pos)).reps = reps_after_match };
}
}
if pos + 8 > $current_len {
pos += 1;
continue;
}
if pos == last_pos {
break;
}
let next_price = unsafe { *node_prices.add(pos + 1) };
if abort_on_worse_match
&& next_price <= base_cost.saturating_add(HC_BITCOST_MULTIPLIER / 2)
{
pos += 1;
continue;
}
let abs_pos = $current_abs_start + pos;
candidates.clear();
unsafe {
$collect(
&mut *$self,
abs_pos,
current_abs_end,
profile.sufficient_match_len,
&(*nodes.add(pos)).reps,
(*nodes.add(pos)).litlen == 0,
&mut *candidates,
)
};
if HAS_LDM {
if let Some(ldm) = $self.backend.bt_mut().ldm_process_match_candidate(
&mut opt_ldm,
pos,
$current_len - pos,
min_match_len,
) {
BtMatcher::push_ldm_candidate(&mut *candidates, ldm, min_match_len);
}
}
let base_reps = unsafe { (*nodes.add(pos)).reps };
let base_litlen = unsafe { (*nodes.add(pos)).litlen as usize };
if let Some(candidate) = candidates.last() {
let longest_len = candidate.match_len.min($current_len - pos);
if longest_len > sufficient_len
|| pos + longest_len >= HC_OPT_NUM
|| pos + longest_len >= $current_len
{
let lit_len = base_litlen;
let off_base = BtMatcher::encode_offset_base_with_reps(
candidate.offset as u32,
lit_len,
base_reps,
);
let off_price = profile
.offset_price_for::<ACCURATE_PRICE, FAVOR_SMALL_OFFSETS>($stats, off_base);
let ml_price = BtMatcher::cached_match_length_price::<ACCURATE_PRICE>(
profile,
$stats,
longest_len,
&mut ml_cache,
ml_price_stamp,
);
let seq_cost = BtMatcher::add_prices(
ll0_price,
profile.match_price_from_parts(off_price, ml_price, $stats),
);
let forced_price = BtMatcher::add_prices(base_cost, seq_cost);
let end_pos = (pos + longest_len).min($current_len);
forced_end = Some(end_pos);
forced_end_state = Some(HcOptimalNode {
off: candidate.offset as u32,
mlen: longest_len as u32,
litlen: 0,
reps: base_reps,
});
forced_end_price = Some(forced_price);
break;
}
}
let mut prev_max_len = min_match_len - 1;
for candidate in candidates.iter() {
debug_assert!(pos <= frontier_limit);
let max_match_len = candidate
.match_len
.min($current_len - pos)
.min(frontier_limit - pos);
let min_len = min_match_len;
if max_match_len < min_len {
continue;
}
let start_len = (prev_max_len + 1).max(min_len);
if start_len > max_match_len {
prev_max_len = prev_max_len.max(max_match_len);
continue;
}
let max_next = pos + max_match_len;
debug_assert!(max_next < frontier_buffer_size);
if max_next > last_pos {
unsafe { BtMatcher::reset_opt_node_prices(node_prices, last_pos + 1, max_next) };
}
let lit_len = base_litlen;
let off_base = BtMatcher::encode_offset_base_with_reps(
candidate.offset as u32,
lit_len,
base_reps,
);
let off_price = profile
.offset_price_for::<ACCURATE_PRICE, FAVOR_SMALL_OFFSETS>($stats, off_base);
debug_assert!(pos + max_match_len < frontier_buffer_size);
if abort_on_worse_match {
for match_len in (start_len..=max_match_len).rev() {
let next = pos + match_len;
let ml_price = BtMatcher::cached_match_length_price::<ACCURATE_PRICE>(
profile,
$stats,
match_len,
&mut ml_cache,
ml_price_stamp,
);
let seq_cost = BtMatcher::add_prices(
ll0_price,
profile.match_price_from_parts(off_price, ml_price, $stats),
);
let next_cost = BtMatcher::add_prices(base_cost, seq_cost);
let node_next_price = unsafe { *node_prices.add(next) };
if next > last_pos || next_cost < node_next_price {
unsafe {
HcOptimalNode::write_match_end(
nodes.add(next),
candidate.offset as u32,
match_len as u32,
);
*node_prices.add(next) = next_cost;
}
if next > last_pos {
last_pos = next;
}
} else {
break;
}
}
} else {
let written = max_next + 1;
let (node_prices_s, nodes_s) = unsafe {
(
core::slice::from_raw_parts_mut(node_prices, written),
core::slice::from_raw_parts_mut(
nodes.cast::<core::mem::MaybeUninit<HcOptimalNode>>(),
written,
),
)
};
#[allow(unused_unsafe)]
{
last_pos = last_pos.max(unsafe {
$priceset!(
ACCURATE_PRICE;
node_prices_s,
nodes_s,
ml_cache,
ml_price_stamp,
profile,
$stats,
pos,
start_len,
max_match_len,
ll0_price,
off_price,
base_cost,
candidate.offset as u32,
last_pos,
)
});
}
}
prev_max_len = prev_max_len.max(max_match_len);
}
if last_pos + 1 < frontier_buffer_size {
unsafe { *node_prices.add(last_pos + 1) = u32::MAX };
}
pos += 1;
}
if last_pos == 0 {
if $current_len == 0 {
let price = 0u32; break $seg (price, initial_reps, initial_litlen, 0);
}
let next_litlen = initial_litlen
.checked_add(1)
.expect("optimal parser next litlen out of usize range");
let price = 0u32;
break $seg (price, initial_reps, next_litlen, 1);
}
let target_pos = forced_end.unwrap_or(last_pos.min(frontier_limit));
let (last_stretch, last_stretch_price) = if let Some(forced_state) = forced_end_state {
(forced_state, forced_end_price.expect("forced state has a price"))
} else {
debug_assert!(target_pos <= last_pos);
unsafe { (*nodes.add(target_pos), *node_prices.add(target_pos)) }
};
if last_stretch_price == u32::MAX {
break $seg (u32::MAX, initial_reps, initial_litlen, $current_len);
}
if last_stretch.mlen == 0 {
break $seg (
last_stretch_price,
last_stretch.reps,
last_stretch.litlen as usize,
target_pos.min($current_len),
);
}
let mut cur = target_pos.saturating_sub(last_stretch.mlen as usize);
let end_reps = if last_stretch.litlen == 0 {
debug_assert!(cur <= last_pos);
debug_assert!(unsafe { *node_prices.add(cur) } != u32::MAX, "unreached node read");
let prev_state = unsafe { *nodes.add(cur) };
let (_, reps_after_match) = BtMatcher::encode_offset_with_reps(
last_stretch.off,
prev_state.litlen as usize,
prev_state.reps,
);
reps_after_match
} else {
let tail_literals = last_stretch.litlen as usize;
if cur < tail_literals {
break $seg (
last_stretch_price,
last_stretch.reps,
tail_literals,
target_pos.min($current_len),
);
}
cur -= tail_literals;
last_stretch.reps
};
let store_end = cur + 2;
if store.len() <= store_end {
store.resize(store_end + 1, HcOptimalNode::default());
}
let mut store_start;
let mut stretch_pos = cur;
if last_stretch.litlen > 0 {
store[store_end] = HcOptimalNode {
litlen: last_stretch.litlen,
mlen: 0,
..HcOptimalNode::default()
};
store_start = store_end.saturating_sub(1);
store[store_start] = last_stretch;
}
store[store_end] = last_stretch;
store_start = store_end;
loop {
debug_assert!(stretch_pos <= last_pos);
debug_assert!(
unsafe { *node_prices.add(stretch_pos) } != u32::MAX,
"unreached node read"
);
let next_stretch = unsafe { *nodes.add(stretch_pos) };
store[store_start].litlen = next_stretch.litlen;
if next_stretch.mlen == 0 {
break;
}
if store_start == 0 {
break;
}
store_start -= 1;
store[store_start] = next_stretch;
let litlen = next_stretch.litlen as usize;
let mlen = next_stretch.mlen as usize;
debug_assert!(litlen + mlen <= $current_len);
let step = litlen + mlen;
if step == 0 || stretch_pos < step {
break;
}
stretch_pos -= step;
}
let mut tail_literals = initial_litlen;
let mut store_pos = store_start;
let mut stats_reps = initial_reps;
let mut recorded = false;
while store_pos <= store_end {
let stretch = store[store_pos];
let llen = stretch.litlen as usize;
let mlen = stretch.mlen as usize;
if mlen == 0 {
tail_literals = llen;
store_pos += 1;
continue;
}
let sequence = HcOptimalSequence {
offset: stretch.off,
match_len: mlen as u32,
lit_len: llen as u32,
};
if $keep_plan {
$out.push(sequence);
}
BtMatcher::record_sequence_stats(
$block,
sequence,
&mut pass.literals_cursor,
&mut stats_reps,
&mut *$opt_state,
);
recorded = true;
tail_literals = 0;
store_pos += 1;
}
if recorded {
$opt_state
.set_base_prices(<$strategy_ty as crate::encoding::strategy::Strategy>::ACCURATE_PRICE);
}
let result = (
last_stretch_price,
end_reps,
if last_stretch.litlen > 0 {
last_stretch.litlen as usize
} else {
tail_literals
},
target_pos.min($current_len),
);
result
}};
}
macro_rules! optimal_block_body {
(
$self:expr,
$strategy_ty:ty,
$current:ident,
$current_abs_start:ident,
$cursor:ident,
$litlen:ident,
$reps:ident,
$profile:ident,
$opt_state:ident,
$plan:ident,
$buffers:ident,
$keep_plan:expr,
$collect:ident,
$priceset:ident $(,)?
) => {{
$self.table.capture_block_coords();
$self.table.skip_insert_until_abs = $self
.table
.skip_insert_until_abs
.max($self.table.history_abs_start);
let collect: OptimalCollectFn = match $self.table.search_mls {
3 => HcMatchGenerator::$collect::<$strategy_ty, 3>,
5 => HcMatchGenerator::$collect::<$strategy_ty, 5>,
6 => HcMatchGenerator::$collect::<$strategy_ty, 6>,
mls => {
debug_assert_eq!(mls, 4, "the binary tree keys on three to six bytes");
HcMatchGenerator::$collect::<$strategy_ty, 4>
}
};
$buffers.pass = HcBlockPass {
cursor: $cursor,
litlen: $litlen,
reps: $reps,
literals_cursor: 0,
};
let block_len = $current.len();
while $buffers.pass.cursor + 8 < block_len {
let cursor = $buffers.pass.cursor;
let segment = &$current[cursor..];
let segment_abs_start = $current_abs_start + cursor;
let segment_len = $current.len() - cursor;
let segment_state = HcOptimalPlanState {
block_offset: cursor,
reps: $buffers.pass.reps,
litlen: $buffers.pass.litlen,
profile: $profile,
};
let segment_stats: &HcOptState = &*$opt_state;
let (_, end_reps, end_litlen, consumed_len) = 'segment: {
build_optimal_plan_impl_body!(
$self,
$strategy_ty,
segment,
segment_abs_start,
segment_len,
segment_state,
segment_stats,
$plan,
$buffers,
collect,
$priceset,
$current,
$opt_state,
$keep_plan,
'segment,
)
};
let pass = &mut $buffers.pass;
pass.reps = end_reps;
pass.litlen = end_litlen;
pass.cursor += consumed_len;
}
}};
}
macro_rules! collect_optimal_candidates_initialized_body {
(
$self:expr,
$strategy_ty:ty,
$abs_pos:ident,
$current_abs_end:ident,
$sufficient_match_len:ident,
$reps:ident,
$ll0:ident,
$out:ident,
$mls:expr,
$bt_insert_range:ident,
$cpl:path,
$cmf:path $(,)?
) => {{
debug_assert_eq!(
$mls, $self.table.search_mls,
"the block chose the search for its key width"
);
let use_hash3: bool = <$strategy_ty as crate::encoding::strategy::Strategy>::USE_HASH3;
debug_assert!(!$self.table.hash_table().is_empty());
debug_assert!($self.table.hash3_log == 0 || !$self.table.hash3_table().is_empty());
debug_assert!(
!use_hash3 || $self.table.hash3_log != 0,
"Strategy::USE_HASH3 = true but runtime hash3_log is 0 — call configure() first",
);
debug_assert!(!$self.table.chain_table().is_empty());
let min_match_len = HC_OPT_MIN_MATCH_LEN;
$out.clear();
if $abs_pos < $self.table.skip_insert_until_abs {
return;
}
{
let update_abs = $self.table.skip_insert_until_abs;
debug_assert!(update_abs >= $self.table.history_abs_start);
debug_assert!($self.table.can_skip_rebase_check($abs_pos));
if update_abs < $abs_pos {
let _ = unsafe {
$self
.table
.$bt_insert_range(update_abs, $abs_pos, $current_abs_end, $abs_pos)
};
}
$self.table.skip_insert_until_abs = $abs_pos;
}
let current_idx = $abs_pos - $self.table.history_abs_start;
if current_idx + 8 > $self.table.block_live.1 {
return;
}
let mut best_len_for_skip = 0usize;
{
let bt_search_depth = $self.table.search_depth;
let best_len_ref = &mut best_len_for_skip;
crate::encoding::hc::generator::bt_insert_and_collect_matches_body!(
$self.table,
bt_search_depth,
$abs_pos,
$current_abs_end,
$sufficient_match_len,
<$strategy_ty as crate::encoding::strategy::Strategy>::MAX_CHAIN_DEPTH,
min_match_len,
best_len_ref,
$out,
$reps,
$ll0,
use_hash3,
$mls,
$cpl,
$cmf,
);
}
}};
}
impl HcMatchGenerator {
#[allow(unused_unsafe)]
pub(crate) fn start_matching_optimal<S: crate::encoding::strategy::Strategy>(
&mut self,
mut handle_sequence: impl FnMut(Sequence),
) {
self.table.ensure_tables();
let (current_abs_start, current_len) = self.table.current_block_range();
if current_len == 0 {
return;
}
let current_ptr = self.table.get_last_space().as_ptr();
let current = unsafe { core::slice::from_raw_parts(current_ptr, current_len) };
let current_abs_end = current_abs_start + current_len;
self.table.arm_block_positions(current_abs_end);
self.table
.apply_limited_update_after_long_match(current_abs_start);
let hash3_start_cursor = self
.table
.skip_insert_until_abs
.max(self.table.history_abs_start);
self.table
.backfill_boundary_positions(current_abs_start, current_abs_end);
self.table.next_to_update3 = hash3_start_cursor;
let mut plan_buffers = self.take_optimal_plan_buffers();
if self.should_run_btultra2_seed_pass::<S>(current_len) {
self.run_btultra2_seed_pass(current, current_abs_start, current_len, &mut plan_buffers);
self.table.arm_block_positions(current_abs_end);
}
let mut profile = HcOptimalCostProfile::const_for_strategy::<S>();
profile.sufficient_match_len = self.hc.sufficient_match_len_for_pass(profile);
debug_assert_eq!(profile.favor_small_offsets, S::FAVOR_SMALL_OFFSETS);
let mut opt_state =
core::mem::replace(&mut self.backend.bt_mut().opt_state, HcOptState::new());
opt_state.rescale_freqs(current, S::ACCURATE_PRICE);
let mut best_plan = core::mem::take(&mut self.backend.bt_mut().opt_segment_plan_scratch);
best_plan.clear();
let plan_reps = self.table.offset_hist;
let (cursor, plan_litlen) = self.table.opt_start_cursor_and_litlen(current_abs_start);
let has_ldm = !self.backend.bt_mut().ldm_sequences.is_empty();
macro_rules! run_main_loop {
($block:ident) => {{
match (S::ACCURATE_PRICE, S::FAVOR_SMALL_OFFSETS, has_ldm) {
(true, false, false) => unsafe {
self.$block::<S, true, false, false, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
(true, false, true) => unsafe {
self.$block::<S, true, false, true, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
(true, true, false) => unsafe {
self.$block::<S, true, true, false, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
(true, true, true) => unsafe {
self.$block::<S, true, true, true, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
(false, false, false) => unsafe {
self.$block::<S, false, false, false, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
(false, false, true) => unsafe {
self.$block::<S, false, false, true, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
(false, true, false) => unsafe {
self.$block::<S, false, true, false, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
(false, true, true) => unsafe {
self.$block::<S, false, true, true, true>(
current,
current_abs_start,
cursor,
plan_litlen,
plan_reps,
profile,
&mut opt_state,
&mut best_plan,
&mut plan_buffers,
)
},
}
}};
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
unsafe {
run_main_loop!(run_optimal_block_neon);
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
use crate::encoding::fastpath::FastpathKernel;
match self.table.kernel {
#[cfg(feature = "kernel-avx2")]
FastpathKernel::Avx2Bmi2 => run_main_loop!(run_optimal_block_avx2_bmi2),
#[cfg(feature = "kernel-sse")]
FastpathKernel::Sse2 => run_main_loop!(run_optimal_block_sse2),
#[cfg(feature = "kernel-sse")]
FastpathKernel::Sse42 => run_main_loop!(run_optimal_block_sse42),
FastpathKernel::Scalar => run_main_loop!(run_optimal_block_scalar),
}
}
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
unsafe {
run_main_loop!(run_optimal_block_simd128);
}
#[cfg(not(any(
all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
),
target_arch = "x86",
target_arch = "x86_64",
all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
)
)))]
{
run_main_loop!(run_optimal_block_scalar);
}
self.table
.emit_optimal_plan(current_len, &best_plan, &mut handle_sequence);
best_plan.clear();
self.backend.bt_mut().opt_segment_plan_scratch = best_plan;
let _ = self
.backend
.bt_mut()
.finish_optimal_plan(plan_buffers, (0, [0; 3], 0, 0));
self.backend.bt_mut().opt_state = opt_state;
}
#[allow(unused_unsafe)]
fn run_btultra2_seed_pass(
&mut self,
current: &[u8],
current_abs_start: usize,
current_len: usize,
plan_buffers: &mut HcOptimalPlanBuffers,
) {
type S = crate::encoding::strategy::BtUltra2;
use crate::encoding::strategy::Strategy;
let mut seed_profile = HcOptimalCostProfile::const_for_strategy::<S>();
seed_profile.sufficient_match_len = self.hc.sufficient_match_len_for_pass(seed_profile);
debug_assert_eq!(seed_profile.favor_small_offsets, S::FAVOR_SMALL_OFFSETS);
let mut opt_state =
core::mem::replace(&mut self.backend.bt_mut().opt_state, HcOptState::new());
opt_state.rescale_freqs(current, S::ACCURATE_PRICE);
let seed_reps = self.table.offset_hist;
let (cursor, seed_litlen) = self.table.opt_start_cursor_and_litlen(current_abs_start);
let mut seed_plan = core::mem::take(&mut self.backend.bt_mut().opt_seed_plan_scratch);
seed_plan.clear();
let has_ldm = !self.backend.bt_mut().ldm_sequences.is_empty();
macro_rules! run_seed_loop {
($block:ident) => {{
match (S::ACCURATE_PRICE, S::FAVOR_SMALL_OFFSETS, has_ldm) {
(true, false, false) => unsafe {
self.$block::<S, true, false, false, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
(true, false, true) => unsafe {
self.$block::<S, true, false, true, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
(true, true, false) => unsafe {
self.$block::<S, true, true, false, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
(true, true, true) => unsafe {
self.$block::<S, true, true, true, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
(false, false, false) => unsafe {
self.$block::<S, false, false, false, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
(false, false, true) => unsafe {
self.$block::<S, false, false, true, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
(false, true, false) => unsafe {
self.$block::<S, false, true, false, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
(false, true, true) => unsafe {
self.$block::<S, false, true, true, false>(
current,
current_abs_start,
cursor,
seed_litlen,
seed_reps,
seed_profile,
&mut opt_state,
&mut seed_plan,
&mut *plan_buffers,
)
},
}
}};
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
unsafe {
run_seed_loop!(run_optimal_block_neon);
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
use crate::encoding::fastpath::FastpathKernel;
match self.table.kernel {
#[cfg(feature = "kernel-avx2")]
FastpathKernel::Avx2Bmi2 => run_seed_loop!(run_optimal_block_avx2_bmi2),
#[cfg(feature = "kernel-sse")]
FastpathKernel::Sse2 => run_seed_loop!(run_optimal_block_sse2),
#[cfg(feature = "kernel-sse")]
FastpathKernel::Sse42 => run_seed_loop!(run_optimal_block_sse42),
FastpathKernel::Scalar => run_seed_loop!(run_optimal_block_scalar),
}
}
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
unsafe {
run_seed_loop!(run_optimal_block_simd128);
}
#[cfg(not(any(
all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
),
target_arch = "x86",
target_arch = "x86_64",
all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
)
)))]
{
run_seed_loop!(run_optimal_block_scalar);
}
seed_plan.clear();
self.backend.bt_mut().opt_seed_plan_scratch = seed_plan;
self.backend.bt_mut().opt_state = opt_state;
debug_assert!(self.table.position_base <= current_abs_start);
let index_shift =
current_abs_start - self.table.position_base + current_len + self.table.index_shift;
self.table.position_base = self.table.history_abs_start;
self.table.index_shift = index_shift;
self.table.next_to_update3 = current_abs_start;
self.table.skip_insert_until_abs = current_abs_start;
}
fn take_optimal_plan_buffers(&mut self) -> HcOptimalPlanBuffers {
let bt = self.backend.bt_mut();
let mut nodes = core::mem::take(&mut bt.opt_nodes_scratch);
let mut node_prices = core::mem::take(&mut bt.opt_node_prices_scratch);
let mut candidates = core::mem::take(&mut bt.opt_candidates_scratch);
let store = core::mem::take(&mut bt.opt_store_scratch);
let mut price_arena = core::mem::take(&mut bt.opt_price_arena);
if nodes.len() < HC_OPT_NODE_LEN {
nodes = alloc::boxed::Box::new_uninit_slice(HC_OPT_NODE_LEN);
}
if node_prices.len() < HC_OPT_NODE_LEN {
node_prices = alloc::boxed::Box::new_uninit_slice(HC_OPT_NODE_LEN);
}
candidates.clear();
candidates.reserve_exact(HC_OPT_CANDIDATE_CAPACITY);
if price_arena.len() < HC_OPT_PRICE_ARENA_LEN {
price_arena = alloc::vec![[0u32; 2]; HC_OPT_PRICE_ARENA_LEN].into_boxed_slice();
}
HcOptimalPlanBuffers {
nodes,
node_prices,
candidates,
store,
price_arena,
candidates_searched_at: None,
pass: HcBlockPass::default(),
}
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
#[target_feature(enable = "neon")]
#[allow(clippy::too_many_arguments)]
unsafe fn run_optimal_block_neon<
S: crate::encoding::strategy::Strategy,
const ACCURATE_PRICE: bool,
const FAVOR_SMALL_OFFSETS: bool,
const HAS_LDM: bool,
const KEEP_PLAN: bool,
>(
&mut self,
current: &[u8],
current_abs_start: usize,
cursor: usize,
litlen: usize,
reps: [u32; 3],
profile: HcOptimalCostProfile,
opt_state: &mut HcOptState,
plan: &mut Vec<HcOptimalSequence>,
buffers: &mut HcOptimalPlanBuffers,
) {
optimal_block_body!(
self,
S,
current,
current_abs_start,
cursor,
litlen,
reps,
profile,
opt_state,
plan,
buffers,
KEEP_PLAN,
collect_optimal_candidates_initialized_neon,
priceset_neon,
)
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
#[target_feature(enable = "sse4.2")]
#[allow(clippy::too_many_arguments)]
unsafe fn run_optimal_block_sse42<
S: crate::encoding::strategy::Strategy,
const ACCURATE_PRICE: bool,
const FAVOR_SMALL_OFFSETS: bool,
const HAS_LDM: bool,
const KEEP_PLAN: bool,
>(
&mut self,
current: &[u8],
current_abs_start: usize,
cursor: usize,
litlen: usize,
reps: [u32; 3],
profile: HcOptimalCostProfile,
opt_state: &mut HcOptState,
plan: &mut Vec<HcOptimalSequence>,
buffers: &mut HcOptimalPlanBuffers,
) {
optimal_block_body!(
self,
S,
current,
current_abs_start,
cursor,
litlen,
reps,
profile,
opt_state,
plan,
buffers,
KEEP_PLAN,
collect_optimal_candidates_initialized_sse42,
priceset_sse41,
)
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
#[target_feature(enable = "sse2")]
#[allow(clippy::too_many_arguments)]
unsafe fn run_optimal_block_sse2<
S: crate::encoding::strategy::Strategy,
const ACCURATE_PRICE: bool,
const FAVOR_SMALL_OFFSETS: bool,
const HAS_LDM: bool,
const KEEP_PLAN: bool,
>(
&mut self,
current: &[u8],
current_abs_start: usize,
cursor: usize,
litlen: usize,
reps: [u32; 3],
profile: HcOptimalCostProfile,
opt_state: &mut HcOptState,
plan: &mut Vec<HcOptimalSequence>,
buffers: &mut HcOptimalPlanBuffers,
) {
optimal_block_body!(
self,
S,
current,
current_abs_start,
cursor,
litlen,
reps,
profile,
opt_state,
plan,
buffers,
KEEP_PLAN,
collect_optimal_candidates_initialized_sse2,
priceset_sse2,
)
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
#[target_feature(enable = "avx2,bmi2")]
#[allow(clippy::too_many_arguments)]
unsafe fn run_optimal_block_avx2_bmi2<
S: crate::encoding::strategy::Strategy,
const ACCURATE_PRICE: bool,
const FAVOR_SMALL_OFFSETS: bool,
const HAS_LDM: bool,
const KEEP_PLAN: bool,
>(
&mut self,
current: &[u8],
current_abs_start: usize,
cursor: usize,
litlen: usize,
reps: [u32; 3],
profile: HcOptimalCostProfile,
opt_state: &mut HcOptState,
plan: &mut Vec<HcOptimalSequence>,
buffers: &mut HcOptimalPlanBuffers,
) {
optimal_block_body!(
self,
S,
current,
current_abs_start,
cursor,
litlen,
reps,
profile,
opt_state,
plan,
buffers,
KEEP_PLAN,
collect_optimal_candidates_initialized_avx2_bmi2,
priceset_avx2,
)
}
#[cfg(not(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
)))]
#[allow(unused_unsafe)]
#[cfg_attr(
all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
),
allow(dead_code)
)]
#[allow(clippy::too_many_arguments)]
fn run_optimal_block_scalar<
S: crate::encoding::strategy::Strategy,
const ACCURATE_PRICE: bool,
const FAVOR_SMALL_OFFSETS: bool,
const HAS_LDM: bool,
const KEEP_PLAN: bool,
>(
&mut self,
current: &[u8],
current_abs_start: usize,
cursor: usize,
litlen: usize,
reps: [u32; 3],
profile: HcOptimalCostProfile,
opt_state: &mut HcOptState,
plan: &mut Vec<HcOptimalSequence>,
buffers: &mut HcOptimalPlanBuffers,
) {
optimal_block_body!(
self,
S,
current,
current_abs_start,
cursor,
litlen,
reps,
profile,
opt_state,
plan,
buffers,
KEEP_PLAN,
collect_optimal_candidates_initialized_scalar,
priceset_scalar,
)
}
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
#[target_feature(enable = "simd128")]
#[allow(unused_unsafe)]
#[allow(clippy::too_many_arguments)]
unsafe fn run_optimal_block_simd128<
S: crate::encoding::strategy::Strategy,
const ACCURATE_PRICE: bool,
const FAVOR_SMALL_OFFSETS: bool,
const HAS_LDM: bool,
const KEEP_PLAN: bool,
>(
&mut self,
current: &[u8],
current_abs_start: usize,
cursor: usize,
litlen: usize,
reps: [u32; 3],
profile: HcOptimalCostProfile,
opt_state: &mut HcOptState,
plan: &mut Vec<HcOptimalSequence>,
buffers: &mut HcOptimalPlanBuffers,
) {
optimal_block_body!(
self,
S,
current,
current_abs_start,
cursor,
litlen,
reps,
profile,
opt_state,
plan,
buffers,
KEEP_PLAN,
collect_optimal_candidates_initialized_simd128,
priceset_simd128,
)
}
#[cfg(test)]
pub(crate) fn collect_optimal_candidates(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
query: HcCandidateQuery,
out: &mut Vec<MatchCandidate>,
) {
use crate::encoding::strategy::{self, StrategyTag};
self.table.ensure_tables();
self.table.capture_block_coords();
self.table.skip_insert_until_abs = self
.table
.skip_insert_until_abs
.max(self.table.history_abs_start);
out.clear();
out.reserve_exact(HC_OPT_CANDIDATE_CAPACITY);
let reps = &query.reps;
let ll0 = query.lit_len == 0;
macro_rules! search {
($strategy:ty) => {
match self.table.search_mls {
3 => self.collect_optimal_candidates_initialized::<$strategy, 3>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
),
5 => self.collect_optimal_candidates_initialized::<$strategy, 5>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
),
6 => self.collect_optimal_candidates_initialized::<$strategy, 6>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
),
_ => self.collect_optimal_candidates_initialized::<$strategy, 4>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
),
}
};
}
match self.strategy_tag {
StrategyTag::BtUltra2 => search!(strategy::BtUltra2),
StrategyTag::BtUltra => search!(strategy::BtUltra),
StrategyTag::Btlazy2 => search!(strategy::Btlazy2),
StrategyTag::BtOpt => search!(strategy::BtOpt),
StrategyTag::Fast | StrategyTag::Dfast | StrategyTag::Greedy | StrategyTag::Lazy => {
unreachable!(
"collect_optimal_candidates is binary-tree only; \
non-BT strategies use their own match finder"
)
}
}
if let Some(ldm) = query.ldm_candidate {
BtMatcher::push_ldm_candidate(out, ldm, HC_OPT_MIN_MATCH_LEN);
}
}
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn collect_optimal_candidates_initialized<
S: crate::encoding::strategy::Strategy,
const MLS: usize,
>(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
reps: &[u32; 3],
ll0: bool,
out: &mut Vec<MatchCandidate>,
) {
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
unsafe {
self.collect_optimal_candidates_initialized_neon::<S, MLS>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
)
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
use crate::encoding::fastpath::FastpathKernel;
match self.table.kernel {
#[cfg(feature = "kernel-avx2")]
FastpathKernel::Avx2Bmi2 => unsafe {
self.collect_optimal_candidates_initialized_avx2_bmi2::<S, MLS>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
)
},
#[cfg(feature = "kernel-sse")]
FastpathKernel::Sse2 => unsafe {
self.collect_optimal_candidates_initialized_sse2::<S, MLS>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
)
},
#[cfg(feature = "kernel-sse")]
FastpathKernel::Sse42 => unsafe {
self.collect_optimal_candidates_initialized_sse42::<S, MLS>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
)
},
FastpathKernel::Scalar => self
.collect_optimal_candidates_initialized_scalar::<S, MLS>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
),
}
}
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
unsafe {
self.collect_optimal_candidates_initialized_simd128::<S, MLS>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
)
}
#[cfg(not(any(
all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
),
target_arch = "x86",
target_arch = "x86_64",
all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
)
)))]
{
self.collect_optimal_candidates_initialized_scalar::<S, MLS>(
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
)
}
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
#[target_feature(enable = "neon")]
#[inline(never)]
unsafe fn collect_optimal_candidates_initialized_neon<
S: crate::encoding::strategy::Strategy,
const MLS: usize,
>(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
reps: &[u32; 3],
ll0: bool,
out: &mut Vec<MatchCandidate>,
) {
collect_optimal_candidates_initialized_body!(
self,
S,
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
MLS,
bt_insert_range_neon,
crate::encoding::fastpath::neon::common_prefix_len_ptr,
crate::encoding::fastpath::neon::count_match_from_indices,
)
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
#[target_feature(enable = "sse4.2")]
#[inline(never)]
unsafe fn collect_optimal_candidates_initialized_sse42<
S: crate::encoding::strategy::Strategy,
const MLS: usize,
>(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
reps: &[u32; 3],
ll0: bool,
out: &mut Vec<MatchCandidate>,
) {
collect_optimal_candidates_initialized_body!(
self,
S,
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
MLS,
bt_insert_range_sse2,
crate::encoding::fastpath::sse2::common_prefix_len_ptr,
crate::encoding::fastpath::sse2::count_match_from_indices,
)
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
#[target_feature(enable = "sse2")]
#[inline(never)]
unsafe fn collect_optimal_candidates_initialized_sse2<
S: crate::encoding::strategy::Strategy,
const MLS: usize,
>(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
reps: &[u32; 3],
ll0: bool,
out: &mut Vec<MatchCandidate>,
) {
collect_optimal_candidates_initialized_body!(
self,
S,
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
MLS,
bt_insert_range_sse2,
crate::encoding::fastpath::sse2::common_prefix_len_ptr,
crate::encoding::fastpath::sse2::count_match_from_indices,
)
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
#[target_feature(enable = "avx2,bmi2")]
#[inline(never)]
unsafe fn collect_optimal_candidates_initialized_avx2_bmi2<
S: crate::encoding::strategy::Strategy,
const MLS: usize,
>(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
reps: &[u32; 3],
ll0: bool,
out: &mut Vec<MatchCandidate>,
) {
collect_optimal_candidates_initialized_body!(
self,
S,
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
MLS,
bt_insert_range_avx2_bmi2,
crate::encoding::fastpath::avx2_bmi2::common_prefix_len_ptr,
crate::encoding::fastpath::avx2_bmi2::count_match_from_indices,
)
}
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
#[target_feature(enable = "simd128")]
#[inline(never)]
unsafe fn collect_optimal_candidates_initialized_simd128<
S: crate::encoding::strategy::Strategy,
const MLS: usize,
>(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
reps: &[u32; 3],
ll0: bool,
out: &mut Vec<MatchCandidate>,
) {
collect_optimal_candidates_initialized_body!(
self,
S,
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
MLS,
bt_insert_range_simd128,
crate::encoding::fastpath::simd128::common_prefix_len_ptr,
crate::encoding::fastpath::simd128::count_match_from_indices,
)
}
#[cfg(not(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
)))]
#[allow(unused_unsafe)]
#[inline(never)]
pub(crate) fn collect_optimal_candidates_initialized_scalar<
S: crate::encoding::strategy::Strategy,
const MLS: usize,
>(
&mut self,
abs_pos: usize,
current_abs_end: usize,
sufficient_match_len: usize,
reps: &[u32; 3],
ll0: bool,
out: &mut Vec<MatchCandidate>,
) {
collect_optimal_candidates_initialized_body!(
self,
S,
abs_pos,
current_abs_end,
sufficient_match_len,
reps,
ll0,
out,
MLS,
bt_insert_range_scalar,
crate::encoding::fastpath::scalar::common_prefix_len_ptr,
crate::encoding::fastpath::scalar::count_match_from_indices,
)
}
}