@react-native-quickjs/quickjs 1.0.0-alpha.2 → 1.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,9 @@
30
30
 
31
31
  #include "cutils.h"
32
32
  #include "libregexp.h"
33
+ #ifndef JS_RE_PREFILTER
34
+ #define JS_RE_PREFILTER 1
35
+ #endif
33
36
  #include "libunicode.h"
34
37
 
35
38
  /* ASCII identifier tables, used by lre_js_is_ident_first/next in libregexp.h
@@ -75,6 +78,14 @@ typedef enum {
75
78
  enough to call the interrupt callback often. */
76
79
  #define INTERRUPT_COUNTER_INIT 10000
77
80
 
81
+ /* Use GCC/Clang computed-goto (label-as-value) threaded dispatch in the
82
+ backtracking matcher where available; fall back to a plain switch. */
83
+ #if defined(EMSCRIPTEN) || defined(_MSC_VER)
84
+ #define LRE_DIRECT_DISPATCH 0
85
+ #else
86
+ #define LRE_DIRECT_DISPATCH 1
87
+ #endif
88
+
78
89
  /* unicode code points */
79
90
  #define CP_LS 0x2028
80
91
  #define CP_PS 0x2029
@@ -132,6 +143,882 @@ static inline int lre_is_digit(int c) {
132
143
  return c >= '0' && c <= '9';
133
144
  }
134
145
 
