From 28624be9f9cd90f9017686a6017eff3f1468c253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Mon, 1 Jun 2026 09:11:19 -0700 Subject: [PATCH] gh-150717: Skip mark array allocation for groupless patterns state_init() always did PyMem_New(state->mark, groups*2), which for a pattern with no capturing groups is PyMem_Malloc(0) -- a real allocation (plus matching free) on every match/search/fullmatch call, for an array that is never read: groupless patterns emit no MARK opcodes and group 0's span is taken from state->start/ptr. Guard the allocation with `if (pattern->groups)`. state->mark stays NULL (set by the preceding memset), and both the error path and state_fini already PyMem_Free(NULL) safely. --- .../2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst | 2 ++ Modules/_sre/sre.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst b/Misc/NEWS.d/next/Library/2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst new file mode 100644 index 00000000000000..da7171fcc72ce4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-01-08-12-34.gh-issue-150717.LVRJXH.rst @@ -0,0 +1,2 @@ +Avoid an unnecessary per-call memory allocation when matching :mod:`re` +patterns that have no capturing groups. Patch by Bernát Gábor. diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 7a07ed1d7aca20..058a03148c823f 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -548,10 +548,16 @@ state_init(SRE_STATE* state, PatternObject* pattern, PyObject* string, memset(state, 0, sizeof(SRE_STATE)); - state->mark = PyMem_New(const void *, pattern->groups * 2); - if (!state->mark) { - PyErr_NoMemory(); - goto err; + /* Patterns with no capturing groups never emit MARK opcodes and never + read state->mark (group 0's span comes from state->start/ptr), so skip + the allocation entirely -- state->mark stays NULL, which both the err + path and state_fini already free safely. */ + if (pattern->groups) { + state->mark = PyMem_New(const void *, pattern->groups * 2); + if (!state->mark) { + PyErr_NoMemory(); + goto err; + } } state->lastmark = -1; state->lastindex = -1;