1use alloc::vec::Vec;
28
29use crate::{
30    meta::{
31        error::{BuildError, RetryError, RetryFailError},
32        regex::RegexInfo,
33    },
34    nfa::thompson::{pikevm, NFA},
35    util::{prefilter::Prefilter, primitives::NonMaxUsize},
36    HalfMatch, Input, Match, MatchKind, PatternID, PatternSet,
37};
38
39#[cfg(feature = "dfa-build")]
40use crate::dfa;
41#[cfg(feature = "dfa-onepass")]
42use crate::dfa::onepass;
43#[cfg(feature = "hybrid")]
44use crate::hybrid;
45#[cfg(feature = "nfa-backtrack")]
46use crate::nfa::thompson::backtrack;
47
48#[derive(Debug)]
49pub(crate) struct PikeVM(PikeVMEngine);
50
51impl PikeVM {
52    pub(crate) fn new(
53        info: &RegexInfo,
54        pre: Option<Prefilter>,
55        nfa: &NFA,
56    ) -> Result<PikeVM, BuildError> {
57        PikeVMEngine::new(info, pre, nfa).map(PikeVM)
58    }
59
60    pub(crate) fn create_cache(&self) -> PikeVMCache {
61        PikeVMCache::none()
62    }
63
64    #[cfg_attr(feature = "perf-inline", inline(always))]
65    pub(crate) fn get(&self) -> &PikeVMEngine {
66        &self.0
67    }
68}
69
70#[derive(Debug)]
71pub(crate) struct PikeVMEngine(pikevm::PikeVM);
72
73impl PikeVMEngine {
74    pub(crate) fn new(
75        info: &RegexInfo,
76        pre: Option<Prefilter>,
77        nfa: &NFA,
78    ) -> Result<PikeVMEngine, BuildError> {
79        let pikevm_config = pikevm::Config::new()
80            .match_kind(info.config().get_match_kind())
81            .prefilter(pre);
82        let engine = pikevm::Builder::new()
83            .configure(pikevm_config)
84            .build_from_nfa(nfa.clone())
85            .map_err(BuildError::nfa)?;
86        debug!("PikeVM built");
87        Ok(PikeVMEngine(engine))
88    }
89
90    #[cfg_attr(feature = "perf-inline", inline(always))]
91    pub(crate) fn is_match(
92        &self,
93        cache: &mut PikeVMCache,
94        input: &Input<'_>,
95    ) -> bool {
96        self.0.is_match(cache.get(&self.0), input.clone())
97    }
98
99    #[cfg_attr(feature = "perf-inline", inline(always))]
100    pub(crate) fn search_slots(
101        &self,
102        cache: &mut PikeVMCache,
103        input: &Input<'_>,
104        slots: &mut [Option<NonMaxUsize>],
105    ) -> Option<PatternID> {
106        self.0.search_slots(cache.get(&self.0), input, slots)
107    }
108
109    #[cfg_attr(feature = "perf-inline", inline(always))]
110    pub(crate) fn which_overlapping_matches(
111        &self,
112        cache: &mut PikeVMCache,
113        input: &Input<'_>,
114        patset: &mut PatternSet,
115    ) {
116        self.0.which_overlapping_matches(cache.get(&self.0), input, patset)
117    }
118}
119
120#[derive(Clone, Debug)]
121pub(crate) struct PikeVMCache(Option<pikevm::Cache>);
122
123impl PikeVMCache {
124    pub(crate) fn none() -> PikeVMCache {
125        PikeVMCache(None)
126    }
127
128    pub(crate) fn reset(&mut self, builder: &PikeVM) {
129        self.get(&builder.get().0).reset(&builder.get().0);
130    }
131
132    pub(crate) fn memory_usage(&self) -> usize {
133        self.0.as_ref().map_or(0, |c| c.memory_usage())
134    }
135
136    fn get(&mut self, vm: &pikevm::PikeVM) -> &mut pikevm::Cache {
137        self.0.get_or_insert_with(|| vm.create_cache())
138    }
139}
140
141#[derive(Debug)]
142pub(crate) struct BoundedBacktracker(Option<BoundedBacktrackerEngine>);
143
144impl BoundedBacktracker {
145    pub(crate) fn new(
146        info: &RegexInfo,
147        pre: Option<Prefilter>,
148        nfa: &NFA,
149    ) -> Result<BoundedBacktracker, BuildError> {
150        BoundedBacktrackerEngine::new(info, pre, nfa).map(BoundedBacktracker)
151    }
152
153    pub(crate) fn create_cache(&self) -> BoundedBacktrackerCache {
154        BoundedBacktrackerCache::none()
155    }
156
157    #[cfg_attr(feature = "perf-inline", inline(always))]
158    pub(crate) fn get(
159        &self,
160        input: &Input<'_>,
161    ) -> Option<&BoundedBacktrackerEngine> {
162        let engine = self.0.as_ref()?;
163        if input.get_earliest() && input.haystack().len() > 128 {
176            return None;
177        }
178        if input.get_span().len() > engine.max_haystack_len() {
181            return None;
182        }
183        Some(engine)
184    }
185}
186
187#[derive(Debug)]
188pub(crate) struct BoundedBacktrackerEngine(
189    #[cfg(feature = "nfa-backtrack")] backtrack::BoundedBacktracker,
190    #[cfg(not(feature = "nfa-backtrack"))] (),
191);
192
193impl BoundedBacktrackerEngine {
194    pub(crate) fn new(
195        info: &RegexInfo,
196        pre: Option<Prefilter>,
197        nfa: &NFA,
198    ) -> Result<Option<BoundedBacktrackerEngine>, BuildError> {
199        #[cfg(feature = "nfa-backtrack")]
200        {
201            if !info.config().get_backtrack()
202                || info.config().get_match_kind() != MatchKind::LeftmostFirst
203            {
204                return Ok(None);
205            }
206            let backtrack_config = backtrack::Config::new().prefilter(pre);
207            let engine = backtrack::Builder::new()
208                .configure(backtrack_config)
209                .build_from_nfa(nfa.clone())
210                .map_err(BuildError::nfa)?;
211            debug!(
212                "BoundedBacktracker built (max haystack length: {:?})",
213                engine.max_haystack_len()
214            );
215            Ok(Some(BoundedBacktrackerEngine(engine)))
216        }
217        #[cfg(not(feature = "nfa-backtrack"))]
218        {
219            Ok(None)
220        }
221    }
222
223    #[cfg_attr(feature = "perf-inline", inline(always))]
224    pub(crate) fn is_match(
225        &self,
226        cache: &mut BoundedBacktrackerCache,
227        input: &Input<'_>,
228    ) -> bool {
229        #[cfg(feature = "nfa-backtrack")]
230        {
231            self.0.try_is_match(cache.get(&self.0), input.clone()).unwrap()
235        }
236        #[cfg(not(feature = "nfa-backtrack"))]
237        {
238            unreachable!()
241        }
242    }
243
244    #[cfg_attr(feature = "perf-inline", inline(always))]
245    pub(crate) fn search_slots(
246        &self,
247        cache: &mut BoundedBacktrackerCache,
248        input: &Input<'_>,
249        slots: &mut [Option<NonMaxUsize>],
250    ) -> Option<PatternID> {
251        #[cfg(feature = "nfa-backtrack")]
252        {
253            self.0.try_search_slots(cache.get(&self.0), input, slots).unwrap()
257        }
258        #[cfg(not(feature = "nfa-backtrack"))]
259        {
260            unreachable!()
263        }
264    }
265
266    #[cfg_attr(feature = "perf-inline", inline(always))]
267    fn max_haystack_len(&self) -> usize {
268        #[cfg(feature = "nfa-backtrack")]
269        {
270            self.0.max_haystack_len()
271        }
272        #[cfg(not(feature = "nfa-backtrack"))]
273        {
274            unreachable!()
277        }
278    }
279}
280
281#[derive(Clone, Debug)]
282pub(crate) struct BoundedBacktrackerCache(
283    #[cfg(feature = "nfa-backtrack")] Option<backtrack::Cache>,
284    #[cfg(not(feature = "nfa-backtrack"))] (),
285);
286
287impl BoundedBacktrackerCache {
288    pub(crate) fn none() -> BoundedBacktrackerCache {
289        #[cfg(feature = "nfa-backtrack")]
290        {
291            BoundedBacktrackerCache(None)
292        }
293        #[cfg(not(feature = "nfa-backtrack"))]
294        {
295            BoundedBacktrackerCache(())
296        }
297    }
298
299    pub(crate) fn reset(&mut self, builder: &BoundedBacktracker) {
300        #[cfg(feature = "nfa-backtrack")]
301        if let Some(ref e) = builder.0 {
302            self.get(&e.0).reset(&e.0);
303        }
304    }
305
306    pub(crate) fn memory_usage(&self) -> usize {
307        #[cfg(feature = "nfa-backtrack")]
308        {
309            self.0.as_ref().map_or(0, |c| c.memory_usage())
310        }
311        #[cfg(not(feature = "nfa-backtrack"))]
312        {
313            0
314        }
315    }
316
317    #[cfg(feature = "nfa-backtrack")]
318    fn get(
319        &mut self,
320        bb: &backtrack::BoundedBacktracker,
321    ) -> &mut backtrack::Cache {
322        self.0.get_or_insert_with(|| bb.create_cache())
323    }
324}
325
326#[derive(Debug)]
327pub(crate) struct OnePass(Option<OnePassEngine>);
328
329impl OnePass {
330    pub(crate) fn new(info: &RegexInfo, nfa: &NFA) -> OnePass {
331        OnePass(OnePassEngine::new(info, nfa))
332    }
333
334    pub(crate) fn create_cache(&self) -> OnePassCache {
335        OnePassCache::new(self)
336    }
337
338    #[cfg_attr(feature = "perf-inline", inline(always))]
339    pub(crate) fn get(&self, input: &Input<'_>) -> Option<&OnePassEngine> {
340        let engine = self.0.as_ref()?;
341        if !input.get_anchored().is_anchored()
342            && !engine.get_nfa().is_always_start_anchored()
343        {
344            return None;
345        }
346        Some(engine)
347    }
348
349    pub(crate) fn memory_usage(&self) -> usize {
350        self.0.as_ref().map_or(0, |e| e.memory_usage())
351    }
352}
353
354#[derive(Debug)]
355pub(crate) struct OnePassEngine(
356    #[cfg(feature = "dfa-onepass")] onepass::DFA,
357    #[cfg(not(feature = "dfa-onepass"))] (),
358);
359
360impl OnePassEngine {
361    pub(crate) fn new(info: &RegexInfo, nfa: &NFA) -> Option<OnePassEngine> {
362        #[cfg(feature = "dfa-onepass")]
363        {
364            if !info.config().get_onepass() {
365                return None;
366            }
367            if info.props_union().explicit_captures_len() == 0
377                && !info.props_union().look_set().contains_word_unicode()
378            {
379                debug!("not building OnePass because it isn't worth it");
380                return None;
381            }
382            let onepass_config = onepass::Config::new()
383                .match_kind(info.config().get_match_kind())
384                .starts_for_each_pattern(true)
388                .byte_classes(info.config().get_byte_classes())
389                .size_limit(info.config().get_onepass_size_limit());
390            let result = onepass::Builder::new()
391                .configure(onepass_config)
392                .build_from_nfa(nfa.clone());
393            let engine = match result {
394                Ok(engine) => engine,
395                Err(_err) => {
396                    debug!("OnePass failed to build: {_err}");
397                    return None;
398                }
399            };
400            debug!("OnePass built, {} bytes", engine.memory_usage());
401            Some(OnePassEngine(engine))
402        }
403        #[cfg(not(feature = "dfa-onepass"))]
404        {
405            None
406        }
407    }
408
409    #[cfg_attr(feature = "perf-inline", inline(always))]
410    pub(crate) fn search_slots(
411        &self,
412        cache: &mut OnePassCache,
413        input: &Input<'_>,
414        slots: &mut [Option<NonMaxUsize>],
415    ) -> Option<PatternID> {
416        #[cfg(feature = "dfa-onepass")]
417        {
418            self.0
421                .try_search_slots(cache.0.as_mut().unwrap(), input, slots)
422                .unwrap()
423        }
424        #[cfg(not(feature = "dfa-onepass"))]
425        {
426            unreachable!()
429        }
430    }
431
432    pub(crate) fn memory_usage(&self) -> usize {
433        #[cfg(feature = "dfa-onepass")]
434        {
435            self.0.memory_usage()
436        }
437        #[cfg(not(feature = "dfa-onepass"))]
438        {
439            unreachable!()
442        }
443    }
444
445    #[cfg_attr(feature = "perf-inline", inline(always))]
446    fn get_nfa(&self) -> &NFA {
447        #[cfg(feature = "dfa-onepass")]
448        {
449            self.0.get_nfa()
450        }
451        #[cfg(not(feature = "dfa-onepass"))]
452        {
453            unreachable!()
456        }
457    }
458}
459
460#[derive(Clone, Debug)]
461pub(crate) struct OnePassCache(
462    #[cfg(feature = "dfa-onepass")] Option<onepass::Cache>,
463    #[cfg(not(feature = "dfa-onepass"))] (),
464);
465
466impl OnePassCache {
467    pub(crate) fn none() -> OnePassCache {
468        #[cfg(feature = "dfa-onepass")]
469        {
470            OnePassCache(None)
471        }
472        #[cfg(not(feature = "dfa-onepass"))]
473        {
474            OnePassCache(())
475        }
476    }
477
478    pub(crate) fn new(builder: &OnePass) -> OnePassCache {
479        #[cfg(feature = "dfa-onepass")]
480        {
481            OnePassCache(builder.0.as_ref().map(|e| e.0.create_cache()))
482        }
483        #[cfg(not(feature = "dfa-onepass"))]
484        {
485            OnePassCache(())
486        }
487    }
488
489    pub(crate) fn reset(&mut self, builder: &OnePass) {
490        #[cfg(feature = "dfa-onepass")]
491        if let Some(ref e) = builder.0 {
492            self.0.as_mut().unwrap().reset(&e.0);
493        }
494    }
495
496    pub(crate) fn memory_usage(&self) -> usize {
497        #[cfg(feature = "dfa-onepass")]
498        {
499            self.0.as_ref().map_or(0, |c| c.memory_usage())
500        }
501        #[cfg(not(feature = "dfa-onepass"))]
502        {
503            0
504        }
505    }
506}
507
508#[derive(Debug)]
509pub(crate) struct Hybrid(Option<HybridEngine>);
510
511impl Hybrid {
512    pub(crate) fn none() -> Hybrid {
513        Hybrid(None)
514    }
515
516    pub(crate) fn new(
517        info: &RegexInfo,
518        pre: Option<Prefilter>,
519        nfa: &NFA,
520        nfarev: &NFA,
521    ) -> Hybrid {
522        Hybrid(HybridEngine::new(info, pre, nfa, nfarev))
523    }
524
525    pub(crate) fn create_cache(&self) -> HybridCache {
526        HybridCache::new(self)
527    }
528
529    #[cfg_attr(feature = "perf-inline", inline(always))]
530    pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&HybridEngine> {
531        let engine = self.0.as_ref()?;
532        Some(engine)
533    }
534
535    pub(crate) fn is_some(&self) -> bool {
536        self.0.is_some()
537    }
538}
539
540#[derive(Debug)]
541pub(crate) struct HybridEngine(
542    #[cfg(feature = "hybrid")] hybrid::regex::Regex,
543    #[cfg(not(feature = "hybrid"))] (),
544);
545
546impl HybridEngine {
547    pub(crate) fn new(
548        info: &RegexInfo,
549        pre: Option<Prefilter>,
550        nfa: &NFA,
551        nfarev: &NFA,
552    ) -> Option<HybridEngine> {
553        #[cfg(feature = "hybrid")]
554        {
555            if !info.config().get_hybrid() {
556                return None;
557            }
558            let dfa_config = hybrid::dfa::Config::new()
559                .match_kind(info.config().get_match_kind())
560                .prefilter(pre.clone())
561                .starts_for_each_pattern(true)
566                .byte_classes(info.config().get_byte_classes())
567                .unicode_word_boundary(true)
568                .specialize_start_states(pre.is_some())
569                .cache_capacity(info.config().get_hybrid_cache_capacity())
570                .skip_cache_capacity_check(false)
584                .minimum_cache_clear_count(Some(3))
587                .minimum_bytes_per_state(Some(10));
588            let result = hybrid::dfa::Builder::new()
589                .configure(dfa_config.clone())
590                .build_from_nfa(nfa.clone());
591            let fwd = match result {
592                Ok(fwd) => fwd,
593                Err(_err) => {
594                    debug!("forward lazy DFA failed to build: {_err}");
595                    return None;
596                }
597            };
598            let result = hybrid::dfa::Builder::new()
599                .configure(
600                    dfa_config
601                        .clone()
602                        .match_kind(MatchKind::All)
603                        .prefilter(None)
604                        .specialize_start_states(false),
605                )
606                .build_from_nfa(nfarev.clone());
607            let rev = match result {
608                Ok(rev) => rev,
609                Err(_err) => {
610                    debug!("reverse lazy DFA failed to build: {_err}");
611                    return None;
612                }
613            };
614            let engine =
615                hybrid::regex::Builder::new().build_from_dfas(fwd, rev);
616            debug!("lazy DFA built");
617            Some(HybridEngine(engine))
618        }
619        #[cfg(not(feature = "hybrid"))]
620        {
621            None
622        }
623    }
624
625    #[cfg_attr(feature = "perf-inline", inline(always))]
626    pub(crate) fn try_search(
627        &self,
628        cache: &mut HybridCache,
629        input: &Input<'_>,
630    ) -> Result<Option<Match>, RetryFailError> {
631        #[cfg(feature = "hybrid")]
632        {
633            let cache = cache.0.as_mut().unwrap();
634            self.0.try_search(cache, input).map_err(|e| e.into())
635        }
636        #[cfg(not(feature = "hybrid"))]
637        {
638            unreachable!()
641        }
642    }
643
644    #[cfg_attr(feature = "perf-inline", inline(always))]
645    pub(crate) fn try_search_half_fwd(
646        &self,
647        cache: &mut HybridCache,
648        input: &Input<'_>,
649    ) -> Result<Option<HalfMatch>, RetryFailError> {
650        #[cfg(feature = "hybrid")]
651        {
652            let fwd = self.0.forward();
653            let mut fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
654            fwd.try_search_fwd(&mut fwdcache, input).map_err(|e| e.into())
655        }
656        #[cfg(not(feature = "hybrid"))]
657        {
658            unreachable!()
661        }
662    }
663
664    #[cfg_attr(feature = "perf-inline", inline(always))]
665    pub(crate) fn try_search_half_fwd_stopat(
666        &self,
667        cache: &mut HybridCache,
668        input: &Input<'_>,
669    ) -> Result<Result<HalfMatch, usize>, RetryFailError> {
670        #[cfg(feature = "hybrid")]
671        {
672            let dfa = self.0.forward();
673            let mut cache = cache.0.as_mut().unwrap().as_parts_mut().0;
674            crate::meta::stopat::hybrid_try_search_half_fwd(
675                dfa, &mut cache, input,
676            )
677        }
678        #[cfg(not(feature = "hybrid"))]
679        {
680            unreachable!()
683        }
684    }
685
686    #[cfg_attr(feature = "perf-inline", inline(always))]
687    pub(crate) fn try_search_half_rev(
688        &self,
689        cache: &mut HybridCache,
690        input: &Input<'_>,
691    ) -> Result<Option<HalfMatch>, RetryFailError> {
692        #[cfg(feature = "hybrid")]
693        {
694            let rev = self.0.reverse();
695            let mut revcache = cache.0.as_mut().unwrap().as_parts_mut().1;
696            rev.try_search_rev(&mut revcache, input).map_err(|e| e.into())
697        }
698        #[cfg(not(feature = "hybrid"))]
699        {
700            unreachable!()
703        }
704    }
705
706    #[cfg_attr(feature = "perf-inline", inline(always))]
707    pub(crate) fn try_search_half_rev_limited(
708        &self,
709        cache: &mut HybridCache,
710        input: &Input<'_>,
711        min_start: usize,
712    ) -> Result<Option<HalfMatch>, RetryError> {
713        #[cfg(feature = "hybrid")]
714        {
715            let dfa = self.0.reverse();
716            let mut cache = cache.0.as_mut().unwrap().as_parts_mut().1;
717            crate::meta::limited::hybrid_try_search_half_rev(
718                dfa, &mut cache, input, min_start,
719            )
720        }
721        #[cfg(not(feature = "hybrid"))]
722        {
723            unreachable!()
726        }
727    }
728
729    #[inline]
730    pub(crate) fn try_which_overlapping_matches(
731        &self,
732        cache: &mut HybridCache,
733        input: &Input<'_>,
734        patset: &mut PatternSet,
735    ) -> Result<(), RetryFailError> {
736        #[cfg(feature = "hybrid")]
737        {
738            let fwd = self.0.forward();
739            let mut fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
740            fwd.try_which_overlapping_matches(&mut fwdcache, input, patset)
741                .map_err(|e| e.into())
742        }
743        #[cfg(not(feature = "hybrid"))]
744        {
745            unreachable!()
748        }
749    }
750}
751
752#[derive(Clone, Debug)]
753pub(crate) struct HybridCache(
754    #[cfg(feature = "hybrid")] Option<hybrid::regex::Cache>,
755    #[cfg(not(feature = "hybrid"))] (),
756);
757
758impl HybridCache {
759    pub(crate) fn none() -> HybridCache {
760        #[cfg(feature = "hybrid")]
761        {
762            HybridCache(None)
763        }
764        #[cfg(not(feature = "hybrid"))]
765        {
766            HybridCache(())
767        }
768    }
769
770    pub(crate) fn new(builder: &Hybrid) -> HybridCache {
771        #[cfg(feature = "hybrid")]
772        {
773            HybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
774        }
775        #[cfg(not(feature = "hybrid"))]
776        {
777            HybridCache(())
778        }
779    }
780
781    pub(crate) fn reset(&mut self, builder: &Hybrid) {
782        #[cfg(feature = "hybrid")]
783        if let Some(ref e) = builder.0 {
784            self.0.as_mut().unwrap().reset(&e.0);
785        }
786    }
787
788    pub(crate) fn memory_usage(&self) -> usize {
789        #[cfg(feature = "hybrid")]
790        {
791            self.0.as_ref().map_or(0, |c| c.memory_usage())
792        }
793        #[cfg(not(feature = "hybrid"))]
794        {
795            0
796        }
797    }
798}
799
800#[derive(Debug)]
801pub(crate) struct DFA(Option<DFAEngine>);
802
803impl DFA {
804    pub(crate) fn none() -> DFA {
805        DFA(None)
806    }
807
808    pub(crate) fn new(
809        info: &RegexInfo,
810        pre: Option<Prefilter>,
811        nfa: &NFA,
812        nfarev: &NFA,
813    ) -> DFA {
814        DFA(DFAEngine::new(info, pre, nfa, nfarev))
815    }
816
817    #[cfg_attr(feature = "perf-inline", inline(always))]
818    pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&DFAEngine> {
819        let engine = self.0.as_ref()?;
820        Some(engine)
821    }
822
823    pub(crate) fn is_some(&self) -> bool {
824        self.0.is_some()
825    }
826
827    pub(crate) fn memory_usage(&self) -> usize {
828        self.0.as_ref().map_or(0, |e| e.memory_usage())
829    }
830}
831
832#[derive(Debug)]
833pub(crate) struct DFAEngine(
834    #[cfg(feature = "dfa-build")] dfa::regex::Regex,
835    #[cfg(not(feature = "dfa-build"))] (),
836);
837
838impl DFAEngine {
839    pub(crate) fn new(
840        info: &RegexInfo,
841        pre: Option<Prefilter>,
842        nfa: &NFA,
843        nfarev: &NFA,
844    ) -> Option<DFAEngine> {
845        #[cfg(feature = "dfa-build")]
846        {
847            if !info.config().get_dfa() {
848                return None;
849            }
850            if let Some(state_limit) = info.config().get_dfa_state_limit() {
852                if nfa.states().len() > state_limit {
853                    debug!(
854                        "skipping full DFA because NFA has {} states, \
855                         which exceeds the heuristic limit of {}",
856                        nfa.states().len(),
857                        state_limit,
858                    );
859                    return None;
860                }
861            }
862            let size_limit = info.config().get_dfa_size_limit().map(|n| n / 4);
868            let dfa_config = dfa::dense::Config::new()
869                .match_kind(info.config().get_match_kind())
870                .prefilter(pre.clone())
871                .starts_for_each_pattern(true)
877                .byte_classes(info.config().get_byte_classes())
878                .unicode_word_boundary(true)
879                .specialize_start_states(pre.is_some())
880                .determinize_size_limit(size_limit)
881                .dfa_size_limit(size_limit);
882            let result = dfa::dense::Builder::new()
883                .configure(dfa_config.clone())
884                .build_from_nfa(&nfa);
885            let fwd = match result {
886                Ok(fwd) => fwd,
887                Err(_err) => {
888                    debug!("forward full DFA failed to build: {_err}");
889                    return None;
890                }
891            };
892            let result = dfa::dense::Builder::new()
893                .configure(
894                    dfa_config
895                        .clone()
896                        .start_kind(dfa::StartKind::Anchored)
904                        .match_kind(MatchKind::All)
905                        .prefilter(None)
906                        .specialize_start_states(false),
907                )
908                .build_from_nfa(&nfarev);
909            let rev = match result {
910                Ok(rev) => rev,
911                Err(_err) => {
912                    debug!("reverse full DFA failed to build: {_err}");
913                    return None;
914                }
915            };
916            let engine = dfa::regex::Builder::new().build_from_dfas(fwd, rev);
917            debug!(
918                "fully compiled forward and reverse DFAs built, {} bytes",
919                engine.forward().memory_usage()
920                    + engine.reverse().memory_usage(),
921            );
922            Some(DFAEngine(engine))
923        }
924        #[cfg(not(feature = "dfa-build"))]
925        {
926            None
927        }
928    }
929
930    #[cfg_attr(feature = "perf-inline", inline(always))]
931    pub(crate) fn try_search(
932        &self,
933        input: &Input<'_>,
934    ) -> Result<Option<Match>, RetryFailError> {
935        #[cfg(feature = "dfa-build")]
936        {
937            self.0.try_search(input).map_err(|e| e.into())
938        }
939        #[cfg(not(feature = "dfa-build"))]
940        {
941            unreachable!()
944        }
945    }
946
947    #[cfg_attr(feature = "perf-inline", inline(always))]
948    pub(crate) fn try_search_half_fwd(
949        &self,
950        input: &Input<'_>,
951    ) -> Result<Option<HalfMatch>, RetryFailError> {
952        #[cfg(feature = "dfa-build")]
953        {
954            use crate::dfa::Automaton;
955            self.0.forward().try_search_fwd(input).map_err(|e| e.into())
956        }
957        #[cfg(not(feature = "dfa-build"))]
958        {
959            unreachable!()
962        }
963    }
964
965    #[cfg_attr(feature = "perf-inline", inline(always))]
966    pub(crate) fn try_search_half_fwd_stopat(
967        &self,
968        input: &Input<'_>,
969    ) -> Result<Result<HalfMatch, usize>, RetryFailError> {
970        #[cfg(feature = "dfa-build")]
971        {
972            let dfa = self.0.forward();
973            crate::meta::stopat::dfa_try_search_half_fwd(dfa, input)
974        }
975        #[cfg(not(feature = "dfa-build"))]
976        {
977            unreachable!()
980        }
981    }
982
983    #[cfg_attr(feature = "perf-inline", inline(always))]
984    pub(crate) fn try_search_half_rev(
985        &self,
986        input: &Input<'_>,
987    ) -> Result<Option<HalfMatch>, RetryFailError> {
988        #[cfg(feature = "dfa-build")]
989        {
990            use crate::dfa::Automaton;
991            self.0.reverse().try_search_rev(&input).map_err(|e| e.into())
992        }
993        #[cfg(not(feature = "dfa-build"))]
994        {
995            unreachable!()
998        }
999    }
1000
1001    #[cfg_attr(feature = "perf-inline", inline(always))]
1002    pub(crate) fn try_search_half_rev_limited(
1003        &self,
1004        input: &Input<'_>,
1005        min_start: usize,
1006    ) -> Result<Option<HalfMatch>, RetryError> {
1007        #[cfg(feature = "dfa-build")]
1008        {
1009            let dfa = self.0.reverse();
1010            crate::meta::limited::dfa_try_search_half_rev(
1011                dfa, input, min_start,
1012            )
1013        }
1014        #[cfg(not(feature = "dfa-build"))]
1015        {
1016            unreachable!()
1019        }
1020    }
1021
1022    #[inline]
1023    pub(crate) fn try_which_overlapping_matches(
1024        &self,
1025        input: &Input<'_>,
1026        patset: &mut PatternSet,
1027    ) -> Result<(), RetryFailError> {
1028        #[cfg(feature = "dfa-build")]
1029        {
1030            use crate::dfa::Automaton;
1031            self.0
1032                .forward()
1033                .try_which_overlapping_matches(input, patset)
1034                .map_err(|e| e.into())
1035        }
1036        #[cfg(not(feature = "dfa-build"))]
1037        {
1038            unreachable!()
1041        }
1042    }
1043
1044    pub(crate) fn memory_usage(&self) -> usize {
1045        #[cfg(feature = "dfa-build")]
1046        {
1047            self.0.forward().memory_usage() + self.0.reverse().memory_usage()
1048        }
1049        #[cfg(not(feature = "dfa-build"))]
1050        {
1051            unreachable!()
1054        }
1055    }
1056}
1057
1058#[derive(Debug)]
1059pub(crate) struct ReverseHybrid(Option<ReverseHybridEngine>);
1060
1061impl ReverseHybrid {
1062    pub(crate) fn none() -> ReverseHybrid {
1063        ReverseHybrid(None)
1064    }
1065
1066    pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseHybrid {
1067        ReverseHybrid(ReverseHybridEngine::new(info, nfarev))
1068    }
1069
1070    pub(crate) fn create_cache(&self) -> ReverseHybridCache {
1071        ReverseHybridCache::new(self)
1072    }
1073
1074    #[cfg_attr(feature = "perf-inline", inline(always))]
1075    pub(crate) fn get(
1076        &self,
1077        _input: &Input<'_>,
1078    ) -> Option<&ReverseHybridEngine> {
1079        let engine = self.0.as_ref()?;
1080        Some(engine)
1081    }
1082}
1083
1084#[derive(Debug)]
1085pub(crate) struct ReverseHybridEngine(
1086    #[cfg(feature = "hybrid")] hybrid::dfa::DFA,
1087    #[cfg(not(feature = "hybrid"))] (),
1088);
1089
1090impl ReverseHybridEngine {
1091    pub(crate) fn new(
1092        info: &RegexInfo,
1093        nfarev: &NFA,
1094    ) -> Option<ReverseHybridEngine> {
1095        #[cfg(feature = "hybrid")]
1096        {
1097            if !info.config().get_hybrid() {
1098                return None;
1099            }
1100            let dfa_config = hybrid::dfa::Config::new()
1104                .match_kind(MatchKind::All)
1105                .prefilter(None)
1106                .starts_for_each_pattern(false)
1107                .byte_classes(info.config().get_byte_classes())
1108                .unicode_word_boundary(true)
1109                .specialize_start_states(false)
1110                .cache_capacity(info.config().get_hybrid_cache_capacity())
1111                .skip_cache_capacity_check(false)
1112                .minimum_cache_clear_count(Some(3))
1113                .minimum_bytes_per_state(Some(10));
1114            let result = hybrid::dfa::Builder::new()
1115                .configure(dfa_config)
1116                .build_from_nfa(nfarev.clone());
1117            let rev = match result {
1118                Ok(rev) => rev,
1119                Err(_err) => {
1120                    debug!("lazy reverse DFA failed to build: {_err}");
1121                    return None;
1122                }
1123            };
1124            debug!("lazy reverse DFA built");
1125            Some(ReverseHybridEngine(rev))
1126        }
1127        #[cfg(not(feature = "hybrid"))]
1128        {
1129            None
1130        }
1131    }
1132
1133    #[cfg_attr(feature = "perf-inline", inline(always))]
1134    pub(crate) fn try_search_half_rev_limited(
1135        &self,
1136        cache: &mut ReverseHybridCache,
1137        input: &Input<'_>,
1138        min_start: usize,
1139    ) -> Result<Option<HalfMatch>, RetryError> {
1140        #[cfg(feature = "hybrid")]
1141        {
1142            let dfa = &self.0;
1143            let mut cache = cache.0.as_mut().unwrap();
1144            crate::meta::limited::hybrid_try_search_half_rev(
1145                dfa, &mut cache, input, min_start,
1146            )
1147        }
1148        #[cfg(not(feature = "hybrid"))]
1149        {
1150            unreachable!()
1153        }
1154    }
1155}
1156
1157#[derive(Clone, Debug)]
1158pub(crate) struct ReverseHybridCache(
1159    #[cfg(feature = "hybrid")] Option<hybrid::dfa::Cache>,
1160    #[cfg(not(feature = "hybrid"))] (),
1161);
1162
1163impl ReverseHybridCache {
1164    pub(crate) fn none() -> ReverseHybridCache {
1165        #[cfg(feature = "hybrid")]
1166        {
1167            ReverseHybridCache(None)
1168        }
1169        #[cfg(not(feature = "hybrid"))]
1170        {
1171            ReverseHybridCache(())
1172        }
1173    }
1174
1175    pub(crate) fn new(builder: &ReverseHybrid) -> ReverseHybridCache {
1176        #[cfg(feature = "hybrid")]
1177        {
1178            ReverseHybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
1179        }
1180        #[cfg(not(feature = "hybrid"))]
1181        {
1182            ReverseHybridCache(())
1183        }
1184    }
1185
1186    pub(crate) fn reset(&mut self, builder: &ReverseHybrid) {
1187        #[cfg(feature = "hybrid")]
1188        if let Some(ref e) = builder.0 {
1189            self.0.as_mut().unwrap().reset(&e.0);
1190        }
1191    }
1192
1193    pub(crate) fn memory_usage(&self) -> usize {
1194        #[cfg(feature = "hybrid")]
1195        {
1196            self.0.as_ref().map_or(0, |c| c.memory_usage())
1197        }
1198        #[cfg(not(feature = "hybrid"))]
1199        {
1200            0
1201        }
1202    }
1203}
1204
1205#[derive(Debug)]
1206pub(crate) struct ReverseDFA(Option<ReverseDFAEngine>);
1207
1208impl ReverseDFA {
1209    pub(crate) fn none() -> ReverseDFA {
1210        ReverseDFA(None)
1211    }
1212
1213    pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseDFA {
1214        ReverseDFA(ReverseDFAEngine::new(info, nfarev))
1215    }
1216
1217    #[cfg_attr(feature = "perf-inline", inline(always))]
1218    pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&ReverseDFAEngine> {
1219        let engine = self.0.as_ref()?;
1220        Some(engine)
1221    }
1222
1223    pub(crate) fn is_some(&self) -> bool {
1224        self.0.is_some()
1225    }
1226
1227    pub(crate) fn memory_usage(&self) -> usize {
1228        self.0.as_ref().map_or(0, |e| e.memory_usage())
1229    }
1230}
1231
1232#[derive(Debug)]
1233pub(crate) struct ReverseDFAEngine(
1234    #[cfg(feature = "dfa-build")] dfa::dense::DFA<Vec<u32>>,
1235    #[cfg(not(feature = "dfa-build"))] (),
1236);
1237
1238impl ReverseDFAEngine {
1239    pub(crate) fn new(
1240        info: &RegexInfo,
1241        nfarev: &NFA,
1242    ) -> Option<ReverseDFAEngine> {
1243        #[cfg(feature = "dfa-build")]
1244        {
1245            if !info.config().get_dfa() {
1246                return None;
1247            }
1248            if let Some(state_limit) = info.config().get_dfa_state_limit() {
1250                if nfarev.states().len() > state_limit {
1251                    debug!(
1252                        "skipping full reverse DFA because NFA has {} states, \
1253                         which exceeds the heuristic limit of {}",
1254                        nfarev.states().len(),
1255                        state_limit,
1256					);
1257                    return None;
1258                }
1259            }
1260            let size_limit = info.config().get_dfa_size_limit().map(|n| n / 2);
1265            let dfa_config = dfa::dense::Config::new()
1271                .match_kind(MatchKind::All)
1272                .prefilter(None)
1273                .accelerate(false)
1274                .start_kind(dfa::StartKind::Anchored)
1275                .starts_for_each_pattern(false)
1276                .byte_classes(info.config().get_byte_classes())
1277                .unicode_word_boundary(true)
1278                .specialize_start_states(false)
1279                .determinize_size_limit(size_limit)
1280                .dfa_size_limit(size_limit);
1281            let result = dfa::dense::Builder::new()
1282                .configure(dfa_config)
1283                .build_from_nfa(&nfarev);
1284            let rev = match result {
1285                Ok(rev) => rev,
1286                Err(_err) => {
1287                    debug!("full reverse DFA failed to build: {_err}");
1288                    return None;
1289                }
1290            };
1291            debug!(
1292                "fully compiled reverse DFA built, {} bytes",
1293                rev.memory_usage()
1294            );
1295            Some(ReverseDFAEngine(rev))
1296        }
1297        #[cfg(not(feature = "dfa-build"))]
1298        {
1299            None
1300        }
1301    }
1302
1303    #[cfg_attr(feature = "perf-inline", inline(always))]
1304    pub(crate) fn try_search_half_rev_limited(
1305        &self,
1306        input: &Input<'_>,
1307        min_start: usize,
1308    ) -> Result<Option<HalfMatch>, RetryError> {
1309        #[cfg(feature = "dfa-build")]
1310        {
1311            let dfa = &self.0;
1312            crate::meta::limited::dfa_try_search_half_rev(
1313                dfa, input, min_start,
1314            )
1315        }
1316        #[cfg(not(feature = "dfa-build"))]
1317        {
1318            unreachable!()
1321        }
1322    }
1323
1324    pub(crate) fn memory_usage(&self) -> usize {
1325        #[cfg(feature = "dfa-build")]
1326        {
1327            self.0.memory_usage()
1328        }
1329        #[cfg(not(feature = "dfa-build"))]
1330        {
1331            unreachable!()
1334        }
1335    }
1336}