Skip to main content

regex_cursor/engines/meta/
wrappers.rs

1/*!
2This module contains a boat load of wrappers around each of our internal regex
3engines. They encapsulate a few things:
4
51. The wrappers manage the conditional existence of the regex engine. Namely,
6the PikeVM is the only required regex engine. The rest are optional. These
7wrappers present a uniform API regardless of which engines are available. And
8availability might be determined by compile time features or by dynamic
9configuration via `meta::Config`. Encapsulating the conditional compilation
10features is in particular a huge simplification for the higher level code that
11composes these engines.
122. The wrappers manage construction of each engine, including skipping it if
13the engine is unavailable or configured to not be used.
143. The wrappers manage whether an engine *can* be used for a particular
15search configuration. For example, `BoundedBacktracker::get` only returns a
16backtracking engine when the haystack is bigger than the maximum supported
17length. The wrappers also sometimes take a position on when an engine *ought*
18to be used, but only in cases where the logic is extremely local to the engine
19itself. Otherwise, things like "choose between the backtracker and the one-pass
20DFA" are managed by the higher level meta strategy code.
21
22There are also corresponding wrappers for the various `Cache` types for each
23regex engine that needs them. If an engine is unavailable or not used, then a
24cache for it will *not* actually be allocated.
25*/
26
27use log::debug;
28use regex_automata::nfa::thompson::NFA;
29use regex_automata::util::prefilter::Prefilter;
30use regex_automata::util::primitives::NonMaxUsize;
31use regex_automata::{dfa, hybrid, HalfMatch, Match, MatchKind, PatternID};
32
33use crate::cursor::Cursor;
34use crate::engines::meta::error::{BuildError, RetryFailError};
35use crate::engines::meta::regex::RegexInfo;
36use crate::engines::pikevm;
37use crate::Input;
38
39#[derive(Debug)]
40pub(crate) struct PikeVM(PikeVMEngine);
41
42impl PikeVM {
43    pub(crate) fn new(
44        info: &RegexInfo,
45        pre: Option<Prefilter>,
46        nfa: &NFA,
47    ) -> Result<PikeVM, BuildError> {
48        PikeVMEngine::new(info, pre, nfa).map(PikeVM)
49    }
50
51    pub(crate) fn create_cache(&self) -> PikeVMCache {
52        PikeVMCache::new(self)
53    }
54
55    #[cfg_attr(feature = "perf-inline", inline(always))]
56    pub(crate) fn get(&self) -> &PikeVMEngine {
57        &self.0
58    }
59}
60
61#[derive(Debug)]
62pub(crate) struct PikeVMEngine(pikevm::PikeVM);
63
64impl PikeVMEngine {
65    pub(crate) fn new(
66        info: &RegexInfo,
67        pre: Option<Prefilter>,
68        nfa: &NFA,
69    ) -> Result<PikeVMEngine, BuildError> {
70        let pikevm_config =
71            pikevm::Config::new().match_kind(info.config().get_match_kind()).prefilter(pre);
72        let engine = pikevm::Builder::new()
73            .configure(pikevm_config)
74            .build_from_nfa(nfa.clone())
75            .map_err(BuildError::nfa)?;
76        debug!("PikeVM built");
77        Ok(PikeVMEngine(engine))
78    }
79
80    #[cfg_attr(feature = "perf-inline", inline(always))]
81    pub(crate) fn is_match(&self, cache: &mut PikeVMCache, input: &mut Input<impl Cursor>) -> bool {
82        crate::engines::pikevm::is_match(&self.0, cache.0.as_mut().unwrap(), input)
83    }
84
85    #[cfg_attr(feature = "perf-inline", inline(always))]
86    pub(crate) fn search_slots(
87        &self,
88        cache: &mut PikeVMCache,
89        input: &mut Input<impl Cursor>,
90        slots: &mut [Option<NonMaxUsize>],
91    ) -> Option<PatternID> {
92        crate::engines::pikevm::search_slots(&self.0, cache.0.as_mut().unwrap(), input, slots)
93    }
94
95    // #[cfg_attr(feature = "perf-inline", inline(always))]
96    // pub(crate) fn which_overlapping_matches(
97    //     &self,
98    //     cache: &mut PikeVMCache,
99    //     input: &mut Input<impl Cursor>,
100    //     patset: &mut PatternSet,
101    // ) {
102    //     self.0.which_overlapping_matches(cache.0.as_mut().unwrap(), input, patset)
103    // }
104}
105
106#[derive(Clone, Debug)]
107pub(crate) struct PikeVMCache(Option<pikevm::Cache>);
108
109impl PikeVMCache {
110    pub(crate) fn none() -> PikeVMCache {
111        PikeVMCache(None)
112    }
113
114    pub(crate) fn new(builder: &PikeVM) -> PikeVMCache {
115        PikeVMCache(Some(pikevm::Cache::new(&builder.get().0)))
116    }
117
118    pub(crate) fn reset(&mut self, builder: &PikeVM) {
119        self.0.as_mut().unwrap().reset(&builder.get().0);
120    }
121
122    pub(crate) fn memory_usage(&self) -> usize {
123        self.0.as_ref().map_or(0, |c| c.memory_usage())
124    }
125}
126
127#[derive(Debug)]
128pub(crate) struct Hybrid(Option<HybridEngine>);
129
130impl Hybrid {
131    pub(crate) fn none() -> Hybrid {
132        Hybrid(None)
133    }
134
135    pub(crate) fn new(info: &RegexInfo, pre: Option<Prefilter>, nfa: &NFA, nfarev: &NFA) -> Hybrid {
136        Hybrid(HybridEngine::new(info, pre, nfa, nfarev))
137    }
138
139    pub(crate) fn create_cache(&self) -> HybridCache {
140        HybridCache::new(self)
141    }
142
143    #[cfg_attr(feature = "perf-inline", inline(always))]
144    pub(crate) fn get(&self, _input: &mut Input<impl Cursor>) -> Option<&HybridEngine> {
145        let engine = self.0.as_ref()?;
146        Some(engine)
147    }
148
149    pub(crate) fn is_some(&self) -> bool {
150        self.0.is_some()
151    }
152}
153
154#[derive(Debug)]
155pub(crate) struct HybridEngine(hybrid::regex::Regex);
156
157impl HybridEngine {
158    pub(crate) fn new(
159        info: &RegexInfo,
160        pre: Option<Prefilter>,
161        nfa: &NFA,
162        nfarev: &NFA,
163    ) -> Option<HybridEngine> {
164        {
165            if !info.config().get_hybrid() {
166                return None;
167            }
168            let dfa_config = hybrid::dfa::Config::new()
169                .match_kind(info.config().get_match_kind())
170                .prefilter(pre.clone())
171                // Enabling this is necessary for ensuring we can service any
172                // kind of 'Input' search without error. For the lazy DFA,
173                // this is not particularly costly, since the start states are
174                // generated lazily.
175                .starts_for_each_pattern(true)
176                .byte_classes(info.config().get_byte_classes())
177                .unicode_word_boundary(true)
178                .specialize_start_states(pre.is_some())
179                .cache_capacity(info.config().get_hybrid_cache_capacity())
180                // This makes it possible for building a lazy DFA to
181                // fail even though the NFA has already been built. Namely,
182                // if the cache capacity is too small to fit some minimum
183                // number of states (which is small, like 4 or 5), then the
184                // DFA will refuse to build.
185                //
186                // We shouldn't enable this to make building always work, since
187                // this could cause the allocation of a cache bigger than the
188                // provided capacity amount.
189                //
190                // This is effectively the only reason why building a lazy DFA
191                // could fail. If it does, then we simply suppress the error
192                // and return None.
193                .skip_cache_capacity_check(false)
194                // This and enabling heuristic Unicode word boundary support
195                // above make it so the lazy DFA can quit at match time.
196                .minimum_cache_clear_count(Some(3))
197                .minimum_bytes_per_state(Some(10));
198            let result = hybrid::dfa::Builder::new()
199                .configure(dfa_config.clone())
200                .build_from_nfa(nfa.clone());
201            let fwd = match result {
202                Ok(fwd) => fwd,
203                Err(_err) => {
204                    debug!("forward lazy DFA failed to build: {}", _err);
205                    return None;
206                }
207            };
208            let result = hybrid::dfa::Builder::new()
209                .configure(
210                    dfa_config
211                        .clone()
212                        .match_kind(MatchKind::All)
213                        .prefilter(None)
214                        .specialize_start_states(false),
215                )
216                .build_from_nfa(nfarev.clone());
217            let rev = match result {
218                Ok(rev) => rev,
219                Err(_err) => {
220                    debug!("reverse lazy DFA failed to build: {}", _err);
221                    return None;
222                }
223            };
224            let engine = hybrid::regex::Builder::new().build_from_dfas(fwd, rev);
225            debug!("lazy DFA built");
226            Some(HybridEngine(engine))
227        }
228    }
229
230    #[cfg_attr(feature = "perf-inline", inline(always))]
231    pub(crate) fn try_search(
232        &self,
233        cache: &mut HybridCache,
234        input: &mut Input<impl Cursor>,
235    ) -> Result<Option<Match>, RetryFailError> {
236        let cache = cache.0.as_mut().unwrap();
237        crate::engines::hybrid::try_search(&self.0, cache, input).map_err(|e| e.into())
238    }
239
240    #[cfg_attr(feature = "perf-inline", inline(always))]
241    pub(crate) fn try_search_half_fwd(
242        &self,
243        cache: &mut HybridCache,
244        input: &mut Input<impl Cursor>,
245    ) -> Result<Option<HalfMatch>, RetryFailError> {
246        let fwd = self.0.forward();
247        let fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
248        crate::engines::hybrid::try_search_fwd(fwd, fwdcache, input).map_err(|e| e.into())
249    }
250
251    // #[cfg_attr(feature = "perf-inline", inline(always))]
252    // pub(crate) fn try_search_half_fwd_stopat(
253    //     &self,
254    //     cache: &mut HybridCache,
255    //     input: &mut Input<impl Cursor>,
256    // ) -> Result<Result<HalfMatch, usize>, RetryFailError> {
257    //     let dfa = self.0.forward();
258    //     let mut cache = cache.0.as_mut().unwrap().as_parts_mut().0;
259    //     crate::meta::stopat::hybrid_try_search_half_fwd(dfa, &mut cache, input)
260    // }
261
262    #[cfg_attr(feature = "perf-inline", inline(always))]
263    pub(crate) fn try_search_half_rev(
264        &self,
265        cache: &mut HybridCache,
266        input: &mut Input<impl Cursor>,
267    ) -> Result<Option<HalfMatch>, RetryFailError> {
268        let rev = self.0.reverse();
269        let revcache = cache.0.as_mut().unwrap().as_parts_mut().1;
270        crate::engines::hybrid::try_search_rev(rev, revcache, input).map_err(|e| e.into())
271    }
272
273    // #[cfg_attr(feature = "perf-inline", inline(always))]
274    // pub(crate) fn try_search_half_rev_limited(
275    //     &self,
276    //     cache: &mut HybridCache,
277    //     input: &mut Input<impl Cursor>,
278    //     min_start: usize,
279    // ) -> Result<Option<HalfMatch>, RetryError> {
280    //     let dfa = self.0.reverse();
281    //     let mut cache = cache.0.as_mut().unwrap().as_parts_mut().1;
282    //     crate::meta::limited::hybrid_try_search_half_rev(dfa, &mut cache, input, min_start)
283    // }
284
285    // #[inline]
286    // pub(crate) fn try_which_overlapping_matches(
287    //     &self,
288    //     cache: &mut HybridCache,
289    //     input: &mut Input<impl Cursor>,
290    //     patset: &mut PatternSet,
291    // ) -> Result<(), RetryFailError> {
292    //         let fwd = self.0.forward();
293    //         let mut fwdcache = cache.0.as_mut().unwrap().as_parts_mut().0;
294    //         fwd.try_which_overlapping_matches(&mut fwdcache, input, patset).map_err(|e| e.into())
295    // }
296}
297
298#[derive(Clone, Debug)]
299pub(crate) struct HybridCache(Option<hybrid::regex::Cache>);
300
301impl HybridCache {
302    pub(crate) fn none() -> HybridCache {
303        HybridCache(None)
304    }
305
306    pub(crate) fn new(builder: &Hybrid) -> HybridCache {
307        HybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
308    }
309
310    pub(crate) fn reset(&mut self, builder: &Hybrid) {
311        if let Some(ref e) = builder.0 {
312            self.0.as_mut().unwrap().reset(&e.0);
313        }
314    }
315
316    pub(crate) fn memory_usage(&self) -> usize {
317        {
318            self.0.as_ref().map_or(0, |c| c.memory_usage())
319        }
320    }
321}
322
323#[derive(Debug)]
324pub(crate) struct DFA(Option<DFAEngine>);
325
326impl DFA {
327    pub(crate) fn none() -> DFA {
328        DFA(None)
329    }
330
331    pub(crate) fn new(info: &RegexInfo, pre: Option<Prefilter>, nfa: &NFA, nfarev: &NFA) -> DFA {
332        DFA(DFAEngine::new(info, pre, nfa, nfarev))
333    }
334
335    #[cfg_attr(feature = "perf-inline", inline(always))]
336    pub(crate) fn get(&self, _input: &mut Input<impl Cursor>) -> Option<&DFAEngine> {
337        let engine = self.0.as_ref()?;
338        Some(engine)
339    }
340
341    pub(crate) fn is_some(&self) -> bool {
342        self.0.is_some()
343    }
344
345    pub(crate) fn memory_usage(&self) -> usize {
346        self.0.as_ref().map_or(0, |e| e.memory_usage())
347    }
348}
349
350#[derive(Debug)]
351pub(crate) struct DFAEngine(dfa::regex::Regex);
352
353impl DFAEngine {
354    pub(crate) fn new(
355        info: &RegexInfo,
356        pre: Option<Prefilter>,
357        nfa: &NFA,
358        nfarev: &NFA,
359    ) -> Option<DFAEngine> {
360        {
361            if !info.config().get_dfa() {
362                return None;
363            }
364            // If our NFA is anything but small, don't even bother with a DFA.
365            if let Some(state_limit) = info.config().get_dfa_state_limit() {
366                if nfa.states().len() > state_limit {
367                    debug!(
368                        "skipping full DFA because NFA has {} states, \
369                         which exceeds the heuristic limit of {}",
370                        nfa.states().len(),
371                        state_limit,
372                    );
373                    return None;
374                }
375            }
376            // We cut the size limit in four because the total heap used by
377            // DFA construction is determinization aux memory and the DFA
378            // itself, and those things are configured independently in the
379            // lower level DFA builder API. And then split that in two because
380            // of forward and reverse DFAs.
381            let size_limit = info.config().get_dfa_size_limit().map(|n| n / 4);
382            let dfa_config = dfa::dense::Config::new()
383                .match_kind(info.config().get_match_kind())
384                .prefilter(pre.clone())
385                // Enabling this is necessary for ensuring we can service any
386                // kind of 'Input' search without error. For the full DFA, this
387                // can be quite costly. But since we have such a small bound
388                // on the size of the DFA, in practice, any multl-regexes are
389                // probably going to blow the limit anyway.
390                .starts_for_each_pattern(true)
391                .byte_classes(info.config().get_byte_classes())
392                .unicode_word_boundary(true)
393                .specialize_start_states(pre.is_some())
394                .determinize_size_limit(size_limit)
395                .dfa_size_limit(size_limit);
396            let result =
397                dfa::dense::Builder::new().configure(dfa_config.clone()).build_from_nfa(nfa);
398            let fwd = match result {
399                Ok(fwd) => fwd,
400                Err(_err) => {
401                    debug!("forward full DFA failed to build: {}", _err);
402                    return None;
403                }
404            };
405            let result = dfa::dense::Builder::new()
406                .configure(
407                    dfa_config
408                        .clone()
409                        // We never need unanchored reverse searches, so
410                        // there's no point in building it into the DFA, which
411                        // WILL take more space. (This isn't done for the lazy
412                        // DFA because the DFA is, well, lazy. It doesn't pay
413                        // the cost for supporting unanchored searches unless
414                        // you actually do an unanchored search, which we
415                        // don't.)
416                        .start_kind(dfa::StartKind::Anchored)
417                        .match_kind(MatchKind::All)
418                        .prefilter(None)
419                        .specialize_start_states(false),
420                )
421                .build_from_nfa(nfarev);
422            let rev = match result {
423                Ok(rev) => rev,
424                Err(_err) => {
425                    debug!("reverse full DFA failed to build: {}", _err);
426                    return None;
427                }
428            };
429            let engine = dfa::regex::Builder::new().build_from_dfas(fwd, rev);
430            debug!(
431                "fully compiled forward and reverse DFAs built, {} bytes",
432                engine.forward().memory_usage() + engine.reverse().memory_usage(),
433            );
434            Some(DFAEngine(engine))
435        }
436    }
437
438    #[cfg_attr(feature = "perf-inline", inline(always))]
439    pub(crate) fn try_search(
440        &self,
441        input: &mut Input<impl Cursor>,
442    ) -> Result<Option<Match>, RetryFailError> {
443        crate::engines::dfa::try_search(&self.0, input).map_err(|err| err.into())
444    }
445
446    #[cfg_attr(feature = "perf-inline", inline(always))]
447    pub(crate) fn try_search_half_fwd(
448        &self,
449        input: &mut Input<impl Cursor>,
450    ) -> Result<Option<HalfMatch>, RetryFailError> {
451        crate::engines::dfa::try_search_fwd(self.0.forward(), input).map_err(|e| e.into())
452    }
453
454    // #[cfg_attr(feature = "perf-inline", inline(always))]
455    // pub(crate) fn try_search_half_fwd_stopat(
456    //     &self,
457    //     input: &mut Input<impl Cursor>,
458    // ) -> Result<Result<HalfMatch, usize>, RetryFailError> {
459    //         let dfa = self.0.forward();
460    //         crate::meta::stopat::dfa_try_search_half_fwd(dfa, input)
461    // }
462
463    #[cfg_attr(feature = "perf-inline", inline(always))]
464    pub(crate) fn try_search_half_rev(
465        &self,
466        input: &mut Input<impl Cursor>,
467    ) -> Result<Option<HalfMatch>, RetryFailError> {
468        crate::engines::dfa::try_search_rev(self.0.reverse(), input).map_err(|e| e.into())
469    }
470
471    // #[cfg_attr(feature = "perf-inline", inline(always))]
472    // pub(crate) fn try_search_half_rev_limited(
473    //     &self,
474    //     input: &mut Input<impl Cursor>,
475    //     min_start: usize,
476    // ) -> Result<Option<HalfMatch>, RetryError> {
477    //     let dfa = self.0.reverse();
478    //     crate::meta::limited::dfa_try_search_half_rev(dfa, input, min_start)
479    // }
480
481    // #[inline]
482    // pub(crate) fn try_which_overlapping_matches(
483    //     &self,
484    //     input: &mut Input<impl Cursor>,
485    //     patset: &mut PatternSet,
486    // ) -> Result<(), RetryFailError> {
487    //         use crate::dfa::Automaton;
488    //         self.0.forward().try_which_overlapping_matches(input, patset).map_err(|e| e.into())
489    // }
490
491    pub(crate) fn memory_usage(&self) -> usize {
492        self.0.forward().memory_usage() + self.0.reverse().memory_usage()
493    }
494}
495
496// #[derive(Debug)]
497// pub(crate) struct ReverseHybrid(Option<ReverseHybridEngine>);
498
499// impl ReverseHybrid {
500//     pub(crate) fn none() -> ReverseHybrid {
501//         ReverseHybrid(None)
502//     }
503
504//     pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseHybrid {
505//         ReverseHybrid(ReverseHybridEngine::new(info, nfarev))
506//     }
507
508//     pub(crate) fn create_cache(&self) -> ReverseHybridCache {
509//         ReverseHybridCache::new(self)
510//     }
511
512//     #[cfg_attr(feature = "perf-inline", inline(always))]
513//     pub(crate) fn get(&self, _input: &mut Input<impl Cursor>) -> Option<&ReverseHybridEngine> {
514//         let engine = self.0.as_ref()?;
515//         Some(engine)
516//     }
517// }
518
519// #[derive(Debug)]
520// pub(crate) struct ReverseHybridEngine(hybrid::dfa::DFA);
521
522// impl ReverseHybridEngine {
523//     pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> Option<ReverseHybridEngine> {
524//         if !info.config().get_hybrid() {
525//             return None;
526//         }
527//         // Since we only use this for reverse searches, we can hard-code
528//         // a number of things like match semantics, prefilters, starts
529//         // for each pattern and so on.
530//         let dfa_config = hybrid::dfa::Config::new()
531//             .match_kind(MatchKind::All)
532//             .prefilter(None)
533//             .starts_for_each_pattern(false)
534//             .byte_classes(info.config().get_byte_classes())
535//             .unicode_word_boundary(true)
536//             .specialize_start_states(false)
537//             .cache_capacity(info.config().get_hybrid_cache_capacity())
538//             .skip_cache_capacity_check(false)
539//             .minimum_cache_clear_count(Some(3))
540//             .minimum_bytes_per_state(Some(10));
541//         let result =
542//             hybrid::dfa::Builder::new().configure(dfa_config).build_from_nfa(nfarev.clone());
543//         let rev = match result {
544//             Ok(rev) => rev,
545//             Err(_err) => {
546//                 debug!("lazy reverse DFA failed to build: {}", _err);
547//                 return None;
548//             }
549//         };
550//         debug!("lazy reverse DFA built");
551//         Some(ReverseHybridEngine(rev))
552//     }
553
554//     #[cfg_attr(feature = "perf-inline", inline(always))]
555//     pub(crate) fn try_search_half_rev_limited(
556//         &self,
557//         cache: &mut ReverseHybridCache,
558//         input: &mut Input<impl Cursor>,
559//         min_start: usize,
560//     ) -> Result<Option<HalfMatch>, RetryError> {
561//         let dfa = &self.0;
562//         let mut cache = cache.0.as_mut().unwrap();
563//         crate::meta::limited::hybrid_try_search_half_rev(dfa, &mut cache, input, min_start)
564//     }
565// }
566
567// #[derive(Clone, Debug)]
568// pub(crate) struct ReverseHybridCache(
569//     #[cfg(feature = "hybrid")] Option<hybrid::dfa::Cache>,
570//     #[cfg(not(feature = "hybrid"))] (),
571// );
572
573// impl ReverseHybridCache {
574//     pub(crate) fn none() -> ReverseHybridCache {
575//         #[cfg(feature = "hybrid")]
576//         {
577//             ReverseHybridCache(None)
578//         }
579//         #[cfg(not(feature = "hybrid"))]
580//         {
581//             ReverseHybridCache(())
582//         }
583//     }
584
585//     pub(crate) fn new(builder: &ReverseHybrid) -> ReverseHybridCache {
586//         #[cfg(feature = "hybrid")]
587//         {
588//             ReverseHybridCache(builder.0.as_ref().map(|e| e.0.create_cache()))
589//         }
590//         #[cfg(not(feature = "hybrid"))]
591//         {
592//             ReverseHybridCache(())
593//         }
594//     }
595
596//     pub(crate) fn reset(&mut self, builder: &ReverseHybrid) {
597//         #[cfg(feature = "hybrid")]
598//         if let Some(ref e) = builder.0 {
599//             self.0.as_mut().unwrap().reset(&e.0);
600//         }
601//     }
602
603//     pub(crate) fn memory_usage(&self) -> usize {
604//         #[cfg(feature = "hybrid")]
605//         {
606//             self.0.as_ref().map_or(0, |c| c.memory_usage())
607//         }
608//         #[cfg(not(feature = "hybrid"))]
609//         {
610//             0
611//         }
612//     }
613// }
614
615// #[derive(Debug)]
616// pub(crate) struct ReverseDFA(Option<ReverseDFAEngine>);
617
618// impl ReverseDFA {
619//     pub(crate) fn none() -> ReverseDFA {
620//         ReverseDFA(None)
621//     }
622
623//     pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> ReverseDFA {
624//         ReverseDFA(ReverseDFAEngine::new(info, nfarev))
625//     }
626
627//     #[cfg_attr(feature = "perf-inline", inline(always))]
628//     pub(crate) fn get(&self, _input: &mut Input<impl Cursor>) -> Option<&ReverseDFAEngine> {
629//         let engine = self.0.as_ref()?;
630//         Some(engine)
631//     }
632
633//     pub(crate) fn is_some(&self) -> bool {
634//         self.0.is_some()
635//     }
636
637//     pub(crate) fn memory_usage(&self) -> usize {
638//         self.0.as_ref().map_or(0, |e| e.memory_usage())
639//     }
640// }
641
642// #[derive(Debug)]
643// pub(crate) struct ReverseDFAEngine(
644//     #[cfg(feature = "dfa-build")] dfa::dense::DFA<Vec<u32>>,
645//     #[cfg(not(feature = "dfa-build"))] (),
646// );
647
648// impl ReverseDFAEngine {
649//     pub(crate) fn new(info: &RegexInfo, nfarev: &NFA) -> Option<ReverseDFAEngine> {
650//         #[cfg(feature = "dfa-build")]
651//         {
652//             if !info.config().get_dfa() {
653//                 return None;
654//             }
655//             // If our NFA is anything but small, don't even bother with a DFA.
656//             if let Some(state_limit) = info.config().get_dfa_state_limit() {
657//                 if nfarev.states().len() > state_limit {
658//                     debug!(
659//                         "skipping full reverse DFA because NFA has {} states, \
660//                          which exceeds the heuristic limit of {}",
661//                         nfarev.states().len(),
662//                         state_limit,
663//                     );
664//                     return None;
665//                 }
666//             }
667//             // We cut the size limit in two because the total heap used by DFA
668//             // construction is determinization aux memory and the DFA itself,
669//             // and those things are configured independently in the lower level
670//             // DFA builder API.
671//             let size_limit = info.config().get_dfa_size_limit().map(|n| n / 2);
672//             // Since we only use this for reverse searches, we can hard-code
673//             // a number of things like match semantics, prefilters, starts
674//             // for each pattern and so on. We also disable acceleration since
675//             // it's incompatible with limited searches (which is the only
676//             // operation we support for this kind of engine at the moment).
677//             let dfa_config = dfa::dense::Config::new()
678//                 .match_kind(MatchKind::All)
679//                 .prefilter(None)
680//                 .accelerate(false)
681//                 .start_kind(dfa::StartKind::Anchored)
682//                 .starts_for_each_pattern(false)
683//                 .byte_classes(info.config().get_byte_classes())
684//                 .unicode_word_boundary(true)
685//                 .specialize_start_states(false)
686//                 .determinize_size_limit(size_limit)
687//                 .dfa_size_limit(size_limit);
688//             let result = dfa::dense::Builder::new().configure(dfa_config).build_from_nfa(&nfarev);
689//             let rev = match result {
690//                 Ok(rev) => rev,
691//                 Err(_err) => {
692//                     debug!("full reverse DFA failed to build: {}", _err);
693//                     return None;
694//                 }
695//             };
696//             debug!("fully compiled reverse DFA built, {} bytes", rev.memory_usage());
697//             Some(ReverseDFAEngine(rev))
698//         }
699//         #[cfg(not(feature = "dfa-build"))]
700//         {
701//             None
702//         }
703//     }
704
705//     #[cfg_attr(feature = "perf-inline", inline(always))]
706//     pub(crate) fn try_search_half_rev_limited(
707//         &self,
708//         input: &mut Input<impl Cursor>,
709//         min_start: usize,
710//     ) -> Result<Option<HalfMatch>, RetryError> {
711//         #[cfg(feature = "dfa-build")]
712//         {
713//             let dfa = &self.0;
714//             crate::meta::limited::dfa_try_search_half_rev(dfa, input, min_start)
715//         }
716//         #[cfg(not(feature = "dfa-build"))]
717//         {
718//             // Impossible to reach because this engine is never constructed
719//             // if the requisite features aren't enabled.
720//             unreachable!()
721//         }
722//     }
723
724//     pub(crate) fn memory_usage(&self) -> usize {
725//         #[cfg(feature = "dfa-build")]
726//         {
727//             self.0.memory_usage()
728//         }
729//         #[cfg(not(feature = "dfa-build"))]
730//         {
731//             // Impossible to reach because this engine is never constructed
732//             // if the requisite features aren't enabled.
733//             unreachable!()
734//         }
735//     }
736// }