146
+ /* ==== RegExp optimization metadata ====================================== *
147
+ * The compiler appends optional metadata after the opcode stream so that the
148
+ * quickjs fast paths can locate matches with direct string searches instead
149
+ * of running the backtracking interpreter at every input position. The three
150
+ * derived kinds are: a raw literal (LRE_META_LITERAL, whole pattern is a
151
+ * substring match), a required literal prefix (LRE_META_PREFIX), and a set of
152
+ * fixed first-character sets (LRE_META_FIRST_SET). */
153
+
154
+ /* Return true if the source pattern is a literal run of characters with no
155
+ metacharacters and no case/unicode folding, i.e. a plain substring. */
156
+ int lre_is_raw_atom(const char *buf, size_t len, int re_flags)
157
+ {
158
+ size_t i;
159
+
160
+ if (len == 0 ||
161
+ (re_flags & (LRE_FLAG_IGNORECASE | LRE_FLAG_STICKY |
162
+ LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS))) {
163
+ return false;
164
+ }
165
+ for (i = 0; i < len; i++) {
166
+ switch ((uint8_t)buf[i]) {
167
+ case '\\': case '^': case '$': case '.': case '*': case '+':
168
+ case '?': case '(': case ')': case '[': case ']': case '{':
169
+ case '}': case '|':
170
+ return false;
171
+ default:
172
+ break;
173
+ }
174
+ }
175
+ return true;
176
+ }
177
+
178
+ /* Match the synthetic ".*?" scan prologue emitted by lre_compile for
179
+ non-sticky patterns: split_goto_first(+6), any, goto(-11), save_start 0.
180
+ On success *pentry_pos is the offset of the leading save_start (11). */
181
+ static bool re_get_scan_entry(const uint8_t *bc_buf, size_t bc_buf_len,
182
+ size_t *pentry_pos)
183
+ {
184
+ const uint8_t *code;
185
+
186
+ if (bc_buf_len < RE_HEADER_LEN + 13)
187
+ return false;
188
+ code = bc_buf + RE_HEADER_LEN;
189
+ if (code[0] != REOP_split_goto_first || get_i32(code + 1) != 6 ||
190
+ code[5] != REOP_any || code[6] != REOP_goto ||
191
+ get_i32(code + 7) != -11 || code[11] != REOP_save_start ||
192
+ code[12] != 0) {
193
+ return false;
194
+ }
195
+ *pentry_pos = 11;
196
+ return true;
197
+ }
198
+
199
+ /* Detect a pattern whose whole body is "save_start 0; char...; save_end 0;
200
+ match", i.e. a bare literal wrapped in the implicit capture 0. */
201
+ static bool re_has_literal_body(const uint8_t *bc_buf, size_t bc_buf_len,
202
+ int re_flags, int capture_count,
203
+ size_t *pchar_pos, uint32_t *plength,
204
+ int *pencoding)
205
+ {
206
+ const uint8_t *code;
207
+ size_t pos, char_pos;
208
+ uint32_t length, c;
209
+ int encoding;
210
+
211
+ if (capture_count != 1 ||
212
+ (re_flags & (LRE_FLAG_IGNORECASE | LRE_FLAG_STICKY |
213
+ LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) ||
214
+ bc_buf_len < RE_HEADER_LEN + 16 ||
215
+ !re_get_scan_entry(bc_buf, bc_buf_len, &char_pos)) {
216
+ return false;
217
+ }
218
+ code = bc_buf + RE_HEADER_LEN;
219
+ bc_buf_len -= RE_HEADER_LEN;
220
+
221
+ pos = char_pos = 13;
222
+ length = 0;
223
+ encoding = 0;
224
+ while (pos < bc_buf_len) {
225
+ if (code[pos] == REOP_char) {
226
+ if (pos + 3 > bc_buf_len)
227
+ return false;
228
+ c = get_u16(code + pos + 1);
229
+ pos += 3;
230
+ } else if (code[pos] == REOP_char32) {
231
+ if (pos + 5 > bc_buf_len)
232
+ return false;
233
+ c = get_u32(code + pos + 1);
234
+ if (c > 0xffff)
235
+ return false;
236
+ pos += 5;
237
+ } else {
238
+ break;
239
+ }
240
+ if (c > 0xff)
241
+ encoding = 1;
242
+ if (length == UINT32_MAX)
243
+ return false;
244
+ length++;
245
+ }
246
+ if (length == 0 || pos + 3 != bc_buf_len ||
247
+ code[pos] != REOP_save_end || code[pos + 1] != 0 ||
248
+ code[pos + 2] != REOP_match) {
249
+ return false;
250
+ }
251
+ *pchar_pos = char_pos;
252
+ *plength = length;
253
+ *pencoding = encoding;
254
+ return true;
255
+ }
256
+
257
+ /* Detect a required literal prefix (a run of chars that any match must begin
258
+ with), possibly shared by a leading two-way alternation. */
259
+ static bool re_get_linear_prefix(const uint8_t *bc_buf, size_t bc_buf_len,
260
+ int re_flags, size_t *pentry_pos,
261
+ size_t *pchar_pos, uint32_t *plength,
262
+ int *pencoding)
263
+ {
264
+ const uint8_t *code;
265
+ size_t pos, pos1, pos2, target;
266
+ uint32_t length, c, c1, c2;
267
+ int encoding;
268
+
269
+ if ((re_flags & (LRE_FLAG_IGNORECASE | LRE_FLAG_STICKY |
270
+ LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) ||
271
+ bc_buf_len < RE_HEADER_LEN + 16 ||
272
+ !re_get_scan_entry(bc_buf, bc_buf_len, &pos)) {
273
+ return false;
274
+ }
275
+ code = bc_buf + RE_HEADER_LEN;
276
+ bc_buf_len -= RE_HEADER_LEN;
277
+
278
+ pos = 13;
279
+ if (code[pos] == REOP_split_goto_first ||
280
+ code[pos] == REOP_split_next_first) {
281
+ if (pos + 5 > bc_buf_len)
282
+ return false;
283
+ target = pos + 5 + get_i32(code + pos + 1);
284
+ if (target >= bc_buf_len)
285
+ return false;
286
+ pos1 = pos + 5;
287
+ pos2 = target;
288
+ length = 0;
289
+ encoding = 0;
290
+ for (;;) {
291
+ if (pos1 >= bc_buf_len || pos2 >= bc_buf_len)
292
+ break;
293
+ if (code[pos1] == REOP_char && pos1 + 3 <= bc_buf_len) {
294
+ c1 = get_u16(code + pos1 + 1);
295
+ pos1 += 3;
296
+ } else if (code[pos1] == REOP_char32 &&
297
+ pos1 + 5 <= bc_buf_len) {
298
+ c1 = get_u32(code + pos1 + 1);
299
+ pos1 += 5;
300
+ } else {
301
+ break;
302
+ }
303
+ if (code[pos2] == REOP_char && pos2 + 3 <= bc_buf_len) {
304
+ c2 = get_u16(code + pos2 + 1);
305
+ pos2 += 3;
306
+ } else if (code[pos2] == REOP_char32 &&
307
+ pos2 + 5 <= bc_buf_len) {
308
+ c2 = get_u32(code + pos2 + 1);
309
+ pos2 += 5;
310
+ } else {
311
+ break;
312
+ }
313
+ if (c1 != c2 || c1 > 0xffff)
314
+ break;
315
+ if (c1 > 0xff)
316
+ encoding = 1;
317
+ length++;
318
+ }
319
+ if (length == 0)
320
+ return false;
321
+ *pentry_pos = 11;
322
+ *pchar_pos = 18;
323
+ *plength = length;
324
+ *pencoding = encoding;
325
+ return true;
326
+ }
327
+ length = 0;
328
+ encoding = 0;
329
+ while (pos < bc_buf_len) {
330
+ if (code[pos] == REOP_char) {
331
+ if (pos + 3 > bc_buf_len)
332
+ return false;
333
+ c = get_u16(code + pos + 1);
334
+ pos += 3;
335
+ } else if (code[pos] == REOP_char32) {
336
+ if (pos + 5 > bc_buf_len)
337
+ return false;
338
+ c = get_u32(code + pos + 1);
339
+ if (c > 0xffff)
340
+ return false;
341
+ pos += 5;
342
+ } else {
343
+ break;
344
+ }
345
+ if (c > 0xff)
346
+ encoding = 1;
347
+ if (length == UINT32_MAX)
348
+ return false;
349
+ length++;
350
+ }
351
+ if (length == 0)
352
+ return false;
353
+ *pentry_pos = 11;
354
+ *pchar_pos = 13;
355
+ *plength = length;
356
+ *pencoding = encoding;
357
+ return true;
358
+ }
359
+
360
+ /* Append a metadata record (24-byte header + optional decoded payload) to the
361
+ compiled bytecode. */
362
+ static int re_append_metadata(REParseState *s, int kind, size_t entry_pos,
363
+ size_t char_pos, uint32_t length, int encoding,
364
+ int metadata_flags)
365
+ {
366
+ uint8_t header[LRE_META_HEADER_LEN];
367
+ size_t primary_payload_size, pos, code_len;
368
+ uint32_t c;
369
+
370
+ if (entry_pos > UINT32_MAX || (encoding && length > UINT32_MAX / 2))
371
+ return 0;
372
+ primary_payload_size = (metadata_flags & LRE_META_FLAG_SOURCE) ? 0 :
373
+ (size_t)length << encoding;
374
+ if (primary_payload_size > UINT32_MAX)
375
+ return 0;
376
+ code_len = get_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN);
377
+ memset(header, 0, sizeof(header));
378
+ header[LRE_META_VERSION_OFFSET] = LRE_META_VERSION;
379
+ header[LRE_META_KIND_OFFSET] = kind;
380
+ header[LRE_META_ENCODING_OFFSET] = encoding;
381
+ header[LRE_META_FLAGS_OFFSET] = metadata_flags;
382
+ put_u32(header + LRE_META_PAYLOAD_SIZE_OFFSET, primary_payload_size);
383
+ put_u32(header + LRE_META_LENGTH_OFFSET, length);
384
+ put_u32(header + LRE_META_ENTRY_OFFSET, entry_pos);
385
+ dbuf_put(&s->byte_code, header, sizeof(header));
386
+ if (dbuf_error(&s->byte_code))
387
+ return -1;
388
+
389
+ pos = char_pos;
390
+ while (primary_payload_size != 0 && pos < code_len) {
391
+ if (s->byte_code.buf[RE_HEADER_LEN + pos] == REOP_char) {
392
+ c = get_u16(s->byte_code.buf + RE_HEADER_LEN + pos + 1);
393
+ pos += 3;
394
+ } else if (s->byte_code.buf[RE_HEADER_LEN + pos] == REOP_char32) {
395
+ c = get_u32(s->byte_code.buf + RE_HEADER_LEN + pos + 1);
396
+ pos += 5;
397
+ } else {
398
+ break;
399
+ }
400
+ if (encoding)
401
+ dbuf_put_u16(&s->byte_code, c);
402
+ else
403
+ dbuf_putc(&s->byte_code, c);
404
+ if (--length == 0)
405
+ break;
406
+ }
407
+ put_u16(s->byte_code.buf + RE_HEADER_FLAGS,
408
+ lre_get_flags(s->byte_code.buf) | LRE_FLAG_HAS_META);
409
+ return dbuf_error(&s->byte_code) ? -1 : 1;
410
+ }
411
+
412
+ static int re_append_literal_metadata(REParseState *s, int re_flags,
413
+ bool payload_matches_source)
414
+ {
415
+ size_t char_pos, code_len;
416
+ uint32_t length;
417
+ int encoding;
418
+
419
+ code_len = get_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN);
420
+ if (!re_has_literal_body(s->byte_code.buf, RE_HEADER_LEN + code_len,
421
+ re_flags, s->capture_count, &char_pos, &length,
422
+ &encoding)) {
423
+ return 0;
424
+ }
425
+ if (!payload_matches_source && length > LRE_LITERAL_MAX_LENGTH)
426
+ return 0;
427
+ if (re_append_metadata(s, LRE_META_LITERAL, 11, char_pos, length,
428
+ encoding, payload_matches_source ?
429
+ LRE_META_FLAG_SOURCE : 0) < 0)
430
+ return -1;
431
+ put_u16(s->byte_code.buf + RE_HEADER_FLAGS,
432
+ lre_get_flags(s->byte_code.buf) | LRE_FLAG_ATOM);
433
+ return 1;
434
+ }
435
+
436
+ static int re_append_prefix_metadata(REParseState *s, int re_flags)
437
+ {
438
+ size_t entry_pos, char_pos, code_len, pos;
439
+ uint32_t c, i, length;
440
+ int encoding;
441
+
442
+ code_len = get_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN);
443
+ if (!re_get_linear_prefix(s->byte_code.buf, RE_HEADER_LEN + code_len,
444
+ re_flags, &entry_pos, &char_pos, &length,
445
+ &encoding) || length < LRE_PREFIX_MIN_LENGTH) {
446
+ return 0;
447
+ }
448
+ if (length > LRE_PREFIX_MAX_LENGTH) {
449
+ length = LRE_PREFIX_MAX_LENGTH;
450
+ encoding = 0;
451
+ pos = char_pos;
452
+ for(i = 0; i < length; i++) {
453
+ if (s->byte_code.buf[RE_HEADER_LEN + pos] == REOP_char) {
454
+ c = get_u16(s->byte_code.buf + RE_HEADER_LEN + pos + 1);
455
+ pos += 3;
456
+ } else {
457
+ c = get_u32(s->byte_code.buf + RE_HEADER_LEN + pos + 1);
458
+ pos += 5;
459
+ }
460
+ if (c > 0xff)
461
+ encoding = 1;
462
+ }
463
+ }
464
+ return re_append_metadata(s, LRE_META_PREFIX, entry_pos, char_pos,
465
+ length, encoding, 0);
466
+ }
467
+
468
+ static bool re_get_first_char_set(const uint8_t *bc_buf, size_t bc_buf_len,
469
+ int re_flags, uint8_t *bitmap, int *phas_wide);
470
+
471
+ /* Derive the set of characters that a match must begin with, when the pattern
472
+ opens with a single mandatory character or character class. Fills a 256-bit
473
+ bitmap for code points 0..255 and sets *phas_wide if the first character may
474
+ be > 0xff. Returns false (no filter) for anything not provably a mandatory
475
+ leading char/class, which is always sound: falling back to a full scan can
476
+ never produce a wrong match. */
477
+ #define RE_FIRST_SET_MAX_DEPTH 24
478
+
479
+ /* Recursively OR the set of possible first characters reachable from 'pos'
480
+ into 'bitmap', following alternation splits and gotos and skipping
481
+ zero-width capture bookkeeping. This lets alternation-prefixed patterns
482
+ (e.g. /foo|bar|baz/) benefit from the leading-scan prefilter, not just
483
+ single-char/class prefixes. Returns false (sound: caller falls back to a
484
+ full scan, never a wrong match) for any op it can't statically resolve or
485
+ if the structure is too deep. *phas_wide is OR-accumulated. */
486
+ static bool re_first_set_scan(const uint8_t *code, size_t bc_buf_len,
487
+ size_t pos, uint8_t *bitmap, int *phas_wide,
488
+ int *phas_fold, int depth)
489
+ {
490
+ uint32_t low, high, c;
491
+ int i, n;
492
+
493
+ if (depth > RE_FIRST_SET_MAX_DEPTH)
494
+ return false;
495
+ /* Skip zero-width bookkeeping to reach the first consuming op. REOP_set_i32
496
+ / REOP_set_char_pos as a *leading* op is the counter setup of a counted
497
+ quantifier with quant_min >= 1 (e.g. \d{3}, [ab]{2,5}): the atom that
498
+ follows is executed unconditionally at least once, so its first-char set
499
+ is the mandatory first set of the whole term. (quant_min == 0 quantifiers
500
+ lead with REOP_split_goto_first, handled by the split case below, which
501
+ correctly unions the optional atom with what follows.) This lets counted-
502
+ quantifier-prefixed patterns get the leading scan instead of a full
503
+ position-by-position VM walk. */
504
+ for (;;) {
505
+ if (pos >= bc_buf_len)
506
+ return false;
507
+ if (code[pos] == REOP_save_start || code[pos] == REOP_save_end) {
508
+ pos += 2;
509
+ } else if (code[pos] == REOP_save_reset) {
510
+ pos += 3;
511
+ } else if (code[pos] == REOP_set_i32) {
512
+ pos += 6;
513
+ } else if (code[pos] == REOP_set_char_pos) {
514
+ pos += 2;
515
+ } else {
516
+ break;
517
+ }
518
+ }
519
+ switch (code[pos]) {
520
+ case REOP_split_goto_first:
521
+ case REOP_split_next_first:
522
+ if (pos + 5 > bc_buf_len)
523
+ return false;
524
+ {
525
+ int32_t off = (int32_t)get_u32(code + pos + 1);
526
+ size_t target = pos + 5 + (size_t)off;
527
+ /* both the fall-through and the goto target can start a match */
528
+ if (!re_first_set_scan(code, bc_buf_len, pos + 5, bitmap,
529
+ phas_wide, phas_fold, depth + 1))
530
+ return false;
531
+ if (target > bc_buf_len)
532
+ return false;
533
+ return re_first_set_scan(code, bc_buf_len, target, bitmap,
534
+ phas_wide, phas_fold, depth + 1);
535
+ }
536
+ case REOP_goto:
537
+ if (pos + 5 > bc_buf_len)
538
+ return false;
539
+ {
540
+ int32_t off = (int32_t)get_u32(code + pos + 1);
541
+ size_t target = pos + 5 + (size_t)off;
542
+ if (target > bc_buf_len)
543
+ return false;
544
+ return re_first_set_scan(code, bc_buf_len, target, bitmap,
545
+ phas_wide, phas_fold, depth + 1);
546
+ }
547
+ case REOP_char_i:
548
+ *phas_fold = 1;
549
+ /* fallthrough */
550
+ case REOP_char:
551
+ if (pos + 3 > bc_buf_len)
552
+ return false;
553
+ c = get_u16(code + pos + 1);
554
+ if (c <= 0xff)
555
+ bitmap[c >> 3] |= 1 << (c & 7);
556
+ else
557
+ *phas_wide = 1;
558
+ return true;
559
+ case REOP_char32_i:
560
+ *phas_fold = 1;
561
+ /* fallthrough */
562
+ case REOP_char32:
563
+ if (pos + 5 > bc_buf_len)
564
+ return false;
565
+ c = get_u32(code + pos + 1);
566
+ if (c <= 0xff)
567
+ bitmap[c >> 3] |= 1 << (c & 7);
568
+ else
569
+ *phas_wide = 1;
570
+ return true;
571
+ #if JS_RE_PREFILTER
572
+ case REOP_space:
573
+ /* W2: \s has its own opcode, so it fell through to `default: return
574
+ false` and every \s-led pattern got NO prefilter at all. The executor tests lre_is_space(), which
575
+ for c < 256 is exactly lre_is_space_byte(); the non-ASCII members
576
+ (U+1680, U+2000..U+200A, U+2028/9, U+202F, U+205F, U+3000, U+FEFF)
577
+ are covered by has_wide. */
578
+ {
579
+ int b;
580
+ for (b = 0; b < 256; b++)
581
+ if (lre_is_space_byte((uint8_t)b))
582
+ bitmap[b >> 3] |= 1 << (b & 7);
583
+ }
584
+ *phas_wide = 1;
585
+ return true;
586
+ #endif /* JS_RE_PREFILTER -- REOP_space first set */
587
+ case REOP_range_i:
588
+ *phas_fold = 1;
589
+ /* fallthrough */
590
+ case REOP_range:
591
+ if (pos + 3 > bc_buf_len)
592
+ return false;
593
+ n = get_u16(code + pos + 1);
594
+ if (n == 0 || (size_t)n > (bc_buf_len - pos - 3) / 4)
595
+ return false;
596
+ for (i = 0; i < n; i++) {
597
+ low = get_u16(code + pos + 3 + i * 4);
598
+ high = get_u16(code + pos + 5 + i * 4);
599
+ if (high > 0xff)
600
+ *phas_wide = 1;
601
+ if (low > 0xff)
602
+ continue;
603
+ if (high > 0xff)
604
+ high = 0xff;
605
+ for (c = low; c <= high; c++)
606
+ bitmap[c >> 3] |= 1 << (c & 7);
607
+ }
608
+ return true;
609
+ case REOP_range32_i:
610
+ *phas_fold = 1;
611
+ /* fallthrough */
612
+ case REOP_range32:
613
+ if (pos + 3 > bc_buf_len)
614
+ return false;
615
+ n = get_u16(code + pos + 1);
616
+ if (n == 0 || (size_t)n > (bc_buf_len - pos - 3) / 8)
617
+ return false;
618
+ for (i = 0; i < n; i++) {
619
+ low = get_u32(code + pos + 3 + i * 8);
620
+ high = get_u32(code + pos + 7 + i * 8);
621
+ if (high > 0xff)
622
+ *phas_wide = 1;
623
+ if (low > 0xff)
624
+ continue;
625
+ if (high > 0xff)
626
+ high = 0xff;
627
+ for (c = low; c <= high; c++)
628
+ bitmap[c >> 3] |= 1 << (c & 7);
629
+ }
630
+ return true;
631
+ default:
632
+ return false;
633
+ }
634
+ }
635
+
636
+ static bool re_get_first_char_set(const uint8_t *bc_buf, size_t bc_buf_len,
637
+ int re_flags, uint8_t *bitmap, int *phas_wide)
638
+ {
639
+ const uint8_t *code;
640
+ size_t entry_pos;
641
+ int has_fold;
642
+
643
+ /* Case-insensitive first sets use the canonicalized character closure.
644
+ Sticky and Unicode-surrogate cases remain on the generic path. */
645
+ if ((re_flags & (LRE_FLAG_STICKY |
646
+ LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) ||
647
+ bc_buf_len < RE_HEADER_LEN + 14 ||
648
+ !re_get_scan_entry(bc_buf, bc_buf_len, &entry_pos)) {
649
+ return false;
650
+ }
651
+ code = bc_buf + RE_HEADER_LEN;
652
+ bc_buf_len -= RE_HEADER_LEN;
653
+
654
+ memset(bitmap, 0, LRE_FIRST_SET_BITMAP_LEN);
655
+ *phas_wide = 0;
656
+ has_fold = 0;
657
+ /* Scan from pos 13 (past the ".*?" scan prologue + its save_start 0),
658
+ following alternation splits/gotos to union every branch's first char.
659
+ Case-folding (_i) leading ops store canonical values and set has_fold. */
660
+ if (!re_first_set_scan(code, bc_buf_len, 13, bitmap, phas_wide, &has_fold, 0))
661
+ return false;
662
+
663
+ if (has_fold) {
664
+ /* Add every input byte that canonicalizes into the scanned set (e.g.
665
+ 'H' -> {'h','H'}), so the NEON scan can locate /i candidates that
666
+ lre_exec_at then verifies. UNION with — never replace — the scanned
667
+ bits: a case-sensitive REOP_char (from (?-i:...)) may store a value
668
+ that is not a fixed-point of canonicalization, so replacing would
669
+ drop it and miss real matches. Over-inclusion is always sound; a wide
670
+ char may also fold into the set, so mark has_wide (the wide path
671
+ rechecks every position). Non-unicode canonicalization only. */
672
+ uint8_t closure[LRE_FIRST_SET_BITMAP_LEN];
673
+ int b;
674
+ uint32_t cb;
675
+ memset(closure, 0, sizeof(closure));
676
+ for (b = 0; b < 256; b++) {
677
+ cb = lre_canonicalize(b, false);
678
+ if (cb <= 0xff && ((bitmap[cb >> 3] >> (cb & 7)) & 1))
679
+ closure[b >> 3] |= 1 << (b & 7);
680
+ }
681
+ for (b = 0; b < LRE_FIRST_SET_BITMAP_LEN; b++)
682
+ bitmap[b] |= closure[b];
683
+ *phas_wide = 1;
684
+ }
685
+ return true;
686
+ }
687
+
688
+ static int re_append_first_set_metadata(REParseState *s, int re_flags)
689
+ {
690
+ uint8_t header[LRE_META_HEADER_LEN];
691
+ uint8_t bitmap[LRE_FIRST_SET_BITMAP_LEN];
692
+ size_t code_len;
693
+ int has_wide;
694
+
695
+ code_len = get_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN);
696
+ if (!re_get_first_char_set(s->byte_code.buf, RE_HEADER_LEN + code_len,
697
+ re_flags, bitmap, &has_wide)) {
698
+ return 0;
699
+ }
700
+ memset(header, 0, sizeof(header));
701
+ header[LRE_META_VERSION_OFFSET] = LRE_META_VERSION;
702
+ header[LRE_META_KIND_OFFSET] = LRE_META_FIRST_SET;
703
+ put_u32(header + LRE_META_PAYLOAD_SIZE_OFFSET, LRE_FIRST_SET_PAYLOAD_LEN);
704
+ put_u32(header + LRE_META_LENGTH_OFFSET, 0);
705
+ put_u32(header + LRE_META_ENTRY_OFFSET, 11);
706
+ dbuf_put(&s->byte_code, header, sizeof(header));
707
+ dbuf_putc(&s->byte_code, has_wide);
708
+ dbuf_put(&s->byte_code, bitmap, sizeof(bitmap));
709
+ put_u16(s->byte_code.buf + RE_HEADER_FLAGS,
710
+ lre_get_flags(s->byte_code.buf) | LRE_FLAG_HAS_META);
711
+ return dbuf_error(&s->byte_code) ? -1 : 1;
712
+ }
713
+
714
+ /* Return the complete length of one serialized instruction, or false when
715
+ the instruction is truncated or its opcode is not in the table. */
716
+ static bool re_checked_op_len(const uint8_t *code, size_t remaining,
717
+ size_t *plen)
718
+ {
719
+ uint8_t opcode;
720
+ size_t len;
721
+ uint32_t count;
722
+
723
+ if (remaining == 0)
724
+ return false;
725
+ opcode = code[0];
726
+ if (opcode >= REOP_COUNT || opcode == REOP_invalid)
727
+ return false;
728
+ len = reopcode_info[opcode].size;
729
+ if (len == 0 || len > remaining)
730
+ return false;
731
+ switch (opcode) {
732
+ case REOP_back_reference:
733
+ case REOP_back_reference_i:
734
+ case REOP_backward_back_reference:
735
+ case REOP_backward_back_reference_i:
736
+ if (remaining < 2 || code[1] > remaining - 2)
737
+ return false;
738
+ len = 2 + code[1];
739
+ break;
740
+ case REOP_range:
741
+ case REOP_range_i:
742
+ if (remaining < 3)
743
+ return false;
744
+ count = get_u16(code + 1);
745
+ if (count == 0 || count > (remaining - 3) / 4)
746
+ return false;
747
+ len = 3 + (size_t)count * 4;
748
+ break;
749
+ case REOP_range32:
750
+ case REOP_range32_i:
751
+ if (remaining < 3)
752
+ return false;
753
+ count = get_u16(code + 1);
754
+ if (count == 0 || count > (remaining - 3) / 8)
755
+ return false;
756
+ len = 3 + (size_t)count * 8;
757
+ break;
758
+ default:
759
+ break;
760
+ }
761
+ if (len > remaining)
762
+ return false;
763
+ *plen = len;
764
+ return true;
765
+ }
766
+
767
+ static bool re_boundary_get(const uint8_t *boundary_bits, size_t pos)
768
+ {
769
+ return (boundary_bits[pos >> 3] >> (pos & 7)) & 1;
770
+ }
771
+
772
+ static void re_boundary_set(uint8_t *boundary_bits, size_t pos)
773
+ {
774
+ boundary_bits[pos >> 3] |= (uint8_t)1 << (pos & 7);
775
+ }
776
+
777
+ static bool re_validate_target(const uint8_t *boundary_bits, size_t code_len,
778
+ size_t base, uint32_t raw_offset);
779
+
780
+ static bool re_validate_code(const uint8_t *code, size_t code_len,
781
+ uint8_t capture_count, uint8_t register_count,
782
+ void *opaque)
783
+ {
784
+ uint8_t *boundary_bits;
785
+ size_t boundary_size;
786
+ size_t pos = 0, len;
787
+ uint8_t last_opcode = REOP_invalid;
788
+ bool valid = false;
789
+
790
+ if (code_len == 0 || code_len > (SIZE_MAX - 7) / 8)
791
+ return false;
792
+ boundary_size = (code_len + 7) / 8;
793
+ /* Keep the map compact; the caller supplies the real allocator context. */
794
+ boundary_bits = lre_realloc(opaque, NULL, boundary_size);
795
+ if (!boundary_bits)
796
+ return false;
797
+ memset(boundary_bits, 0, boundary_size);
798
+ re_boundary_set(boundary_bits, 0);
799
+ pos = 0;
800
+ while (pos < code_len) {
801
+ if (!re_checked_op_len(code + pos, code_len - pos, &len))
802
+ goto done;
803
+ last_opcode = code[pos];
804
+ pos += len;
805
+ if (pos < code_len)
806
+ re_boundary_set(boundary_bits, pos);
807
+ }
808
+ if (pos != code_len ||
809
+ code[0] == REOP_lookahead_match ||
810
+ code[0] == REOP_negative_lookahead_match)
811
+ goto done;
812
+ /* Every executable path must have a terminating matcher instruction.
813
+ Matcher-stack state is still part of the trusted bytecode contract. */
814
+ if (last_opcode != REOP_match)
815
+ goto done;
816
+ pos = 0;
817
+ while (pos < code_len) {
818
+ uint8_t opcode = code[pos];
819
+ uint32_t val, val2;
820
+
821
+ if (!re_checked_op_len(code + pos, code_len - pos, &len))
822
+ goto done;
823
+ switch (opcode) {
824
+ case REOP_save_start:
825
+ case REOP_save_end:
826
+ if (code[pos + 1] >= capture_count)
827
+ goto done;
828
+ break;
829
+ case REOP_save_reset:
830
+ val = code[pos + 1];
831
+ val2 = code[pos + 2];
832
+ if (val > val2 || val2 >= capture_count)
833
+ goto done;
834
+ break;
835
+ case REOP_set_i32:
836
+ case REOP_set_char_pos:
837
+ case REOP_check_advance:
838
+ if (code[pos + 1] >= register_count)
839
+ goto done;
840
+ break;
841
+ case REOP_back_reference:
842
+ case REOP_back_reference_i:
843
+ case REOP_backward_back_reference:
844
+ case REOP_backward_back_reference_i:
845
+ for (val = 0; val < code[pos + 1]; val++)
846
+ if (code[pos + 2 + val] >= capture_count)
847
+ goto done;
848
+ break;
849
+ case REOP_goto:
850
+ case REOP_split_goto_first:
851
+ case REOP_split_next_first:
852
+ case REOP_lookahead:
853
+ case REOP_negative_lookahead:
854
+ val = get_u32(code + pos + 1);
855
+ if (!re_validate_target(boundary_bits, code_len, pos + 5, val))
856
+ goto done;
857
+ break;
858
+ case REOP_loop:
859
+ if (code[pos + 1] >= register_count)
860
+ goto done;
861
+ val = get_u32(code + pos + 2);
862
+ if (!re_validate_target(boundary_bits, code_len, pos + 6, val))
863
+ goto done;
864
+ break;
865
+ case REOP_loop_split_goto_first:
866
+ case REOP_loop_split_next_first:
867
+ if (code[pos + 1] >= register_count)
868
+ goto done;
869
+ val = get_u32(code + pos + 6);
870
+ if (!re_validate_target(boundary_bits, code_len, pos + 10, val))
871
+ goto done;
872
+ break;
873
+ case REOP_loop_check_adv_split_goto_first:
874
+ case REOP_loop_check_adv_split_next_first:
875
+ if (register_count < 2 || code[pos + 1] >= register_count - 1)
876
+ goto done;
877
+ val = get_u32(code + pos + 6);
878
+ if (!re_validate_target(boundary_bits, code_len, pos + 10, val))
879
+ goto done;
880
+ break;
881
+ default:
882
+ break;
883
+ }
884
+ pos += len;
885
+ }
886
+ valid = true;
887
+ done:
888
+ lre_realloc(opaque, boundary_bits, 0);
889
+ return valid;
890
+ }
891
+
892
+ /* Metadata entry offsets must land on an instruction boundary and identify
893
+ the synthetic save_start 0 used by lre_exec_at(). */
894
+ static bool re_validate_entry(const uint8_t *code, size_t code_len,
895
+ uint32_t entry_offset)
896
+ {
897
+ size_t pos = 0, len;
898
+
899
+ if (entry_offset > code_len || code_len - entry_offset < 2)
900
+ return false;
901
+ while (pos < entry_offset) {
902
+ if (!re_checked_op_len(code + pos, entry_offset - pos, &len))
903
+ return false;
904
+ pos += len;
905
+ }
906
+ return pos == entry_offset && code[pos] == REOP_save_start &&
907
+ code[pos + 1] == 0;
908
+ }
909
+
910
+ static bool re_validate_target(const uint8_t *boundary_bits, size_t code_len,
911
+ size_t base, uint32_t raw_offset)
912
+ {
913
+ int32_t offset = (int32_t)raw_offset;
914
+ size_t target;
915
+
916
+ if (offset < 0) {
917
+ size_t distance = (size_t)(-(int64_t)offset);
918
+ if (distance > base)
919
+ return false;
920
+ target = base - distance;
921
+ } else {
922
+ size_t distance = (size_t)offset;
923
+ if (distance > SIZE_MAX - base)
924
+ return false;
925
+ target = base + distance;
926
+ }
927
+ return target < code_len && re_boundary_get(boundary_bits, target);
928
+ }
929
+
930
+ /* Parse the metadata trailer. Returns 1 if present and well-formed, 0 if
931
+ absent, -1 if malformed. *ptrailer (if given) receives the byte following
932
+ the metadata (where group names begin). */
933
+ static int lre_parse_metadata(const uint8_t *bc_buf, size_t bc_buf_len,
934
+ LREMetadata *meta, const uint8_t **ptrailer)
935
+ {
936
+ const uint8_t *p, *end;
937
+ size_t code_len;
938
+ uint32_t payload_size;
939
+ int flags;
940
+
941
+ if (meta)
942
+ memset(meta, 0, sizeof(*meta));
943
+ if (!bc_buf || bc_buf_len < RE_HEADER_LEN)
944
+ return -1;
945
+ code_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN);
946
+ if (code_len > bc_buf_len - RE_HEADER_LEN)
947
+ return -1;
948
+ p = bc_buf + RE_HEADER_LEN + code_len;
949
+ end = bc_buf + bc_buf_len;
950
+ flags = get_u16(bc_buf + RE_HEADER_FLAGS);
951
+ if (!(flags & LRE_FLAG_HAS_META)) {
952
+ if (ptrailer)
953
+ *ptrailer = p;
954
+ return 0;
955
+ }
956
+ if ((size_t)(end - p) < LRE_META_HEADER_LEN ||
957
+ p[LRE_META_VERSION_OFFSET] != LRE_META_VERSION ||
958
+ p[LRE_META_KIND_OFFSET] < LRE_META_LITERAL ||
959
+ p[LRE_META_KIND_OFFSET] > LRE_META_FIRST_SET ||
960
+ p[LRE_META_ENCODING_OFFSET] > 1 ||
961
+ (p[LRE_META_FLAGS_OFFSET] & ~LRE_META_FLAG_SOURCE)) {
962
+ return -1;
963
+ }
964
+ payload_size = get_u32(p + LRE_META_PAYLOAD_SIZE_OFFSET);
965
+ if (payload_size > (size_t)(end - p - LRE_META_HEADER_LEN))
966
+ return -1;
967
+ switch (p[LRE_META_KIND_OFFSET]) {
968
+ case LRE_META_LITERAL:
969
+ if (p[LRE_META_FLAGS_OFFSET] & ~LRE_META_FLAG_SOURCE)
970
+ return -1;
971
+ if (p[LRE_META_FLAGS_OFFSET] & LRE_META_FLAG_SOURCE) {
972
+ if (payload_size != 0)
973
+ return -1;
974
+ } else if (get_u32(p + LRE_META_LENGTH_OFFSET) > LRE_LITERAL_MAX_LENGTH ||
975
+ ((uint64_t)get_u32(p + LRE_META_LENGTH_OFFSET) <<
976
+ p[LRE_META_ENCODING_OFFSET]) != payload_size) {
977
+ return -1;
978
+ }
979
+ break;
980
+ case LRE_META_PREFIX:
981
+ if (p[LRE_META_FLAGS_OFFSET] != 0 ||
982
+ get_u32(p + LRE_META_LENGTH_OFFSET) < LRE_PREFIX_MIN_LENGTH ||
983
+ get_u32(p + LRE_META_LENGTH_OFFSET) > LRE_PREFIX_MAX_LENGTH ||
984
+ ((uint64_t)get_u32(p + LRE_META_LENGTH_OFFSET) <<
985
+ p[LRE_META_ENCODING_OFFSET]) != payload_size)
986
+ return -1;
987
+ break;
988
+ case LRE_META_FIRST_SET:
989
+ if (p[LRE_META_ENCODING_OFFSET] != 0 ||
990
+ p[LRE_META_FLAGS_OFFSET] != 0 ||
991
+ get_u32(p + LRE_META_LENGTH_OFFSET) != 0 ||
992
+ payload_size != LRE_FIRST_SET_PAYLOAD_LEN)
993
+ return -1;
994
+ break;
995
+ default:
996
+ return -1;
997
+ }
998
+ if (meta) {
999
+ meta->kind = p[LRE_META_KIND_OFFSET];
1000
+ meta->encoding = p[LRE_META_ENCODING_OFFSET];
1001
+ meta->flags = p[LRE_META_FLAGS_OFFSET];
1002
+ meta->payload_size = payload_size;
1003
+ meta->primary_payload_size = payload_size;
1004
+ meta->length = get_u32(p + LRE_META_LENGTH_OFFSET);
1005
+ meta->entry_offset = get_u32(p + LRE_META_ENTRY_OFFSET);
1006
+ meta->payload = p + LRE_META_HEADER_LEN;
1007
+ meta->leading_chars = NULL;
1008
+ }
1009
+ if (ptrailer)
1010
+ *ptrailer = p + LRE_META_HEADER_LEN + payload_size;
1011
+ return 1;
1012
+ }
1013
+
1014
+ int lre_get_metadata(const uint8_t *bc_buf, size_t bc_buf_len,
1015
+ LREMetadata *meta)
1016
+ {
1017
+ if (!meta)
1018
+ return -1;
1019
+ return lre_parse_metadata(bc_buf, bc_buf_len, meta, NULL);
1020
+ }
1021
+
135
1022
  /* insert 'len' bytes at position 'pos'. Return < 0 if error. */
