pub(crate) use super::phase2_first_bigram::FirstBigramSet;
#[cfg(feature = "simd")]
use super::phase2_hs::Phase2HsEngine;
use crate::types::LazyRegex;
use aho_corasick::AhoCorasick;
use std::cell::RefCell;
use std::sync::atomic::{AtomicU64, Ordering::Relaxed};
use std::sync::OnceLock;
mod mark_stats;
#[cfg(feature = "simd")]
pub(crate) use mark_stats::record_mark_hs_served;
pub(crate) use mark_stats::{
format_mark_decomposition, phase2_mark_stats, phase2_mark_stats_reset, record_mark_call,
record_mark_gate_skip, record_mark_perpattern_work, record_mark_regexset_served, MarkSnapshot,
};
mod hs_mark_timing;
pub(crate) use hs_mark_timing::{
format_hs_mark_split, hs_mark_timing_reset, hs_mark_timing_snapshot, HsMarkSplit,
};
#[cfg(feature = "simd")]
pub(crate) use hs_mark_timing::{record_hs_mark_dropped_ns, record_hs_mark_scan_ns};
pub(crate) use crate::tuning::*;
pub(crate) const MIN_PREFIX_BYTES: usize = 3;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct Phase2AlwaysActiveGpuEvidence<'a> {
pub(crate) prefixless_admitted: bool,
pub(crate) prefixless_complete: bool,
pub(crate) anchor_present: bool,
pub(crate) anchor_literal_matches: Option<&'a [(u32, u32)]>,
}
impl Phase2AlwaysActiveGpuEvidence<'_> {
#[inline]
pub(crate) const fn prefixless_absence_proven(self) -> bool {
self.prefixless_complete && !self.prefixless_admitted
}
#[inline]
pub(crate) const fn absence_proven(self) -> bool {
self.prefixless_absence_proven() && !self.anchor_present
}
}
pub(crate) fn phase2_pattern_prof_enabled() -> bool {
super::profile::enabled()
}
static PHASE2_PATTERN_NS: OnceLock<Vec<AtomicU64>> = OnceLock::new();
static PHASE2_PATTERN_RUNS: OnceLock<Vec<AtomicU64>> = OnceLock::new();
pub(crate) static POPULATE_PREFILTER_NS: AtomicU64 = AtomicU64::new(0);
pub(crate) static POPULATE_KEYWORD_NS: AtomicU64 = AtomicU64::new(0);
pub(crate) static GATE_BATCH_SKIPS: AtomicU64 = AtomicU64::new(0);
pub(crate) static GATE_BATCH_RUNS: AtomicU64 = AtomicU64::new(0);
pub(crate) static GATE_CALLS: AtomicU64 = AtomicU64::new(0);
#[cfg(test)]
pub(crate) fn phase2_gate_stats_dump() -> (u64, u64, u64) {
let calls = GATE_CALLS.swap(0, Relaxed);
let skips = GATE_BATCH_SKIPS.swap(0, Relaxed);
let runs = GATE_BATCH_RUNS.swap(0, Relaxed);
eprintln!(
"prefix-gate: calls={calls} gateable_batch_skips={skips} gateable_batch_runs={runs} \
({:.1}% skipped)",
if skips + runs > 0 {
100.0 * skips as f64 / (skips + runs) as f64
} else {
0.0
}
);
(calls, skips, runs)
}
pub(crate) fn phase2_pattern_prof_vecs(len: usize) -> (&'static [AtomicU64], &'static [AtomicU64]) {
let ns = PHASE2_PATTERN_NS.get_or_init(|| (0..len).map(|_| AtomicU64::new(0)).collect());
let runs = PHASE2_PATTERN_RUNS.get_or_init(|| (0..len).map(|_| AtomicU64::new(0)).collect());
(ns.as_slice(), runs.as_slice())
}
pub(crate) fn phase2_pattern_prof_reset(len: usize) {
let (ns, runs) = phase2_pattern_prof_vecs(len);
for n in ns {
n.store(0, Relaxed);
}
for r in runs {
r.store(0, Relaxed);
}
POPULATE_PREFILTER_NS.store(0, Relaxed);
POPULATE_KEYWORD_NS.store(0, Relaxed);
GATE_BATCH_SKIPS.store(0, Relaxed);
GATE_BATCH_RUNS.store(0, Relaxed);
GATE_CALLS.store(0, Relaxed);
}
#[inline]
pub(crate) fn phase2_pattern_prof_record(len: usize, index: usize, nanos: u64) {
let (ns, runs) = phase2_pattern_prof_vecs(len);
if let (Some(n), Some(r)) = (ns.get(index), runs.get(index)) {
n.fetch_add(nanos, Relaxed);
r.fetch_add(1, Relaxed);
}
}
pub(crate) struct ActivePatternsScratch {
pub(crate) active: Vec<usize>,
stamp: Vec<u32>,
generation: u32,
}
impl ActivePatternsScratch {
pub(crate) const fn new() -> Self {
Self {
active: Vec::new(),
stamp: Vec::new(),
generation: 0,
}
}
pub(crate) fn begin(&mut self, len: usize) {
if self.stamp.len() < len {
self.stamp.resize(len, 0);
}
self.generation = self.generation.wrapping_add(1);
if self.generation == 0 {
self.stamp.iter_mut().for_each(|s| *s = 0);
self.generation = 1;
}
self.active.clear();
}
#[inline]
pub(crate) fn mark(&mut self, index: usize) {
if let Some(slot) = self.stamp.get_mut(index) {
if *slot != self.generation {
*slot = self.generation;
self.active.push(index);
}
}
}
#[inline]
pub(crate) fn is_active(&self, index: usize) -> bool {
self.stamp.get(index) == Some(&self.generation)
}
}
thread_local! {
pub(crate) static ANCHOR_CANDIDATES: RefCell<Vec<(u32, u32)>> = const { RefCell::new(Vec::new()) };
}
pub(crate) struct PrefilterBatch {
pub(crate) set: regex::RegexSet,
pub(crate) ascii_set: Option<regex::RegexSet>,
pub(crate) set_trunc: regex::RegexSet,
pub(crate) ascii_set_trunc: Option<regex::RegexSet>,
pub(crate) phase2_indices: Vec<usize>,
pub(crate) gateable: bool,
pub(crate) homoglyph_skippable: bool,
}
pub(crate) struct PortablePrefilter {
pub(crate) batches: Vec<PrefilterBatch>,
pub(crate) ungated_indices: Vec<usize>,
pub(crate) ci_gate: Option<AhoCorasick>,
pub(crate) plain_gate: Option<AhoCorasick>,
}
pub(crate) struct CombinedNoCandidateGate {
pub(crate) anchor_ac: AhoCorasick,
pub(crate) non_anchorable: Vec<(usize, LazyRegex)>,
pub(crate) anchor_first_bigram: FirstBigramSet,
}
impl CombinedNoCandidateGate {
#[inline]
pub(crate) fn anchor_present(&self, match_text: &str) -> bool {
self.anchor_first_bigram.may_have_match(match_text) && self.anchor_ac.is_match(match_text)
}
#[inline]
pub(crate) fn mark_non_anchorable(
&self,
match_text: &str,
scratch: &mut ActivePatternsScratch,
allowed_indices: &[usize],
) {
for (idx, re) in &self.non_anchorable {
if allowed_indices.binary_search(idx).is_ok() && re.get().is_match(match_text) {
scratch.mark(*idx);
}
}
}
#[inline]
pub(crate) fn any_non_anchorable_match(&self, match_text: &str) -> bool {
self.non_anchorable
.iter()
.any(|(_, re)| re.get().is_match(match_text))
}
}
pub(crate) struct Phase2AlwaysActivePrefilter {
pub(crate) valid_always_active_indices: Vec<usize>,
pub(crate) anchor_residual_indices: Vec<usize>,
pub(crate) localized_residual_indices: Vec<usize>,
pub(crate) portable: OnceLock<PortablePrefilter>,
pub(crate) portable_anchor_residual: OnceLock<PortablePrefilter>,
pub(crate) portable_localized_residual: OnceLock<PortablePrefilter>,
pub(crate) combined_gate: OnceLock<Option<CombinedNoCandidateGate>>,
#[cfg(feature = "simd")]
pub(crate) hs: OnceLock<Option<Phase2HsEngine>>,
#[cfg(feature = "simd")]
pub(crate) hs_anchor_residual: OnceLock<Option<Phase2HsEngine>>,
#[cfg(feature = "simd")]
pub(crate) hs_localized_residual: OnceLock<Option<Phase2HsEngine>>,
}
pub(crate) const DECODE_FOCUS_MARGIN: usize = 64;
#[inline]
pub(crate) fn homoglyph_skip_applies(chunk_is_ascii: bool, enabled: bool) -> bool {
enabled && (chunk_is_ascii || super::profile::in_decode())
}
pub(crate) fn gate_prefix_literals(src: &str) -> Option<Vec<Vec<u8>>> {
use regex_syntax::hir::literal::{ExtractKind, Extractor};
let hir = regex_syntax::ParserBuilder::new().build().parse(src).ok()?; let mut ex = Extractor::new();
ex.kind(ExtractKind::Prefix);
let seq = ex.extract(&hir);
if !seq.is_finite() {
return None;
}
let literals = seq.literals()?;
if literals.is_empty() {
return None;
}
let mut out = Vec::with_capacity(literals.len());
for lit in literals {
let bytes = lit.as_bytes();
if bytes.len() < MIN_PREFIX_BYTES || !bytes.is_ascii() {
return None;
}
out.push(bytes.to_vec());
}
Some(out)
}
pub(crate) fn ascii_fold_regex_src(src: &str) -> String {
src.chars().filter(char::is_ascii).collect()
}
thread_local! {
pub(crate) static ACTIVE_PATTERNS_POOL: RefCell<ActivePatternsScratch> =
const { RefCell::new(ActivePatternsScratch::new()) };
}