Coverage Report

Created: 2025-07-23 07:16

/src/regex/regex-automata/src/meta/strategy.rs
Line
Count
Source (jump to first uncovered line)
1
use core::{
2
    fmt::Debug,
3
    panic::{RefUnwindSafe, UnwindSafe},
4
};
5
6
use alloc::sync::Arc;
7
8
use regex_syntax::hir::{literal, Hir};
9
10
use crate::{
11
    meta::{
12
        error::{BuildError, RetryError, RetryFailError, RetryQuadraticError},
13
        regex::{Cache, RegexInfo},
14
        reverse_inner, wrappers,
15
    },
16
    nfa::thompson::{self, WhichCaptures, NFA},
17
    util::{
18
        captures::{Captures, GroupInfo},
19
        look::LookMatcher,
20
        prefilter::{self, Prefilter, PrefilterI},
21
        primitives::{NonMaxUsize, PatternID},
22
        search::{Anchored, HalfMatch, Input, Match, MatchKind, PatternSet},
23
    },
24
};
25
26
/// A trait that represents a single meta strategy. Its main utility is in
27
/// providing a way to do dynamic dispatch over a few choices.
28
///
29
/// Why dynamic dispatch? I actually don't have a super compelling reason, and
30
/// importantly, I have not benchmarked it with the main alternative: an enum.
31
/// I went with dynamic dispatch initially because the regex engine search code
32
/// really can't be inlined into caller code in most cases because it's just
33
/// too big. In other words, it is already expected that every regex search
34
/// will entail at least the cost of a function call.
35
///
36
/// I do wonder whether using enums would result in better codegen overall
37
/// though. It's a worthwhile experiment to try. Probably the most interesting
38
/// benchmark to run in such a case would be one with a high match count. That
39
/// is, a benchmark to test the overall latency of a search call.
40
pub(super) trait Strategy:
41
    Debug + Send + Sync + RefUnwindSafe + UnwindSafe + 'static
