Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/regex/rege_dfa.c
Line
Count
Source
1
/*
2
 * DFA routines
3
 * This file is #included by regexec.c.
4
 *
5
 * Copyright (c) 1998, 1999 Henry Spencer.  All rights reserved.
6
 *
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
10
 * thanks all of them.
11
 *
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.
16
 *
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.
19
 *
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.
30
 *
31
 * src/backend/regex/rege_dfa.c
32
 *
33
 */
34
35
/*
36
 * longest - longest-preferred matching engine
37
 *
38
 * On success, returns match endpoint address.  Returns NULL on no match.
39
 * Internal errors also return NULL, with v->err set.
40
 */
41
static chr *
42
longest(struct vars *v,
43
    struct dfa *d,
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 */
47
0
{
48
0
  chr      *cp;
49
0
  chr      *realstop = (stop == v->stop) ? stop : stop + 1;
50
0
  color   co;
51
0
  struct sset *css;
52
0
  struct sset *ss;
53
0
  chr      *post;
54
0
  int     i;
55
0
  struct colormap *cm = d->cm;
56
57
  /* prevent "uninitialized variable" warnings */
58
0
  if (hitstopp != NULL)
59
0
    *hitstopp = 0;
60
61
  /* if this is a backref to a known string, just match against that */
62
0
  if (d->backno >= 0)
63
0
  {
64
0
    assert((size_t) d->backno < v->nmatch);
65
0
    if (v->pmatch[d->backno].rm_so >= 0)
66
0
    {
67
0
      cp = dfa_backref(v, d, start, start, stop, false);
68
0
      if (cp == v->stop && stop == v->stop && hitstopp != NULL)
69
0
        *hitstopp = 1;
70
0
      return cp;
71
0
    }
72
0
  }
73
74
  /* fast path for matchall NFAs */
75
0
  if (d->cnfa->flags & MATCHALL)
76
0
  {
77
0
    size_t    nchr = stop - start;
78
0
    size_t    maxmatchall = d->cnfa->maxmatchall;
79
80
0
    if (nchr < d->cnfa->minmatchall)
81
0
      return NULL;
82
0
    if (maxmatchall == DUPINF)
83
0
    {
84
0
      if (stop == v->stop && hitstopp != NULL)
85
0
        *hitstopp = 1;
86
0
    }
87
0
    else
88
0
    {
89
0
      if (stop == v->stop && nchr <= maxmatchall + 1 && hitstopp != NULL)
90
0
        *hitstopp = 1;
91
0
      if (nchr > maxmatchall)
92
0
        return start + maxmatchall;
93
0
    }
94
0
    return stop;
95
0
  }
96
97
  /* initialize */
98
0
  css = initialize(v, d, start);
99
0
  if (css == NULL)
100
0
    return NULL;
101
0
  cp = start;
102
103
  /* startup */
104
0
  FDEBUG(("+++ startup +++\n"));
105
0
  if (cp == v->start)
106
0
  {
107
0
    co = d->cnfa->bos[(v->eflags & REG_NOTBOL) ? 0 : 1];
108
0
    FDEBUG(("color %ld\n", (long) co));
109
0
  }
110
0
  else
111
0
  {
112
0
    co = GETCOLOR(cm, *(cp - 1));
113
0
    FDEBUG(("char %c, color %ld\n", (char) *(cp - 1), (long) co));
114
0
  }
115
0
  css = miss(v, d, css, co, cp, start);
116
0
  if (css == NULL)
117
0
    return NULL;
118
0
  css->lastseen = cp;
119
120
  /*
121
   * This is the main text-scanning loop.  It seems worth having two copies
122
   * to avoid the overhead of REG_FTRACE tests here, even in REG_DEBUG
123
   * builds, when you're not actively tracing.
124
   */
125
#ifdef REG_DEBUG
126
  if (v->eflags & REG_FTRACE)
127
  {
128
    while (cp < realstop)
129
    {
130
      FDEBUG(("+++ at c%d +++\n", (int) (css - d->ssets)));
131
      co = GETCOLOR(cm, *cp);
132
      FDEBUG(("char %c, color %ld\n", (char) *cp, (long) co));
133
      ss = css->outs[co];
134
      if (ss == NULL)
135
      {
136
        ss = miss(v, d, css, co, cp + 1, start);
137
        if (ss == NULL)
138
          break;    /* NOTE BREAK OUT */
139
      }
140
      cp++;
141
      ss->lastseen = cp;
142
      css = ss;
143
    }
144
  }
145
  else
146
#endif
147
0
  {
148
0
    while (cp < realstop)
149
0
    {
150
0
      co = GETCOLOR(cm, *cp);
151
0
      ss = css->outs[co];
152
0
      if (ss == NULL)
153
0
      {
154
0
        ss = miss(v, d, css, co, cp + 1, start);
155
0
        if (ss == NULL)
156
0
          break;   /* NOTE BREAK OUT */
157
0
      }
158
0
      cp++;
159
0
      ss->lastseen = cp;
160
0
      css = ss;
161
0
    }
162
0
  }
163
164
0
  if (ISERR())
165
0
    return NULL;
166
167
  /* shutdown */
168
0
  FDEBUG(("+++ shutdown at c%d +++\n", (int) (css - d->ssets)));
169
0
  if (cp == v->stop && stop == v->stop)
170
0
  {
171
0
    if (hitstopp != NULL)
172
0
      *hitstopp = 1;
173
0
    co = d->cnfa->eos[(v->eflags & REG_NOTEOL) ? 0 : 1];
174
0
    FDEBUG(("color %ld\n", (long) co));
175
0
    ss = miss(v, d, css, co, cp, start);
176
0
    if (ISERR())
177
0
      return NULL;
178
    /* special case:  match ended at eol? */
179
0
    if (ss != NULL && (ss->flags & POSTSTATE))
180
0
      return cp;
181
0
    else if (ss != NULL)
182
0
      ss->lastseen = cp; /* to be tidy */
183
0
  }
184
185
  /* find last match, if any */
186
0
  post = d->lastpost;
187
0
  for (ss = d->ssets, i = d->nssused; i > 0; ss++, i--)
188
0
    if ((ss->flags & POSTSTATE) && post != ss->lastseen &&
189
0
      (post == NULL || post < ss->lastseen))
190
0
      post = ss->lastseen;
191
0
  if (post != NULL)     /* found one */
192
0
    return post - 1;
193
194
0
  return NULL;
195
0
}
196
197
/*
198
 * shortest - shortest-preferred matching engine
199
 *
200
 * On success, returns match endpoint address.  Returns NULL on no match.
201
 * Internal errors also return NULL, with v->err set.
202
 */
203
static chr *
204
shortest(struct vars *v,
205
     struct dfa *d,
206
     chr *start,      /* where the match should start */
207
     chr *min,        /* match must end at or after here */
208
     chr *max,        /* match must end at or before here */
209
     chr **coldp,     /* store coldstart pointer here, if non-NULL */
210
     int *hitstopp)     /* record whether hit v->stop, if non-NULL */
211
0
{
212
0
  chr      *cp;
213
0
  chr      *realmin = (min == v->stop) ? min : min + 1;
214
0
  chr      *realmax = (max == v->stop) ? max : max + 1;
215
0
  color   co;
216
0
  struct sset *css;
217
0
  struct sset *ss;
218
0
  struct colormap *cm = d->cm;
219
220
  /* prevent "uninitialized variable" warnings */
221
0
  if (coldp != NULL)
222
0
    *coldp = NULL;
223
0
  if (hitstopp != NULL)
224
0
    *hitstopp = 0;
225
226
  /* if this is a backref to a known string, just match against that */
227
0
  if (d->backno >= 0)
228
0
  {
229
0
    assert((size_t) d->backno < v->nmatch);
230
0
    if (v->pmatch[d->backno].rm_so >= 0)
231
0
    {
232
0
      cp = dfa_backref(v, d, start, min, max, true);
233
0
      if (cp != NULL && coldp != NULL)
234
0
        *coldp = start;
235
      /* there is no case where we should set *hitstopp */
236
0
      return cp;
237
0
    }
238
0
  }
239
240
  /* fast path for matchall NFAs */
241
0
  if (d->cnfa->flags & MATCHALL)
242
0
  {
243
0
    size_t    nchr = min - start;
244
245
0
    if (d->cnfa->maxmatchall != DUPINF &&
246
0
      nchr > d->cnfa->maxmatchall)
247
0
      return NULL;
248
0
    if ((max - start) < d->cnfa->minmatchall)
249
0
      return NULL;
250
0
    if (nchr < d->cnfa->minmatchall)
251
0
      min = start + d->cnfa->minmatchall;
252
0
    if (coldp != NULL)
253
0
      *coldp = start;
254
    /* there is no case where we should set *hitstopp */
255
0
    return min;
256
0
  }
257
258
  /* initialize */
259
0
  css = initialize(v, d, start);
260
0
  if (css == NULL)
261
0
    return NULL;
262
0
  cp = start;
263
264
  /* startup */
265
0
  FDEBUG(("--- startup ---\n"));
266
0
  if (cp == v->start)
267
0
  {
268
0
    co = d->cnfa->bos[(v->eflags & REG_NOTBOL) ? 0 : 1];
269
0
    FDEBUG(("color %ld\n", (long) co));
270
0
  }
271
0
  else
272
0
  {
273
0
    co = GETCOLOR(cm, *(cp - 1));
274
0
    FDEBUG(("char %c, color %ld\n", (char) *(cp - 1), (long) co));
275
0
  }
276
0
  css = miss(v, d, css, co, cp, start);
277
0
  if (css == NULL)
278
0
    return NULL;
279
0
  css->lastseen = cp;
280
0
  ss = css;
281
282
  /*
283
   * This is the main text-scanning loop.  It seems worth having two copies
284
   * to avoid the overhead of REG_FTRACE tests here, even in REG_DEBUG
285
   * builds, when you're not actively tracing.
286
   */
287
#ifdef REG_DEBUG
288
  if (v->eflags & REG_FTRACE)
289
  {
290
    while (cp < realmax)
291
    {
292
      FDEBUG(("--- at c%d ---\n", (int) (css - d->ssets)));
293
      co = GETCOLOR(cm, *cp);
294
      FDEBUG(("char %c, color %ld\n", (char) *cp, (long) co));
295
      ss = css->outs[co];
296
      if (ss == NULL)
297
      {
298
        ss = miss(v, d, css, co, cp + 1, start);
299
        if (ss == NULL)
300
          break;    /* NOTE BREAK OUT */
301
      }
302
      cp++;
303
      ss->lastseen = cp;
304
      css = ss;
305
      if ((ss->flags & POSTSTATE) && cp >= realmin)
306
        break;      /* NOTE BREAK OUT */
307
    }
308
  }
309
  else
310
#endif
311
0
  {
312
0
    while (cp < realmax)
313
0
    {
314
0
      co = GETCOLOR(cm, *cp);
315
0
      ss = css->outs[co];
316
0
      if (ss == NULL)
317
0
      {
318
0
        ss = miss(v, d, css, co, cp + 1, start);
319
0
        if (ss == NULL)
320
0
          break;   /* NOTE BREAK OUT */
321
0
      }
322
0
      cp++;
323
0
      ss->lastseen = cp;
324
0
      css = ss;
325
0
      if ((ss->flags & POSTSTATE) && cp >= realmin)
326
0
        break;     /* NOTE BREAK OUT */
327
0
    }
328
0
  }
329
330
0
  if (ss == NULL)
331
0
    return NULL;
332
333
0
  if (coldp != NULL)     /* report last no-progress state set, if any */
334
0
    *coldp = lastcold(v, d);
335
336
0
  if ((ss->flags & POSTSTATE) && cp > min)
337
0
  {
338
0
    assert(cp >= realmin);
339
0
    cp--;
340
0
  }
341
0
  else if (cp == v->stop && max == v->stop)
342
0
  {
343
0
    co = d->cnfa->eos[(v->eflags & REG_NOTEOL) ? 0 : 1];
344
0
    FDEBUG(("color %ld\n", (long) co));
345
0
    ss = miss(v, d, css, co, cp, start);
346
    /* match might have ended at eol */
347
0
    if ((ss == NULL || !(ss->flags & POSTSTATE)) && hitstopp != NULL)
348
0
      *hitstopp = 1;
349
0
  }
350
351
0
  if (ss == NULL || !(ss->flags & POSTSTATE))
352
0
    return NULL;
353
354
0
  return cp;
355
0
}
356
357
/*
358
 * matchuntil - incremental matching engine
359
 *
360
 * This is meant for use with a search-style NFA (that is, the pattern is
361
 * known to act as though it had a leading .*).  We determine whether a
362
 * match exists starting at v->start and ending at probe.  Multiple calls
363
 * require only O(N) time not O(N^2) so long as the probe values are
364
 * nondecreasing.  *lastcss and *lastcp must be initialized to NULL before
365
 * starting a series of calls.
366
 *
367
 * Returns 1 if a match exists, 0 if not.
368
 * Internal errors also return 0, with v->err set.
369
 */
370
static int
371
matchuntil(struct vars *v,
372
       struct dfa *d,
373
       chr *probe,      /* we want to know if a match ends here */
374
       struct sset **lastcss, /* state storage across calls */
375
       chr **lastcp)    /* state storage across calls */
376
0
{
377
0
  chr      *cp = *lastcp;
378
0
  color   co;
379
0
  struct sset *css = *lastcss;
380
0
  struct sset *ss;
381
0
  struct colormap *cm = d->cm;
382
383
  /* fast path for matchall NFAs */
384
0
  if (d->cnfa->flags & MATCHALL)
385
0
  {
386
0
    size_t    nchr = probe - v->start;
387
388
0
    if (nchr < d->cnfa->minmatchall)
389
0
      return 0;
390
    /* maxmatchall will always be infinity, cf. makesearch() */
391
0
    assert(d->cnfa->maxmatchall == DUPINF);
392
0
    return 1;
393
0
  }
394
395
  /* initialize and startup, or restart, if necessary */
396
0
  if (cp == NULL || cp > probe)
397
0
  {
398
0
    cp = v->start;
399
0
    css = initialize(v, d, cp);
400
0
    if (css == NULL)
401
0
      return 0;
402
403
0
    FDEBUG((">>> startup >>>\n"));
404
0
    co = d->cnfa->bos[(v->eflags & REG_NOTBOL) ? 0 : 1];
405
0
    FDEBUG(("color %ld\n", (long) co));
406
407
0
    css = miss(v, d, css, co, cp, v->start);
408
0
    if (css == NULL)
409
0
      return 0;
410
0
    css->lastseen = cp;
411
0
  }
412
0
  else if (css == NULL)
413
0
  {
414
    /* we previously found that no match is possible beyond *lastcp */
415
0
    return 0;
416
0
  }
417
0
  ss = css;
418
419
  /*
420
   * This is the main text-scanning loop.  It seems worth having two copies
421
   * to avoid the overhead of REG_FTRACE tests here, even in REG_DEBUG
422
   * builds, when you're not actively tracing.
423
   */
424
#ifdef REG_DEBUG
425
  if (v->eflags & REG_FTRACE)
426
  {
427
    while (cp < probe)
428
    {
429
      FDEBUG((">>> at c%d >>>\n", (int) (css - d->ssets)));
430
      co = GETCOLOR(cm, *cp);
431
      FDEBUG(("char %c, color %ld\n", (char) *cp, (long) co));
432
      ss = css->outs[co];
433
      if (ss == NULL)
434
      {
435
        ss = miss(v, d, css, co, cp + 1, v->start);
436
        if (ss == NULL)
437
          break;    /* NOTE BREAK OUT */
438
      }
439
      cp++;
440
      ss->lastseen = cp;
441
      css = ss;
442
    }
443
  }
444
  else
445
#endif
446
0
  {
447
0
    while (cp < probe)
448
0
    {
449
0
      co = GETCOLOR(cm, *cp);
450
0
      ss = css->outs[co];
451
0
      if (ss == NULL)
452
0
      {
453
0
        ss = miss(v, d, css, co, cp + 1, v->start);
454
0
        if (ss == NULL)
455
0
          break;   /* NOTE BREAK OUT */
456
0
      }
457
0
      cp++;
458
0
      ss->lastseen = cp;
459
0
      css = ss;
460
0
    }
461
0
  }
462
463
0
  *lastcss = ss;
464
0
  *lastcp = cp;
465
466
0
  if (ss == NULL)
467
0
    return 0;       /* impossible match, or internal error */
468
469
  /* We need to process one more chr, or the EOS symbol, to check match */
470
0
  if (cp < v->stop)
471
0
  {
472
0
    FDEBUG((">>> at c%d >>>\n", (int) (css - d->ssets)));
473
0
    co = GETCOLOR(cm, *cp);
474
0
    FDEBUG(("char %c, color %ld\n", (char) *cp, (long) co));
475
0
    ss = css->outs[co];
476
0
    if (ss == NULL)
477
0
      ss = miss(v, d, css, co, cp + 1, v->start);
478
0
  }
479
0
  else
480
0
  {
481
0
    assert(cp == v->stop);
482
0
    co = d->cnfa->eos[(v->eflags & REG_NOTEOL) ? 0 : 1];
483
0
    FDEBUG(("color %ld\n", (long) co));
484
0
    ss = miss(v, d, css, co, cp, v->start);
485
0
  }
486
487
0
  if (ss == NULL || !(ss->flags & POSTSTATE))
488
0
    return 0;
489
490
0
  return 1;
491
0
}
492
493
/*
494
 * dfa_backref - find best match length for a known backref string
495
 *
496
 * When the backref's referent is already available, we can deliver an exact
497
 * answer with considerably less work than running the backref node's NFA.
498
 *
499
 * Return match endpoint for longest or shortest valid repeated match,
500
 * or NULL if there is no valid match.
501
 *
502
 * Should be in sync with cbrdissect(), although that has the different task
503
 * of checking a match to a predetermined section of the string.
504
 */
505
static chr *
506
dfa_backref(struct vars *v,
507
      struct dfa *d,
508
      chr *start,     /* where the match should start */
509
      chr *min,     /* match must end at or after here */
510
      chr *max,     /* match must end at or before here */
511
      bool shortest)
512
0
{
513
0
  int     n = d->backno;
514
0
  int     backmin = d->backmin;
515
0
  int     backmax = d->backmax;
516
0
  size_t    numreps;
517
0
  size_t    minreps;
518
0
  size_t    maxreps;
519
0
  size_t    brlen;
520
0
  chr      *brstring;
521
0
  chr      *p;
522
523
  /* get the backreferenced string (caller should have checked this) */
524
0
  if (v->pmatch[n].rm_so == -1)
525
0
    return NULL;
526
0
  brstring = v->start + v->pmatch[n].rm_so;
527
0
  brlen = v->pmatch[n].rm_eo - v->pmatch[n].rm_so;
528
529
  /* special-case zero-length backreference to avoid divide by zero */
530
0
  if (brlen == 0)
531
0
  {
532
    /*
533
     * matches only a zero-length string, but any number of repetitions
534
     * can be considered to be present
535
     */
536
0
    if (min == start && backmin <= backmax)
537
0
      return start;
538
0
    return NULL;
539
0
  }
540
541
  /*
542
   * convert min and max into numbers of possible repetitions of the backref
543
   * string, rounding appropriately
544
   */
545
0
  if (min <= start)
546
0
    minreps = 0;
547
0
  else
548
0
    minreps = (min - start - 1) / brlen + 1;
549
0
  maxreps = (max - start) / brlen;
550
551
  /* apply bounds, then see if there is any allowed match length */
552
0
  if (minreps < backmin)
553
0
    minreps = backmin;
554
0
  if (backmax != DUPINF && maxreps > backmax)
555
0
    maxreps = backmax;
556
0
  if (maxreps < minreps)
557
0
    return NULL;
558
559
  /* quick exit if zero-repetitions match is valid and preferred */
560
0
  if (shortest && minreps == 0)
561
0
    return start;
562
563
  /* okay, compare the actual string contents */
564
0
  p = start;
565
0
  numreps = 0;
566
0
  while (numreps < maxreps)
567
0
  {
568
0
    if ((*v->g->compare) (brstring, p, brlen) != 0)
569
0
      break;
570
0
    p += brlen;
571
0
    numreps++;
572
0
    if (shortest && numreps >= minreps)
573
0
      break;
574
0
  }
575
576
0
  if (numreps >= minreps)
577
0
    return p;
578
0
  return NULL;
579
0
}
580
581
/*
582
 * lastcold - determine last point at which no progress had been made
583
 */
584
static chr *          /* endpoint, or NULL */
585
lastcold(struct vars *v,
586
     struct dfa *d)
587
0
{
588
0
  struct sset *ss;
589
0
  chr      *nopr;
590
0
  int     i;
591
592
0
  nopr = d->lastnopr;
593
0
  if (nopr == NULL)
594
0
    nopr = v->start;
595
0
  for (ss = d->ssets, i = d->nssused; i > 0; ss++, i--)
596
0
    if ((ss->flags & NOPROGRESS) && nopr < ss->lastseen)
597
0
      nopr = ss->lastseen;
598
0
  return nopr;
599
0
}
600
601
/*
602
 * newdfa - set up a fresh DFA
603
 *
604
 * Returns NULL (and sets v->err) on failure.
605
 */
606
static struct dfa *
607
newdfa(struct vars *v,
608
     struct cnfa *cnfa,
609
     struct colormap *cm,
610
     struct smalldfa *sml)  /* preallocated space, may be NULL */
611
0
{
612
0
  struct dfa *d;
613
0
  size_t    nss = cnfa->nstates * 2;
614
0
  int     wordsper = (cnfa->nstates + UBITS - 1) / UBITS;
615
0
  bool    ismalloced = false;
616
617
0
  assert(cnfa != NULL && cnfa->nstates != 0);
618
619
0
  if (nss <= FEWSTATES && cnfa->ncolors <= FEWCOLORS)
620
0
  {
621
0
    assert(wordsper == 1);
622
0
    if (sml == NULL)
623
0
    {
624
0
      sml = (struct smalldfa *) MALLOC(sizeof(struct smalldfa));
625
0
      if (sml == NULL)
626
0
      {
627
0
        ERR(REG_ESPACE);
628
0
        return NULL;
629
0
      }
630
0
      ismalloced = true;
631
0
    }
632
0
    d = &sml->dfa;
633
0
    d->ssets = sml->ssets;
634
0
    d->statesarea = sml->statesarea;
635
0
    d->work = &d->statesarea[nss];
636
0
    d->outsarea = sml->outsarea;
637
0
    d->incarea = sml->incarea;
638
0
    d->ismalloced = ismalloced;
639
0
    d->arraysmalloced = false;  /* not separately allocated, anyway */
640
0
  }
641
0
  else
642
0
  {
643
    /*
644
     * Restrict the ranges of nstates and ncolors enough that the arrays
645
     * we allocate here have no more than INT_MAX members.  This protects
646
     * not only the allocation calculations just below, but later indexing
647
     * into these arrays.
648
     */
649
0
    if (wordsper >= INT_MAX / (nss + WORK) ||
650
0
      cnfa->ncolors >= INT_MAX / nss)
651
0
    {
652
0
      ERR(REG_ETOOBIG);
653
0
      return NULL;
654
0
    }
655
0
    d = (struct dfa *) MALLOC(sizeof(struct dfa));
656
0
    if (d == NULL)
657
0
    {
658
0
      ERR(REG_ESPACE);
659
0
      return NULL;
660
0
    }
661
0
    d->ssets = MALLOC_ARRAY(struct sset, nss);
662
0
    d->statesarea = MALLOC_ARRAY(unsigned, (nss + WORK) * wordsper);
663
0
    d->work = &d->statesarea[nss * wordsper];
664
0
    d->outsarea = MALLOC_ARRAY(struct sset *, nss * cnfa->ncolors);
665
0
    d->incarea = MALLOC_ARRAY(struct arcp, nss * cnfa->ncolors);
666
0
    d->ismalloced = true;
667
0
    d->arraysmalloced = true;
668
    /* now freedfa() will behave sanely */
669
0
    if (d->ssets == NULL || d->statesarea == NULL ||
670
0
      d->outsarea == NULL || d->incarea == NULL)
671
0
    {
672
0
      freedfa(d);
673
0
      ERR(REG_ESPACE);
674
0
      return NULL;
675
0
    }
676
0
  }
677
678
0
  d->nssets = (v->eflags & REG_SMALL) ? 7 : nss;
679
0
  d->nssused = 0;
680
0
  d->nstates = cnfa->nstates;
681
0
  d->ncolors = cnfa->ncolors;
682
0
  d->wordsper = wordsper;
683
0
  d->cnfa = cnfa;
684
0
  d->cm = cm;
685
0
  d->lastpost = NULL;
686
0
  d->lastnopr = NULL;
687
0
  d->search = d->ssets;
688
0
  d->backno = -1;       /* may be set by caller */
689
0
  d->backmin = d->backmax = 0;
690
691
  /* initialization of sset fields is done as needed */
692
693
0
  return d;
694
0
}
695
696
/*
697
 * freedfa - free a DFA
698
 */
699
static void
700
freedfa(struct dfa *d)
701
0
{
702
0
  if (d->arraysmalloced)
703
0
  {
704
0
    if (d->ssets != NULL)
705
0
      FREE(d->ssets);
706
0
    if (d->statesarea != NULL)
707
0
      FREE(d->statesarea);
708
0
    if (d->outsarea != NULL)
709
0
      FREE(d->outsarea);
710
0
    if (d->incarea != NULL)
711
0
      FREE(d->incarea);
712
0
  }
713
714
0
  if (d->ismalloced)
715
0
    FREE(d);
716
0
}
717
718
/*
719
 * hash - construct a hash code for a bitvector
720
 *
721
 * There are probably better ways, but they're more expensive.
722
 */
723
static unsigned
724
hash(unsigned *uv,
725
   int n)
726
0
{
727
0
  int     i;
728
0
  unsigned  h;
729
730
0
  h = 0;
731
0
  for (i = 0; i < n; i++)
732
0
    h ^= uv[i];
733
0
  return h;
734
0
}
735
736
/*
737
 * initialize - hand-craft a cache entry for startup, otherwise get ready
738
 */
739
static struct sset *
740
initialize(struct vars *v,
741
       struct dfa *d,
742
       chr *start)
743
0
{
744
0
  struct sset *ss;
745
0
  int     i;
746
747
  /* is previous one still there? */
748
0
  if (d->nssused > 0 && (d->ssets[0].flags & STARTER))
749
0
    ss = &d->ssets[0];
750
0
  else
751
0
  {             /* no, must (re)build it */
752
0
    ss = getvacant(v, d, start, start);
753
0
    if (ss == NULL)
754
0
      return NULL;
755
0
    for (i = 0; i < d->wordsper; i++)
756
0
      ss->states[i] = 0;
757
0
    BSET(ss->states, d->cnfa->pre);
758
0
    ss->hash = HASH(ss->states, d->wordsper);
759
0
    assert(d->cnfa->pre != d->cnfa->post);
760
0
    ss->flags = STARTER | LOCKED | NOPROGRESS;
761
    /* lastseen dealt with below */
762
0
  }
763
764
0
  for (i = 0; i < d->nssused; i++)
765
0
    d->ssets[i].lastseen = NULL;
766
0
  ss->lastseen = start;   /* maybe untrue, but harmless */
767
0
  d->lastpost = NULL;
768
0
  d->lastnopr = NULL;
769
0
  return ss;
770
0
}
771
772
/*
773
 * miss - handle a stateset cache miss
774
 *
775
 * css is the current stateset, co is the color of the current input character,
776
 * cp points to the character after that (which is where we may need to test
777
 * LACONs).  start does not affect matching behavior but is needed for pickss'
778
 * heuristics about which stateset cache entry to replace.
779
 *
780
 * Ordinarily, returns the address of the next stateset (the one that is
781
 * valid after consuming the input character).  Returns NULL if no valid
782
 * NFA states remain, ie we have a certain match failure.
783
 * Internal errors also return NULL, with v->err set.
784
 */
785
static struct sset *
786
miss(struct vars *v,
787
   struct dfa *d,
788
   struct sset *css,
789
   color co,
790
   chr *cp,         /* next chr */
791
   chr *start)        /* where the attempt got started */
792
0
{
793
0
  struct cnfa *cnfa = d->cnfa;
794
0
  int     i;
795
0
  unsigned  h;
796
0
  struct carc *ca;
797
0
  struct sset *p;
798
0
  int     ispseudocolor;
799
0
  int     ispost;
800
0
  int     noprogress;
801
0
  int     gotstate;
802
0
  int     dolacons;
803
0
  int     sawlacons;
804
805
  /* for convenience, we can be called even if it might not be a miss */
806
0
  if (css->outs[co] != NULL)
807
0
  {
808
0
    FDEBUG(("hit\n"));
809
0
    return css->outs[co];
810
0
  }
811
0
  FDEBUG(("miss\n"));
812
813
  /*
814
   * Checking for operation cancel in the inner text search loop seems
815
   * unduly expensive.  As a compromise, check during cache misses.
816
   */
817
0
  INTERRUPT(v->re);
818
819
  /*
820
   * What set of states would we end up in after consuming the co character?
821
   * We first consider PLAIN arcs that consume the character, and then look
822
   * to see what LACON arcs could be traversed after consuming it.
823
   */
824
0
  for (i = 0; i < d->wordsper; i++)
825
0
    d->work[i] = 0;     /* build new stateset bitmap in d->work */
826
0
  ispseudocolor = d->cm->cd[co].flags & PSEUDO;
827
0
  ispost = 0;
828
0
  noprogress = 1;
829
0
  gotstate = 0;
830
0
  for (i = 0; i < d->nstates; i++)
831
0
    if (ISBSET(css->states, i))
832
0
      for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++)
833
0
        if (ca->co == co ||
834
0
          (ca->co == RAINBOW && !ispseudocolor))
835
0
        {
836
0
          BSET(d->work, ca->to);
837
0
          gotstate = 1;
838
0
          if (ca->to == cnfa->post)
839
0
            ispost = 1;
840
0
          if (!(cnfa->stflags[ca->to] & CNFA_NOPROGRESS))
841
0
            noprogress = 0;
842
0
          FDEBUG(("%d -> %d\n", i, ca->to));
843
0
        }
844
0
  if (!gotstate)
845
0
    return NULL;     /* character cannot reach any new state */
846
0
  dolacons = (cnfa->flags & HASLACONS);
847
0
  sawlacons = 0;
848
  /* outer loop handles transitive closure of reachable-by-LACON states */
849
0
  while (dolacons)
850
0
  {
851
0
    dolacons = 0;
852
0
    for (i = 0; i < d->nstates; i++)
853
0
      if (ISBSET(d->work, i))
854
0
        for (ca = cnfa->states[i]; ca->co != COLORLESS; ca++)
855
0
        {
856
0
          if (ca->co < cnfa->ncolors)
857
0
            continue; /* not a LACON arc */
858
0
          if (ISBSET(d->work, ca->to))
859
0
            continue; /* arc would be a no-op anyway */
860
0
          sawlacons = 1;  /* this LACON affects our result */
861
0
          if (!lacon(v, cnfa, cp, ca->co))
862
0
          {
863
0
            if (ISERR())
864
0
              return NULL;
865
0
            continue; /* LACON arc cannot be traversed */
866
0
          }
867
0
          if (ISERR())
868
0
            return NULL;
869
0
          BSET(d->work, ca->to);
870
0
          dolacons = 1;
871
0
          if (ca->to == cnfa->post)
872
0
            ispost = 1;
873
0
          if (!(cnfa->stflags[ca->to] & CNFA_NOPROGRESS))
874
0
            noprogress = 0;
875
0
          FDEBUG(("%d :> %d\n", i, ca->to));
876
0
        }
877
0
  }
878
0
  h = HASH(d->work, d->wordsper);
879
880
  /* Is this stateset already in the cache? */
881
0
  for (p = d->ssets, i = d->nssused; i > 0; p++, i--)
882
0
    if (HIT(h, d->work, p, d->wordsper))
883
0
    {
884
0
      FDEBUG(("cached c%d\n", (int) (p - d->ssets)));
885
0
      break;        /* NOTE BREAK OUT */
886
0
    }
887
0
  if (i == 0)
888
0
  {             /* nope, need a new cache entry */
889
0
    p = getvacant(v, d, cp, start);
890
0
    if (p == NULL)
891
0
      return NULL;
892
0
    assert(p != css);
893
0
    for (i = 0; i < d->wordsper; i++)
894
0
      p->states[i] = d->work[i];
895
0
    p->hash = h;
896
0
    p->flags = (ispost) ? POSTSTATE : 0;
897
0
    if (noprogress)
898
0
      p->flags |= NOPROGRESS;
899
    /* lastseen to be dealt with by caller */
900
0
  }
901
902
  /*
903
   * Link new stateset to old, unless a LACON affected the result, in which
904
   * case we don't create the link.  That forces future transitions across
905
   * this same arc (same prior stateset and character color) to come through
906
   * miss() again, so that we can recheck the LACON(s), which might or might
907
   * not pass since context will be different.
908
   */
909
0
  if (!sawlacons)
910
0
  {
911
0
    FDEBUG(("c%d[%d]->c%d\n",
912
0
        (int) (css - d->ssets), co, (int) (p - d->ssets)));
913
0
    css->outs[co] = p;
914
0
    css->inchain[co] = p->ins;
915
0
    p->ins.ss = css;
916
0
    p->ins.co = co;
917
0
  }
918
0
  return p;
919
0
}
920
921
/*
922
 * lacon - lookaround-constraint checker for miss()
923
 */
