@@ -24,7 +24,9 @@ use crate::{
2424 pool:: { Pool , PoolGuard } ,
2525 prefilter:: Prefilter ,
2626 primitives:: { NonMaxUsize , PatternID } ,
27- search:: { HalfMatch , Input , Match , MatchKind , PatternSet , Span } ,
27+ search:: {
28+ HalfMatch , Input , Match , MatchError , MatchKind , PatternSet , Span ,
29+ } ,
2830 } ,
2931} ;
3032
@@ -540,6 +542,63 @@ impl Regex {
540542 result
541543 }
542544
545+ /// Executes a leftmost is-match search, returning an error instead of
546+ /// falling back to the PikeVM.
547+ ///
548+ /// This is like [`Regex::is_match`], but for a `Regex` built with
549+ /// [`Config::pikevm`] disabled. The meta regex engine's fast engines (the
550+ /// DFAs) can give up on a search, either because a lazy DFA's cache is
551+ /// being used ineffectively or because a Unicode word boundary was seen
552+ /// alongside a non-ASCII byte. The engines with bounded runtime (the
553+ /// one-pass DFA and the bounded backtracker) still pick those up, and with
554+ /// the PikeVM disabled the backtracker is given every search it can serve
555+ /// rather than only the short haystacks it is normally reserved for. Only
556+ /// once nothing is left to run does this routine report a [`MatchError`],
557+ /// which is to say only when the haystack outgrows the backtracker's
558+ /// visited bitset (see [`Config::backtrack_visited_capacity`]).
559+ ///
560+ /// A `Regex` built with the PikeVM enabled (the default) never returns an
561+ /// error here.
562+ ///
563+ /// This is a `topk-io/regex` fork addition.
564+ ///
565+ /// # Example
566+ ///
567+ /// ```
568+ /// use regex_automata::meta::Regex;
569+ ///
570+ /// let re = Regex::builder()
571+ /// .configure(Regex::config().pikevm(false))
572+ /// .build(r"\b\w+\b")?;
573+ /// let mut cache = re.create_cache();
574+ ///
575+ /// assert_eq!(Ok(true), re.try_is_match_with(&mut cache, "quux"));
576+ /// // A Unicode word boundary against a non-ASCII haystack makes the lazy
577+ /// // DFA quit, but the backtracker still answers it.
578+ /// let haystack = "☃".repeat(100);
579+ /// assert_eq!(Ok(false), re.try_is_match_with(&mut cache, &haystack));
580+ /// // Past the backtracker's bound there is no engine left to run.
581+ /// let haystack = "☃".repeat(10_000);
582+ /// assert!(re.try_is_match_with(&mut cache, &haystack).is_err());
583+ ///
584+ /// # Ok::<(), Box<dyn std::error::Error>>(())
585+ /// ```
586+ #[ inline]
587+ pub fn try_is_match_with < ' h , I : Into < Input < ' h > > > (
588+ & self ,
589+ cache : & mut Cache ,
590+ input : I ,
591+ ) -> Result < bool , MatchError > {
592+ let input = input. into ( ) . earliest ( true ) ;
593+ if self . imp . info . is_impossible ( & input) {
594+ return Ok ( false ) ;
595+ }
596+ self . imp
597+ . strat
598+ . try_is_match ( cache, & input)
599+ . map_err ( |err| MatchError :: gave_up ( err. offset ( ) ) )
600+ }
601+
543602 /// Executes a leftmost search and returns the first match that is found,
544603 /// if one exists.
545604 ///
@@ -2481,6 +2540,8 @@ pub struct Config {
24812540 dfa_state_limit : Option < Option < usize > > ,
24822541 onepass : Option < bool > ,
24832542 backtrack : Option < bool > ,
2543+ backtrack_visited_capacity : Option < usize > ,
2544+ pikevm : Option < bool > ,
24842545 byte_classes : Option < bool > ,
24852546 line_terminator : Option < u8 > ,
24862547}
@@ -3097,6 +3158,41 @@ impl Config {
30973158 Config { backtrack : Some ( yes) , ..self }
30983159 }
30993160
3161+ /// Sets the visited capacity, in bytes, used by the bounded backtracker.
3162+ ///
3163+ /// The backtracker refuses to run when the haystack is longer than its
3164+ /// visited bitset can track, which is roughly this capacity divided by the
3165+ /// number of NFA states. Raising it lets the backtracker serve longer
3166+ /// haystacks, which matters when [`Config::pikevm`] is disabled and the
3167+ /// backtracker is the last engine standing.
3168+ ///
3169+ /// When unset, the backtracker's own default is used.
3170+ ///
3171+ /// This is a `topk-io/regex` fork addition.
3172+ pub fn backtrack_visited_capacity ( self , capacity : usize ) -> Config {
3173+ Config { backtrack_visited_capacity : Some ( capacity) , ..self }
3174+ }
3175+
3176+ /// Whether to permit the use of the PikeVM.
3177+ ///
3178+ /// The PikeVM is the meta regex engine's engine of last resort: it can
3179+ /// handle any regex against any haystack, but its NFA simulation can be
3180+ /// orders of magnitude slower than the DFAs. Disabling it means a search
3181+ /// that the faster engines give up on has nothing left to fall back to.
3182+ /// Only [`Regex::try_is_match_with`] can report that condition, by
3183+ /// returning a [`MatchError`]; the other search routines require the
3184+ /// PikeVM and will panic if it is disabled.
3185+ ///
3186+ /// This is useful for rejecting a pathological regex outright rather than
3187+ /// paying for it.
3188+ ///
3189+ /// This is enabled by default.
3190+ ///
3191+ /// This is a `topk-io/regex` fork addition.
3192+ pub fn pikevm ( self , yes : bool ) -> Config {
3193+ Config { pikevm : Some ( yes) , ..self }
3194+ }
3195+
31003196 /// Returns the match kind on this configuration, as set by
31013197 /// [`Config::match_kind`].
31023198 ///
@@ -3270,6 +3366,22 @@ impl Config {
32703366 }
32713367 }
32723368
3369+ /// Returns the backtracker's visited capacity, as set by
3370+ /// [`Config::backtrack_visited_capacity`]. `None` means the backtracker's
3371+ /// own default is used.
3372+ ///
3373+ /// This is a `topk-io/regex` fork addition.
3374+ pub fn get_backtrack_visited_capacity ( & self ) -> Option < usize > {
3375+ self . backtrack_visited_capacity
3376+ }
3377+
3378+ /// Returns whether the PikeVM may be used, as set by [`Config::pikevm`].
3379+ ///
3380+ /// This is a `topk-io/regex` fork addition.
3381+ pub fn get_pikevm ( & self ) -> bool {
3382+ self . pikevm . unwrap_or ( true )
3383+ }
3384+
32733385 /// Returns a "baseline" Thompson configuration for constructing NFAs based
32743386 /// on this configuration.
32753387 ///
@@ -3315,6 +3427,10 @@ impl Config {
33153427 dfa_state_limit : o. dfa_state_limit . or ( self . dfa_state_limit ) ,
33163428 onepass : o. onepass . or ( self . onepass ) ,
33173429 backtrack : o. backtrack . or ( self . backtrack ) ,
3430+ backtrack_visited_capacity : o
3431+ . backtrack_visited_capacity
3432+ . or ( self . backtrack_visited_capacity ) ,
3433+ pikevm : o. pikevm . or ( self . pikevm ) ,
33183434 byte_classes : o. byte_classes . or ( self . byte_classes ) ,
33193435 line_terminator : o. line_terminator . or ( self . line_terminator ) ,
33203436 }
0 commit comments