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.
--- /dev/null
+Avoid an unnecessary per-call memory allocation when matching :mod:`re`
+patterns that have no capturing groups. Patch by Bernát Gábor.
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;