136
1023
  static int dbuf_insert(DynBuf *s, int pos, int len)
137
1024
  {
@@ -239,6 +1126,8 @@ static __maybe_unused void re_string_list_dump(const char *str, const REStringLi
239
1126
  }
240
1127
  #endif /* DUMP_REOP */
241
1128
 
1129
+ /* 'buf' is NULL if 'len' is zero: the empty string is a valid member of a
1130
+ string set, e.g. /[\q{}]/v */
242
1131
  static int re_string_find2(REStringList *s, int len, const uint32_t *buf,
243
1132
  uint32_t h0, bool add_flag)
244
1133
  {
@@ -248,7 +1137,8 @@ static int re_string_find2(REStringList *s, int len, const uint32_t *buf,
248
1137
  h = h0 >> (32 - s->hash_bits);
249
1138
  for(p = s->hash_table[h]; p != NULL; p = p->next) {
250
1139
  if (p->hash == h0 && p->len == len &&
251
- !memcmp(p->buf, buf, len * sizeof(buf[0]))) {
1140
+ (len == 0 ||
1141
+ !memcmp(p->buf, buf, len * sizeof(buf[0])))) {
252
1142
  return 1;
253
1143
  }
254
1144
  }
@@ -291,7 +1181,8 @@ static int re_string_find2(REStringList *s, int len, const uint32_t *buf,
291
1181
  s->n_strings++;
292
1182
  p->hash = h0;
293
1183
  p->len = len;
294
- memcpy(p->buf, buf, sizeof(buf[0]) * len);
1184
+ if (len != 0)
1185
+ memcpy(p->buf, buf, sizeof(buf[0]) * len);
295
1186
  return 1;
296
1187
  }
297
1188
 
@@ -1021,7 +1912,7 @@ static int parse_class_string_disjunction(REParseState *s, REStringList *cr,
1021
1912
  for(;;) {
1022
1913
  str.size = 0;
1023
1914
  while (*p != '}' && *p != '|') {
1024
- c = get_class_atom(s, NULL, &p, false);
1915
+ c = get_class_atom(s, NULL, &p, true);
1025
1916
  if (c < 0)
1026
1917
  goto fail;
1027
1918
  if (dbuf_put_u32(&str, c)) {
@@ -1115,6 +2006,23 @@ static int get_class_atom(REParseState *s, REStringList *cr,
1115
2006
  if (!inclass && s->is_unicode)
1116
2007
  goto invalid_escape;
1117
2008
  break;
2009
+ case '&':
2010
+ case '!':
2011
+ case '#':
2012
+ case '%':
2013
+ case ',':
2014
+ case ':':
2015
+ case ';':
2016
+ case '<':
2017
+ case '=':
2018
+ case '>':
2019
+ case '@':
2020
+ case '`':
2021
+ case '~':
2022
+ if (s->is_unicode && (!inclass || !s->unicode_sets))
2023
+ /* Only illegal if in unicode mode and not in a class */
2024
+ goto invalid_escape;
2025
+ break;
1118
2026
  case '^':
1119
2027
  case '$':
1120
2028
  case '\\':
@@ -1331,6 +2239,10 @@ static int re_emit_string_list(REParseState *s, const REStringList *sl)
1331
2239
  }
1332
2240
  if (!is_last) {
1333
2241
  last_match_pos = re_emit_op_u32(s, REOP_goto, last_match_pos);
2242
+ /* the positions to patch are only valid if all the byte code
2243
+ could be emitted */
2244
+ if (dbuf_error(&s->byte_code))
2245
+ goto out_of_memory;
1334
2246
  put_u32(s->byte_code.buf + split_pos, s->byte_code.size - (split_pos + 4));
1335
2247
  }
1336
2248
  }
@@ -1342,15 +2254,18 @@ static int re_emit_string_list(REParseState *s, const REStringList *sl)
1342
2254
  split_pos = re_emit_op_u32(s, REOP_split_next_first, 0);
1343
2255
  else
1344
2256
  split_pos = 0; /* not used */
1345
- if (re_emit_range(s, &sl->cr)) {
1346
- lre_realloc(s->opaque, tab, 0);
1347
- return -1;
1348
- }
1349
- if (!is_last)
2257
+ if (re_emit_range(s, &sl->cr))
2258
+ goto fail;
2259
+ if (!is_last) {
2260
+ if (dbuf_error(&s->byte_code))
2261
+ goto out_of_memory;
1350
2262
  put_u32(s->byte_code.buf + split_pos, s->byte_code.size - (split_pos + 4));
2263
+ }
1351
2264
  }
1352
2265
 
1353
2266
  /* patch the 'goto match' */
2267
+ if (dbuf_error(&s->byte_code))
2268
+ goto out_of_memory;
1354
2269
  while (last_match_pos != -1) {
1355
2270
  int next_pos = get_u32(s->byte_code.buf + last_match_pos);
1356
2271
  put_u32(s->byte_code.buf + last_match_pos, s->byte_code.size - (last_match_pos + 4));
@@ -1360,6 +2275,11 @@ static int re_emit_string_list(REParseState *s, const REStringList *sl)
1360
2275
  lre_realloc(s->opaque, tab, 0);
1361
2276
  }
1362
2277
  return 0;
2278
+ out_of_memory:
2279
+ re_parse_out_of_memory(s);
2280
+ fail:
2281
+ lre_realloc(s->opaque, tab, 0);
2282
+ return -1;
1363
2283
  }
1364
2284
 
1365
2285
  static int re_parse_nested_class(REParseState *s, REStringList *cr, const uint8_t **pp);
@@ -1860,6 +2780,11 @@ static int re_parse_term(REParseState *s, bool is_backward_dir)
1860
2780
  bool greedy, is_neg, is_backward_lookahead;
1861
2781
  REStringList cr_s, *cr = &cr_s;
1862
2782
 
2783
+ /* after a failed allocation the byte code is incomplete and 'buf' may
2784
+ still be NULL: stop before computing positions inside it */
2785
+ if (dbuf_error(&s->byte_code))
2786
+ return re_parse_out_of_memory(s);
2787
+
1863
2788
  last_atom_start = -1;
1864
2789
  last_capture_count = 0;
1865
2790
  p = s->buf_ptr;
@@ -1988,7 +2913,7 @@ static int re_parse_term(REParseState *s, bool is_backward_dir)
1988
2913
  re_emit_op(s, REOP_lookahead_match + is_neg);
1989
2914
  /* jump after the 'match' after the lookahead is successful */
1990
2915
  if (dbuf_error(&s->byte_code))
1991
- return -1;
2916
+ return re_parse_out_of_memory(s);
1992
2917
  put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4));
1993
2918
  } else if (p[2] == '<') {
1994
2919
  p += 3;
@@ -2403,7 +3328,7 @@ static int re_parse_alternative(REParseState *s, bool is_backward_dir)
2403
3328
  end = s->byte_code.size;
2404
3329
  term_size = end - term_start;
2405
3330
  if (dbuf_claim(&s->byte_code, term_size))
2406
- return -1;
3331
+ return re_parse_out_of_memory(s);
2407
3332
  memmove(s->byte_code.buf + start + term_size,
2408
3333
  s->byte_code.buf + start,
2409
3334
  end - start);
@@ -2446,7 +3371,10 @@ static int re_parse_disjunction(REParseState *s, bool is_backward_dir)
2446
3371
  if (re_parse_alternative(s, is_backward_dir))
2447
3372
  return -1;
2448
3373
 
2449
- /* patch the goto */
3374
+ /* patch the goto ('pos' is only valid if the placeholder for the
3375
+ offset could be emitted) */
3376
+ if (dbuf_error(&s->byte_code))
3377
+ return re_parse_out_of_memory(s);
2450
3378
  len = s->byte_code.size - (pos + 4);
2451
3379
  put_u32(s->byte_code.buf + pos, len);
2452
3380
  }
@@ -2521,7 +3449,7 @@ static int compute_register_count(uint8_t *bc_buf, int bc_buf_len)
2521
3449
  static void *lre_bytecode_realloc(void *opaque, void *ptr, size_t size)
2522
3450
  {
2523
3451
  if (size > (INT32_MAX / 2)) {
2524
- /* the bytecode cannot be larger than 2G. Leave some slack to
3452
+ /* the bytecode cannot be larger than 2G. Leave some slack to
2525
3453
  avoid some overflows. */
2526
3454
  return NULL;
2527
3455
  } else {
@@ -2538,8 +3466,9 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
2538
3466
  void *opaque)
2539
3467
  {
2540
3468
  REParseState s_s, *s = &s_s;
2541
- int register_count;
3469
+ int register_count, metadata_result;
2542
3470
  bool is_sticky;
3471
+ bool payload_matches_source;
2543
3472
 
2544
3473
  memset(s, 0, sizeof(*s));
2545
3474
  s->opaque = opaque;
@@ -2556,6 +3485,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
2556
3485
  s->capture_count = 1;
2557
3486
  s->total_capture_count = -1;
2558
3487
  s->has_named_captures = -1;
3488
+ payload_matches_source = lre_is_raw_atom(buf, buf_len, re_flags);
2559
3489
 
2560
3490
  dbuf_init2(&s->byte_code, opaque, lre_bytecode_realloc);
2561
3491
  dbuf_init2(&s->group_names, opaque, lre_realloc);
@@ -2610,9 +3540,24 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
2610
3540
  put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN,
2611
3541
  s->byte_code.size - RE_HEADER_LEN);
2612
3542
 
3543
+ /* Append the optional search metadata before the named-group trailer. */
3544
+ metadata_result = re_append_literal_metadata(s, re_flags,
3545
+ payload_matches_source);
3546
+ if (metadata_result == 0)
3547
+ metadata_result = re_append_prefix_metadata(s, re_flags);
3548
+ if (metadata_result == 0)
3549
+ metadata_result = re_append_first_set_metadata(s, re_flags);
3550
+ if (metadata_result < 0) {
3551
+ re_parse_out_of_memory(s);
3552
+ goto error;
3553
+ }
3554
+
2613
3555
  /* add the named groups if needed */
2614
3556
  if (s->group_names.size > (s->capture_count - 1) * LRE_GROUP_NAME_TRAILER_LEN) {
2615
- dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size);
3557
+ if (dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size)) {
3558
+ re_parse_out_of_memory(s);
3559
+ goto error;
3560
+ }
2616
3561
  put_u16(s->byte_code.buf + RE_HEADER_FLAGS,
2617
3562
  lre_get_flags(s->byte_code.buf) | LRE_FLAG_NAMED_GROUPS);
2618
3563
  }
