mirror of
https://github.com/gentoo-mirror/gentoo.git
synced 2026-08-31 09:08:08 -07:00
403 lines
12 KiB
Diff
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
|
|
|
|
|