924
static int            /* predicate:  constraint satisfied? */
925
lacon(struct vars *v,
926
    struct cnfa *pcnfa,   /* parent cnfa */
927
    chr *cp,
928
    color co)         /* "color" of the lookaround constraint */
929
0
{
930
0
  int     n;
931
0
  struct subre *sub;
932
0
  struct dfa *d;
933
0
  chr      *end;
934
0
  int     satisfied;
935
936
  /* Since this is recursive, it could be driven to stack overflow */
937
0
  if (STACK_TOO_DEEP(v->re))
938
0
  {
939
0
    ERR(REG_ETOOBIG);
940
0
    return 0;
941
0
  }
942
943
0
  n = co - pcnfa->ncolors;
944
0
  assert(n > 0 && n < v->g->nlacons && v->g->lacons != NULL);
945
0
  FDEBUG(("=== testing lacon %d\n", n));
946
0
  sub = &v->g->lacons[n];
947
0
  d = getladfa(v, n);
948
0
  if (d == NULL)
949
0
    return 0;
950
0
  if (LATYPE_IS_AHEAD(sub->latype))
951
0
  {
952
    /* used to use longest() here, but shortest() could be much cheaper */
953
0
    end = shortest(v, d, cp, cp, v->stop,
954
0
             (chr **) NULL, (int *) NULL);
955
0
    satisfied = LATYPE_IS_POS(sub->latype) ? (end != NULL) : (end == NULL);
956
0
  }
957
0
  else
958
0
  {
959
    /*
960
     * To avoid doing O(N^2) work when repeatedly testing a lookbehind
961
     * constraint in an N-character string, we use matchuntil() which can
962
     * cache the DFA state across calls.  We only need to restart if the
963
     * probe point decreases, which is not common.  The NFA we're using is
964
     * a search NFA, so it doesn't mind scanning over stuff before the
965
     * nominal match.
966
     */
967
0
    satisfied = matchuntil(v, d, cp, &v->lblastcss[n], &v->lblastcp[n]);
968
0
    if (!LATYPE_IS_POS(sub->latype))
969
0
      satisfied = !satisfied;
970
0
  }
971
0
  FDEBUG(("=== lacon %d satisfied %d\n", n, satisfied));
972
0
  return satisfied;
973
0
}
974
975
/*
976
 * getvacant - get a vacant state set
977
 *
978
 * This routine clears out the inarcs and outarcs, but does not otherwise
979
 * clear the innards of the state set -- that's up to the caller.
980
 */
