0006-reduce-constant-factor-for-lacons.patch
text/x-diff
Filename: 0006-reduce-constant-factor-for-lacons.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
Series: patch 0006
| File | + | − |
|---|---|---|
| src/backend/regex/regc_nfa.c | 125 | 36 |
| src/backend/regex/rege_dfa.c | 19 | 12 |
| src/backend/regex/regexec.c | 1 | 1 |
| src/backend/regex/regexport.c | 17 | 11 |
| src/backend/regex/regprefix.c | 36 | 9 |
| src/include/regex/regguts.h | 14 | 13 |
diff --git a/src/backend/regex/regc_nfa.c b/src/backend/regex/regc_nfa.c
index 69929eddfc..3da394b9a5 100644
--- a/src/backend/regex/regc_nfa.c
+++ b/src/backend/regex/regc_nfa.c
@@ -3182,81 +3182,145 @@ compact(struct nfa *nfa,
{
struct state *s;
struct arc *a;
- size_t nstates;
- size_t narcs;
- struct carc *ca;
- struct carc *first;
+ size_t nstates = 0;
+ size_t nlastates = 0;
+ size_t nplainarcs = 0;
+ size_t nlaarcs = 0;
+ struct carc *cpa;
+ struct carc *cla;
assert(!NISERR());
- nstates = 0;
- narcs = 0;
for (s = nfa->states; s != NULL; s = s->next)
{
+ size_t nthislaarcs = 0;
+
nstates++;
- narcs += s->nouts + 1; /* need one extra for endmarker */
+ for (a = s->outs; a != NULL; a = a->outchain)
+ {
+ switch (a->type)
+ {
+ case PLAIN:
+ nplainarcs++;
+ break;
+ case LACON:
+ nthislaarcs++;
+ break;
+ default:
+ NERR(REG_ASSERT);
+ return;
+ }
+ }
+ nplainarcs++; /* each state needs an endmarker arc */
+ if (nthislaarcs > 0)
+ {
+ nlastates++;
+ nlaarcs += nthislaarcs + 1; /* endmarker needed */
+ }
}
cnfa->stflags = (char *) MALLOC(nstates * sizeof(char));
- cnfa->states = (struct carc **) MALLOC(nstates * sizeof(struct carc *));
- cnfa->arcs = (struct carc *) MALLOC(narcs * sizeof(struct carc));
- if (cnfa->stflags == NULL || cnfa->states == NULL || cnfa->arcs == NULL)
+ cnfa->firstarc = (struct carc **) MALLOC(nstates * sizeof(struct carc *));
+ cnfa->arcs = (struct carc *)
+ MALLOC((nplainarcs + nlaarcs) * sizeof(struct carc));
+ if (nlastates > 0)
+ {
+ cnfa->lastates = (int *) MALLOC(nlastates * sizeof(int));
+ cnfa->firstlaarc = (struct carc **)
+ MALLOC(nlastates * sizeof(struct carc *));
+ }
+ else
+ {
+ cnfa->lastates = NULL;
+ cnfa->firstlaarc = NULL;
+ }
+ if (cnfa->stflags == NULL || cnfa->firstarc == NULL || cnfa->arcs == NULL ||
+ (nlastates > 0 && (cnfa->lastates == NULL || cnfa->firstlaarc == NULL)))
{
if (cnfa->stflags != NULL)
FREE(cnfa->stflags);
- if (cnfa->states != NULL)
- FREE(cnfa->states);
+ if (cnfa->firstarc != NULL)
+ FREE(cnfa->firstarc);
if (cnfa->arcs != NULL)
FREE(cnfa->arcs);
+ if (cnfa->lastates != NULL)
+ FREE(cnfa->lastates);
+ if (cnfa->firstlaarc != NULL)
+ FREE(cnfa->firstlaarc);
NERR(REG_ESPACE);
return;
}
+ assert(nstates > 0);
cnfa->nstates = nstates;
+ cnfa->ncolors = maxcolor(nfa->cm) + 1;
+ cnfa->flags = nfa->flags;
+ if (nlastates > 0)
+ cnfa->flags |= HASLACONS;
+ cnfa->nlastates = 0; /* will set below */
cnfa->pre = nfa->pre->no;
cnfa->post = nfa->post->no;
cnfa->bos[0] = nfa->bos[0];
cnfa->bos[1] = nfa->bos[1];
cnfa->eos[0] = nfa->eos[0];
cnfa->eos[1] = nfa->eos[1];
- cnfa->ncolors = maxcolor(nfa->cm) + 1;
- cnfa->flags = nfa->flags;
cnfa->minmatchall = nfa->minmatchall;
cnfa->maxmatchall = nfa->maxmatchall;
- ca = cnfa->arcs;
+ cpa = cnfa->arcs; /* where to store plain arcs */
+ cla = cnfa->arcs + nplainarcs; /* where to store LA arcs */
for (s = nfa->states; s != NULL; s = s->next)
{
+ struct carc *firstp = cpa;
+ struct carc *firstla = cla;
+
assert((size_t) s->no < nstates);
cnfa->stflags[s->no] = 0;
- cnfa->states[s->no] = ca;
- first = ca;
+ cnfa->firstarc[s->no] = cpa;
for (a = s->outs; a != NULL; a = a->outchain)
+ {
switch (a->type)
{
case PLAIN:
- ca->co = a->co;
- ca->to = a->to->no;
- ca++;
+ cpa->co = a->co;
+ cpa->to = a->to->no;
+ cpa++;
break;
case LACON:
assert(s->no != cnfa->pre);
assert(a->co >= 0);
- ca->co = (color) (cnfa->ncolors + a->co);
- ca->to = a->to->no;
- ca++;
- cnfa->flags |= HASLACONS;
+ if (cla == firstla)
+ {
+ cnfa->lastates[cnfa->nlastates] = s->no;
+ cnfa->firstlaarc[cnfa->nlastates] = cla;
+ cnfa->nlastates++;
+ }
+ cla->co = a->co;
+ cla->to = a->to->no;
+ cla++;
break;
default:
NERR(REG_ASSERT);
return;
}
- carcsort(first, ca - first);
- ca->co = COLORLESS;
- ca->to = 0;
- ca++;
+ }
+ /* sort plain arcs, just for neatness */
+ carcsort(firstp, cpa - firstp);
+ /* add endmarker for plain arcs */
+ cpa->co = COLORLESS;
+ cpa->to = 0;
+ cpa++;
+ /* and the same for LA arcs, if this state has any */
+ if (cla != firstla)
+ {
+ carcsort(firstla, cla - firstla);
+ cla->co = COLORLESS;
+ cla->to = 0;
+ cla++;
+ }
}
- assert(ca == &cnfa->arcs[narcs]);
- assert(cnfa->nstates != 0);
+ assert(cpa == &cnfa->arcs[nplainarcs]);
+ assert(cla == &cnfa->arcs[nplainarcs + nlaarcs]);
+ assert(nlastates == cnfa->nlastates);
/* mark no-progress states */
for (a = nfa->pre->outs; a != NULL; a = a->outchain)
@@ -3299,7 +3363,11 @@ freecnfa(struct cnfa *cnfa)
{
assert(!NULLCNFA(*cnfa)); /* not empty already */
FREE(cnfa->stflags);
- FREE(cnfa->states);
+ FREE(cnfa->firstarc);
+ if (cnfa->lastates != NULL)
+ FREE(cnfa->lastates);
+ if (cnfa->firstlaarc != NULL)
+ FREE(cnfa->firstlaarc);
FREE(cnfa->arcs);
ZAPCNFA(*cnfa);
}
@@ -3518,18 +3586,19 @@ dumpcstate(int st,
FILE *f)
{
struct carc *ca;
+ bool hasarc = false;
int pos;
+ int i;
fprintf(f, "%d%s", st, (cnfa->stflags[st] & CNFA_NOPROGRESS) ? ":" : ".");
pos = 1;
- for (ca = cnfa->states[st]; ca->co != COLORLESS; ca++)
+ /* Dump its plain out-arcs */
+ for (ca = cnfa->firstarc[st]; ca->co != COLORLESS; ca++)
{
if (ca->co == RAINBOW)
fprintf(f, "\t[*]->%d", ca->to);
- else if (ca->co < cnfa->ncolors)
- fprintf(f, "\t[%ld]->%d", (long) ca->co, ca->to);
else
- fprintf(f, "\t:%ld:->%d", (long) (ca->co - cnfa->ncolors), ca->to);
+ fprintf(f, "\t[%ld]->%d", (long) ca->co, ca->to);
if (pos == 5)
{
fprintf(f, "\n");
@@ -3537,8 +3606,28 @@ dumpcstate(int st,
}
else
pos++;
+ hasarc = true;
+ }
+ /* Dump its LACON out-arcs, if any */
+ for (i = 0; i < cnfa->nlastates; i++)
+ {
+ if (cnfa->lastates[i] != st)
+ continue;
+ for (ca = cnfa->firstlaarc[i]; ca->co != COLORLESS; ca++)
+ {
+ fprintf(f, "\t:%ld:->%d", (long) ca->co, ca->to);
+ if (pos == 5)
+ {
+ fprintf(f, "\n");
+ pos = 1;
+ }
+ else
+ pos++;
+ hasarc = true;
+ }
+ break; /* needn't look through rest of lastates[] */
}
- if (ca == cnfa->states[st] || pos != 1)
+ if (pos != 1 || !hasarc)
fprintf(f, "\n");
fflush(f);
}
diff --git a/src/backend/regex/rege_dfa.c b/src/backend/regex/rege_dfa.c
index 1db52d1005..ad2c98bd91 100644
--- a/src/backend/regex/rege_dfa.c
+++ b/src/backend/regex/rege_dfa.c
@@ -677,6 +677,8 @@ miss(struct vars *v,
int gotstate;
int dolacons;
int sawlacons;
+ color lastlacon;
+ int lastlaresult;
/* for convenience, we can be called even if it might not be a miss */
if (css->outs[co] != NULL)
@@ -709,7 +711,7 @@ miss(struct vars *v,
gotstate = 0;
for (i = 0; i < d->nstates; i++)
if (ISBSET(css->states, i))
- for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++)
+ for (ca = cnfa->firstarc[i]; ca->co != COLORLESS; ca++)
if (ca->co == co ||
(ca->co == RAINBOW && !ispseudocolor))
{
@@ -725,27 +727,33 @@ miss(struct vars *v,
return NULL; /* character cannot reach any new state */
dolacons = (cnfa->flags & HASLACONS);
sawlacons = 0;
+ lastlacon = COLORLESS;
+ lastlaresult = 0; /* doesn't matter */
/* outer loop handles transitive closure of reachable-by-LACON states */
while (dolacons)
{
+ int j;
+
dolacons = 0;
- for (i = 0; i < d->nstates; i++)
+ for (j = 0; j < cnfa->nlastates; j++)
+ {
+ i = cnfa->lastates[j];
if (ISBSET(d->work, i))
- for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++)
+ for (ca = cnfa->firstlaarc[j]; ca->co != COLORLESS; ca++)
{
- if (ca->co < cnfa->ncolors)
- continue; /* not a LACON arc */
if (ISBSET(d->work, ca->to))
continue; /* arc would be a no-op anyway */
sawlacons = 1; /* this LACON affects our result */
- if (!lacon(v, cnfa, cp, ca->co))
+ /* we might have just eval'd this same LACON */
+ if (ca->co != lastlacon)
{
+ lastlacon = ca->co;
+ lastlaresult = lacon(v, cnfa, cp, lastlacon);
if (ISERR())
return NULL;
- continue; /* LACON arc cannot be traversed */
}
- if (ISERR())
- return NULL;
+ if (!lastlaresult)
+ continue; /* LACON arc cannot be traversed */
BSET(d->work, ca->to);
dolacons = 1;
if (ca->to == cnfa->post)
@@ -754,6 +762,7 @@ miss(struct vars *v,
noprogress = 0;
FDEBUG(("%d :> %d\n", i, ca->to));
}
+ }
}
h = HASH(d->work, d->wordsper);
@@ -805,9 +814,8 @@ static int /* predicate: constraint satisfied? */
lacon(struct vars *v,
struct cnfa *pcnfa, /* parent cnfa */
chr *cp,
- color co) /* "color" of the lookaround constraint */
+ int n) /* index of the lookaround constraint */
{
- int n;
struct subre *sub;
struct dfa *d;
chr *end;
@@ -820,7 +828,6 @@ lacon(struct vars *v,
return 0;
}
- n = co - pcnfa->ncolors;
assert(n > 0 && n < v->g->nlacons && v->g->lacons != NULL);
FDEBUG(("=== testing lacon %d\n", n));
sub = &v->g->lacons[n];
diff --git a/src/backend/regex/regexec.c b/src/backend/regex/regexec.c
index cf95989948..69acc285f7 100644
--- a/src/backend/regex/regexec.c
+++ b/src/backend/regex/regexec.c
@@ -160,7 +160,7 @@ static void freedfa(struct dfa *);
static unsigned hash(unsigned *, int);
static struct sset *initialize(struct vars *, struct dfa *, chr *);
static struct sset *miss(struct vars *, struct dfa *, struct sset *, color, chr *, chr *);
-static int lacon(struct vars *, struct cnfa *, chr *, color);
+static int lacon(struct vars *, struct cnfa *, chr *, int);
static struct sset *getvacant(struct vars *, struct dfa *, chr *, chr *);
static struct sset *pickss(struct vars *, struct dfa *, chr *, chr *);
diff --git a/src/backend/regex/regexport.c b/src/backend/regex/regexport.c
index a493dbe88c..964a017f84 100644
--- a/src/backend/regex/regexport.c
+++ b/src/backend/regex/regexport.c
@@ -95,6 +95,7 @@ traverse_lacons(struct cnfa *cnfa, int st,
regex_arc_t *arcs, int arcs_len)
{
struct carc *ca;
+ int i;
/*
* Since this function recurses, it could theoretically be driven to stack
@@ -103,20 +104,24 @@ traverse_lacons(struct cnfa *cnfa, int st,
*/
check_stack_depth();
- for (ca = cnfa->states[st]; ca->co != COLORLESS; ca++)
+ /* Count and possibly emit state's ordinary out-arcs */
+ for (ca = cnfa->firstarc[st]; ca->co != COLORLESS; ca++)
{
- if (ca->co < cnfa->ncolors)
+ int ndx = (*arcs_count)++;
+
+ if (ndx < arcs_len)
{
- /* Ordinary arc, so count and possibly emit it */
- int ndx = (*arcs_count)++;
-
- if (ndx < arcs_len)
- {
- arcs[ndx].co = ca->co;
- arcs[ndx].to = ca->to;
- }
+ arcs[ndx].co = ca->co;
+ arcs[ndx].to = ca->to;
}
- else
+ }
+
+ /* Find state's LACON out-arcs, if any */
+ for (i = 0; i < cnfa->nlastates; i++)
+ {
+ if (cnfa->lastates[i] != st)
+ continue;
+ for (ca = cnfa->firstlaarc[i]; ca->co != COLORLESS; ca++)
{
/* LACON arc --- assume it's satisfied and recurse... */
/* ... but first, assert it doesn't lead directly to post state */
@@ -124,6 +129,7 @@ traverse_lacons(struct cnfa *cnfa, int st,
traverse_lacons(cnfa, ca->to, arcs_count, arcs, arcs_len);
}
+ break; /* needn't look through rest of lastates[] */
}
}
diff --git a/src/backend/regex/regprefix.c b/src/backend/regex/regprefix.c
index ec435b6f5f..31e158000f 100644
--- a/src/backend/regex/regprefix.c
+++ b/src/backend/regex/regprefix.c
@@ -131,7 +131,7 @@ findprefix(struct cnfa *cnfa,
*/
st = cnfa->pre;
nextst = -1;
- for (ca = cnfa->states[st]; ca->co != COLORLESS; ca++)
+ for (ca = cnfa->firstarc[st]; ca->co != COLORLESS; ca++)
{
if (ca->co == cnfa->bos[0] || ca->co == cnfa->bos[1])
{
@@ -146,6 +146,8 @@ findprefix(struct cnfa *cnfa,
if (nextst == -1)
return REG_NOMATCH;
+ /* The "pre" state won't have LACON out-arcs, so don't bother looking */
+
/*
* Scan through successive states, stopping as soon as we find one with
* more than one acceptable transition character (either multiple colors
@@ -164,18 +166,14 @@ findprefix(struct cnfa *cnfa,
st = nextst;
nextst = -1;
thiscolor = COLORLESS;
- for (ca = cnfa->states[st]; ca->co != COLORLESS; ca++)
+ for (ca = cnfa->firstarc[st]; ca->co != COLORLESS; ca++)
{
/* We can ignore BOS/BOL arcs */
if (ca->co == cnfa->bos[0] || ca->co == cnfa->bos[1])
continue;
-
- /*
- * ... but EOS/EOL arcs terminate the search, as do RAINBOW arcs
- * and LACONs
- */
+ /* ... but EOS/EOL arcs terminate the search, as do RAINBOW arcs */
if (ca->co == cnfa->eos[0] || ca->co == cnfa->eos[1] ||
- ca->co == RAINBOW || ca->co >= cnfa->ncolors)
+ ca->co == RAINBOW)
{
thiscolor = COLORLESS;
break;
@@ -198,6 +196,21 @@ findprefix(struct cnfa *cnfa,
break;
}
}
+ /* If good so far, check to see if state has LACON outarcs */
+ if (thiscolor != COLORLESS)
+ {
+ int i;
+
+ for (i = 0; i < cnfa->nlastates; i++)
+ {
+ if (cnfa->lastates[i] == st)
+ {
+ /* Yes, so terminate the search */
+ thiscolor = COLORLESS;
+ break;
+ }
+ }
+ }
/* Done if we didn't find exactly one color on plain outarcs */
if (thiscolor == COLORLESS)
break;
@@ -235,7 +248,7 @@ findprefix(struct cnfa *cnfa,
* even if the string is of zero length.
*/
nextst = -1;
- for (ca = cnfa->states[st]; ca->co != COLORLESS; ca++)
+ for (ca = cnfa->firstarc[st]; ca->co != COLORLESS; ca++)
{
if (ca->co == cnfa->eos[0] || ca->co == cnfa->eos[1])
{
@@ -253,6 +266,20 @@ findprefix(struct cnfa *cnfa,
break;
}
}
+ /* If good so far, check to see if state has LACON outarcs */
+ if (nextst != -1)
+ {
+ int i;
+
+ for (i = 0; i < cnfa->nlastates; i++)
+ {
+ if (cnfa->lastates[i] == st)
+ {
+ nextst = -1;
+ break;
+ }
+ }
+ }
if (nextst == cnfa->post)
return REG_EXACT;
diff --git a/src/include/regex/regguts.h b/src/include/regex/regguts.h
index 306525eb5f..6b3b399b2c 100644
--- a/src/include/regex/regguts.h
+++ b/src/include/regex/regguts.h
@@ -345,17 +345,15 @@ struct nfa
*
* The main space savings in a compacted NFA is from making the arcs as small
* as possible. We store only the transition color and next-state number for
- * each arc. The list of out arcs for each state is an array beginning at
- * cnfa.states[statenumber], and terminated by a dummy carc struct with
- * co == COLORLESS.
+ * each arc. The list of plain out-arcs for each state is an array beginning
+ * at cnfa.firstarc[statenumber], and terminated by a dummy carc struct with
+ * co == COLORLESS. If a state has LACON out-arcs (most don't), then its
+ * state number appears in cnfa.lastates[k] for some k, and its first LACON
+ * out-arc is at cnfa.firstlaarc[k]. That array is likewise terminated by a
+ * dummy carc struct with co == COLORLESS. The "co" values for LACON out-arcs
+ * are lookaround constraint numbers.
*
- * The non-dummy carc structs are of two types: plain arcs and LACON arcs.
- * Plain arcs just store the transition color number as "co". LACON arcs
- * store the lookaround constraint number plus cnfa.ncolors as "co". LACON
- * arcs can be distinguished from plain by testing for co >= cnfa.ncolors.
- *
- * Note that in a plain arc, "co" can be RAINBOW; since that's negative,
- * it doesn't break the rule about how to recognize LACON arcs.
+ * Note that in a plain arc, "co" can be RAINBOW rather than a regular color.
*
* We have special markings for "trivial" NFAs that can match any string
* (possibly with limits on the number of characters therein). In such a
@@ -375,17 +373,20 @@ struct cnfa
{
int nstates; /* number of states */
int ncolors; /* number of colors (max color in use + 1) */
- int flags;
+ int flags; /* bitmask of the following flags: */
#define HASLACONS 01 /* uses lookaround constraints */
#define MATCHALL 02 /* matches all strings of a range of lengths */
+ int nlastates; /* number of states with lookaround outarcs */
int pre; /* setup state number */
int post; /* teardown state number */
color bos[2]; /* colors, if any, assigned to BOS and BOL */
color eos[2]; /* colors, if any, assigned to EOS and EOL */
char *stflags; /* vector of per-state flags bytes */
#define CNFA_NOPROGRESS 01 /* flag bit for a no-progress state */
- struct carc **states; /* vector of pointers to outarc lists */
- /* states[n] are pointers into a single malloc'd array of arcs */
+ struct carc **firstarc; /* per-state pointers to plain outarc lists */
+ int *lastates; /* numbers of states with lookaround outarcs */
+ struct carc **firstlaarc; /* pointers to LA outarcs for such states */
+ /* firstarc[n] & firstlaarc[n] are pointers into a single array of arcs */
struct carc *arcs; /* the area for the lists */
/* these fields are used only in a MATCHALL NFA (else they're -1): */
int minmatchall; /* min number of chrs to match */