3 * This file is #included by regexec.c.
5 * Copyright (c) 1998, 1999 Henry Spencer. All rights reserved.
7 * Development of this software was funded, in part, by Cray Research Inc.,
8 * UUNET Communications Services Inc., Sun Microsystems Inc., and Scriptics
9 * Corporation, none of whom are responsible for the results. The author
12 * Redistribution and use in source and binary forms -- with or without
13 * modification -- are permitted for any purpose, provided that
14 * redistributions in source form retain this entire copyright notice and
15 * indicate the origin and nature of any modifications.
17 * I'd appreciate being given credit for this package in the documentation
18 * of software which uses it, but that is not a requirement.
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * HENRY SPENCER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * src/backend/regex/rege_dfa.c
36 * longest - longest-preferred matching engine
38 * On success, returns match endpoint address. Returns NULL on no match.
39 * Internal errors also return NULL, with v->err set.
42 longest(struct vars * v,
44 chr *start, /* where the match should start */
45 chr *stop, /* match must end at or before here */
46 int *hitstopp) /* record whether hit v->stop, if non-NULL */
49 chr *realstop = (stop == v->stop) ? stop : stop + 1;
55 struct colormap *cm = d->cm;
57 /* prevent "uninitialized variable" warnings */
62 css = initialize(v, d, start);
68 FDEBUG(("+++ startup +++\n"));
71 co = d->cnfa->bos[(v->eflags & REG_NOTBOL) ? 0 : 1];
72 FDEBUG(("color %ld\n", (long) co));
76 co = GETCOLOR(cm, *(cp - 1));
77 FDEBUG(("char %c, color %ld\n", (char) *(cp - 1), (long) co));
79 css = miss(v, d, css, co, cp, start);
85 * This is the main text-scanning loop. It seems worth having two copies
86 * to avoid the overhead of REG_FTRACE tests here, even in REG_DEBUG
87 * builds, when you're not actively tracing.
90 if (v->eflags & REG_FTRACE)
94 FDEBUG(("+++ at c%d +++\n", (int) (css - d->ssets)));
95 co = GETCOLOR(cm, *cp);
96 FDEBUG(("char %c, color %ld\n", (char) *cp, (long) co));
100 ss = miss(v, d, css, co, cp + 1, start);
102 break; /* NOTE BREAK OUT */
112 while (cp < realstop)
114 co = GETCOLOR(cm, *cp);
118 ss = miss(v, d, css, co, cp + 1, start);
120 break; /* NOTE BREAK OUT */
132 FDEBUG(("+++ shutdown at c%d +++\n", (int) (css - d->ssets)));
133 if (cp == v->stop && stop == v->stop)
135 if (hitstopp != NULL)
137 co = d->cnfa->eos[(v->eflags & REG_NOTEOL) ? 0 : 1];
138 FDEBUG(("color %ld\n", (long) co));
139 ss = miss(v, d, css, co, cp, start);
142 /* special case: match ended at eol? */
143 if (ss != NULL && (ss->flags & POSTSTATE))
146 ss->lastseen = cp; /* to be tidy */
149 /* find last match, if any */
151 for (ss = d->ssets, i = d->nssused; i > 0; ss++, i--)
152 if ((ss->flags & POSTSTATE) && post != ss->lastseen &&
153 (post == NULL || post < ss->lastseen))
155 if (post != NULL) /* found one */
162 * shortest - shortest-preferred matching engine
164 * On success, returns match endpoint address. Returns NULL on no match.
165 * Internal errors also return NULL, with v->err set.
168 shortest(struct vars * v,
170 chr *start, /* where the match should start */
171 chr *min, /* match must end at or after here */
172 chr *max, /* match must end at or before here */
173 chr **coldp, /* store coldstart pointer here, if non-NULL */
174 int *hitstopp) /* record whether hit v->stop, if non-NULL */
177 chr *realmin = (min == v->stop) ? min : min + 1;
178 chr *realmax = (max == v->stop) ? max : max + 1;
182 struct colormap *cm = d->cm;
184 /* prevent "uninitialized variable" warnings */
187 if (hitstopp != NULL)
191 css = initialize(v, d, start);
197 FDEBUG(("--- startup ---\n"));
200 co = d->cnfa->bos[(v->eflags & REG_NOTBOL) ? 0 : 1];
201 FDEBUG(("color %ld\n", (long) co));
205 co = GETCOLOR(cm, *(cp - 1));
206 FDEBUG(("char %c, color %ld\n", (char) *(cp - 1), (long) co));
208 css = miss(v, d, css, co, cp, start);
215 * This is the main text-scanning loop. It seems worth having two copies
216 * to avoid the overhead of REG_FTRACE tests here, even in REG_DEBUG
217 * builds, when you're not actively tracing.
220 if (v->eflags & REG_FTRACE)
224 FDEBUG(("--- at c%d ---\n", (int) (css - d->ssets)));
225 co = GETCOLOR(cm, *cp);
226 FDEBUG(("char %c, color %ld\n", (char) *cp, (long) co));
230 ss = miss(v, d, css, co, cp + 1, start);
232 break; /* NOTE BREAK OUT */
237 if ((ss->flags & POSTSTATE) && cp >= realmin)
238 break; /* NOTE BREAK OUT */
246 co = GETCOLOR(cm, *cp);
250 ss = miss(v, d, css, co, cp + 1, start);
252 break; /* NOTE BREAK OUT */
257 if ((ss->flags & POSTSTATE) && cp >= realmin)
258 break; /* NOTE BREAK OUT */
265 if (coldp != NULL) /* report last no-progress state set, if any */
266 *coldp = lastcold(v, d);
268 if ((ss->flags & POSTSTATE) && cp > min)
270 assert(cp >= realmin);
273 else if (cp == v->stop && max == v->stop)
275 co = d->cnfa->eos[(v->eflags & REG_NOTEOL) ? 0 : 1];
276 FDEBUG(("color %ld\n", (long) co));
277 ss = miss(v, d, css, co, cp, start);
278 /* match might have ended at eol */
279 if ((ss == NULL || !(ss->flags & POSTSTATE)) && hitstopp != NULL)
283 if (ss == NULL || !(ss->flags & POSTSTATE))
290 * lastcold - determine last point at which no progress had been made
292 static chr * /* endpoint, or NULL */
293 lastcold(struct vars * v,
303 for (ss = d->ssets, i = d->nssused; i > 0; ss++, i--)
304 if ((ss->flags & NOPROGRESS) && nopr < ss->lastseen)
310 * newdfa - set up a fresh DFA
313 newdfa(struct vars * v,
315 struct colormap * cm,
316 struct smalldfa * sml) /* preallocated space, may be NULL */
319 size_t nss = cnfa->nstates * 2;
320 int wordsper = (cnfa->nstates + UBITS - 1) / UBITS;
321 struct smalldfa *smallwas = sml;
323 assert(cnfa != NULL && cnfa->nstates != 0);
325 if (nss <= FEWSTATES && cnfa->ncolors <= FEWCOLORS)
327 assert(wordsper == 1);
330 sml = (struct smalldfa *) MALLOC(sizeof(struct smalldfa));
338 d->ssets = sml->ssets;
339 d->statesarea = sml->statesarea;
340 d->work = &d->statesarea[nss];
341 d->outsarea = sml->outsarea;
342 d->incarea = sml->incarea;
344 d->mallocarea = (smallwas == NULL) ? (char *) sml : NULL;
348 d = (struct dfa *) MALLOC(sizeof(struct dfa));
354 d->ssets = (struct sset *) MALLOC(nss * sizeof(struct sset));
355 d->statesarea = (unsigned *) MALLOC((nss + WORK) * wordsper *
357 d->work = &d->statesarea[nss * wordsper];
358 d->outsarea = (struct sset **) MALLOC(nss * cnfa->ncolors *
359 sizeof(struct sset *));
360 d->incarea = (struct arcp *) MALLOC(nss * cnfa->ncolors *
361 sizeof(struct arcp));
363 d->mallocarea = (char *) d;
364 if (d->ssets == NULL || d->statesarea == NULL ||
365 d->outsarea == NULL || d->incarea == NULL)
373 d->nssets = (v->eflags & REG_SMALL) ? 7 : nss;
375 d->nstates = cnfa->nstates;
376 d->ncolors = cnfa->ncolors;
377 d->wordsper = wordsper;
382 d->search = d->ssets;
384 /* initialization of sset fields is done as needed */
390 * freedfa - free a DFA
393 freedfa(struct dfa * d)
397 if (d->ssets != NULL)
399 if (d->statesarea != NULL)
401 if (d->outsarea != NULL)
403 if (d->incarea != NULL)
407 if (d->mallocarea != NULL)
412 * hash - construct a hash code for a bitvector
414 * There are probably better ways, but they're more expensive.
424 for (i = 0; i < n; i++)
430 * initialize - hand-craft a cache entry for startup, otherwise get ready
433 initialize(struct vars * v,
440 /* is previous one still there? */
441 if (d->nssused > 0 && (d->ssets[0].flags & STARTER))
444 { /* no, must (re)build it */
445 ss = getvacant(v, d, start, start);
448 for (i = 0; i < d->wordsper; i++)
450 BSET(ss->states, d->cnfa->pre);
451 ss->hash = HASH(ss->states, d->wordsper);
452 assert(d->cnfa->pre != d->cnfa->post);
453 ss->flags = STARTER | LOCKED | NOPROGRESS;
454 /* lastseen dealt with below */
457 for (i = 0; i < d->nssused; i++)
458 d->ssets[i].lastseen = NULL;
459 ss->lastseen = start; /* maybe untrue, but harmless */
466 * miss - handle a stateset cache miss
468 * css is the current stateset, co is the color of the current input character,
469 * cp points to the character after that (which is where we may need to test
470 * LACONs). start does not affect matching behavior but is needed for pickss'
471 * heuristics about which stateset cache entry to replace.
473 * Ordinarily, returns the address of the next stateset (the one that is
474 * valid after consuming the input character). Returns NULL if no valid
475 * NFA states remain, ie we have a certain match failure.
476 * Internal errors also return NULL, with v->err set.
479 miss(struct vars * v,
483 chr *cp, /* next chr */
484 chr *start) /* where the attempt got started */
486 struct cnfa *cnfa = d->cnfa;
497 /* for convenience, we can be called even if it might not be a miss */
498 if (css->outs[co] != NULL)
501 return css->outs[co];
506 * Checking for operation cancel in the inner text search loop seems
507 * unduly expensive. As a compromise, check during cache misses.
509 if (CANCEL_REQUESTED(v->re))
516 * What set of states would we end up in after consuming the co character?
517 * We first consider PLAIN arcs that consume the character, and then look
518 * to see what LACON arcs could be traversed after consuming it.
520 for (i = 0; i < d->wordsper; i++)
521 d->work[i] = 0; /* build new stateset bitmap in d->work */
525 for (i = 0; i < d->nstates; i++)
526 if (ISBSET(css->states, i))
527 for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++)
530 BSET(d->work, ca->to);
532 if (ca->to == cnfa->post)
534 if (!(cnfa->stflags[ca->to] & CNFA_NOPROGRESS))
536 FDEBUG(("%d -> %d\n", i, ca->to));
539 return NULL; /* character cannot reach any new state */
540 dolacons = (cnfa->flags & HASLACONS);
542 /* outer loop handles transitive closure of reachable-by-LACON states */
546 for (i = 0; i < d->nstates; i++)
547 if (ISBSET(d->work, i))
548 for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++)
550 if (ca->co < cnfa->ncolors)
551 continue; /* not a LACON arc */
552 if (ISBSET(d->work, ca->to))
553 continue; /* arc would be a no-op anyway */
554 sawlacons = 1; /* this LACON affects our result */
555 if (!lacon(v, cnfa, cp, ca->co))
559 continue; /* LACON arc cannot be traversed */
563 BSET(d->work, ca->to);
565 if (ca->to == cnfa->post)
567 if (!(cnfa->stflags[ca->to] & CNFA_NOPROGRESS))
569 FDEBUG(("%d :> %d\n", i, ca->to));
572 h = HASH(d->work, d->wordsper);
574 /* Is this stateset already in the cache? */
575 for (p = d->ssets, i = d->nssused; i > 0; p++, i--)
576 if (HIT(h, d->work, p, d->wordsper))
578 FDEBUG(("cached c%d\n", (int) (p - d->ssets)));
579 break; /* NOTE BREAK OUT */
582 { /* nope, need a new cache entry */
583 p = getvacant(v, d, cp, start);
587 for (i = 0; i < d->wordsper; i++)
588 p->states[i] = d->work[i];
590 p->flags = (ispost) ? POSTSTATE : 0;
592 p->flags |= NOPROGRESS;
593 /* lastseen to be dealt with by caller */
597 * Link new stateset to old, unless a LACON affected the result, in which
598 * case we don't create the link. That forces future transitions across
599 * this same arc (same prior stateset and character color) to come through
600 * miss() again, so that we can recheck the LACON(s), which might or might
601 * not pass since context will be different.
605 FDEBUG(("c%d[%d]->c%d\n",
606 (int) (css - d->ssets), co, (int) (p - d->ssets)));
608 css->inchain[co] = p->ins;
610 p->ins.co = (color) co;
616 * lacon - lookahead-constraint checker for miss()
618 static int /* predicate: constraint satisfied? */
619 lacon(struct vars * v,
620 struct cnfa * pcnfa, /* parent cnfa */
622 pcolor co) /* "color" of the lookahead constraint */
630 /* Since this is recursive, it could be driven to stack overflow */
631 if (STACK_TOO_DEEP(v->re))
637 n = co - pcnfa->ncolors;
638 assert(n < v->g->nlacons && v->g->lacons != NULL);
639 FDEBUG(("=== testing lacon %d\n", n));
640 sub = &v->g->lacons[n];
641 d = newdfa(v, &sub->cnfa, &v->g->cmap, &sd);
647 end = longest(v, d, cp, v->stop, (int *) NULL);
649 FDEBUG(("=== lacon %d match %d\n", n, (end != NULL)));
650 return (sub->subno) ? (end != NULL) : (end == NULL);
654 * getvacant - get a vacant state set
656 * This routine clears out the inarcs and outarcs, but does not otherwise
657 * clear the innards of the state set -- that's up to the caller.
660 getvacant(struct vars * v,
671 ss = pickss(v, d, cp, start);
674 assert(!(ss->flags & LOCKED));
676 /* clear out its inarcs, including self-referential ones */
678 while ((p = ap.ss) != NULL)
681 FDEBUG(("zapping c%d's %ld outarc\n", (int) (p - d->ssets), (long) co));
684 p->inchain[co].ss = NULL; /* paranoia */
688 /* take it off the inarc chains of the ssets reached by its outarcs */
689 for (i = 0; i < d->ncolors; i++)
692 assert(p != ss); /* not self-referential */
694 continue; /* NOTE CONTINUE */
695 FDEBUG(("del outarc %d from c%d's in chn\n", i, (int) (p - d->ssets)));
696 if (p->ins.ss == ss && p->ins.co == i)
697 p->ins = ss->inchain[i];
700 struct arcp lastap = {NULL, 0};
702 assert(p->ins.ss != NULL);
703 for (ap = p->ins; ap.ss != NULL &&
704 !(ap.ss == ss && ap.co == i);
705 ap = ap.ss->inchain[ap.co])
707 assert(ap.ss != NULL);
708 lastap.ss->inchain[lastap.co] = ss->inchain[i];
711 ss->inchain[i].ss = NULL;
714 /* if ss was a success state, may need to remember location */
715 if ((ss->flags & POSTSTATE) && ss->lastseen != d->lastpost &&
716 (d->lastpost == NULL || d->lastpost < ss->lastseen))
717 d->lastpost = ss->lastseen;
719 /* likewise for a no-progress state */
720 if ((ss->flags & NOPROGRESS) && ss->lastseen != d->lastnopr &&
721 (d->lastnopr == NULL || d->lastnopr < ss->lastseen))
722 d->lastnopr = ss->lastseen;
728 * pickss - pick the next stateset to be used
731 pickss(struct vars * v,
741 /* shortcut for cases where cache isn't full */
742 if (d->nssused < d->nssets)
747 FDEBUG(("new c%d\n", i));
749 ss->states = &d->statesarea[i * d->wordsper];
752 ss->ins.co = WHITE; /* give it some value */
753 ss->outs = &d->outsarea[i * d->ncolors];
754 ss->inchain = &d->incarea[i * d->ncolors];
755 for (i = 0; i < d->ncolors; i++)
758 ss->inchain[i].ss = NULL;
763 /* look for oldest, or old enough anyway */
764 if (cp - start > d->nssets * 2 / 3) /* oldest 33% are expendable */
765 ancient = cp - d->nssets * 2 / 3;
768 for (ss = d->search, end = &d->ssets[d->nssets]; ss < end; ss++)
769 if ((ss->lastseen == NULL || ss->lastseen < ancient) &&
770 !(ss->flags & LOCKED))
773 FDEBUG(("replacing c%d\n", (int) (ss - d->ssets)));
776 for (ss = d->ssets, end = d->search; ss < end; ss++)
777 if ((ss->lastseen == NULL || ss->lastseen < ancient) &&
778 !(ss->flags & LOCKED))
781 FDEBUG(("replacing c%d\n", (int) (ss - d->ssets)));
785 /* nobody's old enough?!? -- something's really wrong */
786 FDEBUG(("cannot find victim to replace!\n"));