@@ -2785,563 +3730,19 @@ static no_inline int stack_realloc(REExecContext *s, size_t n)
2785
3730
  }
2786
3731
 
2787
3732
  /* return 1 if match, 0 if not match or < 0 if error. */
2788
- static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
2789
- const uint8_t *pc, const uint8_t *cptr)
2790
- {
2791
- int opcode;
2792
- int cbuf_type;
2793
- uint32_t val, c, idx;
2794
- const uint8_t *cbuf_end;
2795
- StackElem *sp, *bp, *stack_end;
2796
- #ifdef DUMP_EXEC
2797
- const uint8_t *pc_start = pc; /* TEST */
2798
- #endif
2799
- cbuf_type = s->cbuf_type;
2800
- cbuf_end = s->cbuf_end;
2801
-
2802
- sp = s->stack_buf;
2803
- bp = s->stack_buf;
2804
- stack_end = s->stack_buf + s->stack_size;
2805
-
2806
- #define CHECK_STACK_SPACE(n) \
2807
- if (unlikely((stack_end - sp) < (n))) { \
2808
- size_t saved_sp = sp - s->stack_buf; \
2809
- size_t saved_bp = bp - s->stack_buf; \
2810
- if (stack_realloc(s, sp - s->stack_buf + (n))) \
2811
- return LRE_RET_MEMORY_ERROR; \
2812
- stack_end = s->stack_buf + s->stack_size; \
2813
- sp = s->stack_buf + saved_sp; \
2814
- bp = s->stack_buf + saved_bp; \
2815
- }
2816
-
2817
- /* XXX: could test if the value was saved to reduce the stack size
2818
- but slower */
2819
- #define SAVE_CAPTURE(idx, value) \
2820
- { \
2821
- CHECK_STACK_SPACE(2); \
2822
- sp[0].val = idx; \
2823
- sp[1].ptr = capture[idx]; \
2824
- sp += 2; \
2825
- capture[idx] = (value); \
2826
- }
2827
-
2828
- /* avoid saving the previous value if already saved */
2829
- #define SAVE_CAPTURE_CHECK(idx, value) \
2830
- { \
2831
- StackElem *sp1; \
2832
- sp1 = sp; \
2833
- for(;;) { \
2834
- if (sp1 > bp) { \
2835
- if (sp1[-2].val == idx) \
2836
- break; \
2837
- sp1 -= 2; \
2838
- } else { \
2839
- CHECK_STACK_SPACE(2); \
2840
- sp[0].val = idx; \
2841
- sp[1].ptr = capture[idx]; \
2842
- sp += 2; \
2843
- break; \
2844
- } \
2845
- } \
2846
- capture[idx] = (value); \
2847
- }
2848
-
2849
-
2850
- #ifdef DUMP_EXEC
2851
- printf("%5s %5s %5s %5s %s\n", "PC", "CP", "BP", "SP", "OPCODE");
2852
- #endif
2853
- for(;;) {
2854
- opcode = *pc++;
2855
- #ifdef DUMP_EXEC
2856
- printf("%5ld %5ld %5ld %5ld %s\n",
2857
- pc - 1 - pc_start,
2858
- cbuf_type == 0 ? cptr - s->cbuf : (cptr - s->cbuf) / 2,
2859
- bp - s->stack_buf,
2860
- sp - s->stack_buf,
2861
- reopcode_info[opcode].name);
2862
- #endif
2863
- switch(opcode) {
2864
- case REOP_match:
2865
- return 1;
2866
- no_match:
2867
- for(;;) {
2868
- REExecStateEnum type;
2869
- if (bp == s->stack_buf)
2870
- return 0;
2871
- /* undo the modifications to capture[] */
2872
- while (sp > bp) {
2873
- capture[sp[-2].val] = sp[-1].ptr;
2874
- sp -= 2;
2875
- }
2876
-
2877
- pc = sp[-3].ptr;
2878
- cptr = sp[-2].ptr;
2879
- type = sp[-1].bp.type;
2880
- bp = s->stack_buf + sp[-1].bp.val;
2881
- sp -= 3;
2882
- if (type != RE_EXEC_STATE_LOOKAHEAD)
2883
- break;
2884
- }
2885
- if (lre_poll_timeout(s))
2886
- return LRE_RET_TIMEOUT;
2887
- break;
2888
- case REOP_lookahead_match:
2889
- /* pop all the saved states until reaching the start of
2890
- the lookahead and keep the updated captures and
2891
- variables and the corresponding undo info. */
2892
- {
2893
- StackElem *sp1, *sp_top, *next_sp;
2894
- REExecStateEnum type;
2895
-
2896
- sp_top = sp;
2897
- for(;;) {
2898
- sp1 = sp;
2899
- sp = bp;
2900
- pc = sp[-3].ptr;
2901
- cptr = sp[-2].ptr;
2902
- type = sp[-1].bp.type;
2903
- bp = s->stack_buf + sp[-1].bp.val;
2904
- sp[-1].ptr = (void *)sp1; /* save the next value for the copy step */
2905
- sp -= 3;
2906
- if (type == RE_EXEC_STATE_LOOKAHEAD)
2907
- break;
2908
- }
2909
- if (sp != s->stack_buf) {
2910
- /* keep the undo info if there is a saved state */
2911
- sp1 = sp;
2912
- while (sp1 < sp_top) {
2913
- next_sp = (void *)sp1[2].ptr;
2914
- sp1 += 3;
2915
- while (sp1 < next_sp)
2916
- *sp++ = *sp1++;
2917
- }
2918
- }
2919
- }
2920
- break;
2921
- case REOP_negative_lookahead_match:
2922
- /* pop all the saved states until reaching start of the negative lookahead */
2923
- for(;;) {
2924
- REExecStateEnum type;
2925
- type = bp[-1].bp.type;
2926
- /* undo the modifications to capture[] */
2927
- while (sp > bp) {
2928
- capture[sp[-2].val] = sp[-1].ptr;
2929
- sp -= 2;
2930
- }
2931
- pc = sp[-3].ptr;
2932
- cptr = sp[-2].ptr;
2933
- type = sp[-1].bp.type;
2934
- bp = s->stack_buf + sp[-1].bp.val;
2935
- sp -= 3;
2936
- if (type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD)
2937
- break;
2938
- }
2939
- goto no_match;
2940
- case REOP_char32:
2941
- case REOP_char32_i:
2942
- val = get_u32(pc);
2943
- pc += 4;
2944
- goto test_char;
2945
- case REOP_char:
2946
- case REOP_char_i:
2947
- val = get_u16(pc);
2948
- pc += 2;
2949
- test_char:
2950
- if (cptr >= cbuf_end)
2951
- goto no_match;
2952
- GET_CHAR(c, cptr, cbuf_end, cbuf_type);
2953
- if (opcode == REOP_char_i || opcode == REOP_char32_i) {
2954
- c = lre_canonicalize(c, s->is_unicode);
2955
- }
2956
- if (val != c)
2957
- goto no_match;
2958
- break;
2959
- case REOP_split_goto_first:
2960
- case REOP_split_next_first:
2961
- {
2962
- const uint8_t *pc1;
2963
-
2964
- val = get_u32(pc);
2965
- pc += 4;
2966
- if (opcode == REOP_split_next_first) {
2967
- pc1 = pc + (int)val;
2968
- } else {
2969
- pc1 = pc;
2970
- pc = pc + (int)val;
2971
- }
2972
- CHECK_STACK_SPACE(3);
2973
- sp[0].ptr = (uint8_t *)pc1;
2974
- sp[1].ptr = (uint8_t *)cptr;
2975
- sp[2].bp.val = bp - s->stack_buf;
2976
- sp[2].bp.type = RE_EXEC_STATE_SPLIT;
2977
- sp += 3;
2978
- bp = sp;
2979
- }
2980
- break;
2981
- case REOP_lookahead:
2982
- case REOP_negative_lookahead:
2983
- val = get_u32(pc);
2984
- pc += 4;
2985
- CHECK_STACK_SPACE(3);
2986
- sp[0].ptr = (uint8_t *)(pc + (int)val);
2987
- sp[1].ptr = (uint8_t *)cptr;
2988
- sp[2].bp.val = bp - s->stack_buf;
2989
- sp[2].bp.type = RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead;
2990
- sp += 3;
2991
- bp = sp;
2992
- break;
2993
- case REOP_goto:
2994
- val = get_u32(pc);
2995
- pc += 4 + (int)val;
2996
- if (lre_poll_timeout(s))
2997
- return LRE_RET_TIMEOUT;
2998
- break;
2999
- case REOP_line_start:
3000
- case REOP_line_start_m:
3001
- if (cptr == s->cbuf)
3002
- break;
3003
- if (opcode == REOP_line_start)
3004
- goto no_match;
3005
- PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type);
3006
- if (!is_line_terminator(c))
3007
- goto no_match;
3008
- break;
3009
- case REOP_line_end:
3010
- case REOP_line_end_m:
3011
- if (cptr == cbuf_end)
3012
- break;
3013
- if (opcode == REOP_line_end)
3014
- goto no_match;
3015
- PEEK_CHAR(c, cptr, cbuf_end, cbuf_type);
3016
- if (!is_line_terminator(c))
3017
- goto no_match;
3018
- break;
3019
- case REOP_dot:
3020
- if (cptr == cbuf_end)
3021
- goto no_match;
3022
- GET_CHAR(c, cptr, cbuf_end, cbuf_type);
3023
- if (is_line_terminator(c))
3024
- goto no_match;
3025
- break;
3026
- case REOP_any:
3027
- if (cptr == cbuf_end)
3028
- goto no_match;
3029
- GET_CHAR(c, cptr, cbuf_end, cbuf_type);
3030
- break;
3031
- case REOP_space:
3032
- if (cptr == cbuf_end)
3033
- goto no_match;
3034
- GET_CHAR(c, cptr, cbuf_end, cbuf_type);
3035
- if (!lre_is_space(c))
3036
- goto no_match;
3037
- break;
3038
- case REOP_not_space:
3039
- if (cptr == cbuf_end)
3040
- goto no_match;
3041
- GET_CHAR(c, cptr, cbuf_end, cbuf_type);
3042
- if (lre_is_space(c))
3043
- goto no_match;
3044
- break;
3045
- case REOP_save_start:
3046
- case REOP_save_end:
3047
- val = *pc++;
3048
- if (val >= (uint32_t)s->capture_count)
3049
- return LRE_RET_BYTECODE_ERROR;
3050
- idx = 2 * val + opcode - REOP_save_start;
3051
- SAVE_CAPTURE(idx, (uint8_t *)cptr);
3052
- break;
3053
- case REOP_save_reset:
3054
- {
3055
- uint32_t val2;
3056
- val = pc[0];
3057
- val2 = pc[1];
3058
- pc += 2;
3059
- if (val2 >= (uint32_t)s->capture_count)
3060
- return LRE_RET_BYTECODE_ERROR;
3061
- CHECK_STACK_SPACE(2 * (val2 - val + 1));
3062
- while (val <= val2) {
3063
- idx = 2 * val;
3064
- SAVE_CAPTURE(idx, NULL);
3065
- idx = 2 * val + 1;
3066
- SAVE_CAPTURE(idx, NULL);
3067
- val++;
3068
- }
3069
- }
3070
- break;
3071
- case REOP_set_i32:
3072
- idx = 2 * s->capture_count + pc[0];
3073
- val = get_u32(pc + 1);
3074
- pc += 5;
3075
- SAVE_CAPTURE_CHECK(idx, (void *)(uintptr_t)val);
3076
- break;
3077
- case REOP_loop:
3078
- {
3079
- uint32_t val2;
3080
- idx = 2 * s->capture_count + pc[0];
3081
- val = get_u32(pc + 1);
3082
- pc += 5;
3083
-
3084
- val2 = (uintptr_t)capture[idx] - 1;
3085
- SAVE_CAPTURE_CHECK(idx, (void *)(uintptr_t)val2);
3086
- if (val2 != 0) {
3087
- pc += (int)val;
3088
- if (lre_poll_timeout(s))
3089
- return LRE_RET_TIMEOUT;
3090
- }
3091
- }
3092
- break;
3093
- case REOP_loop_split_goto_first:
3094
- case REOP_loop_split_next_first:
3095
- case REOP_loop_check_adv_split_goto_first:
3096
- case REOP_loop_check_adv_split_next_first:
3097
- {
3098
- const uint8_t *pc1;
3099
- uint32_t val2, limit;
3100
- idx = 2 * s->capture_count + pc[0];
3101
- limit = get_u32(pc + 1);
3102
- val = get_u32(pc + 5);
3103
- pc += 9;
3104
-
3105
- /* decrement the counter */
3106
- val2 = (uintptr_t)capture[idx] - 1;
3107
- SAVE_CAPTURE_CHECK(idx, (void *)(uintptr_t)val2);
3108
-
3109
- if (val2 > limit) {
3110
- /* normal loop if counter > limit */
3111
- pc += (int)val;
3112
- if (lre_poll_timeout(s))
3113
- return LRE_RET_TIMEOUT;
3114
- } else {
3115
- /* check advance */
3116
- if ((opcode == REOP_loop_check_adv_split_goto_first ||
3117
- opcode == REOP_loop_check_adv_split_next_first) &&
3118
- capture[idx + 1] == cptr &&
3119
- val2 != limit) {
3120
- goto no_match;
3121
- }
3122
-
3123
- /* otherwise conditional split */
3124
- if (val2 != 0) {
3125
- if (opcode == REOP_loop_split_next_first ||
3126
- opcode == REOP_loop_check_adv_split_next_first) {
3127
- pc1 = pc + (int)val;
3128
- } else {
3129
- pc1 = pc;
3130
- pc = pc + (int)val;
3131
- }
3132
- CHECK_STACK_SPACE(3);
3133
- sp[0].ptr = (uint8_t *)pc1;
3134
- sp[1].ptr = (uint8_t *)cptr;
3135
- sp[2].bp.val = bp - s->stack_buf;
3136
- sp[2].bp.type = RE_EXEC_STATE_SPLIT;
3137
- sp += 3;
3138
- bp = sp;
3139
- }
3140
- }
3141
- }
3142
- break;
3143
- case REOP_set_char_pos:
3144
- idx = 2 * s->capture_count + pc[0];
3145
- pc++;
3146
- SAVE_CAPTURE_CHECK(idx, (uint8_t *)cptr);
3147
- break;
3148
- case REOP_check_advance:
3149
- idx = 2 * s->capture_count + pc[0];
3150
- pc++;
3151
- if (capture[idx] == cptr)
3152
- goto no_match;
3153
- break;
3154
- case REOP_word_boundary:
3155
- case REOP_word_boundary_i:
3156
- case REOP_not_word_boundary:
3157
- case REOP_not_word_boundary_i:
3158
- {
3159
- bool v1, v2;
3160
- int ignore_case = (opcode == REOP_word_boundary_i || opcode == REOP_not_word_boundary_i);
3161
- bool is_boundary = (opcode == REOP_word_boundary || opcode == REOP_word_boundary_i);
3162
- /* char before */
3163
- if (cptr == s->cbuf) {
3164
- v1 = false;
3165
- } else {
3166
- PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type);
3167
- if (c < 256) {
3168
- v1 = (lre_is_word_byte(c) != 0);
3169
- } else {
3170
- v1 = ignore_case && (c == 0x017f || c == 0x212a);
3171
- }
3172
- }
3173
- /* current char */
3174
- if (cptr >= cbuf_end) {
3175
- v2 = false;
3176
- } else {
3177
- PEEK_CHAR(c, cptr, cbuf_end, cbuf_type);
3178
- if (c < 256) {
3179
- v2 = (lre_is_word_byte(c) != 0);
3180
- } else {
3181
- v2 = ignore_case && (c == 0x017f || c == 0x212a);
3182
- }
3183
- }
3184
- if (v1 ^ v2 ^ is_boundary)
3185
- goto no_match;
3186
- }
3187
- break;
3188
- case REOP_back_reference:
3189
- case REOP_back_reference_i:
3190
- case REOP_backward_back_reference:
3191
- case REOP_backward_back_reference_i:
3192
- {
3193
- const uint8_t *cptr1, *cptr1_end, *cptr1_start;
3194
- const uint8_t *pc1;
3195
- uint32_t c1, c2;
3196
- int i, n;
3197
-
3198
- n = *pc++;
3199
- pc1 = pc;
3200
- pc += n;
3201
-
3202
- for(i = 0; i < n; i++) {
3203
- val = pc1[i];
3204
- if (val >= s->capture_count)
3205
- goto no_match;
3206
- cptr1_start = capture[2 * val];
3207
- cptr1_end = capture[2 * val + 1];
3208
- /* test the first not empty capture */
3209
- if (cptr1_start && cptr1_end) {
3210
- if (opcode == REOP_back_reference ||
3211
- opcode == REOP_back_reference_i) {
3212
- cptr1 = cptr1_start;
3213
- while (cptr1 < cptr1_end) {
3214
- if (cptr >= cbuf_end)
3215
- goto no_match;
3216
- GET_CHAR(c1, cptr1, cptr1_end, cbuf_type);
3217
- GET_CHAR(c2, cptr, cbuf_end, cbuf_type);
3218
- if (opcode == REOP_back_reference_i) {
3219
- c1 = lre_canonicalize(c1, s->is_unicode);
3220
- c2 = lre_canonicalize(c2, s->is_unicode);
3221
- }
3222
- if (c1 != c2)
3223
- goto no_match;
3224
- }
3225
- } else {
3226
- cptr1 = cptr1_end;
3227
- while (cptr1 > cptr1_start) {
3228
- if (cptr == s->cbuf)
3229
- goto no_match;
3230
- GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type);
3231
- GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type);
3232
- if (opcode == REOP_backward_back_reference_i) {
3233
- c1 = lre_canonicalize(c1, s->is_unicode);
3234
- c2 = lre_canonicalize(c2, s->is_unicode);
3235
- }
3236
- if (c1 != c2)
3237
- goto no_match;
3238
- }
3239
- }
3240
- break;
3241
- }
3242
- }
3243
- }
3244
- break;
3245
- case REOP_range:
3246
- case REOP_range_i:
3247
- {
3248
- int n;
3249
- uint32_t low, high, idx_min, idx_max, idx;
3250
-
3251
- n = get_u16(pc); /* n must be >= 1 */
3252
- pc += 2;
3253
- if (cptr >= cbuf_end)
3254
- goto no_match;
3255
- GET_CHAR(c, cptr, cbuf_end, cbuf_type);
3256
- if (opcode == REOP_range_i) {
3257
- c = lre_canonicalize(c, s->is_unicode);
3258
- }
3259
- idx_min = 0;
3260
- low = get_u16(pc + 0 * 4);
3261
- if (c < low)
3262
- goto no_match;
3263
- idx_max = n - 1;
3264
- high = get_u16(pc + idx_max * 4 + 2);
3265
- /* 0xffff in for last value means +infinity */
3266
- if (unlikely(c >= 0xffff) && high == 0xffff)
3267
- goto range_match;
3268
- if (c > high)
3269
- goto no_match;
3270
- while (idx_min <= idx_max) {
3271
- idx = (idx_min + idx_max) / 2;
3272
- low = get_u16(pc + idx * 4);
3273
- high = get_u16(pc + idx * 4 + 2);
3274
- if (c < low)
3275
- idx_max = idx - 1;
3276
- else if (c > high)
3277
- idx_min = idx + 1;
3278
- else
3279
- goto range_match;
3280
- }
3281
- goto no_match;
3282
- range_match:
3283
- pc += 4 * n;
3284
- }
3285
- break;
3286
- case REOP_range32:
3287
- case REOP_range32_i:
3288
- {
3289
- int n;
3290
- uint32_t low, high, idx_min, idx_max, idx;
3291
-
3292
- n = get_u16(pc); /* n must be >= 1 */
3293
- pc += 2;
3294
- if (cptr >= cbuf_end)
3295
- goto no_match;
3296
- GET_CHAR(c, cptr, cbuf_end, cbuf_type);
3297
- if (opcode == REOP_range32_i) {
3298
- c = lre_canonicalize(c, s->is_unicode);
3299
- }
3300
- idx_min = 0;
3301
- low = get_u32(pc + 0 * 8);
3302
- if (c < low)
3303
- goto no_match;
3304
- idx_max = n - 1;
3305
- high = get_u32(pc + idx_max * 8 + 4);
3306
- if (c > high)
3307
- goto no_match;
3308
- while (idx_min <= idx_max) {
3309
- idx = (idx_min + idx_max) / 2;
3310
- low = get_u32(pc + idx * 8);
3311
- high = get_u32(pc + idx * 8 + 4);
3312
- if (c < low)
3313
- idx_max = idx - 1;
3314
- else if (c > high)
3315
- idx_min = idx + 1;
3316
- else
3317
- goto range32_match;
3318
- }
3319
- goto no_match;
3320
- range32_match:
3321
- pc += 8 * n;
3322
- }
3323
- break;
3324
- case REOP_prev:
3325
- /* go to the previous char */
3326
- if (cptr == s->cbuf)
3327
- goto no_match;
3328
- PREV_CHAR(cptr, s->cbuf, cbuf_type);
3329
- break;
3330
- default:
3331
- #ifdef DUMP_EXEC
3332
- printf("unknown opcode pc=%ld\n", pc - 1 - pc_start);
3333
- #endif
3334
- abort();
3335
- }
3336
- }
3337
- }
3733
+ #define LRE_BT_FUNC lre_exec_backtrack
3734
+ #define LRE_CBUF_TYPE_INIT (s->cbuf_type)
3735
+ #include "libregexp-backtrack.c.inc"
3736
+ #undef LRE_BT_FUNC
3737
+ #undef LRE_CBUF_TYPE_INIT
3338
3738
 