981
static struct sset *
982
getvacant(struct vars *v,
983
      struct dfa *d,
984
      chr *cp,
985
      chr *start)
986
0
{
987
0
  int     i;
988
0
  struct sset *ss;
989
0
  struct sset *p;
990
0
  struct arcp ap;
991
0
  color   co;
992
993
0
  ss = pickss(v, d, cp, start);
994
0
  if (ss == NULL)
995
0
    return NULL;
996
0
  assert(!(ss->flags & LOCKED));
997
998
  /* clear out its inarcs, including self-referential ones */
999
0
  ap = ss->ins;
1000
0
  while ((p = ap.ss) != NULL)
1001
0
  {
1002
0
    co = ap.co;
1003
0
    FDEBUG(("zapping c%d's %ld outarc\n", (int) (p - d->ssets), (long) co));
1004
0
    p->outs[co] = NULL;
1005
0
    ap = p->inchain[co];
1006
0
    p->inchain[co].ss = NULL; /* paranoia */
1007
0
  }
1008
0
  ss->ins.ss = NULL;
1009
1010
  /* take it off the inarc chains of the ssets reached by its outarcs */
1011
0
  for (i = 0; i < d->ncolors; i++)
1012
0
  {
1013
0
    p = ss->outs[i];
1014
0
    assert(p != ss);    /* not self-referential */
1015
0
    if (p == NULL)
1016
0
      continue;     /* NOTE CONTINUE */
1017
0
    FDEBUG(("del outarc %d from c%d's in chn\n", i, (int) (p - d->ssets)));
1018
0
    if (p->ins.ss == ss && p->ins.co == i)
1019
0
      p->ins = ss->inchain[i];
1020
0
    else
1021
0
    {
1022
0
      struct arcp lastap = {NULL, 0};
1023
1024
0
      assert(p->ins.ss != NULL);
1025
0
      for (ap = p->ins; ap.ss != NULL &&
1026
0
         !(ap.ss == ss && ap.co == i);
1027
0
         ap = ap.ss->inchain[ap.co])
1028
0
        lastap = ap;
1029
0
      assert(ap.ss != NULL);
1030
0
      lastap.ss->inchain[lastap.co] = ss->inchain[i];
1031
0
    }
1032
0
    ss->outs[i] = NULL;
1033
0
    ss->inchain[i].ss = NULL;
1034
0
  }
1035
1036
  /* if ss was a success state, may need to remember location */
1037
0
  if ((ss->flags & POSTSTATE) && ss->lastseen != d->lastpost &&
1038
0
    (d->lastpost == NULL || d->lastpost < ss->lastseen))
1039
0
    d->lastpost = ss->lastseen;
1040
1041
  /* likewise for a no-progress state */
1042
0
  if ((ss->flags & NOPROGRESS) && ss->lastseen != d->lastnopr &&
1043
0
    (d->lastnopr == NULL || d->lastnopr < ss->lastseen))
1044
0
    d->lastnopr = ss->lastseen;
1045
1046
0
  return ss;
1047
0
}
1048
1049
/*
1050
 * pickss - pick the next stateset to be used
1051
 */
