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}