3339
3739
  /* Return 1 if match, 0 if not match or < 0 if error (see LRE_RET_x). cindex is the
3340
3740
  starting position of the match and must be such as 0 <= cindex <=
3341
3741
  clen. */
3342
- int lre_exec(uint8_t **capture,
3343
- const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
3344
- int cbuf_type, void *opaque)
3742
+ static int lre_exec_internal(uint8_t **capture, const uint8_t *bc_buf,
3743
+ uint32_t bytecode_offset,
3744
+ const uint8_t *cbuf, int cindex, int clen,
3745
+ int cbuf_type, void *opaque)
3345
3746
  {
3346
3747
  REExecContext s_s, *s = &s_s;
3347
3748
  int re_flags, i, ret;
@@ -3372,13 +3773,42 @@ int lre_exec(uint8_t **capture,
3372
3773
  }
3373
3774
  }
3374
3775
 
3375
- ret = lre_exec_backtrack(s, capture, bc_buf + RE_HEADER_LEN, cptr);
3776
+ ret = lre_exec_backtrack(s, capture,
3777
+ bc_buf + RE_HEADER_LEN + bytecode_offset, cptr);
3376
3778
 
3377
3779
  if (s->stack_buf != s->static_stack_buf)
3378
3780
  lre_realloc(s->opaque, s->stack_buf, 0);
