regex_automata/hybrid/dfa.rs
1/*!
2Types and routines specific to lazy DFAs.
3
4This module is the home of [`hybrid::dfa::DFA`](DFA).
5
6This module also contains a [`hybrid::dfa::Builder`](Builder) and a
7[`hybrid::dfa::Config`](Config) for configuring and building a lazy DFA.
8*/
9
10use core::{iter, mem::size_of};
11
12use alloc::vec::Vec;
13
14use crate::{
15 hybrid::{
16 error::{BuildError, CacheError, StartError},
17 id::{LazyStateID, LazyStateIDError},
18 search,
19 },
20 nfa::thompson,
21 util::{
22 alphabet::{self, ByteClasses, ByteSet},
23 determinize::{self, State, StateBuilderEmpty, StateBuilderNFA},
24 empty,
25 prefilter::Prefilter,
26 primitives::{PatternID, StateID as NFAStateID},
27 search::{
28 Anchored, HalfMatch, Input, MatchError, MatchKind, PatternSet,
29 },
30 sparse_set::SparseSets,
31 start::{self, Start, StartByteMap},
32 },
33};
34
35/// The minimum number of states that a lazy DFA's cache size must support.
36///
37/// This is checked at time of construction to ensure that at least some small
38/// number of states can fit in the given capacity allotment. If we can't fit
39/// at least this number of states, then the thinking is that it's pretty
40/// senseless to use the lazy DFA. More to the point, parts of the code do
41/// assume that the cache can fit at least some small number of states.
42const MIN_STATES: usize = SENTINEL_STATES + 2;
43
44/// The number of "sentinel" states that get added to every lazy DFA.
45///
46/// These are special states indicating status conditions of a search: unknown,
47/// dead and quit. These states in particular also use zero NFA states, so
48/// their memory usage is quite small. This is relevant for computing the
49/// minimum memory needed for a lazy DFA cache.
50const SENTINEL_STATES: usize = 3;
51
52/// A hybrid NFA/DFA (also called a "lazy DFA") for regex searching.
53///
54/// A lazy DFA is a DFA that builds itself at search time. It otherwise has
55/// very similar characteristics as a [`dense::DFA`](crate::dfa::dense::DFA).
56/// Indeed, both support precisely the same regex features with precisely the
57/// same semantics.
58///
59/// Where as a `dense::DFA` must be completely built to handle any input before
60/// it may be used for search, a lazy DFA starts off effectively empty. During
61/// a search, a lazy DFA will build itself depending on whether it has already
62/// computed the next transition or not. If it has, then it looks a lot like
63/// a `dense::DFA` internally: it does a very fast table based access to find
64/// the next transition. Otherwise, if the state hasn't been computed, then it
65/// does determinization _for that specific transition_ to compute the next DFA
66/// state.
67///
68/// The main selling point of a lazy DFA is that, in practice, it has
69/// the performance profile of a `dense::DFA` without the weakness of it
70/// taking worst case exponential time to build. Indeed, for each byte of
71/// input, the lazy DFA will construct as most one new DFA state. Thus, a
72/// lazy DFA achieves worst case `O(mn)` time for regex search (where `m ~
73/// pattern.len()` and `n ~ haystack.len()`).
74///
75/// The main downsides of a lazy DFA are:
76///
77/// 1. It requires mutable "cache" space during search. This is where the
78/// transition table, among other things, is stored.
79/// 2. In pathological cases (e.g., if the cache is too small), it will run
80/// out of room and either require a bigger cache capacity or will repeatedly
81/// clear the cache and thus repeatedly regenerate DFA states. Overall, this
82/// will tend to be slower than a typical NFA simulation.
83///
84/// # Capabilities
85///
86/// Like a `dense::DFA`, a single lazy DFA fundamentally supports the following
87/// operations:
88///
89/// 1. Detection of a match.
90/// 2. Location of the end of a match.
91/// 3. In the case of a lazy DFA with multiple patterns, which pattern matched
92/// is reported as well.
93///
94/// A notable absence from the above list of capabilities is the location of
95/// the *start* of a match. In order to provide both the start and end of
96/// a match, *two* lazy DFAs are required. This functionality is provided by a
97/// [`Regex`](crate::hybrid::regex::Regex).
98///
99/// # Example
100///
101/// This shows how to build a lazy DFA with the default configuration and
102/// execute a search. Notice how, in contrast to a `dense::DFA`, we must create
103/// a cache and pass it to our search routine.
104///
105/// ```
106/// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
107///
108/// let dfa = DFA::new("foo[0-9]+")?;
109/// let mut cache = dfa.create_cache();
110///
111/// let expected = Some(HalfMatch::must(0, 8));
112/// assert_eq!(expected, dfa.try_search_fwd(
113/// &mut cache, &Input::new("foo12345"))?,
114/// );
115/// # Ok::<(), Box<dyn std::error::Error>>(())
116/// ```
117#[derive(Clone, Debug)]
118pub struct DFA {
119 config: Config,
120 nfa: thompson::NFA,
121 stride2: usize,
122 start_map: StartByteMap,
123 classes: ByteClasses,
124 quitset: ByteSet,
125 cache_capacity: usize,
126}
127
128impl DFA {
129 /// Parse the given regular expression using a default configuration and
130 /// return the corresponding lazy DFA.
131 ///
132 /// If you want a non-default configuration, then use the [`Builder`] to
133 /// set your own configuration.
134 ///
135 /// # Example
136 ///
137 /// ```
138 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
139 ///
140 /// let dfa = DFA::new("foo[0-9]+bar")?;
141 /// let mut cache = dfa.create_cache();
142 ///
143 /// let expected = HalfMatch::must(0, 11);
144 /// assert_eq!(
145 /// Some(expected),
146 /// dfa.try_search_fwd(&mut cache, &Input::new("foo12345bar"))?,
147 /// );
148 /// # Ok::<(), Box<dyn std::error::Error>>(())
149 /// ```
150 #[cfg(feature = "syntax")]
151 pub fn new(pattern: &str) -> Result<DFA, BuildError> {
152 DFA::builder().build(pattern)
153 }
154
155 /// Parse the given regular expressions using a default configuration and
156 /// return the corresponding lazy multi-DFA.
157 ///
158 /// If you want a non-default configuration, then use the [`Builder`] to
159 /// set your own configuration.
160 ///
161 /// # Example
162 ///
163 /// ```
164 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
165 ///
166 /// let dfa = DFA::new_many(&["[0-9]+", "[a-z]+"])?;
167 /// let mut cache = dfa.create_cache();
168 ///
169 /// let expected = HalfMatch::must(1, 3);
170 /// assert_eq!(
171 /// Some(expected),
172 /// dfa.try_search_fwd(&mut cache, &Input::new("foo12345bar"))?,
173 /// );
174 /// # Ok::<(), Box<dyn std::error::Error>>(())
175 /// ```
176 #[cfg(feature = "syntax")]
177 pub fn new_many<P: AsRef<str>>(patterns: &[P]) -> Result<DFA, BuildError> {
178 DFA::builder().build_many(patterns)
179 }
180
181 /// Create a new lazy DFA that matches every input.
182 ///
183 /// # Example
184 ///
185 /// ```
186 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
187 ///
188 /// let dfa = DFA::always_match()?;
189 /// let mut cache = dfa.create_cache();
190 ///
191 /// let expected = HalfMatch::must(0, 0);
192 /// assert_eq!(Some(expected), dfa.try_search_fwd(
193 /// &mut cache, &Input::new(""))?,
194 /// );
195 /// assert_eq!(Some(expected), dfa.try_search_fwd(
196 /// &mut cache, &Input::new("foo"))?,
197 /// );
198 /// # Ok::<(), Box<dyn std::error::Error>>(())
199 /// ```
200 pub fn always_match() -> Result<DFA, BuildError> {
201 let nfa = thompson::NFA::always_match();
202 Builder::new().build_from_nfa(nfa)
203 }
204
205 /// Create a new lazy DFA that never matches any input.
206 ///
207 /// # Example
208 ///
209 /// ```
210 /// use regex_automata::{hybrid::dfa::DFA, Input};
211 ///
212 /// let dfa = DFA::never_match()?;
213 /// let mut cache = dfa.create_cache();
214 ///
215 /// assert_eq!(None, dfa.try_search_fwd(&mut cache, &Input::new(""))?);
216 /// assert_eq!(None, dfa.try_search_fwd(&mut cache, &Input::new("foo"))?);
217 /// # Ok::<(), Box<dyn std::error::Error>>(())
218 /// ```
219 pub fn never_match() -> Result<DFA, BuildError> {
220 let nfa = thompson::NFA::never_match();
221 Builder::new().build_from_nfa(nfa)
222 }
223
224 /// Return a default configuration for a `DFA`.
225 ///
226 /// This is a convenience routine to avoid needing to import the [`Config`]
227 /// type when customizing the construction of a lazy DFA.
228 ///
229 /// # Example
230 ///
231 /// This example shows how to build a lazy DFA that heuristically supports
232 /// Unicode word boundaries.
233 ///
234 /// ```
235 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
236 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, MatchError, Input};
237 ///
238 /// let re = DFA::builder()
239 /// .configure(DFA::config().unicode_word_boundary(true))
240 /// .build(r"\b\w+\b")?;
241 /// let mut cache = re.create_cache();
242 ///
243 /// // Since our haystack is all ASCII, the DFA search sees then and knows
244 /// // it is legal to interpret Unicode word boundaries as ASCII word
245 /// // boundaries.
246 /// let input = Input::new("!!foo!!");
247 /// let expected = HalfMatch::must(0, 5);
248 /// assert_eq!(Some(expected), re.try_search_fwd(&mut cache, &input)?);
249 ///
250 /// // But if our haystack contains non-ASCII, then the search will fail
251 /// // with an error.
252 /// let input = Input::new("!!βββ!!");
253 /// let expected = MatchError::quit(b'\xCE', 2);
254 /// assert_eq!(Err(expected), re.try_search_fwd(&mut cache, &input));
255 ///
256 /// # Ok::<(), Box<dyn std::error::Error>>(())
257 /// ```
258 pub fn config() -> Config {
259 Config::new()
260 }
261
262 /// Return a builder for configuring the construction of a `Regex`.
263 ///
264 /// This is a convenience routine to avoid needing to import the
265 /// [`Builder`] type in common cases.
266 ///
267 /// # Example
268 ///
269 /// This example shows how to use the builder to disable UTF-8 mode
270 /// everywhere for lazy DFAs.
271 ///
272 /// ```
273 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
274 /// use regex_automata::{hybrid::dfa::DFA, util::syntax, HalfMatch, Input};
275 ///
276 /// let re = DFA::builder()
277 /// .syntax(syntax::Config::new().utf8(false))
278 /// .build(r"foo(?-u:[^b])ar.*")?;
279 /// let mut cache = re.create_cache();
280 ///
281 /// let input = Input::new(b"\xFEfoo\xFFarzz\xE2\x98\xFF\n");
282 /// let expected = Some(HalfMatch::must(0, 9));
283 /// let got = re.try_search_fwd(&mut cache, &input)?;
284 /// assert_eq!(expected, got);
285 ///
286 /// # Ok::<(), Box<dyn std::error::Error>>(())
287 /// ```
288 pub fn builder() -> Builder {
289 Builder::new()
290 }
291
292 /// Create a new cache for this lazy DFA.
293 ///
294 /// The cache returned should only be used for searches for this
295 /// lazy DFA. If you want to reuse the cache for another DFA, then
296 /// you must call [`Cache::reset`] with that DFA (or, equivalently,
297 /// [`DFA::reset_cache`]).
298 pub fn create_cache(&self) -> Cache {
299 Cache::new(self)
300 }
301
302 /// Reset the given cache such that it can be used for searching with the
303 /// this lazy DFA (and only this DFA).
304 ///
305 /// A cache reset permits reusing memory already allocated in this cache
306 /// with a different lazy DFA.
307 ///
308 /// Resetting a cache sets its "clear count" to 0. This is relevant if the
309 /// lazy DFA has been configured to "give up" after it has cleared the
310 /// cache a certain number of times.
311 ///
312 /// Any lazy state ID generated by the cache prior to resetting it is
313 /// invalid after the reset.
314 ///
315 /// # Example
316 ///
317 /// This shows how to re-purpose a cache for use with a different DFA.
318 ///
319 /// ```
320 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
321 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
322 ///
323 /// let dfa1 = DFA::new(r"\w")?;
324 /// let dfa2 = DFA::new(r"\W")?;
325 ///
326 /// let mut cache = dfa1.create_cache();
327 /// assert_eq!(
328 /// Some(HalfMatch::must(0, 2)),
329 /// dfa1.try_search_fwd(&mut cache, &Input::new("Δ"))?,
330 /// );
331 ///
332 /// // Using 'cache' with dfa2 is not allowed. It may result in panics or
333 /// // incorrect results. In order to re-purpose the cache, we must reset
334 /// // it with the DFA we'd like to use it with.
335 /// //
336 /// // Similarly, after this reset, using the cache with 'dfa1' is also not
337 /// // allowed.
338 /// dfa2.reset_cache(&mut cache);
339 /// assert_eq!(
340 /// Some(HalfMatch::must(0, 3)),
341 /// dfa2.try_search_fwd(&mut cache, &Input::new("☃"))?,
342 /// );
343 ///
344 /// # Ok::<(), Box<dyn std::error::Error>>(())
345 /// ```
346 pub fn reset_cache(&self, cache: &mut Cache) {
347 Lazy::new(self, cache).reset_cache()
348 }
349
350 /// Returns the total number of patterns compiled into this lazy DFA.
351 ///
352 /// In the case of a DFA that contains no patterns, this returns `0`.
353 ///
354 /// # Example
355 ///
356 /// This example shows the pattern length for a DFA that never matches:
357 ///
358 /// ```
359 /// use regex_automata::hybrid::dfa::DFA;
360 ///
361 /// let dfa = DFA::never_match()?;
362 /// assert_eq!(dfa.pattern_len(), 0);
363 /// # Ok::<(), Box<dyn std::error::Error>>(())
364 /// ```
365 ///
366 /// And another example for a DFA that matches at every position:
367 ///
368 /// ```
369 /// use regex_automata::hybrid::dfa::DFA;
370 ///
371 /// let dfa = DFA::always_match()?;
372 /// assert_eq!(dfa.pattern_len(), 1);
373 /// # Ok::<(), Box<dyn std::error::Error>>(())
374 /// ```
375 ///
376 /// And finally, a DFA that was constructed from multiple patterns:
377 ///
378 /// ```
379 /// use regex_automata::hybrid::dfa::DFA;
380 ///
381 /// let dfa = DFA::new_many(&["[0-9]+", "[a-z]+", "[A-Z]+"])?;
382 /// assert_eq!(dfa.pattern_len(), 3);
383 /// # Ok::<(), Box<dyn std::error::Error>>(())
384 /// ```
385 pub fn pattern_len(&self) -> usize {
386 self.nfa.pattern_len()
387 }
388
389 /// Returns the equivalence classes that make up the alphabet for this DFA.
390 ///
391 /// Unless [`Config::byte_classes`] was disabled, it is possible that
392 /// multiple distinct bytes are grouped into the same equivalence class
393 /// if it is impossible for them to discriminate between a match and a
394 /// non-match. This has the effect of reducing the overall alphabet size
395 /// and in turn potentially substantially reducing the size of the DFA's
396 /// transition table.
397 ///
398 /// The downside of using equivalence classes like this is that every state
399 /// transition will automatically use this map to convert an arbitrary
400 /// byte to its corresponding equivalence class. In practice this has a
401 /// negligible impact on performance.
402 pub fn byte_classes(&self) -> &ByteClasses {
403 &self.classes
404 }
405
406 /// Returns this lazy DFA's configuration.
407 pub fn get_config(&self) -> &Config {
408 &self.config
409 }
410
411 /// Returns a reference to the underlying NFA.
412 pub fn get_nfa(&self) -> &thompson::NFA {
413 &self.nfa
414 }
415
416 /// Returns the stride, as a base-2 exponent, required for these
417 /// equivalence classes.
418 ///
419 /// The stride is always the smallest power of 2 that is greater than or
420 /// equal to the alphabet length. This is done so that converting between
421 /// state IDs and indices can be done with shifts alone, which is much
422 /// faster than integer division.
423 fn stride2(&self) -> usize {
424 self.stride2
425 }
426
427 /// Returns the total stride for every state in this lazy DFA. This
428 /// corresponds to the total number of transitions used by each state in
429 /// this DFA's transition table.
430 fn stride(&self) -> usize {
431 1 << self.stride2()
432 }
433
434 /// Returns the memory usage, in bytes, of this lazy DFA.
435 ///
436 /// This does **not** include the stack size used up by this lazy DFA. To
437 /// compute that, use `std::mem::size_of::<DFA>()`. This also does not
438 /// include the size of the `Cache` used.
439 ///
440 /// This also does not include any heap memory used by the NFA inside of
441 /// this hybrid NFA/DFA. This is because the NFA's ownership is shared, and
442 /// thus not owned by this hybrid NFA/DFA. More practically, several regex
443 /// engines in this crate embed an NFA, and reporting the NFA's memory
444 /// usage in all of them would likely result in reporting higher heap
445 /// memory than is actually used.
446 pub fn memory_usage(&self) -> usize {
447 // The only thing that uses heap memory in a DFA is the NFA. But the
448 // NFA has shared ownership, so reporting its memory as part of the
449 // hybrid DFA is likely to lead to double-counting the NFA memory
450 // somehow. In particular, this DFA does not really own an NFA, so
451 // including it in the DFA's memory usage doesn't seem semantically
452 // correct.
453 0
454 }
455}
456
457impl DFA {
458 /// Executes a forward search and returns the end position of the leftmost
459 /// match that is found. If no match exists, then `None` is returned.
460 ///
461 /// In particular, this method continues searching even after it enters
462 /// a match state. The search only terminates once it has reached the
463 /// end of the input or when it has entered a dead or quit state. Upon
464 /// termination, the position of the last byte seen while still in a match
465 /// state is returned.
466 ///
467 /// # Errors
468 ///
469 /// This routine errors if the search could not complete. This can occur
470 /// in a number of circumstances:
471 ///
472 /// * The configuration of the lazy DFA may permit it to "quit" the search.
473 /// For example, setting quit bytes or enabling heuristic support for
474 /// Unicode word boundaries. The default configuration does not enable any
475 /// option that could result in the lazy DFA quitting.
476 /// * The configuration of the lazy DFA may also permit it to "give up"
477 /// on a search if it makes ineffective use of its transition table
478 /// cache. The default configuration does not enable this by default,
479 /// although it is typically a good idea to.
480 /// * When the provided `Input` configuration is not supported. For
481 /// example, by providing an unsupported anchor mode.
482 ///
483 /// When a search returns an error, callers cannot know whether a match
484 /// exists or not.
485 ///
486 /// # Example
487 ///
488 /// This example shows how to run a basic search.
489 ///
490 /// ```
491 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
492 ///
493 /// let dfa = DFA::new("foo[0-9]+")?;
494 /// let mut cache = dfa.create_cache();
495 /// let expected = HalfMatch::must(0, 8);
496 /// assert_eq!(Some(expected), dfa.try_search_fwd(
497 /// &mut cache, &Input::new("foo12345"))?,
498 /// );
499 ///
500 /// // Even though a match is found after reading the first byte (`a`),
501 /// // the leftmost first match semantics demand that we find the earliest
502 /// // match that prefers earlier parts of the pattern over later parts.
503 /// let dfa = DFA::new("abc|a")?;
504 /// let mut cache = dfa.create_cache();
505 /// let expected = HalfMatch::must(0, 3);
506 /// assert_eq!(Some(expected), dfa.try_search_fwd(
507 /// &mut cache, &Input::new("abc"))?,
508 /// );
509 ///
510 /// # Ok::<(), Box<dyn std::error::Error>>(())
511 /// ```
512 ///
513 /// # Example: specific pattern search
514 ///
515 /// This example shows how to build a lazy multi-DFA that permits searching
516 /// for specific patterns.
517 ///
518 /// ```
519 /// use regex_automata::{
520 /// hybrid::dfa::DFA,
521 /// Anchored, HalfMatch, PatternID, Input,
522 /// };
523 ///
524 /// let dfa = DFA::builder()
525 /// .configure(DFA::config().starts_for_each_pattern(true))
526 /// .build_many(&["[a-z0-9]{6}", "[a-z][a-z0-9]{5}"])?;
527 /// let mut cache = dfa.create_cache();
528 /// let haystack = "foo123";
529 ///
530 /// // Since we are using the default leftmost-first match and both
531 /// // patterns match at the same starting position, only the first pattern
532 /// // will be returned in this case when doing a search for any of the
533 /// // patterns.
534 /// let expected = Some(HalfMatch::must(0, 6));
535 /// let got = dfa.try_search_fwd(&mut cache, &Input::new(haystack))?;
536 /// assert_eq!(expected, got);
537 ///
538 /// // But if we want to check whether some other pattern matches, then we
539 /// // can provide its pattern ID.
540 /// let expected = Some(HalfMatch::must(1, 6));
541 /// let input = Input::new(haystack)
542 /// .anchored(Anchored::Pattern(PatternID::must(1)));
543 /// let got = dfa.try_search_fwd(&mut cache, &input)?;
544 /// assert_eq!(expected, got);
545 ///
546 /// # Ok::<(), Box<dyn std::error::Error>>(())
547 /// ```
548 ///
549 /// # Example: specifying the bounds of a search
550 ///
551 /// This example shows how providing the bounds of a search can produce
552 /// different results than simply sub-slicing the haystack.
553 ///
554 /// ```
555 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
556 ///
557 /// // N.B. We disable Unicode here so that we use a simple ASCII word
558 /// // boundary. Alternatively, we could enable heuristic support for
559 /// // Unicode word boundaries since our haystack is pure ASCII.
560 /// let dfa = DFA::new(r"(?-u)\b[0-9]{3}\b")?;
561 /// let mut cache = dfa.create_cache();
562 /// let haystack = "foo123bar";
563 ///
564 /// // Since we sub-slice the haystack, the search doesn't know about the
565 /// // larger context and assumes that `123` is surrounded by word
566 /// // boundaries. And of course, the match position is reported relative
567 /// // to the sub-slice as well, which means we get `3` instead of `6`.
568 /// let expected = Some(HalfMatch::must(0, 3));
569 /// let got = dfa.try_search_fwd(
570 /// &mut cache,
571 /// &Input::new(&haystack[3..6]),
572 /// )?;
573 /// assert_eq!(expected, got);
574 ///
575 /// // But if we provide the bounds of the search within the context of the
576 /// // entire haystack, then the search can take the surrounding context
577 /// // into account. (And if we did find a match, it would be reported
578 /// // as a valid offset into `haystack` instead of its sub-slice.)
579 /// let expected = None;
580 /// let got = dfa.try_search_fwd(
581 /// &mut cache,
582 /// &Input::new(haystack).range(3..6),
583 /// )?;
584 /// assert_eq!(expected, got);
585 ///
586 /// # Ok::<(), Box<dyn std::error::Error>>(())
587 /// ```
588 #[inline]
589 pub fn try_search_fwd(
590 &self,
591 cache: &mut Cache,
592 input: &Input<'_>,
593 ) -> Result<Option<HalfMatch>, MatchError> {
594 let utf8empty = self.get_nfa().has_empty() && self.get_nfa().is_utf8();
595 let hm = match search::find_fwd(self, cache, input)? {
596 None => return Ok(None),
597 Some(hm) if !utf8empty => return Ok(Some(hm)),
598 Some(hm) => hm,
599 };
600 // We get to this point when we know our DFA can match the empty string
601 // AND when UTF-8 mode is enabled. In this case, we skip any matches
602 // whose offset splits a codepoint. Such a match is necessarily a
603 // zero-width match, because UTF-8 mode requires the underlying NFA
604 // to be built such that all non-empty matches span valid UTF-8.
605 // Therefore, any match that ends in the middle of a codepoint cannot
606 // be part of a span of valid UTF-8 and thus must be an empty match.
607 // In such cases, we skip it, so as not to report matches that split a
608 // codepoint.
609 //
610 // Note that this is not a checked assumption. Callers *can* provide an
611 // NFA with UTF-8 mode enabled but produces non-empty matches that span
612 // invalid UTF-8. But doing so is documented to result in unspecified
613 // behavior.
614 empty::skip_splits_fwd(input, hm, hm.offset(), |input| {
615 let got = search::find_fwd(self, cache, input)?;
616 Ok(got.map(|hm| (hm, hm.offset())))
617 })
618 }
619
620 /// Executes a reverse search and returns the start of the position of the
621 /// leftmost match that is found. If no match exists, then `None` is
622 /// returned.
623 ///
624 /// # Errors
625 ///
626 /// This routine errors if the search could not complete. This can occur
627 /// in a number of circumstances:
628 ///
629 /// * The configuration of the lazy DFA may permit it to "quit" the search.
630 /// For example, setting quit bytes or enabling heuristic support for
631 /// Unicode word boundaries. The default configuration does not enable any
632 /// option that could result in the lazy DFA quitting.
633 /// * The configuration of the lazy DFA may also permit it to "give up"
634 /// on a search if it makes ineffective use of its transition table
635 /// cache. The default configuration does not enable this by default,
636 /// although it is typically a good idea to.
637 /// * When the provided `Input` configuration is not supported. For
638 /// example, by providing an unsupported anchor mode.
639 ///
640 /// When a search returns an error, callers cannot know whether a match
641 /// exists or not.
642 ///
643 /// # Example
644 ///
645 /// This routine is principally useful when used in
646 /// conjunction with the
647 /// [`nfa::thompson::Config::reverse`](crate::nfa::thompson::Config::reverse)
648 /// configuration. In general, it's unlikely to be correct to use both
649 /// `try_search_fwd` and `try_search_rev` with the same DFA since any
650 /// particular DFA will only support searching in one direction with
651 /// respect to the pattern.
652 ///
653 /// ```
654 /// use regex_automata::{
655 /// nfa::thompson,
656 /// hybrid::dfa::DFA,
657 /// HalfMatch, Input,
658 /// };
659 ///
660 /// let dfa = DFA::builder()
661 /// .thompson(thompson::Config::new().reverse(true))
662 /// .build("foo[0-9]+")?;
663 /// let mut cache = dfa.create_cache();
664 /// let expected = HalfMatch::must(0, 0);
665 /// assert_eq!(
666 /// Some(expected),
667 /// dfa.try_search_rev(&mut cache, &Input::new("foo12345"))?,
668 /// );
669 ///
670 /// // Even though a match is found after reading the last byte (`c`),
671 /// // the leftmost first match semantics demand that we find the earliest
672 /// // match that prefers earlier parts of the pattern over latter parts.
673 /// let dfa = DFA::builder()
674 /// .thompson(thompson::Config::new().reverse(true))
675 /// .build("abc|c")?;
676 /// let mut cache = dfa.create_cache();
677 /// let expected = HalfMatch::must(0, 0);
678 /// assert_eq!(Some(expected), dfa.try_search_rev(
679 /// &mut cache, &Input::new("abc"))?,
680 /// );
681 ///
682 /// # Ok::<(), Box<dyn std::error::Error>>(())
683 /// ```
684 ///
685 /// # Example: UTF-8 mode
686 ///
687 /// This examples demonstrates that UTF-8 mode applies to reverse
688 /// DFAs. When UTF-8 mode is enabled in the underlying NFA, then all
689 /// matches reported must correspond to valid UTF-8 spans. This includes
690 /// prohibiting zero-width matches that split a codepoint.
691 ///
692 /// UTF-8 mode is enabled by default. Notice below how the only zero-width
693 /// matches reported are those at UTF-8 boundaries:
694 ///
695 /// ```
696 /// use regex_automata::{
697 /// hybrid::dfa::DFA,
698 /// nfa::thompson,
699 /// HalfMatch, Input, MatchKind,
700 /// };
701 ///
702 /// let dfa = DFA::builder()
703 /// .thompson(thompson::Config::new().reverse(true))
704 /// .build(r"")?;
705 /// let mut cache = dfa.create_cache();
706 ///
707 /// // Run the reverse DFA to collect all matches.
708 /// let mut input = Input::new("☃");
709 /// let mut matches = vec![];
710 /// loop {
711 /// match dfa.try_search_rev(&mut cache, &input)? {
712 /// None => break,
713 /// Some(hm) => {
714 /// matches.push(hm);
715 /// if hm.offset() == 0 || input.end() == 0 {
716 /// break;
717 /// } else if hm.offset() < input.end() {
718 /// input.set_end(hm.offset());
719 /// } else {
720 /// // This is only necessary to handle zero-width
721 /// // matches, which of course occur in this example.
722 /// // Without this, the search would never advance
723 /// // backwards beyond the initial match.
724 /// input.set_end(input.end() - 1);
725 /// }
726 /// }
727 /// }
728 /// }
729 ///
730 /// // No matches split a codepoint.
731 /// let expected = vec![
732 /// HalfMatch::must(0, 3),
733 /// HalfMatch::must(0, 0),
734 /// ];
735 /// assert_eq!(expected, matches);
736 ///
737 /// # Ok::<(), Box<dyn std::error::Error>>(())
738 /// ```
739 ///
740 /// Now let's look at the same example, but with UTF-8 mode on the
741 /// underlying NFA disabled:
742 ///
743 /// ```
744 /// use regex_automata::{
745 /// hybrid::dfa::DFA,
746 /// nfa::thompson,
747 /// HalfMatch, Input, MatchKind,
748 /// };
749 ///
750 /// let dfa = DFA::builder()
751 /// .thompson(thompson::Config::new().reverse(true).utf8(false))
752 /// .build(r"")?;
753 /// let mut cache = dfa.create_cache();
754 ///
755 /// // Run the reverse DFA to collect all matches.
756 /// let mut input = Input::new("☃");
757 /// let mut matches = vec![];
758 /// loop {
759 /// match dfa.try_search_rev(&mut cache, &input)? {
760 /// None => break,
761 /// Some(hm) => {
762 /// matches.push(hm);
763 /// if hm.offset() == 0 || input.end() == 0 {
764 /// break;
765 /// } else if hm.offset() < input.end() {
766 /// input.set_end(hm.offset());
767 /// } else {
768 /// // This is only necessary to handle zero-width
769 /// // matches, which of course occur in this example.
770 /// // Without this, the search would never advance
771 /// // backwards beyond the initial match.
772 /// input.set_end(input.end() - 1);
773 /// }
774 /// }
775 /// }
776 /// }
777 ///
778 /// // No matches split a codepoint.
779 /// let expected = vec![
780 /// HalfMatch::must(0, 3),
781 /// HalfMatch::must(0, 2),
782 /// HalfMatch::must(0, 1),
783 /// HalfMatch::must(0, 0),
784 /// ];
785 /// assert_eq!(expected, matches);
786 ///
787 /// # Ok::<(), Box<dyn std::error::Error>>(())
788 /// ```
789 #[inline]
790 pub fn try_search_rev(
791 &self,
792 cache: &mut Cache,
793 input: &Input<'_>,
794 ) -> Result<Option<HalfMatch>, MatchError> {
795 let utf8empty = self.get_nfa().has_empty() && self.get_nfa().is_utf8();
796 let hm = match search::find_rev(self, cache, input)? {
797 None => return Ok(None),
798 Some(hm) if !utf8empty => return Ok(Some(hm)),
799 Some(hm) => hm,
800 };
801 empty::skip_splits_rev(input, hm, hm.offset(), |input| {
802 let got = search::find_rev(self, cache, input)?;
803 Ok(got.map(|hm| (hm, hm.offset())))
804 })
805 }
806
807 /// Executes an overlapping forward search and returns the end position of
808 /// matches as they are found. If no match exists, then `None` is returned.
809 ///
810 /// This routine is principally only useful when searching for multiple
811 /// patterns on inputs where multiple patterns may match the same regions
812 /// of text. In particular, callers must preserve the automaton's search
813 /// state from prior calls so that the implementation knows where the last
814 /// match occurred.
815 ///
816 /// When using this routine to implement an iterator of overlapping
817 /// matches, the `start` of the search should remain invariant throughout
818 /// iteration. The `OverlappingState` given to the search will keep track
819 /// of the current position of the search. (This is because multiple
820 /// matches may be reported at the same position, so only the search
821 /// implementation itself knows when to advance the position.)
822 ///
823 /// If for some reason you want the search to forget about its previous
824 /// state and restart the search at a particular position, then setting the
825 /// state to [`OverlappingState::start`] will accomplish that.
826 ///
827 /// # Errors
828 ///
829 /// This routine errors if the search could not complete. This can occur
830 /// in a number of circumstances:
831 ///
832 /// * The configuration of the lazy DFA may permit it to "quit" the search.
833 /// For example, setting quit bytes or enabling heuristic support for
834 /// Unicode word boundaries. The default configuration does not enable any
835 /// option that could result in the lazy DFA quitting.
836 /// * The configuration of the lazy DFA may also permit it to "give up"
837 /// on a search if it makes ineffective use of its transition table
838 /// cache. The default configuration does not enable this by default,
839 /// although it is typically a good idea to.
840 /// * When the provided `Input` configuration is not supported. For
841 /// example, by providing an unsupported anchor mode.
842 ///
843 /// When a search returns an error, callers cannot know whether a match
844 /// exists or not.
845 ///
846 /// # Example
847 ///
848 /// This example shows how to run a basic overlapping search. Notice
849 /// that we build the automaton with a `MatchKind::All` configuration.
850 /// Overlapping searches are unlikely to work as one would expect when
851 /// using the default `MatchKind::LeftmostFirst` match semantics, since
852 /// leftmost-first matching is fundamentally incompatible with overlapping
853 /// searches. Namely, overlapping searches need to report matches as they
854 /// are seen, where as leftmost-first searches will continue searching even
855 /// after a match has been observed in order to find the conventional end
856 /// position of the match. More concretely, leftmost-first searches use
857 /// dead states to terminate a search after a specific match can no longer
858 /// be extended. Overlapping searches instead do the opposite by continuing
859 /// the search to find totally new matches (potentially of other patterns).
860 ///
861 /// ```
862 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
863 /// use regex_automata::{
864 /// hybrid::dfa::{DFA, OverlappingState},
865 /// HalfMatch, Input, MatchKind,
866 /// };
867 ///
868 /// let dfa = DFA::builder()
869 /// .configure(DFA::config().match_kind(MatchKind::All))
870 /// .build_many(&[r"\w+$", r"\S+$"])?;
871 /// let mut cache = dfa.create_cache();
872 ///
873 /// let haystack = "@foo";
874 /// let mut state = OverlappingState::start();
875 ///
876 /// let expected = Some(HalfMatch::must(1, 4));
877 /// dfa.try_search_overlapping_fwd(
878 /// &mut cache, &Input::new(haystack), &mut state,
879 /// )?;
880 /// assert_eq!(expected, state.get_match());
881 ///
882 /// // The first pattern also matches at the same position, so re-running
883 /// // the search will yield another match. Notice also that the first
884 /// // pattern is returned after the second. This is because the second
885 /// // pattern begins its match before the first, is therefore an earlier
886 /// // match and is thus reported first.
887 /// let expected = Some(HalfMatch::must(0, 4));
888 /// dfa.try_search_overlapping_fwd(
889 /// &mut cache, &Input::new(haystack), &mut state,
890 /// )?;
891 /// assert_eq!(expected, state.get_match());
892 ///
893 /// # Ok::<(), Box<dyn std::error::Error>>(())
894 /// ```
895 #[inline]
896 pub fn try_search_overlapping_fwd(
897 &self,
898 cache: &mut Cache,
899 input: &Input<'_>,
900 state: &mut OverlappingState,
901 ) -> Result<(), MatchError> {
902 let utf8empty = self.get_nfa().has_empty() && self.get_nfa().is_utf8();
903 search::find_overlapping_fwd(self, cache, input, state)?;
904 match state.get_match() {
905 None => Ok(()),
906 Some(_) if !utf8empty => Ok(()),
907 Some(_) => skip_empty_utf8_splits_overlapping(
908 input,
909 state,
910 |input, state| {
911 search::find_overlapping_fwd(self, cache, input, state)
912 },
913 ),
914 }
915 }
916
917 /// Executes a reverse overlapping search and returns the start of the
918 /// position of the leftmost match that is found. If no match exists, then
919 /// `None` is returned.
920 ///
921 /// When using this routine to implement an iterator of overlapping
922 /// matches, the `start` of the search should remain invariant throughout
923 /// iteration. The `OverlappingState` given to the search will keep track
924 /// of the current position of the search. (This is because multiple
925 /// matches may be reported at the same position, so only the search
926 /// implementation itself knows when to advance the position.)
927 ///
928 /// If for some reason you want the search to forget about its previous
929 /// state and restart the search at a particular position, then setting the
930 /// state to [`OverlappingState::start`] will accomplish that.
931 ///
932 /// # Errors
933 ///
934 /// This routine errors if the search could not complete. This can occur
935 /// in a number of circumstances:
936 ///
937 /// * The configuration of the lazy DFA may permit it to "quit" the search.
938 /// For example, setting quit bytes or enabling heuristic support for
939 /// Unicode word boundaries. The default configuration does not enable any
940 /// option that could result in the lazy DFA quitting.
941 /// * The configuration of the lazy DFA may also permit it to "give up"
942 /// on a search if it makes ineffective use of its transition table
943 /// cache. The default configuration does not enable this by default,
944 /// although it is typically a good idea to.
945 /// * When the provided `Input` configuration is not supported. For
946 /// example, by providing an unsupported anchor mode.
947 ///
948 /// When a search returns an error, callers cannot know whether a match
949 /// exists or not.
950 ///
951 /// # Example: UTF-8 mode
952 ///
953 /// This examples demonstrates that UTF-8 mode applies to reverse
954 /// DFAs. When UTF-8 mode is enabled in the underlying NFA, then all
955 /// matches reported must correspond to valid UTF-8 spans. This includes
956 /// prohibiting zero-width matches that split a codepoint.
957 ///
958 /// UTF-8 mode is enabled by default. Notice below how the only zero-width
959 /// matches reported are those at UTF-8 boundaries:
960 ///
961 /// ```
962 /// use regex_automata::{
963 /// hybrid::dfa::{DFA, OverlappingState},
964 /// nfa::thompson,
965 /// HalfMatch, Input, MatchKind,
966 /// };
967 ///
968 /// let dfa = DFA::builder()
969 /// .configure(DFA::config().match_kind(MatchKind::All))
970 /// .thompson(thompson::Config::new().reverse(true))
971 /// .build_many(&[r"", r"☃"])?;
972 /// let mut cache = dfa.create_cache();
973 ///
974 /// // Run the reverse DFA to collect all matches.
975 /// let input = Input::new("☃");
976 /// let mut state = OverlappingState::start();
977 /// let mut matches = vec![];
978 /// loop {
979 /// dfa.try_search_overlapping_rev(&mut cache, &input, &mut state)?;
980 /// match state.get_match() {
981 /// None => break,
982 /// Some(hm) => matches.push(hm),
983 /// }
984 /// }
985 ///
986 /// // No matches split a codepoint.
987 /// let expected = vec![
988 /// HalfMatch::must(0, 3),
989 /// HalfMatch::must(1, 0),
990 /// HalfMatch::must(0, 0),
991 /// ];
992 /// assert_eq!(expected, matches);
993 ///
994 /// # Ok::<(), Box<dyn std::error::Error>>(())
995 /// ```
996 ///
997 /// Now let's look at the same example, but with UTF-8 mode on the
998 /// underlying NFA disabled:
999 ///
1000 /// ```
1001 /// use regex_automata::{
1002 /// hybrid::dfa::{DFA, OverlappingState},
1003 /// nfa::thompson,
1004 /// HalfMatch, Input, MatchKind,
1005 /// };
1006 ///
1007 /// let dfa = DFA::builder()
1008 /// .configure(DFA::config().match_kind(MatchKind::All))
1009 /// .thompson(thompson::Config::new().reverse(true).utf8(false))
1010 /// .build_many(&[r"", r"☃"])?;
1011 /// let mut cache = dfa.create_cache();
1012 ///
1013 /// // Run the reverse DFA to collect all matches.
1014 /// let input = Input::new("☃");
1015 /// let mut state = OverlappingState::start();
1016 /// let mut matches = vec![];
1017 /// loop {
1018 /// dfa.try_search_overlapping_rev(&mut cache, &input, &mut state)?;
1019 /// match state.get_match() {
1020 /// None => break,
1021 /// Some(hm) => matches.push(hm),
1022 /// }
1023 /// }
1024 ///
1025 /// // Now *all* positions match, even within a codepoint,
1026 /// // because we lifted the requirement that matches
1027 /// // correspond to valid UTF-8 spans.
1028 /// let expected = vec![
1029 /// HalfMatch::must(0, 3),
1030 /// HalfMatch::must(0, 2),
1031 /// HalfMatch::must(0, 1),
1032 /// HalfMatch::must(1, 0),
1033 /// HalfMatch::must(0, 0),
1034 /// ];
1035 /// assert_eq!(expected, matches);
1036 ///
1037 /// # Ok::<(), Box<dyn std::error::Error>>(())
1038 /// ```
1039 #[inline]
1040 pub fn try_search_overlapping_rev(
1041 &self,
1042 cache: &mut Cache,
1043 input: &Input<'_>,
1044 state: &mut OverlappingState,
1045 ) -> Result<(), MatchError> {
1046 let utf8empty = self.get_nfa().has_empty() && self.get_nfa().is_utf8();
1047 search::find_overlapping_rev(self, cache, input, state)?;
1048 match state.get_match() {
1049 None => Ok(()),
1050 Some(_) if !utf8empty => Ok(()),
1051 Some(_) => skip_empty_utf8_splits_overlapping(
1052 input,
1053 state,
1054 |input, state| {
1055 search::find_overlapping_rev(self, cache, input, state)
1056 },
1057 ),
1058 }
1059 }
1060
1061 /// Writes the set of patterns that match anywhere in the given search
1062 /// configuration to `patset`. If multiple patterns match at the same
1063 /// position and the underlying DFA supports overlapping matches, then all
1064 /// matching patterns are written to the given set.
1065 ///
1066 /// Unless all of the patterns in this DFA are anchored, then generally
1067 /// speaking, this will visit every byte in the haystack.
1068 ///
1069 /// This search routine *does not* clear the pattern set. This gives some
1070 /// flexibility to the caller (e.g., running multiple searches with the
1071 /// same pattern set), but does make the API bug-prone if you're reusing
1072 /// the same pattern set for multiple searches but intended them to be
1073 /// independent.
1074 ///
1075 /// If a pattern ID matched but the given `PatternSet` does not have
1076 /// sufficient capacity to store it, then it is not inserted and silently
1077 /// dropped.
1078 ///
1079 /// # Errors
1080 ///
1081 /// This routine errors if the search could not complete. This can occur
1082 /// in a number of circumstances:
1083 ///
1084 /// * The configuration of the lazy DFA may permit it to "quit" the search.
1085 /// For example, setting quit bytes or enabling heuristic support for
1086 /// Unicode word boundaries. The default configuration does not enable any
1087 /// option that could result in the lazy DFA quitting.
1088 /// * The configuration of the lazy DFA may also permit it to "give up"
1089 /// on a search if it makes ineffective use of its transition table
1090 /// cache. The default configuration does not enable this by default,
1091 /// although it is typically a good idea to.
1092 /// * When the provided `Input` configuration is not supported. For
1093 /// example, by providing an unsupported anchor mode.
1094 ///
1095 /// When a search returns an error, callers cannot know whether a match
1096 /// exists or not.
1097 ///
1098 /// # Example
1099 ///
1100 /// This example shows how to find all matching patterns in a haystack,
1101 /// even when some patterns match at the same position as other patterns.
1102 ///
1103 /// ```
1104 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
1105 /// use regex_automata::{
1106 /// hybrid::dfa::DFA,
1107 /// Input, MatchKind, PatternSet,
1108 /// };
1109 ///
1110 /// let patterns = &[
1111 /// r"\w+", r"\d+", r"\pL+", r"foo", r"bar", r"barfoo", r"foobar",
1112 /// ];
1113 /// let dfa = DFA::builder()
1114 /// .configure(DFA::config().match_kind(MatchKind::All))
1115 /// .build_many(patterns)?;
1116 /// let mut cache = dfa.create_cache();
1117 ///
1118 /// let input = Input::new("foobar");
1119 /// let mut patset = PatternSet::new(dfa.pattern_len());
1120 /// dfa.try_which_overlapping_matches(&mut cache, &input, &mut patset)?;
1121 /// let expected = vec![0, 2, 3, 4, 6];
1122 /// let got: Vec<usize> = patset.iter().map(|p| p.as_usize()).collect();
1123 /// assert_eq!(expected, got);
1124 ///
1125 /// # Ok::<(), Box<dyn std::error::Error>>(())
1126 /// ```
1127 #[inline]
1128 pub fn try_which_overlapping_matches(
1129 &self,
1130 cache: &mut Cache,
1131 input: &Input<'_>,
1132 patset: &mut PatternSet,
1133 ) -> Result<(), MatchError> {
1134 let mut state = OverlappingState::start();
1135 while let Some(m) = {
1136 self.try_search_overlapping_fwd(cache, input, &mut state)?;
1137 state.get_match()
1138 } {
1139 let _ = patset.try_insert(m.pattern());
1140 // There's nothing left to find, so we can stop. Or the caller
1141 // asked us to.
1142 if patset.is_full() || input.get_earliest() {
1143 break;
1144 }
1145 }
1146 Ok(())
1147 }
1148}
1149
1150impl DFA {
1151 /// Transitions from the current state to the next state, given the next
1152 /// byte of input.
1153 ///
1154 /// The given cache is used to either reuse pre-computed state
1155 /// transitions, or to store this newly computed transition for future
1156 /// reuse. Thus, this routine guarantees that it will never return a state
1157 /// ID that has an "unknown" tag.
1158 ///
1159 /// # State identifier validity
1160 ///
1161 /// The only valid value for `current` is the lazy state ID returned
1162 /// by the most recent call to `next_state`, `next_state_untagged`,
1163 /// `next_state_untagged_unchecked`, `start_state_forward` or
1164 /// `state_state_reverse` for the given `cache`. Any state ID returned from
1165 /// prior calls to these routines (with the same `cache`) is considered
1166 /// invalid (even if it gives an appearance of working). State IDs returned
1167 /// from _any_ prior call for different `cache` values are also always
1168 /// invalid.
1169 ///
1170 /// The returned ID is always a valid ID when `current` refers to a valid
1171 /// ID. Moreover, this routine is defined for all possible values of
1172 /// `input`.
1173 ///
1174 /// These validity rules are not checked, even in debug mode. Callers are
1175 /// required to uphold these rules themselves.
1176 ///
1177 /// Violating these state ID validity rules will not sacrifice memory
1178 /// safety, but _may_ produce an incorrect result or a panic.
1179 ///
1180 /// # Panics
1181 ///
1182 /// If the given ID does not refer to a valid state, then this routine
1183 /// may panic but it also may not panic and instead return an invalid or
1184 /// incorrect ID.
1185 ///
1186 /// # Example
1187 ///
1188 /// This shows a simplistic example for walking a lazy DFA for a given
1189 /// haystack by using the `next_state` method.
1190 ///
1191 /// ```
1192 /// use regex_automata::{hybrid::dfa::DFA, Input};
1193 ///
1194 /// let dfa = DFA::new(r"[a-z]+r")?;
1195 /// let mut cache = dfa.create_cache();
1196 /// let haystack = "bar".as_bytes();
1197 ///
1198 /// // The start state is determined by inspecting the position and the
1199 /// // initial bytes of the haystack.
1200 /// let mut sid = dfa.start_state_forward(
1201 /// &mut cache, &Input::new(haystack),
1202 /// )?;
1203 /// // Walk all the bytes in the haystack.
1204 /// for &b in haystack {
1205 /// sid = dfa.next_state(&mut cache, sid, b)?;
1206 /// }
1207 /// // Matches are always delayed by 1 byte, so we must explicitly walk the
1208 /// // special "EOI" transition at the end of the search.
1209 /// sid = dfa.next_eoi_state(&mut cache, sid)?;
1210 /// assert!(sid.is_match());
1211 ///
1212 /// # Ok::<(), Box<dyn std::error::Error>>(())
1213 /// ```
1214 #[inline]
1215 pub fn next_state(
1216 &self,
1217 cache: &mut Cache,
1218 current: LazyStateID,
1219 input: u8,
1220 ) -> Result<LazyStateID, CacheError> {
1221 let class = usize::from(self.classes.get(input));
1222 let offset = current.as_usize_untagged() + class;
1223 let sid = cache.trans[offset];
1224 if !sid.is_unknown() {
1225 return Ok(sid);
1226 }
1227 let unit = alphabet::Unit::u8(input);
1228 Lazy::new(self, cache).cache_next_state(current, unit)
1229 }
1230
1231 /// Transitions from the current state to the next state, given the next
1232 /// byte of input and a state ID that is not tagged.
1233 ///
1234 /// The only reason to use this routine is performance. In particular, the
1235 /// `next_state` method needs to do some additional checks, among them is
1236 /// to account for identifiers to states that are not yet computed. In
1237 /// such a case, the transition is computed on the fly. However, if it is
1238 /// known that the `current` state ID is untagged, then these checks can be
1239 /// omitted.
1240 ///
1241 /// Since this routine does not compute states on the fly, it does not
1242 /// modify the cache and thus cannot return an error. Consequently, `cache`
1243 /// does not need to be mutable and it is possible for this routine to
1244 /// return a state ID corresponding to the special "unknown" state. In
1245 /// this case, it is the caller's responsibility to use the prior state
1246 /// ID and `input` with `next_state` in order to force the computation of
1247 /// the unknown transition. Otherwise, trying to use the "unknown" state
1248 /// ID will just result in transitioning back to itself, and thus never
1249 /// terminating. (This is technically a special exemption to the state ID
1250 /// validity rules, but is permissible since this routine is guaranteed to
1251 /// never mutate the given `cache`, and thus the identifier is guaranteed
1252 /// to remain valid.)
1253 ///
1254 /// See [`LazyStateID`] for more details on what it means for a state ID
1255 /// to be tagged. Also, see
1256 /// [`next_state_untagged_unchecked`](DFA::next_state_untagged_unchecked)
1257 /// for this same idea, but with bounds checks forcefully elided.
1258 ///
1259 /// # State identifier validity
1260 ///
1261 /// The only valid value for `current` is an **untagged** lazy
1262 /// state ID returned by the most recent call to `next_state`,
1263 /// `next_state_untagged`, `next_state_untagged_unchecked`,
1264 /// `start_state_forward` or `state_state_reverse` for the given `cache`.
1265 /// Any state ID returned from prior calls to these routines (with the
1266 /// same `cache`) is considered invalid (even if it gives an appearance
1267 /// of working). State IDs returned from _any_ prior call for different
1268 /// `cache` values are also always invalid.
1269 ///
1270 /// The returned ID is always a valid ID when `current` refers to a valid
1271 /// ID, although it may be tagged. Moreover, this routine is defined for
1272 /// all possible values of `input`.
1273 ///
1274 /// Not all validity rules are checked, even in debug mode. Callers are
1275 /// required to uphold these rules themselves.
1276 ///
1277 /// Violating these state ID validity rules will not sacrifice memory
1278 /// safety, but _may_ produce an incorrect result or a panic.
1279 ///
1280 /// # Panics
1281 ///
1282 /// If the given ID does not refer to a valid state, then this routine
1283 /// may panic but it also may not panic and instead return an invalid or
1284 /// incorrect ID.
1285 ///
1286 /// # Example
1287 ///
1288 /// This shows a simplistic example for walking a lazy DFA for a given
1289 /// haystack by using the `next_state_untagged` method where possible.
1290 ///
1291 /// ```
1292 /// use regex_automata::{hybrid::dfa::DFA, Input};
1293 ///
1294 /// let dfa = DFA::new(r"[a-z]+r")?;
1295 /// let mut cache = dfa.create_cache();
1296 /// let haystack = "bar".as_bytes();
1297 ///
1298 /// // The start state is determined by inspecting the position and the
1299 /// // initial bytes of the haystack.
1300 /// let mut sid = dfa.start_state_forward(
1301 /// &mut cache, &Input::new(haystack),
1302 /// )?;
1303 /// // Walk all the bytes in the haystack.
1304 /// let mut at = 0;
1305 /// while at < haystack.len() {
1306 /// if sid.is_tagged() {
1307 /// sid = dfa.next_state(&mut cache, sid, haystack[at])?;
1308 /// } else {
1309 /// let mut prev_sid = sid;
1310 /// // We attempt to chew through as much as we can while moving
1311 /// // through untagged state IDs. Thus, the transition function
1312 /// // does less work on average per byte. (Unrolling this loop
1313 /// // may help even more.)
1314 /// while at < haystack.len() {
1315 /// prev_sid = sid;
1316 /// sid = dfa.next_state_untagged(
1317 /// &mut cache, sid, haystack[at],
1318 /// );
1319 /// at += 1;
1320 /// if sid.is_tagged() {
1321 /// break;
1322 /// }
1323 /// }
1324 /// // We must ensure that we never proceed to the next iteration
1325 /// // with an unknown state ID. If we don't account for this
1326 /// // case, then search isn't guaranteed to terminate since all
1327 /// // transitions on unknown states loop back to itself.
1328 /// if sid.is_unknown() {
1329 /// sid = dfa.next_state(
1330 /// &mut cache, prev_sid, haystack[at - 1],
1331 /// )?;
1332 /// }
1333 /// }
1334 /// }
1335 /// // Matches are always delayed by 1 byte, so we must explicitly walk the
1336 /// // special "EOI" transition at the end of the search.
1337 /// sid = dfa.next_eoi_state(&mut cache, sid)?;
1338 /// assert!(sid.is_match());
1339 ///
1340 /// # Ok::<(), Box<dyn std::error::Error>>(())
1341 /// ```
1342 #[inline]
1343 pub fn next_state_untagged(
1344 &self,
1345 cache: &Cache,
1346 current: LazyStateID,
1347 input: u8,
1348 ) -> LazyStateID {
1349 debug_assert!(!current.is_tagged());
1350 let class = usize::from(self.classes.get(input));
1351 let offset = current.as_usize_unchecked() + class;
1352 cache.trans[offset]
1353 }
1354
1355 /// Transitions from the current state to the next state, eliding bounds
1356 /// checks, given the next byte of input and a state ID that is not tagged.
1357 ///
1358 /// The only reason to use this routine is performance. In particular, the
1359 /// `next_state` method needs to do some additional checks, among them is
1360 /// to account for identifiers to states that are not yet computed. In
1361 /// such a case, the transition is computed on the fly. However, if it is
1362 /// known that the `current` state ID is untagged, then these checks can be
1363 /// omitted.
1364 ///
1365 /// Since this routine does not compute states on the fly, it does not
1366 /// modify the cache and thus cannot return an error. Consequently, `cache`
1367 /// does not need to be mutable and it is possible for this routine to
1368 /// return a state ID corresponding to the special "unknown" state. In
1369 /// this case, it is the caller's responsibility to use the prior state
1370 /// ID and `input` with `next_state` in order to force the computation of
1371 /// the unknown transition. Otherwise, trying to use the "unknown" state
1372 /// ID will just result in transitioning back to itself, and thus never
1373 /// terminating. (This is technically a special exemption to the state ID
1374 /// validity rules, but is permissible since this routine is guaranteed to
1375 /// never mutate the given `cache`, and thus the identifier is guaranteed
1376 /// to remain valid.)
1377 ///
1378 /// See [`LazyStateID`] for more details on what it means for a state ID
1379 /// to be tagged. Also, see
1380 /// [`next_state_untagged`](DFA::next_state_untagged)
1381 /// for this same idea, but with memory safety guaranteed by retaining
1382 /// bounds checks.
1383 ///
1384 /// # State identifier validity
1385 ///
1386 /// The only valid value for `current` is an **untagged** lazy
1387 /// state ID returned by the most recent call to `next_state`,
1388 /// `next_state_untagged`, `next_state_untagged_unchecked`,
1389 /// `start_state_forward` or `state_state_reverse` for the given `cache`.
1390 /// Any state ID returned from prior calls to these routines (with the
1391 /// same `cache`) is considered invalid (even if it gives an appearance
1392 /// of working). State IDs returned from _any_ prior call for different
1393 /// `cache` values are also always invalid.
1394 ///
1395 /// The returned ID is always a valid ID when `current` refers to a valid
1396 /// ID, although it may be tagged. Moreover, this routine is defined for
1397 /// all possible values of `input`.
1398 ///
1399 /// Not all validity rules are checked, even in debug mode. Callers are
1400 /// required to uphold these rules themselves.
1401 ///
1402 /// Violating these state ID validity rules will not sacrifice memory
1403 /// safety, but _may_ produce an incorrect result or a panic.
1404 ///
1405 /// # Safety
1406 ///
1407 /// Callers of this method must guarantee that `current` refers to a valid
1408 /// state ID according to the rules described above. If `current` is not a
1409 /// valid state ID for this automaton, then calling this routine may result
1410 /// in undefined behavior.
1411 ///
1412 /// If `current` is valid, then the ID returned is valid for all possible
1413 /// values of `input`.
1414 #[inline]
1415 pub unsafe fn next_state_untagged_unchecked(
1416 &self,
1417 cache: &Cache,
1418 current: LazyStateID,
1419 input: u8,
1420 ) -> LazyStateID {
1421 debug_assert!(!current.is_tagged());
1422 let class = usize::from(self.classes.get(input));
1423 let offset = current.as_usize_unchecked() + class;
1424 *cache.trans.get_unchecked(offset)
1425 }
1426
1427 /// Transitions from the current state to the next state for the special
1428 /// EOI symbol.
1429 ///
1430 /// The given cache is used to either reuse pre-computed state
1431 /// transitions, or to store this newly computed transition for future
1432 /// reuse. Thus, this routine guarantees that it will never return a state
1433 /// ID that has an "unknown" tag.
1434 ///
1435 /// This routine must be called at the end of every search in a correct
1436 /// implementation of search. Namely, lazy DFAs in this crate delay matches
1437 /// by one byte in order to support look-around operators. Thus, after
1438 /// reaching the end of a haystack, a search implementation must follow one
1439 /// last EOI transition.
1440 ///
1441 /// It is best to think of EOI as an additional symbol in the alphabet of a
1442 /// DFA that is distinct from every other symbol. That is, the alphabet of
1443 /// lazy DFAs in this crate has a logical size of 257 instead of 256, where
1444 /// 256 corresponds to every possible inhabitant of `u8`. (In practice, the
1445 /// physical alphabet size may be smaller because of alphabet compression
1446 /// via equivalence classes, but EOI is always represented somehow in the
1447 /// alphabet.)
1448 ///
1449 /// # State identifier validity
1450 ///
1451 /// The only valid value for `current` is the lazy state ID returned
1452 /// by the most recent call to `next_state`, `next_state_untagged`,
1453 /// `next_state_untagged_unchecked`, `start_state_forward` or
1454 /// `state_state_reverse` for the given `cache`. Any state ID returned from
1455 /// prior calls to these routines (with the same `cache`) is considered
1456 /// invalid (even if it gives an appearance of working). State IDs returned
1457 /// from _any_ prior call for different `cache` values are also always
1458 /// invalid.
1459 ///
1460 /// The returned ID is always a valid ID when `current` refers to a valid
1461 /// ID.
1462 ///
1463 /// These validity rules are not checked, even in debug mode. Callers are
1464 /// required to uphold these rules themselves.
1465 ///
1466 /// Violating these state ID validity rules will not sacrifice memory
1467 /// safety, but _may_ produce an incorrect result or a panic.
1468 ///
1469 /// # Panics
1470 ///
1471 /// If the given ID does not refer to a valid state, then this routine
1472 /// may panic but it also may not panic and instead return an invalid or
1473 /// incorrect ID.
1474 ///
1475 /// # Example
1476 ///
1477 /// This shows a simplistic example for walking a DFA for a given haystack,
1478 /// and then finishing the search with the final EOI transition.
1479 ///
1480 /// ```
1481 /// use regex_automata::{hybrid::dfa::DFA, Input};
1482 ///
1483 /// let dfa = DFA::new(r"[a-z]+r")?;
1484 /// let mut cache = dfa.create_cache();
1485 /// let haystack = "bar".as_bytes();
1486 ///
1487 /// // The start state is determined by inspecting the position and the
1488 /// // initial bytes of the haystack.
1489 /// let mut sid = dfa.start_state_forward(
1490 /// &mut cache, &Input::new(haystack),
1491 /// )?;
1492 /// // Walk all the bytes in the haystack.
1493 /// for &b in haystack {
1494 /// sid = dfa.next_state(&mut cache, sid, b)?;
1495 /// }
1496 /// // Matches are always delayed by 1 byte, so we must explicitly walk
1497 /// // the special "EOI" transition at the end of the search. Without this
1498 /// // final transition, the assert below will fail since the DFA will not
1499 /// // have entered a match state yet!
1500 /// sid = dfa.next_eoi_state(&mut cache, sid)?;
1501 /// assert!(sid.is_match());
1502 ///
1503 /// # Ok::<(), Box<dyn std::error::Error>>(())
1504 /// ```
1505 #[inline]
1506 pub fn next_eoi_state(
1507 &self,
1508 cache: &mut Cache,
1509 current: LazyStateID,
1510 ) -> Result<LazyStateID, CacheError> {
1511 let eoi = self.classes.eoi().as_usize();
1512 let offset = current.as_usize_untagged() + eoi;
1513 let sid = cache.trans[offset];
1514 if !sid.is_unknown() {
1515 return Ok(sid);
1516 }
1517 let unit = self.classes.eoi();
1518 Lazy::new(self, cache).cache_next_state(current, unit)
1519 }
1520
1521 /// Return the ID of the start state for this lazy DFA for the given
1522 /// starting configuration.
1523 ///
1524 /// Unlike typical DFA implementations, the start state for DFAs in this
1525 /// crate is dependent on a few different factors:
1526 ///
1527 /// * The [`Anchored`] mode of the search. Unanchored, anchored and
1528 /// anchored searches for a specific [`PatternID`] all use different start
1529 /// states.
1530 /// * Whether a "look-behind" byte exists. For example, the `^` anchor
1531 /// matches if and only if there is no look-behind byte.
1532 /// * The specific value of that look-behind byte. For example, a `(?m:^)`
1533 /// assertion only matches when there is either no look-behind byte, or
1534 /// when the look-behind byte is a line terminator.
1535 ///
1536 /// The [starting configuration](start::Config) provides the above
1537 /// information.
1538 ///
1539 /// This routine can be used for either forward or reverse searches.
1540 /// Although, as a convenience, if you have an [`Input`], then it
1541 /// may be more succinct to use [`DFA::start_state_forward`] or
1542 /// [`DFA::start_state_reverse`]. Note, for example, that the convenience
1543 /// routines return a [`MatchError`] on failure where as this routine
1544 /// returns a [`StartError`].
1545 ///
1546 /// # Errors
1547 ///
1548 /// This may return a [`StartError`] if the search needs to give up when
1549 /// determining the start state (for example, if it sees a "quit" byte
1550 /// or if the cache has become inefficient). This can also return an
1551 /// error if the given configuration contains an unsupported [`Anchored`]
1552 /// configuration.
1553 #[cfg_attr(feature = "perf-inline", inline(always))]
1554 pub fn start_state(
1555 &self,
1556 cache: &mut Cache,
1557 config: &start::Config,
1558 ) -> Result<LazyStateID, StartError> {
1559 let lazy = LazyRef::new(self, cache);
1560 let anchored = config.get_anchored();
1561 let start = match config.get_look_behind() {
1562 None => Start::Text,
1563 Some(byte) => {
1564 if !self.quitset.is_empty() && self.quitset.contains(byte) {
1565 return Err(StartError::quit(byte));
1566 }
1567 self.start_map.get(byte)
1568 }
1569 };
1570 let start_id = lazy.get_cached_start_id(anchored, start)?;
1571 if !start_id.is_unknown() {
1572 return Ok(start_id);
1573 }
1574 Lazy::new(self, cache).cache_start_group(anchored, start)
1575 }
1576
1577 /// Return the ID of the start state for this lazy DFA when executing a
1578 /// forward search.
1579 ///
1580 /// This is a convenience routine for calling [`DFA::start_state`] that
1581 /// converts the given [`Input`] to a [start configuration](start::Config).
1582 /// Additionally, if an error occurs, it is converted from a [`StartError`]
1583 /// to a [`MatchError`] using the offset information in the given
1584 /// [`Input`].
1585 ///
1586 /// # Errors
1587 ///
1588 /// This may return a [`MatchError`] if the search needs to give up when
1589 /// determining the start state (for example, if it sees a "quit" byte or
1590 /// if the cache has become inefficient). This can also return an error if
1591 /// the given `Input` contains an unsupported [`Anchored`] configuration.
1592 #[cfg_attr(feature = "perf-inline", inline(always))]
1593 pub fn start_state_forward(
1594 &self,
1595 cache: &mut Cache,
1596 input: &Input<'_>,
1597 ) -> Result<LazyStateID, MatchError> {
1598 let config = start::Config::from_input_forward(input);
1599 self.start_state(cache, &config).map_err(|err| match err {
1600 StartError::Cache { .. } => MatchError::gave_up(input.start()),
1601 StartError::Quit { byte } => {
1602 let offset = input
1603 .start()
1604 .checked_sub(1)
1605 .expect("no quit in start without look-behind");
1606 MatchError::quit(byte, offset)
1607 }
1608 StartError::UnsupportedAnchored { mode } => {
1609 MatchError::unsupported_anchored(mode)
1610 }
1611 })
1612 }
1613
1614 /// Return the ID of the start state for this lazy DFA when executing a
1615 /// reverse search.
1616 ///
1617 /// This is a convenience routine for calling [`DFA::start_state`] that
1618 /// converts the given [`Input`] to a [start configuration](start::Config).
1619 /// Additionally, if an error occurs, it is converted from a [`StartError`]
1620 /// to a [`MatchError`] using the offset information in the given
1621 /// [`Input`].
1622 ///
1623 /// # Errors
1624 ///
1625 /// This may return a [`MatchError`] if the search needs to give up when
1626 /// determining the start state (for example, if it sees a "quit" byte or
1627 /// if the cache has become inefficient). This can also return an error if
1628 /// the given `Input` contains an unsupported [`Anchored`] configuration.
1629 #[cfg_attr(feature = "perf-inline", inline(always))]
1630 pub fn start_state_reverse(
1631 &self,
1632 cache: &mut Cache,
1633 input: &Input<'_>,
1634 ) -> Result<LazyStateID, MatchError> {
1635 let config = start::Config::from_input_reverse(input);
1636 self.start_state(cache, &config).map_err(|err| match err {
1637 StartError::Cache { .. } => MatchError::gave_up(input.end()),
1638 StartError::Quit { byte } => {
1639 let offset = input.end();
1640 MatchError::quit(byte, offset)
1641 }
1642 StartError::UnsupportedAnchored { mode } => {
1643 MatchError::unsupported_anchored(mode)
1644 }
1645 })
1646 }
1647
1648 /// Returns the total number of patterns that match in this state.
1649 ///
1650 /// If the lazy DFA was compiled with one pattern, then this must
1651 /// necessarily always return `1` for all match states.
1652 ///
1653 /// A lazy DFA guarantees that [`DFA::match_pattern`] can be called with
1654 /// indices up to (but not including) the length returned by this routine
1655 /// without panicking.
1656 ///
1657 /// # Panics
1658 ///
1659 /// If the given state is not a match state, then this may either panic
1660 /// or return an incorrect result.
1661 ///
1662 /// # Example
1663 ///
1664 /// This example shows a simple instance of implementing overlapping
1665 /// matches. In particular, it shows not only how to determine how many
1666 /// patterns have matched in a particular state, but also how to access
1667 /// which specific patterns have matched.
1668 ///
1669 /// Notice that we must use [`MatchKind::All`] when building the DFA. If we
1670 /// used [`MatchKind::LeftmostFirst`] instead, then the DFA would not be
1671 /// constructed in a way that supports overlapping matches. (It would only
1672 /// report a single pattern that matches at any particular point in time.)
1673 ///
1674 /// Another thing to take note of is the patterns used and the order in
1675 /// which the pattern IDs are reported. In the example below, pattern `3`
1676 /// is yielded first. Why? Because it corresponds to the match that
1677 /// appears first. Namely, the `@` symbol is part of `\S+` but not part
1678 /// of any of the other patterns. Since the `\S+` pattern has a match that
1679 /// starts to the left of any other pattern, its ID is returned before any
1680 /// other.
1681 ///
1682 /// ```
1683 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
1684 /// use regex_automata::{hybrid::dfa::DFA, Input, MatchKind};
1685 ///
1686 /// let dfa = DFA::builder()
1687 /// .configure(DFA::config().match_kind(MatchKind::All))
1688 /// .build_many(&[
1689 /// r"\w+", r"[a-z]+", r"[A-Z]+", r"\S+",
1690 /// ])?;
1691 /// let mut cache = dfa.create_cache();
1692 /// let haystack = "@bar".as_bytes();
1693 ///
1694 /// // The start state is determined by inspecting the position and the
1695 /// // initial bytes of the haystack.
1696 /// let mut sid = dfa.start_state_forward(
1697 /// &mut cache, &Input::new(haystack),
1698 /// )?;
1699 /// // Walk all the bytes in the haystack.
1700 /// for &b in haystack {
1701 /// sid = dfa.next_state(&mut cache, sid, b)?;
1702 /// }
1703 /// sid = dfa.next_eoi_state(&mut cache, sid)?;
1704 ///
1705 /// assert!(sid.is_match());
1706 /// assert_eq!(dfa.match_len(&mut cache, sid), 3);
1707 /// // The following calls are guaranteed to not panic since `match_len`
1708 /// // returned `3` above.
1709 /// assert_eq!(dfa.match_pattern(&mut cache, sid, 0).as_usize(), 3);
1710 /// assert_eq!(dfa.match_pattern(&mut cache, sid, 1).as_usize(), 0);
1711 /// assert_eq!(dfa.match_pattern(&mut cache, sid, 2).as_usize(), 1);
1712 ///
1713 /// # Ok::<(), Box<dyn std::error::Error>>(())
1714 /// ```
1715 #[inline]
1716 pub fn match_len(&self, cache: &Cache, id: LazyStateID) -> usize {
1717 assert!(id.is_match());
1718 LazyRef::new(self, cache).get_cached_state(id).match_len()
1719 }
1720
1721 /// Returns the pattern ID corresponding to the given match index in the
1722 /// given state.
1723 ///
1724 /// See [`DFA::match_len`] for an example of how to use this method
1725 /// correctly. Note that if you know your lazy DFA is configured with a
1726 /// single pattern, then this routine is never necessary since it will
1727 /// always return a pattern ID of `0` for an index of `0` when `id`
1728 /// corresponds to a match state.
1729 ///
1730 /// Typically, this routine is used when implementing an overlapping
1731 /// search, as the example for `DFA::match_len` does.
1732 ///
1733 /// # Panics
1734 ///
1735 /// If the state ID is not a match state or if the match index is out
1736 /// of bounds for the given state, then this routine may either panic
1737 /// or produce an incorrect result. If the state ID is correct and the
1738 /// match index is correct, then this routine always produces a valid
1739 /// `PatternID`.
1740 #[inline]
1741 pub fn match_pattern(
1742 &self,
1743 cache: &Cache,
1744 id: LazyStateID,
1745 match_index: usize,
1746 ) -> PatternID {
1747 // This is an optimization for the very common case of a DFA with a
1748 // single pattern. This conditional avoids a somewhat more costly path
1749 // that finds the pattern ID from the corresponding `State`, which
1750 // requires a bit of slicing/pointer-chasing. This optimization tends
1751 // to only matter when matches are frequent.
1752 if self.pattern_len() == 1 {
1753 return PatternID::ZERO;
1754 }
1755 LazyRef::new(self, cache)
1756 .get_cached_state(id)
1757 .match_pattern(match_index)
1758 }
1759}
1760
1761/// A cache represents a partially computed DFA.
1762///
1763/// A cache is the key component that differentiates a classical DFA and a
1764/// hybrid NFA/DFA (also called a "lazy DFA"). Where a classical DFA builds a
1765/// complete transition table that can handle all possible inputs, a hybrid
1766/// NFA/DFA starts with an empty transition table and builds only the parts
1767/// required during search. The parts that are built are stored in a cache. For
1768/// this reason, a cache is a required parameter for nearly every operation on
1769/// a [`DFA`].
1770///
1771/// Caches can be created from their corresponding DFA via
1772/// [`DFA::create_cache`]. A cache can only be used with either the DFA that
1773/// created it, or the DFA that was most recently used to reset it with
1774/// [`Cache::reset`]. Using a cache with any other DFA may result in panics
1775/// or incorrect results.
1776#[derive(Clone, Debug)]
1777pub struct Cache {
1778 // N.B. If you're looking to understand how determinization works, it
1779 // is probably simpler to first grok src/dfa/determinize.rs, since that
1780 // doesn't have the "laziness" component.
1781 /// The transition table.
1782 ///
1783 /// Given a `current` LazyStateID and an `input` byte, the next state can
1784 /// be computed via `trans[untagged(current) + equiv_class(input)]`. Notice
1785 /// that no multiplication is used. That's because state identifiers are
1786 /// "premultiplied."
1787 ///
1788 /// Note that the next state may be the "unknown" state. In this case, the
1789 /// next state is not known and determinization for `current` on `input`
1790 /// must be performed.
1791 trans: Vec<LazyStateID>,
1792 /// The starting states for this DFA.
1793 ///
1794 /// These are computed lazily. Initially, these are all set to "unknown"
1795 /// lazy state IDs.
1796 ///
1797 /// When 'starts_for_each_pattern' is disabled (the default), then the size
1798 /// of this is constrained to the possible starting configurations based
1799 /// on the search parameters. (At time of writing, that's 4.) However,
1800 /// when starting states for each pattern is enabled, then there are N
1801 /// additional groups of starting states, where each group reflects the
1802 /// different possible configurations and N is the number of patterns.
1803 starts: Vec<LazyStateID>,
1804 /// A sequence of NFA/DFA powerset states that have been computed for this
1805 /// lazy DFA. This sequence is indexable by untagged LazyStateIDs. (Every
1806 /// tagged LazyStateID can be used to index this sequence by converting it
1807 /// to its untagged form.)
1808 states: Vec<State>,
1809 /// A map from states to their corresponding IDs. This map may be accessed
1810 /// via the raw byte representation of a state, which means that a `State`
1811 /// does not need to be allocated to determine whether it already exists
1812 /// in this map. Indeed, the existence of such a state is what determines
1813 /// whether we allocate a new `State` or not.
1814 ///
1815 /// The higher level idea here is that we do just enough determinization
1816 /// for a state to check whether we've already computed it. If we have,
1817 /// then we can save a little (albeit not much) work. The real savings is
1818 /// in memory usage. If we never checked for trivially duplicate states,
1819 /// then our memory usage would explode to unreasonable levels.
1820 states_to_id: StateMap,
1821 /// Sparse sets used to track which NFA states have been visited during
1822 /// various traversals.
1823 sparses: SparseSets,
1824 /// Scratch space for traversing the NFA graph. (We use space on the heap
1825 /// instead of the call stack.)
1826 stack: Vec<NFAStateID>,
1827 /// Scratch space for building a NFA/DFA powerset state. This is used to
1828 /// help amortize allocation since not every powerset state generated is
1829 /// added to the cache. In particular, if it already exists in the cache,
1830 /// then there is no need to allocate a new `State` for it.
1831 scratch_state_builder: StateBuilderEmpty,
1832 /// A simple abstraction for handling the saving of at most a single state
1833 /// across a cache clearing. This is required for correctness. Namely, if
1834 /// adding a new state after clearing the cache fails, then the caller
1835 /// must retain the ability to continue using the state ID given. The
1836 /// state corresponding to the state ID is what we preserve across cache
1837 /// clearings.
1838 state_saver: StateSaver,
1839 /// The memory usage, in bytes, used by 'states' and 'states_to_id'. We
1840 /// track this as new states are added since states use a variable amount
1841 /// of heap. Tracking this as we add states makes it possible to compute
1842 /// the total amount of memory used by the determinizer in constant time.
1843 memory_usage_state: usize,
1844 /// The number of times the cache has been cleared. When a minimum cache
1845 /// clear count is set, then the cache will return an error instead of
1846 /// clearing the cache if the count has been exceeded.
1847 clear_count: usize,
1848 /// The total number of bytes searched since the last time this cache was
1849 /// cleared, not including the current search.
1850 ///
1851 /// This can be added to the length of the current search to get the true
1852 /// total number of bytes searched.
1853 ///
1854 /// This is generally only non-zero when the
1855 /// `Cache::search_{start,update,finish}` APIs are used to track search
1856 /// progress.
1857 bytes_searched: usize,
1858 /// The progress of the current search.
1859 ///
1860 /// This is only non-`None` when callers utilize the `Cache::search_start`,
1861 /// `Cache::search_update` and `Cache::search_finish` APIs.
1862 ///
1863 /// The purpose of recording search progress is to be able to make a
1864 /// determination about the efficiency of the cache. Namely, by keeping
1865 /// track of the
1866 progress: Option<SearchProgress>,
1867}
1868
1869impl Cache {
1870 /// Create a new cache for the given lazy DFA.
1871 ///
1872 /// The cache returned should only be used for searches for the given DFA.
1873 /// If you want to reuse the cache for another DFA, then you must call
1874 /// [`Cache::reset`] with that DFA.
1875 pub fn new(dfa: &DFA) -> Cache {
1876 let mut cache = Cache {
1877 trans: alloc::vec![],
1878 starts: alloc::vec![],
1879 states: alloc::vec![],
1880 states_to_id: StateMap::new(),
1881 sparses: SparseSets::new(dfa.get_nfa().states().len()),
1882 stack: alloc::vec![],
1883 scratch_state_builder: StateBuilderEmpty::new(),
1884 state_saver: StateSaver::none(),
1885 memory_usage_state: 0,
1886 clear_count: 0,
1887 bytes_searched: 0,
1888 progress: None,
1889 };
1890 debug!("pre-init lazy DFA cache size: {}", cache.memory_usage());
1891 Lazy { dfa, cache: &mut cache }.init_cache();
1892 debug!("post-init lazy DFA cache size: {}", cache.memory_usage());
1893 cache
1894 }
1895
1896 /// Reset this cache such that it can be used for searching with the given
1897 /// lazy DFA (and only that DFA).
1898 ///
1899 /// A cache reset permits reusing memory already allocated in this cache
1900 /// with a different lazy DFA.
1901 ///
1902 /// Resetting a cache sets its "clear count" to 0. This is relevant if the
1903 /// lazy DFA has been configured to "give up" after it has cleared the
1904 /// cache a certain number of times.
1905 ///
1906 /// Any lazy state ID generated by the cache prior to resetting it is
1907 /// invalid after the reset.
1908 ///
1909 /// # Example
1910 ///
1911 /// This shows how to re-purpose a cache for use with a different DFA.
1912 ///
1913 /// ```
1914 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
1915 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
1916 ///
1917 /// let dfa1 = DFA::new(r"\w")?;
1918 /// let dfa2 = DFA::new(r"\W")?;
1919 ///
1920 /// let mut cache = dfa1.create_cache();
1921 /// assert_eq!(
1922 /// Some(HalfMatch::must(0, 2)),
1923 /// dfa1.try_search_fwd(&mut cache, &Input::new("Δ"))?,
1924 /// );
1925 ///
1926 /// // Using 'cache' with dfa2 is not allowed. It may result in panics or
1927 /// // incorrect results. In order to re-purpose the cache, we must reset
1928 /// // it with the DFA we'd like to use it with.
1929 /// //
1930 /// // Similarly, after this reset, using the cache with 'dfa1' is also not
1931 /// // allowed.
1932 /// cache.reset(&dfa2);
1933 /// assert_eq!(
1934 /// Some(HalfMatch::must(0, 3)),
1935 /// dfa2.try_search_fwd(&mut cache, &Input::new("☃"))?,
1936 /// );
1937 ///
1938 /// # Ok::<(), Box<dyn std::error::Error>>(())
1939 /// ```
1940 pub fn reset(&mut self, dfa: &DFA) {
1941 Lazy::new(dfa, self).reset_cache()
1942 }
1943
1944 /// Initializes a new search starting at the given position.
1945 ///
1946 /// If a previous search was unfinished, then it is finished automatically
1947 /// and a new search is begun.
1948 ///
1949 /// Note that keeping track of search progress is _not necessary_
1950 /// for correct implementations of search using a lazy DFA. Keeping
1951 /// track of search progress is only necessary if you want the
1952 /// [`Config::minimum_bytes_per_state`] configuration knob to work.
1953 #[inline]
1954 pub fn search_start(&mut self, at: usize) {
1955 // If a previous search wasn't marked as finished, then finish it
1956 // now automatically.
1957 if let Some(p) = self.progress.take() {
1958 self.bytes_searched += p.len();
1959 }
1960 self.progress = Some(SearchProgress { start: at, at });
1961 }
1962
1963 /// Updates the current search to indicate that it has search to the
1964 /// current position.
1965 ///
1966 /// No special care needs to be taken for reverse searches. Namely, the
1967 /// position given may be _less than_ the starting position of the search.
1968 ///
1969 /// # Panics
1970 ///
1971 /// This panics if no search has been started by [`Cache::search_start`].
1972 #[inline]
1973 pub fn search_update(&mut self, at: usize) {
1974 let p =
1975 self.progress.as_mut().expect("no in-progress search to update");
1976 p.at = at;
1977 }
1978
1979 /// Indicates that a search has finished at the given position.
1980 ///
1981 /// # Panics
1982 ///
1983 /// This panics if no search has been started by [`Cache::search_start`].
1984 #[inline]
1985 pub fn search_finish(&mut self, at: usize) {
1986 let mut p =
1987 self.progress.take().expect("no in-progress search to finish");
1988 p.at = at;
1989 self.bytes_searched += p.len();
1990 }
1991
1992 /// Returns the total number of bytes that have been searched since this
1993 /// cache was last cleared.
1994 ///
1995 /// This is useful for determining the efficiency of the cache. For
1996 /// example, the lazy DFA uses this value in conjunction with the
1997 /// [`Config::minimum_bytes_per_state`] knob to help determine whether it
1998 /// should quit searching.
1999 ///
2000 /// This always returns `0` if search progress isn't being tracked. Note
2001 /// that the lazy DFA search routines in this crate always track search
2002 /// progress.
2003 pub fn search_total_len(&self) -> usize {
2004 self.bytes_searched + self.progress.as_ref().map_or(0, |p| p.len())
2005 }
2006
2007 /// Returns the total number of times this cache has been cleared since it
2008 /// was either created or last reset.
2009 ///
2010 /// This is useful for informational purposes or if you want to change
2011 /// search strategies based on the number of times the cache has been
2012 /// cleared.
2013 pub fn clear_count(&self) -> usize {
2014 self.clear_count
2015 }
2016
2017 /// Returns the heap memory usage, in bytes, of this cache.
2018 ///
2019 /// This does **not** include the stack size used up by this cache. To
2020 /// compute that, use `std::mem::size_of::<Cache>()`.
2021 pub fn memory_usage(&self) -> usize {
2022 const ID_SIZE: usize = size_of::<LazyStateID>();
2023 const STATE_SIZE: usize = size_of::<State>();
2024
2025 // NOTE: If you make changes to the below, then
2026 // 'minimum_cache_capacity' should be updated correspondingly.
2027
2028 self.trans.len() * ID_SIZE
2029 + self.starts.len() * ID_SIZE
2030 + self.states.len() * STATE_SIZE
2031 // Maps likely use more memory than this, but it's probably close.
2032 + self.states_to_id.len() * (STATE_SIZE + ID_SIZE)
2033 + self.sparses.memory_usage()
2034 + self.stack.capacity() * ID_SIZE
2035 + self.scratch_state_builder.capacity()
2036 // Heap memory used by 'State' in both 'states' and 'states_to_id'.
2037 + self.memory_usage_state
2038 }
2039}
2040
2041/// Keeps track of the progress of the current search.
2042///
2043/// This is updated via the `Cache::search_{start,update,finish}` APIs to
2044/// record how many bytes have been searched. This permits computing a
2045/// heuristic that represents the efficiency of a cache, and thus helps inform
2046/// whether the lazy DFA should give up or not.
2047#[derive(Clone, Debug)]
2048struct SearchProgress {
2049 start: usize,
2050 at: usize,
2051}
2052
2053impl SearchProgress {
2054 /// Returns the length, in bytes, of this search so far.
2055 ///
2056 /// This automatically handles the case of a reverse search, where `at`
2057 /// is likely to be less than `start`.
2058 fn len(&self) -> usize {
2059 if self.start <= self.at {
2060 self.at - self.start
2061 } else {
2062 self.start - self.at
2063 }
2064 }
2065}
2066
2067/// A map from states to state identifiers. When using std, we use a standard
2068/// hashmap, since it's a bit faster for this use case. (Other maps, like
2069/// one's based on FNV, have not yet been benchmarked.)
2070///
2071/// The main purpose of this map is to reuse states where possible. This won't
2072/// fully minimize the DFA, but it works well in a lot of cases.
2073#[cfg(feature = "std")]
2074type StateMap = std::collections::HashMap<State, LazyStateID>;
2075#[cfg(not(feature = "std"))]
2076type StateMap = alloc::collections::BTreeMap<State, LazyStateID>;
2077
2078/// A type that groups methods that require the base NFA/DFA and writable
2079/// access to the cache.
2080#[derive(Debug)]
2081struct Lazy<'i, 'c> {
2082 dfa: &'i DFA,
2083 cache: &'c mut Cache,
2084}
2085
2086impl<'i, 'c> Lazy<'i, 'c> {
2087 /// Creates a new 'Lazy' wrapper for a DFA and its corresponding cache.
2088 fn new(dfa: &'i DFA, cache: &'c mut Cache) -> Lazy<'i, 'c> {
2089 Lazy { dfa, cache }
2090 }
2091
2092 /// Return an immutable view by downgrading a writable cache to a read-only
2093 /// cache.
2094 fn as_ref<'a>(&'a self) -> LazyRef<'i, 'a> {
2095 LazyRef::new(self.dfa, self.cache)
2096 }
2097
2098 /// This is marked as 'inline(never)' to avoid bloating methods on 'DFA'
2099 /// like 'next_state' and 'next_eoi_state' that are called in critical
2100 /// areas. The idea is to let the optimizer focus on the other areas of
2101 /// those methods as the hot path.
2102 ///
2103 /// Here's an example that justifies 'inline(never)'
2104 ///
2105 /// ```ignore
2106 /// regex-cli find match hybrid \
2107 /// --cache-capacity 100000000 \
2108 /// -p '\pL{100}'
2109 /// all-codepoints-utf8-100x
2110 /// ```
2111 ///
2112 /// Where 'all-codepoints-utf8-100x' is the UTF-8 encoding of every
2113 /// codepoint, in sequence, repeated 100 times.
2114 ///
2115 /// With 'inline(never)' hyperfine reports 1.1s per run. With
2116 /// 'inline(always)', hyperfine reports 1.23s. So that's a 10% improvement.
2117 #[cold]
2118 #[inline(never)]
2119 fn cache_next_state(
2120 &mut self,
2121 mut current: LazyStateID,
2122 unit: alphabet::Unit,
2123 ) -> Result<LazyStateID, CacheError> {
2124 let stride2 = self.dfa.stride2();
2125 let empty_builder = self.get_state_builder();
2126 let builder = determinize::next(
2127 self.dfa.get_nfa(),
2128 self.dfa.get_config().get_match_kind(),
2129 &mut self.cache.sparses,
2130 &mut self.cache.stack,
2131 &self.cache.states[current.as_usize_untagged() >> stride2],
2132 unit,
2133 empty_builder,
2134 );
2135 // This is subtle, but if we *might* clear the cache, then we should
2136 // try to save the current state so that we can re-map its ID after
2137 // cache clearing. We *might* clear the cache when either the new
2138 // state can't fit in the cache or when the number of transitions has
2139 // reached the maximum. Even if either of these conditions is true,
2140 // the cache might not be cleared if we can reuse an existing state.
2141 // But we don't know that at this point. Moreover, we don't save the
2142 // current state every time because it is costly.
2143 //
2144 // TODO: We should try to find a way to make this less subtle and error
2145 // prone. ---AG
2146 let save_state = !self.as_ref().state_builder_fits_in_cache(&builder)
2147 || self.cache.trans.len() >= LazyStateID::MAX;
2148 if save_state {
2149 self.save_state(current);
2150 }
2151 let next = self.add_builder_state(builder, |sid| sid)?;
2152 if save_state {
2153 current = self.saved_state_id();
2154 }
2155 // This is the payoff. The next time 'next_state' is called with this
2156 // state and alphabet unit, it will find this transition and avoid
2157 // having to re-determinize this transition.
2158 self.set_transition(current, unit, next);
2159 Ok(next)
2160 }
2161
2162 /// Compute and cache the starting state for the given pattern ID (if
2163 /// present) and the starting configuration.
2164 ///
2165 /// This panics if a pattern ID is given and the DFA isn't configured to
2166 /// build anchored start states for each pattern.
2167 ///
2168 /// This will never return an unknown lazy state ID.
2169 ///
2170 /// If caching this state would otherwise result in a cache that has been
2171 /// cleared too many times, then an error is returned.
2172 #[cold]
2173 #[inline(never)]
2174 fn cache_start_group(
2175 &mut self,
2176 anchored: Anchored,
2177 start: Start,
2178 ) -> Result<LazyStateID, StartError> {
2179 let nfa_start_id = match anchored {
2180 Anchored::No => self.dfa.get_nfa().start_unanchored(),
2181 Anchored::Yes => self.dfa.get_nfa().start_anchored(),
2182 Anchored::Pattern(pid) => {
2183 if !self.dfa.get_config().get_starts_for_each_pattern() {
2184 return Err(StartError::unsupported_anchored(anchored));
2185 }
2186 match self.dfa.get_nfa().start_pattern(pid) {
2187 None => return Ok(self.as_ref().dead_id()),
2188 Some(sid) => sid,
2189 }
2190 }
2191 };
2192
2193 let id = self
2194 .cache_start_one(nfa_start_id, start)
2195 .map_err(StartError::cache)?;
2196 self.set_start_state(anchored, start, id);
2197 Ok(id)
2198 }
2199
2200 /// Compute and cache the starting state for the given NFA state ID and the
2201 /// starting configuration. The NFA state ID might be one of the following:
2202 ///
2203 /// 1) An unanchored start state to match any pattern.
2204 /// 2) An anchored start state to match any pattern.
2205 /// 3) An anchored start state for a particular pattern.
2206 ///
2207 /// This will never return an unknown lazy state ID.
2208 ///
2209 /// If caching this state would otherwise result in a cache that has been
2210 /// cleared too many times, then an error is returned.
2211 fn cache_start_one(
2212 &mut self,
2213 nfa_start_id: NFAStateID,
2214 start: Start,
2215 ) -> Result<LazyStateID, CacheError> {
2216 let mut builder_matches = self.get_state_builder().into_matches();
2217 determinize::set_lookbehind_from_start(
2218 self.dfa.get_nfa(),
2219 &start,
2220 &mut builder_matches,
2221 );
2222 self.cache.sparses.set1.clear();
2223 determinize::epsilon_closure(
2224 self.dfa.get_nfa(),
2225 nfa_start_id,
2226 builder_matches.look_have(),
2227 &mut self.cache.stack,
2228 &mut self.cache.sparses.set1,
2229 );
2230 let mut builder = builder_matches.into_nfa();
2231 determinize::add_nfa_states(
2232 &self.dfa.get_nfa(),
2233 &self.cache.sparses.set1,
2234 &mut builder,
2235 );
2236 let tag_starts = self.dfa.get_config().get_specialize_start_states();
2237 self.add_builder_state(builder, |id| {
2238 if tag_starts {
2239 id.to_start()
2240 } else {
2241 id
2242 }
2243 })
2244 }
2245
2246 /// Either add the given builder state to this cache, or return an ID to an
2247 /// equivalent state already in this cache.
2248 ///
2249 /// In the case where no equivalent state exists, the idmap function given
2250 /// may be used to transform the identifier allocated. This is useful if
2251 /// the caller needs to tag the ID with additional information.
2252 ///
2253 /// This will never return an unknown lazy state ID.
2254 ///
2255 /// If caching this state would otherwise result in a cache that has been
2256 /// cleared too many times, then an error is returned.
2257 fn add_builder_state(
2258 &mut self,
2259 builder: StateBuilderNFA,
2260 idmap: impl Fn(LazyStateID) -> LazyStateID,
2261 ) -> Result<LazyStateID, CacheError> {
2262 if let Some(&cached_id) =
2263 self.cache.states_to_id.get(builder.as_bytes())
2264 {
2265 // Since we have a cached state, put the constructed state's
2266 // memory back into our scratch space, so that it can be reused.
2267 self.put_state_builder(builder);
2268 return Ok(cached_id);
2269 }
2270 let result = self.add_state(builder.to_state(), idmap);
2271 self.put_state_builder(builder);
2272 result
2273 }
2274
2275 /// Allocate a new state ID and add the given state to this cache.
2276 ///
2277 /// The idmap function given may be used to transform the identifier
2278 /// allocated. This is useful if the caller needs to tag the ID with
2279 /// additional information.
2280 ///
2281 /// This will never return an unknown lazy state ID.
2282 ///
2283 /// If caching this state would otherwise result in a cache that has been
2284 /// cleared too many times, then an error is returned.
2285 fn add_state(
2286 &mut self,
2287 state: State,
2288 idmap: impl Fn(LazyStateID) -> LazyStateID,
2289 ) -> Result<LazyStateID, CacheError> {
2290 if !self.as_ref().state_fits_in_cache(&state) {
2291 self.try_clear_cache()?;
2292 }
2293 // It's important for this to come second, since the above may clear
2294 // the cache. If we clear the cache after ID generation, then the ID
2295 // is likely bunk since it would have been generated based on a larger
2296 // transition table.
2297 let mut id = idmap(self.next_state_id()?);
2298 if state.is_match() {
2299 id = id.to_match();
2300 }
2301 // Add room in the transition table. Since this is a fresh state, all
2302 // of its transitions are unknown.
2303 self.cache.trans.extend(
2304 iter::repeat(self.as_ref().unknown_id()).take(self.dfa.stride()),
2305 );
2306 // When we add a sentinel state, we never want to set any quit
2307 // transitions. Technically, this is harmless, since sentinel states
2308 // have all of their transitions set to loop back to themselves. But
2309 // when creating sentinel states before the quit sentinel state,
2310 // this will try to call 'set_transition' on a state ID that doesn't
2311 // actually exist yet, which isn't allowed. So we just skip doing so
2312 // entirely.
2313 if !self.dfa.quitset.is_empty() && !self.as_ref().is_sentinel(id) {
2314 let quit_id = self.as_ref().quit_id();
2315 for b in self.dfa.quitset.iter() {
2316 self.set_transition(id, alphabet::Unit::u8(b), quit_id);
2317 }
2318 }
2319 self.cache.memory_usage_state += state.memory_usage();
2320 self.cache.states.push(state.clone());
2321 self.cache.states_to_id.insert(state, id);
2322 Ok(id)
2323 }
2324
2325 /// Allocate a new state ID.
2326 ///
2327 /// This will never return an unknown lazy state ID.
2328 ///
2329 /// If caching this state would otherwise result in a cache that has been
2330 /// cleared too many times, then an error is returned.
2331 fn next_state_id(&mut self) -> Result<LazyStateID, CacheError> {
2332 let sid = match LazyStateID::new(self.cache.trans.len()) {
2333 Ok(sid) => sid,
2334 Err(_) => {
2335 self.try_clear_cache()?;
2336 // This has to pass since we check that ID capacity at
2337 // construction time can fit at least MIN_STATES states.
2338 LazyStateID::new(self.cache.trans.len()).unwrap()
2339 }
2340 };
2341 Ok(sid)
2342 }
2343
2344 /// Attempt to clear the cache used by this lazy DFA.
2345 ///
2346 /// If clearing the cache exceeds the minimum number of required cache
2347 /// clearings, then this will return a cache error. In this case,
2348 /// callers should bubble this up as the cache can't be used until it is
2349 /// reset. Implementations of search should convert this error into a
2350 /// [`MatchError::gave_up`].
2351 ///
2352 /// If 'self.state_saver' is set to save a state, then this state is
2353 /// persisted through cache clearing. Otherwise, the cache is returned to
2354 /// its state after initialization with two exceptions: its clear count
2355 /// is incremented and some of its memory likely has additional capacity.
2356 /// That is, clearing a cache does _not_ release memory.
2357 ///
2358 /// Otherwise, any lazy state ID generated by the cache prior to resetting
2359 /// it is invalid after the reset.
2360 fn try_clear_cache(&mut self) -> Result<(), CacheError> {
2361 let c = self.dfa.get_config();
2362 if let Some(min_count) = c.get_minimum_cache_clear_count() {
2363 if self.cache.clear_count >= min_count {
2364 if let Some(min_bytes_per) = c.get_minimum_bytes_per_state() {
2365 let len = self.cache.search_total_len();
2366 let min_bytes =
2367 min_bytes_per.saturating_mul(self.cache.states.len());
2368 // If we've searched 0 bytes then probably something has
2369 // gone wrong and the lazy DFA search implementation isn't
2370 // correctly updating the search progress state.
2371 if len == 0 {
2372 trace!(
2373 "number of bytes searched is 0, but \
2374 a minimum bytes per state searched ({}) is \
2375 enabled, maybe Cache::search_update \
2376 is not being used?",
2377 min_bytes_per,
2378 );
2379 }
2380 if len < min_bytes {
2381 trace!(
2382 "lazy DFA cache has been cleared {} times, \
2383 which exceeds the limit of {}, \
2384 AND its bytes searched per state is less \
2385 than the configured minimum of {}, \
2386 therefore lazy DFA is giving up \
2387 (bytes searched since cache clear = {}, \
2388 number of states = {})",
2389 self.cache.clear_count,
2390 min_count,
2391 min_bytes_per,
2392 len,
2393 self.cache.states.len(),
2394 );
2395 return Err(CacheError::bad_efficiency());
2396 } else {
2397 trace!(
2398 "lazy DFA cache has been cleared {} times, \
2399 which exceeds the limit of {}, \
2400 AND its bytes searched per state is greater \
2401 than the configured minimum of {}, \
2402 therefore lazy DFA is continuing! \
2403 (bytes searched since cache clear = {}, \
2404 number of states = {})",
2405 self.cache.clear_count,
2406 min_count,
2407 min_bytes_per,
2408 len,
2409 self.cache.states.len(),
2410 );
2411 }
2412 } else {
2413 trace!(
2414 "lazy DFA cache has been cleared {} times, \
2415 which exceeds the limit of {}, \
2416 since there is no configured bytes per state \
2417 minimum, lazy DFA is giving up",
2418 self.cache.clear_count,
2419 min_count,
2420 );
2421 return Err(CacheError::too_many_cache_clears());
2422 }
2423 }
2424 }
2425 self.clear_cache();
2426 Ok(())
2427 }
2428
2429 /// Clears _and_ resets the cache. Resetting the cache means that no
2430 /// states are persisted and the clear count is reset to 0. No heap memory
2431 /// is released.
2432 ///
2433 /// Note that the caller may reset a cache with a different DFA than what
2434 /// it was created from. In which case, the cache can now be used with the
2435 /// new DFA (and not the old DFA).
2436 fn reset_cache(&mut self) {
2437 self.cache.state_saver = StateSaver::none();
2438 self.clear_cache();
2439 // If a new DFA is used, it might have a different number of NFA
2440 // states, so we need to make sure our sparse sets have the appropriate
2441 // size.
2442 self.cache.sparses.resize(self.dfa.get_nfa().states().len());
2443 self.cache.clear_count = 0;
2444 self.cache.progress = None;
2445 }
2446
2447 /// Clear the cache used by this lazy DFA.
2448 ///
2449 /// If 'self.state_saver' is set to save a state, then this state is
2450 /// persisted through cache clearing. Otherwise, the cache is returned to
2451 /// its state after initialization with two exceptions: its clear count
2452 /// is incremented and some of its memory likely has additional capacity.
2453 /// That is, clearing a cache does _not_ release memory.
2454 ///
2455 /// Otherwise, any lazy state ID generated by the cache prior to resetting
2456 /// it is invalid after the reset.
2457 fn clear_cache(&mut self) {
2458 self.cache.trans.clear();
2459 self.cache.starts.clear();
2460 self.cache.states.clear();
2461 self.cache.states_to_id.clear();
2462 self.cache.memory_usage_state = 0;
2463 self.cache.clear_count += 1;
2464 self.cache.bytes_searched = 0;
2465 if let Some(ref mut progress) = self.cache.progress {
2466 progress.start = progress.at;
2467 }
2468 trace!(
2469 "lazy DFA cache has been cleared (count: {})",
2470 self.cache.clear_count
2471 );
2472 self.init_cache();
2473 // If the state we want to save is one of the sentinel
2474 // (unknown/dead/quit) states, then 'init_cache' adds those back, and
2475 // their identifier values remains invariant. So there's no need to add
2476 // it again. (And indeed, doing so would be incorrect!)
2477 if let Some((old_id, state)) = self.cache.state_saver.take_to_save() {
2478 // If the state is one of the special sentinel states, then it is
2479 // automatically added by cache initialization and its ID always
2480 // remains the same. With that said, this should never occur since
2481 // the sentinel states are all loop states back to themselves. So
2482 // we should never be in a position where we're attempting to save
2483 // a sentinel state since we never compute transitions out of a
2484 // sentinel state.
2485 assert!(
2486 !self.as_ref().is_sentinel(old_id),
2487 "cannot save sentinel state"
2488 );
2489 let new_id = self
2490 .add_state(state, |id| {
2491 if old_id.is_start() {
2492 // We don't need to consult the
2493 // 'specialize_start_states' config knob here, because
2494 // if it's disabled, old_id.is_start() will never
2495 // return true.
2496 id.to_start()
2497 } else {
2498 id
2499 }
2500 })
2501 // The unwrap here is OK because lazy DFA creation ensures that
2502 // we have room in the cache to add MIN_STATES states. Since
2503 // 'init_cache' above adds 3, this adds a 4th.
2504 .expect("adding one state after cache clear must work");
2505 self.cache.state_saver = StateSaver::Saved(new_id);
2506 }
2507 }
2508
2509 /// Initialize this cache from emptiness to a place where it can be used
2510 /// for search.
2511 ///
2512 /// This is called both at cache creation time and after the cache has been
2513 /// cleared.
2514 ///
2515 /// Primarily, this adds the three sentinel states and allocates some
2516 /// initial memory.
2517 fn init_cache(&mut self) {
2518 // Why multiply by 2 here? Because we make room for both the unanchored
2519 // and anchored start states. Unanchored is first and then anchored.
2520 let mut starts_len = Start::len().checked_mul(2).unwrap();
2521 // ... but if we also want start states for every pattern, we make room
2522 // for that too.
2523 if self.dfa.get_config().get_starts_for_each_pattern() {
2524 starts_len += Start::len() * self.dfa.pattern_len();
2525 }
2526 self.cache
2527 .starts
2528 .extend(iter::repeat(self.as_ref().unknown_id()).take(starts_len));
2529 // This is the set of NFA states that corresponds to each of our three
2530 // sentinel states: the empty set.
2531 let dead = State::dead();
2532 // This sets up some states that we use as sentinels that are present
2533 // in every DFA. While it would be technically possible to implement
2534 // this DFA without explicitly putting these states in the transition
2535 // table, this is convenient to do to make `next_state` correct for all
2536 // valid state IDs without needing explicit conditionals to special
2537 // case these sentinel states.
2538 //
2539 // All three of these states are "dead" states. That is, all of
2540 // them transition only to themselves. So once you enter one of
2541 // these states, it's impossible to leave them. Thus, any correct
2542 // search routine must explicitly check for these state types. (Sans
2543 // `unknown`, since that is only used internally to represent missing
2544 // states.)
2545 let unk_id =
2546 self.add_state(dead.clone(), |id| id.to_unknown()).unwrap();
2547 let dead_id = self.add_state(dead.clone(), |id| id.to_dead()).unwrap();
2548 let quit_id = self.add_state(dead.clone(), |id| id.to_quit()).unwrap();
2549 assert_eq!(unk_id, self.as_ref().unknown_id());
2550 assert_eq!(dead_id, self.as_ref().dead_id());
2551 assert_eq!(quit_id, self.as_ref().quit_id());
2552 // The idea here is that if you start in an unknown/dead/quit state and
2553 // try to transition on them, then you should end up where you started.
2554 self.set_all_transitions(unk_id, unk_id);
2555 self.set_all_transitions(dead_id, dead_id);
2556 self.set_all_transitions(quit_id, quit_id);
2557 // All of these states are technically equivalent from the FSM
2558 // perspective, so putting all three of them in the cache isn't
2559 // possible. (They are distinct merely because we use their
2560 // identifiers as sentinels to mean something, as indicated by the
2561 // names.) Moreover, we wouldn't want to do that. Unknown and quit
2562 // states are special in that they are artificial constructions
2563 // this implementation. But dead states are a natural part of
2564 // determinization. When you reach a point in the NFA where you cannot
2565 // go anywhere else, a dead state will naturally arise and we MUST
2566 // reuse the canonical dead state that we've created here. Why? Because
2567 // it is the state ID that tells the search routine whether a state is
2568 // dead or not, and thus, whether to stop the search. Having a bunch of
2569 // distinct dead states would be quite wasteful!
2570 self.cache.states_to_id.insert(dead, dead_id);
2571 }
2572
2573 /// Save the state corresponding to the ID given such that the state
2574 /// persists through a cache clearing.
2575 ///
2576 /// While the state may persist, the ID may not. In order to discover the
2577 /// new state ID, one must call 'saved_state_id' after a cache clearing.
2578 fn save_state(&mut self, id: LazyStateID) {
2579 let state = self.as_ref().get_cached_state(id).clone();
2580 self.cache.state_saver = StateSaver::ToSave { id, state };
2581 }
2582
2583 /// Returns the updated lazy state ID for a state that was persisted
2584 /// through a cache clearing.
2585 ///
2586 /// It is only correct to call this routine when both a state has been
2587 /// saved and the cache has just been cleared. Otherwise, this panics.
2588 fn saved_state_id(&mut self) -> LazyStateID {
2589 self.cache
2590 .state_saver
2591 .take_saved()
2592 .expect("state saver does not have saved state ID")
2593 }
2594
2595 /// Set all transitions on the state 'from' to 'to'.
2596 fn set_all_transitions(&mut self, from: LazyStateID, to: LazyStateID) {
2597 for unit in self.dfa.classes.representatives(..) {
2598 self.set_transition(from, unit, to);
2599 }
2600 }
2601
2602 /// Set the transition on 'from' for 'unit' to 'to'.
2603 ///
2604 /// This panics if either 'from' or 'to' is invalid.
2605 ///
2606 /// All unit values are OK.
2607 fn set_transition(
2608 &mut self,
2609 from: LazyStateID,
2610 unit: alphabet::Unit,
2611 to: LazyStateID,
2612 ) {
2613 assert!(self.as_ref().is_valid(from), "invalid 'from' id: {from:?}");
2614 assert!(self.as_ref().is_valid(to), "invalid 'to' id: {to:?}");
2615 let offset =
2616 from.as_usize_untagged() + self.dfa.classes.get_by_unit(unit);
2617 self.cache.trans[offset] = to;
2618 }
2619
2620 /// Set the start ID for the given pattern ID (if given) and starting
2621 /// configuration to the ID given.
2622 ///
2623 /// This panics if 'id' is not valid or if a pattern ID is given and
2624 /// 'starts_for_each_pattern' is not enabled.
2625 fn set_start_state(
2626 &mut self,
2627 anchored: Anchored,
2628 start: Start,
2629 id: LazyStateID,
2630 ) {
2631 assert!(self.as_ref().is_valid(id));
2632 let start_index = start.as_usize();
2633 let index = match anchored {
2634 Anchored::No => start_index,
2635 Anchored::Yes => Start::len() + start_index,
2636 Anchored::Pattern(pid) => {
2637 assert!(
2638 self.dfa.get_config().get_starts_for_each_pattern(),
2639 "attempted to search for a specific pattern \
2640 without enabling starts_for_each_pattern",
2641 );
2642 let pid = pid.as_usize();
2643 (2 * Start::len()) + (Start::len() * pid) + start_index
2644 }
2645 };
2646 self.cache.starts[index] = id;
2647 }
2648
2649 /// Returns a state builder from this DFA that might have existing
2650 /// capacity. This helps avoid allocs in cases where a state is built that
2651 /// turns out to already be cached.
2652 ///
2653 /// Callers must put the state builder back with 'put_state_builder',
2654 /// otherwise the allocation reuse won't work.
2655 fn get_state_builder(&mut self) -> StateBuilderEmpty {
2656 core::mem::replace(
2657 &mut self.cache.scratch_state_builder,
2658 StateBuilderEmpty::new(),
2659 )
2660 }
2661
2662 /// Puts the given state builder back into this DFA for reuse.
2663 ///
2664 /// Note that building a 'State' from a builder always creates a new alloc,
2665 /// so callers should always put the builder back.
2666 fn put_state_builder(&mut self, builder: StateBuilderNFA) {
2667 let _ = core::mem::replace(
2668 &mut self.cache.scratch_state_builder,
2669 builder.clear(),
2670 );
2671 }
2672}
2673
2674/// A type that groups methods that require the base NFA/DFA and read-only
2675/// access to the cache.
2676#[derive(Debug)]
2677struct LazyRef<'i, 'c> {
2678 dfa: &'i DFA,
2679 cache: &'c Cache,
2680}
2681
2682impl<'i, 'c> LazyRef<'i, 'c> {
2683 /// Creates a new 'Lazy' wrapper for a DFA and its corresponding cache.
2684 fn new(dfa: &'i DFA, cache: &'c Cache) -> LazyRef<'i, 'c> {
2685 LazyRef { dfa, cache }
2686 }
2687
2688 /// Return the ID of the start state for the given configuration.
2689 ///
2690 /// If the start state has not yet been computed, then this returns an
2691 /// unknown lazy state ID.
2692 #[cfg_attr(feature = "perf-inline", inline(always))]
2693 fn get_cached_start_id(
2694 &self,
2695 anchored: Anchored,
2696 start: Start,
2697 ) -> Result<LazyStateID, StartError> {
2698 let start_index = start.as_usize();
2699 let index = match anchored {
2700 Anchored::No => start_index,
2701 Anchored::Yes => Start::len() + start_index,
2702 Anchored::Pattern(pid) => {
2703 if !self.dfa.get_config().get_starts_for_each_pattern() {
2704 return Err(StartError::unsupported_anchored(anchored));
2705 }
2706 if pid.as_usize() >= self.dfa.pattern_len() {
2707 return Ok(self.dead_id());
2708 }
2709 (2 * Start::len())
2710 + (Start::len() * pid.as_usize())
2711 + start_index
2712 }
2713 };
2714 Ok(self.cache.starts[index])
2715 }
2716
2717 /// Return the cached NFA/DFA powerset state for the given ID.
2718 ///
2719 /// This panics if the given ID does not address a valid state.
2720 fn get_cached_state(&self, sid: LazyStateID) -> &State {
2721 let index = sid.as_usize_untagged() >> self.dfa.stride2();
2722 &self.cache.states[index]
2723 }
2724
2725 /// Returns true if and only if the given ID corresponds to a "sentinel"
2726 /// state.
2727 ///
2728 /// A sentinel state is a state that signifies a special condition of
2729 /// search, and where every transition maps back to itself. See LazyStateID
2730 /// for more details. Note that start and match states are _not_ sentinels
2731 /// since they may otherwise be real states with non-trivial transitions.
2732 /// The purposes of sentinel states is purely to indicate something. Their
2733 /// transitions are not meant to be followed.
2734 fn is_sentinel(&self, id: LazyStateID) -> bool {
2735 id == self.unknown_id() || id == self.dead_id() || id == self.quit_id()
2736 }
2737
2738 /// Returns the ID of the unknown state for this lazy DFA.
2739 fn unknown_id(&self) -> LazyStateID {
2740 // This unwrap is OK since 0 is always a valid state ID.
2741 LazyStateID::new(0).unwrap().to_unknown()
2742 }
2743
2744 /// Returns the ID of the dead state for this lazy DFA.
2745 fn dead_id(&self) -> LazyStateID {
2746 // This unwrap is OK since the maximum value here is 1 * 512 = 512,
2747 // which is <= 2047 (the maximum state ID on 16-bit systems). Where
2748 // 512 is the worst case for our equivalence classes (every byte is a
2749 // distinct class).
2750 LazyStateID::new(1 << self.dfa.stride2()).unwrap().to_dead()
2751 }
2752
2753 /// Returns the ID of the quit state for this lazy DFA.
2754 fn quit_id(&self) -> LazyStateID {
2755 // This unwrap is OK since the maximum value here is 2 * 512 = 1024,
2756 // which is <= 2047 (the maximum state ID on 16-bit systems). Where
2757 // 512 is the worst case for our equivalence classes (every byte is a
2758 // distinct class).
2759 LazyStateID::new(2 << self.dfa.stride2()).unwrap().to_quit()
2760 }
2761
2762 /// Returns true if and only if the given ID is valid.
2763 ///
2764 /// An ID is valid if it is both a valid index into the transition table
2765 /// and is a multiple of the DFA's stride.
2766 fn is_valid(&self, id: LazyStateID) -> bool {
2767 let id = id.as_usize_untagged();
2768 id < self.cache.trans.len() && id % self.dfa.stride() == 0
2769 }
2770
2771 /// Returns true if adding the state given would fit in this cache.
2772 fn state_fits_in_cache(&self, state: &State) -> bool {
2773 let needed = self.cache.memory_usage()
2774 + self.memory_usage_for_one_more_state(state.memory_usage());
2775 trace!(
2776 "lazy DFA cache capacity state check: {:?} ?<=? {:?}",
2777 needed,
2778 self.dfa.cache_capacity
2779 );
2780 needed <= self.dfa.cache_capacity
2781 }
2782
2783 /// Returns true if adding the state to be built by the given builder would
2784 /// fit in this cache.
2785 fn state_builder_fits_in_cache(&self, state: &StateBuilderNFA) -> bool {
2786 let needed = self.cache.memory_usage()
2787 + self.memory_usage_for_one_more_state(state.as_bytes().len());
2788 trace!(
2789 "lazy DFA cache capacity state builder check: {:?} ?<=? {:?}",
2790 needed,
2791 self.dfa.cache_capacity
2792 );
2793 needed <= self.dfa.cache_capacity
2794 }
2795
2796 /// Returns the additional memory usage, in bytes, required to add one more
2797 /// state to this cache. The given size should be the heap size, in bytes,
2798 /// that would be used by the new state being added.
2799 fn memory_usage_for_one_more_state(
2800 &self,
2801 state_heap_size: usize,
2802 ) -> usize {
2803 const ID_SIZE: usize = size_of::<LazyStateID>();
2804 const STATE_SIZE: usize = size_of::<State>();
2805
2806 self.dfa.stride() * ID_SIZE // additional space needed in trans table
2807 + STATE_SIZE // space in cache.states
2808 + (STATE_SIZE + ID_SIZE) // space in cache.states_to_id
2809 + state_heap_size // heap memory used by state itself
2810 }
2811}
2812
2813/// A simple type that encapsulates the saving of a state ID through a cache
2814/// clearing.
2815///
2816/// A state ID can be marked for saving with ToSave, while a state ID can be
2817/// saved itself with Saved.
2818#[derive(Clone, Debug)]
2819enum StateSaver {
2820 /// An empty state saver. In this case, no states (other than the special
2821 /// sentinel states) are preserved after clearing the cache.
2822 None,
2823 /// An ID of a state (and the state itself) that should be preserved after
2824 /// the lazy DFA's cache has been cleared. After clearing, the updated ID
2825 /// is stored in 'Saved' since it may have changed.
2826 ToSave { id: LazyStateID, state: State },
2827 /// An ID that of a state that has been persisted through a lazy DFA
2828 /// cache clearing. The ID recorded here corresponds to an ID that was
2829 /// once marked as ToSave. The IDs are likely not equivalent even though
2830 /// the states they point to are.
2831 Saved(LazyStateID),
2832}
2833
2834impl StateSaver {
2835 /// Create an empty state saver.
2836 fn none() -> StateSaver {
2837 StateSaver::None
2838 }
2839
2840 /// Replace this state saver with an empty saver, and if this saver is a
2841 /// request to save a state, return that request.
2842 fn take_to_save(&mut self) -> Option<(LazyStateID, State)> {
2843 match core::mem::replace(self, StateSaver::None) {
2844 StateSaver::None | StateSaver::Saved(_) => None,
2845 StateSaver::ToSave { id, state } => Some((id, state)),
2846 }
2847 }
2848
2849 /// Replace this state saver with an empty saver, and if this saver is a
2850 /// saved state (or a request to save a state), return that state's ID.
2851 ///
2852 /// The idea here is that a request to save a state isn't necessarily
2853 /// honored because it might not be needed. e.g., Some higher level code
2854 /// might request a state to be saved on the off chance that the cache gets
2855 /// cleared when a new state is added at a lower level. But if that new
2856 /// state is never added, then the cache is never cleared and the state and
2857 /// its ID remain unchanged.
2858 fn take_saved(&mut self) -> Option<LazyStateID> {
2859 match core::mem::replace(self, StateSaver::None) {
2860 StateSaver::None => None,
2861 StateSaver::Saved(id) | StateSaver::ToSave { id, .. } => Some(id),
2862 }
2863 }
2864}
2865
2866/// The configuration used for building a lazy DFA.
2867///
2868/// As a convenience, [`DFA::config`] is an alias for [`Config::new`]. The
2869/// advantage of the former is that it often lets you avoid importing the
2870/// `Config` type directly.
2871///
2872/// A lazy DFA configuration is a simple data object that is typically used
2873/// with [`Builder::configure`].
2874///
2875/// The default configuration guarantees that a search will never return a
2876/// "gave up" or "quit" error, although it is possible for a search to fail
2877/// if [`Config::starts_for_each_pattern`] wasn't enabled (which it is not by
2878/// default) and an [`Anchored::Pattern`] mode is requested via [`Input`].
2879#[derive(Clone, Debug, Default)]
2880pub struct Config {
2881 // As with other configuration types in this crate, we put all our knobs
2882 // in options so that we can distinguish between "default" and "not set."
2883 // This makes it possible to easily combine multiple configurations
2884 // without default values overwriting explicitly specified values. See the
2885 // 'overwrite' method.
2886 //
2887 // For docs on the fields below, see the corresponding method setters.
2888 match_kind: Option<MatchKind>,
2889 pre: Option<Option<Prefilter>>,
2890 starts_for_each_pattern: Option<bool>,
2891 byte_classes: Option<bool>,
2892 unicode_word_boundary: Option<bool>,
2893 quitset: Option<ByteSet>,
2894 specialize_start_states: Option<bool>,
2895 cache_capacity: Option<usize>,
2896 skip_cache_capacity_check: Option<bool>,
2897 minimum_cache_clear_count: Option<Option<usize>>,
2898 minimum_bytes_per_state: Option<Option<usize>>,
2899}
2900
2901impl Config {
2902 /// Return a new default lazy DFA builder configuration.
2903 pub fn new() -> Config {
2904 Config::default()
2905 }
2906
2907 /// Set the desired match semantics.
2908 ///
2909 /// The default is [`MatchKind::LeftmostFirst`], which corresponds to the
2910 /// match semantics of Perl-like regex engines. That is, when multiple
2911 /// patterns would match at the same leftmost position, the pattern that
2912 /// appears first in the concrete syntax is chosen.
2913 ///
2914 /// Currently, the only other kind of match semantics supported is
2915 /// [`MatchKind::All`]. This corresponds to classical DFA construction
2916 /// where all possible matches are added to the lazy DFA.
2917 ///
2918 /// Typically, `All` is used when one wants to execute an overlapping
2919 /// search and `LeftmostFirst` otherwise. In particular, it rarely makes
2920 /// sense to use `All` with the various "leftmost" find routines, since the
2921 /// leftmost routines depend on the `LeftmostFirst` automata construction
2922 /// strategy. Specifically, `LeftmostFirst` adds dead states to the
2923 /// lazy DFA as a way to terminate the search and report a match.
2924 /// `LeftmostFirst` also supports non-greedy matches using this strategy
2925 /// where as `All` does not.
2926 ///
2927 /// # Example: overlapping search
2928 ///
2929 /// This example shows the typical use of `MatchKind::All`, which is to
2930 /// report overlapping matches.
2931 ///
2932 /// ```
2933 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
2934 /// use regex_automata::{
2935 /// hybrid::dfa::{DFA, OverlappingState},
2936 /// HalfMatch, Input, MatchKind,
2937 /// };
2938 ///
2939 /// let dfa = DFA::builder()
2940 /// .configure(DFA::config().match_kind(MatchKind::All))
2941 /// .build_many(&[r"\w+$", r"\S+$"])?;
2942 /// let mut cache = dfa.create_cache();
2943 /// let haystack = "@foo";
2944 /// let mut state = OverlappingState::start();
2945 ///
2946 /// let expected = Some(HalfMatch::must(1, 4));
2947 /// dfa.try_search_overlapping_fwd(
2948 /// &mut cache, &Input::new(haystack), &mut state,
2949 /// )?;
2950 /// assert_eq!(expected, state.get_match());
2951 ///
2952 /// // The first pattern also matches at the same position, so re-running
2953 /// // the search will yield another match. Notice also that the first
2954 /// // pattern is returned after the second. This is because the second
2955 /// // pattern begins its match before the first, is therefore an earlier
2956 /// // match and is thus reported first.
2957 /// let expected = Some(HalfMatch::must(0, 4));
2958 /// dfa.try_search_overlapping_fwd(
2959 /// &mut cache, &Input::new(haystack), &mut state,
2960 /// )?;
2961 /// assert_eq!(expected, state.get_match());
2962 ///
2963 /// # Ok::<(), Box<dyn std::error::Error>>(())
2964 /// ```
2965 ///
2966 /// # Example: reverse automaton to find start of match
2967 ///
2968 /// Another example for using `MatchKind::All` is for constructing a
2969 /// reverse automaton to find the start of a match. `All` semantics are
2970 /// used for this in order to find the longest possible match, which
2971 /// corresponds to the leftmost starting position.
2972 ///
2973 /// Note that if you need the starting position then
2974 /// [`hybrid::regex::Regex`](crate::hybrid::regex::Regex) will handle this
2975 /// for you, so it's usually not necessary to do this yourself.
2976 ///
2977 /// ```
2978 /// use regex_automata::{
2979 /// hybrid::dfa::DFA,
2980 /// nfa::thompson::NFA,
2981 /// Anchored, HalfMatch, Input, MatchKind,
2982 /// };
2983 ///
2984 /// let input = Input::new("123foobar456");
2985 /// let pattern = r"[a-z]+r";
2986 ///
2987 /// let dfa_fwd = DFA::new(pattern)?;
2988 /// let dfa_rev = DFA::builder()
2989 /// .thompson(NFA::config().reverse(true))
2990 /// .configure(DFA::config().match_kind(MatchKind::All))
2991 /// .build(pattern)?;
2992 /// let mut cache_fwd = dfa_fwd.create_cache();
2993 /// let mut cache_rev = dfa_rev.create_cache();
2994 ///
2995 /// let expected_fwd = HalfMatch::must(0, 9);
2996 /// let expected_rev = HalfMatch::must(0, 3);
2997 /// let got_fwd = dfa_fwd.try_search_fwd(&mut cache_fwd, &input)?.unwrap();
2998 /// // Here we don't specify the pattern to search for since there's only
2999 /// // one pattern and we're doing a leftmost search. But if this were an
3000 /// // overlapping search, you'd need to specify the pattern that matched
3001 /// // in the forward direction. (Otherwise, you might wind up finding the
3002 /// // starting position of a match of some other pattern.) That in turn
3003 /// // requires building the reverse automaton with starts_for_each_pattern
3004 /// // enabled.
3005 /// let input = input
3006 /// .clone()
3007 /// .range(..got_fwd.offset())
3008 /// .anchored(Anchored::Yes);
3009 /// let got_rev = dfa_rev.try_search_rev(&mut cache_rev, &input)?.unwrap();
3010 /// assert_eq!(expected_fwd, got_fwd);
3011 /// assert_eq!(expected_rev, got_rev);
3012 ///
3013 /// # Ok::<(), Box<dyn std::error::Error>>(())
3014 /// ```
3015 pub fn match_kind(mut self, kind: MatchKind) -> Config {
3016 self.match_kind = Some(kind);
3017 self
3018 }
3019
3020 /// Set a prefilter to be used whenever a start state is entered.
3021 ///
3022 /// A [`Prefilter`] in this context is meant to accelerate searches by
3023 /// looking for literal prefixes that every match for the corresponding
3024 /// pattern (or patterns) must start with. Once a prefilter produces a
3025 /// match, the underlying search routine continues on to try and confirm
3026 /// the match.
3027 ///
3028 /// Be warned that setting a prefilter does not guarantee that the search
3029 /// will be faster. While it's usually a good bet, if the prefilter
3030 /// produces a lot of false positive candidates (i.e., positions matched
3031 /// by the prefilter but not by the regex), then the overall result can
3032 /// be slower than if you had just executed the regex engine without any
3033 /// prefilters.
3034 ///
3035 /// Note that unless [`Config::specialize_start_states`] has been
3036 /// explicitly set, then setting this will also enable (when `pre` is
3037 /// `Some`) or disable (when `pre` is `None`) start state specialization.
3038 /// This occurs because without start state specialization, a prefilter
3039 /// is likely to be less effective. And without a prefilter, start state
3040 /// specialization is usually pointless.
3041 ///
3042 /// By default no prefilter is set.
3043 ///
3044 /// # Example
3045 ///
3046 /// ```
3047 /// use regex_automata::{
3048 /// hybrid::dfa::DFA,
3049 /// util::prefilter::Prefilter,
3050 /// Input, HalfMatch, MatchKind,
3051 /// };
3052 ///
3053 /// let pre = Prefilter::new(MatchKind::LeftmostFirst, &["foo", "bar"]);
3054 /// let re = DFA::builder()
3055 /// .configure(DFA::config().prefilter(pre))
3056 /// .build(r"(foo|bar)[a-z]+")?;
3057 /// let mut cache = re.create_cache();
3058 /// let input = Input::new("foo1 barfox bar");
3059 /// assert_eq!(
3060 /// Some(HalfMatch::must(0, 11)),
3061 /// re.try_search_fwd(&mut cache, &input)?,
3062 /// );
3063 ///
3064 /// # Ok::<(), Box<dyn std::error::Error>>(())
3065 /// ```
3066 ///
3067 /// Be warned though that an incorrect prefilter can lead to incorrect
3068 /// results!
3069 ///
3070 /// ```
3071 /// use regex_automata::{
3072 /// hybrid::dfa::DFA,
3073 /// util::prefilter::Prefilter,
3074 /// Input, HalfMatch, MatchKind,
3075 /// };
3076 ///
3077 /// let pre = Prefilter::new(MatchKind::LeftmostFirst, &["foo", "car"]);
3078 /// let re = DFA::builder()
3079 /// .configure(DFA::config().prefilter(pre))
3080 /// .build(r"(foo|bar)[a-z]+")?;
3081 /// let mut cache = re.create_cache();
3082 /// let input = Input::new("foo1 barfox bar");
3083 /// assert_eq!(
3084 /// // No match reported even though there clearly is one!
3085 /// None,
3086 /// re.try_search_fwd(&mut cache, &input)?,
3087 /// );
3088 ///
3089 /// # Ok::<(), Box<dyn std::error::Error>>(())
3090 /// ```
3091 pub fn prefilter(mut self, pre: Option<Prefilter>) -> Config {
3092 self.pre = Some(pre);
3093 if self.specialize_start_states.is_none() {
3094 self.specialize_start_states =
3095 Some(self.get_prefilter().is_some());
3096 }
3097 self
3098 }
3099
3100 /// Whether to compile a separate start state for each pattern in the
3101 /// lazy DFA.
3102 ///
3103 /// When enabled, a separate **anchored** start state is added for each
3104 /// pattern in the lazy DFA. When this start state is used, then the DFA
3105 /// will only search for matches for the pattern specified, even if there
3106 /// are other patterns in the DFA.
3107 ///
3108 /// The main downside of this option is that it can potentially increase
3109 /// the size of the DFA and/or increase the time it takes to build the
3110 /// DFA at search time. However, since this is configuration for a lazy
3111 /// DFA, these states aren't actually built unless they're used. Enabling
3112 /// this isn't necessarily free, however, as it may result in higher cache
3113 /// usage.
3114 ///
3115 /// There are a few reasons one might want to enable this (it's disabled
3116 /// by default):
3117 ///
3118 /// 1. When looking for the start of an overlapping match (using a reverse
3119 /// DFA), doing it correctly requires starting the reverse search using the
3120 /// starting state of the pattern that matched in the forward direction.
3121 /// Indeed, when building a [`Regex`](crate::hybrid::regex::Regex), it
3122 /// will automatically enable this option when building the reverse DFA
3123 /// internally.
3124 /// 2. When you want to use a DFA with multiple patterns to both search
3125 /// for matches of any pattern or to search for anchored matches of one
3126 /// particular pattern while using the same DFA. (Otherwise, you would need
3127 /// to compile a new DFA for each pattern.)
3128 ///
3129 /// By default this is disabled.
3130 ///
3131 /// # Example
3132 ///
3133 /// This example shows how to use this option to permit the same lazy DFA
3134 /// to run both general searches for any pattern and anchored searches for
3135 /// a specific pattern.
3136 ///
3137 /// ```
3138 /// use regex_automata::{
3139 /// hybrid::dfa::DFA,
3140 /// Anchored, HalfMatch, Input, PatternID,
3141 /// };
3142 ///
3143 /// let dfa = DFA::builder()
3144 /// .configure(DFA::config().starts_for_each_pattern(true))
3145 /// .build_many(&[r"[a-z0-9]{6}", r"[a-z][a-z0-9]{5}"])?;
3146 /// let mut cache = dfa.create_cache();
3147 /// let haystack = "bar foo123";
3148 ///
3149 /// // Here's a normal unanchored search that looks for any pattern.
3150 /// let expected = HalfMatch::must(0, 10);
3151 /// let input = Input::new(haystack);
3152 /// assert_eq!(Some(expected), dfa.try_search_fwd(&mut cache, &input)?);
3153 /// // We can also do a normal anchored search for any pattern. Since it's
3154 /// // an anchored search, we position the start of the search where we
3155 /// // know the match will begin.
3156 /// let expected = HalfMatch::must(0, 10);
3157 /// let input = Input::new(haystack).range(4..);
3158 /// assert_eq!(Some(expected), dfa.try_search_fwd(&mut cache, &input)?);
3159 /// // Since we compiled anchored start states for each pattern, we can
3160 /// // also look for matches of other patterns explicitly, even if a
3161 /// // different pattern would have normally matched.
3162 /// let expected = HalfMatch::must(1, 10);
3163 /// let input = Input::new(haystack)
3164 /// .range(4..)
3165 /// .anchored(Anchored::Pattern(PatternID::must(1)));
3166 /// assert_eq!(Some(expected), dfa.try_search_fwd(&mut cache, &input)?);
3167 ///
3168 /// # Ok::<(), Box<dyn std::error::Error>>(())
3169 /// ```
3170 pub fn starts_for_each_pattern(mut self, yes: bool) -> Config {
3171 self.starts_for_each_pattern = Some(yes);
3172 self
3173 }
3174
3175 /// Whether to attempt to shrink the size of the lazy DFA's alphabet or
3176 /// not.
3177 ///
3178 /// This option is enabled by default and should never be disabled unless
3179 /// one is debugging the lazy DFA.
3180 ///
3181 /// When enabled, the lazy DFA will use a map from all possible bytes
3182 /// to their corresponding equivalence class. Each equivalence class
3183 /// represents a set of bytes that does not discriminate between a match
3184 /// and a non-match in the DFA. For example, the pattern `[ab]+` has at
3185 /// least two equivalence classes: a set containing `a` and `b` and a set
3186 /// containing every byte except for `a` and `b`. `a` and `b` are in the
3187 /// same equivalence classes because they never discriminate between a
3188 /// match and a non-match.
3189 ///
3190 /// The advantage of this map is that the size of the transition table
3191 /// can be reduced drastically from `#states * 256 * sizeof(LazyStateID)`
3192 /// to `#states * k * sizeof(LazyStateID)` where `k` is the number of
3193 /// equivalence classes (rounded up to the nearest power of 2). As a
3194 /// result, total space usage can decrease substantially. Moreover, since a
3195 /// smaller alphabet is used, DFA compilation during search becomes faster
3196 /// as well since it will potentially be able to reuse a single transition
3197 /// for multiple bytes.
3198 ///
3199 /// **WARNING:** This is only useful for debugging lazy DFAs. Disabling
3200 /// this does not yield any speed advantages. Namely, even when this is
3201 /// disabled, a byte class map is still used while searching. The only
3202 /// difference is that every byte will be forced into its own distinct
3203 /// equivalence class. This is useful for debugging the actual generated
3204 /// transitions because it lets one see the transitions defined on actual
3205 /// bytes instead of the equivalence classes.
3206 pub fn byte_classes(mut self, yes: bool) -> Config {
3207 self.byte_classes = Some(yes);
3208 self
3209 }
3210
3211 /// Heuristically enable Unicode word boundaries.
3212 ///
3213 /// When set, this will attempt to implement Unicode word boundaries as if
3214 /// they were ASCII word boundaries. This only works when the search input
3215 /// is ASCII only. If a non-ASCII byte is observed while searching, then a
3216 /// [`MatchError::quit`] error is returned.
3217 ///
3218 /// A possible alternative to enabling this option is to simply use an
3219 /// ASCII word boundary, e.g., via `(?-u:\b)`. The main reason to use this
3220 /// option is if you absolutely need Unicode support. This option lets one
3221 /// use a fast search implementation (a DFA) for some potentially very
3222 /// common cases, while providing the option to fall back to some other
3223 /// regex engine to handle the general case when an error is returned.
3224 ///
3225 /// If the pattern provided has no Unicode word boundary in it, then this
3226 /// option has no effect. (That is, quitting on a non-ASCII byte only
3227 /// occurs when this option is enabled _and_ a Unicode word boundary is
3228 /// present in the pattern.)
3229 ///
3230 /// This is almost equivalent to setting all non-ASCII bytes to be quit
3231 /// bytes. The only difference is that this will cause non-ASCII bytes to
3232 /// be quit bytes _only_ when a Unicode word boundary is present in the
3233 /// pattern.
3234 ///
3235 /// When enabling this option, callers _must_ be prepared to
3236 /// handle a [`MatchError`] error during search. When using a
3237 /// [`Regex`](crate::hybrid::regex::Regex), this corresponds to using the
3238 /// `try_` suite of methods. Alternatively, if callers can guarantee that
3239 /// their input is ASCII only, then a [`MatchError::quit`] error will never
3240 /// be returned while searching.
3241 ///
3242 /// This is disabled by default.
3243 ///
3244 /// # Example
3245 ///
3246 /// This example shows how to heuristically enable Unicode word boundaries
3247 /// in a pattern. It also shows what happens when a search comes across a
3248 /// non-ASCII byte.
3249 ///
3250 /// ```
3251 /// use regex_automata::{
3252 /// hybrid::dfa::DFA,
3253 /// HalfMatch, Input, MatchError,
3254 /// };
3255 ///
3256 /// let dfa = DFA::builder()
3257 /// .configure(DFA::config().unicode_word_boundary(true))
3258 /// .build(r"\b[0-9]+\b")?;
3259 /// let mut cache = dfa.create_cache();
3260 ///
3261 /// // The match occurs before the search ever observes the snowman
3262 /// // character, so no error occurs.
3263 /// let haystack = "foo 123 ☃";
3264 /// let expected = Some(HalfMatch::must(0, 7));
3265 /// let got = dfa.try_search_fwd(&mut cache, &Input::new(haystack))?;
3266 /// assert_eq!(expected, got);
3267 ///
3268 /// // Notice that this search fails, even though the snowman character
3269 /// // occurs after the ending match offset. This is because search
3270 /// // routines read one byte past the end of the search to account for
3271 /// // look-around, and indeed, this is required here to determine whether
3272 /// // the trailing \b matches.
3273 /// let haystack = "foo 123 ☃";
3274 /// let expected = MatchError::quit(0xE2, 8);
3275 /// let got = dfa.try_search_fwd(&mut cache, &Input::new(haystack));
3276 /// assert_eq!(Err(expected), got);
3277 ///
3278 /// // Another example is executing a search where the span of the haystack
3279 /// // we specify is all ASCII, but there is non-ASCII just before it. This
3280 /// // correctly also reports an error.
3281 /// let input = Input::new("β123").range(2..);
3282 /// let expected = MatchError::quit(0xB2, 1);
3283 /// let got = dfa.try_search_fwd(&mut cache, &input);
3284 /// assert_eq!(Err(expected), got);
3285 ///
3286 /// // And similarly for the trailing word boundary.
3287 /// let input = Input::new("123β").range(..3);
3288 /// let expected = MatchError::quit(0xCE, 3);
3289 /// let got = dfa.try_search_fwd(&mut cache, &input);
3290 /// assert_eq!(Err(expected), got);
3291 ///
3292 /// # Ok::<(), Box<dyn std::error::Error>>(())
3293 /// ```
3294 pub fn unicode_word_boundary(mut self, yes: bool) -> Config {
3295 // We have a separate option for this instead of just setting the
3296 // appropriate quit bytes here because we don't want to set quit bytes
3297 // for every regex. We only want to set them when the regex contains a
3298 // Unicode word boundary.
3299 self.unicode_word_boundary = Some(yes);
3300 self
3301 }
3302
3303 /// Add a "quit" byte to the lazy DFA.
3304 ///
3305 /// When a quit byte is seen during search time, then search will return a
3306 /// [`MatchError::quit`] error indicating the offset at which the search
3307 /// stopped.
3308 ///
3309 /// A quit byte will always overrule any other aspects of a regex. For
3310 /// example, if the `x` byte is added as a quit byte and the regex `\w` is
3311 /// used, then observing `x` will cause the search to quit immediately
3312 /// despite the fact that `x` is in the `\w` class.
3313 ///
3314 /// This mechanism is primarily useful for heuristically enabling certain
3315 /// features like Unicode word boundaries in a DFA. Namely, if the input
3316 /// to search is ASCII, then a Unicode word boundary can be implemented
3317 /// via an ASCII word boundary with no change in semantics. Thus, a DFA
3318 /// can attempt to match a Unicode word boundary but give up as soon as it
3319 /// observes a non-ASCII byte. Indeed, if callers set all non-ASCII bytes
3320 /// to be quit bytes, then Unicode word boundaries will be permitted when
3321 /// building lazy DFAs. Of course, callers should enable
3322 /// [`Config::unicode_word_boundary`] if they want this behavior instead.
3323 /// (The advantage being that non-ASCII quit bytes will only be added if a
3324 /// Unicode word boundary is in the pattern.)
3325 ///
3326 /// When enabling this option, callers _must_ be prepared to
3327 /// handle a [`MatchError`] error during search. When using a
3328 /// [`Regex`](crate::hybrid::regex::Regex), this corresponds to using the
3329 /// `try_` suite of methods.
3330 ///
3331 /// By default, there are no quit bytes set.
3332 ///
3333 /// # Panics
3334 ///
3335 /// This panics if heuristic Unicode word boundaries are enabled and any
3336 /// non-ASCII byte is removed from the set of quit bytes. Namely, enabling
3337 /// Unicode word boundaries requires setting every non-ASCII byte to a quit
3338 /// byte. So if the caller attempts to undo any of that, then this will
3339 /// panic.
3340 ///
3341 /// # Example
3342 ///
3343 /// This example shows how to cause a search to terminate if it sees a
3344 /// `\n` byte. This could be useful if, for example, you wanted to prevent
3345 /// a user supplied pattern from matching across a line boundary.
3346 ///
3347 /// ```
3348 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
3349 /// use regex_automata::{hybrid::dfa::DFA, MatchError, Input};
3350 ///
3351 /// let dfa = DFA::builder()
3352 /// .configure(DFA::config().quit(b'\n', true))
3353 /// .build(r"foo\p{any}+bar")?;
3354 /// let mut cache = dfa.create_cache();
3355 ///
3356 /// let haystack = "foo\nbar";
3357 /// // Normally this would produce a match, since \p{any} contains '\n'.
3358 /// // But since we instructed the automaton to enter a quit state if a
3359 /// // '\n' is observed, this produces a match error instead.
3360 /// let expected = MatchError::quit(b'\n', 3);
3361 /// let got = dfa.try_search_fwd(
3362 /// &mut cache,
3363 /// &Input::new(haystack),
3364 /// ).unwrap_err();
3365 /// assert_eq!(expected, got);
3366 ///
3367 /// # Ok::<(), Box<dyn std::error::Error>>(())
3368 /// ```
3369 pub fn quit(mut self, byte: u8, yes: bool) -> Config {
3370 if self.get_unicode_word_boundary() && !byte.is_ascii() && !yes {
3371 panic!(
3372 "cannot set non-ASCII byte to be non-quit when \
3373 Unicode word boundaries are enabled"
3374 );
3375 }
3376 if self.quitset.is_none() {
3377 self.quitset = Some(ByteSet::empty());
3378 }
3379 if yes {
3380 self.quitset.as_mut().unwrap().add(byte);
3381 } else {
3382 self.quitset.as_mut().unwrap().remove(byte);
3383 }
3384 self
3385 }
3386
3387 /// Enable specializing start states in the lazy DFA.
3388 ///
3389 /// When start states are specialized, an implementor of a search routine
3390 /// using a lazy DFA can tell when the search has entered a starting state.
3391 /// When start states aren't specialized, then it is impossible to know
3392 /// whether the search has entered a start state.
3393 ///
3394 /// Ideally, this option wouldn't need to exist and we could always
3395 /// specialize start states. The problem is that start states can be quite
3396 /// active. This in turn means that an efficient search routine is likely
3397 /// to ping-pong between a heavily optimized hot loop that handles most
3398 /// states and to a less optimized specialized handling of start states.
3399 /// This causes branches to get heavily mispredicted and overall can
3400 /// materially decrease throughput. Therefore, specializing start states
3401 /// should only be enabled when it is needed.
3402 ///
3403 /// Knowing whether a search is in a start state is typically useful when a
3404 /// prefilter is active for the search. A prefilter is typically only run
3405 /// when in a start state and a prefilter can greatly accelerate a search.
3406 /// Therefore, the possible cost of specializing start states is worth it
3407 /// in this case. Otherwise, if you have no prefilter, there is likely no
3408 /// reason to specialize start states.
3409 ///
3410 /// This is disabled by default, but note that it is automatically
3411 /// enabled (or disabled) if [`Config::prefilter`] is set. Namely, unless
3412 /// `specialize_start_states` has already been set, [`Config::prefilter`]
3413 /// will automatically enable or disable it based on whether a prefilter
3414 /// is present or not, respectively. This is done because a prefilter's
3415 /// effectiveness is rooted in being executed whenever the DFA is in a
3416 /// start state, and that's only possible to do when they are specialized.
3417 ///
3418 /// Note that it is plausibly reasonable to _disable_ this option
3419 /// explicitly while _enabling_ a prefilter. In that case, a prefilter
3420 /// will still be run at the beginning of a search, but never again. This
3421 /// in theory could strike a good balance if you're in a situation where a
3422 /// prefilter is likely to produce many false positive candidates.
3423 ///
3424 /// # Example
3425 ///
3426 /// This example shows how to enable start state specialization and then
3427 /// shows how to check whether a state is a start state or not.
3428 ///
3429 /// ```
3430 /// use regex_automata::{hybrid::dfa::DFA, MatchError, Input};
3431 ///
3432 /// let dfa = DFA::builder()
3433 /// .configure(DFA::config().specialize_start_states(true))
3434 /// .build(r"[a-z]+")?;
3435 /// let mut cache = dfa.create_cache();
3436 ///
3437 /// let haystack = "123 foobar 4567".as_bytes();
3438 /// let sid = dfa.start_state_forward(&mut cache, &Input::new(haystack))?;
3439 /// // The ID returned by 'start_state_forward' will always be tagged as
3440 /// // a start state when start state specialization is enabled.
3441 /// assert!(sid.is_tagged());
3442 /// assert!(sid.is_start());
3443 ///
3444 /// # Ok::<(), Box<dyn std::error::Error>>(())
3445 /// ```
3446 ///
3447 /// Compare the above with the default lazy DFA configuration where
3448 /// start states are _not_ specialized. In this case, the start state
3449 /// is not tagged and `sid.is_start()` returns false.
3450 ///
3451 /// ```
3452 /// use regex_automata::{hybrid::dfa::DFA, MatchError, Input};
3453 ///
3454 /// let dfa = DFA::new(r"[a-z]+")?;
3455 /// let mut cache = dfa.create_cache();
3456 ///
3457 /// let haystack = "123 foobar 4567".as_bytes();
3458 /// let sid = dfa.start_state_forward(&mut cache, &Input::new(haystack))?;
3459 /// // Start states are not tagged in the default configuration!
3460 /// assert!(!sid.is_tagged());
3461 /// assert!(!sid.is_start());
3462 ///
3463 /// # Ok::<(), Box<dyn std::error::Error>>(())
3464 /// ```
3465 pub fn specialize_start_states(mut self, yes: bool) -> Config {
3466 self.specialize_start_states = Some(yes);
3467 self
3468 }
3469
3470 /// Sets the maximum amount of heap memory, in bytes, to allocate to the
3471 /// cache for use during a lazy DFA search. If the lazy DFA would otherwise
3472 /// use more heap memory, then, depending on other configuration knobs,
3473 /// either stop the search and return an error or clear the cache and
3474 /// continue the search.
3475 ///
3476 /// The default cache capacity is some "reasonable" number that will
3477 /// accommodate most regular expressions. You may find that if you need
3478 /// to build a large DFA then it may be necessary to increase the cache
3479 /// capacity.
3480 ///
3481 /// Note that while building a lazy DFA will do a "minimum" check to ensure
3482 /// the capacity is big enough, this is more or less about correctness.
3483 /// If the cache is bigger than the minimum but still "too small," then the
3484 /// lazy DFA could wind up spending a lot of time clearing the cache and
3485 /// recomputing transitions, thus negating the performance benefits of a
3486 /// lazy DFA. Thus, setting the cache capacity is mostly an experimental
3487 /// endeavor. For most common patterns, however, the default should be
3488 /// sufficient.
3489 ///
3490 /// For more details on how the lazy DFA's cache is used, see the
3491 /// documentation for [`Cache`].
3492 ///
3493 /// # Example
3494 ///
3495 /// This example shows what happens if the configured cache capacity is
3496 /// too small. In such cases, one can override the cache capacity to make
3497 /// it bigger. Alternatively, one might want to use less memory by setting
3498 /// a smaller cache capacity.
3499 ///
3500 /// ```
3501 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
3502 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
3503 ///
3504 /// let pattern = r"\p{L}{1000}";
3505 ///
3506 /// // The default cache capacity is likely too small to deal with regexes
3507 /// // that are very large. Large repetitions of large Unicode character
3508 /// // classes are a common way to make very large regexes.
3509 /// let _ = DFA::new(pattern).unwrap_err();
3510 /// // Bump up the capacity to something bigger.
3511 /// let dfa = DFA::builder()
3512 /// .configure(DFA::config().cache_capacity(100 * (1<<20))) // 100 MB
3513 /// .build(pattern)?;
3514 /// let mut cache = dfa.create_cache();
3515 ///
3516 /// let haystack = "ͰͲͶͿΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙ".repeat(50);
3517 /// let expected = Some(HalfMatch::must(0, 2000));
3518 /// let got = dfa.try_search_fwd(&mut cache, &Input::new(&haystack))?;
3519 /// assert_eq!(expected, got);
3520 ///
3521 /// # Ok::<(), Box<dyn std::error::Error>>(())
3522 /// ```
3523 pub fn cache_capacity(mut self, bytes: usize) -> Config {
3524 self.cache_capacity = Some(bytes);
3525 self
3526 }
3527
3528 /// Configures construction of a lazy DFA to use the minimum cache capacity
3529 /// if the configured capacity is otherwise too small for the provided NFA.
3530 ///
3531 /// This is useful if you never want lazy DFA construction to fail because
3532 /// of a capacity that is too small.
3533 ///
3534 /// In general, this option is typically not a good idea. In particular,
3535 /// while a minimum cache capacity does permit the lazy DFA to function
3536 /// where it otherwise couldn't, it's plausible that it may not function
3537 /// well if it's constantly running out of room. In that case, the speed
3538 /// advantages of the lazy DFA may be negated. On the other hand, the
3539 /// "minimum" cache capacity computed may not be completely accurate and
3540 /// could actually be bigger than what is really necessary. Therefore, it
3541 /// is plausible that using the minimum cache capacity could still result
3542 /// in very good performance.
3543 ///
3544 /// This is disabled by default.
3545 ///
3546 /// # Example
3547 ///
3548 /// This example shows what happens if the configured cache capacity is
3549 /// too small. In such cases, one could override the capacity explicitly.
3550 /// An alternative, demonstrated here, let's us force construction to use
3551 /// the minimum cache capacity if the configured capacity is otherwise
3552 /// too small.
3553 ///
3554 /// ```
3555 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
3556 /// use regex_automata::{hybrid::dfa::DFA, HalfMatch, Input};
3557 ///
3558 /// let pattern = r"\p{L}{1000}";
3559 ///
3560 /// // The default cache capacity is likely too small to deal with regexes
3561 /// // that are very large. Large repetitions of large Unicode character
3562 /// // classes are a common way to make very large regexes.
3563 /// let _ = DFA::new(pattern).unwrap_err();
3564 /// // Configure construction such it automatically selects the minimum
3565 /// // cache capacity if it would otherwise be too small.
3566 /// let dfa = DFA::builder()
3567 /// .configure(DFA::config().skip_cache_capacity_check(true))
3568 /// .build(pattern)?;
3569 /// let mut cache = dfa.create_cache();
3570 ///
3571 /// let haystack = "ͰͲͶͿΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙ".repeat(50);
3572 /// let expected = Some(HalfMatch::must(0, 2000));
3573 /// let got = dfa.try_search_fwd(&mut cache, &Input::new(&haystack))?;
3574 /// assert_eq!(expected, got);
3575 ///
3576 /// # Ok::<(), Box<dyn std::error::Error>>(())
3577 /// ```
3578 pub fn skip_cache_capacity_check(mut self, yes: bool) -> Config {
3579 self.skip_cache_capacity_check = Some(yes);
3580 self
3581 }
3582
3583 /// Configure a lazy DFA search to quit after a certain number of cache
3584 /// clearings.
3585 ///
3586 /// When a minimum is set, then a lazy DFA search will *possibly* "give
3587 /// up" after the minimum number of cache clearings has occurred. This is
3588 /// typically useful in scenarios where callers want to detect whether the
3589 /// lazy DFA search is "efficient" or not. If the cache is cleared too many
3590 /// times, this is a good indicator that it is not efficient, and thus, the
3591 /// caller may wish to use some other regex engine.
3592 ///
3593 /// Note that the number of times a cache is cleared is a property of
3594 /// the cache itself. Thus, if a cache is used in a subsequent search
3595 /// with a similarly configured lazy DFA, then it could cause the
3596 /// search to "give up" if the cache needed to be cleared, depending
3597 /// on its internal count and configured minimum. The cache clear
3598 /// count can only be reset to `0` via [`DFA::reset_cache`] (or
3599 /// [`Regex::reset_cache`](crate::hybrid::regex::Regex::reset_cache) if
3600 /// you're using the `Regex` API).
3601 ///
3602 /// By default, no minimum is configured. Thus, a lazy DFA search will
3603 /// never give up due to cache clearings. If you do set this option, you
3604 /// might consider also setting [`Config::minimum_bytes_per_state`] in
3605 /// order for the lazy DFA to take efficiency into account before giving
3606 /// up.
3607 ///
3608 /// # Example
3609 ///
3610 /// This example uses a somewhat pathological configuration to demonstrate
3611 /// the _possible_ behavior of cache clearing and how it might result
3612 /// in a search that returns an error.
3613 ///
3614 /// It is important to note that the precise mechanics of how and when
3615 /// a cache gets cleared is an implementation detail.
3616 ///
3617 /// ```
3618 /// # if cfg!(miri) { return Ok(()); } // miri takes too long
3619 /// use regex_automata::{hybrid::dfa::DFA, Input, MatchError, MatchErrorKind};
3620 ///
3621 /// // This is a carefully chosen regex. The idea is to pick one
3622 /// // that requires some decent number of states (hence the bounded
3623 /// // repetition). But we specifically choose to create a class with an
3624 /// // ASCII letter and a non-ASCII letter so that we can check that no new
3625 /// // states are created once the cache is full. Namely, if we fill up the
3626 /// // cache on a haystack of 'a's, then in order to match one 'β', a new
3627 /// // state will need to be created since a 'β' is encoded with multiple
3628 /// // bytes. Since there's no room for this state, the search should quit
3629 /// // at the very first position.
3630 /// let pattern = r"[aβ]{100}";
3631 /// let dfa = DFA::builder()
3632 /// .configure(
3633 /// // Configure it so that we have the minimum cache capacity
3634 /// // possible. And that if any clearings occur, the search quits.
3635 /// DFA::config()
3636 /// .skip_cache_capacity_check(true)
3637 /// .cache_capacity(0)
3638 /// .minimum_cache_clear_count(Some(0)),
3639 /// )
3640 /// .build(pattern)?;
3641 /// let mut cache = dfa.create_cache();
3642 ///
3643 /// // Our search will give up before reaching the end!
3644 /// let haystack = "a".repeat(101).into_bytes();
3645 /// let result = dfa.try_search_fwd(&mut cache, &Input::new(&haystack));
3646 /// assert!(matches!(
3647 /// *result.unwrap_err().kind(),
3648 /// MatchErrorKind::GaveUp { .. },
3649 /// ));
3650 ///
3651 /// // Now that we know the cache is full, if we search a haystack that we
3652 /// // know will require creating at least one new state, it should not
3653 /// // be able to make much progress.
3654 /// let haystack = "β".repeat(101).into_bytes();
3655 /// let result = dfa.try_search_fwd(&mut cache, &Input::new(&haystack));
3656 /// assert!(matches!(
3657 /// *result.unwrap_err().kind(),
3658 /// MatchErrorKind::GaveUp { .. },
3659 /// ));
3660 ///
3661 /// // If we reset the cache, then we should be able to create more states
3662 /// // and make more progress with searching for betas.
3663 /// cache.reset(&dfa);
3664 /// let haystack = "β".repeat(101).into_bytes();
3665 /// let result = dfa.try_search_fwd(&mut cache, &Input::new(&haystack));
3666 /// assert!(matches!(
3667 /// *result.unwrap_err().kind(),
3668 /// MatchErrorKind::GaveUp { .. },
3669 /// ));
3670 ///
3671 /// // ... switching back to ASCII still makes progress since it just needs
3672 /// // to set transitions on existing states!
3673 /// let haystack = "a".repeat(101).into_bytes();
3674 /// let result = dfa.try_search_fwd(&mut cache, &Input::new(&haystack));
3675 /// assert!(matches!(
3676 /// *result.unwrap_err().kind(),
3677 /// MatchErrorKind::GaveUp { .. },
3678 /// ));
3679 ///
3680 /// # Ok::<(), Box<dyn std::error::Error>>(())
3681 /// ```
3682 pub fn minimum_cache_clear_count(mut self, min: Option<usize>) -> Config {
3683 self.minimum_cache_clear_count = Some(min);
3684 self
3685 }
3686
3687 /// Configure a lazy DFA search to quit only when its efficiency drops
3688 /// below the given minimum.
3689 ///
3690 /// The efficiency of the cache is determined by the number of DFA states
3691 /// compiled per byte of haystack searched. For example, if the efficiency
3692 /// is 2, then it means the lazy DFA is creating a new DFA state after
3693 /// searching approximately 2 bytes in a haystack. Generally speaking, 2
3694 /// is quite bad and it's likely that even a slower regex engine like the
3695 /// [`PikeVM`](crate::nfa::thompson::pikevm::PikeVM) would be faster.
3696 ///
3697 /// This has no effect if [`Config::minimum_cache_clear_count`] is not set.
3698 /// Namely, this option only kicks in when the cache has been cleared more
3699 /// than the minimum number. If no minimum is set, then the cache is simply
3700 /// cleared whenever it fills up and it is impossible for the lazy DFA to
3701 /// quit due to ineffective use of the cache.
3702 ///
3703 /// In general, if one is setting [`Config::minimum_cache_clear_count`],
3704 /// then one should probably also set this knob as well. The reason is
3705 /// that the absolute number of times the cache is cleared is generally
3706 /// not a great predictor of efficiency. For example, if a new DFA state
3707 /// is created for every 1,000 bytes searched, then it wouldn't be hard
3708 /// for the cache to get cleared more than `N` times and then cause the
3709 /// lazy DFA to quit. But a new DFA state every 1,000 bytes is likely quite
3710 /// good from a performance perspective, and it's likely that the lazy
3711 /// DFA should continue searching, even if it requires clearing the cache
3712 /// occasionally.
3713 ///
3714 /// Finally, note that if you're implementing your own lazy DFA search
3715 /// routine and also want this efficiency check to work correctly, then
3716 /// you'll need to use the following routines to record search progress:
3717 ///
3718 /// * Call [`Cache::search_start`] at the beginning of every search.
3719 /// * Call [`Cache::search_update`] whenever [`DFA::next_state`] is
3720 /// called.
3721 /// * Call [`Cache::search_finish`] before completing a search. (It is
3722 /// not strictly necessary to call this when an error is returned, as
3723 /// `Cache::search_start` will automatically finish the previous search
3724 /// for you. But calling it where possible before returning helps improve
3725 /// the accuracy of how many bytes have actually been searched.)
3726 pub fn minimum_bytes_per_state(mut self, min: Option<usize>) -> Config {
3727 self.minimum_bytes_per_state = Some(min);
3728 self
3729 }
3730
3731 /// Returns the match semantics set in this configuration.
3732 pub fn get_match_kind(&self) -> MatchKind {
3733 self.match_kind.unwrap_or(MatchKind::LeftmostFirst)
3734 }
3735
3736 /// Returns the prefilter set in this configuration, if one at all.
3737 pub fn get_prefilter(&self) -> Option<&Prefilter> {
3738 self.pre.as_ref().unwrap_or(&None).as_ref()
3739 }
3740
3741 /// Returns whether this configuration has enabled anchored starting states
3742 /// for every pattern in the DFA.
3743 pub fn get_starts_for_each_pattern(&self) -> bool {
3744 self.starts_for_each_pattern.unwrap_or(false)
3745 }
3746
3747 /// Returns whether this configuration has enabled byte classes or not.
3748 /// This is typically a debugging oriented option, as disabling it confers
3749 /// no speed benefit.
3750 pub fn get_byte_classes(&self) -> bool {
3751 self.byte_classes.unwrap_or(true)
3752 }
3753
3754 /// Returns whether this configuration has enabled heuristic Unicode word
3755 /// boundary support. When enabled, it is possible for a search to return
3756 /// an error.
3757 pub fn get_unicode_word_boundary(&self) -> bool {
3758 self.unicode_word_boundary.unwrap_or(false)
3759 }
3760
3761 /// Returns whether this configuration will instruct the lazy DFA to enter
3762 /// a quit state whenever the given byte is seen during a search. When at
3763 /// least one byte has this enabled, it is possible for a search to return
3764 /// an error.
3765 pub fn get_quit(&self, byte: u8) -> bool {
3766 self.quitset.map_or(false, |q| q.contains(byte))
3767 }
3768
3769 /// Returns whether this configuration will instruct the lazy DFA to
3770 /// "specialize" start states. When enabled, the lazy DFA will tag start
3771 /// states so that search routines using the lazy DFA can detect when
3772 /// it's in a start state and do some kind of optimization (like run a
3773 /// prefilter).
3774 pub fn get_specialize_start_states(&self) -> bool {
3775 self.specialize_start_states.unwrap_or(false)
3776 }
3777
3778 /// Returns the cache capacity set on this configuration.
3779 pub fn get_cache_capacity(&self) -> usize {
3780 self.cache_capacity.unwrap_or(2 * (1 << 20))
3781 }
3782
3783 /// Returns whether the cache capacity check should be skipped.
3784 pub fn get_skip_cache_capacity_check(&self) -> bool {
3785 self.skip_cache_capacity_check.unwrap_or(false)
3786 }
3787
3788 /// Returns, if set, the minimum number of times the cache must be cleared
3789 /// before a lazy DFA search can give up. When no minimum is set, then a
3790 /// search will never quit and will always clear the cache whenever it
3791 /// fills up.
3792 pub fn get_minimum_cache_clear_count(&self) -> Option<usize> {
3793 self.minimum_cache_clear_count.unwrap_or(None)
3794 }
3795
3796 /// Returns, if set, the minimum number of bytes per state that need to be
3797 /// processed in order for the lazy DFA to keep going. If the minimum falls
3798 /// below this number (and the cache has been cleared a minimum number of
3799 /// times), then the lazy DFA will return a "gave up" error.
3800 pub fn get_minimum_bytes_per_state(&self) -> Option<usize> {
3801 self.minimum_bytes_per_state.unwrap_or(None)
3802 }
3803
3804 /// Returns the minimum lazy DFA cache capacity required for the given NFA.
3805 ///
3806 /// The cache capacity required for a particular NFA may change without
3807 /// notice. Callers should not rely on it being stable.
3808 ///
3809 /// This is useful for informational purposes, but can also be useful for
3810 /// other reasons. For example, if one wants to check the minimum cache
3811 /// capacity themselves or if one wants to set the capacity based on the
3812 /// minimum.
3813 ///
3814 /// This may return an error if this configuration does not support all of
3815 /// the instructions used in the given NFA. For example, if the NFA has a
3816 /// Unicode word boundary but this configuration does not enable heuristic
3817 /// support for Unicode word boundaries.
3818 pub fn get_minimum_cache_capacity(
3819 &self,
3820 nfa: &thompson::NFA,
3821 ) -> Result<usize, BuildError> {
3822 let quitset = self.quit_set_from_nfa(nfa)?;
3823 let classes = self.byte_classes_from_nfa(nfa, &quitset);
3824 let starts = self.get_starts_for_each_pattern();
3825 Ok(minimum_cache_capacity(nfa, &classes, starts))
3826 }
3827
3828 /// Returns the byte class map used during search from the given NFA.
3829 ///
3830 /// If byte classes are disabled on this configuration, then a map is
3831 /// returned that puts each byte in its own equivalent class.
3832 fn byte_classes_from_nfa(
3833 &self,
3834 nfa: &thompson::NFA,
3835 quit: &ByteSet,
3836 ) -> ByteClasses {
3837 if !self.get_byte_classes() {
3838 // The lazy DFA will always use the equivalence class map, but
3839 // enabling this option is useful for debugging. Namely, this will
3840 // cause all transitions to be defined over their actual bytes
3841 // instead of an opaque equivalence class identifier. The former is
3842 // much easier to grok as a human.
3843 ByteClasses::singletons()
3844 } else {
3845 let mut set = nfa.byte_class_set().clone();
3846 // It is important to distinguish any "quit" bytes from all other
3847 // bytes. Otherwise, a non-quit byte may end up in the same class
3848 // as a quit byte, and thus cause the DFA stop when it shouldn't.
3849 //
3850 // Test case:
3851 //
3852 // regex-cli find match hybrid --unicode-word-boundary \
3853 // -p '^#' -p '\b10\.55\.182\.100\b' -y @conn.json.1000x.log
3854 if !quit.is_empty() {
3855 set.add_set(&quit);
3856 }
3857 set.byte_classes()
3858 }
3859 }
3860
3861 /// Return the quit set for this configuration and the given NFA.
3862 ///
3863 /// This may return an error if the NFA is incompatible with this
3864 /// configuration's quit set. For example, if the NFA has a Unicode word
3865 /// boundary and the quit set doesn't include non-ASCII bytes.
3866 fn quit_set_from_nfa(
3867 &self,
3868 nfa: &thompson::NFA,
3869 ) -> Result<ByteSet, BuildError> {
3870 let mut quit = self.quitset.unwrap_or(ByteSet::empty());
3871 if nfa.look_set_any().contains_word_unicode() {
3872 if self.get_unicode_word_boundary() {
3873 for b in 0x80..=0xFF {
3874 quit.add(b);
3875 }
3876 } else {
3877 // If heuristic support for Unicode word boundaries wasn't
3878 // enabled, then we can still check if our quit set is correct.
3879 // If the caller set their quit bytes in a way that causes the
3880 // DFA to quit on at least all non-ASCII bytes, then that's all
3881 // we need for heuristic support to work.
3882 if !quit.contains_range(0x80, 0xFF) {
3883 return Err(
3884 BuildError::unsupported_dfa_word_boundary_unicode(),
3885 );
3886 }
3887 }
3888 }
3889 Ok(quit)
3890 }
3891
3892 /// Overwrite the default configuration such that the options in `o` are
3893 /// always used. If an option in `o` is not set, then the corresponding
3894 /// option in `self` is used. If it's not set in `self` either, then it
3895 /// remains not set.
3896 fn overwrite(&self, o: Config) -> Config {
3897 Config {
3898 match_kind: o.match_kind.or(self.match_kind),
3899 pre: o.pre.or_else(|| self.pre.clone()),
3900 starts_for_each_pattern: o
3901 .starts_for_each_pattern
3902 .or(self.starts_for_each_pattern),
3903 byte_classes: o.byte_classes.or(self.byte_classes),
3904 unicode_word_boundary: o
3905 .unicode_word_boundary
3906 .or(self.unicode_word_boundary),
3907 quitset: o.quitset.or(self.quitset),
3908 specialize_start_states: o
3909 .specialize_start_states
3910 .or(self.specialize_start_states),
3911 cache_capacity: o.cache_capacity.or(self.cache_capacity),
3912 skip_cache_capacity_check: o
3913 .skip_cache_capacity_check
3914 .or(self.skip_cache_capacity_check),
3915 minimum_cache_clear_count: o
3916 .minimum_cache_clear_count
3917 .or(self.minimum_cache_clear_count),
3918 minimum_bytes_per_state: o
3919 .minimum_bytes_per_state
3920 .or(self.minimum_bytes_per_state),
3921 }
3922 }
3923}
3924
3925/// A builder for constructing a lazy deterministic finite automaton from
3926/// regular expressions.
3927///
3928/// As a convenience, [`DFA::builder`] is an alias for [`Builder::new`]. The
3929/// advantage of the former is that it often lets you avoid importing the
3930/// `Builder` type directly.
3931///
3932/// This builder provides two main things:
3933///
3934/// 1. It provides a few different `build` routines for actually constructing
3935/// a DFA from different kinds of inputs. The most convenient is
3936/// [`Builder::build`], which builds a DFA directly from a pattern string. The
3937/// most flexible is [`Builder::build_from_nfa`], which builds a DFA straight
3938/// from an NFA.
3939/// 2. The builder permits configuring a number of things.
3940/// [`Builder::configure`] is used with [`Config`] to configure aspects of
3941/// the DFA and the construction process itself. [`Builder::syntax`] and
3942/// [`Builder::thompson`] permit configuring the regex parser and Thompson NFA
3943/// construction, respectively. The syntax and thompson configurations only
3944/// apply when building from a pattern string.
3945///
3946/// This builder always constructs a *single* lazy DFA. As such, this builder
3947/// can only be used to construct regexes that either detect the presence
3948/// of a match or find the end location of a match. A single DFA cannot
3949/// produce both the start and end of a match. For that information, use a
3950/// [`Regex`](crate::hybrid::regex::Regex), which can be similarly configured
3951/// using [`regex::Builder`](crate::hybrid::regex::Builder). The main reason
3952/// to use a DFA directly is if the end location of a match is enough for your
3953/// use case. Namely, a `Regex` will construct two lazy DFAs instead of one,
3954/// since a second reverse DFA is needed to find the start of a match.
3955///
3956/// # Example
3957///
3958/// This example shows how to build a lazy DFA that uses a tiny cache capacity
3959/// and completely disables Unicode. That is:
3960///
3961/// * Things such as `\w`, `.` and `\b` are no longer Unicode-aware. `\w`
3962/// and `\b` are ASCII-only while `.` matches any byte except for `\n`
3963/// (instead of any UTF-8 encoding of a Unicode scalar value except for
3964/// `\n`). Things that are Unicode only, such as `\pL`, are not allowed.
3965/// * The pattern itself is permitted to match invalid UTF-8. For example,
3966/// things like `[^a]` that match any byte except for `a` are permitted.
3967///
3968/// ```
3969/// use regex_automata::{
3970/// hybrid::dfa::DFA,
3971/// nfa::thompson,
3972/// util::syntax,
3973/// HalfMatch, Input,
3974/// };
3975///
3976/// let dfa = DFA::builder()
3977/// .configure(DFA::config().cache_capacity(5_000))
3978/// .thompson(thompson::Config::new().utf8(false))
3979/// .syntax(syntax::Config::new().unicode(false).utf8(false))
3980/// .build(r"foo[^b]ar.*")?;
3981/// let mut cache = dfa.create_cache();
3982///
3983/// let haystack = b"\xFEfoo\xFFar\xE2\x98\xFF\n";
3984/// let expected = Some(HalfMatch::must(0, 10));
3985/// let got = dfa.try_search_fwd(&mut cache, &Input::new(haystack))?;
3986/// assert_eq!(expected, got);
3987///
3988/// # Ok::<(), Box<dyn std::error::Error>>(())
3989/// ```
3990#[derive(Clone, Debug)]
3991pub struct Builder {
3992 config: Config,
3993 #[cfg(feature = "syntax")]
3994 thompson: thompson::Compiler,
3995}
3996
3997impl Builder {
3998 /// Create a new lazy DFA builder with the default configuration.
3999 pub fn new() -> Builder {
4000 Builder {
4001 config: Config::default(),
4002 #[cfg(feature = "syntax")]
4003 thompson: thompson::Compiler::new(),
4004 }
4005 }
4006
4007 /// Build a lazy DFA from the given pattern.
4008 ///
4009 /// If there was a problem parsing or compiling the pattern, then an error
4010 /// is returned.
4011 #[cfg(feature = "syntax")]
4012 pub fn build(&self, pattern: &str) -> Result<DFA, BuildError> {
4013 self.build_many(&[pattern])
4014 }
4015
4016 /// Build a lazy DFA from the given patterns.
4017 ///
4018 /// When matches are returned, the pattern ID corresponds to the index of
4019 /// the pattern in the slice given.
4020 #[cfg(feature = "syntax")]
4021 pub fn build_many<P: AsRef<str>>(
4022 &self,
4023 patterns: &[P],
4024 ) -> Result<DFA, BuildError> {
4025 let nfa = self
4026 .thompson
4027 .clone()
4028 // We can always forcefully disable captures because DFAs do not
4029 // support them.
4030 .configure(
4031 thompson::Config::new()
4032 .which_captures(thompson::WhichCaptures::None),
4033 )
4034 .build_many(patterns)
4035 .map_err(BuildError::nfa)?;
4036 self.build_from_nfa(nfa)
4037 }
4038
4039 /// Build a DFA from the given NFA.
4040 ///
4041 /// Note that this requires owning a `thompson::NFA`. While this may force
4042 /// you to clone the NFA, such a clone is not a deep clone. Namely, NFAs
4043 /// are defined internally to support shared ownership such that cloning is
4044 /// very cheap.
4045 ///
4046 /// # Example
4047 ///
4048 /// This example shows how to build a lazy DFA if you already have an NFA
4049 /// in hand.
4050 ///
4051 /// ```
4052 /// use regex_automata::{
4053 /// hybrid::dfa::DFA,
4054 /// nfa::thompson,
4055 /// HalfMatch, Input,
4056 /// };
4057 ///
4058 /// let haystack = "foo123bar";
4059 ///
4060 /// // This shows how to set non-default options for building an NFA.
4061 /// let nfa = thompson::Compiler::new()
4062 /// .configure(thompson::Config::new().shrink(true))
4063 /// .build(r"[0-9]+")?;
4064 /// let dfa = DFA::builder().build_from_nfa(nfa)?;
4065 /// let mut cache = dfa.create_cache();
4066 /// let expected = Some(HalfMatch::must(0, 6));
4067 /// let got = dfa.try_search_fwd(&mut cache, &Input::new(haystack))?;
4068 /// assert_eq!(expected, got);
4069 ///
4070 /// # Ok::<(), Box<dyn std::error::Error>>(())
4071 /// ```
4072 pub fn build_from_nfa(
4073 &self,
4074 nfa: thompson::NFA,
4075 ) -> Result<DFA, BuildError> {
4076 let quitset = self.config.quit_set_from_nfa(&nfa)?;
4077 let classes = self.config.byte_classes_from_nfa(&nfa, &quitset);
4078 // Check that we can fit at least a few states into our cache,
4079 // otherwise it's pretty senseless to use the lazy DFA. This does have
4080 // a possible failure mode though. This assumes the maximum size of a
4081 // state in powerset space (so, the total number of NFA states), which
4082 // may never actually materialize, and could be quite a bit larger
4083 // than the actual biggest state. If this turns out to be a problem,
4084 // we could expose a knob that disables this check. But if so, we have
4085 // to be careful not to panic in other areas of the code (the cache
4086 // clearing and init code) that tend to assume some minimum useful
4087 // cache capacity.
4088 let min_cache = minimum_cache_capacity(
4089 &nfa,
4090 &classes,
4091 self.config.get_starts_for_each_pattern(),
4092 );
4093 let mut cache_capacity = self.config.get_cache_capacity();
4094 if cache_capacity < min_cache {
4095 // When the caller has asked us to skip the cache capacity check,
4096 // then we simply force the cache capacity to its minimum amount
4097 // and mush on.
4098 if self.config.get_skip_cache_capacity_check() {
4099 debug!(
4100 "given capacity ({cache_capacity}) is too small, \
4101 since skip_cache_capacity_check is enabled, \
4102 setting cache capacity to minimum ({min_cache})",
4103 );
4104 cache_capacity = min_cache;
4105 } else {
4106 return Err(BuildError::insufficient_cache_capacity(
4107 min_cache,
4108 cache_capacity,
4109 ));
4110 }
4111 }
4112 // We also need to check that we can fit at least some small number
4113 // of states in our state ID space. This is unlikely to trigger in
4114 // >=32-bit systems, but 16-bit systems have a pretty small state ID
4115 // space since a number of bits are used up as sentinels.
4116 if let Err(err) = minimum_lazy_state_id(&classes) {
4117 return Err(BuildError::insufficient_state_id_capacity(err));
4118 }
4119 let stride2 = classes.stride2();
4120 let start_map = StartByteMap::new(nfa.look_matcher());
4121 Ok(DFA {
4122 config: self.config.clone(),
4123 nfa,
4124 stride2,
4125 start_map,
4126 classes,
4127 quitset,
4128 cache_capacity,
4129 })
4130 }
4131
4132 /// Apply the given lazy DFA configuration options to this builder.
4133 pub fn configure(&mut self, config: Config) -> &mut Builder {
4134 self.config = self.config.overwrite(config);
4135 self
4136 }
4137
4138 /// Set the syntax configuration for this builder using
4139 /// [`syntax::Config`](crate::util::syntax::Config).
4140 ///
4141 /// This permits setting things like case insensitivity, Unicode and multi
4142 /// line mode.
4143 ///
4144 /// These settings only apply when constructing a lazy DFA directly from a
4145 /// pattern.
4146 #[cfg(feature = "syntax")]
4147 pub fn syntax(
4148 &mut self,
4149 config: crate::util::syntax::Config,
4150 ) -> &mut Builder {
4151 self.thompson.syntax(config);
4152 self
4153 }
4154
4155 /// Set the Thompson NFA configuration for this builder using
4156 /// [`nfa::thompson::Config`](crate::nfa::thompson::Config).
4157 ///
4158 /// This permits setting things like whether the DFA should match the regex
4159 /// in reverse or if additional time should be spent shrinking the size of
4160 /// the NFA.
4161 ///
4162 /// These settings only apply when constructing a DFA directly from a
4163 /// pattern.
4164 #[cfg(feature = "syntax")]
4165 pub fn thompson(&mut self, config: thompson::Config) -> &mut Builder {
4166 self.thompson.configure(config);
4167 self
4168 }
4169}
4170
4171/// Represents the current state of an overlapping search.
4172///
4173/// This is used for overlapping searches since they need to know something
4174/// about the previous search. For example, when multiple patterns match at the
4175/// same position, this state tracks the last reported pattern so that the next
4176/// search knows whether to report another matching pattern or continue with
4177/// the search at the next position. Additionally, it also tracks which state
4178/// the last search call terminated in.
4179///
4180/// This type provides little introspection capabilities. The only thing a
4181/// caller can do is construct it and pass it around to permit search routines
4182/// to use it to track state, and also ask whether a match has been found.
4183///
4184/// Callers should always provide a fresh state constructed via
4185/// [`OverlappingState::start`] when starting a new search. Reusing state from
4186/// a previous search may result in incorrect results.
4187#[derive(Clone, Debug, Eq, PartialEq)]
4188pub struct OverlappingState {
4189 /// The match reported by the most recent overlapping search to use this
4190 /// state.
4191 ///
4192 /// If a search does not find any matches, then it is expected to clear
4193 /// this value.
4194 pub(crate) mat: Option<HalfMatch>,
4195 /// The state ID of the state at which the search was in when the call
4196 /// terminated. When this is a match state, `last_match` must be set to a
4197 /// non-None value.
4198 ///
4199 /// A `None` value indicates the start state of the corresponding
4200 /// automaton. We cannot use the actual ID, since any one automaton may
4201 /// have many start states, and which one is in use depends on several
4202 /// search-time factors.
4203 pub(crate) id: Option<LazyStateID>,
4204 /// The position of the search.
4205 ///
4206 /// When `id` is None (i.e., we are starting a search), this is set to
4207 /// the beginning of the search as given by the caller regardless of its
4208 /// current value. Subsequent calls to an overlapping search pick up at
4209 /// this offset.
4210 pub(crate) at: usize,
4211 /// The index into the matching patterns of the next match to report if the
4212 /// current state is a match state. Note that this may be 1 greater than
4213 /// the total number of matches to report for the current match state. (In
4214 /// which case, no more matches should be reported at the current position
4215 /// and the search should advance to the next position.)
4216 pub(crate) next_match_index: Option<usize>,
4217 /// This is set to true when a reverse overlapping search has entered its
4218 /// EOI transitions.
4219 ///
4220 /// This isn't used in a forward search because it knows to stop once the
4221 /// position exceeds the end of the search range. In a reverse search,
4222 /// since we use unsigned offsets, we don't "know" once we've gone past
4223 /// `0`. So the only way to detect it is with this extra flag. The reverse
4224 /// overlapping search knows to terminate specifically after it has
4225 /// reported all matches after following the EOI transition.
4226 pub(crate) rev_eoi: bool,
4227}
4228
4229impl OverlappingState {
4230 /// Create a new overlapping state that begins at the start state of any
4231 /// automaton.
4232 pub fn start() -> OverlappingState {
4233 OverlappingState {
4234 mat: None,
4235 id: None,
4236 at: 0,
4237 next_match_index: None,
4238 rev_eoi: false,
4239 }
4240 }
4241
4242 /// Return the match result of the most recent search to execute with this
4243 /// state.
4244 ///
4245 /// A searches will clear this result automatically, such that if no
4246 /// match is found, this will correctly report `None`.
4247 pub fn get_match(&self) -> Option<HalfMatch> {
4248 self.mat
4249 }
4250}
4251
4252/// Runs the given overlapping `search` function (forwards or backwards) until
4253/// a match is found whose offset does not split a codepoint.
4254///
4255/// This is *not* always correct to call. It should only be called when the
4256/// underlying NFA has UTF-8 mode enabled *and* it can produce zero-width
4257/// matches. Calling this when both of those things aren't true might result
4258/// in legitimate matches getting skipped.
4259#[cold]
4260#[inline(never)]
4261fn skip_empty_utf8_splits_overlapping<F>(
4262 input: &Input<'_>,
4263 state: &mut OverlappingState,
4264 mut search: F,
4265) -> Result<(), MatchError>
4266where
4267 F: FnMut(&Input<'_>, &mut OverlappingState) -> Result<(), MatchError>,
4268{
4269 // Note that this routine works for forwards and reverse searches
4270 // even though there's no code here to handle those cases. That's
4271 // because overlapping searches drive themselves to completion via
4272 // `OverlappingState`. So all we have to do is push it until no matches are
4273 // found.
4274
4275 let mut hm = match state.get_match() {
4276 None => return Ok(()),
4277 Some(hm) => hm,
4278 };
4279 if input.get_anchored().is_anchored() {
4280 if !input.is_char_boundary(hm.offset()) {
4281 state.mat = None;
4282 }
4283 return Ok(());
4284 }
4285 while !input.is_char_boundary(hm.offset()) {
4286 search(input, state)?;
4287 hm = match state.get_match() {
4288 None => return Ok(()),
4289 Some(hm) => hm,
4290 };
4291 }
4292 Ok(())
4293}
4294
4295/// Based on the minimum number of states required for a useful lazy DFA cache,
4296/// this returns the minimum lazy state ID that must be representable.
4297///
4298/// It's not likely for this to have any impact 32-bit systems (or higher), but
4299/// on 16-bit systems, the lazy state ID space is quite constrained and thus
4300/// may be insufficient if our MIN_STATES value is (for some reason) too high.
4301fn minimum_lazy_state_id(
4302 classes: &ByteClasses,
4303) -> Result<LazyStateID, LazyStateIDError> {
4304 let stride = 1 << classes.stride2();
4305 let min_state_index = MIN_STATES.checked_sub(1).unwrap();
4306 LazyStateID::new(min_state_index * stride)
4307}
4308
4309/// Based on the minimum number of states required for a useful lazy DFA cache,
4310/// this returns a heuristic minimum number of bytes of heap space required.
4311///
4312/// This is a "heuristic" because the minimum it returns is likely bigger than
4313/// the true minimum. Namely, it assumes that each powerset NFA/DFA state uses
4314/// the maximum number of NFA states (all of them). This is likely bigger
4315/// than what is required in practice. Computing the true minimum effectively
4316/// requires determinization, which is probably too much work to do for a
4317/// simple check like this.
4318///
4319/// One of the issues with this approach IMO is that it requires that this
4320/// be in sync with the calculation above for computing how much heap memory
4321/// the DFA cache uses. If we get it wrong, it's possible for example for the
4322/// minimum to be smaller than the computed heap memory, and thus, it may be
4323/// the case that we can't add the required minimum number of states. That in
4324/// turn will make lazy DFA panic because we assume that we can add at least a
4325/// minimum number of states.
4326///
4327/// Another approach would be to always allow the minimum number of states to
4328/// be added to the lazy DFA cache, even if it exceeds the configured cache
4329/// limit. This does mean that the limit isn't really a limit in all cases,
4330/// which is unfortunate. But it does at least guarantee that the lazy DFA can
4331/// always make progress, even if it is slow. (This approach is very similar to
4332/// enabling the 'skip_cache_capacity_check' config knob, except it wouldn't
4333/// rely on cache size calculation. Instead, it would just always permit a
4334/// minimum number of states to be added.)
4335fn minimum_cache_capacity(
4336 nfa: &thompson::NFA,
4337 classes: &ByteClasses,
4338 starts_for_each_pattern: bool,
4339) -> usize {
4340 const ID_SIZE: usize = size_of::<LazyStateID>();
4341 const STATE_SIZE: usize = size_of::<State>();
4342
4343 let stride = 1 << classes.stride2();
4344 let states_len = nfa.states().len();
4345 let sparses = 2 * states_len * NFAStateID::SIZE;
4346 let trans = MIN_STATES * stride * ID_SIZE;
4347
4348 let mut starts = Start::len() * ID_SIZE;
4349 if starts_for_each_pattern {
4350 starts += (Start::len() * nfa.pattern_len()) * ID_SIZE;
4351 }
4352
4353 // The min number of states HAS to be at least 4: we have 3 sentinel states
4354 // and then we need space for one more when we save a state after clearing
4355 // the cache. We also need space for one more, otherwise we get stuck in a
4356 // loop where we try to add a 5th state, which gets rejected, which clears
4357 // the cache, which adds back a saved state (4th total state) which then
4358 // tries to add the 5th state again.
4359 assert!(MIN_STATES >= 5, "minimum number of states has to be at least 5");
4360 // The minimum number of non-sentinel states. We consider this separately
4361 // because sentinel states are much smaller in that they contain no NFA
4362 // states. Given our aggressive calculation here, it's worth being more
4363 // precise with the number of states we need.
4364 let non_sentinel = MIN_STATES.checked_sub(SENTINEL_STATES).unwrap();
4365
4366 // Every `State` has 5 bytes for flags, 4 bytes (max) for the number of
4367 // patterns, followed by 32-bit encodings of patterns and then delta
4368 // varint encodings of NFA state IDs. We use the worst case (which isn't
4369 // technically possible) of 5 bytes for each NFA state ID.
4370 //
4371 // HOWEVER, three of the states needed by a lazy DFA are just the sentinel
4372 // unknown, dead and quit states. Those states have a known size and it is
4373 // small.
4374 let dead_state_size = State::dead().memory_usage();
4375 let max_state_size = 5 + 4 + (nfa.pattern_len() * 4) + (states_len * 5);
4376 let states = (SENTINEL_STATES * (STATE_SIZE + dead_state_size))
4377 + (non_sentinel * (STATE_SIZE + max_state_size));
4378 // NOTE: We don't double count heap memory used by State for this map since
4379 // we use reference counting to avoid doubling memory usage. (This tends to
4380 // be where most memory is allocated in the cache.)
4381 let states_to_sid = (MIN_STATES * STATE_SIZE) + (MIN_STATES * ID_SIZE);
4382 let stack = states_len * NFAStateID::SIZE;
4383 let scratch_state_builder = max_state_size;
4384
4385 trans
4386 + starts
4387 + states
4388 + states_to_sid
4389 + sparses
4390 + stack
4391 + scratch_state_builder
4392}
4393
4394#[cfg(all(test, feature = "syntax"))]
4395mod tests {
4396 use super::*;
4397
4398 // Tests that we handle heuristic Unicode word boundary support in reverse
4399 // DFAs in the specific case of contextual searches.
4400 //
4401 // I wrote this test when I discovered a bug in how heuristic word
4402 // boundaries were handled. Namely, that the starting state selection
4403 // didn't consider the DFA's quit byte set when looking at the byte
4404 // immediately before the start of the search (or immediately after the
4405 // end of the search in the case of a reverse search). As a result, it was
4406 // possible for '\bfoo\b' to match 'β123' because the trailing \xB2 byte
4407 // in the 'β' codepoint would be treated as a non-word character. But of
4408 // course, this search should trigger the DFA to quit, since there is a
4409 // non-ASCII byte in consideration.
4410 //
4411 // Thus, I fixed 'start_state_{forward,reverse}' to check the quit byte set
4412 // if it wasn't empty. The forward case is tested in the doc test for the
4413 // Config::unicode_word_boundary API. We test the reverse case here, which
4414 // is sufficiently niche that it doesn't really belong in a doc test.
4415 #[test]
4416 fn heuristic_unicode_reverse() {
4417 let dfa = DFA::builder()
4418 .configure(DFA::config().unicode_word_boundary(true))
4419 .thompson(thompson::Config::new().reverse(true))
4420 .build(r"\b[0-9]+\b")
4421 .unwrap();
4422 let mut cache = dfa.create_cache();
4423
4424 let input = Input::new("β123").range(2..);
4425 let expected = MatchError::quit(0xB2, 1);
4426 let got = dfa.try_search_rev(&mut cache, &input);
4427 assert_eq!(Err(expected), got);
4428
4429 let input = Input::new("123β").range(..3);
4430 let expected = MatchError::quit(0xCE, 3);
4431 let got = dfa.try_search_rev(&mut cache, &input);
4432 assert_eq!(Err(expected), got);
4433 }
4434}