use alloc::vec::Vec;
use crate::{
meta::{
error::{BuildError, RetryError, RetryFailError},
regex::RegexInfo,
},
nfa::thompson::{pikevm, NFA},
util::{prefilter::Prefilter, primitives::NonMaxUsize},
HalfMatch, Input, Match, MatchKind, PatternID, PatternSet,
};
#[cfg(feature = "dfa-build")]
use crate::dfa;
#[cfg(feature = "dfa-onepass")]
use crate::dfa::onepass;
#[cfg(feature = "hybrid")]
use crate::hybrid;
#[cfg(feature = "nfa-backtrack")]
use crate::nfa::thompson::backtrack;
#[derive(Debug)]
pub(crate) struct PikeVM(PikeVMEngine);
impl PikeVM {
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
) -> Result<PikeVM, BuildError> {
PikeVMEngine::new(info, pre, nfa).map(PikeVM)
}
pub(crate) fn create_cache(&self) -> PikeVMCache {
PikeVMCache::new(self)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn get(&self) -> &PikeVMEngine {
&self.0
}
}
#[derive(Debug)]
pub(crate) struct PikeVMEngine(pikevm::PikeVM);
impl PikeVMEngine {
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
) -> Result<PikeVMEngine, BuildError> {
let pikevm_config = pikevm::Config::new()
.match_kind(info.config().get_match_kind())
.prefilter(pre);
let engine = pikevm::Builder::new()
.configure(pikevm_config)
.build_from_nfa(nfa.clone())
.map_err(BuildError::nfa)?;
debug!("PikeVM built");
Ok(PikeVMEngine(engine))
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn search_slots(
&self,
cache: &mut PikeVMCache,
input: &Input<'_>,
slots: &mut [Option<NonMaxUsize>],
) -> Option<PatternID> {
self.0.search_slots(cache.0.as_mut().unwrap(), input, slots)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn which_overlapping_matches(
&self,
cache: &mut PikeVMCache,
input: &Input<'_>,
patset: &mut PatternSet,
) {
self.0.which_overlapping_matches(
cache.0.as_mut().unwrap(),
input,
patset,
)
}
}
#[derive(Clone, Debug)]
pub(crate) struct PikeVMCache(Option<pikevm::Cache>);
impl PikeVMCache {
pub(crate) fn none() -> PikeVMCache {
PikeVMCache(None)
}
pub(crate) fn new(builder: &PikeVM) -> PikeVMCache {
PikeVMCache(Some(builder.get().0.create_cache()))
}
pub(crate) fn reset(&mut self, builder: &PikeVM) {
self.0.as_mut().unwrap().reset(&builder.get().0);
}
pub(crate) fn memory_usage(&self) -> usize {
self.0.as_ref().map_or(0, |c| c.memory_usage())
}
}
#[derive(Debug)]
pub(crate) struct BoundedBacktracker(Option<BoundedBacktrackerEngine>);
impl BoundedBacktracker {
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
) -> Result<BoundedBacktracker, BuildError> {
BoundedBacktrackerEngine::new(info, pre, nfa).map(BoundedBacktracker)
}
pub(crate) fn create_cache(&self) -> BoundedBacktrackerCache {
BoundedBacktrackerCache::new(self)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn get(
&self,
input: &Input<'_>,
) -> Option<&BoundedBacktrackerEngine> {
let engine = self.0.as_ref()?;
if input.get_earliest() && input.haystack().len() > 128 {
return None;
}
if input.get_span().len() > engine.max_haystack_len() {
return None;
}
Some(engine)
}
}
#[derive(Debug)]
pub(crate) struct BoundedBacktrackerEngine(
#[cfg(feature = "nfa-backtrack")] backtrack::BoundedBacktracker,
#[cfg(not(feature = "nfa-backtrack"))] (),
);
impl BoundedBacktrackerEngine {
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
) -> Result<Option<BoundedBacktrackerEngine>, BuildError> {
#[cfg(feature = "nfa-backtrack")]
{
if !info.config().get_backtrack()
|| info.config().get_match_kind() != MatchKind::LeftmostFirst
{
return Ok(None);
}
let backtrack_config = backtrack::Config::new().prefilter(pre);
let engine = backtrack::Builder::new()
.configure(backtrack_config)
.build_from_nfa(nfa.clone())
.map_err(BuildError::nfa)?;
debug!("BoundedBacktracker built");
Ok(Some(BoundedBacktrackerEngine(engine)))
}
#[cfg(not(feature = "nfa-backtrack"))]
{
Ok(None)
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn search_slots(
&self,
cache: &mut BoundedBacktrackerCache,
input: &Input<'_>,
slots: &mut [Option<NonMaxUsize>],
) -> Option<PatternID> {
#[cfg(feature = "nfa-backtrack")]
{
self.0
.try_search_slots(cache.0.as_mut().unwrap(), input, slots)
.unwrap()
}
#[cfg(not(feature = "nfa-backtrack"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
fn max_haystack_len(&self) -> usize {
#[cfg(feature = "nfa-backtrack")]
{
self.0.max_haystack_len()
}
#[cfg(not(feature = "nfa-backtrack"))]
{
unreachable!()
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct BoundedBacktrackerCache(
#[cfg(feature = "nfa-backtrack")] Option<backtrack::Cache>,
#[cfg(not(feature = "nfa-backtrack"))] (),
);
impl BoundedBacktrackerCache {
pub(crate) fn none() -> BoundedBacktrackerCache {
#[cfg(feature = "nfa-backtrack")]
{
BoundedBacktrackerCache(None)
}
#[cfg(not(feature = "nfa-backtrack"))]
{
BoundedBacktrackerCache(())
}
}
pub(crate) fn new(
builder: &BoundedBacktracker,
) -> BoundedBacktrackerCache {
#[cfg(feature = "nfa-backtrack")]
{
BoundedBacktrackerCache(
builder.0.as_ref().map(|e| e.0.create_cache()),
)
}
#[cfg(not(feature = "nfa-backtrack"))]
{
BoundedBacktrackerCache(())
}
}
pub(crate) fn reset(&mut self, builder: &BoundedBacktracker) {
#[cfg(feature = "nfa-backtrack")]
if let Some(ref e) = builder.0 {
self.0.as_mut().unwrap().reset(&e.0);
}
}
pub(crate) fn memory_usage(&self) -> usize {
#[cfg(feature = "nfa-backtrack")]
{
self.0.as_ref().map_or(0, |c| c.memory_usage())
}
#[cfg(not(feature = "nfa-backtrack"))]
{
0
}
}
}
#[derive(Debug)]
pub(crate) struct OnePass(Option<OnePassEngine>);
impl OnePass {
pub(crate) fn new(info: &RegexInfo, nfa: &NFA) -> OnePass {
OnePass(OnePassEngine::new(info, nfa))
}
pub(crate) fn create_cache(&self) -> OnePassCache {
OnePassCache::new(self)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn get(&self, input: &Input<'_>) -> Option<&OnePassEngine> {
let engine = self.0.as_ref()?;
if !input.get_anchored().is_anchored()
&& !engine.get_nfa().is_always_start_anchored()
{
return None;
}
Some(engine)
}
pub(crate) fn memory_usage(&self) -> usize {
self.0.as_ref().map_or(0, |e| e.memory_usage())
}
}
#[derive(Debug)]
pub(crate) struct OnePassEngine(
#[cfg(feature = "dfa-onepass")] onepass::DFA,
#[cfg(not(feature = "dfa-onepass"))] (),
);
impl OnePassEngine {
pub(crate) fn new(info: &RegexInfo, nfa: &NFA) -> Option<OnePassEngine> {
#[cfg(feature = "dfa-onepass")]
{
if !info.config().get_onepass() {
return None;
}
if info.props_union().explicit_captures_len() == 0
&& !info.props_union().look_set().contains_word_unicode()
{
debug!("not building OnePass because it isn't worth it");
return None;
}
let onepass_config = onepass::Config::new()
.match_kind(info.config().get_match_kind())
.starts_for_each_pattern(true)
.byte_classes(info.config().get_byte_classes())
.size_limit(info.config().get_onepass_size_limit());
let result = onepass::Builder::new()
.configure(onepass_config)
.build_from_nfa(nfa.clone());
let engine = match result {
Ok(engine) => engine,
Err(_err) => {
debug!("OnePass failed to build: {}", _err);
return None;
}
};
debug!("OnePass built, {} bytes", engine.memory_usage());
Some(OnePassEngine(engine))
}
#[cfg(not(feature = "dfa-onepass"))]
{
None
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn search_slots(
&self,
cache: &mut OnePassCache,
input: &Input<'_>,
slots: &mut [Option<NonMaxUsize>],
) -> Option<PatternID> {
#[cfg(feature = "dfa-onepass")]
{
self.0
.try_search_slots(cache.0.as_mut().unwrap(), input, slots)
.unwrap()
}
#[cfg(not(feature = "dfa-onepass"))]
{
unreachable!()
}
}
pub(crate) fn memory_usage(&self) -> usize {
#[cfg(feature = "dfa-onepass")]
{
self.0.memory_usage()
}
#[cfg(not(feature = "dfa-onepass"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
fn get_nfa(&self) -> &NFA {
#[cfg(feature = "dfa-onepass")]
{
self.0.get_nfa()
}
#[cfg(not(feature = "dfa-onepass"))]
{
unreachable!()
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct OnePassCache(
#[cfg(feature = "dfa-onepass")] Option<onepass::Cache>,
#[cfg(not(feature = "dfa-onepass"))] (),
);
impl OnePassCache {
pub(crate) fn none() -> OnePassCache {
#[cfg(feature = "dfa-onepass")]
{
OnePassCache(None)
}
#[cfg(not(feature = "dfa-onepass"))]
{
OnePassCache(())
}
}
pub(crate) fn new(builder: &OnePass) -> OnePassCache {
#[cfg(feature = "dfa-onepass")]
{
OnePassCache(builder.0.as_ref().map(|e| e.0.create_cache()))
}
#[cfg(not(feature = "dfa-onepass"))]
{
OnePassCache(())
}
}
pub(crate) fn reset(&mut self, builder: &OnePass) {
#[cfg(feature = "dfa-onepass")]
if let Some(ref e) = builder.0 {
self.0.as_mut().unwrap().reset(&e.0);
}
}
pub(crate) fn memory_usage(&self) -> usize {
#[cfg(feature = "dfa-onepass")]
{
self.0.as_ref().map_or(0, |c| c.memory_usage())
}
#[cfg(not(feature = "dfa-onepass"))]
{
0
}
}
}
#[derive(Debug)]
pub(crate) struct Hybrid(Option<HybridEngine>);
impl Hybrid {
pub(crate) fn none() -> Hybrid {
Hybrid(None)
}
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
nfarev: &NFA,
) -> Hybrid {
Hybrid(HybridEngine::new(info, pre, nfa, nfarev))
}
pub(crate) fn create_cache(&self) -> HybridCache {
HybridCache::new(self)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&HybridEngine> {
let engine = self.0.as_ref()?;
Some(engine)
}
pub(crate) fn is_some(&self) -> bool {
self.0.is_some()
}
}
#[derive(Debug)]
pub(crate) struct HybridEngine(
#[cfg(feature = "hybrid")] hybrid::regex::Regex,
#[cfg(not(feature = "hybrid"))] (),
);
impl HybridEngine {
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
nfarev: &NFA,
) -> Option<HybridEngine> {
#[cfg(feature = "hybrid")]
{
if !info.config().get_hybrid() {
return None;
}
let dfa_config = hybrid::dfa::Config::new()
.match_kind(info.config().get_match_kind())
.prefilter(pre.clone())
.starts_for_each_pattern(true)
.byte_classes(info.config().get_byte_classes())
.unicode_word_boundary(true)
.specialize_start_states(pre.is_some())
.cache_capacity(info.config().get_hybrid_cache_capacity())
.skip_cache_capacity_check(false)
.minimum_cache_clear_count(Some(3))
.minimum_bytes_per_state(Some(10));
let result = hybrid::dfa::Builder::new()
.configure(dfa_config.clone())
.build_from_nfa(nfa.clone());
let fwd = match result {
Ok(fwd) => fwd,
Err(_err) => {
debug!("forward lazy DFA failed to build: {}", _err);
return None;
}
};
let result = hybrid::dfa::Builder::new()
.configure(
dfa_config
.clone()
.match_kind(MatchKind::All)
.prefilter(None)
.specialize_start_states(false),
)
.build_from_nfa(nfarev.clone());
let rev = match result {
Ok(rev) => rev,
Err(_err) => {
debug!("reverse lazy DFA failed to build: {}", _err);
return None;
}
};
let engine =
hybrid::regex::Builder::new().build_from_dfas(fwd, rev);
debug!("lazy DFA built");
Some(HybridEngine(engine))
}
#[cfg(not(feature = "hybrid"))]
{
None
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search(
&self,
cache: &mut HybridCache,
input: &Input<'_>,
) -> Result<Option<Match>, RetryFailError> {
#[cfg(feature = "hybrid")]
{
let cache = cache.0.as_mut().unwrap();
self.0.try_search(cache, input).map_err(|e| e.into())
}
#[cfg(not(feature = "hybrid"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_fwd(
&self,
cache: &mut HybridCache,
input: &Input<'_>,
) -> Result<Option<HalfMatch>, RetryFailError> {
#[cfg(feature = "hybrid")]
{
let fwd = self.0.forward();
let mut fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
fwd.try_search_fwd(&mut fwdcache, input).map_err(|e| e.into())
}
#[cfg(not(feature = "hybrid"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_fwd_stopat(
&self,
cache: &mut HybridCache,
input: &Input<'_>,
) -> Result<Result<HalfMatch, usize>, RetryFailError> {
#[cfg(feature = "hybrid")]
{
let dfa = self.0.forward();
let mut cache = cache.0.as_mut().unwrap().as_parts_mut().0;
crate::meta::stopat::hybrid_try_search_half_fwd(
dfa, &mut cache, input,
)
}
#[cfg(not(feature = "hybrid"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_rev(
&self,
cache: &mut HybridCache,
input: &Input<'_>,
) -> Result<Option<HalfMatch>, RetryFailError> {
#[cfg(feature = "hybrid")]
{
let rev = self.0.reverse();
let mut revcache = cache.0.as_mut().unwrap().as_parts_mut().1;
rev.try_search_rev(&mut revcache, input).map_err(|e| e.into())
}
#[cfg(not(feature = "hybrid"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_rev_limited(
&self,
cache: &mut HybridCache,
input: &Input<'_>,
min_start: usize,
) -> Result<Option<HalfMatch>, RetryError> {
#[cfg(feature = "hybrid")]
{
let dfa = self.0.reverse();
let mut cache = cache.0.as_mut().unwrap().as_parts_mut().1;
crate::meta::limited::hybrid_try_search_half_rev(
dfa, &mut cache, input, min_start,
)
}
#[cfg(not(feature = "hybrid"))]
{
unreachable!()
}
}
#[inline]
pub(crate) fn try_which_overlapping_matches(
&self,
cache: &mut HybridCache,
input: &Input<'_>,
patset: &mut PatternSet,
) -> Result<(), RetryFailError> {
#[cfg(feature = "hybrid")]
{
let fwd = self.0.forward();
let mut fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
fwd.try_which_overlapping_matches(&mut fwdcache, input, patset)
.map_err(|e| e.into())
}
#[cfg(not(feature = "hybrid"))]
{
unreachable!()
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct HybridCache(
#[cfg(feature = "hybrid")] Option<hybrid::regex::Cache>,
#[cfg(not(feature = "hybrid"))] (),
);
impl HybridCache {
pub(crate) fn none() -> HybridCache {
#[cfg(feature = "hybrid")]
{
HybridCache(None)
}
#[cfg(not(feature = "hybrid"))]
{
HybridCache(())
}
}
pub(crate) fn new(builder: &Hybrid) -> HybridCache {
#[cfg(feature = "hybrid")]
{
HybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
}
#[cfg(not(feature = "hybrid"))]
{
HybridCache(())
}
}
pub(crate) fn reset(&mut self, builder: &Hybrid) {
#[cfg(feature = "hybrid")]
if let Some(ref e) = builder.0 {
self.0.as_mut().unwrap().reset(&e.0);
}
}
pub(crate) fn memory_usage(&self) -> usize {
#[cfg(feature = "hybrid")]
{
self.0.as_ref().map_or(0, |c| c.memory_usage())
}
#[cfg(not(feature = "hybrid"))]
{
0
}
}
}
#[derive(Debug)]
pub(crate) struct DFA(Option<DFAEngine>);
impl DFA {
pub(crate) fn none() -> DFA {
DFA(None)
}
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
nfarev: &NFA,
) -> DFA {
DFA(DFAEngine::new(info, pre, nfa, nfarev))
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&DFAEngine> {
let engine = self.0.as_ref()?;
Some(engine)
}
pub(crate) fn is_some(&self) -> bool {
self.0.is_some()
}
pub(crate) fn memory_usage(&self) -> usize {
self.0.as_ref().map_or(0, |e| e.memory_usage())
}
}
#[derive(Debug)]
pub(crate) struct DFAEngine(
#[cfg(feature = "dfa-build")] dfa::regex::Regex,
#[cfg(not(feature = "dfa-build"))] (),
);
impl DFAEngine {
pub(crate) fn new(
info: &RegexInfo,
pre: Option<Prefilter>,
nfa: &NFA,
nfarev: &NFA,
) -> Option<DFAEngine> {
#[cfg(feature = "dfa-build")]
{
if !info.config().get_dfa() {
return None;
}
if let Some(state_limit) = info.config().get_dfa_state_limit() {
if nfa.states().len() > state_limit {
debug!(
"skipping full DFA because NFA has {} states, \
which exceeds the heuristic limit of {}",
nfa.states().len(),
state_limit,
);
return None;
}
}
let size_limit = info.config().get_dfa_size_limit().map(|n| n / 4);
let dfa_config = dfa::dense::Config::new()
.match_kind(info.config().get_match_kind())
.prefilter(pre.clone())
.starts_for_each_pattern(true)
.byte_classes(info.config().get_byte_classes())
.unicode_word_boundary(true)
.specialize_start_states(pre.is_some())
.determinize_size_limit(size_limit)
.dfa_size_limit(size_limit);
let result = dfa::dense::Builder::new()
.configure(dfa_config.clone())
.build_from_nfa(&nfa);
let fwd = match result {
Ok(fwd) => fwd,
Err(_err) => {
debug!("forward full DFA failed to build: {}", _err);
return None;
}
};
let result = dfa::dense::Builder::new()
.configure(
dfa_config
.clone()
.start_kind(dfa::StartKind::Anchored)
.match_kind(MatchKind::All)
.prefilter(None)
.specialize_start_states(false),
)
.build_from_nfa(&nfarev);
let rev = match result {
Ok(rev) => rev,
Err(_err) => {
debug!("reverse full DFA failed to build: {}", _err);
return None;
}
};
let engine = dfa::regex::Builder::new().build_from_dfas(fwd, rev);
debug!(
"fully compiled forward and reverse DFAs built, {} bytes",
engine.forward().memory_usage()
+ engine.reverse().memory_usage(),
);
Some(DFAEngine(engine))
}
#[cfg(not(feature = "dfa-build"))]
{
None
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search(
&self,
input: &Input<'_>,
) -> Result<Option<Match>, RetryFailError> {
#[cfg(feature = "dfa-build")]
{
self.0.try_search(input).map_err(|e| e.into())
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_fwd(
&self,
input: &Input<'_>,
) -> Result<Option<HalfMatch>, RetryFailError> {
#[cfg(feature = "dfa-build")]
{
use crate::dfa::Automaton;
self.0.forward().try_search_fwd(input).map_err(|e| e.into())
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_fwd_stopat(
&self,
input: &Input<'_>,
) -> Result<Result<HalfMatch, usize>, RetryFailError> {
#[cfg(feature = "dfa-build")]
{
let dfa = self.0.forward();
crate::meta::stopat::dfa_try_search_half_fwd(dfa, input)
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_rev(
&self,
input: &Input<'_>,
) -> Result<Option<HalfMatch>, RetryFailError> {
#[cfg(feature = "dfa-build")]
{
use crate::dfa::Automaton;
self.0.reverse().try_search_rev(&input).map_err(|e| e.into())
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_rev_limited(
&self,
input: &Input<'_>,
min_start: usize,
) -> Result<Option<HalfMatch>, RetryError> {
#[cfg(feature = "dfa-build")]
{
let dfa = self.0.reverse();
crate::meta::limited::dfa_try_search_half_rev(
dfa, input, min_start,
)
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
#[inline]
pub(crate) fn try_which_overlapping_matches(
&self,
input: &Input<'_>,
patset: &mut PatternSet,
) -> Result<(), RetryFailError> {
#[cfg(feature = "dfa-build")]
{
use crate::dfa::Automaton;
self.0
.forward()
.try_which_overlapping_matches(input, patset)
.map_err(|e| e.into())
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
pub(crate) fn memory_usage(&self) -> usize {
#[cfg(feature = "dfa-build")]
{
self.0.forward().memory_usage() + self.0.reverse().memory_usage()
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
}
#[derive(Debug)]
pub(crate) struct ReverseHybrid(Option<ReverseHybridEngine>);
impl ReverseHybrid {
pub(crate) fn none() -> ReverseHybrid {
ReverseHybrid(None)
}
pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseHybrid {
ReverseHybrid(ReverseHybridEngine::new(info, nfarev))
}
pub(crate) fn create_cache(&self) -> ReverseHybridCache {
ReverseHybridCache::new(self)
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn get(
&self,
_input: &Input<'_>,
) -> Option<&ReverseHybridEngine> {
let engine = self.0.as_ref()?;
Some(engine)
}
}
#[derive(Debug)]
pub(crate) struct ReverseHybridEngine(
#[cfg(feature = "hybrid")] hybrid::dfa::DFA,
#[cfg(not(feature = "hybrid"))] (),
);
impl ReverseHybridEngine {
pub(crate) fn new(
info: &RegexInfo,
nfarev: &NFA,
) -> Option<ReverseHybridEngine> {
#[cfg(feature = "hybrid")]
{
if !info.config().get_hybrid() {
return None;
}
let dfa_config = hybrid::dfa::Config::new()
.match_kind(MatchKind::All)
.prefilter(None)
.starts_for_each_pattern(false)
.byte_classes(info.config().get_byte_classes())
.unicode_word_boundary(true)
.specialize_start_states(false)
.cache_capacity(info.config().get_hybrid_cache_capacity())
.skip_cache_capacity_check(false)
.minimum_cache_clear_count(Some(3))
.minimum_bytes_per_state(Some(10));
let result = hybrid::dfa::Builder::new()
.configure(dfa_config)
.build_from_nfa(nfarev.clone());
let rev = match result {
Ok(rev) => rev,
Err(_err) => {
debug!("lazy reverse DFA failed to build: {}", _err);
return None;
}
};
debug!("lazy reverse DFA built");
Some(ReverseHybridEngine(rev))
}
#[cfg(not(feature = "hybrid"))]
{
None
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_rev_limited(
&self,
cache: &mut ReverseHybridCache,
input: &Input<'_>,
min_start: usize,
) -> Result<Option<HalfMatch>, RetryError> {
#[cfg(feature = "hybrid")]
{
let dfa = &self.0;
let mut cache = cache.0.as_mut().unwrap();
crate::meta::limited::hybrid_try_search_half_rev(
dfa, &mut cache, input, min_start,
)
}
#[cfg(not(feature = "hybrid"))]
{
unreachable!()
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct ReverseHybridCache(
#[cfg(feature = "hybrid")] Option<hybrid::dfa::Cache>,
#[cfg(not(feature = "hybrid"))] (),
);
impl ReverseHybridCache {
pub(crate) fn none() -> ReverseHybridCache {
#[cfg(feature = "hybrid")]
{
ReverseHybridCache(None)
}
#[cfg(not(feature = "hybrid"))]
{
ReverseHybridCache(())
}
}
pub(crate) fn new(builder: &ReverseHybrid) -> ReverseHybridCache {
#[cfg(feature = "hybrid")]
{
ReverseHybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
}
#[cfg(not(feature = "hybrid"))]
{
ReverseHybridCache(())
}
}
pub(crate) fn reset(&mut self, builder: &ReverseHybrid) {
#[cfg(feature = "hybrid")]
if let Some(ref e) = builder.0 {
self.0.as_mut().unwrap().reset(&e.0);
}
}
pub(crate) fn memory_usage(&self) -> usize {
#[cfg(feature = "hybrid")]
{
self.0.as_ref().map_or(0, |c| c.memory_usage())
}
#[cfg(not(feature = "hybrid"))]
{
0
}
}
}
#[derive(Debug)]
pub(crate) struct ReverseDFA(Option<ReverseDFAEngine>);
impl ReverseDFA {
pub(crate) fn none() -> ReverseDFA {
ReverseDFA(None)
}
pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseDFA {
ReverseDFA(ReverseDFAEngine::new(info, nfarev))
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&ReverseDFAEngine> {
let engine = self.0.as_ref()?;
Some(engine)
}
pub(crate) fn is_some(&self) -> bool {
self.0.is_some()
}
pub(crate) fn memory_usage(&self) -> usize {
self.0.as_ref().map_or(0, |e| e.memory_usage())
}
}
#[derive(Debug)]
pub(crate) struct ReverseDFAEngine(
#[cfg(feature = "dfa-build")] dfa::dense::DFA<Vec<u32>>,
#[cfg(not(feature = "dfa-build"))] (),
);
impl ReverseDFAEngine {
pub(crate) fn new(
info: &RegexInfo,
nfarev: &NFA,
) -> Option<ReverseDFAEngine> {
#[cfg(feature = "dfa-build")]
{
if !info.config().get_dfa() {
return None;
}
if let Some(state_limit) = info.config().get_dfa_state_limit() {
if nfarev.states().len() > state_limit {
debug!(
"skipping full reverse DFA because NFA has {} states, \
which exceeds the heuristic limit of {}",
nfarev.states().len(),
state_limit,
);
return None;
}
}
let size_limit = info.config().get_dfa_size_limit().map(|n| n / 2);
let dfa_config = dfa::dense::Config::new()
.match_kind(MatchKind::All)
.prefilter(None)
.accelerate(false)
.start_kind(dfa::StartKind::Anchored)
.starts_for_each_pattern(false)
.byte_classes(info.config().get_byte_classes())
.unicode_word_boundary(true)
.specialize_start_states(false)
.determinize_size_limit(size_limit)
.dfa_size_limit(size_limit);
let result = dfa::dense::Builder::new()
.configure(dfa_config)
.build_from_nfa(&nfarev);
let rev = match result {
Ok(rev) => rev,
Err(_err) => {
debug!("full reverse DFA failed to build: {}", _err);
return None;
}
};
debug!(
"fully compiled reverse DFA built, {} bytes",
rev.memory_usage()
);
Some(ReverseDFAEngine(rev))
}
#[cfg(not(feature = "dfa-build"))]
{
None
}
}
#[cfg_attr(feature = "perf-inline", inline(always))]
pub(crate) fn try_search_half_rev_limited(
&self,
input: &Input<'_>,
min_start: usize,
) -> Result<Option<HalfMatch>, RetryError> {
#[cfg(feature = "dfa-build")]
{
let dfa = &self.0;
crate::meta::limited::dfa_try_search_half_rev(
dfa, input, min_start,
)
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
pub(crate) fn memory_usage(&self) -> usize {
#[cfg(feature = "dfa-build")]
{
self.0.memory_usage()
}
#[cfg(not(feature = "dfa-build"))]
{
unreachable!()
}
}
}