Files
gentoo/sys-apps/gawk/files/gawk-5.4.1-update-minrx.patch
2026-08-14 03:52:52 +01:00

403 lines
12 KiB
Diff

https://github.com/mikehaertel/minrx/pull/68
https://cgit.git.savannah.gnu.org/cgit/gawk.git/commit/?h=gawk-5.4-stable&id=1a61f93cd203dca8b0bc94f04a3eff80effae152
From 1a61f93cd203dca8b0bc94f04a3eff80effae152 Mon Sep 17 00:00:00 2001
From: "Arnold D. Robbins" <arnold@skeeve.com>
Date: Sat, 8 Aug 2026 22:24:22 +0300
Subject: Update minrx.c from upstream: memory management improvements.
---
support/minrx.c | 201 ++++++++++++++++++++++++++++++++++++++++++------------
4 files changed, 174 insertions(+), 42 deletions(-)
diff --git a/support/minrx.c b/support/minrx.c
index 1e03fb99..5e268213 100644
--- a/support/minrx.c
+++ b/support/minrx.c
@@ -185,15 +185,6 @@ cowvec_allocator_construct(COWVec_Allocator *a, size_t length)
a->length = length;
}
-static void
-cowvec_allocator_destruct(COWVec_Allocator *a)
-{
- for (COWVec_Storage *s = a->freelist, *sfreelink = (COWVec_Storage *) NULL; s != (COWVec_Storage *) NULL; s = sfreelink) {
- sfreelink = s->u.freelink;
- free(s);
- }
-}
-
static size_t
cowvec_storage_get(const COWVec_Storage *cvs, size_t i)
{
@@ -1272,6 +1263,28 @@ nodelist_empty(void)
return r;
}
+// Scratch space for an execution, kept with the compiled Regexp so that
+// the next execution can have it again. Everything in it is sized from
+// the Regexp alone, so it need only be built once; building it afresh
+// for every execution otherwise dominates the cost of matching a short
+// subject.
+//
+// Only one execution can have it at a time, so an execution that finds
+// it taken -- a concurrent or a nested one -- falls back to its own.
+typedef struct Scratch Scratch;
+struct Scratch {
+ bool ready;
+#ifdef HAVE_PTHREADS
+ pthread_mutex_t inuse;
+#else
+ bool inuse;
+#endif
+ COWVec_Storage *freelist;
+ QSet epsq;
+ QVec epsv;
+ QVec mcsv[2];
+};
+
typedef struct Regexp Regexp;
struct Regexp {
WConv_Encoding enc;
@@ -1287,8 +1300,89 @@ struct Regexp {
FirstBytes firstbytes;
int32_t firstunique;
bool anchored;
+ Scratch scratch;
};
+static bool
+scratch_construct(Scratch *sc, size_t nnode)
+{
+ if (sc->ready)
+ return true;
+ if (!qset_construct(&sc->epsq, nnode)
+ || !qvec_construct(&sc->epsv, nnode)
+ || !qvec_construct(&sc->mcsv[0], nnode)
+ || !qvec_construct(&sc->mcsv[1], nnode)) {
+ qvec_destruct(&sc->mcsv[1]);
+ qvec_destruct(&sc->mcsv[0]);
+ qvec_destruct(&sc->epsv);
+ qset_destruct(&sc->epsq);
+ return false;
+ }
+ sc->ready = true;
+ return true;
+}
+
+static void
+scratch_destruct(Scratch *sc)
+{
+ if (sc->ready) {
+ qvec_destruct(&sc->mcsv[1]);
+ qvec_destruct(&sc->mcsv[0]);
+ qvec_destruct(&sc->epsv);
+ qset_destruct(&sc->epsq);
+ sc->ready = false;
+ }
+ for (COWVec_Storage *s = sc->freelist, *next = (COWVec_Storage *) NULL; s != (COWVec_Storage *) NULL; s = next) {
+ next = s->u.freelink;
+ free(s);
+ }
+ sc->freelist = (COWVec_Storage *) NULL;
+}
+
+// Claim the scratch space for an execution, or report it already taken.
+static bool
+scratch_acquire(Scratch *sc)
+{
+#ifdef HAVE_PTHREADS
+ return pthread_mutex_trylock(&sc->inuse) == 0;
+#else
+ if (sc->inuse)
+ return false;
+ sc->inuse = true;
+ return true;
+#endif
+}
+
+static void
+scratch_release(Scratch *sc)
+{
+#ifdef HAVE_PTHREADS
+ pthread_mutex_unlock(&sc->inuse);
+#else
+ sc->inuse = false;
+#endif
+}
+
+// For the Regexp's own scratch space, as opposed to the one an execution
+// falls back to, which is private to it and needs neither of these.
+static void
+scratch_init(Scratch *sc)
+{
+ memset(sc, 0, sizeof *sc);
+#ifdef HAVE_PTHREADS
+ pthread_mutex_init(&sc->inuse, (const pthread_mutexattr_t *) NULL);
+#endif
+}
+
+static void
+scratch_fini(Scratch *sc)
+{
+ scratch_destruct(sc);
+#ifdef HAVE_PTHREADS
+ pthread_mutex_destroy(&sc->inuse);
+#endif
+}
+
static NInt
satmul(NInt x, NInt y)
{
@@ -1879,6 +1973,7 @@ compile(Compile *c)
memset(&r->firstbytes, 0, sizeof r->firstbytes);
r->firstunique = -1;
r->anchored = false;
+ scratch_init(&r->scratch);
int err;
if ((err = setjmp(c->errjmp)) != 0) {
c = vc, r = vr;
@@ -1944,13 +2039,16 @@ struct Execute {
COWVec_Allocator allocator;
COWVec best;
NInt bestmincount; // note mincounts are negated so this means +infinity
- QSet epsq;
- QVec epsv;
+ Scratch *scratch; // the scratch space this execution is using
+ QSet *epsq; // aliases of the members of *scratch, cached
+ QVec *epsv; // here to save an indirection on every use
+ QVec *mcsv;
+ Scratch own; // used where the Regexp's is already taken
bool exiting;
};
static bool
-execute_construct(Execute *e, const Regexp *r, minrx_regexec_flags_t flags, const char *bp, const char *ep)
+execute_construct(Execute *e, Regexp *r, minrx_regexec_flags_t flags, const char *bp, const char *ep)
{
e->r = r;
e->flags = flags;
@@ -1965,21 +2063,47 @@ execute_construct(Execute *e, const Regexp *r, minrx_regexec_flags_t flags, cons
cowvec_allocator_construct(&e->allocator, e->nestoff + r->nmin);
cowvec_construct(&e->best, (COWVec_Allocator *) NULL);
e->bestmincount = 0;
- if (!qset_construct(&e->epsq, r->nnode) || !qvec_construct(&e->epsv, r->nnode)) {
- qset_destruct(&e->epsq);
+ if (scratch_acquire(&r->scratch)) {
+ e->scratch = &r->scratch;
+ } else {
+ // Private to this execution, so only what scratch_construct()
+ // reads needs setting here; it fills in the rest.
+ e->own.ready = false;
+ e->own.freelist = (COWVec_Storage *) NULL;
+ e->scratch = &e->own;
+ }
+ if (!scratch_construct(e->scratch, r->nnode)) {
+ if (e->scratch != &e->own)
+ scratch_release(e->scratch);
return false;
}
+ e->epsq = &e->scratch->epsq;
+ e->epsv = &e->scratch->epsv;
+ e->mcsv = e->scratch->mcsv;
+ e->allocator.freelist = e->scratch->freelist;
e->exiting = r->anchored;
return true;
}
+// 'reusable' is false where the execution has failed part way through and
+// may have left the scratch space in a state the next one, which would
+// otherwise inherit it, should not build upon.
static void
-execute_destruct(Execute *e)
+execute_destruct(Execute *e, bool reusable)
{
- qvec_destruct(&e->epsv);
- qset_destruct(&e->epsq);
+ Scratch *sc = e->scratch;
cowvec_destruct(&e->best);
- cowvec_allocator_destruct(&e->allocator);
+ // Drain, but keep the storage for the next execution.
+ qvec_clear(&e->mcsv[1]);
+ qvec_clear(&e->mcsv[0]);
+ qvec_clear(e->epsv);
+ while (!qset_empty(e->epsq))
+ (void) qset_remove(e->epsq);
+ sc->freelist = e->allocator.freelist;
+ if (!reusable || sc == &e->own)
+ scratch_destruct(sc);
+ if (sc != &e->own)
+ scratch_release(sc);
}
inline static void
@@ -2001,7 +2125,7 @@ execute_add(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WChar
qvi.newnsp->gen = e->gen;
}
} else {
- QVecInsert qvi = qvec_insert(&e->epsv, k, nsp);
+ QVecInsert qvi = qvec_insert(e->epsv, k, nsp);
if (qvi.newly) {
nstate_construct_copy(qvi.newnsp, nsp);
} else {
@@ -2012,7 +2136,7 @@ execute_add(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WChar
return;
}
qvi.newnsp->gen = e->gen;
- qset_insert(&e->epsq, k);
+ qset_insert(e->epsq, k);
}
}
@@ -2036,7 +2160,7 @@ execute_add_1(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WCha
cowvec_put(&qvi.newnsp->substack, nstk - 1, arg1);
}
} else {
- QVecInsert qvi = qvec_insert(&e->epsv, k, nsp);
+ QVecInsert qvi = qvec_insert(e->epsv, k, nsp);
if (qvi.newly) {
nstate_construct_copy(qvi.newnsp, nsp);
} else {
@@ -2048,7 +2172,7 @@ execute_add_1(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WCha
}
qvi.newnsp->gen = e->gen;
cowvec_put(&qvi.newnsp->substack, nstk - 1, arg1);
- qset_insert(&e->epsq, k);
+ qset_insert(e->epsq, k);
}
}
@@ -2073,7 +2197,7 @@ execute_add_2(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WCha
cowvec_put(&qvi.newnsp->substack, nstk - 1, arg2);
}
} else {
- QVecInsert qvi = qvec_insert(&e->epsv, k, nsp);
+ QVecInsert qvi = qvec_insert(e->epsv, k, nsp);
if (qvi.newly) {
nstate_construct_copy(qvi.newnsp, nsp);
} else {
@@ -2086,7 +2210,7 @@ execute_add_2(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WCha
qvi.newnsp->gen = e->gen;
cowvec_put(&qvi.newnsp->substack, nstk - 2, arg1);
cowvec_put(&qvi.newnsp->substack, nstk - 1, arg2);
- qset_insert(&e->epsq, k);
+ qset_insert(e->epsq, k);
}
}
@@ -2112,7 +2236,7 @@ execute_add_3(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WCha
cowvec_put(&qvi.newnsp->substack, nstk - 1, arg3);
}
} else {
- QVecInsert qvi = qvec_insert(&e->epsv, k, nsp);
+ QVecInsert qvi = qvec_insert(e->epsv, k, nsp);
if (qvi.newly) {
nstate_construct_copy(qvi.newnsp, nsp);
} else {
@@ -2126,7 +2250,7 @@ execute_add_3(Execute *e, QVec *ncsv, NInt k, NInt nstk, const NState *nsp, WCha
cowvec_put(&qvi.newnsp->substack, nstk - 3, arg1);
cowvec_put(&qvi.newnsp->substack, nstk - 2, arg2);
cowvec_put(&qvi.newnsp->substack, nstk - 1, arg3);
- qset_insert(&e->epsq, k);
+ qset_insert(e->epsq, k);
}
}
@@ -2148,8 +2272,8 @@ execute_epsclosure(Execute *e, QVec *ncsv, WChar wcnext)
const Node *nodes = e->nodes;
bool (*is_word)(WChar) = e->r->enc == Byte ? is_word_byte : is_word_wide;
do {
- NInt k = qset_remove(&e->epsq);
- NState *nsp = qvec_lookup(&e->epsv, k);
+ NInt k = qset_remove(e->epsq);
+ NState *nsp = qvec_lookup(e->epsv, k);
if (cowvec_valid(&e->best) && nsp->boff > cowvec_get(&e->best, e->suboff + 0))
continue;
const Node *np = &e->nodes[k];
@@ -2305,7 +2429,7 @@ execute_epsclosure(Execute *e, QVec *ncsv, WChar wcnext)
abort();
break;
}
- } while (!qset_empty(&e->epsq));
+ } while (!qset_empty(e->epsq));
}
#define WCNEXT(E, WCN) ((E)->wcprev = (WCN), (E)->off = wconv_off(&(E)->wconv), (WCN) = wconv_nextchr(&(E)->wconv))
@@ -2313,18 +2437,12 @@ execute_epsclosure(Execute *e, QVec *ncsv, WChar wcnext)
static int
execute(Execute *e, size_t nm, minrx_regmatch_t *rm)
{
- QVec mcsvs[2];
- if (!qvec_construct(&mcsvs[0], e->r->nnode) || !qvec_construct(&mcsvs[1], e->r->nnode)) {
- qvec_destruct(&mcsvs[0]);
- return MINRX_REG_ESPACE;
- }
+ QVec *mcsvs = e->mcsv;
NState nsinit;
nstate_construct(&nsinit, (COWVec_Allocator *) NULL); // fake construction so "exception handler" will be safe
int err;
if ((err = setjmp(e->allocator.errjmp)) != 0) {
nstate_destruct(&nsinit);
- qvec_destruct(&mcsvs[1]);
- qvec_destruct(&mcsvs[0]);
return err;
}
nstate_construct(&nsinit, &e->allocator); // real construction
@@ -2381,7 +2499,7 @@ execute(Execute *e, size_t nm, minrx_regmatch_t *rm)
for (size_t i = 0; i < e->r->nmin; ++i)
cowvec_put(&nsinit.substack, e->nestoff + i, 0);
execute_add(e, &mcsvs[0], 0, 0, &nsinit, wcnext);
- if (!qset_empty(&e->epsq))
+ if (!qset_empty(e->epsq))
execute_epsclosure(e, &mcsvs[0], wcnext);
for (;;) { // unrolled to ping-pong roles of mcsvs[0]/[1]
if (wcnext == End)
@@ -2396,7 +2514,7 @@ execute(Execute *e, size_t nm, minrx_regmatch_t *rm)
nsinit.boff = e->off;
execute_add(e, &mcsvs[1], 0, 0, &nsinit, wcnext);
}
- if (!qset_empty(&e->epsq))
+ if (!qset_empty(e->epsq))
execute_epsclosure(e, &mcsvs[1], wcnext);
if (qvec_empty(&mcsvs[1])) {
if (e->exiting)
@@ -2416,7 +2534,7 @@ execute(Execute *e, size_t nm, minrx_regmatch_t *rm)
nsinit.boff = e->off;
execute_add(e, &mcsvs[0], 0, 0, &nsinit, wcnext);
}
- if (!qset_empty(&e->epsq))
+ if (!qset_empty(e->epsq))
execute_epsclosure(e, &mcsvs[0], wcnext);
if (qvec_empty(&mcsvs[0])) {
if (e->exiting)
@@ -2427,8 +2545,6 @@ execute(Execute *e, size_t nm, minrx_regmatch_t *rm)
}
exit:
nstate_destruct(&nsinit);
- qvec_destruct(&mcsvs[1]);
- qvec_destruct(&mcsvs[0]);
if (cowvec_valid(&e->best)) {
if (rm) {
size_t nsub = MIN(nm, e->r->nsub);
@@ -2504,7 +2620,7 @@ minrx_regnexec(minrx_regex_t *rx, size_t ns, const char *s, size_t nm, minrx_reg
if (!execute_construct(&e, r, (minrx_regexec_flags_t) flags, s, s + ns))
return MINRX_REG_ESPACE;
int ret = execute(&e, nm, rm);
- execute_destruct(&e);
+ execute_destruct(&e, ret == MINRX_REG_SUCCESS || ret == MINRX_REG_NOMATCH);
return ret;
}
@@ -2513,6 +2629,7 @@ minrx_regfree(minrx_regex_t *rx)
{
Regexp *r = (Regexp *) rx->re_regexp;
if (r) {
+ scratch_fini(&r->scratch);
if (r->firstcset) {
cset_destruct(r->firstcset);
free((void *) r->firstcset);
--
cgit v1.3