regex_automata/util/prefilter/mod.rs
1/*!
2Defines a prefilter for accelerating regex searches.
3
4A prefilter can be created by building a [`Prefilter`] value.
5
6A prefilter represents one of the most important optimizations available for
7accelerating regex searches. The idea of a prefilter is to very quickly find
8candidate locations in a haystack where a regex _could_ match. Once a candidate
9is found, it is then intended for the regex engine to run at that position to
10determine whether the candidate is a match or a false positive.
11
12In the aforementioned description of the prefilter optimization also lay its
13demise. Namely, if a prefilter has a high false positive rate and it produces
14lots of candidates, then a prefilter can overall make a regex search slower.
15It can run more slowly because more time is spent ping-ponging between the
16prefilter search and the regex engine attempting to confirm each candidate as
17a match. This ping-ponging has overhead that adds up, and is exacerbated by
18a high false positive rate.
19
20Nevertheless, the optimization is still generally worth performing in most
21cases. Particularly given just how much throughput can be improved. (It is not
22uncommon for prefilter optimizations to improve throughput by one or two orders
23of magnitude.)
24
25Typically a prefilter is used to find occurrences of literal prefixes from a
26regex pattern, but this isn't required. A prefilter can be used to look for
27suffixes or even inner literals.
28
29Note that as of now, prefilters throw away information about which pattern
30each literal comes from. In other words, when a prefilter finds a match,
31there's no way to know which pattern (or patterns) it came from. Therefore,
32in order to confirm a match, you'll have to check all of the patterns by
33running the full regex engine.
34*/
35
36mod aho_corasick;
37mod byteset;
38mod memchr;
39mod memmem;
40mod teddy;
41
42use core::{
43 borrow::Borrow,
44 fmt::Debug,
45 panic::{RefUnwindSafe, UnwindSafe},
46};
47
48#[cfg(feature = "alloc")]
49use alloc::sync::Arc;
50
51#[cfg(feature = "syntax")]
52use regex_syntax::hir::{literal, Hir};
53
54use crate::util::search::{MatchKind, Span};
55
56pub(crate) use crate::util::prefilter::{
57 aho_corasick::AhoCorasick,
58 byteset::ByteSet,
59 memchr::{Memchr, Memchr2, Memchr3},
60 memmem::Memmem,
61 teddy::Teddy,
62};
63
64/// A prefilter for accelerating regex searches.
65///
66/// If you already have your literals that you want to search with,
67/// then the vanilla [`Prefilter::new`] constructor is for you. But
68/// if you have an [`Hir`] value from the `regex-syntax` crate, then
69/// [`Prefilter::from_hir_prefix`] might be more convenient. Namely, it uses
70/// the [`regex-syntax::hir::literal`](regex_syntax::hir::literal) module to
71/// extract literal prefixes for you, optimize them and then select and build a
72/// prefilter matcher.
73///
74/// A prefilter must have **zero false negatives**. However, by its very
75/// nature, it may produce false positives. That is, a prefilter will never
76/// skip over a position in the haystack that corresponds to a match of the
77/// original regex pattern, but it *may* produce a match for a position
78/// in the haystack that does *not* correspond to a match of the original
79/// regex pattern. If you use either the [`Prefilter::from_hir_prefix`] or
80/// [`Prefilter::from_hirs_prefix`] constructors, then this guarantee is
81/// upheld for you automatically. This guarantee is not preserved if you use
82/// [`Prefilter::new`] though, since it is up to the caller to provide correct
83/// literal strings with respect to the original regex pattern.
84///
85/// # Cloning
86///
87/// It is an API guarantee that cloning a prefilter is cheap. That is, cloning
88/// it will not duplicate whatever heap memory is used to represent the
89/// underlying matcher.
90///
91/// # Example
92///
93/// This example shows how to attach a `Prefilter` to the
94/// [`PikeVM`](crate::nfa::thompson::pikevm::PikeVM) in order to accelerate
95/// searches.
96///
97/// ```
98/// use regex_automata::{
99/// nfa::thompson::pikevm::PikeVM,
100/// util::prefilter::Prefilter,
101/// Match, MatchKind,
102/// };
103///
104/// let pre = Prefilter::new(MatchKind::LeftmostFirst, &["Bruce "])
105/// .expect("a prefilter");
106/// let re = PikeVM::builder()
107/// .configure(PikeVM::config().prefilter(Some(pre)))
108/// .build(r"Bruce \w+")?;
109/// let mut cache = re.create_cache();
110/// assert_eq!(
111/// Some(Match::must(0, 6..23)),
112/// re.find(&mut cache, "Hello Bruce Springsteen!"),
113/// );
114/// # Ok::<(), Box<dyn std::error::Error>>(())
115/// ```
116///
117/// But note that if you get your prefilter incorrect, it could lead to an
118/// incorrect result!
119///
120/// ```
121/// use regex_automata::{
122/// nfa::thompson::pikevm::PikeVM,
123/// util::prefilter::Prefilter,
124/// Match, MatchKind,
125/// };
126///
127/// // This prefilter is wrong!
128/// let pre = Prefilter::new(MatchKind::LeftmostFirst, &["Patti "])
129/// .expect("a prefilter");
130/// let re = PikeVM::builder()
131/// .configure(PikeVM::config().prefilter(Some(pre)))
132/// .build(r"Bruce \w+")?;
133/// let mut cache = re.create_cache();
134/// // We find no match even though the regex does match.
135/// assert_eq!(
136/// None,
137/// re.find(&mut cache, "Hello Bruce Springsteen!"),
138/// );
139/// # Ok::<(), Box<dyn std::error::Error>>(())
140/// ```
141#[derive(Clone, Debug)]
142pub struct Prefilter {
143 #[cfg(not(feature = "alloc"))]
144 _unused: (),
145 #[cfg(feature = "alloc")]
146 pre: Arc<dyn PrefilterI>,
147 #[cfg(feature = "alloc")]
148 is_fast: bool,
149 #[cfg(feature = "alloc")]
150 max_needle_len: usize,
151}
152
153impl Prefilter {
154 /// Create a new prefilter from a sequence of needles and a corresponding
155 /// match semantics.
156 ///
157 /// This may return `None` for a variety of reasons, for example, if
158 /// a suitable prefilter could not be constructed. That might occur
159 /// if they are unavailable (e.g., the `perf-literal-substring` and
160 /// `perf-literal-multisubstring` features aren't enabled), or it might
161 /// occur because of heuristics or other artifacts of how the prefilter
162 /// works.
163 ///
164 /// Note that if you have an [`Hir`] expression, it may be more convenient
165 /// to use [`Prefilter::from_hir_prefix`]. It will automatically handle the
166 /// task of extracting prefix literals for you.
167 ///
168 /// # Example
169 ///
170 /// This example shows how match semantics can impact the matching
171 /// algorithm used by the prefilter. For this reason, it is important to
172 /// ensure that the match semantics given here are consistent with the
173 /// match semantics intended for the regular expression that the literals
174 /// were extracted from.
175 ///
176 /// ```
177 /// use regex_automata::{
178 /// util::{prefilter::Prefilter, syntax},
179 /// MatchKind, Span,
180 /// };
181 ///
182 /// let hay = "Hello samwise";
183 ///
184 /// // With leftmost-first, we find 'samwise' here because it comes
185 /// // before 'sam' in the sequence we give it..
186 /// let pre = Prefilter::new(MatchKind::LeftmostFirst, &["samwise", "sam"])
187 /// .expect("a prefilter");
188 /// assert_eq!(
189 /// Some(Span::from(6..13)),
190 /// pre.find(hay.as_bytes(), Span::from(0..hay.len())),
191 /// );
192 /// // Still with leftmost-first but with the literals reverse, now 'sam'
193 /// // will match instead!
194 /// let pre = Prefilter::new(MatchKind::LeftmostFirst, &["sam", "samwise"])
195 /// .expect("a prefilter");
196 /// assert_eq!(
197 /// Some(Span::from(6..9)),
198 /// pre.find(hay.as_bytes(), Span::from(0..hay.len())),
199 /// );
200 ///
201 /// # Ok::<(), Box<dyn std::error::Error>>(())
202 /// ```
203 pub fn new<B: AsRef<[u8]>>(
204 kind: MatchKind,
205 needles: &[B],
206 ) -> Option<Prefilter> {
207 Choice::new(kind, needles).and_then(|choice| {
208 let max_needle_len =
209 needles.iter().map(|b| b.as_ref().len()).max().unwrap_or(0);
210 Prefilter::from_choice(choice, max_needle_len)
211 })
212 }
213
214 /// This turns a prefilter selection into a `Prefilter`. That is, in turns
215 /// the enum given into a trait object.
216 fn from_choice(
217 choice: Choice,
218 max_needle_len: usize,
219 ) -> Option<Prefilter> {
220 #[cfg(not(feature = "alloc"))]
221 {
222 None
223 }
224 #[cfg(feature = "alloc")]
225 {
226 let pre: Arc<dyn PrefilterI> = match choice {
227 Choice::Memchr(p) => Arc::new(p),
228 Choice::Memchr2(p) => Arc::new(p),
229 Choice::Memchr3(p) => Arc::new(p),
230 Choice::Memmem(p) => Arc::new(p),
231 Choice::Teddy(p) => Arc::new(p),
232 Choice::ByteSet(p) => Arc::new(p),
233 Choice::AhoCorasick(p) => Arc::new(p),
234 };
235 let is_fast = pre.is_fast();
236 Some(Prefilter { pre, is_fast, max_needle_len })
237 }
238 }
239
240 /// This attempts to extract prefixes from the given `Hir` expression for
241 /// the given match semantics, and if possible, builds a prefilter for
242 /// them.
243 ///
244 /// # Example
245 ///
246 /// This example shows how to build a prefilter directly from an [`Hir`]
247 /// expression, and use to find an occurrence of a prefix from the regex
248 /// pattern.
249 ///
250 /// ```
251 /// use regex_automata::{
252 /// util::{prefilter::Prefilter, syntax},
253 /// MatchKind, Span,
254 /// };
255 ///
256 /// let hir = syntax::parse(r"(Bruce|Patti) \w+")?;
257 /// let pre = Prefilter::from_hir_prefix(MatchKind::LeftmostFirst, &hir)
258 /// .expect("a prefilter");
259 /// let hay = "Hello Patti Scialfa!";
260 /// assert_eq!(
261 /// Some(Span::from(6..12)),
262 /// pre.find(hay.as_bytes(), Span::from(0..hay.len())),
263 /// );
264 ///
265 /// # Ok::<(), Box<dyn std::error::Error>>(())
266 /// ```
267 #[cfg(feature = "syntax")]
268 pub fn from_hir_prefix(kind: MatchKind, hir: &Hir) -> Option<Prefilter> {
269 Prefilter::from_hirs_prefix(kind, &[hir])
270 }
271
272 /// This attempts to extract prefixes from the given `Hir` expressions for
273 /// the given match semantics, and if possible, builds a prefilter for
274 /// them.
275 ///
276 /// Note that as of now, prefilters throw away information about which
277 /// pattern each literal comes from. In other words, when a prefilter finds
278 /// a match, there's no way to know which pattern (or patterns) it came
279 /// from. Therefore, in order to confirm a match, you'll have to check all
280 /// of the patterns by running the full regex engine.
281 ///
282 /// # Example
283 ///
284 /// This example shows how to build a prefilter directly from multiple
285 /// `Hir` expressions expression, and use it to find an occurrence of a
286 /// prefix from the regex patterns.
287 ///
288 /// ```
289 /// use regex_automata::{
290 /// util::{prefilter::Prefilter, syntax},
291 /// MatchKind, Span,
292 /// };
293 ///
294 /// let hirs = syntax::parse_many(&[
295 /// r"(Bruce|Patti) \w+",
296 /// r"Mrs?\. Doubtfire",
297 /// ])?;
298 /// let pre = Prefilter::from_hirs_prefix(MatchKind::LeftmostFirst, &hirs)
299 /// .expect("a prefilter");
300 /// let hay = "Hello Mrs. Doubtfire";
301 /// assert_eq!(
302 /// Some(Span::from(6..20)),
303 /// pre.find(hay.as_bytes(), Span::from(0..hay.len())),
304 /// );
305 ///
306 /// # Ok::<(), Box<dyn std::error::Error>>(())
307 /// ```
308 #[cfg(feature = "syntax")]
309 pub fn from_hirs_prefix<H: Borrow<Hir>>(
310 kind: MatchKind,
311 hirs: &[H],
312 ) -> Option<Prefilter> {
313 prefixes(kind, hirs)
314 .literals()
315 .and_then(|lits| Prefilter::new(kind, lits))
316 }
317
318 /// Run this prefilter on `haystack[span.start..end]` and return a matching
319 /// span if one exists.
320 ///
321 /// The span returned is guaranteed to have a start position greater than
322 /// or equal to the one given, and an end position less than or equal to
323 /// the one given.
324 ///
325 /// # Example
326 ///
327 /// This example shows how to build a prefilter directly from an [`Hir`]
328 /// expression, and use it to find an occurrence of a prefix from the regex
329 /// pattern.
330 ///
331 /// ```
332 /// use regex_automata::{
333 /// util::{prefilter::Prefilter, syntax},
334 /// MatchKind, Span,
335 /// };
336 ///
337 /// let hir = syntax::parse(r"Bruce \w+")?;
338 /// let pre = Prefilter::from_hir_prefix(MatchKind::LeftmostFirst, &hir)
339 /// .expect("a prefilter");
340 /// let hay = "Hello Bruce Springsteen!";
341 /// assert_eq!(
342 /// Some(Span::from(6..12)),
343 /// pre.find(hay.as_bytes(), Span::from(0..hay.len())),
344 /// );
345 ///
346 /// # Ok::<(), Box<dyn std::error::Error>>(())
347 /// ```
348 #[inline]
349 pub fn find(&self, haystack: &[u8], span: Span) -> Option<Span> {
350 #[cfg(not(feature = "alloc"))]
351 {
352 unreachable!()
353 }
354 #[cfg(feature = "alloc")]
355 {
356 self.pre.find(haystack, span)
357 }
358 }
359
360 /// Returns the span of a prefix of `haystack[span.start..span.end]` if
361 /// the prefilter matches.
362 ///
363 /// The span returned is guaranteed to have a start position equivalent to
364 /// the one given, and an end position less than or equal to the one given.
365 ///
366 /// # Example
367 ///
368 /// This example shows how to build a prefilter directly from an [`Hir`]
369 /// expression, and use it to find an occurrence of a prefix from the regex
370 /// pattern that begins at the start of a haystack only.
371 ///
372 /// ```
373 /// use regex_automata::{
374 /// util::{prefilter::Prefilter, syntax},
375 /// MatchKind, Span,
376 /// };
377 ///
378 /// let hir = syntax::parse(r"Bruce \w+")?;
379 /// let pre = Prefilter::from_hir_prefix(MatchKind::LeftmostFirst, &hir)
380 /// .expect("a prefilter");
381 /// let hay = "Hello Bruce Springsteen!";
382 /// // Nothing is found here because 'Bruce' does
383 /// // not occur at the beginning of our search.
384 /// assert_eq!(
385 /// None,
386 /// pre.prefix(hay.as_bytes(), Span::from(0..hay.len())),
387 /// );
388 /// // But if we change where we start the search
389 /// // to begin where 'Bruce ' begins, then a
390 /// // match will be found.
391 /// assert_eq!(
392 /// Some(Span::from(6..12)),
393 /// pre.prefix(hay.as_bytes(), Span::from(6..hay.len())),
394 /// );
395 ///
396 /// # Ok::<(), Box<dyn std::error::Error>>(())
397 /// ```
398 #[inline]
399 pub fn prefix(&self, haystack: &[u8], span: Span) -> Option<Span> {
400 #[cfg(not(feature = "alloc"))]
401 {
402 unreachable!()
403 }
404 #[cfg(feature = "alloc")]
405 {
406 self.pre.prefix(haystack, span)
407 }
408 }
409
410 /// Returns the heap memory, in bytes, used by the underlying prefilter.
411 #[inline]
412 pub fn memory_usage(&self) -> usize {
413 #[cfg(not(feature = "alloc"))]
414 {
415 unreachable!()
416 }
417 #[cfg(feature = "alloc")]
418 {
419 self.pre.memory_usage()
420 }
421 }
422
423 /// Return the length of the longest needle
424 /// in this Prefilter
425 #[inline]
426 pub fn max_needle_len(&self) -> usize {
427 #[cfg(not(feature = "alloc"))]
428 {
429 unreachable!()
430 }
431 #[cfg(feature = "alloc")]
432 {
433 self.max_needle_len
434 }
435 }
436
437 /// Implementations might return true here if they believe themselves to
438 /// be "fast." The concept of "fast" is deliberately left vague, but in
439 /// practice this usually corresponds to whether it's believed that SIMD
440 /// will be used.
441 ///
442 /// Why do we care about this? Well, some prefilter tricks tend to come
443 /// with their own bits of overhead, and so might only make sense if we
444 /// know that a scan will be *much* faster than the regex engine itself.
445 /// Otherwise, the trick may not be worth doing. Whether something is
446 /// "much" faster than the regex engine generally boils down to whether
447 /// SIMD is used. (But not always. Even a SIMD matcher with a high false
448 /// positive rate can become quite slow.)
449 ///
450 /// Even if this returns true, it is still possible for the prefilter to
451 /// be "slow." Remember, prefilters are just heuristics. We can't really
452 /// *know* a prefilter will be fast without actually trying the prefilter.
453 /// (Which of course we cannot afford to do.)
454 #[inline]
455 pub fn is_fast(&self) -> bool {
456 #[cfg(not(feature = "alloc"))]
457 {
458 unreachable!()
459 }
460 #[cfg(feature = "alloc")]
461 {
462 self.is_fast
463 }
464 }
465}
466
467/// A trait for abstracting over prefilters. Basically, a prefilter is
468/// something that do an unanchored *and* an anchored search in a haystack
469/// within a given span.
470///
471/// This exists pretty much only so that we can use prefilters as a trait
472/// object (which is what `Prefilter` is). If we ever move off of trait objects
473/// and to an enum, then it's likely this trait could be removed.
474pub(crate) trait PrefilterI:
475 Debug + Send + Sync + RefUnwindSafe + UnwindSafe + 'static
476{
477 /// Return the name of this prefilter.
478 fn name(&self) -> &'static str;
479
480 /// Run this prefilter on `haystack[span.start..end]` and return a matching
481 /// span if one exists.
482 ///
483 /// The span returned is guaranteed to have a start position greater than
484 /// or equal to the one given, and an end position less than or equal to
485 /// the one given.
486 fn find(&self, haystack: &[u8], span: Span) -> Option<Span>;
487
488 /// Returns the span of a prefix of `haystack[span.start..span.end]` if
489 /// the prefilter matches.
490 ///
491 /// The span returned is guaranteed to have a start position equivalent to
492 /// the one given, and an end position less than or equal to the one given.
493 fn prefix(&self, haystack: &[u8], span: Span) -> Option<Span>;
494
495 /// Returns the heap memory, in bytes, used by the underlying prefilter.
496 fn memory_usage(&self) -> usize;
497
498 /// Implementations might return true here if they believe themselves to
499 /// be "fast." See [`Prefilter::is_fast`] for more details.
500 fn is_fast(&self) -> bool;
501}
502
503#[cfg(feature = "alloc")]
504impl<P: PrefilterI + ?Sized> PrefilterI for Arc<P> {
505 fn name(&self) -> &'static str {
506 (**self).name()
507 }
508
509 #[cfg_attr(feature = "perf-inline", inline(always))]
510 fn find(&self, haystack: &[u8], span: Span) -> Option<Span> {
511 (**self).find(haystack, span)
512 }
513
514 #[cfg_attr(feature = "perf-inline", inline(always))]
515 fn prefix(&self, haystack: &[u8], span: Span) -> Option<Span> {
516 (**self).prefix(haystack, span)
517 }
518
519 #[cfg_attr(feature = "perf-inline", inline(always))]
520 fn memory_usage(&self) -> usize {
521 (**self).memory_usage()
522 }
523
524 #[cfg_attr(feature = "perf-inline", inline(always))]
525 fn is_fast(&self) -> bool {
526 (&**self).is_fast()
527 }
528}
529
530/// A type that encapsulates the selection of a prefilter algorithm from a
531/// sequence of needles.
532///
533/// The existence of this type is a little tricky, because we don't (currently)
534/// use it for performing a search. Instead, we really only consume it by
535/// converting the underlying prefilter into a trait object, whether that be
536/// `dyn PrefilterI` or `dyn Strategy` (for the meta regex engine). In order
537/// to avoid re-copying the prefilter selection logic, we isolate it here, and
538/// then force anything downstream that wants to convert it to a trait object
539/// to do trivial case analysis on it.
540///
541/// One wonders whether we *should* use an enum instead of a trait object.
542/// At time of writing, I chose trait objects based on instinct because 1) I
543/// knew I wasn't going to inline anything and 2) there would potentially be
544/// many different choices. However, as of time of writing, I haven't actually
545/// compared the trait object approach to the enum approach. That probably
546/// should be litigated, but I ran out of steam.
547///
548/// Note that if the `alloc` feature is disabled, then values of this type
549/// are (and should) never be constructed. Also, in practice, for any of the
550/// prefilters to be selected, you'll need at least one of the `perf-literal-*`
551/// features enabled.
552#[derive(Clone, Debug)]
553pub(crate) enum Choice {
554 Memchr(Memchr),
555 Memchr2(Memchr2),
556 Memchr3(Memchr3),
557 Memmem(Memmem),
558 Teddy(Teddy),
559 ByteSet(ByteSet),
560 AhoCorasick(AhoCorasick),
561}
562
563impl Choice {
564 /// Select what is believed to be the best prefilter algorithm for the
565 /// match semantics and sequence of needles given.
566 ///
567 /// This selection algorithm uses the needles as given without any
568 /// modification. For example, if `[bar]` is given, then this doesn't
569 /// try to select `memchr` for `b`. Instead, it would select `memmem`
570 /// for `bar`. If callers would want `memchr` selected for `[bar]`, then
571 /// callers should massages the literals themselves. That is, callers are
572 /// responsible for heuristics surrounding which sequence of literals is
573 /// best.
574 ///
575 /// What this selection algorithm does is attempt to use the fastest
576 /// prefilter that works for the literals given. So if `[a, b]`, is given,
577 /// then `memchr2` is selected.
578 ///
579 /// Of course, which prefilter is selected is also subject to what
580 /// is available. For example, if `alloc` isn't enabled, then
581 /// that limits which prefilters can be selected. Similarly, if
582 /// `perf-literal-substring` isn't enabled, then nothing from the `memchr`
583 /// crate can be returned.
584 pub(crate) fn new<B: AsRef<[u8]>>(
585 kind: MatchKind,
586 needles: &[B],
587 ) -> Option<Choice> {
588 // An empty set means the regex matches nothing, so no sense in
589 // building a prefilter.
590 if needles.len() == 0 {
591 debug!("prefilter building failed: found empty set of literals");
592 return None;
593 }
594 // If the regex can match the empty string, then the prefilter
595 // will by definition match at every position. This is obviously
596 // completely ineffective.
597 if needles.iter().any(|n| n.as_ref().is_empty()) {
598 debug!("prefilter building failed: literals match empty string");
599 return None;
600 }
601 // BREADCRUMBS: Perhaps the literal optimizer should special case
602 // sequences of length two or three if the leading bytes of each are
603 // "rare"? Or perhaps, if there are two or three total possible leading
604 // bytes, regardless of the number of literals, and all are rare...
605 // Then well, perhaps we should use memchr2 or memchr3 in those cases?
606 if let Some(pre) = Memchr::new(kind, needles) {
607 debug!("prefilter built: memchr");
608 return Some(Choice::Memchr(pre));
609 }
610 if let Some(pre) = Memchr2::new(kind, needles) {
611 debug!("prefilter built: memchr2");
612 return Some(Choice::Memchr2(pre));
613 }
614 if let Some(pre) = Memchr3::new(kind, needles) {
615 debug!("prefilter built: memchr3");
616 return Some(Choice::Memchr3(pre));
617 }
618 if let Some(pre) = Memmem::new(kind, needles) {
619 debug!("prefilter built: memmem");
620 return Some(Choice::Memmem(pre));
621 }
622 if let Some(pre) = Teddy::new(kind, needles) {
623 debug!("prefilter built: teddy");
624 return Some(Choice::Teddy(pre));
625 }
626 if let Some(pre) = ByteSet::new(kind, needles) {
627 debug!("prefilter built: byteset");
628 return Some(Choice::ByteSet(pre));
629 }
630 if let Some(pre) = AhoCorasick::new(kind, needles) {
631 debug!("prefilter built: aho-corasick");
632 return Some(Choice::AhoCorasick(pre));
633 }
634 debug!("prefilter building failed: no strategy could be found");
635 None
636 }
637}
638
639/// Extracts all of the prefix literals from the given HIR expressions into a
640/// single `Seq`. The literals in the sequence are ordered with respect to the
641/// order of the given HIR expressions and consistent with the match semantics
642/// given.
643///
644/// The sequence returned is "optimized." That is, they may be shrunk or even
645/// truncated according to heuristics with the intent of making them more
646/// useful as a prefilter. (Which translates to both using faster algorithms
647/// and minimizing the false positive rate.)
648///
649/// Note that this erases any connection between the literals and which pattern
650/// (or patterns) they came from.
651///
652/// The match kind given must correspond to the match semantics of the regex
653/// that is represented by the HIRs given. The match semantics may change the
654/// literal sequence returned.
655#[cfg(feature = "syntax")]
656pub(crate) fn prefixes<H>(kind: MatchKind, hirs: &[H]) -> literal::Seq
657where
658 H: core::borrow::Borrow<Hir>,
659{
660 let mut extractor = literal::Extractor::new();
661 extractor.kind(literal::ExtractKind::Prefix);
662
663 let mut prefixes = literal::Seq::empty();
664 for hir in hirs {
665 prefixes.union(&mut extractor.extract(hir.borrow()));
666 }
667 debug!(
668 "prefixes (len={:?}, exact={:?}) extracted before optimization: {:?}",
669 prefixes.len(),
670 prefixes.is_exact(),
671 prefixes
672 );
673 match kind {
674 MatchKind::All => {
675 prefixes.sort();
676 prefixes.dedup();
677 }
678 MatchKind::LeftmostFirst => {
679 prefixes.optimize_for_prefix_by_preference();
680 }
681 }
682 debug!(
683 "prefixes (len={:?}, exact={:?}) extracted after optimization: {:?}",
684 prefixes.len(),
685 prefixes.is_exact(),
686 prefixes
687 );
688 prefixes
689}
690
691/// Like `prefixes`, but for all suffixes of all matches for the given HIRs.
692#[cfg(feature = "syntax")]
693pub(crate) fn suffixes<H>(kind: MatchKind, hirs: &[H]) -> literal::Seq
694where
695 H: core::borrow::Borrow<Hir>,
696{
697 let mut extractor = literal::Extractor::new();
698 extractor.kind(literal::ExtractKind::Suffix);
699
700 let mut suffixes = literal::Seq::empty();
701 for hir in hirs {
702 suffixes.union(&mut extractor.extract(hir.borrow()));
703 }
704 debug!(
705 "suffixes (len={:?}, exact={:?}) extracted before optimization: {:?}",
706 suffixes.len(),
707 suffixes.is_exact(),
708 suffixes
709 );
710 match kind {
711 MatchKind::All => {
712 suffixes.sort();
713 suffixes.dedup();
714 }
715 MatchKind::LeftmostFirst => {
716 suffixes.optimize_for_suffix_by_preference();
717 }
718 }
719 debug!(
720 "suffixes (len={:?}, exact={:?}) extracted after optimization: {:?}",
721 suffixes.len(),
722 suffixes.is_exact(),
723 suffixes
724 );
725 suffixes
726}