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::new(self)
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.0.as_mut().unwrap(), 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.0.as_mut().unwrap(), 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(
117 cache.0.as_mut().unwrap(),
118 input,
119 patset,
120 )
121 }
122}
123
124#[derive(Clone, Debug)]
125pub(crate) struct PikeVMCache(Option<pikevm::Cache>);
126
127impl PikeVMCache {
128 pub(crate) fn none() -> PikeVMCache {
129 PikeVMCache(None)
130 }
131
132 pub(crate) fn new(builder: &PikeVM) -> PikeVMCache {
133 PikeVMCache(Some(builder.get().0.create_cache()))
134 }
135
136 pub(crate) fn reset(&mut self, builder: &PikeVM) {
137 self.0.as_mut().unwrap().reset(&builder.get().0);
138 }
139
140 pub(crate) fn memory_usage(&self) -> usize {
141 self.0.as_ref().map_or(0, |c| c.memory_usage())
142 }
143}
144
145#[derive(Debug)]
146pub(crate) struct BoundedBacktracker(Option<BoundedBacktrackerEngine>);
147
148impl BoundedBacktracker {
149 pub(crate) fn new(
150 info: &RegexInfo,
151 pre: Option<Prefilter>,
152 nfa: &NFA,
153 ) -> Result<BoundedBacktracker, BuildError> {
154 BoundedBacktrackerEngine::new(info, pre, nfa).map(BoundedBacktracker)
155 }
156
157 pub(crate) fn create_cache(&self) -> BoundedBacktrackerCache {
158 BoundedBacktrackerCache::new(self)
159 }
160
161 #[cfg_attr(feature = "perf-inline", inline(always))]
162 pub(crate) fn get(
163 &self,
164 input: &Input<'_>,
165 ) -> Option<&BoundedBacktrackerEngine> {
166 let engine = self.0.as_ref()?;
167 if input.get_earliest() && input.haystack().len() > 128 {
180 return None;
181 }
182 if input.get_span().len() > engine.max_haystack_len() {
185 return None;
186 }
187 Some(engine)
188 }
189}
190
191#[derive(Debug)]
192pub(crate) struct BoundedBacktrackerEngine(
193 #[cfg(feature = "nfa-backtrack")] backtrack::BoundedBacktracker,
194 #[cfg(not(feature = "nfa-backtrack"))] (),
195);
196
197impl BoundedBacktrackerEngine {
198 pub(crate) fn new(
199 info: &RegexInfo,
200 pre: Option<Prefilter>,
201 nfa: &NFA,
202 ) -> Result<Option<BoundedBacktrackerEngine>, BuildError> {
203 #[cfg(feature = "nfa-backtrack")]
204 {
205 if !info.config().get_backtrack()
206 || info.config().get_match_kind() != MatchKind::LeftmostFirst
207 {
208 return Ok(None);
209 }
210 let backtrack_config = backtrack::Config::new().prefilter(pre);
211 let engine = backtrack::Builder::new()
212 .configure(backtrack_config)
213 .build_from_nfa(nfa.clone())
214 .map_err(BuildError::nfa)?;
215 debug!(
216 "BoundedBacktracker built (max haystack length: {:?})",
217 engine.max_haystack_len()
218 );
219 Ok(Some(BoundedBacktrackerEngine(engine)))
220 }
221 #[cfg(not(feature = "nfa-backtrack"))]
222 {
223 Ok(None)
224 }
225 }
226
227 #[cfg_attr(feature = "perf-inline", inline(always))]
228 pub(crate) fn is_match(
229 &self,
230 cache: &mut BoundedBacktrackerCache,
231 input: &Input<'_>,
232 ) -> bool {
233 #[cfg(feature = "nfa-backtrack")]
234 {
235 self.0
239 .try_is_match(cache.0.as_mut().unwrap(), input.clone())
240 .unwrap()
241 }
242 #[cfg(not(feature = "nfa-backtrack"))]
243 {
244 unreachable!()
247 }
248 }
249
250 #[cfg_attr(feature = "perf-inline", inline(always))]
251 pub(crate) fn search_slots(
252 &self,
253 cache: &mut BoundedBacktrackerCache,
254 input: &Input<'_>,
255 slots: &mut [Option<NonMaxUsize>],
256 ) -> Option<PatternID> {
257 #[cfg(feature = "nfa-backtrack")]
258 {
259 self.0
263 .try_search_slots(cache.0.as_mut().unwrap(), input, slots)
264 .unwrap()
265 }
266 #[cfg(not(feature = "nfa-backtrack"))]
267 {
268 unreachable!()
271 }
272 }
273
274 #[cfg_attr(feature = "perf-inline", inline(always))]
275 fn max_haystack_len(&self) -> usize {
276 #[cfg(feature = "nfa-backtrack")]
277 {
278 self.0.max_haystack_len()
279 }
280 #[cfg(not(feature = "nfa-backtrack"))]
281 {
282 unreachable!()
285 }
286 }
287}
288
289#[derive(Clone, Debug)]
290pub(crate) struct BoundedBacktrackerCache(
291 #[cfg(feature = "nfa-backtrack")] Option<backtrack::Cache>,
292 #[cfg(not(feature = "nfa-backtrack"))] (),
293);
294
295impl BoundedBacktrackerCache {
296 pub(crate) fn none() -> BoundedBacktrackerCache {
297 #[cfg(feature = "nfa-backtrack")]
298 {
299 BoundedBacktrackerCache(None)
300 }
301 #[cfg(not(feature = "nfa-backtrack"))]
302 {
303 BoundedBacktrackerCache(())
304 }
305 }
306
307 pub(crate) fn new(
308 builder: &BoundedBacktracker,
309 ) -> BoundedBacktrackerCache {
310 #[cfg(feature = "nfa-backtrack")]
311 {
312 BoundedBacktrackerCache(
313 builder.0.as_ref().map(|e| e.0.create_cache()),
314 )
315 }
316 #[cfg(not(feature = "nfa-backtrack"))]
317 {
318 BoundedBacktrackerCache(())
319 }
320 }
321
322 pub(crate) fn reset(&mut self, builder: &BoundedBacktracker) {
323 #[cfg(feature = "nfa-backtrack")]
324 if let Some(ref e) = builder.0 {
325 self.0.as_mut().unwrap().reset(&e.0);
326 }
327 }
328
329 pub(crate) fn memory_usage(&self) -> usize {
330 #[cfg(feature = "nfa-backtrack")]
331 {
332 self.0.as_ref().map_or(0, |c| c.memory_usage())
333 }
334 #[cfg(not(feature = "nfa-backtrack"))]
335 {
336 0
337 }
338 }
339}
340
341#[derive(Debug)]
342pub(crate) struct OnePass(Option<OnePassEngine>);
343
344impl OnePass {
345 pub(crate) fn new(info: &RegexInfo, nfa: &NFA) -> OnePass {
346 OnePass(OnePassEngine::new(info, nfa))
347 }
348
349 pub(crate) fn create_cache(&self) -> OnePassCache {
350 OnePassCache::new(self)
351 }
352
353 #[cfg_attr(feature = "perf-inline", inline(always))]
354 pub(crate) fn get(&self, input: &Input<'_>) -> Option<&OnePassEngine> {
355 let engine = self.0.as_ref()?;
356 if !input.get_anchored().is_anchored()
357 && !engine.get_nfa().is_always_start_anchored()
358 {
359 return None;
360 }
361 Some(engine)
362 }
363
364 pub(crate) fn memory_usage(&self) -> usize {
365 self.0.as_ref().map_or(0, |e| e.memory_usage())
366 }
367}
368
369#[derive(Debug)]
370pub(crate) struct OnePassEngine(
371 #[cfg(feature = "dfa-onepass")] onepass::DFA,
372 #[cfg(not(feature = "dfa-onepass"))] (),
373);
374
375impl OnePassEngine {
376 pub(crate) fn new(info: &RegexInfo, nfa: &NFA) -> Option<OnePassEngine> {
377 #[cfg(feature = "dfa-onepass")]
378 {
379 if !info.config().get_onepass() {
380 return None;
381 }
382 if info.props_union().explicit_captures_len() == 0
392 && !info.props_union().look_set().contains_word_unicode()
393 {
394 debug!("not building OnePass because it isn't worth it");
395 return None;
396 }
397 let onepass_config = onepass::Config::new()
398 .match_kind(info.config().get_match_kind())
399 .starts_for_each_pattern(true)
403 .byte_classes(info.config().get_byte_classes())
404 .size_limit(info.config().get_onepass_size_limit());
405 let result = onepass::Builder::new()
406 .configure(onepass_config)
407 .build_from_nfa(nfa.clone());
408 let engine = match result {
409 Ok(engine) => engine,
410 Err(_err) => {
411 debug!("OnePass failed to build: {}", _err);
412 return None;
413 }
414 };
415 debug!("OnePass built, {} bytes", engine.memory_usage());
416 Some(OnePassEngine(engine))
417 }
418 #[cfg(not(feature = "dfa-onepass"))]
419 {
420 None
421 }
422 }
423
424 #[cfg_attr(feature = "perf-inline", inline(always))]
425 pub(crate) fn search_slots(
426 &self,
427 cache: &mut OnePassCache,
428 input: &Input<'_>,
429 slots: &mut [Option<NonMaxUsize>],
430 ) -> Option<PatternID> {
431 #[cfg(feature = "dfa-onepass")]
432 {
433 self.0
436 .try_search_slots(cache.0.as_mut().unwrap(), input, slots)
437 .unwrap()
438 }
439 #[cfg(not(feature = "dfa-onepass"))]
440 {
441 unreachable!()
444 }
445 }
446
447 pub(crate) fn memory_usage(&self) -> usize {
448 #[cfg(feature = "dfa-onepass")]
449 {
450 self.0.memory_usage()
451 }
452 #[cfg(not(feature = "dfa-onepass"))]
453 {
454 unreachable!()
457 }
458 }
459
460 #[cfg_attr(feature = "perf-inline", inline(always))]
461 fn get_nfa(&self) -> &NFA {
462 #[cfg(feature = "dfa-onepass")]
463 {
464 self.0.get_nfa()
465 }
466 #[cfg(not(feature = "dfa-onepass"))]
467 {
468 unreachable!()
471 }
472 }
473}
474
475#[derive(Clone, Debug)]
476pub(crate) struct OnePassCache(
477 #[cfg(feature = "dfa-onepass")] Option<onepass::Cache>,
478 #[cfg(not(feature = "dfa-onepass"))] (),
479);
480
481impl OnePassCache {
482 pub(crate) fn none() -> OnePassCache {
483 #[cfg(feature = "dfa-onepass")]
484 {
485 OnePassCache(None)
486 }
487 #[cfg(not(feature = "dfa-onepass"))]
488 {
489 OnePassCache(())
490 }
491 }
492
493 pub(crate) fn new(builder: &OnePass) -> OnePassCache {
494 #[cfg(feature = "dfa-onepass")]
495 {
496 OnePassCache(builder.0.as_ref().map(|e| e.0.create_cache()))
497 }
498 #[cfg(not(feature = "dfa-onepass"))]
499 {
500 OnePassCache(())
501 }
502 }
503
504 pub(crate) fn reset(&mut self, builder: &OnePass) {
505 #[cfg(feature = "dfa-onepass")]
506 if let Some(ref e) = builder.0 {
507 self.0.as_mut().unwrap().reset(&e.0);
508 }
509 }
510
511 pub(crate) fn memory_usage(&self) -> usize {
512 #[cfg(feature = "dfa-onepass")]
513 {
514 self.0.as_ref().map_or(0, |c| c.memory_usage())
515 }
516 #[cfg(not(feature = "dfa-onepass"))]
517 {
518 0
519 }
520 }
521}
522
523#[derive(Debug)]
524pub(crate) struct Hybrid(Option<HybridEngine>);
525
526impl Hybrid {
527 pub(crate) fn none() -> Hybrid {
528 Hybrid(None)
529 }
530
531 pub(crate) fn new(
532 info: &RegexInfo,
533 pre: Option<Prefilter>,
534 nfa: &NFA,
535 nfarev: &NFA,
536 ) -> Hybrid {
537 Hybrid(HybridEngine::new(info, pre, nfa, nfarev))
538 }
539
540 pub(crate) fn create_cache(&self) -> HybridCache {
541 HybridCache::new(self)
542 }
543
544 #[cfg_attr(feature = "perf-inline", inline(always))]
545 pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&HybridEngine> {
546 let engine = self.0.as_ref()?;
547 Some(engine)
548 }
549
550 pub(crate) fn is_some(&self) -> bool {
551 self.0.is_some()
552 }
553}
554
555#[derive(Debug)]
556pub(crate) struct HybridEngine(
557 #[cfg(feature = "hybrid")] hybrid::regex::Regex,
558 #[cfg(not(feature = "hybrid"))] (),
559);
560
561impl HybridEngine {
562 pub(crate) fn new(
563 info: &RegexInfo,
564 pre: Option<Prefilter>,
565 nfa: &NFA,
566 nfarev: &NFA,
567 ) -> Option<HybridEngine> {
568 #[cfg(feature = "hybrid")]
569 {
570 if !info.config().get_hybrid() {
571 return None;
572 }
573 let dfa_config = hybrid::dfa::Config::new()
574 .match_kind(info.config().get_match_kind())
575 .prefilter(pre.clone())
576 .starts_for_each_pattern(true)
581 .byte_classes(info.config().get_byte_classes())
582 .unicode_word_boundary(true)
583 .specialize_start_states(pre.is_some())
584 .cache_capacity(info.config().get_hybrid_cache_capacity())
585 .skip_cache_capacity_check(false)
599 .minimum_cache_clear_count(Some(3))
602 .minimum_bytes_per_state(Some(10));
603 let result = hybrid::dfa::Builder::new()
604 .configure(dfa_config.clone())
605 .build_from_nfa(nfa.clone());
606 let fwd = match result {
607 Ok(fwd) => fwd,
608 Err(_err) => {
609 debug!("forward lazy DFA failed to build: {}", _err);
610 return None;
611 }
612 };
613 let result = hybrid::dfa::Builder::new()
614 .configure(
615 dfa_config
616 .clone()
617 .match_kind(MatchKind::All)
618 .prefilter(None)
619 .specialize_start_states(false),
620 )
621 .build_from_nfa(nfarev.clone());
622 let rev = match result {
623 Ok(rev) => rev,
624 Err(_err) => {
625 debug!("reverse lazy DFA failed to build: {}", _err);
626 return None;
627 }
628 };
629 let engine =
630 hybrid::regex::Builder::new().build_from_dfas(fwd, rev);
631 debug!("lazy DFA built");
632 Some(HybridEngine(engine))
633 }
634 #[cfg(not(feature = "hybrid"))]
635 {
636 None
637 }
638 }
639
640 #[cfg_attr(feature = "perf-inline", inline(always))]
641 pub(crate) fn try_search(
642 &self,
643 cache: &mut HybridCache,
644 input: &Input<'_>,
645 ) -> Result<Option<Match>, RetryFailError> {
646 #[cfg(feature = "hybrid")]
647 {
648 let cache = cache.0.as_mut().unwrap();
649 self.0.try_search(cache, input).map_err(|e| e.into())
650 }
651 #[cfg(not(feature = "hybrid"))]
652 {
653 unreachable!()
656 }
657 }
658
659 #[cfg_attr(feature = "perf-inline", inline(always))]
660 pub(crate) fn try_search_half_fwd(
661 &self,
662 cache: &mut HybridCache,
663 input: &Input<'_>,
664 ) -> Result<Option<HalfMatch>, RetryFailError> {
665 #[cfg(feature = "hybrid")]
666 {
667 let fwd = self.0.forward();
668 let mut fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
669 fwd.try_search_fwd(&mut fwdcache, input).map_err(|e| e.into())
670 }
671 #[cfg(not(feature = "hybrid"))]
672 {
673 unreachable!()
676 }
677 }
678
679 #[cfg_attr(feature = "perf-inline", inline(always))]
680 pub(crate) fn try_search_half_fwd_stopat(
681 &self,
682 cache: &mut HybridCache,
683 input: &Input<'_>,
684 ) -> Result<Result<HalfMatch, usize>, RetryFailError> {
685 #[cfg(feature = "hybrid")]
686 {
687 let dfa = self.0.forward();
688 let mut cache = cache.0.as_mut().unwrap().as_parts_mut().0;
689 crate::meta::stopat::hybrid_try_search_half_fwd(
690 dfa, &mut cache, input,
691 )
692 }
693 #[cfg(not(feature = "hybrid"))]
694 {
695 unreachable!()
698 }
699 }
700
701 #[cfg_attr(feature = "perf-inline", inline(always))]
702 pub(crate) fn try_search_half_rev(
703 &self,
704 cache: &mut HybridCache,
705 input: &Input<'_>,
706 ) -> Result<Option<HalfMatch>, RetryFailError> {
707 #[cfg(feature = "hybrid")]
708 {
709 let rev = self.0.reverse();
710 let mut revcache = cache.0.as_mut().unwrap().as_parts_mut().1;
711 rev.try_search_rev(&mut revcache, input).map_err(|e| e.into())
712 }
713 #[cfg(not(feature = "hybrid"))]
714 {
715 unreachable!()
718 }
719 }
720
721 #[cfg_attr(feature = "perf-inline", inline(always))]
722 pub(crate) fn try_search_half_rev_limited(
723 &self,
724 cache: &mut HybridCache,
725 input: &Input<'_>,
726 min_start: usize,
727 ) -> Result<Option<HalfMatch>, RetryError> {
728 #[cfg(feature = "hybrid")]
729 {
730 let dfa = self.0.reverse();
731 let mut cache = cache.0.as_mut().unwrap().as_parts_mut().1;
732 crate::meta::limited::hybrid_try_search_half_rev(
733 dfa, &mut cache, input, min_start,
734 )
735 }
736 #[cfg(not(feature = "hybrid"))]
737 {
738 unreachable!()
741 }
742 }
743
744 #[inline]
745 pub(crate) fn try_which_overlapping_matches(
746 &self,
747 cache: &mut HybridCache,
748 input: &Input<'_>,
749 patset: &mut PatternSet,
750 ) -> Result<(), RetryFailError> {
751 #[cfg(feature = "hybrid")]
752 {
753 let fwd = self.0.forward();
754 let mut fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
755 fwd.try_which_overlapping_matches(&mut fwdcache, input, patset)
756 .map_err(|e| e.into())
757 }
758 #[cfg(not(feature = "hybrid"))]
759 {
760 unreachable!()
763 }
764 }
765}
766
767#[derive(Clone, Debug)]
768pub(crate) struct HybridCache(
769 #[cfg(feature = "hybrid")] Option<hybrid::regex::Cache>,
770 #[cfg(not(feature = "hybrid"))] (),
771);
772
773impl HybridCache {
774 pub(crate) fn none() -> HybridCache {
775 #[cfg(feature = "hybrid")]
776 {
777 HybridCache(None)
778 }
779 #[cfg(not(feature = "hybrid"))]
780 {
781 HybridCache(())
782 }
783 }
784
785 pub(crate) fn new(builder: &Hybrid) -> HybridCache {
786 #[cfg(feature = "hybrid")]
787 {
788 HybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
789 }
790 #[cfg(not(feature = "hybrid"))]
791 {
792 HybridCache(())
793 }
794 }
795
796 pub(crate) fn reset(&mut self, builder: &Hybrid) {
797 #[cfg(feature = "hybrid")]
798 if let Some(ref e) = builder.0 {
799 self.0.as_mut().unwrap().reset(&e.0);
800 }
801 }
802
803 pub(crate) fn memory_usage(&self) -> usize {
804 #[cfg(feature = "hybrid")]
805 {
806 self.0.as_ref().map_or(0, |c| c.memory_usage())
807 }
808 #[cfg(not(feature = "hybrid"))]
809 {
810 0
811 }
812 }
813}
814
815#[derive(Debug)]
816pub(crate) struct DFA(Option<DFAEngine>);
817
818impl DFA {
819 pub(crate) fn none() -> DFA {
820 DFA(None)
821 }
822
823 pub(crate) fn new(
824 info: &RegexInfo,
825 pre: Option<Prefilter>,
826 nfa: &NFA,
827 nfarev: &NFA,
828 ) -> DFA {
829 DFA(DFAEngine::new(info, pre, nfa, nfarev))
830 }
831
832 #[cfg_attr(feature = "perf-inline", inline(always))]
833 pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&DFAEngine> {
834 let engine = self.0.as_ref()?;
835 Some(engine)
836 }
837
838 pub(crate) fn is_some(&self) -> bool {
839 self.0.is_some()
840 }
841
842 pub(crate) fn memory_usage(&self) -> usize {
843 self.0.as_ref().map_or(0, |e| e.memory_usage())
844 }
845}
846
847#[derive(Debug)]
848pub(crate) struct DFAEngine(
849 #[cfg(feature = "dfa-build")] dfa::regex::Regex,
850 #[cfg(not(feature = "dfa-build"))] (),
851);
852
853impl DFAEngine {
854 pub(crate) fn new(
855 info: &RegexInfo,
856 pre: Option<Prefilter>,
857 nfa: &NFA,
858 nfarev: &NFA,
859 ) -> Option<DFAEngine> {
860 #[cfg(feature = "dfa-build")]
861 {
862 if !info.config().get_dfa() {
863 return None;
864 }
865 if let Some(state_limit) = info.config().get_dfa_state_limit() {
867 if nfa.states().len() > state_limit {
868 debug!(
869 "skipping full DFA because NFA has {} states, \
870 which exceeds the heuristic limit of {}",
871 nfa.states().len(),
872 state_limit,
873 );
874 return None;
875 }
876 }
877 let size_limit = info.config().get_dfa_size_limit().map(|n| n / 4);
883 let dfa_config = dfa::dense::Config::new()
884 .match_kind(info.config().get_match_kind())
885 .prefilter(pre.clone())
886 .starts_for_each_pattern(true)
892 .byte_classes(info.config().get_byte_classes())
893 .unicode_word_boundary(true)
894 .specialize_start_states(pre.is_some())
895 .determinize_size_limit(size_limit)
896 .dfa_size_limit(size_limit);
897 let result = dfa::dense::Builder::new()
898 .configure(dfa_config.clone())
899 .build_from_nfa(&nfa);
900 let fwd = match result {
901 Ok(fwd) => fwd,
902 Err(_err) => {
903 debug!("forward full DFA failed to build: {}", _err);
904 return None;
905 }
906 };
907 let result = dfa::dense::Builder::new()
908 .configure(
909 dfa_config
910 .clone()
911 .start_kind(dfa::StartKind::Anchored)
919 .match_kind(MatchKind::All)
920 .prefilter(None)
921 .specialize_start_states(false),
922 )
923 .build_from_nfa(&nfarev);
924 let rev = match result {
925 Ok(rev) => rev,
926 Err(_err) => {
927 debug!("reverse full DFA failed to build: {}", _err);
928 return None;
929 }
930 };
931 let engine = dfa::regex::Builder::new().build_from_dfas(fwd, rev);
932 debug!(
933 "fully compiled forward and reverse DFAs built, {} bytes",
934 engine.forward().memory_usage()
935 + engine.reverse().memory_usage(),
936 );
937 Some(DFAEngine(engine))
938 }
939 #[cfg(not(feature = "dfa-build"))]
940 {
941 None
942 }
943 }
944
945 #[cfg_attr(feature = "perf-inline", inline(always))]
946 pub(crate) fn try_search(
947 &self,
948 input: &Input<'_>,
949 ) -> Result<Option<Match>, RetryFailError> {
950 #[cfg(feature = "dfa-build")]
951 {
952 self.0.try_search(input).map_err(|e| e.into())
953 }
954 #[cfg(not(feature = "dfa-build"))]
955 {
956 unreachable!()
959 }
960 }
961
962 #[cfg_attr(feature = "perf-inline", inline(always))]
963 pub(crate) fn try_search_half_fwd(
964 &self,
965 input: &Input<'_>,
966 ) -> Result<Option<HalfMatch>, RetryFailError> {
967 #[cfg(feature = "dfa-build")]
968 {
969 use crate::dfa::Automaton;
970 self.0.forward().try_search_fwd(input).map_err(|e| e.into())
971 }
972 #[cfg(not(feature = "dfa-build"))]
973 {
974 unreachable!()
977 }
978 }
979
980 #[cfg_attr(feature = "perf-inline", inline(always))]
981 pub(crate) fn try_search_half_fwd_stopat(
982 &self,
983 input: &Input<'_>,
984 ) -> Result<Result<HalfMatch, usize>, RetryFailError> {
985 #[cfg(feature = "dfa-build")]
986 {
987 let dfa = self.0.forward();
988 crate::meta::stopat::dfa_try_search_half_fwd(dfa, input)
989 }
990 #[cfg(not(feature = "dfa-build"))]
991 {
992 unreachable!()
995 }
996 }
997
998 #[cfg_attr(feature = "perf-inline", inline(always))]
999 pub(crate) fn try_search_half_rev(
1000 &self,
1001 input: &Input<'_>,
1002 ) -> Result<Option<HalfMatch>, RetryFailError> {
1003 #[cfg(feature = "dfa-build")]
1004 {
1005 use crate::dfa::Automaton;
1006 self.0.reverse().try_search_rev(&input).map_err(|e| e.into())
1007 }
1008 #[cfg(not(feature = "dfa-build"))]
1009 {
1010 unreachable!()
1013 }
1014 }
1015
1016 #[cfg_attr(feature = "perf-inline", inline(always))]
1017 pub(crate) fn try_search_half_rev_limited(
1018 &self,
1019 input: &Input<'_>,
1020 min_start: usize,
1021 ) -> Result<Option<HalfMatch>, RetryError> {
1022 #[cfg(feature = "dfa-build")]
1023 {
1024 let dfa = self.0.reverse();
1025 crate::meta::limited::dfa_try_search_half_rev(
1026 dfa, input, min_start,
1027 )
1028 }
1029 #[cfg(not(feature = "dfa-build"))]
1030 {
1031 unreachable!()
1034 }
1035 }
1036
1037 #[inline]
1038 pub(crate) fn try_which_overlapping_matches(
1039 &self,
1040 input: &Input<'_>,
1041 patset: &mut PatternSet,
1042 ) -> Result<(), RetryFailError> {
1043 #[cfg(feature = "dfa-build")]
1044 {
1045 use crate::dfa::Automaton;
1046 self.0
1047 .forward()
1048 .try_which_overlapping_matches(input, patset)
1049 .map_err(|e| e.into())
1050 }
1051 #[cfg(not(feature = "dfa-build"))]
1052 {
1053 unreachable!()
1056 }
1057 }
1058
1059 pub(crate) fn memory_usage(&self) -> usize {
1060 #[cfg(feature = "dfa-build")]
1061 {
1062 self.0.forward().memory_usage() + self.0.reverse().memory_usage()
1063 }
1064 #[cfg(not(feature = "dfa-build"))]
1065 {
1066 unreachable!()
1069 }
1070 }
1071}
1072
1073#[derive(Debug)]
1074pub(crate) struct ReverseHybrid(Option<ReverseHybridEngine>);
1075
1076impl ReverseHybrid {
1077 pub(crate) fn none() -> ReverseHybrid {
1078 ReverseHybrid(None)
1079 }
1080
1081 pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseHybrid {
1082 ReverseHybrid(ReverseHybridEngine::new(info, nfarev))
1083 }
1084
1085 pub(crate) fn create_cache(&self) -> ReverseHybridCache {
1086 ReverseHybridCache::new(self)
1087 }
1088
1089 #[cfg_attr(feature = "perf-inline", inline(always))]
1090 pub(crate) fn get(
1091 &self,
1092 _input: &Input<'_>,
1093 ) -> Option<&ReverseHybridEngine> {
1094 let engine = self.0.as_ref()?;
1095 Some(engine)
1096 }
1097}
1098
1099#[derive(Debug)]
1100pub(crate) struct ReverseHybridEngine(
1101 #[cfg(feature = "hybrid")] hybrid::dfa::DFA,
1102 #[cfg(not(feature = "hybrid"))] (),
1103);
1104
1105impl ReverseHybridEngine {
1106 pub(crate) fn new(
1107 info: &RegexInfo,
1108 nfarev: &NFA,
1109 ) -> Option<ReverseHybridEngine> {
1110 #[cfg(feature = "hybrid")]
1111 {
1112 if !info.config().get_hybrid() {
1113 return None;
1114 }
1115 let dfa_config = hybrid::dfa::Config::new()
1119 .match_kind(MatchKind::All)
1120 .prefilter(None)
1121 .starts_for_each_pattern(false)
1122 .byte_classes(info.config().get_byte_classes())
1123 .unicode_word_boundary(true)
1124 .specialize_start_states(false)
1125 .cache_capacity(info.config().get_hybrid_cache_capacity())
1126 .skip_cache_capacity_check(false)
1127 .minimum_cache_clear_count(Some(3))
1128 .minimum_bytes_per_state(Some(10));
1129 let result = hybrid::dfa::Builder::new()
1130 .configure(dfa_config)
1131 .build_from_nfa(nfarev.clone());
1132 let rev = match result {
1133 Ok(rev) => rev,
1134 Err(_err) => {
1135 debug!("lazy reverse DFA failed to build: {}", _err);
1136 return None;
1137 }
1138 };
1139 debug!("lazy reverse DFA built");
1140 Some(ReverseHybridEngine(rev))
1141 }
1142 #[cfg(not(feature = "hybrid"))]
1143 {
1144 None
1145 }
1146 }
1147
1148 #[cfg_attr(feature = "perf-inline", inline(always))]
1149 pub(crate) fn try_search_half_rev_limited(
1150 &self,
1151 cache: &mut ReverseHybridCache,
1152 input: &Input<'_>,
1153 min_start: usize,
1154 ) -> Result<Option<HalfMatch>, RetryError> {
1155 #[cfg(feature = "hybrid")]
1156 {
1157 let dfa = &self.0;
1158 let mut cache = cache.0.as_mut().unwrap();
1159 crate::meta::limited::hybrid_try_search_half_rev(
1160 dfa, &mut cache, input, min_start,
1161 )
1162 }
1163 #[cfg(not(feature = "hybrid"))]
1164 {
1165 unreachable!()
1168 }
1169 }
1170}
1171
1172#[derive(Clone, Debug)]
1173pub(crate) struct ReverseHybridCache(
1174 #[cfg(feature = "hybrid")] Option<hybrid::dfa::Cache>,
1175 #[cfg(not(feature = "hybrid"))] (),
1176);
1177
1178impl ReverseHybridCache {
1179 pub(crate) fn none() -> ReverseHybridCache {
1180 #[cfg(feature = "hybrid")]
1181 {
1182 ReverseHybridCache(None)
1183 }
1184 #[cfg(not(feature = "hybrid"))]
1185 {
1186 ReverseHybridCache(())
1187 }
1188 }
1189
1190 pub(crate) fn new(builder: &ReverseHybrid) -> ReverseHybridCache {
1191 #[cfg(feature = "hybrid")]
1192 {
1193 ReverseHybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
1194 }
1195 #[cfg(not(feature = "hybrid"))]
1196 {
1197 ReverseHybridCache(())
1198 }
1199 }
1200
1201 pub(crate) fn reset(&mut self, builder: &ReverseHybrid) {
1202 #[cfg(feature = "hybrid")]
1203 if let Some(ref e) = builder.0 {
1204 self.0.as_mut().unwrap().reset(&e.0);
1205 }
1206 }
1207
1208 pub(crate) fn memory_usage(&self) -> usize {
1209 #[cfg(feature = "hybrid")]
1210 {
1211 self.0.as_ref().map_or(0, |c| c.memory_usage())
1212 }
1213 #[cfg(not(feature = "hybrid"))]
1214 {
1215 0
1216 }
1217 }
1218}
1219
1220#[derive(Debug)]
1221pub(crate) struct ReverseDFA(Option<ReverseDFAEngine>);
1222
1223impl ReverseDFA {
1224 pub(crate) fn none() -> ReverseDFA {
1225 ReverseDFA(None)
1226 }
1227
1228 pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseDFA {
1229 ReverseDFA(ReverseDFAEngine::new(info, nfarev))
1230 }
1231
1232 #[cfg_attr(feature = "perf-inline", inline(always))]
1233 pub(crate) fn get(&self, _input: &Input<'_>) -> Option<&ReverseDFAEngine> {
1234 let engine = self.0.as_ref()?;
1235 Some(engine)
1236 }
1237
1238 pub(crate) fn is_some(&self) -> bool {
1239 self.0.is_some()
1240 }
1241
1242 pub(crate) fn memory_usage(&self) -> usize {
1243 self.0.as_ref().map_or(0, |e| e.memory_usage())
1244 }
1245}
1246
1247#[derive(Debug)]
1248pub(crate) struct ReverseDFAEngine(
1249 #[cfg(feature = "dfa-build")] dfa::dense::DFA<Vec<u32>>,
1250 #[cfg(not(feature = "dfa-build"))] (),
1251);
1252
1253impl ReverseDFAEngine {
1254 pub(crate) fn new(
1255 info: &RegexInfo,
1256 nfarev: &NFA,
1257 ) -> Option<ReverseDFAEngine> {
1258 #[cfg(feature = "dfa-build")]
1259 {
1260 if !info.config().get_dfa() {
1261 return None;
1262 }
1263 if let Some(state_limit) = info.config().get_dfa_state_limit() {
1265 if nfarev.states().len() > state_limit {
1266 debug!(
1267 "skipping full reverse DFA because NFA has {} states, \
1268 which exceeds the heuristic limit of {}",
1269 nfarev.states().len(),
1270 state_limit,
1271 );
1272 return None;
1273 }
1274 }
1275 let size_limit = info.config().get_dfa_size_limit().map(|n| n / 2);
1280 let dfa_config = dfa::dense::Config::new()
1286 .match_kind(MatchKind::All)
1287 .prefilter(None)
1288 .accelerate(false)
1289 .start_kind(dfa::StartKind::Anchored)
1290 .starts_for_each_pattern(false)
1291 .byte_classes(info.config().get_byte_classes())
1292 .unicode_word_boundary(true)
1293 .specialize_start_states(false)
1294 .determinize_size_limit(size_limit)
1295 .dfa_size_limit(size_limit);
1296 let result = dfa::dense::Builder::new()
1297 .configure(dfa_config)
1298 .build_from_nfa(&nfarev);
1299 let rev = match result {
1300 Ok(rev) => rev,
1301 Err(_err) => {
1302 debug!("full reverse DFA failed to build: {}", _err);
1303 return None;
1304 }
1305 };
1306 debug!(
1307 "fully compiled reverse DFA built, {} bytes",
1308 rev.memory_usage()
1309 );
1310 Some(ReverseDFAEngine(rev))
1311 }
1312 #[cfg(not(feature = "dfa-build"))]
1313 {
1314 None
1315 }
1316 }
1317
1318 #[cfg_attr(feature = "perf-inline", inline(always))]
1319 pub(crate) fn try_search_half_rev_limited(
1320 &self,
1321 input: &Input<'_>,
1322 min_start: usize,
1323 ) -> Result<Option<HalfMatch>, RetryError> {
1324 #[cfg(feature = "dfa-build")]
1325 {
1326 let dfa = &self.0;
1327 crate::meta::limited::dfa_try_search_half_rev(
1328 dfa, input, min_start,
1329 )
1330 }
1331 #[cfg(not(feature = "dfa-build"))]
1332 {
1333 unreachable!()
1336 }
1337 }
1338
1339 pub(crate) fn memory_usage(&self) -> usize {
1340 #[cfg(feature = "dfa-build")]
1341 {
1342 self.0.memory_usage()
1343 }
1344 #[cfg(not(feature = "dfa-build"))]
1345 {
1346 unreachable!()
1349 }
1350 }
1351}