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// }