@@ -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 ///
@@ -1257,6 +1316,32 @@ impl Regex {
12571316 self . imp . strat . search ( cache, input)
12581317 }
12591318
1319+ /// This is like [`Regex::search_with`], but returns an error instead of
1320+ /// falling back to the PikeVM.
1321+ ///
1322+ /// A `Regex` built with [`Config::pikevm`] disabled has no engine left
1323+ /// once the DFAs and the bounded engines have all given up on a search.
1324+ /// This routine reports that as a [`MatchError`], where the infallible
1325+ /// variants panic. A `Regex` built with the PikeVM enabled (the default)
1326+ /// never returns an error here.
1327+ ///
1328+ /// This is a `topk-io/regex` fork addition.
1329+ pub fn try_search_with (
1330+ & self ,
1331+ cache : & mut Cache ,
1332+ input : & Input < ' _ > ,
1333+ ) -> Result < Option < Match > , MatchError > {
1334+ if self . imp . info . captures_disabled ( )
1335+ || self . imp . info . is_impossible ( input)
1336+ {
1337+ return Ok ( None ) ;
1338+ }
1339+ self . imp
1340+ . strat
1341+ . try_search ( cache, input)
1342+ . map_err ( |err| MatchError :: gave_up ( err. offset ( ) ) )
1343+ }
1344+
12601345 /// This is like [`Regex::search_half`], but requires the caller to
12611346 /// explicitly pass a [`Cache`].
12621347 ///
@@ -1301,6 +1386,32 @@ impl Regex {
13011386 self . imp . strat . search_half ( cache, input)
13021387 }
13031388
1389+ /// This is like [`Regex::search_half_with`], but returns an error instead of
1390+ /// falling back to the PikeVM.
1391+ ///
1392+ /// A `Regex` built with [`Config::pikevm`] disabled has no engine left
1393+ /// once the DFAs and the bounded engines have all given up on a search.
1394+ /// This routine reports that as a [`MatchError`], where the infallible
1395+ /// variants panic. A `Regex` built with the PikeVM enabled (the default)
1396+ /// never returns an error here.
1397+ ///
1398+ /// This is a `topk-io/regex` fork addition.
1399+ pub fn try_search_half_with (
1400+ & self ,
1401+ cache : & mut Cache ,
1402+ input : & Input < ' _ > ,
1403+ ) -> Result < Option < HalfMatch > , MatchError > {
1404+ if self . imp . info . captures_disabled ( )
1405+ || self . imp . info . is_impossible ( input)
1406+ {
1407+ return Ok ( None ) ;
1408+ }
1409+ self . imp
1410+ . strat
1411+ . try_search_half ( cache, input)
1412+ . map_err ( |err| MatchError :: gave_up ( err. offset ( ) ) )
1413+ }
1414+
13041415 /// This is like [`Regex::search_captures`], but requires the caller to
13051416 /// explicitly pass a [`Cache`].
13061417 ///
@@ -1394,6 +1505,29 @@ impl Regex {
13941505 caps. set_pattern ( pid) ;
13951506 }
13961507
1508+ /// This is like [`Regex::search_captures_with`], but returns an error instead of
1509+ /// falling back to the PikeVM.
1510+ ///
1511+ /// A `Regex` built with [`Config::pikevm`] disabled has no engine left
1512+ /// once the DFAs and the bounded engines have all given up on a search.
1513+ /// This routine reports that as a [`MatchError`], where the infallible
1514+ /// variants panic. A `Regex` built with the PikeVM enabled (the default)
1515+ /// never returns an error here.
1516+ ///
1517+ /// This is a `topk-io/regex` fork addition.
1518+ pub fn try_search_captures_with (
1519+ & self ,
1520+ cache : & mut Cache ,
1521+ input : & Input < ' _ > ,
1522+ caps : & mut Captures ,
1523+ ) -> Result < ( ) , MatchError > {
1524+ caps. set_pattern ( None ) ;
1525+ let pid =
1526+ self . try_search_slots_with ( cache, input, caps. slots_mut ( ) ) ?;
1527+ caps. set_pattern ( pid) ;
1528+ Ok ( ( ) )
1529+ }
1530+
13971531 /// This is like [`Regex::search_slots`], but requires the caller to
13981532 /// explicitly pass a [`Cache`].
13991533 ///
@@ -1456,6 +1590,33 @@ impl Regex {
14561590 self . imp . strat . search_slots ( cache, input, slots)
14571591 }
14581592
1593+ /// This is like [`Regex::search_slots_with`], but returns an error instead of
1594+ /// falling back to the PikeVM.
1595+ ///
1596+ /// A `Regex` built with [`Config::pikevm`] disabled has no engine left
1597+ /// once the DFAs and the bounded engines have all given up on a search.
1598+ /// This routine reports that as a [`MatchError`], where the infallible
1599+ /// variants panic. A `Regex` built with the PikeVM enabled (the default)
1600+ /// never returns an error here.
1601+ ///
1602+ /// This is a `topk-io/regex` fork addition.
1603+ pub fn try_search_slots_with (
1604+ & self ,
1605+ cache : & mut Cache ,
1606+ input : & Input < ' _ > ,
1607+ slots : & mut [ Option < NonMaxUsize > ] ,
1608+ ) -> Result < Option < PatternID > , MatchError > {
1609+ if self . imp . info . captures_disabled ( )
1610+ || self . imp . info . is_impossible ( input)
1611+ {
1612+ return Ok ( None ) ;
1613+ }
1614+ self . imp
1615+ . strat
1616+ . try_search_slots ( cache, input, slots)
1617+ . map_err ( |err| MatchError :: gave_up ( err. offset ( ) ) )
1618+ }
1619+
14591620 /// This is like [`Regex::which_overlapping_matches`], but requires the
14601621 /// caller to explicitly pass a [`Cache`].
14611622 ///
@@ -1504,6 +1665,31 @@ impl Regex {
15041665 }
15051666 self . imp . strat . which_overlapping_matches ( cache, input, patset)
15061667 }
1668+
1669+ /// This is like [`Regex::which_overlapping_matches_with`], but returns an error instead of
1670+ /// falling back to the PikeVM.
1671+ ///
1672+ /// A `Regex` built with [`Config::pikevm`] disabled has no engine left
1673+ /// once the DFAs and the bounded engines have all given up on a search.
1674+ /// This routine reports that as a [`MatchError`], where the infallible
1675+ /// variants panic. A `Regex` built with the PikeVM enabled (the default)
1676+ /// never returns an error here.
1677+ ///
1678+ /// This is a `topk-io/regex` fork addition.
1679+ pub fn try_which_overlapping_matches_with (
1680+ & self ,
1681+ cache : & mut Cache ,
1682+ input : & Input < ' _ > ,
1683+ patset : & mut PatternSet ,
1684+ ) -> Result < ( ) , MatchError > {
1685+ if self . imp . info . is_impossible ( input) {
1686+ return Ok ( ( ) ) ;
1687+ }
1688+ self . imp
1689+ . strat
1690+ . try_which_overlapping_matches ( cache, input, patset)
1691+ . map_err ( |err| MatchError :: gave_up ( err. offset ( ) ) )
1692+ }
15071693}
15081694
15091695/// Various non-search routines for querying properties of a `Regex` and
@@ -2481,6 +2667,8 @@ pub struct Config {
24812667 dfa_state_limit : Option < Option < usize > > ,
24822668 onepass : Option < bool > ,
24832669 backtrack : Option < bool > ,
2670+ backtrack_visited_capacity : Option < usize > ,
2671+ pikevm : Option < bool > ,
24842672 byte_classes : Option < bool > ,
24852673 line_terminator : Option < u8 > ,
24862674}
@@ -3097,6 +3285,41 @@ impl Config {
30973285 Config { backtrack : Some ( yes) , ..self }
30983286 }
30993287
3288+ /// Sets the visited capacity, in bytes, used by the bounded backtracker.
3289+ ///
3290+ /// The backtracker refuses to run when the haystack is longer than its
3291+ /// visited bitset can track, which is roughly this capacity divided by the
3292+ /// number of NFA states. Raising it lets the backtracker serve longer
3293+ /// haystacks, which matters when [`Config::pikevm`] is disabled and the
3294+ /// backtracker is the last engine standing.
3295+ ///
3296+ /// When unset, the backtracker's own default is used.
3297+ ///
3298+ /// This is a `topk-io/regex` fork addition.
3299+ pub fn backtrack_visited_capacity ( self , capacity : usize ) -> Config {
3300+ Config { backtrack_visited_capacity : Some ( capacity) , ..self }
3301+ }
3302+
3303+ /// Whether to permit the use of the PikeVM.
3304+ ///
3305+ /// The PikeVM is the meta regex engine's engine of last resort: it can
3306+ /// handle any regex against any haystack, but its NFA simulation can be
3307+ /// orders of magnitude slower than the DFAs. Disabling it means a search
3308+ /// that the faster engines give up on has nothing left to fall back to.
3309+ /// Only [`Regex::try_is_match_with`] can report that condition, by
3310+ /// returning a [`MatchError`]; the other search routines require the
3311+ /// PikeVM and will panic if it is disabled.
3312+ ///
3313+ /// This is useful for rejecting a pathological regex outright rather than
3314+ /// paying for it.
3315+ ///
3316+ /// This is enabled by default.
3317+ ///
3318+ /// This is a `topk-io/regex` fork addition.
3319+ pub fn pikevm ( self , yes : bool ) -> Config {
3320+ Config { pikevm : Some ( yes) , ..self }
3321+ }
3322+
31003323 /// Returns the match kind on this configuration, as set by
31013324 /// [`Config::match_kind`].
31023325 ///
@@ -3270,6 +3493,22 @@ impl Config {
32703493 }
32713494 }
32723495
3496+ /// Returns the backtracker's visited capacity, as set by
3497+ /// [`Config::backtrack_visited_capacity`]. `None` means the backtracker's
3498+ /// own default is used.
3499+ ///
3500+ /// This is a `topk-io/regex` fork addition.
3501+ pub fn get_backtrack_visited_capacity ( & self ) -> Option < usize > {
3502+ self . backtrack_visited_capacity
3503+ }
3504+
3505+ /// Returns whether the PikeVM may be used, as set by [`Config::pikevm`].
3506+ ///
3507+ /// This is a `topk-io/regex` fork addition.
3508+ pub fn get_pikevm ( & self ) -> bool {
3509+ self . pikevm . unwrap_or ( true )
3510+ }
3511+
32733512 /// Returns a "baseline" Thompson configuration for constructing NFAs based
32743513 /// on this configuration.
32753514 ///
@@ -3315,6 +3554,10 @@ impl Config {
33153554 dfa_state_limit : o. dfa_state_limit . or ( self . dfa_state_limit ) ,
33163555 onepass : o. onepass . or ( self . onepass ) ,
33173556 backtrack : o. backtrack . or ( self . backtrack ) ,
3557+ backtrack_visited_capacity : o
3558+ . backtrack_visited_capacity
3559+ . or ( self . backtrack_visited_capacity ) ,
3560+ pikevm : o. pikevm . or ( self . pikevm ) ,
33183561 byte_classes : o. byte_classes . or ( self . byte_classes ) ,
33193562 line_terminator : o. line_terminator . or ( self . line_terminator ) ,
33203563 }
0 commit comments