]> git.eshelyaron.com Git - emacs.git/commitdiff
regex-emacs.c: Reduce the use of backtracking a bit further
authorStefan Monnier <monnier@iro.umontreal.ca>
Tue, 21 Feb 2023 02:22:41 +0000 (21:22 -0500)
committerStefan Monnier <monnier@iro.umontreal.ca>
Tue, 21 Feb 2023 02:22:41 +0000 (21:22 -0500)
bug#61514 exhibited some undesirable backtracking in a case where
it's easy to avoid it by making `mutually_exclusive_p` just a bit
more careful.

* src/regex-emacs.c (mutually_exclusive_p): Handle `on_failure_jump`s.

* test/src/regex-emacs-tests.el (regexp-tests-backtrack-optimization):
Add a few tests.

src/regex-emacs.c
test/src/regex-emacs-tests.el

index 2dca0d16ad91bdede814557c6e6e4bed16b8315c..2571812cb3981c365de7c02734eed0b78f3abfa0 100644 (file)
@@ -3653,6 +3653,7 @@ mutually_exclusive_p (struct re_pattern_buffer *bufp, re_char *p1,
   re_opcode_t op2;
   bool multibyte = RE_MULTIBYTE_P (bufp);
   unsigned char *pend = bufp->buffer + bufp->used;
+  re_char *p2_orig = p2;
 
   eassert (p1 >= bufp->buffer && p1 < pend
           && p2 >= bufp->buffer && p2 <= pend);
@@ -3822,6 +3823,23 @@ mutually_exclusive_p (struct re_pattern_buffer *bufp, re_char *p1,
     case notcategoryspec:
       return ((re_opcode_t) *p1 == categoryspec && p1[1] == p2[1]);
 
+    case on_failure_jump_nastyloop:
+    case on_failure_jump_smart:
+    case on_failure_jump_loop:
+    case on_failure_keep_string_jump:
+    case on_failure_jump:
+      {
+        int mcnt;
+       p2++;
+       EXTRACT_NUMBER_AND_INCR (mcnt, p2);
+       /* Don't just test `mcnt > 0` because non-greedy loops have
+          their test at the end with an unconditional jump at the start.  */
+       if (p2 + mcnt > p2_orig) /* Ensure forward progress.  */
+         return (mutually_exclusive_p (bufp, p1, p2)
+                 && mutually_exclusive_p (bufp, p1, p2 + mcnt));
+       break;
+      }
+
     default:
       ;
     }
index cd4924f978569eb1ff111844f74090402dc8e439..c8f161c9b24ce44f515a0879cb918de3257f4331 100644 (file)
@@ -872,4 +872,15 @@ This evaluates the TESTS test cases from glibc."
   (should (equal (string-match "\\`\\(?:ab\\)*\\'" "a") nil))
   (should (equal (string-match "\\`a\\{2\\}*\\'" "a") nil)))
 
+(ert-deftest regexp-tests-backtrack-optimization () ;bug#61514
+  ;; Make sure we don't use up the regexp stack needlessly.
+  (with-current-buffer (get-buffer-create "*bug*")
+    (erase-buffer)
+    (insert (make-string 1000000 ?x) "=")
+    (goto-char (point-min))
+    (should (looking-at "x*=*"))
+    (should (looking-at "x*\\(=\\|:\\)"))
+    (should (looking-at "x*\\(=\\|:\\)*"))
+    (should (looking-at "x*=*?"))))
+
 ;;; regex-emacs-tests.el ends here