From afe1b03fc2a74844f6c29050fc045031c0f2ef72 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 6 Jul 2026 08:22:00 +0300 Subject: [PATCH] gh-153169: Look through marks in the regex alternation quick check The BRANCH quick check skips an alternative whose first literal or character set cannot match the current character, but only when it is the very first op, so alternatives starting with a capturing group or with a repeat with a nonzero minimum were never skipped. Look through MARK ops and into the repeated item. Co-Authored-By: Claude Opus 4.8 --- ...-07-06-18-00-00.gh-issue-153169.QkChk1.rst | 4 +++ Modules/_sre/sre_lib.h | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-07-06-18-00-00.gh-issue-153169.QkChk1.rst diff --git a/Misc/NEWS.d/next/Library/2026-07-06-18-00-00.gh-issue-153169.QkChk1.rst b/Misc/NEWS.d/next/Library/2026-07-06-18-00-00.gh-issue-153169.QkChk1.rst new file mode 100644 index 00000000000000..92b7c7884fb753 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-06-18-00-00.gh-issue-153169.QkChk1.rst @@ -0,0 +1,4 @@ +Speed up matching of regular expression alternations whose alternatives +start with a capturing group or with a repeat with a nonzero minimum: +such alternatives can now be skipped without entering them when their +first character cannot match. diff --git a/Modules/_sre/sre_lib.h b/Modules/_sre/sre_lib.h index 6e6ae46f05a50f..21fede231c5f48 100644 --- a/Modules/_sre/sre_lib.h +++ b/Modules/_sre/sre_lib.h @@ -875,6 +875,42 @@ SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel) !SRE(charset)(state, pattern + 3, (SRE_CODE) *ptr))) continue; + if (pattern[1] == SRE_OP_MARK) { + /* the quick checks above also apply behind group marks + and to a repeat of one item with a nonzero minimum */ + const SRE_CODE *alt = pattern + 1; + do { + alt += 2; + } while (alt[0] == SRE_OP_MARK); + if ((alt[0] == SRE_OP_REPEAT_ONE || + alt[0] == SRE_OP_MIN_REPEAT_ONE || + alt[0] == SRE_OP_POSSESSIVE_REPEAT_ONE) && + alt[2] > 0) + alt += 4; + if (alt[0] == SRE_OP_LITERAL && + (ptr >= end || + (SRE_CODE) *ptr != alt[1])) + continue; + if (alt[0] == SRE_OP_IN && + (ptr >= end || + !SRE(charset)(state, alt + 2, + (SRE_CODE) *ptr))) + continue; + } + else if ((pattern[1] == SRE_OP_REPEAT_ONE || + pattern[1] == SRE_OP_MIN_REPEAT_ONE || + pattern[1] == SRE_OP_POSSESSIVE_REPEAT_ONE) && + pattern[3] > 0) { + if (pattern[5] == SRE_OP_LITERAL && + (ptr >= end || + (SRE_CODE) *ptr != pattern[6])) + continue; + if (pattern[5] == SRE_OP_IN && + (ptr >= end || + !SRE(charset)(state, pattern + 7, + (SRE_CODE) *ptr))) + continue; + } state->ptr = ptr; DO_JUMP(JUMP_BRANCH, jump_branch, pattern+1); if (ret) {