1052
static struct sset *
1053
pickss(struct vars *v,
1054
     struct dfa *d,
1055
     chr *cp,
1056
     chr *start)
1057
0
{
1058
0
  int     i;
1059
0
  struct sset *ss;
1060
0
  struct sset *end;
1061
0
  chr      *ancient;
1062
1063
  /* shortcut for cases where cache isn't full */
1064
0
  if (d->nssused < d->nssets)
1065
0
  {
1066
0
    i = d->nssused;
1067
0
    d->nssused++;
1068
0
    ss = &d->ssets[i];
1069
0
    FDEBUG(("new c%d\n", i));
1070
    /* set up innards */
1071
0
    ss->states = &d->statesarea[i * d->wordsper];
1072
0
    ss->flags = 0;
1073
0
    ss->ins.ss = NULL;
1074
0
    ss->ins.co = WHITE;   /* give it some value */
1075
0
    ss->outs = &d->outsarea[i * d->ncolors];
1076
0
    ss->inchain = &d->incarea[i * d->ncolors];
1077
0
    for (i = 0; i < d->ncolors; i++)
1078
0
    {
1079
0
      ss->outs[i] = NULL;
1080
0
      ss->inchain[i].ss = NULL;
1081
0
    }
1082
0
    return ss;
1083
0
  }
1084
1085
  /* look for oldest, or old enough anyway */
1086
0
  if (cp - start > d->nssets * 2 / 3) /* oldest 33% are expendable */
1087
0
    ancient = cp - d->nssets * 2 / 3;
1088
0
  else
1089
0
    ancient = start;
1090
0
  for (ss = d->search, end = &d->ssets[d->nssets]; ss < end; ss++)
1091
0
    if ((ss->lastseen == NULL || ss->lastseen < ancient) &&
1092
0
      !(ss->flags & LOCKED))
1093
0
    {
1094
0
      d->search = ss + 1;
1095
0
      FDEBUG(("replacing c%d\n", (int) (ss - d->ssets)));
1096
0
      return ss;
1097
0
    }
1098
0
  for (ss = d->ssets, end = d->search; ss < end; ss++)
1099
0
    if ((ss->lastseen == NULL || ss->lastseen < ancient) &&
1100
0
      !(ss->flags & LOCKED))
1101
0
    {
1102
0
      d->search = ss + 1;
1103
0
      FDEBUG(("replacing c%d\n", (int) (ss - d->ssets)));
1104
0
      return ss;
1105
0
    }
1106
1107
  /* nobody's old enough?!? -- something's really wrong */
1108
0
  FDEBUG(("cannot find victim to replace!\n"));
1109
0
  ERR(REG_ASSERT);
1110
  return NULL;
1111
0
}