42
{
43
    fn group_info(&self) -> &GroupInfo;
44
45
    fn create_cache(&self) -> Cache;
46
47
    fn reset_cache(&self, cache: &mut Cache);
48
49
    fn is_accelerated(&self) -> bool;
50
51
    fn memory_usage(&self) -> usize;
52
53
    fn search(&self, cache: &mut Cache, input: &Input<'_>) -> Option<Match>;
54
55
    fn search_half(
56
        &self,
57
        cache: &mut Cache,
58
        input: &Input<'_>,
59
    ) -> Option<HalfMatch>;
60
61
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool;
62
63
    fn search_slots(
64
        &self,
65
        cache: &mut Cache,
66
        input: &Input<'_>,
67
        slots: &mut [Option<NonMaxUsize>],
68
    ) -> Option<PatternID>;
69
70
    fn which_overlapping_matches(
71
        &self,
72
        cache: &mut Cache,
73
        input: &Input<'_>,
74
        patset: &mut PatternSet,
75
    );
76
}
77
78
80.8k
pub(super) fn new(
79
80.8k
    info: &RegexInfo,
80
80.8k
    hirs: &[&Hir],
81
80.8k
) -> Result<Arc<dyn Strategy>, BuildError> {
82
    // At this point, we're committed to a regex engine of some kind. So pull
83
    // out a prefilter if we can, which will feed to each of the constituent
84
    // regex engines.
85
80.8k
    let pre = if info.is_always_anchored_start() {
86
        // PERF: I'm not sure we necessarily want to do this... We may want to
87
        // run a prefilter for quickly rejecting in some cases. The problem
88
        // is that anchored searches overlap quite a bit with the use case
89
        // of "run a regex on every line to extract data." In that case, the
90
        // regex always matches, so running a prefilter doesn't really help us
91
        // there. The main place where a prefilter helps in an anchored search
92
        // is if the anchored search is not expected to match frequently. That
93
        // is, the prefilter gives us a way to possibly reject a haystack very
94
        // quickly.
95
        //
96
        // Maybe we should do use a prefilter, but only for longer haystacks?
97
        // Or maybe we should only use a prefilter when we think it's "fast"?
98
        //
99
        // Interestingly, I think we currently lack the infrastructure for
100
        // disabling a prefilter based on haystack length. That would probably
101
        // need to be a new 'Input' option. (Interestingly, an 'Input' used to
102
        // carry a 'Prefilter' with it, but I moved away from that.)
103
1.92k
        debug!("skipping literal extraction since regex is anchored");
104
1.92k
        None
105
78.9k
    } else if let Some(pre) = info.config().get_prefilter() {
106
0
        debug!(
107
0
            "skipping literal extraction since the caller provided a prefilter"
108
        );
109
0
        Some(pre.clone())
110
78.9k
    } else if info.config().get_auto_prefilter() {
111
78.9k
        let kind = info.config().get_match_kind();
112
78.9k
        let prefixes = crate::util::prefilter::prefixes(kind, hirs);
113
        // If we can build a full `Strategy` from just the extracted prefixes,
114
        // then we can short-circuit and avoid building a regex engine at all.
115
78.9k
        if let Some(pre) = Pre::from_prefixes(info, &prefixes) {
116
12.8k
            debug!(
117
0
                "found that the regex can be broken down to a literal \
118
0
                 search, avoiding the regex engine entirely",
119
            );
120
12.8k
            return Ok(pre);
121
66.1k
        }
122
        // This now attempts another short-circuit of the regex engine: if we
123
        // have a huge alternation of just plain literals, then we can just use
124
        // Aho-Corasick for that and avoid the regex engine entirely.
125
        //
126
        // You might think this case would just be handled by
127
        // `Pre::from_prefixes`, but that technique relies on heuristic literal
128
        // extraction from the corresponding `Hir`. That works, but part of
129
        // heuristics limit the size and number of literals returned. This case
130
        // will specifically handle patterns with very large alternations.
131
        //
132
        // One wonders if we should just roll this our heuristic literal
133
        // extraction, and then I think this case could disappear entirely.
134
66.1k
        if let Some(pre) = Pre::from_alternation_literals(info, hirs) {
135
253
            debug!(
136
0
                "found plain alternation of literals, \
137
0
                 avoiding regex engine entirely and using Aho-Corasick"
138
            );
139
253
            return Ok(pre);
140
65.8k
        }
141
65.8k
        prefixes.literals().and_then(|strings| {
142
28.5k
            debug!(
143
0
                "creating prefilter from {} literals: {:?}",
144
0
                strings.len(),
145
                strings,
146
            );
147
28.5k
            Prefilter::new(kind, strings)
148
65.8k
        })
149
    } else {
150
0
        debug!("skipping literal extraction since prefilters were disabled");
151
0
        None
152
    };
153
67.8k
    let mut core = Core::new(info.clone(), pre.clone(), hirs)?;
154
    // Now that we have our core regex engines built, there are a few cases
155
    // where we can do a little bit better than just a normal "search forward
156
    // and maybe use a prefilter when in a start state." However, these cases
157
    // may not always work or otherwise build on top of the Core searcher.
158
    // For example, the reverse anchored optimization seems like it might
159
    // always work, but only the DFAs support reverse searching and the DFAs
160
    // might give up or quit for reasons. If we had, e.g., a PikeVM that
161
    // supported reverse searching, then we could avoid building a full Core
162
    // engine for this case.
163
65.7k
    core = match ReverseAnchored::new(core) {
164
64.7k
        Err(core) => core,
165
1.04k
        Ok(ra) => {
166
1.04k
            debug!("using reverse anchored strategy");
167
1.04k
            return Ok(Arc::new(ra));
168
        }
169
    };
170
64.7k
    core = match ReverseSuffix::new(core, hirs) {
171
61.0k
        Err(core) => core,
172
3.70k
        Ok(rs) => {
173
3.70k
            debug!("using reverse suffix strategy");
174
3.70k
            return Ok(Arc::new(rs));
175
        }
176
    };
177
61.0k
    core = match ReverseInner::new(core, hirs) {
178
56.2k
        Err(core) => core,
179
4.75k
        Ok(ri) => {
180
4.75k
            debug!("using reverse inner strategy");
181
4.75k
            return Ok(Arc::new(ri));
182
        }
183
    };
184
56.2k
    debug!("using core strategy");
185
56.2k
    Ok(Arc::new(core))
186
80.8k
}
187
188
#[derive(Clone, Debug)]
189
struct Pre<P> {
190
    pre: P,
191
    group_info: GroupInfo,
192
}
193
194
impl<P: PrefilterI> Pre<P> {
195
13.0k
    fn new(pre: P) -> Arc<dyn Strategy> {
196
13.0k
        // The only thing we support when we use prefilters directly as a
197
13.0k
        // strategy is the start and end of the overall match for a single
198
13.0k
        // pattern. In other words, exactly one implicit capturing group. Which
199
13.0k
        // is exactly what we use here for a GroupInfo.
200
13.0k
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
13.0k
        Arc::new(Pre { pre, group_info })
202
13.0k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick>>::new
Line
Count
Source
195
1.09k
    fn new(pre: P) -> Arc<dyn Strategy> {
196
1.09k
        // The only thing we support when we use prefilters directly as a
197
1.09k
        // strategy is the start and end of the overall match for a single
198
1.09k
        // pattern. In other words, exactly one implicit capturing group. Which
199
1.09k
        // is exactly what we use here for a GroupInfo.
200
1.09k
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
1.09k
        Arc::new(Pre { pre, group_info })
202
1.09k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy>>::new
Line
Count
Source
195
8.20k
    fn new(pre: P) -> Arc<dyn Strategy> {
196
8.20k
        // The only thing we support when we use prefilters directly as a
197
8.20k
        // strategy is the start and end of the overall match for a single
198
8.20k
        // pattern. In other words, exactly one implicit capturing group. Which
199
8.20k
        // is exactly what we use here for a GroupInfo.
200
8.20k
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
8.20k
        Arc::new(Pre { pre, group_info })
202
8.20k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr>>::new
Line
Count
Source
195
970
    fn new(pre: P) -> Arc<dyn Strategy> {
196
970
        // The only thing we support when we use prefilters directly as a
197
970
        // strategy is the start and end of the overall match for a single
198
970
        // pattern. In other words, exactly one implicit capturing group. Which
199
970
        // is exactly what we use here for a GroupInfo.
200
970
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
970
        Arc::new(Pre { pre, group_info })
202
970
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2>>::new
Line
Count
Source
195
193
    fn new(pre: P) -> Arc<dyn Strategy> {
196
193
        // The only thing we support when we use prefilters directly as a
197
193
        // strategy is the start and end of the overall match for a single
198
193
        // pattern. In other words, exactly one implicit capturing group. Which
199
193
        // is exactly what we use here for a GroupInfo.
200
193
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
193
        Arc::new(Pre { pre, group_info })
202
193
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3>>::new
Line
Count
Source
195
155
    fn new(pre: P) -> Arc<dyn Strategy> {
196
155
        // The only thing we support when we use prefilters directly as a
197
155
        // strategy is the start and end of the overall match for a single
198
155
        // pattern. In other words, exactly one implicit capturing group. Which
199
155
        // is exactly what we use here for a GroupInfo.
200
155
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
155
        Arc::new(Pre { pre, group_info })
202
155
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem>>::new
Line
Count
Source
195
2.39k
    fn new(pre: P) -> Arc<dyn Strategy> {
196
2.39k
        // The only thing we support when we use prefilters directly as a
197
2.39k
        // strategy is the start and end of the overall match for a single
198
2.39k
        // pattern. In other words, exactly one implicit capturing group. Which
199
2.39k
        // is exactly what we use here for a GroupInfo.
200
2.39k
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
2.39k
        Arc::new(Pre { pre, group_info })
202
2.39k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet>>::new
Line
Count
Source
195
43
    fn new(pre: P) -> Arc<dyn Strategy> {
196
43
        // The only thing we support when we use prefilters directly as a
197
43
        // strategy is the start and end of the overall match for a single
198
43
        // pattern. In other words, exactly one implicit capturing group. Which
199
43
        // is exactly what we use here for a GroupInfo.
200
43
        let group_info = GroupInfo::new([[None::<&str>]]).unwrap();
201
43
        Arc::new(Pre { pre, group_info })
202
43
    }
203
}
204
205
// This is a little weird, but we don't actually care about the type parameter
206
// here because we're selecting which underlying prefilter to use. So we just
207
// define it on an arbitrary type.
208
impl Pre<()> {
209
    /// Given a sequence of prefixes, attempt to return a full `Strategy` using
210
    /// just the prefixes.
211
    ///
212
    /// Basically, this occurs when the prefixes given not just prefixes,
213
    /// but an enumeration of the entire language matched by the regular
214
    /// expression.
215
    ///
216
    /// A number of other conditions need to be true too. For example, there
217
    /// can be only one pattern, the number of explicit capture groups is 0, no
218
    /// look-around assertions and so on.
219
    ///
220
    /// Note that this ignores `Config::get_auto_prefilter` because if this
221
    /// returns something, then it isn't a prefilter but a matcher itself.
222
    /// Therefore, it shouldn't suffer from the problems typical to prefilters
223
    /// (such as a high false positive rate).
224
78.9k
    fn from_prefixes(
225
78.9k
        info: &RegexInfo,
226
78.9k
        prefixes: &literal::Seq,
227
78.9k
    ) -> Option<Arc<dyn Strategy>> {
228
78.9k
        let kind = info.config().get_match_kind();
229
78.9k
        // Check to see if our prefixes are exact, which means we might be
230
78.9k
        // able to bypass the regex engine entirely and just rely on literal
231
78.9k
        // searches.
232
78.9k
        if !prefixes.is_exact() {
233
54.2k
            return None;
234
24.7k
        }
235
24.7k
        // We also require that we have a single regex pattern. Namely,
236
24.7k
        // we reuse the prefilter infrastructure to implement search and
237
24.7k
        // prefilters only report spans. Prefilters don't know about pattern
238
24.7k
        // IDs. The multi-regex case isn't a lost cause, we might still use
239
24.7k
        // Aho-Corasick and we might still just use a regular prefilter, but
240
24.7k
        // that's done below.
241
24.7k
        if info.pattern_len() != 1 {
242
0
            return None;
243
24.7k
        }
244
24.7k
        // We can't have any capture groups either. The literal engines don't
245
24.7k
        // know how to deal with things like '(foo)(bar)'. In that case, a
246
24.7k
        // prefilter will just be used and then the regex engine will resolve
247
24.7k
        // the capture groups.
248
24.7k
        if info.props()[0].explicit_captures_len() != 0 {
249
3.55k
            return None;
250
21.1k
        }
251
21.1k
        // We also require that it has zero look-around assertions. Namely,
252
21.1k
        // literal extraction treats look-around assertions as if they match
253
21.1k
        // *every* empty string. But of course, that isn't true. So for
254
21.1k
        // example, 'foo\bquux' never matches anything, but 'fooquux' is
255
21.1k
        // extracted from that as an exact literal. Such cases should just run
256
21.1k
        // the regex engine. 'fooquux' will be used as a normal prefilter, and
257
21.1k
        // then the regex engine will try to look for an actual match.
258
21.1k
        if !info.props()[0].look_set().is_empty() {
259
6.47k
            return None;
260
14.6k
        }
261
14.6k
        // Finally, currently, our prefilters are all oriented around
262
14.6k
        // leftmost-first match semantics, so don't try to use them if the
263
14.6k
        // caller asked for anything else.
264
14.6k
        if kind != MatchKind::LeftmostFirst {
265
0
            return None;
266
14.6k
        }
267
14.6k
        // The above seems like a lot of requirements to meet, but it applies
268
14.6k
        // to a lot of cases. 'foo', '[abc][123]' and 'foo|bar|quux' all meet
269
14.6k
        // the above criteria, for example.
270
14.6k
        //
271
14.6k
        // Note that this is effectively a latency optimization. If we didn't
272
14.6k
        // do this, then the extracted literals would still get bundled into
273
14.6k
        // a prefilter, and every regex engine capable of running unanchored
274
14.6k
        // searches supports prefilters. So this optimization merely sidesteps
275
14.6k
        // having to run the regex engine at all to confirm the match. Thus, it
276
14.6k
        // decreases the latency of a match.
277
14.6k
278
14.6k
        // OK because we know the set is exact and thus finite.
279
14.6k
        let prefixes = prefixes.literals().unwrap();
280
14.6k
        debug!(
281
0
            "trying to bypass regex engine by creating \
282
0
             prefilter from {} literals: {:?}",
283
0
            prefixes.len(),
284
            prefixes,
285
        );
286
14.6k
        let choice = match prefilter::Choice::new(kind, prefixes) {
287
12.8k
            Some(choice) => choice,
288
            None => {
289
1.87k
                debug!(
290
0
                    "regex bypass failed because no prefilter could be built"
291
                );
292
1.87k
                return None;
293
            }
294
        };
295
12.8k
        let strat: Arc<dyn Strategy> = match choice {
296
970
            prefilter::Choice::Memchr(pre) => Pre::new(pre),
297
193
            prefilter::Choice::Memchr2(pre) => Pre::new(pre),
298
155
            prefilter::Choice::Memchr3(pre) => Pre::new(pre),
299
2.39k
            prefilter::Choice::Memmem(pre) => Pre::new(pre),
300
8.20k
            prefilter::Choice::Teddy(pre) => Pre::new(pre),
301
43
            prefilter::Choice::ByteSet(pre) => Pre::new(pre),
302
842
            prefilter::Choice::AhoCorasick(pre) => Pre::new(pre),
303
        };
304
12.8k
        Some(strat)
305
78.9k
    }
306
307
    /// Attempts to extract an alternation of literals, and if it's deemed
308
    /// worth doing, returns an Aho-Corasick prefilter as a strategy.
309
    ///
310
    /// And currently, this only returns something when 'hirs.len() == 1'. This
311
    /// could in theory do something if there are multiple HIRs where all of
312
    /// them are alternation of literals, but I haven't had the time to go down
313
    /// that path yet.
314
66.1k
    fn from_alternation_literals(
315
66.1k
        info: &RegexInfo,
316
66.1k
        hirs: &[&Hir],
317
66.1k
    ) -> Option<Arc<dyn Strategy>> {
318
        use crate::util::prefilter::AhoCorasick;
319
320
66.1k
        let lits = crate::meta::literal::alternation_literals(info, hirs)?;
321
253
        let ac = AhoCorasick::new(MatchKind::LeftmostFirst, &lits)?;
322
253
        Some(Pre::new(ac))
323
66.1k
    }
324
}
325
326
// This implements Strategy for anything that implements PrefilterI.
327
//
328
// Note that this must only be used for regexes of length 1. Multi-regexes
329
// don't work here. The prefilter interface only provides the span of a match
330
// and not the pattern ID. (I did consider making it more expressive, but I
331
// couldn't figure out how to tie everything together elegantly.) Thus, so long
332
// as the regex only contains one pattern, we can simply assume that a match
333
// corresponds to PatternID::ZERO. And indeed, that's what we do here.
334
//
335
// In practice, since this impl is used to report matches directly and thus
336
// completely bypasses the regex engine, we only wind up using this under the
337
// following restrictions:
338
//
339
// * There must be only one pattern. As explained above.
340
// * The literal sequence must be finite and only contain exact literals.
341
// * There must not be any look-around assertions. If there are, the literals
342
// extracted might be exact, but a match doesn't necessarily imply an overall
343
// match. As a trivial example, 'foo\bbar' does not match 'foobar'.
344
// * The pattern must not have any explicit capturing groups. If it does, the
345
// caller might expect them to be resolved. e.g., 'foo(bar)'.
346
//
347
// So when all of those things are true, we use a prefilter directly as a
348
// strategy.
349
//
350
// In the case where the number of patterns is more than 1, we don't use this
351
// but do use a special Aho-Corasick strategy if all of the regexes are just
352
// simple literals or alternations of literals. (We also use the Aho-Corasick
353
// strategy when len(patterns)==1 if the number of literals is large. In that
354
// case, literal extraction gives up and will return an infinite set.)
355
impl<P: PrefilterI> Strategy for Pre<P> {
356
    #[cfg_attr(feature = "perf-inline", inline(always))]
357
14.3k
    fn group_info(&self) -> &GroupInfo {
358
14.3k
        &self.group_info
359
14.3k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::group_info
Line
Count
Source
357
592
    fn group_info(&self) -> &GroupInfo {
358
592
        &self.group_info
359
592
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::group_info
Line
Count
Source
357
10.3k
    fn group_info(&self) -> &GroupInfo {
358
10.3k
        &self.group_info
359
10.3k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::group_info
Line
Count
Source
357
817
    fn group_info(&self) -> &GroupInfo {
358
817
        &self.group_info
359
817
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::group_info
Line
Count
Source
357
141
    fn group_info(&self) -> &GroupInfo {
358
141
        &self.group_info
359
141
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::group_info
Line
Count
Source
357
139
    fn group_info(&self) -> &GroupInfo {
358
139
        &self.group_info
359
139
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::group_info
Line
Count
Source
357
2.18k
    fn group_info(&self) -> &GroupInfo {
358
2.18k
        &self.group_info
359
2.18k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::group_info
Line
Count
Source
357
60
    fn group_info(&self) -> &GroupInfo {
358
60
        &self.group_info
359
60
    }
360
361
8.08k
    fn create_cache(&self) -> Cache {
362
8.08k
        Cache {
363
8.08k
            capmatches: Captures::all(self.group_info().clone()),
364
8.08k
            pikevm: wrappers::PikeVMCache::none(),
365
8.08k
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
8.08k
            onepass: wrappers::OnePassCache::none(),
367
8.08k
            hybrid: wrappers::HybridCache::none(),
368
8.08k
            revhybrid: wrappers::ReverseHybridCache::none(),
369
8.08k
        }
370
8.08k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::create_cache
Line
Count
Source
361
343
    fn create_cache(&self) -> Cache {
362
343
        Cache {
363
343
            capmatches: Captures::all(self.group_info().clone()),
364
343
            pikevm: wrappers::PikeVMCache::none(),
365
343
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
343
            onepass: wrappers::OnePassCache::none(),
367
343
            hybrid: wrappers::HybridCache::none(),
368
343
            revhybrid: wrappers::ReverseHybridCache::none(),
369
343
        }
370
343
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::create_cache
Line
Count
Source
361
6.29k
    fn create_cache(&self) -> Cache {
362
6.29k
        Cache {
363
6.29k
            capmatches: Captures::all(self.group_info().clone()),
364
6.29k
            pikevm: wrappers::PikeVMCache::none(),
365
6.29k
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
6.29k
            onepass: wrappers::OnePassCache::none(),
367
6.29k
            hybrid: wrappers::HybridCache::none(),
368
6.29k
            revhybrid: wrappers::ReverseHybridCache::none(),
369
6.29k
        }
370
6.29k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::create_cache
Line
Count
Source
361
215
    fn create_cache(&self) -> Cache {
362
215
        Cache {
363
215
            capmatches: Captures::all(self.group_info().clone()),
364
215
            pikevm: wrappers::PikeVMCache::none(),
365
215
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
215
            onepass: wrappers::OnePassCache::none(),
367
215
            hybrid: wrappers::HybridCache::none(),
368
215
            revhybrid: wrappers::ReverseHybridCache::none(),
369
215
        }
370
215
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::create_cache
Line
Count
Source
361
68
    fn create_cache(&self) -> Cache {
362
68
        Cache {
363
68
            capmatches: Captures::all(self.group_info().clone()),
364
68
            pikevm: wrappers::PikeVMCache::none(),
365
68
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
68
            onepass: wrappers::OnePassCache::none(),
367
68
            hybrid: wrappers::HybridCache::none(),
368
68
            revhybrid: wrappers::ReverseHybridCache::none(),
369
68
        }
370
68
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::create_cache
Line
Count
Source
361
66
    fn create_cache(&self) -> Cache {
362
66
        Cache {
363
66
            capmatches: Captures::all(self.group_info().clone()),
364
66
            pikevm: wrappers::PikeVMCache::none(),
365
66
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
66
            onepass: wrappers::OnePassCache::none(),
367
66
            hybrid: wrappers::HybridCache::none(),
368
66
            revhybrid: wrappers::ReverseHybridCache::none(),
369
66
        }
370
66
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::create_cache
Line
Count
Source
361
1.06k
    fn create_cache(&self) -> Cache {
362
1.06k
        Cache {
363
1.06k
            capmatches: Captures::all(self.group_info().clone()),
364
1.06k
            pikevm: wrappers::PikeVMCache::none(),
365
1.06k
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
1.06k
            onepass: wrappers::OnePassCache::none(),
367
1.06k
            hybrid: wrappers::HybridCache::none(),
368
1.06k
            revhybrid: wrappers::ReverseHybridCache::none(),
369
1.06k
        }
370
1.06k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::create_cache
Line
Count
Source
361
36
    fn create_cache(&self) -> Cache {
362
36
        Cache {
363
36
            capmatches: Captures::all(self.group_info().clone()),
364
36
            pikevm: wrappers::PikeVMCache::none(),
365
36
            backtrack: wrappers::BoundedBacktrackerCache::none(),
366
36
            onepass: wrappers::OnePassCache::none(),
367
36
            hybrid: wrappers::HybridCache::none(),
368
36
            revhybrid: wrappers::ReverseHybridCache::none(),
369
36
        }
370
36
    }
371
372
0
    fn reset_cache(&self, _cache: &mut Cache) {}
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::reset_cache
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::reset_cache
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::reset_cache
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::reset_cache
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::reset_cache
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::reset_cache
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::reset_cache
373
374
0
    fn is_accelerated(&self) -> bool {
375
0
        self.pre.is_fast()
376
0
    }
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::is_accelerated
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::is_accelerated
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::is_accelerated
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::is_accelerated
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::is_accelerated
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::is_accelerated
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::is_accelerated
377
378
0
    fn memory_usage(&self) -> usize {
379
0
        self.pre.memory_usage()
380
0
    }
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::memory_usage
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::memory_usage
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::memory_usage
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::memory_usage
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::memory_usage
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::memory_usage
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::memory_usage
381
382
    #[cfg_attr(feature = "perf-inline", inline(always))]
383
16.8k
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
16.8k
        if input.is_done() {
385
0
            return None;
386
16.8k
        }
387
16.8k
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::search::{closure#0}
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::search::{closure#0}
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::search::{closure#0}
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::search::{closure#0}
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::search::{closure#0}
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::search::{closure#0}
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::search::{closure#0}
392
16.8k
        }
393
16.8k
        self.pre
394
16.8k
            .find(input.haystack(), input.get_span())
395
16.8k
            .map(|sp| Match::new(PatternID::ZERO, sp))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::search::{closure#1}
Line
Count
Source
395
184
            .map(|sp| Match::new(PatternID::ZERO, sp))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::search::{closure#1}
Line
Count
Source
395
2.44k
            .map(|sp| Match::new(PatternID::ZERO, sp))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::search::{closure#1}
Line
Count
Source
395
433
            .map(|sp| Match::new(PatternID::ZERO, sp))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::search::{closure#1}
Line
Count
Source
395
121
            .map(|sp| Match::new(PatternID::ZERO, sp))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::search::{closure#1}
Line
Count
Source
395
144
            .map(|sp| Match::new(PatternID::ZERO, sp))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::search::{closure#1}
Line
Count
Source
395
286
            .map(|sp| Match::new(PatternID::ZERO, sp))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::search::{closure#1}
Line
Count
Source
395
38
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
16.8k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::search
Line
Count
Source
383
503
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
503
        if input.is_done() {
385
0
            return None;
386
503
        }
387
503
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
392
503
        }
393
503
        self.pre
394
503
            .find(input.haystack(), input.get_span())
395
503
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
503
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::search
Line
Count
Source
383
12.9k
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
12.9k
        if input.is_done() {
385
0
            return None;
386
12.9k
        }
387
12.9k
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
392
12.9k
        }
393
12.9k
        self.pre
394
12.9k
            .find(input.haystack(), input.get_span())
395
12.9k
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
12.9k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::search
Line
Count
Source
383
583
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
583
        if input.is_done() {
385
0
            return None;
386
583
        }
387
583
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
392
583
        }
393
583
        self.pre
394
583
            .find(input.haystack(), input.get_span())
395
583
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
583
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::search
Line
Count
Source
383
162
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
162
        if input.is_done() {
385
0
            return None;
386
162
        }
387
162
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
392
162
        }
393
162
        self.pre
394
162
            .find(input.haystack(), input.get_span())
395
162
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
162
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::search
Line
Count
Source
383
168
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
168
        if input.is_done() {
385
0
            return None;
386
168
        }
387
168
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
392
168
        }
393
168
        self.pre
394
168
            .find(input.haystack(), input.get_span())
395
168
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
168
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::search
Line
Count
Source
383
2.37k
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
2.37k
        if input.is_done() {
385
0
            return None;
386
2.37k
        }
387
2.37k
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
392
2.37k
        }
393
2.37k
        self.pre
394
2.37k
            .find(input.haystack(), input.get_span())
395
2.37k
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
2.37k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::search
Line
Count
Source
383
80
    fn search(&self, _cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
384
80
        if input.is_done() {
385
0
            return None;
386
80
        }
387
80
        if input.get_anchored().is_anchored() {
388
0
            return self
389
0
                .pre
390
0
                .prefix(input.haystack(), input.get_span())
391
0
                .map(|sp| Match::new(PatternID::ZERO, sp));
392
80
        }
393
80
        self.pre
394
80
            .find(input.haystack(), input.get_span())
395
80
            .map(|sp| Match::new(PatternID::ZERO, sp))
396
80
    }
397
398
    #[cfg_attr(feature = "perf-inline", inline(always))]
399
6.86k
    fn search_half(
400
6.86k
        &self,
401
6.86k
        cache: &mut Cache,
402
6.86k
        input: &Input<'_>,
403
6.86k
    ) -> Option<HalfMatch> {
404
6.86k
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::search_half::{closure#0}
Line
Count
Source
404
126
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::search_half::{closure#0}
Line
Count
Source
404
931
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::search_half::{closure#0}
Line
Count
Source
404
133
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::search_half::{closure#0}
Line
Count
Source
404
36
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::search_half::{closure#0}
Line
Count
Source
404
34
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::search_half::{closure#0}
Line
Count
Source
404
88
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::search_half::{closure#0}
Line
Count
Source
404
12
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
6.86k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::search_half
Line
Count
Source
399
318
    fn search_half(
400
318
        &self,
401
318
        cache: &mut Cache,
402
318
        input: &Input<'_>,
403
318
    ) -> Option<HalfMatch> {
404
318
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
318
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::search_half
Line
Count
Source
399
5.45k
    fn search_half(
400
5.45k
        &self,
401
5.45k
        cache: &mut Cache,
402
5.45k
        input: &Input<'_>,
403
5.45k
    ) -> Option<HalfMatch> {
404
5.45k
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
5.45k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::search_half
Line
Count
Source
399
185
    fn search_half(
400
185
        &self,
401
185
        cache: &mut Cache,
402
185
        input: &Input<'_>,
403
185
    ) -> Option<HalfMatch> {
404
185
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
185
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::search_half
Line
Count
Source
399
50
    fn search_half(
400
50
        &self,
401
50
        cache: &mut Cache,
402
50
        input: &Input<'_>,
403
50
    ) -> Option<HalfMatch> {
404
50
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
50
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::search_half
Line
Count
Source
399
44
    fn search_half(
400
44
        &self,
401
44
        cache: &mut Cache,
402
44
        input: &Input<'_>,
403
44
    ) -> Option<HalfMatch> {
404
44
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
44
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::search_half
Line
Count
Source
399
782
    fn search_half(
400
782
        &self,
401
782
        cache: &mut Cache,
402
782
        input: &Input<'_>,
403
782
    ) -> Option<HalfMatch> {
404
782
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
782
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::search_half
Line
Count
Source
399
31
    fn search_half(
400
31
        &self,
401
31
        cache: &mut Cache,
402
31
        input: &Input<'_>,
403
31
    ) -> Option<HalfMatch> {
404
31
        self.search(cache, input).map(|m| HalfMatch::new(m.pattern(), m.end()))
405
31
    }
406
407
    #[cfg_attr(feature = "perf-inline", inline(always))]
408
1.22k
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
1.22k
        self.search(cache, input).is_some()
410
1.22k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::is_match
Line
Count
Source
408
25
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
25
        self.search(cache, input).is_some()
410
25
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::is_match
Line
Count
Source
408
838
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
838
        self.search(cache, input).is_some()
410
838
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::is_match
Line
Count
Source
408
30
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
30
        self.search(cache, input).is_some()
410
30
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::is_match
Line
Count
Source
408
18
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
18
        self.search(cache, input).is_some()
410
18
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::is_match
Line
Count
Source
408
22
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
22
        self.search(cache, input).is_some()
410
22
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::is_match
Line
Count
Source
408
282
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
282
        self.search(cache, input).is_some()
410
282
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::is_match
Line
Count
Source
408
5
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
409
5
        self.search(cache, input).is_some()
410
5
    }
411
412
    #[cfg_attr(feature = "perf-inline", inline(always))]
413
4.37k
    fn search_slots(
414
4.37k
        &self,
415
4.37k
        cache: &mut Cache,
416
4.37k
        input: &Input<'_>,
417
4.37k
        slots: &mut [Option<NonMaxUsize>],
418
4.37k
    ) -> Option<PatternID> {
419
4.37k
        let m = self.search(cache, input)?;
420
1.02k
        if let Some(slot) = slots.get_mut(0) {
421
1.02k
            *slot = NonMaxUsize::new(m.start());
422
1.02k
        }
423
1.02k
        if let Some(slot) = slots.get_mut(1) {
424
1.02k
            *slot = NonMaxUsize::new(m.end());
425
1.02k
        }
426
1.02k
        Some(m.pattern())
427
4.37k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::search_slots
Line
Count
Source
413
80
    fn search_slots(
414
80
        &self,
415
80
        cache: &mut Cache,
416
80
        input: &Input<'_>,
417
80
        slots: &mut [Option<NonMaxUsize>],
418
80
    ) -> Option<PatternID> {
419
80
        let m = self.search(cache, input)?;
420
25
        if let Some(slot) = slots.get_mut(0) {
421
25
            *slot = NonMaxUsize::new(m.start());
422
25
        }
423
25
        if let Some(slot) = slots.get_mut(1) {
424
25
            *slot = NonMaxUsize::new(m.end());
425
25
        }
426
25
        Some(m.pattern())
427
80
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::search_slots
Line
Count
Source
413
3.33k
    fn search_slots(
414
3.33k
        &self,
415
3.33k
        cache: &mut Cache,
416
3.33k
        input: &Input<'_>,
417
3.33k
        slots: &mut [Option<NonMaxUsize>],
418
3.33k
    ) -> Option<PatternID> {
419
3.33k
        let m = self.search(cache, input)?;
420
682
        if let Some(slot) = slots.get_mut(0) {
421
682
            *slot = NonMaxUsize::new(m.start());
422
682
        }
423
682
        if let Some(slot) = slots.get_mut(1) {
424
682
            *slot = NonMaxUsize::new(m.end());
425
682
        }
426
682
        Some(m.pattern())
427
3.33k
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::search_slots
Line
Count
Source
413
184
    fn search_slots(
414
184
        &self,
415
184
        cache: &mut Cache,
416
184
        input: &Input<'_>,
417
184
        slots: &mut [Option<NonMaxUsize>],
418
184
    ) -> Option<PatternID> {
419
184
        let m = self.search(cache, input)?;
420
139
        if let Some(slot) = slots.get_mut(0) {
421
139
            *slot = NonMaxUsize::new(m.start());
422
139
        }
423
139
        if let Some(slot) = slots.get_mut(1) {
424
139
            *slot = NonMaxUsize::new(m.end());
425
139
        }
426
139
        Some(m.pattern())
427
184
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::search_slots
Line
Count
Source
413
47
    fn search_slots(
414
47
        &self,
415
47
        cache: &mut Cache,
416
47
        input: &Input<'_>,
417
47
        slots: &mut [Option<NonMaxUsize>],
418
47
    ) -> Option<PatternID> {
419
47
        let m = self.search(cache, input)?;
420
37
        if let Some(slot) = slots.get_mut(0) {
421
37
            *slot = NonMaxUsize::new(m.start());
422
37
        }
423
37
        if let Some(slot) = slots.get_mut(1) {
424
37
            *slot = NonMaxUsize::new(m.end());
425
37
        }
426
37
        Some(m.pattern())
427
47
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::search_slots
Line
Count
Source
413
51
    fn search_slots(
414
51
        &self,
415
51
        cache: &mut Cache,
416
51
        input: &Input<'_>,
417
51
        slots: &mut [Option<NonMaxUsize>],
418
51
    ) -> Option<PatternID> {
419
51
        let m = self.search(cache, input)?;
420
46
        if let Some(slot) = slots.get_mut(0) {
421
46
            *slot = NonMaxUsize::new(m.start());
422
46
        }
423
46
        if let Some(slot) = slots.get_mut(1) {
424
46
            *slot = NonMaxUsize::new(m.end());
425
46
        }
426
46
        Some(m.pattern())
427
51
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::search_slots
Line
Count
Source
413
657
    fn search_slots(
414
657
        &self,
415
657
        cache: &mut Cache,
416
657
        input: &Input<'_>,
417
657
        slots: &mut [Option<NonMaxUsize>],
418
657
    ) -> Option<PatternID> {
419
657
        let m = self.search(cache, input)?;
420
80
        if let Some(slot) = slots.get_mut(0) {
421
80
            *slot = NonMaxUsize::new(m.start());
422
80
        }
423
80
        if let Some(slot) = slots.get_mut(1) {
424
80
            *slot = NonMaxUsize::new(m.end());
425
80
        }
426
80
        Some(m.pattern())
427
657
    }
<regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::search_slots
Line
Count
Source
413
22
    fn search_slots(
414
22
        &self,
415
22
        cache: &mut Cache,
416
22
        input: &Input<'_>,
417
22
        slots: &mut [Option<NonMaxUsize>],
418
22
    ) -> Option<PatternID> {
419
22
        let m = self.search(cache, input)?;
420
12
        if let Some(slot) = slots.get_mut(0) {
421
12
            *slot = NonMaxUsize::new(m.start());
422
12
        }
423
12
        if let Some(slot) = slots.get_mut(1) {
424
12
            *slot = NonMaxUsize::new(m.end());
425
12
        }
426
12
        Some(m.pattern())
427
22
    }
428
429
    #[cfg_attr(feature = "perf-inline", inline(always))]
430
0
    fn which_overlapping_matches(
431
0
        &self,
432
0
        cache: &mut Cache,
433
0
        input: &Input<'_>,
434
0
        patset: &mut PatternSet,
435
0
    ) {
436
0
        if self.search(cache, input).is_some() {
437
0
            patset.insert(PatternID::ZERO);
438
0
        }
439
0
    }
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::aho_corasick::AhoCorasick> as regex_automata::meta::strategy::Strategy>::which_overlapping_matches
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::teddy::Teddy> as regex_automata::meta::strategy::Strategy>::which_overlapping_matches
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr> as regex_automata::meta::strategy::Strategy>::which_overlapping_matches
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr2> as regex_automata::meta::strategy::Strategy>::which_overlapping_matches
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memchr::Memchr3> as regex_automata::meta::strategy::Strategy>::which_overlapping_matches
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::memmem::Memmem> as regex_automata::meta::strategy::Strategy>::which_overlapping_matches
Unexecuted instantiation: <regex_automata::meta::strategy::Pre<regex_automata::util::prefilter::byteset::ByteSet> as regex_automata::meta::strategy::Strategy>::which_overlapping_matches
440
}
441
442
#[derive(Debug)]
443
struct Core {
444
    info: RegexInfo,
445
    pre: Option<Prefilter>,
446
    nfa: NFA,
447
    nfarev: Option<NFA>,
448
    pikevm: wrappers::PikeVM,
449
    backtrack: wrappers::BoundedBacktracker,
450
    onepass: wrappers::OnePass,
451
    hybrid: wrappers::Hybrid,
452
    dfa: wrappers::DFA,
453
}
454
455
impl Core {
456
67.8k
    fn new(
457
67.8k
        info: RegexInfo,
458
67.8k
        pre: Option<Prefilter>,
459
67.8k
        hirs: &[&Hir],
460
67.8k
    ) -> Result<Core, BuildError> {
461
67.8k
        let mut lookm = LookMatcher::new();
462
67.8k
        lookm.set_line_terminator(info.config().get_line_terminator());
463
67.8k
        let thompson_config = thompson::Config::new()
464
67.8k
            .utf8(info.config().get_utf8_empty())
465
67.8k
            .nfa_size_limit(info.config().get_nfa_size_limit())
466
67.8k
            .shrink(false)
467
67.8k
            .which_captures(info.config().get_which_captures())
468
67.8k
            .look_matcher(lookm);
469
67.8k
        let nfa = thompson::Compiler::new()
470
67.8k
            .configure(thompson_config.clone())
471
67.8k
            .build_many_from_hir(hirs)
472
67.8k
            .map_err(BuildError::nfa)?;
473
        // It's possible for the PikeVM or the BB to fail to build, even though
474
        // at this point, we already have a full NFA in hand. They can fail
475
        // when a Unicode word boundary is used but where Unicode word boundary
476
        // support is disabled at compile time, thus making it impossible to
477
        // match. (Construction can also fail if the NFA was compiled without
478
        // captures, but we always enable that above.)
479
65.9k
        let pikevm = wrappers::PikeVM::new(&info, pre.clone(), &nfa)?;
480
65.9k
        let backtrack =
481
65.9k
            wrappers::BoundedBacktracker::new(&info, pre.clone(), &nfa)?;
482
        // The onepass engine can of course fail to build, but we expect it to
483
        // fail in many cases because it is an optimization that doesn't apply
484
        // to all regexes. The 'OnePass' wrapper encapsulates this failure (and
485
        // logs a message if it occurs).
486
65.9k
        let onepass = wrappers::OnePass::new(&info, &nfa);
487
        // We try to encapsulate whether a particular regex engine should be
488
        // used within each respective wrapper, but the DFAs need a reverse NFA
489
        // to build itself, and we really do not want to build a reverse NFA if
490
        // we know we aren't going to use the lazy DFA. So we do a config check
491
        // up front, which is in practice the only way we won't try to use the
492
        // DFA.
493
65.7k
        let (nfarev, hybrid, dfa) =
494
65.9k
            if !info.config().get_hybrid() && !info.config().get_dfa() {
495
0
                (None, wrappers::Hybrid::none(), wrappers::DFA::none())
496
            } else {
497
                // FIXME: Technically, we don't quite yet KNOW that we need
498
                // a reverse NFA. It's possible for the DFAs below to both
499
                // fail to build just based on the forward NFA. In which case,
500
                // building the reverse NFA was totally wasted work. But...
501
                // fixing this requires breaking DFA construction apart into
502
                // two pieces: one for the forward part and another for the
503
                // reverse part. Quite annoying. Making it worse, when building
504
                // both DFAs fails, it's quite likely that the NFA is large and
505
                // that it will take quite some time to build the reverse NFA
506
                // too. So... it's really probably worth it to do this!
507
65.9k
                let nfarev = thompson::Compiler::new()
508
65.9k
                    // Currently, reverse NFAs don't support capturing groups,
509
65.9k
                    // so we MUST disable them. But even if we didn't have to,
510
65.9k
                    // we would, because nothing in this crate does anything
511
65.9k
                    // useful with capturing groups in reverse. And of course,
512
65.9k
                    // the lazy DFA ignores capturing groups in all cases.
513
65.9k
                    .configure(
514
65.9k
                        thompson_config
515
65.9k
                            .clone()
516
65.9k
                            .which_captures(WhichCaptures::None)
517
65.9k
                            .reverse(true),
518
65.9k
                    )
519
65.9k
                    .build_many_from_hir(hirs)
520
65.9k
                    .map_err(BuildError::nfa)?;
521
65.7k
                let dfa = if !info.config().get_dfa() {
522
0
                    wrappers::DFA::none()
523
                } else {
524
65.7k
                    wrappers::DFA::new(&info, pre.clone(), &nfa, &nfarev)
525
                };
526
65.7k
                let hybrid = if !info.config().get_hybrid() {
527
0
                    wrappers::Hybrid::none()
528
65.7k
                } else if dfa.is_some() {
529
38.5k
                    debug!("skipping lazy DFA because we have a full DFA");
530
38.5k
                    wrappers::Hybrid::none()
531
                } else {
532
27.1k
                    wrappers::Hybrid::new(&info, pre.clone(), &nfa, &nfarev)
533
                };
534
65.7k
                (Some(nfarev), hybrid, dfa)
535
            };
536
65.7k
        Ok(Core {
537
65.7k
            info,
538
65.7k
            pre,
539
65.7k
            nfa,
540
65.7k
            nfarev,
541
65.7k
            pikevm,
542
65.7k
            backtrack,
543
65.7k
            onepass,
544
65.7k
            hybrid,
545
65.7k
            dfa,
546
65.7k
        })
547
67.8k
    }
548
549
    #[cfg_attr(feature = "perf-inline", inline(always))]
550
8.10k
    fn try_search_mayfail(
551
8.10k
        &self,
552
8.10k
        cache: &mut Cache,
553
8.10k
        input: &Input<'_>,
554
8.10k
    ) -> Option<Result<Option<Match>, RetryFailError>> {
555
8.10k
        if let Some(e) = self.dfa.get(input) {
556
3.57k
            trace!("using full DFA for search at {:?}", input.get_span());
557
3.57k
            Some(e.try_search(input))
558
4.52k
        } else if let Some(e) = self.hybrid.get(input) {
559
4.52k
            trace!("using lazy DFA for search at {:?}", input.get_span());
560
4.52k
            Some(e.try_search(&mut cache.hybrid, input))
561
        } else {
562
0
            None
563
        }
564
8.10k
    }
565
566
26.7k
    fn search_nofail(
567
26.7k
        &self,
568
26.7k
        cache: &mut Cache,
569
26.7k
        input: &Input<'_>,
570
26.7k
    ) -> Option<Match> {
571
26.7k
        let caps = &mut cache.capmatches;
572
26.7k
        caps.set_pattern(None);
573
        // We manually inline 'try_search_slots_nofail' here because we need to
574
        // borrow from 'cache.capmatches' in this method, but if we do, then
575
        // we can't pass 'cache' wholesale to to 'try_slots_no_hybrid'. It's a
576
        // classic example of how the borrow checker inhibits decomposition.
577
        // There are of course work-arounds (more types and/or interior
578
        // mutability), but that's more annoying than this IMO.
579
26.7k
        let pid = if let Some(ref e) = self.onepass.get(input) {
580
604
            trace!("using OnePass for search at {:?}", input.get_span());
581
604
            e.search_slots(&mut cache.onepass, input, caps.slots_mut())
582
26.1k
        } else if let Some(ref e) = self.backtrack.get(input) {
583
20.6k
            trace!(
584
0
                "using BoundedBacktracker for search at {:?}",
585
0
                input.get_span()
586
            );
587
20.6k
            e.search_slots(&mut cache.backtrack, input, caps.slots_mut())
588
        } else {
589
5.51k
            trace!("using PikeVM for search at {:?}", input.get_span());
590
5.51k
            let e = self.pikevm.get();
591
5.51k
            e.search_slots(&mut cache.pikevm, input, caps.slots_mut())
592
        };
593
26.7k
        caps.set_pattern(pid);
594
26.7k
        caps.get_match()
595
26.7k
    }
596
597
9.19k
    fn search_half_nofail(
598
9.19k
        &self,
599
9.19k
        cache: &mut Cache,
600
9.19k
        input: &Input<'_>,
601
9.19k
    ) -> Option<HalfMatch> {
602
        // Only the lazy/full DFA returns half-matches, since the DFA requires
603
        // a reverse scan to find the start position. These fallback regex
604
        // engines can find the start and end in a single pass, so we just do
605
        // that and throw away the start offset to conform to the API.
606
9.19k
        let m = self.search_nofail(cache, input)?;
607
1.86k
        Some(HalfMatch::new(m.pattern(), m.end()))
608
9.19k
    }
609
610
7.18k
    fn search_slots_nofail(
611
7.18k
        &self,
612
7.18k
        cache: &mut Cache,
613
7.18k
        input: &Input<'_>,
614
7.18k
        slots: &mut [Option<NonMaxUsize>],
615
7.18k
    ) -> Option<PatternID> {
616
7.18k
        if let Some(ref e) = self.onepass.get(input) {
617
1.30k
            trace!(
618
0
                "using OnePass for capture search at {:?}",
619
0
                input.get_span()
620
            );
621
1.30k
            e.search_slots(&mut cache.onepass, input, slots)
622
5.87k
        } else if let Some(ref e) = self.backtrack.get(input) {
623
5.35k
            trace!(
624
0
                "using BoundedBacktracker for capture search at {:?}",
625
0
                input.get_span()
626
            );
627
5.35k
            e.search_slots(&mut cache.backtrack, input, slots)
628
        } else {
629
527
            trace!(
630
0
                "using PikeVM for capture search at {:?}",
631
0
                input.get_span()
632
            );
633
527
            let e = self.pikevm.get();
634
527
            e.search_slots(&mut cache.pikevm, input, slots)
635
        }
636
7.18k
    }
637
638
5.11k
    fn is_match_nofail(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
639
5.11k
        if let Some(ref e) = self.onepass.get(input) {
640
99
            trace!(
641
0
                "using OnePass for is-match search at {:?}",
642
0
                input.get_span()
643
            );
644
99
            e.search_slots(&mut cache.onepass, input, &mut []).is_some()
645
5.01k
        } else if let Some(ref e) = self.backtrack.get(input) {
646
2.41k
            trace!(
647
0
                "using BoundedBacktracker for is-match search at {:?}",
648
0
                input.get_span()
649
            );
650
2.41k
            e.is_match(&mut cache.backtrack, input)
651
        } else {
652
2.60k
            trace!(
653
0
                "using PikeVM for is-match search at {:?}",
654
0
                input.get_span()
655
            );
656
2.60k
            let e = self.pikevm.get();
657
2.60k
            e.is_match(&mut cache.pikevm, input)
658
        }
659
5.11k
    }
660
661
30.3k
    fn is_capture_search_needed(&self, slots_len: usize) -> bool {
662
30.3k
        slots_len > self.nfa.group_info().implicit_slot_len()
663
30.3k
    }
664
}
665
666
impl Strategy for Core {
667
    #[cfg_attr(feature = "perf-inline", inline(always))]
668
81.5k
    fn group_info(&self) -> &GroupInfo {
669
81.5k
        self.nfa.group_info()
670
81.5k
    }
671
672
    #[cfg_attr(feature = "perf-inline", inline(always))]
673
41.0k
    fn create_cache(&self) -> Cache {
674
41.0k
        Cache {
675
41.0k
            capmatches: Captures::all(self.group_info().clone()),
676
41.0k
            pikevm: self.pikevm.create_cache(),
677
41.0k
            backtrack: self.backtrack.create_cache(),
678
41.0k
            onepass: self.onepass.create_cache(),
679
41.0k
            hybrid: self.hybrid.create_cache(),
680
41.0k
            revhybrid: wrappers::ReverseHybridCache::none(),
681
41.0k
        }
682
41.0k
    }
683
684
    #[cfg_attr(feature = "perf-inline", inline(always))]
685
0
    fn reset_cache(&self, cache: &mut Cache) {
686
0
        cache.pikevm.reset(&self.pikevm);
687
0
        cache.backtrack.reset(&self.backtrack);
688
0
        cache.onepass.reset(&self.onepass);
689
0
        cache.hybrid.reset(&self.hybrid);
690
0
    }
691
692
0
    fn is_accelerated(&self) -> bool {
693
0
        self.pre.as_ref().map_or(false, |pre| pre.is_fast())
694
0
    }
695
696
0
    fn memory_usage(&self) -> usize {
697
0
        self.info.memory_usage()
698
0
            + self.pre.as_ref().map_or(0, |pre| pre.memory_usage())
699
0
            + self.nfa.memory_usage()
700
0
            + self.nfarev.as_ref().map_or(0, |nfa| nfa.memory_usage())
701
0
            + self.onepass.memory_usage()
702
0
            + self.dfa.memory_usage()
703
0
    }
704
705
    #[cfg_attr(feature = "perf-inline", inline(always))]
706
47.6k
    fn search(&self, cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
707
        // We manually inline try_search_mayfail here because letting the
708
        // compiler do it seems to produce pretty crappy codegen.
709
47.6k
        return if let Some(e) = self.dfa.get(input) {
710
30.6k
            trace!("using full DFA for full search at {:?}", input.get_span());
711
30.6k
            match e.try_search(input) {
712
18.3k
                Ok(x) => x,
713
12.2k
                Err(_err) => {
714
12.2k
                    trace!("full DFA search failed: {}", _err);
715
12.2k
                    self.search_nofail(cache, input)
716
                }
717
            }
718
17.0k
        } else if let Some(e) = self.hybrid.get(input) {
719
17.0k
            trace!("using lazy DFA for full search at {:?}", input.get_span());
720
17.0k
            match e.try_search(&mut cache.hybrid, input) {
721
12.6k
                Ok(x) => x,
722
4.37k
                Err(_err) => {
723
4.37k
                    trace!("lazy DFA search failed: {}", _err);
724
4.37k
                    self.search_nofail(cache, input)
725
                }
726
            }
727
        } else {
728
0
            self.search_nofail(cache, input)
729
        };
730
47.6k
    }
731
732
    #[cfg_attr(feature = "perf-inline", inline(always))]
733
24.3k
    fn search_half(
734
24.3k
        &self,
735
24.3k
        cache: &mut Cache,
736
24.3k
        input: &Input<'_>,
737
24.3k
    ) -> Option<HalfMatch> {
738
        // The main difference with 'search' is that if we're using a DFA, we
739
        // can use a single forward scan without needing to run the reverse
740
        // DFA.
741
24.3k
        if let Some(e) = self.dfa.get(input) {
742
15.0k
            trace!("using full DFA for half search at {:?}", input.get_span());
743
15.0k
            match e.try_search_half_fwd(input) {
744
9.34k
                Ok(x) => x,
745
5.74k
                Err(_err) => {
746
5.74k
                    trace!("full DFA half search failed: {}", _err);
747
5.74k
                    self.search_half_nofail(cache, input)
748
                }
749
            }
750
9.25k
        } else if let Some(e) = self.hybrid.get(input) {
751
9.25k
            trace!("using lazy DFA for half search at {:?}", input.get_span());
752
9.25k
            match e.try_search_half_fwd(&mut cache.hybrid, input) {
753
6.08k
                Ok(x) => x,
754
3.16k
                Err(_err) => {
755
3.16k
                    trace!("lazy DFA half search failed: {}", _err);
756
3.16k
                    self.search_half_nofail(cache, input)
757
                }
758
            }
759
        } else {
760
0
            self.search_half_nofail(cache, input)
761
        }
762
24.3k
    }
763
764
    #[cfg_attr(feature = "perf-inline", inline(always))]
765
12.7k
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
766
12.7k
        if let Some(e) = self.dfa.get(input) {
767
7.58k
            trace!(
768
0
                "using full DFA for is-match search at {:?}",
769
0
                input.get_span()
770
            );
771
7.58k
            match e.try_search_half_fwd(input) {
772
4.87k
                Ok(x) => x.is_some(),
773
2.71k
                Err(_err) => {
774
2.71k
                    trace!("full DFA half search failed: {}", _err);
775
2.71k
                    self.is_match_nofail(cache, input)
776
                }
777
            }
778
5.18k
        } else if let Some(e) = self.hybrid.get(input) {
779
5.18k
            trace!(
780
0
                "using lazy DFA for is-match search at {:?}",
781
0
                input.get_span()
782
            );
783
5.18k
            match e.try_search_half_fwd(&mut cache.hybrid, input) {
784
3.97k
                Ok(x) => x.is_some(),
785
1.21k
                Err(_err) => {
786
1.21k
                    trace!("lazy DFA half search failed: {}", _err);
787
1.21k
                    self.is_match_nofail(cache, input)
788
                }
789
            }
790
        } else {
791
0
            self.is_match_nofail(cache, input)
792
        }
793
12.7k
    }
794
795
    #[cfg_attr(feature = "perf-inline", inline(always))]
796
27.2k
    fn search_slots(
797
27.2k
        &self,
798
27.2k
        cache: &mut Cache,
799
27.2k
        input: &Input<'_>,
800
27.2k
        slots: &mut [Option<NonMaxUsize>],
801
27.2k
    ) -> Option<PatternID> {
802
27.2k
        // Even if the regex has explicit capture groups, if the caller didn't
803
27.2k
        // provide any explicit slots, then it doesn't make sense to try and do
804
27.2k
        // extra work to get offsets for those slots. Ideally the caller should
805
27.2k
        // realize this and not call this routine in the first place, but alas,
806
27.2k
        // we try to save the caller from themselves if they do.
807
27.2k
        if !self.is_capture_search_needed(slots.len()) {
808
18.9k
            trace!("asked for slots unnecessarily, trying fast path");
809
18.9k
            let m = self.search(cache, input)?;
810
6.52k
            copy_match_to_slots(m, slots);
811
6.52k
            return Some(m.pattern());
812
8.32k
        }
813
8.32k
        // If the onepass DFA is available for this search (which only happens
814
8.32k
        // when it's anchored), then skip running a fallible DFA. The onepass
815
8.32k
        // DFA isn't as fast as a full or lazy DFA, but it is typically quite
816
8.32k
        // a bit faster than the backtracker or the PikeVM. So it isn't as
817
8.32k
        // advantageous to try and do a full/lazy DFA scan first.
818
8.32k
        //
819
8.32k
        // We still theorize that it's better to do a full/lazy DFA scan, even
820
8.32k
        // when it's anchored, because it's usually much faster and permits us
821
8.32k
        // to say "no match" much more quickly. This does hurt the case of,
822
8.32k
        // say, parsing each line in a log file into capture groups, because
823
8.32k
        // in that case, the line always matches. So the lazy DFA scan is
824
8.32k
        // usually just wasted work. But, the lazy DFA is usually quite fast
825
8.32k
        // and doesn't cost too much here.
826
8.32k
        if self.onepass.get(&input).is_some() {
827
227
            return self.search_slots_nofail(cache, &input, slots);
828
8.10k
        }
829
8.10k
        let m = match self.try_search_mayfail(cache, input) {
830
3.38k
            Some(Ok(Some(m))) => m,
831
1.87k
            Some(Ok(None)) => return None,
832
2.83k
            Some(Err(_err)) => {
833
2.83k
                trace!("fast capture search failed: {}", _err);
834
2.83k
                return self.search_slots_nofail(cache, input, slots);
835
            }
836
            None => {
837
0
                return self.search_slots_nofail(cache, input, slots);
838
            }
839
        };
840
        // At this point, now that we've found the bounds of the
841
        // match, we need to re-run something that can resolve
842
        // capturing groups. But we only need to run on it on the
843
        // match bounds and not the entire haystack.
844
3.38k
        trace!(
845
0
            "match found at {}..{} in capture search, \
846
0
         using another engine to find captures",
847
0
            m.start(),
848
0
            m.end(),
849
        );
850
3.38k
        let input = input
851
3.38k
            .clone()
852
3.38k
            .span(m.start()..m.end())
853
3.38k
            .anchored(Anchored::Pattern(m.pattern()));
854
3.38k
        Some(
855
3.38k
            self.search_slots_nofail(cache, &input, slots)
856
3.38k
                .expect("should find a match"),
857
3.38k
        )
858
27.2k
    }
859
860
    #[cfg_attr(feature = "perf-inline", inline(always))]
861
0
    fn which_overlapping_matches(
862
0
        &self,
863
0
        cache: &mut Cache,
864
0
        input: &Input<'_>,
865
0
        patset: &mut PatternSet,
866
0
    ) {
867
0
        if let Some(e) = self.dfa.get(input) {
868
0
            trace!(
869
0
                "using full DFA for overlapping search at {:?}",
870
0
                input.get_span()
871
            );
872
0
            let _err = match e.try_which_overlapping_matches(input, patset) {
873
0
                Ok(()) => return,
874
0
                Err(err) => err,
875
0
            };
876
0
            trace!("fast overlapping search failed: {}", _err);
877
0
        } else if let Some(e) = self.hybrid.get(input) {
878
0
            trace!(
879
0
                "using lazy DFA for overlapping search at {:?}",
880
0
                input.get_span()
881
            );
882
0
            let _err = match e.try_which_overlapping_matches(
883
0
                &mut cache.hybrid,
884
0
                input,
885
0
                patset,
886
0
            ) {
887
                Ok(()) => {
888
0
                    return;
889
                }
890
0
                Err(err) => err,
891
0
            };
892
0
            trace!("fast overlapping search failed: {}", _err);
893
0
        }
894
0
        trace!(
895
0
            "using PikeVM for overlapping search at {:?}",
896
0
            input.get_span()
897
        );
898
0
        let e = self.pikevm.get();
899
0
        e.which_overlapping_matches(&mut cache.pikevm, input, patset)
900
0
    }
901
}
902
903
#[derive(Debug)]
904
struct ReverseAnchored {
905
    core: Core,
906
}
907
908
impl ReverseAnchored {
909
65.7k
    fn new(core: Core) -> Result<ReverseAnchored, Core> {
910
65.7k
        if !core.info.is_always_anchored_end() {
911
64.6k
            debug!(
912
0
                "skipping reverse anchored optimization because \
913
0
         the regex is not always anchored at the end"
914
            );
915
64.6k
            return Err(core);
916
1.12k
        }
917
1.12k
        // Note that the caller can still request an anchored search even when
918
1.12k
        // the regex isn't anchored at the start. We detect that case in the
919
1.12k
        // search routines below and just fallback to the core engine. This
920
1.12k
        // is fine because both searches are anchored. It's just a matter of
921
1.12k
        // picking one. Falling back to the core engine is a little simpler,
922
1.12k
        // since if we used the reverse anchored approach, we'd have to add an
923
1.12k
        // extra check to ensure the match reported starts at the place where
924
1.12k
        // the caller requested the search to start.
925
1.12k
        if core.info.is_always_anchored_start() {
926
85
            debug!(
927
0
                "skipping reverse anchored optimization because \
928
0
         the regex is also anchored at the start"
929
            );
930
85
            return Err(core);
931
1.04k
        }
932
1.04k
        // Only DFAs can do reverse searches (currently), so we need one of
933
1.04k
        // them in order to do this optimization. It's possible (although
934
1.04k
        // pretty unlikely) that we have neither and need to give up.
935
1.04k
        if !core.hybrid.is_some() && !core.dfa.is_some() {
936
0
            debug!(
937
0
                "skipping reverse anchored optimization because \
938
0
         we don't have a lazy DFA or a full DFA"
939
            );
940
0
            return Err(core);
941
1.04k
        }
942
1.04k
        Ok(ReverseAnchored { core })
943
65.7k
    }
944
945
    #[cfg_attr(feature = "perf-inline", inline(always))]
946
2.30k
    fn try_search_half_anchored_rev(
947
2.30k
        &self,
948
2.30k
        cache: &mut Cache,
949
2.30k
        input: &Input<'_>,
950
2.30k
    ) -> Result<Option<HalfMatch>, RetryFailError> {
951
2.30k
        // We of course always want an anchored search. In theory, the
952
2.30k
        // underlying regex engines should automatically enable anchored
953
2.30k
        // searches since the regex is itself anchored, but this more clearly
954
2.30k
        // expresses intent and is always correct.
955
2.30k
        let input = input.clone().anchored(Anchored::Yes);
956
2.30k
        if let Some(e) = self.core.dfa.get(&input) {
957
1.72k
            trace!(
958
0
                "using full DFA for reverse anchored search at {:?}",
959
0
                input.get_span()
960
            );
961
1.72k
            e.try_search_half_rev(&input)
962
579
        } else if let Some(e) = self.core.hybrid.get(&input) {
963
579
            trace!(
964
0
                "using lazy DFA for reverse anchored search at {:?}",
965
0
                input.get_span()
966
            );
967
579
            e.try_search_half_rev(&mut cache.hybrid, &input)
968
        } else {
969
0
            unreachable!("ReverseAnchored always has a DFA")
970
        }
971
2.30k
    }
972
}
973
974
// Note that in this impl, we don't check that 'input.end() ==
975
// input.haystack().len()'. In particular, when that condition is false, a
976
// match is always impossible because we know that the regex is always anchored
977
// at the end (or else 'ReverseAnchored' won't be built). We don't check that
978
// here because the 'Regex' wrapper actually does that for us in all cases.
979
// Thus, in this impl, we can actually assume that the end position in 'input'
980
// is equivalent to the length of the haystack.
981
impl Strategy for ReverseAnchored {
982
    #[cfg_attr(feature = "perf-inline", inline(always))]
983
748
    fn group_info(&self) -> &GroupInfo {
984
748
        self.core.group_info()
985
748
    }
986
987
    #[cfg_attr(feature = "perf-inline", inline(always))]
988
898
    fn create_cache(&self) -> Cache {
989
898
        self.core.create_cache()
990
898
    }
991
992
    #[cfg_attr(feature = "perf-inline", inline(always))]
993
0
    fn reset_cache(&self, cache: &mut Cache) {
994
0
        self.core.reset_cache(cache);
995
0
    }
996
997
0
    fn is_accelerated(&self) -> bool {
998
0
        // Since this is anchored at the end, a reverse anchored search is
999
0
        // almost certainly guaranteed to result in a much faster search than
1000
0
        // a standard forward search.
1001
0
        true
1002
0
    }
1003
1004
0
    fn memory_usage(&self) -> usize {
1005
0
        self.core.memory_usage()
1006
0
    }
1007
1008
    #[cfg_attr(feature = "perf-inline", inline(always))]
1009
704
    fn search(&self, cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
1010
704
        if input.get_anchored().is_anchored() {
1011
0
            return self.core.search(cache, input);
1012
704
        }
1013
704
        match self.try_search_half_anchored_rev(cache, input) {
1014
189
            Err(_err) => {
1015
189
                trace!("fast reverse anchored search failed: {}", _err);
1016
189
                self.core.search_nofail(cache, input)
1017
            }
1018
304
            Ok(None) => None,
1019
211
            Ok(Some(hm)) => {
1020
211
                Some(Match::new(hm.pattern(), hm.offset()..input.end()))
1021
            }
1022
        }
1023
704
    }
1024
1025
    #[cfg_attr(feature = "perf-inline", inline(always))]
1026
494
    fn search_half(
1027
494
        &self,
1028
494
        cache: &mut Cache,
1029
494
        input: &Input<'_>,
1030
494
    ) -> Option<HalfMatch> {
1031
494
        if input.get_anchored().is_anchored() {
1032
0
            return self.core.search_half(cache, input);
1033
494
        }
1034
494
        match self.try_search_half_anchored_rev(cache, input) {
1035
73
            Err(_err) => {
1036
73
                trace!("fast reverse anchored search failed: {}", _err);
1037
73
                self.core.search_half_nofail(cache, input)
1038
            }
1039
187
            Ok(None) => None,
1040
234
            Ok(Some(hm)) => {
1041
234
                // Careful here! 'try_search_half' is a *forward* search that
1042
234
                // only cares about the *end* position of a match. But
1043
234
                // 'hm.offset()' is actually the start of the match. So we
1044
234
                // actually just throw that away here and, since we know we
1045
234
                // have a match, return the only possible position at which a
1046
234
                // match can occur: input.end().
1047
234
                Some(HalfMatch::new(hm.pattern(), input.end()))
1048
            }
1049
        }
1050
494
    }
1051
1052
    #[cfg_attr(feature = "perf-inline", inline(always))]
1053
404
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
1054
404
        if input.get_anchored().is_anchored() {
1055
0
            return self.core.is_match(cache, input);
1056
404
        }
1057
404
        match self.try_search_half_anchored_rev(cache, input) {
1058
45
            Err(_err) => {
1059
45
                trace!("fast reverse anchored search failed: {}", _err);
1060
45
                self.core.is_match_nofail(cache, input)
1061
            }
1062
221
            Ok(None) => false,
1063
138
            Ok(Some(_)) => true,
1064
        }
1065
404
    }
1066
1067
    #[cfg_attr(feature = "perf-inline", inline(always))]
1068
704
    fn search_slots(
1069
704
        &self,
1070
704
        cache: &mut Cache,
1071
704
        input: &Input<'_>,
1072
704
        slots: &mut [Option<NonMaxUsize>],
1073
704
    ) -> Option<PatternID> {
1074
704
        if input.get_anchored().is_anchored() {
1075
0
            return self.core.search_slots(cache, input, slots);
1076
704
        }
1077
704
        match self.try_search_half_anchored_rev(cache, input) {
1078
199
            Err(_err) => {
1079
199
                trace!("fast reverse anchored search failed: {}", _err);
1080
199
                self.core.search_slots_nofail(cache, input, slots)
1081
            }
1082
294
            Ok(None) => None,
1083
211
            Ok(Some(hm)) => {
1084
211
                if !self.core.is_capture_search_needed(slots.len()) {
1085
152
                    trace!("asked for slots unnecessarily, skipping captures");
1086
152
                    let m = Match::new(hm.pattern(), hm.offset()..input.end());
1087
152
                    copy_match_to_slots(m, slots);
1088
152
                    return Some(m.pattern());
1089
59
                }
1090
59
                let start = hm.offset();
1091
59
                let input = input
1092
59
                    .clone()
1093
59
                    .span(start..input.end())
1094
59
                    .anchored(Anchored::Pattern(hm.pattern()));
1095
59
                self.core.search_slots_nofail(cache, &input, slots)
1096
            }
1097
        }
1098
704
    }
1099
1100
    #[cfg_attr(feature = "perf-inline", inline(always))]
1101
0
    fn which_overlapping_matches(
1102
0
        &self,
1103
0
        cache: &mut Cache,
1104
0
        input: &Input<'_>,
1105
0
        patset: &mut PatternSet,
1106
0
    ) {
1107
0
        // It seems like this could probably benefit from a reverse anchored
1108
0
        // optimization, perhaps by doing an overlapping reverse search (which
1109
0
        // the DFAs do support). I haven't given it much thought though, and
1110
0
        // I'm currently focus more on the single pattern case.
1111
0
        self.core.which_overlapping_matches(cache, input, patset)
1112
0
    }
1113
}
1114
1115
#[derive(Debug)]
1116
struct ReverseSuffix {
1117
    core: Core,
1118
    pre: Prefilter,
1119
}
1120
1121
impl ReverseSuffix {
1122
64.7k
    fn new(core: Core, hirs: &[&Hir]) -> Result<ReverseSuffix, Core> {
1123
64.7k
        if !core.info.config().get_auto_prefilter() {
1124
0
            debug!(
1125
0
                "skipping reverse suffix optimization because \
1126
0
                 automatic prefilters are disabled"
1127
            );
1128
0
            return Err(core);
1129
64.7k
        }
1130
64.7k
        // Like the reverse inner optimization, we don't do this for regexes
1131
64.7k
        // that are always anchored. It could lead to scanning too much, but
1132
64.7k
        // could say "no match" much more quickly than running the regex
1133
64.7k
        // engine if the initial literal scan doesn't match. With that said,
1134
64.7k
        // the reverse suffix optimization has lower overhead, since it only
1135
64.7k
        // requires a reverse scan after a literal match to confirm or reject
1136
64.7k
        // the match. (Although, in the case of confirmation, it then needs to
1137
64.7k
        // do another forward scan to find the end position.)
1138
64.7k
        //
1139
64.7k
        // Note that the caller can still request an anchored search even
1140
64.7k
        // when the regex isn't anchored. We detect that case in the search
1141
64.7k
        // routines below and just fallback to the core engine. Currently this
1142
64.7k
        // optimization assumes all searches are unanchored, so if we do want
1143
64.7k
        // to enable this optimization for anchored searches, it will need a
1144
64.7k
        // little work to support it.
1145
64.7k
        if core.info.is_always_anchored_start() {
1146
1.90k
            debug!(
1147
0
                "skipping reverse suffix optimization because \
1148
0
         the regex is always anchored at the start",
1149
            );
1150
1.90k
            return Err(core);
1151
62.8k
        }
1152
62.8k
        // Only DFAs can do reverse searches (currently), so we need one of
1153
62.8k
        // them in order to do this optimization. It's possible (although
1154
62.8k
        // pretty unlikely) that we have neither and need to give up.
1155
62.8k
        if !core.hybrid.is_some() && !core.dfa.is_some() {
1156
0
            debug!(
1157
0
                "skipping reverse suffix optimization because \
1158
0
         we don't have a lazy DFA or a full DFA"
1159
            );
1160
0
            return Err(core);
1161
62.8k
        }
1162
62.8k
        if core.pre.as_ref().map_or(false, |p| p.is_fast()) {
1163
14.5k
            debug!(
1164
0
                "skipping reverse suffix optimization because \
1165
0
         we already have a prefilter that we think is fast"
1166
            );
1167
14.5k
            return Err(core);
1168
48.2k
        }
1169
48.2k
        let kind = core.info.config().get_match_kind();
1170
48.2k
        let suffixes = crate::util::prefilter::suffixes(kind, hirs);
1171
48.2k
        let lcs = match suffixes.longest_common_suffix() {
1172
            None => {
1173
35.0k
                debug!(
1174
0
                    "skipping reverse suffix optimization because \
1175
0
                     a longest common suffix could not be found",
1176
                );
1177
35.0k
                return Err(core);
1178
            }
1179
13.2k
            Some(lcs) if lcs.is_empty() => {
1180
9.52k
                debug!(
1181
0
                    "skipping reverse suffix optimization because \
1182
0
                     the longest common suffix is the empty string",
1183
                );
1184
9.52k
                return Err(core);
1185
            }
1186
3.70k
            Some(lcs) => lcs,
1187
        };
1188
3.70k
        let pre = match Prefilter::new(kind, &[lcs]) {
1189
3.70k
            Some(pre) => pre,
1190
            None => {
1191
0
                debug!(
1192
0
                    "skipping reverse suffix optimization because \
1193
0
                     a prefilter could not be constructed from the \
1194
0
                     longest common suffix",
1195
                );
1196
0
                return Err(core);
1197
            }
1198
        };
1199
3.70k
        if !pre.is_fast() {
1200
0
            debug!(
1201
0
                "skipping reverse suffix optimization because \
1202
0
         while we have a suffix prefilter, it is not \
1203
0
         believed to be 'fast'"
1204
            );
1205
0
            return Err(core);
1206
3.70k
        }
1207
3.70k
        Ok(ReverseSuffix { core, pre })
1208
64.7k
    }
1209
1210
    #[cfg_attr(feature = "perf-inline", inline(always))]
1211
5.25k
    fn try_search_half_start(
1212
5.25k
        &self,
1213
5.25k
        cache: &mut Cache,
1214
5.25k
        input: &Input<'_>,
1215
5.25k
    ) -> Result<Option<HalfMatch>, RetryError> {
1216
5.25k
        let mut span = input.get_span();
1217
5.25k
        let mut min_start = 0;
1218
        loop {
1219
282k
            let litmatch = match self.pre.find(input.haystack(), span) {
1220
898
                None => return Ok(None),
1221
281k
                Some(span) => span,
1222
281k
            };
1223
281k
            trace!("reverse suffix scan found suffix match at {:?}", litmatch);
1224
281k
            let revinput = input
1225
281k
                .clone()
1226
281k
                .anchored(Anchored::Yes)
1227
281k
                .span(input.start()..litmatch.end);
1228
281k
            match self
1229
281k
                .try_search_half_rev_limited(cache, &revinput, min_start)?
1230
            {
1231
                None => {
1232
277k
                    if span.start >= span.end {
1233
0
                        break;
1234
277k
                    }
1235
277k
                    span.start = litmatch.start.checked_add(1).unwrap();
1236
                }
1237
752
                Some(hm) => return Ok(Some(hm)),
1238
            }
1239
277k
            min_start = litmatch.end;
1240
        }
1241
0
        Ok(None)
1242
5.25k
    }
1243
1244
    #[cfg_attr(feature = "perf-inline", inline(always))]
1245
544
    fn try_search_half_fwd(
1246
544
        &self,
1247
544
        cache: &mut Cache,
1248
544
        input: &Input<'_>,
1249
544
    ) -> Result<Option<HalfMatch>, RetryFailError> {
1250
544
        if let Some(e) = self.core.dfa.get(&input) {
1251
211
            trace!(
1252
0
                "using full DFA for forward reverse suffix search at {:?}",
1253
0
                input.get_span()
1254
            );
1255
211
            e.try_search_half_fwd(&input)
1256
333
        } else if let Some(e) = self.core.hybrid.get(&input) {
1257
333
            trace!(
1258
0
                "using lazy DFA for forward reverse suffix search at {:?}",
1259
0
                input.get_span()
1260
            );
1261
333
            e.try_search_half_fwd(&mut cache.hybrid, &input)
1262
        } else {
1263
0
            unreachable!("ReverseSuffix always has a DFA")
1264
        }
1265
544
    }
1266
1267
    #[cfg_attr(feature = "perf-inline", inline(always))]
1268
281k
    fn try_search_half_rev_limited(
1269
281k
        &self,
1270
281k
        cache: &mut Cache,
1271
281k
        input: &Input<'_>,
1272
281k
        min_start: usize,
1273
281k
    ) -> Result<Option<HalfMatch>, RetryError> {
1274
281k
        if let Some(e) = self.core.dfa.get(&input) {
1275
239k
            trace!(
1276
0
                "using full DFA for reverse suffix search at {:?}, \
1277
0
                 but will be stopped at {} to avoid quadratic behavior",
1278
0
                input.get_span(),
1279
                min_start,
1280
            );
1281
239k
            e.try_search_half_rev_limited(&input, min_start)
1282
41.4k
        } else if let Some(e) = self.core.hybrid.get(&input) {
1283
41.4k
            trace!(
1284
0
                "using lazy DFA for reverse suffix search at {:?}, \
1285
0
                 but will be stopped at {} to avoid quadratic behavior",
1286
0
                input.get_span(),
1287
                min_start,
1288
            );
1289
41.4k
            e.try_search_half_rev_limited(&mut cache.hybrid, &input, min_start)
1290
        } else {
1291
0
            unreachable!("ReverseSuffix always has a DFA")
1292
        }
1293
281k
    }
1294
}
1295
1296
impl Strategy for ReverseSuffix {
1297
    #[cfg_attr(feature = "perf-inline", inline(always))]
1298
2.05k
    fn group_info(&self) -> &GroupInfo {
1299
2.05k
        self.core.group_info()
1300
2.05k
    }
1301
1302
    #[cfg_attr(feature = "perf-inline", inline(always))]
1303
2.28k
    fn create_cache(&self) -> Cache {
1304
2.28k
        self.core.create_cache()
1305
2.28k
    }
1306
1307
    #[cfg_attr(feature = "perf-inline", inline(always))]
1308
0
    fn reset_cache(&self, cache: &mut Cache) {
1309
0
        self.core.reset_cache(cache);
1310
0
    }
1311
1312
0
    fn is_accelerated(&self) -> bool {
1313
0
        self.pre.is_fast()
1314
0
    }
1315
1316
0
    fn memory_usage(&self) -> usize {
1317
0
        self.core.memory_usage() + self.pre.memory_usage()
1318
0
    }
1319
1320
    #[cfg_attr(feature = "perf-inline", inline(always))]
1321
2.24k
    fn search(&self, cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
1322
2.24k
        if input.get_anchored().is_anchored() {
1323
0
            return self.core.search(cache, input);
1324
2.24k
        }
1325
2.24k
        match self.try_search_half_start(cache, input) {
1326
988
            Err(RetryError::Quadratic(_err)) => {
1327
988
                trace!("reverse suffix optimization failed: {}", _err);
1328
988
                self.core.search(cache, input)
1329
            }
1330
579
            Err(RetryError::Fail(_err)) => {
1331
579
                trace!("reverse suffix reverse fast search failed: {}", _err);
1332
579
                self.core.search_nofail(cache, input)
1333
            }
1334
394
            Ok(None) => None,
1335
287
            Ok(Some(hm_start)) => {
1336
287
                let fwdinput = input
1337
287
                    .clone()
1338
287
                    .anchored(Anchored::Pattern(hm_start.pattern()))
1339
287
                    .span(hm_start.offset()..input.end());
1340
287
                match self.try_search_half_fwd(cache, &fwdinput) {
1341
87
                    Err(_err) => {
1342
87
                        trace!(
1343
0
                            "reverse suffix forward fast search failed: {}",
1344
                            _err
1345
                        );
1346
87
                        self.core.search_nofail(cache, input)
1347
                    }
1348
                    Ok(None) => {
1349
0
                        unreachable!(
1350
0
                            "suffix match plus reverse match implies \
1351
0
                 there must be a match",
1352
0
                        )
1353
                    }
1354
200
                    Ok(Some(hm_end)) => Some(Match::new(
1355
200
                        hm_start.pattern(),
1356
200
                        hm_start.offset()..hm_end.offset(),
1357
200
                    )),
1358
                }
1359
            }
1360
        }
1361
2.24k
    }
1362
1363
    #[cfg_attr(feature = "perf-inline", inline(always))]
1364
1.54k
    fn search_half(
1365
1.54k
        &self,
1366
1.54k
        cache: &mut Cache,
1367
1.54k
        input: &Input<'_>,
1368
1.54k
    ) -> Option<HalfMatch> {
1369
1.54k
        if input.get_anchored().is_anchored() {
1370
0
            return self.core.search_half(cache, input);
1371
1.54k
        }
1372
1.54k
        match self.try_search_half_start(cache, input) {
1373
865
            Err(RetryError::Quadratic(_err)) => {
1374
865
                trace!("reverse suffix half optimization failed: {}", _err);
1375
865
                self.core.search_half(cache, input)
1376
            }
1377
129
            Err(RetryError::Fail(_err)) => {
1378
129
                trace!(
1379
0
                    "reverse suffix reverse fast half search failed: {}",
1380
                    _err
1381
                );
1382
129
                self.core.search_half_nofail(cache, input)
1383
            }
1384
289
            Ok(None) => None,
1385
257
            Ok(Some(hm_start)) => {
1386
257
                // This is a bit subtle. It is tempting to just stop searching
1387
257
                // at this point and return a half-match with an offset
1388
257
                // corresponding to where the suffix was found. But the suffix
1389
257
                // match does not necessarily correspond to the end of the
1390
257
                // proper leftmost-first match. Consider /[a-z]+ing/ against
1391
257
                // 'tingling'. The first suffix match is the first 'ing', and
1392
257
                // the /[a-z]+/ matches the 't'. So if we stopped here, then
1393
257
                // we'd report 'ting' as the match. But 'tingling' is the
1394
257
                // correct match because of greediness.
1395
257
                let fwdinput = input
1396
257
                    .clone()
1397
257
                    .anchored(Anchored::Pattern(hm_start.pattern()))
1398
257
                    .span(hm_start.offset()..input.end());
1399
257
                match self.try_search_half_fwd(cache, &fwdinput) {
1400
0
                    Err(_err) => {
1401
0
                        trace!(
1402
0
                            "reverse suffix forward fast search failed: {}",
1403
                            _err
1404
                        );
1405
0
                        self.core.search_half_nofail(cache, input)
1406
                    }
1407
                    Ok(None) => {
1408
0
                        unreachable!(
1409
0
                            "suffix match plus reverse match implies \
1410
0
                 there must be a match",
1411
0
                        )
1412
                    }
1413
257
                    Ok(Some(hm_end)) => Some(hm_end),
1414
                }
1415
            }
1416
        }
1417
1.54k
    }
1418
1419
    #[cfg_attr(feature = "perf-inline", inline(always))]
1420
740
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
1421
740
        if input.get_anchored().is_anchored() {
1422
0
            return self.core.is_match(cache, input);
1423
740
        }
1424
740
        match self.try_search_half_start(cache, input) {
1425
223
            Err(RetryError::Quadratic(_err)) => {
1426
223
                trace!("reverse suffix half optimization failed: {}", _err);
1427
223
                self.core.is_match_nofail(cache, input)
1428
            }
1429
354
            Err(RetryError::Fail(_err)) => {
1430
354
                trace!(
1431
0
                    "reverse suffix reverse fast half search failed: {}",
1432
                    _err
1433
                );
1434
354
                self.core.is_match_nofail(cache, input)
1435
            }
1436
101
            Ok(None) => false,
1437
62
            Ok(Some(_)) => true,
1438
        }
1439
740
    }
1440
1441
    #[cfg_attr(feature = "perf-inline", inline(always))]
1442
1.48k
    fn search_slots(
1443
1.48k
        &self,
1444
1.48k
        cache: &mut Cache,
1445
1.48k
        input: &Input<'_>,
1446
1.48k
        slots: &mut [Option<NonMaxUsize>],
1447
1.48k
    ) -> Option<PatternID> {
1448
1.48k
        if input.get_anchored().is_anchored() {
1449
0
            return self.core.search_slots(cache, input, slots);
1450
1.48k
        }
1451
1.48k
        if !self.core.is_capture_search_needed(slots.len()) {
1452
762
            trace!("asked for slots unnecessarily, trying fast path");
1453
762
            let m = self.search(cache, input)?;
1454
159
            copy_match_to_slots(m, slots);
1455
159
            return Some(m.pattern());
1456
724
        }
1457
724
        let hm_start = match self.try_search_half_start(cache, input) {
1458
195
            Err(RetryError::Quadratic(_err)) => {
1459
195
                trace!(
1460
0
                    "reverse suffix captures optimization failed: {}",
1461
                    _err
1462
                );
1463
195
                return self.core.search_slots(cache, input, slots);
1464
            }
1465
269
            Err(RetryError::Fail(_err)) => {
1466
269
                trace!(
1467
0
                    "reverse suffix reverse fast captures search failed: {}",
1468
                    _err
1469
                );
1470
269
                return self.core.search_slots_nofail(cache, input, slots);
1471
            }
1472
114
            Ok(None) => return None,
1473
146
            Ok(Some(hm_start)) => hm_start,
1474
146
        };
1475
146
        trace!(
1476
0
            "match found at {}..{} in capture search, \
1477
0
         using another engine to find captures",
1478
0
            hm_start.offset(),
1479
0
            input.end(),
1480
        );
1481
146
        let start = hm_start.offset();
1482
146
        let input = input
1483
146
            .clone()
1484
146
            .span(start..input.end())
1485
146
            .anchored(Anchored::Pattern(hm_start.pattern()));
1486
146
        self.core.search_slots_nofail(cache, &input, slots)
1487
1.48k
    }
1488
1489
    #[cfg_attr(feature = "perf-inline", inline(always))]
1490
0
    fn which_overlapping_matches(
1491
0
        &self,
1492
0
        cache: &mut Cache,
1493
0
        input: &Input<'_>,
1494
0
        patset: &mut PatternSet,
1495
0
    ) {
1496
0
        self.core.which_overlapping_matches(cache, input, patset)
1497
0
    }
1498
}
1499
1500
#[derive(Debug)]
1501
struct ReverseInner {
1502
    core: Core,
1503
    preinner: Prefilter,
1504
    nfarev: NFA,
1505
    hybrid: wrappers::ReverseHybrid,
1506
    dfa: wrappers::ReverseDFA,
1507
}
1508
1509
impl ReverseInner {
1510
61.0k
    fn new(core: Core, hirs: &[&Hir]) -> Result<ReverseInner, Core> {
1511
61.0k
        if !core.info.config().get_auto_prefilter() {
1512
0
            debug!(
1513
0
                "skipping reverse inner optimization because \
1514
0
                 automatic prefilters are disabled"
1515
            );
1516
0
            return Err(core);
1517
61.0k
        }
1518
61.0k
        // Currently we hard-code the assumption of leftmost-first match
1519
61.0k
        // semantics. This isn't a huge deal because 'all' semantics tend to
1520
61.0k
        // only be used for forward overlapping searches with multiple regexes,
1521
61.0k
        // and this optimization only supports a single pattern at the moment.
1522
61.0k
        if core.info.config().get_match_kind() != MatchKind::LeftmostFirst {
1523
0
            debug!(
1524
0
                "skipping reverse inner optimization because \
1525
0
         match kind is {:?} but this only supports leftmost-first",
1526
0
                core.info.config().get_match_kind(),
1527
            );
1528
0
            return Err(core);
1529
61.0k
        }
1530
61.0k
        // It's likely that a reverse inner scan has too much overhead for it
1531
61.0k
        // to be worth it when the regex is anchored at the start. It is
1532
61.0k
        // possible for it to be quite a bit faster if the initial literal
1533
61.0k
        // scan fails to detect a match, in which case, we can say "no match"
1534
61.0k
        // very quickly. But this could be undesirable, e.g., scanning too far
1535
61.0k
        // or when the literal scan matches. If it matches, then confirming the
1536
61.0k
        // match requires a reverse scan followed by a forward scan to confirm
1537
61.0k
        // or reject, which is a fair bit of work.
1538
61.0k
        //
1539
61.0k
        // Note that the caller can still request an anchored search even
1540
61.0k
        // when the regex isn't anchored. We detect that case in the search
1541
61.0k
        // routines below and just fallback to the core engine. Currently this
1542
61.0k
        // optimization assumes all searches are unanchored, so if we do want
1543
61.0k
        // to enable this optimization for anchored searches, it will need a
1544
61.0k
        // little work to support it.
1545
61.0k
        if core.info.is_always_anchored_start() {
1546
1.90k
            debug!(
1547
0
                "skipping reverse inner optimization because \
1548
0
         the regex is always anchored at the start",
1549
            );
1550
1.90k
            return Err(core);
1551
59.1k
        }
1552
59.1k
        // Only DFAs can do reverse searches (currently), so we need one of
1553
59.1k
        // them in order to do this optimization. It's possible (although
1554
59.1k
        // pretty unlikely) that we have neither and need to give up.
1555
59.1k
        if !core.hybrid.is_some() && !core.dfa.is_some() {
1556
0
            debug!(
1557
0
                "skipping reverse inner optimization because \
1558
0
         we don't have a lazy DFA or a full DFA"
1559
            );
1560
0
            return Err(core);
1561
59.1k
        }
1562
59.1k
        if core.pre.as_ref().map_or(false, |p| p.is_fast()) {
1563
14.5k
            debug!(
1564
0
                "skipping reverse inner optimization because \
1565
0
         we already have a prefilter that we think is fast"
1566
            );
1567
14.5k
            return Err(core);
1568
44.5k
        } else if core.pre.is_some() {
1569
8.81k
            debug!(
1570
0
                "core engine has a prefix prefilter, but it is \
1571
0
                 probably not fast, so continuing with attempt to \
1572
0
                 use reverse inner prefilter"
1573
            );
1574
35.7k
        }
1575
44.5k
        let (concat_prefix, preinner) = match reverse_inner::extract(hirs) {
1576
4.75k
            Some(x) => x,
1577
            // N.B. the 'extract' function emits debug messages explaining
1578
            // why we bailed out here.
1579
39.7k
            None => return Err(core),
1580
        };
1581
4.75k
        debug!("building reverse NFA for prefix before inner literal");
1582
4.75k
        let mut lookm = LookMatcher::new();
1583
4.75k
        lookm.set_line_terminator(core.info.config().get_line_terminator());
1584
4.75k
        let thompson_config = thompson::Config::new()
1585
4.75k
            .reverse(true)
1586
4.75k
            .utf8(core.info.config().get_utf8_empty())
1587
4.75k
            .nfa_size_limit(core.info.config().get_nfa_size_limit())
1588
4.75k
            .shrink(false)
1589
4.75k
            .which_captures(WhichCaptures::None)
1590
4.75k
            .look_matcher(lookm);
1591
4.75k
        let result = thompson::Compiler::new()
1592
4.75k
            .configure(thompson_config)
1593
4.75k
            .build_from_hir(&concat_prefix);
1594
4.75k
        let nfarev = match result {
1595
4.75k
            Ok(nfarev) => nfarev,
1596
0
            Err(_err) => {
1597
0
                debug!(
1598
0
                    "skipping reverse inner optimization because the \
1599
0
           reverse NFA failed to build: {}",
1600
                    _err,
1601
                );
1602
0
                return Err(core);
1603
            }
1604
        };
1605
4.75k
        debug!("building reverse DFA for prefix before inner literal");
1606
4.75k
        let dfa = if !core.info.config().get_dfa() {
1607
0
            wrappers::ReverseDFA::none()
1608
        } else {
1609
4.75k
            wrappers::ReverseDFA::new(&core.info, &nfarev)
1610
        };
1611
4.75k
        let hybrid = if !core.info.config().get_hybrid() {
1612
0
            wrappers::ReverseHybrid::none()
1613
4.75k
        } else if dfa.is_some() {
1614
2.68k
            debug!(
1615
0
                "skipping lazy DFA for reverse inner optimization \
1616
0
         because we have a full DFA"
1617
            );
1618
2.68k
            wrappers::ReverseHybrid::none()
1619
        } else {
1620
2.07k
            wrappers::ReverseHybrid::new(&core.info, &nfarev)
1621
        };
1622
4.75k
        Ok(ReverseInner { core, preinner, nfarev, hybrid, dfa })
1623
61.0k
    }
1624
1625
    #[cfg_attr(feature = "perf-inline", inline(always))]
1626
4.80k
    fn try_search_full(
1627
4.80k
        &self,
1628
4.80k
        cache: &mut Cache,
1629
4.80k
        input: &Input<'_>,
1630
4.80k
    ) -> Result<Option<Match>, RetryError> {
1631
4.80k
        let mut span = input.get_span();
1632
4.80k
        let mut min_match_start = 0;
1633
4.80k
        let mut min_pre_start = 0;
1634
        loop {
1635
344k
            let litmatch = match self.preinner.find(input.haystack(), span) {
1636
1.64k
                None => return Ok(None),
1637
342k
                Some(span) => span,
1638
342k
            };
1639
342k
            if litmatch.start < min_pre_start {
1640
817
                trace!(
1641
0
                    "found inner prefilter match at {:?}, which starts \
1642
0
           before the end of the last forward scan at {}, \
1643
0
           quitting to avoid quadratic behavior",
1644
                    litmatch,
1645
                    min_pre_start,
1646
                );
1647
817
                return Err(RetryError::Quadratic(RetryQuadraticError::new()));
1648
341k
            }
1649
341k
            trace!("reverse inner scan found inner match at {:?}", litmatch);
1650
341k
            let revinput = input
1651
341k
                .clone()
1652
341k
                .anchored(Anchored::Yes)
1653
341k
                .span(input.start()..litmatch.start);
1654
341k
            // Note that in addition to the literal search above scanning past
1655
341k
            // our minimum start point, this routine can also return an error
1656
341k
            // as a result of detecting possible quadratic behavior if the
1657
341k
            // reverse scan goes past the minimum start point. That is, the
1658
341k
            // literal search might not, but the reverse regex search for the
1659
341k
            // prefix might!
1660
341k
            match self.try_search_half_rev_limited(
1661
341k
                cache,
1662
341k
                &revinput,
1663
341k
                min_match_start,
1664
341k
            )? {
1665
                None => {
1666
307k
                    if span.start >= span.end {
1667
0
                        break;
1668
307k
                    }
1669
307k
                    span.start = litmatch.start.checked_add(1).unwrap();
1670
                }
1671
32.4k
                Some(hm_start) => {
1672
32.4k
                    let fwdinput = input
1673
32.4k
                        .clone()
1674
32.4k
                        .anchored(Anchored::Pattern(hm_start.pattern()))
1675
32.4k
                        .span(hm_start.offset()..input.end());
1676
32.4k
                    match self.try_search_half_fwd_stopat(cache, &fwdinput)? {
1677
31.7k
                        Err(stopat) => {
1678
31.7k
                            min_pre_start = stopat;
1679
31.7k
                            span.start =
1680
31.7k
                                litmatch.start.checked_add(1).unwrap();
1681
31.7k
                        }
1682
475
                        Ok(hm_end) => {
1683
475
                            return Ok(Some(Match::new(
1684
475
                                hm_start.pattern(),
1685
475
                                hm_start.offset()..hm_end.offset(),
1686
475
                            )))
1687
                        }
1688
                    }
1689
                }
1690
            }
1691
339k
            min_match_start = litmatch.end;
1692
        }
1693
0
        Ok(None)
1694
4.80k
    }
1695
1696
    #[cfg_attr(feature = "perf-inline", inline(always))]
1697
32.4k
    fn try_search_half_fwd_stopat(
1698
32.4k
        &self,
1699
32.4k
        cache: &mut Cache,
1700
32.4k
        input: &Input<'_>,
1701
32.4k
    ) -> Result<Result<HalfMatch, usize>, RetryFailError> {
1702
32.4k
        if let Some(e) = self.core.dfa.get(&input) {
1703
6.18k
            trace!(
1704
0
                "using full DFA for forward reverse inner search at {:?}",
1705
0
                input.get_span()
1706
            );
1707
6.18k
            e.try_search_half_fwd_stopat(&input)
1708
26.2k
        } else if let Some(e) = self.core.hybrid.get(&input) {
1709
26.2k
            trace!(
1710
0
                "using lazy DFA for forward reverse inner search at {:?}",
1711
0
                input.get_span()
1712
            );
1713
26.2k
            e.try_search_half_fwd_stopat(&mut cache.hybrid, &input)
1714
        } else {
1715
0
            unreachable!("ReverseInner always has a DFA")
1716
        }
1717
32.4k
    }
1718
1719
    #[cfg_attr(feature = "perf-inline", inline(always))]
1720
341k
    fn try_search_half_rev_limited(
1721
341k
        &self,
1722
341k
        cache: &mut Cache,
1723
341k
        input: &Input<'_>,
1724
341k
        min_start: usize,
1725
341k
    ) -> Result<Option<HalfMatch>, RetryError> {
1726
341k
        if let Some(e) = self.dfa.get(&input) {
1727
54.5k
            trace!(
1728
0
                "using full DFA for reverse inner search at {:?}, \
1729
0
                 but will be stopped at {} to avoid quadratic behavior",
1730
0
                input.get_span(),
1731
                min_start,
1732
            );
1733
54.5k
            e.try_search_half_rev_limited(&input, min_start)
1734
287k
        } else if let Some(e) = self.hybrid.get(&input) {
1735
287k
            trace!(
1736
0
                "using lazy DFA for reverse inner search at {:?}, \
1737
0
                 but will be stopped at {} to avoid quadratic behavior",
1738
0
                input.get_span(),
1739
                min_start,
1740
            );
1741
287k
            e.try_search_half_rev_limited(
1742
287k
                &mut cache.revhybrid,
1743
287k
                &input,
1744
287k
                min_start,
1745
287k
            )
1746
        } else {
1747
0
            unreachable!("ReverseInner always has a DFA")
1748
        }
1749
341k
    }
1750
}
1751
1752
impl Strategy for ReverseInner {
1753
    #[cfg_attr(feature = "perf-inline", inline(always))]
1754
2.48k
    fn group_info(&self) -> &GroupInfo {
1755
2.48k
        self.core.group_info()
1756
2.48k
    }
1757
1758
    #[cfg_attr(feature = "perf-inline", inline(always))]
1759
2.05k
    fn create_cache(&self) -> Cache {
1760
2.05k
        let mut cache = self.core.create_cache();
1761
2.05k
        cache.revhybrid = self.hybrid.create_cache();
1762
2.05k
        cache
1763
2.05k
    }
1764
1765
    #[cfg_attr(feature = "perf-inline", inline(always))]
1766
0
    fn reset_cache(&self, cache: &mut Cache) {
1767
0
        self.core.reset_cache(cache);
1768
0
        cache.revhybrid.reset(&self.hybrid);
1769
0
    }
1770
1771
0
    fn is_accelerated(&self) -> bool {
1772
0
        self.preinner.is_fast()
1773
0
    }
1774
1775
0
    fn memory_usage(&self) -> usize {
1776
0
        self.core.memory_usage()
1777
0
            + self.preinner.memory_usage()
1778
0
            + self.nfarev.memory_usage()
1779
0
            + self.dfa.memory_usage()
1780
0
    }
1781
1782
    #[cfg_attr(feature = "perf-inline", inline(always))]
1783
2.13k
    fn search(&self, cache: &mut Cache, input: &Input<'_>) -> Option<Match> {
1784
2.13k
        if input.get_anchored().is_anchored() {
1785
0
            return self.core.search(cache, input);
1786
2.13k
        }
1787
2.13k
        match self.try_search_full(cache, input) {
1788
1.06k
            Err(RetryError::Quadratic(_err)) => {
1789
1.06k
                trace!("reverse inner optimization failed: {}", _err);
1790
1.06k
                self.core.search(cache, input)
1791
            }
1792
101
            Err(RetryError::Fail(_err)) => {
1793
101
                trace!("reverse inner fast search failed: {}", _err);
1794
101
                self.core.search_nofail(cache, input)
1795
            }
1796
973
            Ok(matornot) => matornot,
1797
        }
1798
2.13k
    }
1799
1800
    #[cfg_attr(feature = "perf-inline", inline(always))]
1801
1.20k
    fn search_half(
1802
1.20k
        &self,
1803
1.20k
        cache: &mut Cache,
1804
1.20k
        input: &Input<'_>,
1805
1.20k
    ) -> Option<HalfMatch> {
1806
1.20k
        if input.get_anchored().is_anchored() {
1807
0
            return self.core.search_half(cache, input);
1808
1.20k
        }
1809
1.20k
        match self.try_search_full(cache, input) {
1810
414
            Err(RetryError::Quadratic(_err)) => {
1811
414
                trace!("reverse inner half optimization failed: {}", _err);
1812
414
                self.core.search_half(cache, input)
1813
            }
1814
85
            Err(RetryError::Fail(_err)) => {
1815
85
                trace!("reverse inner fast half search failed: {}", _err);
1816
85
                self.core.search_half_nofail(cache, input)
1817
            }
1818
595
            Ok(None) => None,
1819
110
            Ok(Some(m)) => Some(HalfMatch::new(m.pattern(), m.end())),
1820
        }
1821
1.20k
    }
1822
1823
    #[cfg_attr(feature = "perf-inline", inline(always))]
1824
847
    fn is_match(&self, cache: &mut Cache, input: &Input<'_>) -> bool {
1825
847
        if input.get_anchored().is_anchored() {
1826
0
            return self.core.is_match(cache, input);
1827
847
        }
1828
847
        match self.try_search_full(cache, input) {
1829
537
            Err(RetryError::Quadratic(_err)) => {
1830
537
                trace!("reverse inner half optimization failed: {}", _err);
1831
537
                self.core.is_match_nofail(cache, input)
1832
            }
1833
29
            Err(RetryError::Fail(_err)) => {
1834
29
                trace!("reverse inner fast half search failed: {}", _err);
1835
29
                self.core.is_match_nofail(cache, input)
1836
            }
1837
200
            Ok(None) => false,
1838
81
            Ok(Some(_)) => true,
1839
        }
1840
847
    }
1841
1842
    #[cfg_attr(feature = "perf-inline", inline(always))]
1843
1.37k
    fn search_slots(
1844
1.37k
        &self,
1845
1.37k
        cache: &mut Cache,
1846
1.37k
        input: &Input<'_>,
1847
1.37k
        slots: &mut [Option<NonMaxUsize>],
1848
1.37k
    ) -> Option<PatternID> {
1849
1.37k
        if input.get_anchored().is_anchored() {
1850
0
            return self.core.search_slots(cache, input, slots);
1851
1.37k
        }
1852
1.37k
        if !self.core.is_capture_search_needed(slots.len()) {
1853
756
            trace!("asked for slots unnecessarily, trying fast path");
1854
756
            let m = self.search(cache, input)?;
1855
222
            copy_match_to_slots(m, slots);
1856
222
            return Some(m.pattern());
1857
622
        }
1858
622
        let m = match self.try_search_full(cache, input) {
1859
424
            Err(RetryError::Quadratic(_err)) => {
1860
424
                trace!("reverse inner captures optimization failed: {}", _err);
1861
424
                return self.core.search_slots(cache, input, slots);
1862
            }
1863
37
            Err(RetryError::Fail(_err)) => {
1864
37
                trace!("reverse inner fast captures search failed: {}", _err);
1865
37
                return self.core.search_slots_nofail(cache, input, slots);
1866
            }
1867
142
            Ok(None) => return None,
1868
19
            Ok(Some(m)) => m,
1869
19
        };
1870
19
        trace!(
1871
0
            "match found at {}..{} in capture search, \
1872
0
         using another engine to find captures",
1873
0
            m.start(),
1874
0
            m.end(),
1875
        );
1876
19
        let input = input
1877
19
            .clone()
1878
19
            .span(m.start()..m.end())
1879
19
            .anchored(Anchored::Pattern(m.pattern()));
1880
19
        self.core.search_slots_nofail(cache, &input, slots)
1881
1.37k
    }
1882
1883
    #[cfg_attr(feature = "perf-inline", inline(always))]
1884
0
    fn which_overlapping_matches(
1885
0
        &self,
1886
0
        cache: &mut Cache,
1887
0
        input: &Input<'_>,
1888
0
        patset: &mut PatternSet,
1889
0
    ) {
1890
0
        self.core.which_overlapping_matches(cache, input, patset)
1891
0
    }
1892
}
1893
1894
/// Copies the offsets in the given match to the corresponding positions in
1895
/// `slots`.
1896
///
1897
/// In effect, this sets the slots corresponding to the implicit group for the
1898
/// pattern in the given match. If the indices for the corresponding slots do
1899
/// not exist, then no slots are set.
1900
///
1901
/// This is useful when the caller provides slots (or captures), but you use a
1902
/// regex engine that doesn't operate on slots (like a lazy DFA). This function
1903
/// lets you map the match you get back to the slots provided by the caller.
1904
#[cfg_attr(feature = "perf-inline", inline(always))]
1905
7.05k
fn copy_match_to_slots(m: Match, slots: &mut [Option<NonMaxUsize>]) {
1906
7.05k
    let slot_start = m.pattern().as_usize() * 2;
1907
7.05k
    let slot_end = slot_start + 1;
1908
7.05k
    if let Some(slot) = slots.get_mut(slot_start) {
1909
7.05k
        *slot = NonMaxUsize::new(m.start());
1910
7.05k
    }
1911
7.05k
    if let Some(slot) = slots.get_mut(slot_end) {
1912
7.05k
        *slot = NonMaxUsize::new(m.end());
1913
7.05k
    }
1914
7.05k
}