3379
3781
  return ret;
3380
3782
  }
3381
3783
 
3784
+ int lre_exec(uint8_t **capture,
3785
+ const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
3786
+ int cbuf_type, void *opaque)
3787
+ {
3788
+ return lre_exec_internal(capture, bc_buf, 0, cbuf, cindex, clen,
3789
+ cbuf_type, opaque);
3790
+ }
3791
+
3792
+ /* Execute the pattern anchored at 'cindex', entering the bytecode at
3793
+ 'bytecode_offset' (which must point at the leading REOP_save_start 0 that
3794
+ follows the synthetic .*? scan prologue). Used by the quickjs fast paths
3795
+ after they have located a candidate start position by direct string
3796
+ search. Returns 0 when 'bytecode_offset' is not a valid save_start entry. */
3797
+ int lre_exec_at(uint8_t **capture,
3798
+ const uint8_t *bc_buf, uint32_t bytecode_offset,
3799
+ const uint8_t *cbuf, int cindex, int clen,
3800
+ int cbuf_type, void *opaque)
3801
+ {
3802
+ uint32_t bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN);
3803
+
3804
+ if (bytecode_len < 2 || bytecode_offset > bytecode_len - 2 ||
3805
+ bc_buf[RE_HEADER_LEN + bytecode_offset] != REOP_save_start ||
3806
+ bc_buf[RE_HEADER_LEN + bytecode_offset + 1] != 0)
3807
+ return 0;
3808
+ return lre_exec_internal(capture, bc_buf, bytecode_offset, cbuf, cindex,
3809
+ clen, cbuf_type, opaque);
3810
+ }
3811
+
3382
3812
  int lre_get_alloc_count(const uint8_t *bc_buf)
3383
3813
  {
3384
3814
  return bc_buf[RE_HEADER_CAPTURE_COUNT] * 2 +
@@ -3388,14 +3818,25 @@ int lre_get_alloc_count(const uint8_t *bc_buf)
3388
3818
  /* Structurally validate serialized regexp bytecode (e.g. from JS_ReadObject)
3389
3819
  before it is executed: a present header and a body length that fits within
3390
3820
  the buffer. Returns 0 on success, -1 if the bytecode is malformed. */
3391
- int lre_check_bytecode(const uint8_t *bc_buf, int bc_buf_len)
3821
+ int lre_check_bytecode(const uint8_t *bc_buf, int bc_buf_len, void *opaque)
3392
3822
  {
3393
3823
  uint32_t re_bytecode_len;
3824
+ LREMetadata meta;
3394
3825
  if (bc_buf_len < RE_HEADER_LEN)
3395
3826
  return -1;
3396
3827
  re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN);
3397
3828
  if (re_bytecode_len > (uint32_t)(bc_buf_len - RE_HEADER_LEN))
3398
3829
  return -1;
3830
+ if (!re_validate_code(bc_buf + RE_HEADER_LEN, re_bytecode_len,
3831
+ bc_buf[RE_HEADER_CAPTURE_COUNT],
3832
+ bc_buf[RE_HEADER_REGISTER_COUNT], opaque))
3833
+ return -1;
3834
+ if (lre_parse_metadata(bc_buf, bc_buf_len, &meta, NULL) < 0)
3835
+ return -1;
3836
+ if (meta.kind != LRE_META_NONE &&
3837
+ !re_validate_entry(bc_buf + RE_HEADER_LEN, re_bytecode_len,
3838
+ meta.entry_offset))
3839
+ return -1;
3399
3840
  return 0;
3400
3841
  }
3401
3842
 
@@ -3411,13 +3852,20 @@ int lre_get_flags(const uint8_t *bc_buf)
3411
3852
 
3412
3853
  /* Return NULL if no group names. Otherwise, return a pointer to
3413
3854
  'capture_count - 1' zero terminated UTF-8 strings. */
3414
- const char *lre_get_groupnames(const uint8_t *bc_buf)
3855
+ const char *lre_get_groupnames(const uint8_t *bc_buf, size_t bc_buf_len)
3415
3856
  {
3416
- uint32_t re_bytecode_len;
3857
+ const uint8_t *trailer;
3858
+
3859
+ if (!bc_buf || bc_buf_len < RE_HEADER_LEN)
3860
+ return NULL;
3417
3861
  if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0)
3418
3862
  return NULL;
3419
- re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN);
3420
- return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len);
3863
+ /* Group names follow the optional optimization metadata; parse it to
3864
+ find the trailer. */
3865
+ if (lre_parse_metadata(bc_buf, bc_buf_len, NULL, &trailer) < 0 ||
3866
+ trailer >= bc_buf + bc_buf_len)
3867
+ return NULL;
3868
+ return (const char *)trailer;
3421
3869
  }
3422
3870
 
3423
3871
  #ifdef TEST