re2js 2.8.5 → 2.8.7

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.
package/build/index.cjs CHANGED
@@ -2,10 +2,10 @@
2
2
  * re2js
3
3
  * RE2JS is the JavaScript port of RE2, a regular expression engine that provides linear time matching
4
4
  *
5
- * @version v2.8.5
5
+ * @version v2.8.7
6
6
  * @author Oleksii Vasyliev
7
7
  * @homepage https://github.com/le0pard/re2js#readme
8
- * @repository github:le0pard/re2js
8
+ * @repository [object Object]
9
9
  * @license MIT
10
10
  */
11
11
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
@@ -500,6 +500,10 @@ var UnicodeTables = class {
500
500
  */
501
501
  var Unicode = class Unicode {
502
502
  static MAX_RUNE = 1114111;
503
+ static RUNE_ERROR = 65533;
504
+ static isUnsearchable(r) {
505
+ return r === Unicode.RUNE_ERROR || r >= Unicode.MIN_HIGH_SURROGATE && r <= Unicode.MAX_LOW_SURROGATE;
506
+ }
503
507
  static MAX_ASCII = 127;
504
508
  static MAX_LATIN1 = 255;
505
509
  static MAX_BMP = 65535;
@@ -602,7 +606,7 @@ var Utils = class Utils {
602
606
  return [];
603
607
  }
604
608
  static isByteArray(input) {
605
- return Array.isArray(input) || input instanceof Uint8Array;
609
+ return Array.isArray(input) || Object.prototype.toString.call(input) === "[object Uint8Array]";
606
610
  }
607
611
  static isalnum(c) {
608
612
  return Codepoint.CODES.get("0") <= c && c <= Codepoint.CODES.get("9") || Codepoint.CODES.get("a") <= c && c <= Codepoint.CODES.get("z") || Codepoint.CODES.get("A") <= c && c <= Codepoint.CODES.get("Z");
@@ -661,7 +665,7 @@ var Utils = class Utils {
661
665
  runes.push(cp);
662
666
  i += cp > Unicode.MAX_BMP ? 2 : 1;
663
667
  }
664
- return runes;
668
+ return new Int32Array(runes);
665
669
  }
666
670
  static runeToString(r) {
667
671
  return String.fromCodePoint(r);
@@ -708,7 +712,7 @@ var Utils = class Utils {
708
712
  static stringToUtf8ByteArray(str) {
709
713
  if (globalThis.TextEncoder) {
710
714
  if (!cachedNativeEncoder) cachedNativeEncoder = new TextEncoder();
711
- return Utils.toArray(cachedNativeEncoder.encode(str));
715
+ return cachedNativeEncoder.encode(str);
712
716
  } else {
713
717
  let out = [], p = 0;
714
718
  for (let i = 0; i < str.length; i++) {
@@ -788,7 +792,7 @@ var MatcherInputBase = class MatcherInputBase {
788
792
  asCharSequence() {
789
793
  throw Error("not implemented");
790
794
  }
791
- /** @returns {Uint8Array|number[]} */
795
+ /** @returns {Uint8Array|number[]|null} */
792
796
  asBytes() {
793
797
  throw Error("not implemented");
794
798
  }
@@ -919,7 +923,8 @@ var MachineInputBase = class {
919
923
  return 0;
920
924
  }
921
925
  };
922
- var MachineUTF8Input = class extends MachineInputBase {
926
+ var MachineUTF8Input = class MachineUTF8Input extends MachineInputBase {
927
+ static RUNE_ERROR_STEP = Unicode.RUNE_ERROR << 3 | 1;
923
928
  constructor(bytes, start = 0, end = bytes.length) {
924
929
  super();
925
930
  this.bytes = bytes;
@@ -933,33 +938,46 @@ var MachineUTF8Input = class extends MachineInputBase {
933
938
  return idx !== -1 && idx <= this.end - target.length;
934
939
  }
935
940
  hasAnyString(prefilter, pos) {
936
- if (!prefilter.ac8) return false;
937
- return prefilter.ac8.searchUTF8(this.bytes, this.start + pos, this.end);
941
+ return prefilter.automaton8().searchUTF8(this.bytes, this.start + pos, this.end);
938
942
  }
939
943
  step(pos) {
940
944
  pos += this.start;
941
945
  if (pos >= this.end) return MachineInputBase.EOF();
942
946
  const c = this.bytes[pos] & 255;
943
947
  if (c < 128) return c << 3 | 1;
944
- else if (c >= 194 && c <= 223 && pos + 1 < this.end) {
945
- const c1 = this.bytes[pos + 1] & 255;
946
- if ((c1 & 192) !== 128) return c << 3 | 1;
947
- return ((c & 31) << 6 | c1 & 63) << 3 | 2;
948
- } else if (c >= 224 && c <= 239 && pos + 2 < this.end) {
949
- const c1 = this.bytes[pos + 1] & 255;
950
- if ((c1 & 192) !== 128) return c << 3 | 1;
951
- const c2 = this.bytes[pos + 2] & 255;
952
- if ((c2 & 192) !== 128) return c << 3 | 1;
953
- return ((c & 15) << 12 | (c1 & 63) << 6 | c2 & 63) << 3 | 3;
954
- } else if (c >= 240 && c <= 244 && pos + 3 < this.end) {
955
- const c1 = this.bytes[pos + 1] & 255;
956
- if ((c1 & 192) !== 128) return c << 3 | 1;
948
+ let size;
949
+ let rune;
950
+ let lo = 128;
951
+ let hi = 191;
952
+ if (c >= 194 && c <= 223) {
953
+ size = 2;
954
+ rune = c & 31;
955
+ } else if (c >= 224 && c <= 239) {
956
+ size = 3;
957
+ rune = c & 15;
958
+ if (c === 224) lo = 160;
959
+ else if (c === 237) hi = 159;
960
+ } else if (c >= 240 && c <= 244) {
961
+ size = 4;
962
+ rune = c & 7;
963
+ if (c === 240) lo = 144;
964
+ else if (c === 244) hi = 143;
965
+ } else return MachineUTF8Input.RUNE_ERROR_STEP;
966
+ if (pos + size > this.end) return MachineUTF8Input.RUNE_ERROR_STEP;
967
+ const c1 = this.bytes[pos + 1] & 255;
968
+ if (c1 < lo || c1 > hi) return MachineUTF8Input.RUNE_ERROR_STEP;
969
+ rune = rune << 6 | c1 & 63;
970
+ if (size > 2) {
957
971
  const c2 = this.bytes[pos + 2] & 255;
958
- if ((c2 & 192) !== 128) return c << 3 | 1;
959
- const c3 = this.bytes[pos + 3] & 255;
960
- if ((c3 & 192) !== 128) return c << 3 | 1;
961
- return ((c & 7) << 18 | (c1 & 63) << 12 | (c2 & 63) << 6 | c3 & 63) << 3 | 4;
962
- } else return c << 3 | 1;
972
+ if ((c2 & 192) !== 128) return MachineUTF8Input.RUNE_ERROR_STEP;
973
+ rune = rune << 6 | c2 & 63;
974
+ if (size > 3) {
975
+ const c3 = this.bytes[pos + 3] & 255;
976
+ if ((c3 & 192) !== 128) return MachineUTF8Input.RUNE_ERROR_STEP;
977
+ rune = rune << 6 | c3 & 63;
978
+ }
979
+ }
980
+ return rune << 3 | size;
963
981
  }
964
982
  index(re2, pos) {
965
983
  pos += this.start;
@@ -1024,8 +1042,7 @@ var MachineUTF16Input = class extends MachineInputBase {
1024
1042
  return idx !== -1 && idx <= this.end - prefilter.str.length;
1025
1043
  }
1026
1044
  hasAnyString(prefilter, pos) {
1027
- if (!prefilter.ac16) return false;
1028
- return prefilter.ac16.searchUTF16(this.charSequence, this.start + pos, this.end);
1045
+ return prefilter.automaton16().searchUTF16(this.charSequence, this.start + pos, this.end);
1029
1046
  }
1030
1047
  step(pos) {
1031
1048
  pos += this.start;
@@ -1218,6 +1235,8 @@ var Matcher = class Matcher {
1218
1235
  this.namedGroups = re2.namedGroups;
1219
1236
  /** @type {number} */
1220
1237
  this.numberOfInstructions = re2.numberOfInstructions();
1238
+ /** @type {import('./Machine.js').LookbehindState|null} */
1239
+ this.lookbehindState = null;
1221
1240
  if (input instanceof MatcherInputBase) this.resetMatcherInput(input);
1222
1241
  else if (Utils.isByteArray(input)) this.resetMatcherInput(MatcherInput.utf8(input));
1223
1242
  else this.resetMatcherInput(MatcherInput.utf16(input));
@@ -1251,9 +1270,13 @@ var Matcher = class Matcher {
1251
1270
  */
1252
1271
  resetMatcherInput(input) {
1253
1272
  if (input === null) throw new Error("input is null");
1254
- if (!(input instanceof MatcherInputBase)) if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
1255
- else input = MatcherInput.utf16(input);
1273
+ if (!(input instanceof MatcherInputBase)) {
1274
+ if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
1275
+ else input = MatcherInput.utf16(input);
1276
+ }
1256
1277
  this.matcherInput = input;
1278
+ if (this.lookbehindState !== null) this.lookbehindState.reset();
1279
+ else this.lookbehindState = this.patternInput.re2().createLookbehindState();
1257
1280
  this.reset();
1258
1281
  return this;
1259
1282
  }
@@ -1341,11 +1364,11 @@ var Matcher = class Matcher {
1341
1364
  * @private
1342
1365
  */
1343
1366
  loadGroup(group) {
1344
- if (group < 0 || group > this.patternGroupCount) throw new RE2JSGroupException(`Group index out of bounds: ${group}`);
1367
+ if (!Number.isInteger(group) || group < 0 || group > this.patternGroupCount) throw new RE2JSGroupException(`Group index out of bounds: ${group}`);
1345
1368
  if (!this.hasMatch) throw new RE2JSGroupException("perhaps no match attempted");
1346
1369
  if (group === 0 || this.hasGroups) return;
1347
1370
  const end = this.matcherInputLength;
1348
- const res = this.patternInput.re2().matchMachineInput(this.matcherInput, this.groups[0], end, this.anchorFlag, 1 + this.patternGroupCount);
1371
+ const res = this.patternInput.re2().matchMachineInput(this.matcherInput, this.groups[0], end, this.anchorFlag, 1 + this.patternGroupCount, this.lookbehindState);
1349
1372
  if (!res[0]) throw new RE2JSGroupException("inconsistency in matching group data");
1350
1373
  this.groups = res[1];
1351
1374
  this.hasGroups = true;
@@ -1378,7 +1401,7 @@ var Matcher = class Matcher {
1378
1401
  */
1379
1402
  find(start = null) {
1380
1403
  if (start !== null) {
1381
- if (start < 0 || start > this.matcherInputLength) throw new RE2JSGroupException(`start index out of bounds: ${start}`);
1404
+ if (!Number.isInteger(start) || start < 0 || start > this.matcherInputLength) throw new RE2JSGroupException(`start index out of bounds: ${start}`);
1382
1405
  this.reset();
1383
1406
  return this.genMatch(start, 0);
1384
1407
  }
@@ -1401,8 +1424,11 @@ var Matcher = class Matcher {
1401
1424
  * @private
1402
1425
  */
1403
1426
  genMatch(startByte, anchor) {
1404
- const res = this.patternInput.re2().matchMachineInput(this.matcherInput, startByte, this.matcherInputLength, anchor, 1);
1405
- if (!res[0]) return false;
1427
+ const res = this.patternInput.re2().matchMachineInput(this.matcherInput, startByte, this.matcherInputLength, anchor, 1, this.lookbehindState);
1428
+ if (!res[0]) {
1429
+ this.hasMatch = false;
1430
+ return false;
1431
+ }
1406
1432
  this.groups = res[1];
1407
1433
  this.hasMatch = true;
1408
1434
  this.hasGroups = this.patternGroupCount === 0;
@@ -1465,43 +1491,47 @@ var Matcher = class Matcher {
1465
1491
  let res = "";
1466
1492
  let last = 0;
1467
1493
  const m = replacement.length;
1468
- for (let i = 0; i < m - 1; i++) {
1469
- if (replacement.codePointAt(i) === Codepoint.CODES.get("\\")) {
1494
+ let i = 0;
1495
+ while (i < m) {
1496
+ const cCode = replacement.codePointAt(i);
1497
+ if (cCode === Codepoint.CODES.get("\\")) {
1470
1498
  if (last < i) res += replacement.substring(last, i);
1471
1499
  i++;
1500
+ if (i >= m) throw new RE2JSGroupException("character to be escaped is missing");
1472
1501
  last = i;
1502
+ i++;
1473
1503
  continue;
1474
1504
  }
1475
- if (replacement.codePointAt(i) === Codepoint.CODES.get("$")) {
1476
- let c = replacement.codePointAt(i + 1);
1477
- if (Codepoint.CODES.get("0") <= c && c <= Codepoint.CODES.get("9")) {
1478
- let n = c - Codepoint.CODES.get("0");
1479
- if (last < i) res += replacement.substring(last, i);
1480
- for (i += 2; i < m; i++) {
1481
- c = replacement.codePointAt(i);
1482
- if (c < Codepoint.CODES.get("0") || c > Codepoint.CODES.get("9") || n * 10 + c - Codepoint.CODES.get("0") > this.patternGroupCount) break;
1483
- n = n * 10 + c - Codepoint.CODES.get("0");
1505
+ if (cCode === Codepoint.CODES.get("$")) {
1506
+ if (last < i) res += replacement.substring(last, i);
1507
+ if (i + 1 >= m) throw new RE2JSGroupException("Illegal group reference: group index is missing");
1508
+ const nextCode = replacement.codePointAt(i + 1);
1509
+ if (Codepoint.CODES.get("0") <= nextCode && nextCode <= Codepoint.CODES.get("9")) {
1510
+ let n = nextCode - Codepoint.CODES.get("0");
1511
+ let j = i + 2;
1512
+ for (; j < m; j++) {
1513
+ const digit = replacement.codePointAt(j);
1514
+ if (digit < Codepoint.CODES.get("0") || digit > Codepoint.CODES.get("9") || n * 10 + digit - Codepoint.CODES.get("0") > this.patternGroupCount) break;
1515
+ n = n * 10 + digit - Codepoint.CODES.get("0");
1484
1516
  }
1485
1517
  if (n > this.patternGroupCount) throw new RE2JSGroupException(`n > number of groups: ${n}`);
1486
1518
  const group = this.group(n);
1487
1519
  if (group !== null) res += group;
1520
+ i = j;
1488
1521
  last = i;
1489
- i--;
1490
- continue;
1491
- } else if (c === Codepoint.CODES.get("{")) {
1492
- if (last < i) res += replacement.substring(last, i);
1493
- i++;
1494
- let j = i + 1;
1495
- while (j < replacement.length && replacement.codePointAt(j) !== Codepoint.CODES.get("}") && replacement.codePointAt(j) !== Codepoint.CODES.get(" ")) j++;
1496
- if (j === replacement.length || replacement.codePointAt(j) !== Codepoint.CODES.get("}")) throw new RE2JSGroupException("named capture group is missing trailing '}'");
1497
- const groupName = replacement.substring(i + 1, j);
1522
+ } else if (nextCode === Codepoint.CODES.get("{")) {
1523
+ let j = i + 2;
1524
+ while (j < m && replacement.codePointAt(j) !== Codepoint.CODES.get("}")) j++;
1525
+ if (j >= m) throw new RE2JSGroupException("named capture group is missing trailing '}'");
1526
+ const groupName = replacement.substring(i + 2, j);
1498
1527
  const groupVal = this.group(groupName);
1499
1528
  if (groupVal !== null) res += groupVal;
1500
- last = j + 1;
1501
- i = j;
1502
- continue;
1503
- }
1529
+ i = j + 1;
1530
+ last = i;
1531
+ } else throw new RE2JSGroupException("Illegal group reference");
1532
+ continue;
1504
1533
  }
1534
+ i++;
1505
1535
  }
1506
1536
  if (last < m) res += replacement.substring(last, m);
1507
1537
  return res;
@@ -1723,7 +1753,8 @@ var Inst = class Inst {
1723
1753
  this.op = op;
1724
1754
  this.out = 0;
1725
1755
  this.arg = 0;
1726
- this.runes = [];
1756
+ /** @type {Int32Array} */
1757
+ this.runes = /* @__PURE__ */ new Int32Array(0);
1727
1758
  /** @type {Uint32Array | number[] | null} */
1728
1759
  this.next = null;
1729
1760
  }
@@ -1812,18 +1843,26 @@ var Inst = class Inst {
1812
1843
  };
1813
1844
  //#endregion
1814
1845
  //#region src/Machine.js
1815
- var Thread = class {
1816
- constructor() {
1817
- this.inst = null;
1818
- this.cap = null;
1819
- }
1820
- };
1821
- var Queue = class {
1846
+ var Queue = class Queue {
1822
1847
  constructor(numInst) {
1823
1848
  this.sparse = new Int32Array(numInst);
1824
1849
  this.densePcs = new Int32Array(numInst);
1825
- this.denseThreads = new Array(numInst);
1850
+ this.denseCaps = null;
1826
1851
  this.size = 0;
1852
+ this.ncap = 0;
1853
+ this.mainThreads = 0;
1854
+ }
1855
+ static INITIAL_THREADS = 32;
1856
+ init(ncap) {
1857
+ this.ncap = ncap;
1858
+ const needed = Math.min(this.densePcs.length, Queue.INITIAL_THREADS) * ncap;
1859
+ if (!this.denseCaps || this.denseCaps.length < needed) this.denseCaps = new Int32Array(needed);
1860
+ }
1861
+ growCaps(needed) {
1862
+ const limit = this.densePcs.length * this.ncap;
1863
+ const grown = new Int32Array(Math.min(limit, Math.max(needed, 2 * this.denseCaps.length)));
1864
+ grown.set(this.denseCaps);
1865
+ this.denseCaps = grown;
1827
1866
  }
1828
1867
  contains(pc) {
1829
1868
  const j = this.sparse[pc];
@@ -1835,13 +1874,12 @@ var Queue = class {
1835
1874
  add(pc) {
1836
1875
  const j = this.size++;
1837
1876
  this.sparse[pc] = j;
1838
- this.denseThreads[j] = null;
1839
1877
  this.densePcs[j] = pc;
1840
1878
  return j;
1841
1879
  }
1842
1880
  clear() {
1843
- for (let i = 0; i < this.size; i++) this.denseThreads[i] = null;
1844
1881
  this.size = 0;
1882
+ this.mainThreads = 0;
1845
1883
  }
1846
1884
  toString() {
1847
1885
  let out = "{";
@@ -1853,31 +1891,37 @@ var Queue = class {
1853
1891
  return out;
1854
1892
  }
1855
1893
  };
1894
+ var LookbehindState = class {
1895
+ constructor(prog) {
1896
+ this.pos = -1;
1897
+ this.pcs = new Int32Array(prog.numInst());
1898
+ this.size = 0;
1899
+ this.lbTable = new Int32Array(prog.numLb + 1);
1900
+ }
1901
+ reset() {
1902
+ this.pos = -1;
1903
+ this.size = 0;
1904
+ }
1905
+ };
1856
1906
  var Machine = class Machine {
1857
- static THREADS_CHUNK_SIZE = 128;
1858
1907
  static fromRE2(re2) {
1859
1908
  const m = new Machine();
1860
1909
  m.prog = re2.prog;
1861
1910
  m.re2 = re2;
1862
1911
  m.q0 = new Queue(m.prog.numInst());
1863
1912
  m.q1 = new Queue(m.prog.numInst());
1864
- m.pool = [];
1865
- m.poolSize = 0;
1913
+ m.addStack = new Int32Array(2 * (m.prog.numInst() + 1));
1866
1914
  m.matched = false;
1867
1915
  m.matchcap = new Int32Array(m.prog.numCap < 2 ? 2 : m.prog.numCap);
1868
1916
  m.ncap = 0;
1869
1917
  return m;
1870
1918
  }
1871
- static fromMachine(machine) {
1872
- return Machine.fromRE2(machine.re2);
1873
- }
1874
1919
  constructor() {
1875
1920
  this.prog = null;
1876
1921
  this.re2 = null;
1877
1922
  this.q0 = null;
1878
1923
  this.q1 = null;
1879
- this.pool = [];
1880
- this.poolSize = 0;
1924
+ this.addStack = null;
1881
1925
  this.matched = false;
1882
1926
  this.matchcap = null;
1883
1927
  this.ncap = 0;
@@ -1885,59 +1929,36 @@ var Machine = class Machine {
1885
1929
  }
1886
1930
  init(ncap) {
1887
1931
  this.ncap = ncap;
1888
- if (ncap > this.matchcap.length) this.initNewCap(ncap);
1889
- else this.resetCap();
1932
+ if (ncap > this.matchcap.length) this.matchcap = new Int32Array(ncap).fill(-1);
1933
+ else this.matchcap.fill(-1);
1934
+ this.q0.init(ncap);
1935
+ this.q1.init(ncap);
1890
1936
  if (this.prog.numLb > 0) {
1891
1937
  if (!this.lbTable || this.lbTable.length < this.prog.numLb + 1) this.lbTable = new Int32Array(this.prog.numLb + 1);
1892
1938
  this.lbTable.fill(-1);
1893
1939
  }
1894
1940
  }
1895
- resetCap() {
1896
- for (let i = 0; i < this.poolSize; i++) this.pool[i].cap.fill(-1);
1897
- }
1898
- initNewCap(ncap) {
1899
- for (let i = 0; i < this.poolSize; i++) {
1900
- const t = this.pool[i];
1901
- t.cap = new Int32Array(ncap).fill(-1);
1902
- }
1903
- this.matchcap = new Int32Array(ncap).fill(-1);
1904
- }
1905
1941
  submatches() {
1906
1942
  if (this.ncap === 0) return Utils.emptyInts();
1907
1943
  return Utils.toArray(this.matchcap.subarray(0, this.ncap));
1908
1944
  }
1909
- alloc(inst) {
1910
- if (this.poolSize === 0) {
1911
- const capLen = this.matchcap.length;
1912
- for (let i = 0; i < Machine.THREADS_CHUNK_SIZE; i++) {
1913
- const t = new Thread();
1914
- t.cap = new Int32Array(capLen);
1915
- this.pool[this.poolSize++] = t;
1916
- }
1917
- }
1918
- this.poolSize--;
1919
- const t = this.pool[this.poolSize];
1920
- t.inst = inst;
1921
- return t;
1922
- }
1923
- freeQueue(queue, from = 0) {
1924
- for (let i = from; i < queue.size; i++) {
1925
- const t = queue.denseThreads[i];
1926
- if (t !== null) this.pool[this.poolSize++] = t;
1927
- }
1928
- queue.clear();
1929
- }
1930
- freeThread(t) {
1931
- this.pool[this.poolSize++] = t;
1932
- }
1933
- match(input, pos, anchor) {
1945
+ match(input, pos, anchor, lbState = null) {
1934
1946
  const startCond = this.re2.cond;
1935
1947
  if (startCond === Utils.EMPTY_ALL) return false;
1936
1948
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return false;
1937
1949
  this.matched = false;
1938
1950
  this.matchcap.fill(-1);
1939
- let currentPos = this.prog.numLb > 0 ? 0 : pos;
1940
- let matchStartPos = pos;
1951
+ const hasLookbehinds = this.prog.numLb > 0;
1952
+ const matchStartPos = pos;
1953
+ let currentPos = pos;
1954
+ let resumeLookbehinds = false;
1955
+ if (hasLookbehinds) {
1956
+ if (lbState !== null && lbState.pos >= 0 && lbState.pos <= pos) {
1957
+ currentPos = lbState.pos;
1958
+ resumeLookbehinds = true;
1959
+ } else currentPos = 0;
1960
+ }
1961
+ let saveLookbehinds = hasLookbehinds && lbState !== null;
1941
1962
  let runq = this.q0;
1942
1963
  let nextq = this.q1;
1943
1964
  let r = input.step(currentPos);
@@ -1953,12 +1974,16 @@ var Machine = class Machine {
1953
1974
  let flag;
1954
1975
  if (currentPos === 0) flag = Utils.emptyOpContext(-1, rune);
1955
1976
  else flag = input.context(currentPos);
1977
+ if (hasLookbehinds) {
1978
+ if (resumeLookbehinds) this.restoreLookbehinds(lbState, runq, flag);
1979
+ else this.spawnLookbehinds(runq, currentPos, flag);
1980
+ }
1956
1981
  while (true) {
1957
- if (runq.isEmpty()) {
1982
+ if (runq.mainThreads === 0) {
1958
1983
  if ((startCond & Utils.EMPTY_BEGIN_TEXT) !== 0 && currentPos !== 0) break;
1959
1984
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && currentPos !== 0) break;
1960
1985
  if (this.matched) break;
1961
- if (this.prog.numLb === 0 && !(this.re2.prefix.length === 0) && rune1 !== this.re2.prefixRune && input.canCheckPrefix()) {
1986
+ if (this.prog.numLb === 0 && runq.isEmpty() && !(this.re2.prefix.length === 0) && rune1 !== this.re2.prefixRune && input.canCheckPrefix()) {
1962
1987
  const advance = input.index(this.re2, currentPos);
1963
1988
  if (advance < 0) break;
1964
1989
  currentPos += advance;
@@ -1968,13 +1993,17 @@ var Machine = class Machine {
1968
1993
  r = input.step(currentPos + width);
1969
1994
  rune1 = r >> 3;
1970
1995
  width1 = r & 7;
1996
+ flag = input.context(currentPos);
1971
1997
  }
1972
1998
  }
1973
- if (currentPos === 0 && this.prog.numLb > 0) for (let i = 0; i < this.prog.lbStarts.length; i++) this.add(runq, this.prog.lbStarts[i], currentPos, this.matchcap, flag, null);
1974
- if (!this.matched && (currentPos === 0 || anchor === RE2Flags.UNANCHORED)) {
1975
- if (currentPos >= matchStartPos) {
1999
+ if (currentPos >= matchStartPos) {
2000
+ if (saveLookbehinds) {
2001
+ this.saveLookbehinds(lbState, runq, currentPos);
2002
+ saveLookbehinds = false;
2003
+ }
2004
+ if (!this.matched && (currentPos === 0 || anchor === RE2Flags.UNANCHORED)) {
1976
2005
  if (this.ncap > 0) this.matchcap[0] = currentPos;
1977
- this.add(runq, this.prog.start, currentPos, this.matchcap, flag, null);
2006
+ this.add(runq, this.prog.start, currentPos, this.matchcap, 0, flag);
1978
2007
  }
1979
2008
  }
1980
2009
  const nextPos = currentPos + width;
@@ -1994,7 +2023,8 @@ var Machine = class Machine {
1994
2023
  runq = nextq;
1995
2024
  nextq = tmpq;
1996
2025
  }
1997
- this.freeQueue(nextq);
2026
+ runq.clear();
2027
+ nextq.clear();
1998
2028
  return this.matched;
1999
2029
  }
2000
2030
  matchSet(input, pos, anchor) {
@@ -2017,21 +2047,21 @@ var Machine = class Machine {
2017
2047
  }
2018
2048
  let flag = currentPos === 0 ? Utils.emptyOpContext(-1, rune) : input.context(currentPos);
2019
2049
  const matches = /* @__PURE__ */ new Set();
2050
+ if (this.prog.numLb > 0) this.spawnLookbehinds(runq, currentPos, flag);
2020
2051
  while (true) {
2021
- if (runq.isEmpty()) {
2052
+ if (runq.mainThreads === 0) {
2022
2053
  if ((startCond & Utils.EMPTY_BEGIN_TEXT) !== 0 && currentPos !== 0) break;
2023
2054
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && currentPos !== 0) break;
2024
2055
  }
2025
- if (currentPos === 0 && this.prog.numLb > 0) for (let i = 0; i < this.prog.lbStarts.length; i++) this.add(runq, this.prog.lbStarts[i], currentPos, this.matchcap, flag, null);
2026
2056
  if (currentPos === 0 || anchor === RE2Flags.UNANCHORED) {
2027
- if (currentPos >= matchStartPos) this.add(runq, this.prog.start, currentPos, this.matchcap, flag, null);
2057
+ if (currentPos >= matchStartPos) this.add(runq, this.prog.start, currentPos, this.matchcap, 0, flag);
2028
2058
  }
2029
2059
  const nextPos = currentPos + width;
2030
2060
  flag = input.context(nextPos);
2031
2061
  for (let j = 0; j < runq.size; j++) {
2032
- let t = runq.denseThreads[j];
2033
- if (t === null) continue;
2034
- const i = t.inst;
2062
+ const pc = runq.densePcs[j];
2063
+ const i = this.prog.inst[pc];
2064
+ const capOffset = j * this.ncap;
2035
2065
  let add = false;
2036
2066
  switch (i.op) {
2037
2067
  case Inst.MATCH:
@@ -2048,15 +2078,11 @@ var Machine = class Machine {
2048
2078
  add = true;
2049
2079
  break;
2050
2080
  case Inst.RUNE_ANY_NOT_NL:
2051
- add = rune !== Codepoint.CODES.get("\n");
2081
+ add = rune !== 10;
2052
2082
  break;
2053
- default: throw new RE2JSInternalException("bad inst");
2054
- }
2055
- if (add) t = this.add(nextq, i.out, nextPos, t.cap, flag, t);
2056
- if (t !== null) {
2057
- this.freeThread(t);
2058
- runq.denseThreads[j] = null;
2083
+ default: continue;
2059
2084
  }
2085
+ if (add) this.add(nextq, i.out, nextPos, runq.denseCaps, capOffset, flag);
2060
2086
  }
2061
2087
  runq.clear();
2062
2088
  if (width === 0) break;
@@ -2072,28 +2098,25 @@ var Machine = class Machine {
2072
2098
  runq = nextq;
2073
2099
  nextq = tmpq;
2074
2100
  }
2075
- this.freeQueue(nextq);
2101
+ nextq.clear();
2076
2102
  return Array.from(matches).sort((a, b) => a - b);
2077
2103
  }
2078
2104
  step(runq, nextq, pos, nextPos, c, nextCond, anchor, atEnd) {
2079
2105
  const longest = this.re2.longest;
2080
2106
  for (let j = 0; j < runq.size; j++) {
2081
- let t = runq.denseThreads[j];
2082
- if (t === null) continue;
2083
- if (longest && this.matched && this.ncap > 0 && this.matchcap[0] < t.cap[0]) {
2084
- this.freeThread(t);
2085
- continue;
2086
- }
2087
- const i = t.inst;
2107
+ const pc = runq.densePcs[j];
2108
+ const capOffset = j * this.ncap;
2109
+ if (longest && this.matched && this.ncap > 0 && this.matchcap[0] < runq.denseCaps[capOffset]) continue;
2110
+ const i = this.prog.inst[pc];
2088
2111
  let add = false;
2089
2112
  switch (i.op) {
2090
2113
  case Inst.MATCH:
2091
2114
  if (anchor === RE2Flags.ANCHOR_BOTH && !atEnd) break;
2092
2115
  if (this.ncap > 0 && (!longest || !this.matched || this.matchcap[1] < pos)) {
2093
- t.cap[1] = pos;
2094
- this.matchcap.set(t.cap.subarray(0, this.ncap));
2116
+ runq.denseCaps[capOffset + 1] = pos;
2117
+ for (let k = 0; k < this.ncap; k++) this.matchcap[k] = runq.denseCaps[capOffset + k];
2095
2118
  }
2096
- if (!longest) this.freeQueue(runq, j + 1);
2119
+ if (!longest) runq.size = 0;
2097
2120
  this.matched = true;
2098
2121
  break;
2099
2122
  case Inst.RUNE:
@@ -2106,78 +2129,93 @@ var Machine = class Machine {
2106
2129
  add = true;
2107
2130
  break;
2108
2131
  case Inst.RUNE_ANY_NOT_NL:
2109
- add = c !== Codepoint.CODES.get("\n");
2132
+ add = c !== 10;
2110
2133
  break;
2111
- default: throw new RE2JSInternalException("bad inst");
2112
- }
2113
- if (add) t = this.add(nextq, i.out, nextPos, t.cap, nextCond, t);
2114
- if (t !== null) {
2115
- this.freeThread(t);
2116
- runq.denseThreads[j] = null;
2134
+ default: continue;
2117
2135
  }
2136
+ if (add) this.add(nextq, i.out, nextPos, runq.denseCaps, capOffset, nextCond);
2118
2137
  }
2119
2138
  runq.clear();
2120
2139
  }
2121
- add(q, pc, pos, cap, cond, t) {
2122
- while (true) {
2123
- if (pc === 0) return t;
2124
- if (q.contains(pc)) return t;
2125
- const d = q.add(pc);
2126
- const inst = this.prog.inst[pc];
2127
- switch (inst.op) {
2128
- case Inst.FAIL: return t;
2129
- case Inst.ALT:
2130
- case Inst.ALT_MATCH:
2131
- t = this.add(q, inst.out, pos, cap, cond, t);
2132
- pc = inst.arg;
2133
- continue;
2134
- case Inst.EMPTY_WIDTH:
2135
- if ((inst.arg & ~cond) === 0) {
2140
+ spawnLookbehinds(q, pos, flag) {
2141
+ for (let i = 0; i < this.prog.lbStarts.length; i++) this.add(q, this.prog.lbStarts[i], pos, this.matchcap, 0, flag);
2142
+ }
2143
+ restoreLookbehinds(lbState, q, flag) {
2144
+ this.lbTable.set(lbState.lbTable);
2145
+ for (let j = 0; j < lbState.size; j++) this.add(q, lbState.pcs[j], lbState.pos, this.matchcap, 0, flag);
2146
+ }
2147
+ saveLookbehinds(lbState, q, pos) {
2148
+ let size = 0;
2149
+ for (let j = 0; j < q.size; j++) {
2150
+ const pc = q.densePcs[j];
2151
+ if (this.prog.lbInst[pc] === 1 && Inst.isRuneOp(this.prog.inst[pc].op)) lbState.pcs[size++] = pc;
2152
+ }
2153
+ lbState.size = size;
2154
+ lbState.pos = pos;
2155
+ lbState.lbTable.set(this.lbTable);
2156
+ }
2157
+ add(q, pc, pos, capArray, capOffset, cond) {
2158
+ const stk = this.addStack;
2159
+ let nstk = 0;
2160
+ for (;;) {
2161
+ while (pc !== 0 && !q.contains(pc)) {
2162
+ const d = q.add(pc);
2163
+ const inst = this.prog.inst[pc];
2164
+ switch (inst.op) {
2165
+ case Inst.FAIL:
2166
+ pc = 0;
2167
+ break;
2168
+ case Inst.ALT:
2169
+ case Inst.ALT_MATCH:
2170
+ stk[nstk++] = inst.arg;
2171
+ stk[nstk++] = 0;
2136
2172
  pc = inst.out;
2137
- continue;
2138
- }
2139
- return t;
2140
- case Inst.NOP:
2141
- pc = inst.out;
2142
- continue;
2143
- case Inst.CAPTURE: if (inst.arg < this.ncap) {
2144
- const opos = cap[inst.arg];
2145
- cap[inst.arg] = pos;
2146
- this.add(q, inst.out, pos, cap, cond, null);
2147
- cap[inst.arg] = opos;
2148
- return t;
2149
- } else {
2150
- pc = inst.out;
2151
- continue;
2152
- }
2153
- case Inst.LB_WRITE:
2154
- this.lbTable[Math.abs(inst.arg)] = pos;
2155
- pc = inst.out;
2156
- continue;
2157
- case Inst.LB_CHECK:
2158
- if (inst.arg > 0) {
2159
- if (this.lbTable[inst.arg] === pos) {
2160
- pc = inst.out;
2161
- continue;
2173
+ break;
2174
+ case Inst.EMPTY_WIDTH:
2175
+ pc = (inst.arg & ~cond) === 0 ? inst.out : 0;
2176
+ break;
2177
+ case Inst.NOP:
2178
+ pc = inst.out;
2179
+ break;
2180
+ case Inst.CAPTURE:
2181
+ if (inst.arg < this.ncap) {
2182
+ stk[nstk++] = -inst.arg - 1;
2183
+ stk[nstk++] = capArray[capOffset + inst.arg];
2184
+ capArray[capOffset + inst.arg] = pos;
2162
2185
  }
2163
- } else if (this.lbTable[-inst.arg] !== pos) {
2164
2186
  pc = inst.out;
2165
- continue;
2166
- }
2167
- return t;
2168
- case Inst.MATCH:
2169
- case Inst.RUNE:
2170
- case Inst.RUNE1:
2171
- case Inst.RUNE_ANY:
2172
- case Inst.RUNE_ANY_NOT_NL:
2173
- if (t === null) t = this.alloc(inst);
2174
- else t.inst = inst;
2175
- if (this.ncap > 0 && t.cap !== cap) for (let c = 0; c < this.ncap; c++) t.cap[c] = cap[c];
2176
- q.denseThreads[d] = t;
2177
- t = null;
2178
- return t;
2179
- default: throw new Error("unhandled");
2187
+ break;
2188
+ case Inst.LB_WRITE:
2189
+ this.lbTable[Math.abs(inst.arg)] = pos;
2190
+ pc = inst.out;
2191
+ break;
2192
+ case Inst.LB_CHECK:
2193
+ if (inst.arg > 0) pc = this.lbTable[inst.arg] === pos ? inst.out : 0;
2194
+ else pc = this.lbTable[-inst.arg] !== pos ? inst.out : 0;
2195
+ break;
2196
+ case Inst.MATCH:
2197
+ case Inst.RUNE:
2198
+ case Inst.RUNE1:
2199
+ case Inst.RUNE_ANY:
2200
+ case Inst.RUNE_ANY_NOT_NL:
2201
+ if (this.prog.lbInst[pc] === 0) q.mainThreads++;
2202
+ if (this.ncap > 0) {
2203
+ const destOffset = d * this.ncap;
2204
+ if (destOffset + this.ncap > q.denseCaps.length) q.growCaps(destOffset + this.ncap);
2205
+ for (let c = 0; c < this.ncap; c++) q.denseCaps[destOffset + c] = capArray[capOffset + c];
2206
+ }
2207
+ pc = 0;
2208
+ break;
2209
+ default: throw new RE2JSInternalException("unhandled");
2210
+ }
2180
2211
  }
2212
+ if (nstk === 0) return;
2213
+ nstk -= 2;
2214
+ const entry = stk[nstk];
2215
+ if (entry < 0) {
2216
+ capArray[capOffset - entry - 1] = stk[nstk + 1];
2217
+ pc = 0;
2218
+ } else pc = entry;
2181
2219
  }
2182
2220
  }
2183
2221
  };
@@ -2197,35 +2235,85 @@ const arraysEqual = (a, b) => {
2197
2235
  return true;
2198
2236
  };
2199
2237
  var DFAState = class {
2200
- constructor(nfaStates, isMatch, matchIDs = []) {
2238
+ constructor(nfaStates, isMatch, matchIDs) {
2201
2239
  this.nfaStates = nfaStates;
2202
2240
  this.isMatch = isMatch;
2203
2241
  this.matchIDs = matchIDs;
2204
- this.nextLatin1 = new Array(Unicode.MAX_LATIN1 + 1).fill(null);
2205
- this.nextLatin1Anchored = new Array(Unicode.MAX_LATIN1 + 1).fill(null);
2206
- this.transKeys = [];
2207
- this.transVals = [];
2208
- this.lastSeen = 0;
2242
+ this.next = null;
2243
+ this.nextAnchored = null;
2209
2244
  }
2210
2245
  };
2211
2246
  var DFA = class DFA {
2212
- static MAX_CACHE_CLEARS = 5;
2213
- static STATE_MEMORY_ESTIMATE = 838;
2247
+ static STATE_OVERHEAD = 160;
2248
+ static TABLE_OVERHEAD = 16;
2249
+ static PROGRESS_PER_STATE = 10;
2214
2250
  constructor(prog, maxMem = 8388608) {
2215
2251
  this.prog = prog;
2252
+ this.maxMem = maxMem;
2216
2253
  this.stateCache = /* @__PURE__ */ new Map();
2217
2254
  this.stateCount = 0;
2218
2255
  this.startState = null;
2219
- this.stateLimit = Math.max(1, Math.floor(maxMem / DFA.STATE_MEMORY_ESTIMATE));
2220
- this.cacheClears = 0;
2221
2256
  this.failed = false;
2222
- this.clock = 0;
2257
+ for (let pc = 0; pc < prog.numInst(); pc++) {
2258
+ const op = prog.getInst(pc).op;
2259
+ if (op === Inst.EMPTY_WIDTH || op === Inst.LB_WRITE || op === Inst.LB_CHECK) {
2260
+ this.failed = true;
2261
+ break;
2262
+ }
2263
+ }
2264
+ this.computeRuneClasses();
2265
+ this.stateBudget = maxMem;
2266
+ this.memBudget = maxMem;
2267
+ }
2268
+ computeRuneClasses() {
2269
+ const bounds = /* @__PURE__ */ new Set();
2270
+ const addRune = (r) => {
2271
+ bounds.add(r);
2272
+ bounds.add(r + 1);
2273
+ };
2274
+ for (let pc = 0; pc < this.prog.numInst(); pc++) {
2275
+ const inst = this.prog.getInst(pc);
2276
+ switch (inst.op) {
2277
+ case Inst.RUNE:
2278
+ case Inst.RUNE1:
2279
+ if (inst.runes.length === 1) {
2280
+ addRune(inst.runes[0]);
2281
+ if ((inst.arg & RE2Flags.FOLD_CASE) !== 0) for (let f = Unicode.simpleFold(inst.runes[0]); f !== inst.runes[0]; f = Unicode.simpleFold(f)) addRune(f);
2282
+ } else for (let j = 0; j < inst.runes.length; j += 2) {
2283
+ bounds.add(inst.runes[j]);
2284
+ bounds.add(inst.runes[j + 1] + 1);
2285
+ }
2286
+ break;
2287
+ case Inst.RUNE_ANY_NOT_NL: addRune(10);
2288
+ }
2289
+ }
2290
+ bounds.delete(0);
2291
+ this.classBounds = Int32Array.from(bounds).sort();
2292
+ this.numClasses = this.classBounds.length + 1;
2293
+ const latin1Class = new Int32Array(Unicode.MAX_LATIN1 + 1);
2294
+ for (let r = 0; r <= Unicode.MAX_LATIN1; r++) latin1Class[r] = this.searchClass(r);
2295
+ this.latin1Class = latin1Class;
2296
+ }
2297
+ runeClass(r) {
2298
+ if (r <= Unicode.MAX_LATIN1) return this.latin1Class[r];
2299
+ return this.searchClass(r);
2300
+ }
2301
+ searchClass(r) {
2302
+ const bounds = this.classBounds;
2303
+ let lo = 0;
2304
+ let hi = bounds.length;
2305
+ while (lo < hi) {
2306
+ const mid = lo + hi >> 1;
2307
+ if (bounds[mid] <= r) lo = mid + 1;
2308
+ else hi = mid;
2309
+ }
2310
+ return lo;
2223
2311
  }
2224
2312
  computeClosure(pcs) {
2225
2313
  const closure = /* @__PURE__ */ new Set();
2226
2314
  const stack = [...pcs];
2227
2315
  let isMatch = false;
2228
- const matchIDs = [];
2316
+ const matchIDs = /* @__PURE__ */ new Set();
2229
2317
  while (stack.length > 0) {
2230
2318
  const pc = stack.pop();
2231
2319
  if (closure.has(pc)) continue;
@@ -2234,7 +2322,7 @@ var DFA = class DFA {
2234
2322
  switch (inst.op) {
2235
2323
  case Inst.MATCH:
2236
2324
  isMatch = true;
2237
- if (!matchIDs.includes(inst.arg)) matchIDs.push(inst.arg);
2325
+ matchIDs.add(inst.arg);
2238
2326
  break;
2239
2327
  case Inst.ALT:
2240
2328
  case Inst.ALT_MATCH:
@@ -2251,11 +2339,11 @@ var DFA = class DFA {
2251
2339
  }
2252
2340
  }
2253
2341
  const sortedPCs = Int32Array.from(closure).sort();
2254
- matchIDs.sort((a, b) => a - b);
2342
+ const ids = Array.from(matchIDs).sort((a, b) => a - b);
2255
2343
  return {
2256
2344
  pcs: sortedPCs,
2257
2345
  isMatch,
2258
- matchIDs
2346
+ matchIDs: ids
2259
2347
  };
2260
2348
  }
2261
2349
  getState(pcs) {
@@ -2266,156 +2354,126 @@ var DFA = class DFA {
2266
2354
  let bucket = this.stateCache.get(hash);
2267
2355
  if (bucket) for (let i = 0; i < bucket.length; i++) {
2268
2356
  const state = bucket[i];
2269
- if (arraysEqual(state.nfaStates, sortedPCs)) {
2270
- state.lastSeen = ++this.clock;
2271
- return state;
2272
- }
2357
+ if (arraysEqual(state.nfaStates, sortedPCs)) return state;
2273
2358
  }
2274
- else {
2359
+ const cost = DFA.STATE_OVERHEAD + 4 * sortedPCs.length + 8 * closureResult.matchIDs.length;
2360
+ if (this.memBudget < cost) return null;
2361
+ this.memBudget -= cost;
2362
+ if (!bucket) {
2275
2363
  bucket = [];
2276
2364
  this.stateCache.set(hash, bucket);
2277
2365
  }
2278
- if (this.failed) return null;
2279
- if (this.stateCount >= this.stateLimit) {
2280
- this.cacheClears++;
2281
- if (this.cacheClears >= DFA.MAX_CACHE_CLEARS) {
2282
- this.failed = true;
2283
- this.stateCache.clear();
2284
- this.stateCount = 0;
2285
- this.startState = null;
2286
- return null;
2287
- }
2288
- this.evictCache();
2289
- bucket = this.stateCache.get(hash);
2290
- if (!bucket) {
2291
- bucket = [];
2292
- this.stateCache.set(hash, bucket);
2293
- }
2294
- }
2295
2366
  const state = new DFAState(sortedPCs, closureResult.isMatch, closureResult.matchIDs);
2296
- state.lastSeen = ++this.clock;
2297
2367
  bucket.push(state);
2298
2368
  this.stateCount++;
2299
2369
  return state;
2300
2370
  }
2301
- evictCache() {
2302
- const allStates = [];
2303
- for (const bucket of this.stateCache.values()) for (let i = 0; i < bucket.length; i++) allStates.push(bucket[i]);
2304
- allStates.sort((a, b) => a.lastSeen - b.lastSeen);
2305
- const keepCount = Math.max(1, Math.floor(this.stateLimit / 2));
2306
- const startIndex = allStates.length - keepCount;
2307
- const survivorsArray = allStates.slice(startIndex);
2308
- const survivors = new Set(survivorsArray);
2371
+ allocTable(state, anchor) {
2372
+ const cost = DFA.TABLE_OVERHEAD + 8 * this.numClasses;
2373
+ if (this.memBudget < cost) return null;
2374
+ this.memBudget -= cost;
2375
+ const table = new Array(this.numClasses).fill(null);
2376
+ if (anchor === RE2Flags.UNANCHORED) state.next = table;
2377
+ else state.nextAnchored = table;
2378
+ return table;
2379
+ }
2380
+ clearCache() {
2309
2381
  this.stateCache.clear();
2310
2382
  this.stateCount = 0;
2311
- for (let i = 0; i < survivorsArray.length; i++) {
2312
- const state = survivorsArray[i];
2313
- state.nextLatin1.fill(null);
2314
- state.nextLatin1Anchored.fill(null);
2315
- state.transKeys.length = 0;
2316
- state.transVals.length = 0;
2317
- const hash = hashPCs(state.nfaStates);
2318
- let bucket = this.stateCache.get(hash);
2319
- if (!bucket) {
2320
- bucket = [];
2321
- this.stateCache.set(hash, bucket);
2322
- }
2323
- bucket.push(state);
2324
- this.stateCount++;
2325
- }
2326
- if (this.startState && !survivors.has(this.startState)) this.startState = null;
2383
+ this.startState = null;
2384
+ this.memBudget = this.stateBudget;
2385
+ }
2386
+ resetCache(current) {
2387
+ this.clearCache();
2388
+ return this.getState(current.nfaStates);
2327
2389
  }
2328
- step(state, charCode, anchor) {
2329
- if (charCode <= Unicode.MAX_LATIN1) if (anchor === RE2Flags.UNANCHORED) {
2330
- const next = state.nextLatin1[charCode];
2331
- if (next !== null) return next;
2390
+ step(state, cls, rune, anchor) {
2391
+ let table = anchor === RE2Flags.UNANCHORED ? state.next : state.nextAnchored;
2392
+ if (table !== null) {
2393
+ const cached = table[cls];
2394
+ if (cached !== null) return cached;
2332
2395
  } else {
2333
- const next = state.nextLatin1Anchored[charCode];
2334
- if (next !== null) return next;
2335
- }
2336
- else {
2337
- const key = charCode + (anchor === RE2Flags.UNANCHORED ? 0 : Unicode.MAX_RUNE + 1);
2338
- const keys = state.transKeys;
2339
- const len = keys.length;
2340
- for (let i = 0; i < len; i++) if (keys[i] === key) return state.transVals[i];
2396
+ table = this.allocTable(state, anchor);
2397
+ if (table === null) return null;
2341
2398
  }
2342
2399
  const nextPCs = [];
2343
2400
  for (let i = 0; i < state.nfaStates.length; i++) {
2344
2401
  const pc = state.nfaStates[i];
2345
2402
  const inst = this.prog.getInst(pc);
2346
- if (Inst.isRuneOp(inst.op) && inst.matchRune(charCode)) nextPCs.push(inst.out);
2403
+ if (Inst.isRuneOp(inst.op) && inst.matchRune(rune)) nextPCs.push(inst.out);
2347
2404
  }
2348
2405
  if (anchor === RE2Flags.UNANCHORED) nextPCs.push(this.prog.start);
2349
2406
  const nextState = this.getState(nextPCs);
2350
- if (charCode <= Unicode.MAX_LATIN1) if (anchor === RE2Flags.UNANCHORED) state.nextLatin1[charCode] = nextState;
2351
- else state.nextLatin1Anchored[charCode] = nextState;
2352
- else {
2353
- const key = charCode + (anchor === RE2Flags.UNANCHORED ? 0 : Unicode.MAX_RUNE + 1);
2354
- state.transKeys.push(key);
2355
- state.transVals.push(nextState);
2356
- }
2407
+ if (nextState === null) return null;
2408
+ table[cls] = nextState;
2357
2409
  return nextState;
2358
2410
  }
2359
- match(input, pos, anchor) {
2360
- if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return false;
2361
- if (!this.startState) {
2411
+ start() {
2412
+ if (this.startState === null) {
2362
2413
  this.startState = this.getState([this.prog.start]);
2363
- if (!this.startState) return null;
2414
+ if (this.startState === null && !this.failed) {
2415
+ this.clearCache();
2416
+ this.startState = this.getState([this.prog.start]);
2417
+ }
2364
2418
  }
2365
- let endPos = input.endPos();
2366
- let currentState = this.startState;
2367
- if (currentState.isMatch) if (anchor === RE2Flags.ANCHOR_BOTH) {
2368
- if (pos === endPos) return true;
2369
- } else return true;
2419
+ return this.startState;
2420
+ }
2421
+ run(input, pos, anchor, state, onState, noMatch) {
2422
+ const endPos = input.endPos();
2423
+ const latin1Class = this.latin1Class;
2424
+ let resetPos = -1;
2370
2425
  let i = pos;
2371
2426
  while (i < endPos) {
2372
2427
  const r = input.step(i);
2373
2428
  const rune = r >> 3;
2374
2429
  const width = r & 7;
2375
2430
  if (width === 0) break;
2376
- currentState = anchor === RE2Flags.UNANCHORED && rune <= Unicode.MAX_LATIN1 && currentState.nextLatin1[rune] || this.step(currentState, rune, anchor);
2377
- if (currentState === null) return null;
2378
- currentState.lastSeen = ++this.clock;
2379
- if (currentState.isMatch) if (anchor === RE2Flags.ANCHOR_BOTH) {
2380
- if (i + width === endPos) return true;
2381
- } else return true;
2382
- if (currentState.nfaStates.length === 0) {
2383
- if (anchor !== RE2Flags.UNANCHORED) return false;
2431
+ const cls = rune <= Unicode.MAX_LATIN1 ? latin1Class[rune] : this.runeClass(rune);
2432
+ let next = anchor === RE2Flags.UNANCHORED && state.next !== null ? state.next[cls] : null;
2433
+ if (next === null) {
2434
+ next = this.step(state, cls, rune, anchor);
2435
+ if (next === null) {
2436
+ if (resetPos >= 0 && i - resetPos < DFA.PROGRESS_PER_STATE * this.stateCount) return null;
2437
+ resetPos = i;
2438
+ state = this.resetCache(state);
2439
+ if (state === null) return null;
2440
+ next = this.step(state, cls, rune, anchor);
2441
+ if (next === null) return null;
2442
+ }
2384
2443
  }
2444
+ state = next;
2385
2445
  i += width;
2446
+ const result = onState(state, i);
2447
+ if (result !== null) return result;
2448
+ if (state.nfaStates.length === 0 && anchor !== RE2Flags.UNANCHORED) return noMatch;
2386
2449
  }
2387
- return false;
2450
+ return noMatch;
2451
+ }
2452
+ match(input, pos, anchor) {
2453
+ if (this.failed) return null;
2454
+ if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return false;
2455
+ const startState = this.start();
2456
+ if (startState === null) return null;
2457
+ const endPos = input.endPos();
2458
+ if (startState.isMatch && (anchor !== RE2Flags.ANCHOR_BOTH || pos === endPos)) return true;
2459
+ return this.run(input, pos, anchor, startState, (state, nextPos) => {
2460
+ if (state.isMatch && (anchor !== RE2Flags.ANCHOR_BOTH || nextPos === endPos)) return true;
2461
+ return null;
2462
+ }, false);
2388
2463
  }
2389
2464
  matchSet(input, pos, anchor) {
2465
+ if (this.failed) return null;
2390
2466
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return [];
2391
- if (!this.startState) {
2392
- this.startState = this.getState([this.prog.start]);
2393
- if (!this.startState) return null;
2394
- }
2395
- let endPos = input.endPos();
2396
- let currentState = this.startState;
2467
+ const startState = this.start();
2468
+ if (startState === null) return null;
2469
+ const endPos = input.endPos();
2397
2470
  const matches = /* @__PURE__ */ new Set();
2398
- const checkMatch = (state, currentPos) => {
2399
- if (state.isMatch) if (anchor === RE2Flags.ANCHOR_BOTH) {
2400
- if (currentPos === endPos) state.matchIDs.forEach((id) => matches.add(id));
2401
- } else state.matchIDs.forEach((id) => matches.add(id));
2471
+ const collect = (state, currentPos) => {
2472
+ if (state.isMatch && (anchor !== RE2Flags.ANCHOR_BOTH || currentPos === endPos)) for (let k = 0; k < state.matchIDs.length; k++) matches.add(state.matchIDs[k]);
2473
+ return null;
2402
2474
  };
2403
- checkMatch(currentState, pos);
2404
- let i = pos;
2405
- while (i < endPos) {
2406
- const r = input.step(i);
2407
- const rune = r >> 3;
2408
- const width = r & 7;
2409
- if (width === 0) break;
2410
- currentState = anchor === RE2Flags.UNANCHORED && rune <= Unicode.MAX_LATIN1 && currentState.nextLatin1[rune] || this.step(currentState, rune, anchor);
2411
- if (currentState === null) return null;
2412
- currentState.lastSeen = ++this.clock;
2413
- i += width;
2414
- checkMatch(currentState, i);
2415
- if (currentState.nfaStates.length === 0) {
2416
- if (anchor !== RE2Flags.UNANCHORED) break;
2417
- }
2418
- }
2475
+ collect(startState, pos);
2476
+ if (this.run(input, pos, anchor, startState, collect, false) === null) return null;
2419
2477
  return Array.from(matches).sort((a, b) => a - b);
2420
2478
  }
2421
2479
  };
@@ -2424,7 +2482,7 @@ var DFA = class DFA {
2424
2482
  const VISITED_BITS = 32;
2425
2483
  const MAX_BACKTRACK_PROG = 500;
2426
2484
  const INITIAL_JOB_CAPACITY = 256;
2427
- const MAX_BACKTRACK_VECTOR = 256 * 1024;
2485
+ const MAX_BACKTRACK_VECTOR = 262144;
2428
2486
  var BitState = class {
2429
2487
  constructor() {
2430
2488
  this.end = 0;
@@ -2707,7 +2765,7 @@ var OnePassProg = class {
2707
2765
  const inst = new Inst(orig.op);
2708
2766
  inst.out = orig.out;
2709
2767
  inst.arg = orig.arg;
2710
- inst.runes = orig.runes ? orig.runes.slice() : [];
2768
+ inst.runes = orig.runes ? new Int32Array(orig.runes) : /* @__PURE__ */ new Int32Array(0);
2711
2769
  inst.next = null;
2712
2770
  this.inst[i] = inst;
2713
2771
  }
@@ -2773,9 +2831,11 @@ const makeOnePass = (p) => {
2773
2831
  m[pc] = true;
2774
2832
  inst.op = Inst.ALT_MATCH;
2775
2833
  }
2776
- const mergeRes = mergeRuneSets(onePassRunes[inst.out] || [], onePassRunes[inst.arg] || [], inst.out, inst.arg);
2834
+ const leftRunes = onePassRunes[inst.out] || [];
2835
+ const rightRunes = onePassRunes[inst.arg] || [];
2836
+ const mergeRes = mergeRuneSets(leftRunes, rightRunes, inst.out, inst.arg);
2777
2837
  if (!mergeRes) return false;
2778
- onePassRunes[pc] = mergeRes.merged;
2838
+ onePassRunes[pc] = new Int32Array(mergeRes.merged);
2779
2839
  inst.next = new Uint32Array(mergeRes.next);
2780
2840
  break;
2781
2841
  }
@@ -2784,7 +2844,7 @@ const makeOnePass = (p) => {
2784
2844
  case Inst.NOP:
2785
2845
  ok = check(inst.out);
2786
2846
  m[pc] = m[inst.out];
2787
- onePassRunes[pc] = onePassRunes[inst.out] ? onePassRunes[inst.out].slice() : [];
2847
+ onePassRunes[pc] = onePassRunes[inst.out] ? new Int32Array(onePassRunes[inst.out]) : /* @__PURE__ */ new Int32Array(0);
2788
2848
  inst.next = new Uint32Array(Math.floor(onePassRunes[pc].length / 2) + 1).fill(inst.out);
2789
2849
  break;
2790
2850
  case Inst.MATCH:
@@ -2796,7 +2856,7 @@ const makeOnePass = (p) => {
2796
2856
  if (inst.next && inst.next.length > 0) break;
2797
2857
  instQueue.insert(inst.out);
2798
2858
  if (!inst.runes || inst.runes.length === 0) {
2799
- onePassRunes[pc] = [];
2859
+ onePassRunes[pc] = /* @__PURE__ */ new Int32Array(0);
2800
2860
  inst.next = new Uint32Array([inst.out]);
2801
2861
  break;
2802
2862
  }
@@ -2807,7 +2867,7 @@ const makeOnePass = (p) => {
2807
2867
  for (let r1 = Unicode.simpleFold(r0); r1 !== r0; r1 = Unicode.simpleFold(r1)) runes.push(r1, r1);
2808
2868
  runes.sort((a, b) => a - b);
2809
2869
  } else for (let j = 0; j < inst.runes.length; j++) runes.push(inst.runes[j]);
2810
- onePassRunes[pc] = runes;
2870
+ onePassRunes[pc] = new Int32Array(runes);
2811
2871
  inst.next = new Uint32Array(Math.floor(runes.length / 2) + 1).fill(inst.out);
2812
2872
  inst.op = Inst.RUNE;
2813
2873
  break;
@@ -2823,7 +2883,7 @@ const makeOnePass = (p) => {
2823
2883
  for (let r1 = Unicode.simpleFold(r0); r1 !== r0; r1 = Unicode.simpleFold(r1)) runes.push(r1, r1);
2824
2884
  runes.sort((a, b) => a - b);
2825
2885
  } else runes.push(inst.runes[0], inst.runes[0]);
2826
- onePassRunes[pc] = runes;
2886
+ onePassRunes[pc] = new Int32Array(runes);
2827
2887
  inst.next = new Uint32Array(Math.floor(runes.length / 2) + 1).fill(inst.out);
2828
2888
  inst.op = Inst.RUNE;
2829
2889
  break;
@@ -2832,21 +2892,20 @@ const makeOnePass = (p) => {
2832
2892
  m[pc] = false;
2833
2893
  if (inst.next && inst.next.length > 0) break;
2834
2894
  instQueue.insert(inst.out);
2835
- onePassRunes[pc] = [0, Unicode.MAX_RUNE];
2895
+ onePassRunes[pc] = new Int32Array([0, Unicode.MAX_RUNE]);
2836
2896
  inst.next = new Uint32Array([inst.out]);
2837
2897
  break;
2838
2898
  case Inst.RUNE_ANY_NOT_NL:
2839
2899
  m[pc] = false;
2840
2900
  if (inst.next && inst.next.length > 0) break;
2841
2901
  instQueue.insert(inst.out);
2842
- onePassRunes[pc] = [
2902
+ onePassRunes[pc] = new Int32Array([
2843
2903
  0,
2844
2904
  9,
2845
2905
  11,
2846
2906
  Unicode.MAX_RUNE
2847
- ];
2907
+ ]);
2848
2908
  inst.next = new Uint32Array(Math.floor(onePassRunes[pc].length / 2) + 1).fill(inst.out);
2849
- break;
2850
2909
  }
2851
2910
  return ok;
2852
2911
  };
@@ -2878,8 +2937,7 @@ const cleanupOnePass = (p, original) => {
2878
2937
  case Inst.RUNE_ANY_NOT_NL:
2879
2938
  p.inst[ix].next = null;
2880
2939
  p.inst[ix].op = instOriginal.op;
2881
- p.inst[ix].runes = instOriginal.runes ? instOriginal.runes.slice() : [];
2882
- break;
2940
+ p.inst[ix].runes = instOriginal.runes ? new Int32Array(instOriginal.runes) : /* @__PURE__ */ new Int32Array(0);
2883
2941
  }
2884
2942
  }
2885
2943
  };
@@ -2908,9 +2966,7 @@ var OnePass = class OnePass {
2908
2966
  return null;
2909
2967
  }
2910
2968
  break;
2911
- default:
2912
- if (opOut === Inst.MATCH && hasAlt) return null;
2913
- break;
2969
+ default: if (opOut === Inst.MATCH && hasAlt) return null;
2914
2970
  }
2915
2971
  }
2916
2972
  let p = onePassCopy(prog);
@@ -3061,7 +3117,7 @@ var Regexp = class Regexp {
3061
3117
  this.op = op;
3062
3118
  this.flags = 0;
3063
3119
  this.subs = Regexp.emptySubs();
3064
- this.runes = [];
3120
+ this.runes = /* @__PURE__ */ new Int32Array(0);
3065
3121
  this.min = 0;
3066
3122
  this.max = 0;
3067
3123
  this.cap = 0;
@@ -3072,7 +3128,7 @@ var Regexp = class Regexp {
3072
3128
  reinit() {
3073
3129
  this.flags = 0;
3074
3130
  this.subs = Regexp.emptySubs();
3075
- this.runes = [];
3131
+ this.runes = /* @__PURE__ */ new Int32Array(0);
3076
3132
  this.cap = 0;
3077
3133
  this.min = 0;
3078
3134
  this.max = 0;
@@ -3116,7 +3172,6 @@ var Regexp = class Regexp {
3116
3172
  if (this.max >= 0) out += this.max;
3117
3173
  }
3118
3174
  out += "}";
3119
- break;
3120
3175
  }
3121
3176
  if ((this.flags & RE2Flags.NON_GREEDY) !== 0) out += "?";
3122
3177
  break;
@@ -3209,9 +3264,7 @@ var Regexp = class Regexp {
3209
3264
  }
3210
3265
  out += "]";
3211
3266
  break;
3212
- default:
3213
- out += this.op;
3214
- break;
3267
+ default: out += this.op;
3215
3268
  }
3216
3269
  return out;
3217
3270
  }
@@ -3255,70 +3308,158 @@ var Regexp = class Regexp {
3255
3308
  if (this.cap !== that.cap || (this.name === null ? that.name !== null : this.name !== that.name) || !this.subs[0].equals(that.subs[0])) return false;
3256
3309
  break;
3257
3310
  case Regexp.Op.PLB:
3258
- case Regexp.Op.NLB:
3259
- if (this.lb !== that.lb || !this.subs[0].equals(that.subs[0])) return false;
3260
- break;
3311
+ case Regexp.Op.NLB: if (this.lb !== that.lb || !this.subs[0].equals(that.subs[0])) return false;
3261
3312
  }
3262
3313
  return true;
3263
3314
  }
3264
3315
  };
3265
3316
  //#endregion
3317
+ //#region src/RegexpWalker.js
3318
+ /**
3319
+ * Walks a Regexp tree without recursion, visiting nodes in the same order a recursive walk
3320
+ * would (RE2: re2/walker-inl.h, "Helper class for traversing Regexps without recursion").
3321
+ *
3322
+ * The handler sees a node without subexpressions once, through `leaf(re)`, which returns its
3323
+ * result. Any other node is seen up to three times:
3324
+ * - `enter(re)` before its children. It returns the state kept for `re` while its children are
3325
+ * visited; a state whose `result` is already set skips the children.
3326
+ * - `child(re, state, result, i)` after child `i` produced `result`. Setting `state.result`
3327
+ * stops visiting the remaining children.
3328
+ * - `leave(re, state)` after the last child; it returns the result for `re`.
3329
+ *
3330
+ * Counted repetitions expand into one nesting level per copy, so the simplified tree can be
3331
+ * thousands of levels deep; recursion over it would overflow the JS stack, which has no fixed
3332
+ * relation to the parser's nesting limit. This walk keeps its frames on the heap instead.
3333
+ */
3334
+ var RegexpWalker = class {
3335
+ static walk(root, handler) {
3336
+ const stack = [];
3337
+ let re = root;
3338
+ let result;
3339
+ for (;;) {
3340
+ if (re.subs.length === 0) result = handler.leaf(re);
3341
+ else {
3342
+ const state = handler.enter(re);
3343
+ if (state.result === void 0) {
3344
+ stack.push({
3345
+ re,
3346
+ state,
3347
+ i: 0
3348
+ });
3349
+ re = re.subs[0];
3350
+ continue;
3351
+ }
3352
+ result = state.result;
3353
+ }
3354
+ for (;;) {
3355
+ if (stack.length === 0) return result;
3356
+ const frame = stack[stack.length - 1];
3357
+ handler.child(frame.re, frame.state, result, frame.i);
3358
+ frame.i++;
3359
+ if (frame.state.result === void 0 && frame.i < frame.re.subs.length) {
3360
+ re = frame.re.subs[frame.i];
3361
+ break;
3362
+ }
3363
+ stack.pop();
3364
+ result = frame.state.result !== void 0 ? frame.state.result : handler.leave(frame.re, frame.state);
3365
+ }
3366
+ }
3367
+ }
3368
+ };
3369
+ //#endregion
3266
3370
  //#region src/Prefilter.js
3267
3371
  var AhoCorasick = class {
3268
- constructor(wordArrays) {
3269
- this.next = [Object.create(null)];
3270
- this.fail = [0];
3271
- this.match = [false];
3272
- for (const word of wordArrays) {
3372
+ constructor(words, alphabetSize) {
3373
+ const edges = /* @__PURE__ */ new Map();
3374
+ const parent = [0];
3375
+ const unit = [0];
3376
+ const depth = [0];
3377
+ const terminal = [0];
3378
+ for (const word of words) {
3273
3379
  let node = 0;
3274
3380
  for (let i = 0; i < word.length; i++) {
3275
- const val = word[i];
3276
- if (!(val in this.next[node])) {
3277
- this.next.push(Object.create(null));
3278
- this.fail.push(0);
3279
- this.match.push(false);
3280
- this.next[node][val] = this.next.length - 1;
3381
+ const key = node * alphabetSize + word[i];
3382
+ let child = edges.get(key);
3383
+ if (child === void 0) {
3384
+ child = parent.length;
3385
+ edges.set(key, child);
3386
+ parent.push(node);
3387
+ unit.push(word[i]);
3388
+ depth.push(depth[node] + 1);
3389
+ terminal.push(0);
3281
3390
  }
3282
- node = this.next[node][val];
3391
+ node = child;
3283
3392
  }
3284
- this.match[node] = true;
3285
- }
3286
- const queue = [];
3287
- for (const val in this.next[0]) if (Object.prototype.hasOwnProperty.call(this.next[0], val)) {
3288
- const child = this.next[0][val];
3289
- this.fail[child] = 0;
3290
- queue.push(child);
3291
- }
3292
- while (queue.length > 0) {
3293
- const curr = queue.shift();
3294
- for (const val in this.next[curr]) if (Object.prototype.hasOwnProperty.call(this.next[curr], val)) {
3295
- const child = this.next[curr][val];
3296
- let failNode = this.fail[curr];
3297
- while (failNode !== 0 && !(val in this.next[failNode])) failNode = this.fail[failNode];
3298
- if (val in this.next[failNode]) this.fail[child] = this.next[failNode][val];
3299
- else this.fail[child] = 0;
3300
- this.match[child] = this.match[child] || this.match[this.fail[child]];
3301
- queue.push(child);
3393
+ terminal[node] = 1;
3394
+ }
3395
+ const numNodes = parent.length;
3396
+ const children = new Int32Array(numNodes - 1);
3397
+ for (let c = 1; c < numNodes; c++) children[c - 1] = c;
3398
+ children.sort((a, b) => parent[a] - parent[b] || unit[a] - unit[b]);
3399
+ const childStart = new Int32Array(numNodes + 1);
3400
+ for (let c = 1; c < numNodes; c++) childStart[parent[c] + 1]++;
3401
+ for (let n = 0; n < numNodes; n++) childStart[n + 1] += childStart[n];
3402
+ const childUnit = new Int32Array(numNodes - 1);
3403
+ for (let k = 0; k < children.length; k++) childUnit[k] = unit[children[k]];
3404
+ this.childStart = childStart;
3405
+ this.childUnit = childUnit;
3406
+ this.childNode = children;
3407
+ const byDepth = new Int32Array(numNodes);
3408
+ for (let n = 0; n < numNodes; n++) byDepth[n] = n;
3409
+ byDepth.sort((a, b) => depth[a] - depth[b]);
3410
+ const fail = new Int32Array(numNodes);
3411
+ const match = Uint8Array.from(terminal);
3412
+ for (let k = 1; k < numNodes; k++) {
3413
+ const c = byDepth[k];
3414
+ const p = parent[c];
3415
+ if (p !== 0) {
3416
+ let f = fail[p];
3417
+ let next = this.next(f, unit[c]);
3418
+ while (next < 0 && f !== 0) {
3419
+ f = fail[f];
3420
+ next = this.next(f, unit[c]);
3421
+ }
3422
+ fail[c] = next >= 0 ? next : 0;
3302
3423
  }
3424
+ match[c] |= match[fail[c]];
3425
+ }
3426
+ this.fail = fail;
3427
+ this.match = match;
3428
+ }
3429
+ next(node, u) {
3430
+ let lo = this.childStart[node];
3431
+ let hi = this.childStart[node + 1];
3432
+ const units = this.childUnit;
3433
+ while (lo < hi) {
3434
+ const mid = lo + hi >> 1;
3435
+ const m = units[mid];
3436
+ if (m === u) return this.childNode[mid];
3437
+ if (m < u) lo = mid + 1;
3438
+ else hi = mid;
3439
+ }
3440
+ return -1;
3441
+ }
3442
+ advance(node, u) {
3443
+ for (;;) {
3444
+ const next = this.next(node, u);
3445
+ if (next >= 0) return next;
3446
+ if (node === 0) return 0;
3447
+ node = this.fail[node];
3303
3448
  }
3304
3449
  }
3305
3450
  searchUTF16(charSeq, start, end) {
3306
3451
  let node = 0;
3307
3452
  for (let i = start; i < end; i++) {
3308
- const val = charSeq.charCodeAt(i);
3309
- while (node !== 0 && !(val in this.next[node])) node = this.fail[node];
3310
- if (val in this.next[node]) node = this.next[node][val];
3311
- if (this.match[node]) return true;
3453
+ node = this.advance(node, charSeq.charCodeAt(i));
3454
+ if (this.match[node] === 1) return true;
3312
3455
  }
3313
3456
  return false;
3314
3457
  }
3315
3458
  searchUTF8(bytes, start, end) {
3316
3459
  let node = 0;
3317
3460
  for (let i = start; i < end; i++) {
3318
- const val = bytes[i];
3319
- while (node !== 0 && !(val in this.next[node])) node = this.fail[node];
3320
- if (val in this.next[node]) node = this.next[node][val];
3321
- if (this.match[node]) return true;
3461
+ node = this.advance(node, bytes[i]);
3462
+ if (this.match[node] === 1) return true;
3322
3463
  }
3323
3464
  return false;
3324
3465
  }
@@ -3330,14 +3471,33 @@ var Prefilter = class Prefilter {
3330
3471
  AND: 2,
3331
3472
  OR: 3
3332
3473
  };
3474
+ static MAX_SCANS = 32;
3475
+ static MAX_AC_UNITS = 1 << 18;
3333
3476
  constructor(type) {
3334
3477
  this.type = type;
3335
3478
  this.subs = [];
3336
3479
  this.str = "";
3337
3480
  this.bytes = null;
3481
+ this.cost = type === Prefilter.Type.EXACT ? 1 : 0;
3482
+ this.allExact = false;
3338
3483
  this.ac16 = null;
3339
3484
  this.ac8 = null;
3340
3485
  }
3486
+ automaton16() {
3487
+ if (this.ac16 === null) {
3488
+ const words = this.subs.map((s) => {
3489
+ const arr = new Array(s.str.length);
3490
+ for (let i = 0; i < s.str.length; i++) arr[i] = s.str.charCodeAt(i);
3491
+ return arr;
3492
+ });
3493
+ this.ac16 = new AhoCorasick(words, 65536);
3494
+ }
3495
+ return this.ac16;
3496
+ }
3497
+ automaton8() {
3498
+ if (this.ac8 === null) this.ac8 = new AhoCorasick(this.subs.map((s) => s.bytes), 256);
3499
+ return this.ac8;
3500
+ }
3341
3501
  eval(input, pos) {
3342
3502
  switch (this.type) {
3343
3503
  case Prefilter.Type.NONE: return true;
@@ -3346,7 +3506,7 @@ var Prefilter = class Prefilter {
3346
3506
  for (let i = 0; i < this.subs.length; i++) if (!this.subs[i].eval(input, pos)) return false;
3347
3507
  return true;
3348
3508
  case Prefilter.Type.OR:
3349
- if (this.ac16 && this.ac8) return input.hasAnyString(this, pos);
3509
+ if (this.allExact) return input.hasAnyString(this, pos);
3350
3510
  for (let i = 0; i < this.subs.length; i++) if (this.subs[i].eval(input, pos)) return true;
3351
3511
  return false;
3352
3512
  default: return true;
@@ -3355,100 +3515,115 @@ var Prefilter = class Prefilter {
3355
3515
  };
3356
3516
  var PrefilterTree = class PrefilterTree {
3357
3517
  static build(re) {
3358
- const pf = PrefilterTree.fromRegexp(re);
3359
- return PrefilterTree.simplify(pf);
3360
- }
3361
- static fromRegexp(re) {
3362
3518
  if (!re) return new Prefilter(Prefilter.Type.NONE);
3519
+ return RegexpWalker.walk(re, PrefilterTree.WALKER);
3520
+ }
3521
+ static WALKER = {
3522
+ leaf: (re) => PrefilterTree.leaf(re),
3523
+ enter: (re) => PrefilterTree.enter(re),
3524
+ child: (re, state, result) => PrefilterTree.child(re, state, result),
3525
+ leave: (re, state) => PrefilterTree.leave(re, state)
3526
+ };
3527
+ static leaf(re) {
3528
+ if (re.op !== Regexp.Op.LITERAL) return new Prefilter(Prefilter.Type.NONE);
3529
+ if (re.runes.length === 0 || (re.flags & RE2Flags.FOLD_CASE) !== 0) return new Prefilter(Prefilter.Type.NONE);
3530
+ let str = "";
3531
+ for (let i = 0; i < re.runes.length; i++) {
3532
+ if (Unicode.isUnsearchable(re.runes[i])) return new Prefilter(Prefilter.Type.NONE);
3533
+ str += String.fromCodePoint(re.runes[i]);
3534
+ }
3535
+ const pf = new Prefilter(Prefilter.Type.EXACT);
3536
+ pf.str = str;
3537
+ pf.bytes = Utils.stringToUtf8ByteArray(pf.str);
3538
+ return pf;
3539
+ }
3540
+ static enter(re) {
3363
3541
  switch (re.op) {
3364
- case Regexp.Op.PLB:
3365
- case Regexp.Op.NLB:
3366
- case Regexp.Op.NO_MATCH:
3367
- case Regexp.Op.EMPTY_MATCH:
3368
- case Regexp.Op.BEGIN_LINE:
3369
- case Regexp.Op.END_LINE:
3370
- case Regexp.Op.BEGIN_TEXT:
3371
- case Regexp.Op.END_TEXT:
3372
- case Regexp.Op.WORD_BOUNDARY:
3373
- case Regexp.Op.NO_WORD_BOUNDARY:
3374
- case Regexp.Op.CHAR_CLASS:
3375
- case Regexp.Op.ANY_CHAR_NOT_NL:
3376
- case Regexp.Op.ANY_CHAR: return new Prefilter(Prefilter.Type.NONE);
3377
- case Regexp.Op.LITERAL: {
3378
- if (re.runes.length === 0 || (re.flags & RE2Flags.FOLD_CASE) !== 0) return new Prefilter(Prefilter.Type.NONE);
3379
- const pf = new Prefilter(Prefilter.Type.EXACT);
3380
- let str = "";
3381
- for (let i = 0; i < re.runes.length; i++) str += String.fromCodePoint(re.runes[i]);
3382
- pf.str = str;
3383
- pf.bytes = Utils.stringToUtf8ByteArray(pf.str);
3384
- return pf;
3385
- }
3386
3542
  case Regexp.Op.CAPTURE:
3387
- case Regexp.Op.PLUS: return PrefilterTree.fromRegexp(re.subs[0]);
3543
+ case Regexp.Op.PLUS: return { sub: null };
3388
3544
  case Regexp.Op.REPEAT:
3389
- if (re.min >= 1) return PrefilterTree.fromRegexp(re.subs[0]);
3390
- return new Prefilter(Prefilter.Type.NONE);
3391
- case Regexp.Op.CONCAT: {
3392
- const pf = new Prefilter(Prefilter.Type.AND);
3393
- for (const sub of re.subs) pf.subs.push(PrefilterTree.fromRegexp(sub));
3394
- return pf;
3545
+ if (re.min >= 1) return { sub: null };
3546
+ return { result: new Prefilter(Prefilter.Type.NONE) };
3547
+ case Regexp.Op.CONCAT: return {
3548
+ pf: new Prefilter(Prefilter.Type.AND),
3549
+ subs: []
3550
+ };
3551
+ case Regexp.Op.ALTERNATE: return {
3552
+ pf: new Prefilter(Prefilter.Type.OR),
3553
+ subs: []
3554
+ };
3555
+ default: return { result: new Prefilter(Prefilter.Type.NONE) };
3556
+ }
3557
+ }
3558
+ static child(re, state, s) {
3559
+ if (state.subs === void 0) {
3560
+ state.sub = s;
3561
+ return;
3562
+ }
3563
+ if (state.pf.type === Prefilter.Type.AND) {
3564
+ if (s.type === Prefilter.Type.NONE) return;
3565
+ if (s.type === Prefilter.Type.AND) {
3566
+ for (let j = 0; j < s.subs.length; j++) state.subs.push(s.subs[j]);
3567
+ return;
3395
3568
  }
3396
- case Regexp.Op.ALTERNATE: {
3397
- const pf = new Prefilter(Prefilter.Type.OR);
3398
- for (const sub of re.subs) pf.subs.push(PrefilterTree.fromRegexp(sub));
3399
- return pf;
3569
+ state.subs.push(s);
3570
+ return;
3571
+ }
3572
+ if (s.type === Prefilter.Type.NONE) {
3573
+ state.result = new Prefilter(Prefilter.Type.NONE);
3574
+ return;
3575
+ }
3576
+ if (s.type === Prefilter.Type.OR) {
3577
+ for (let j = 0; j < s.subs.length; j++) state.subs.push(s.subs[j]);
3578
+ return;
3579
+ }
3580
+ state.subs.push(s);
3581
+ }
3582
+ static leave(re, state) {
3583
+ if (state.subs === void 0) return state.sub;
3584
+ const pf = state.pf;
3585
+ const seen = /* @__PURE__ */ new Set();
3586
+ const subs = [];
3587
+ for (const sub of state.subs) {
3588
+ if (sub.type === Prefilter.Type.EXACT) {
3589
+ if (seen.has(sub.str)) continue;
3590
+ seen.add(sub.str);
3400
3591
  }
3401
- default: return new Prefilter(Prefilter.Type.NONE);
3592
+ subs.push(sub);
3402
3593
  }
3403
- }
3404
- static simplify(pf) {
3405
- if (pf.type === Prefilter.Type.EXACT || pf.type === Prefilter.Type.NONE) return pf;
3406
3594
  if (pf.type === Prefilter.Type.AND) {
3407
- const newSubs = [];
3408
- for (const sub of pf.subs) {
3409
- const s = PrefilterTree.simplify(sub);
3410
- if (s.type !== Prefilter.Type.NONE) if (s.type === Prefilter.Type.AND) for (let j = 0; j < s.subs.length; j++) newSubs.push(s.subs[j]);
3411
- else newSubs.push(s);
3595
+ const kept = [];
3596
+ let cost = 0;
3597
+ for (const sub of subs) {
3598
+ if (cost + sub.cost > Prefilter.MAX_SCANS) continue;
3599
+ kept.push(sub);
3600
+ cost += sub.cost;
3412
3601
  }
3413
- if (newSubs.length === 0) return new Prefilter(Prefilter.Type.NONE);
3414
- if (newSubs.length === 1) return newSubs[0];
3415
- pf.subs = newSubs;
3602
+ if (kept.length === 0) return new Prefilter(Prefilter.Type.NONE);
3603
+ if (kept.length === 1) return kept[0];
3604
+ pf.subs = kept;
3605
+ pf.cost = cost;
3416
3606
  return pf;
3417
3607
  }
3418
- if (pf.type === Prefilter.Type.OR) {
3419
- const newSubs = [];
3420
- for (const sub of pf.subs) {
3421
- const s = PrefilterTree.simplify(sub);
3422
- if (s.type === Prefilter.Type.NONE) return new Prefilter(Prefilter.Type.NONE);
3423
- if (s.type === Prefilter.Type.OR) for (let j = 0; j < s.subs.length; j++) newSubs.push(s.subs[j]);
3424
- else newSubs.push(s);
3425
- }
3426
- if (newSubs.length === 0) return new Prefilter(Prefilter.Type.NONE);
3427
- if (newSubs.length === 1) return newSubs[0];
3428
- const seen = /* @__PURE__ */ new Set();
3429
- const uniqueSubs = [];
3430
- for (const sub of newSubs) if (sub.type === Prefilter.Type.EXACT) {
3431
- if (!seen.has(sub.str)) {
3432
- seen.add(sub.str);
3433
- uniqueSubs.push(sub);
3434
- }
3435
- } else uniqueSubs.push(sub);
3436
- pf.subs = uniqueSubs;
3437
- let allExact = true;
3438
- for (const sub of uniqueSubs) if (sub.type !== Prefilter.Type.EXACT) {
3439
- allExact = false;
3440
- break;
3441
- }
3442
- if (allExact && uniqueSubs.length > 1) {
3443
- pf.ac16 = new AhoCorasick(uniqueSubs.map((s) => {
3444
- const arr = [];
3445
- for (let i = 0; i < s.str.length; i++) arr.push(s.str.charCodeAt(i));
3446
- return arr;
3447
- }));
3448
- pf.ac8 = new AhoCorasick(uniqueSubs.map((s) => s.bytes));
3449
- }
3608
+ if (subs.length === 0) return new Prefilter(Prefilter.Type.NONE);
3609
+ if (subs.length === 1) return subs[0];
3610
+ pf.subs = subs;
3611
+ let allExact = true;
3612
+ let cost = 0;
3613
+ let units = 0;
3614
+ for (const sub of subs) {
3615
+ if (sub.type !== Prefilter.Type.EXACT) allExact = false;
3616
+ else units += sub.bytes.length;
3617
+ cost += sub.cost;
3618
+ }
3619
+ if (allExact) {
3620
+ if (units > Prefilter.MAX_AC_UNITS) return new Prefilter(Prefilter.Type.NONE);
3621
+ pf.allExact = true;
3622
+ pf.cost = 1;
3450
3623
  return pf;
3451
3624
  }
3625
+ if (cost > Prefilter.MAX_SCANS) return new Prefilter(Prefilter.Type.NONE);
3626
+ pf.cost = cost;
3452
3627
  return pf;
3453
3628
  }
3454
3629
  };
@@ -3484,6 +3659,7 @@ var Prog = class {
3484
3659
  this.numCap = 2;
3485
3660
  this.lbStarts = [];
3486
3661
  this.numLb = 0;
3662
+ this.lbInst = null;
3487
3663
  }
3488
3664
  getInst(pc) {
3489
3665
  return this.inst[pc];
@@ -3494,6 +3670,26 @@ var Prog = class {
3494
3670
  addInst(op) {
3495
3671
  this.inst.push(new Inst(op));
3496
3672
  }
3673
+ markLookbehindInsts() {
3674
+ const lbInst = new Uint8Array(this.inst.length);
3675
+ const pending = this.lbStarts.slice();
3676
+ while (pending.length > 0) {
3677
+ const pc = pending.pop();
3678
+ if (pc === 0 || lbInst[pc] === 1) continue;
3679
+ lbInst[pc] = 1;
3680
+ const i = this.inst[pc];
3681
+ switch (i.op) {
3682
+ case Inst.ALT:
3683
+ case Inst.ALT_MATCH:
3684
+ pending.push(i.out, i.arg);
3685
+ break;
3686
+ case Inst.MATCH:
3687
+ case Inst.FAIL: break;
3688
+ default: pending.push(i.out);
3689
+ }
3690
+ }
3691
+ this.lbInst = lbInst;
3692
+ }
3497
3693
  skipNop(pc) {
3498
3694
  let i = this.inst[pc];
3499
3695
  while (i.op === Inst.NOP || i.op === Inst.CAPTURE) {
@@ -3506,7 +3702,7 @@ var Prog = class {
3506
3702
  let prefix = "";
3507
3703
  let i = this.skipNop(this.start);
3508
3704
  if (!Inst.isRuneOp(i.op) || i.runes.length !== 1) return [i.op === Inst.MATCH, prefix];
3509
- while (Inst.isRuneOp(i.op) && i.runes.length === 1 && (i.arg & RE2Flags.FOLD_CASE) === 0) {
3705
+ while (Inst.isRuneOp(i.op) && i.runes.length === 1 && (i.arg & RE2Flags.FOLD_CASE) === 0 && !Unicode.isUnsearchable(i.runes[0])) {
3510
3706
  prefix += String.fromCodePoint(i.runes[0]);
3511
3707
  i = this.skipNop(i.out);
3512
3708
  }
@@ -3570,6 +3766,13 @@ var Prog = class {
3570
3766
  };
3571
3767
  //#endregion
3572
3768
  //#region src/Compiler.js
3769
+ const ANY_RUNE_NOT_NL = new Int32Array([
3770
+ 0,
3771
+ Codepoint.CODES.get("\n") - 1,
3772
+ Codepoint.CODES.get("\n") + 1,
3773
+ Unicode.MAX_RUNE
3774
+ ]);
3775
+ const ANY_RUNE = new Int32Array([0, Unicode.MAX_RUNE]);
3573
3776
  /**
3574
3777
  * A fragment of a compiled regular expression program.
3575
3778
  *
@@ -3589,28 +3792,19 @@ var Frag = class {
3589
3792
  * The only entry point is {@link #compileRegexp}.
3590
3793
  */
3591
3794
  var Compiler = class Compiler {
3592
- static ANY_RUNE_NOT_NL() {
3593
- return [
3594
- 0,
3595
- Codepoint.CODES.get("\n") - 1,
3596
- Codepoint.CODES.get("\n") + 1,
3597
- Unicode.MAX_RUNE
3598
- ];
3599
- }
3600
- static ANY_RUNE() {
3601
- return [0, Unicode.MAX_RUNE];
3602
- }
3603
3795
  static compileRegexp(re) {
3604
3796
  const c = new Compiler();
3605
3797
  const f = c.compile(re);
3606
3798
  c.prog.patch(f.out, c.newInst(Inst.MATCH).i);
3607
3799
  c.prog.start = f.i;
3800
+ c.prog.markLookbehindInsts();
3608
3801
  return c.prog;
3609
3802
  }
3610
3803
  static compileSet(regexps) {
3611
3804
  const c = new Compiler();
3612
3805
  if (regexps.length === 0) {
3613
3806
  c.prog.start = c.newInst(Inst.FAIL).i;
3807
+ c.prog.markLookbehindInsts();
3614
3808
  return c.prog;
3615
3809
  }
3616
3810
  let starts = [];
@@ -3630,11 +3824,19 @@ var Compiler = class Compiler {
3630
3824
  start = f.i;
3631
3825
  }
3632
3826
  c.prog.start = start;
3827
+ c.prog.markLookbehindInsts();
3633
3828
  return c.prog;
3634
3829
  }
3635
3830
  constructor() {
3636
3831
  this.prog = new Prog();
3637
3832
  this.newInst(Inst.FAIL);
3833
+ this.runeCache = /* @__PURE__ */ new Map();
3834
+ this.walker = {
3835
+ leaf: (re) => this.leaf(re),
3836
+ enter: (re) => this.enter(re),
3837
+ child: (re, state, result) => this.child(re, state, result),
3838
+ leave: (re, state) => this.leave(re, state)
3839
+ };
3638
3840
  }
3639
3841
  newInst(op) {
3640
3842
  this.prog.addInst(op);
@@ -3727,7 +3929,7 @@ var Compiler = class Compiler {
3727
3929
  lookBehind(a, lb) {
3728
3930
  const id = this.newInst(Inst.LB_WRITE);
3729
3931
  this.prog.getInst(id.i).arg = lb;
3730
- const any = this.rune(Compiler.ANY_RUNE(), 0);
3932
+ const any = this.rune(ANY_RUNE, 0);
3731
3933
  const dotStar = this.star(any, true);
3732
3934
  const lbAutomaton = this.cat(dotStar, a);
3733
3935
  this.prog.patch(lbAutomaton.out, id.i);
@@ -3739,56 +3941,74 @@ var Compiler = class Compiler {
3739
3941
  return checkId;
3740
3942
  }
3741
3943
  compile(re) {
3944
+ return RegexpWalker.walk(re, this.walker);
3945
+ }
3946
+ leaf(re) {
3742
3947
  switch (re.op) {
3743
3948
  case Regexp.Op.NO_MATCH: return this.fail();
3744
3949
  case Regexp.Op.EMPTY_MATCH: return this.nop();
3745
- case Regexp.Op.LITERAL: if (re.runes.length === 0) return this.nop();
3746
- else {
3950
+ case Regexp.Op.LITERAL: {
3951
+ if (re.runes.length === 0) return this.nop();
3747
3952
  let f = null;
3748
3953
  for (let r of re.runes) {
3749
- const f1 = this.rune([r], re.flags);
3954
+ let singleRune = this.runeCache.get(r);
3955
+ if (singleRune === void 0) {
3956
+ singleRune = new Int32Array([r]);
3957
+ this.runeCache.set(r, singleRune);
3958
+ }
3959
+ const f1 = this.rune(singleRune, re.flags);
3750
3960
  f = f === null ? f1 : this.cat(f, f1);
3751
3961
  }
3752
3962
  return f;
3753
3963
  }
3754
3964
  case Regexp.Op.CHAR_CLASS: return this.rune(re.runes, re.flags);
3755
- case Regexp.Op.ANY_CHAR_NOT_NL: return this.rune(Compiler.ANY_RUNE_NOT_NL(), 0);
3756
- case Regexp.Op.ANY_CHAR: return this.rune(Compiler.ANY_RUNE(), 0);
3965
+ case Regexp.Op.ANY_CHAR_NOT_NL: return this.rune(ANY_RUNE_NOT_NL, 0);
3966
+ case Regexp.Op.ANY_CHAR: return this.rune(ANY_RUNE, 0);
3757
3967
  case Regexp.Op.BEGIN_LINE: return this.empty(Utils.EMPTY_BEGIN_LINE);
3758
3968
  case Regexp.Op.END_LINE: return this.empty(Utils.EMPTY_END_LINE);
3759
3969
  case Regexp.Op.BEGIN_TEXT: return this.empty(Utils.EMPTY_BEGIN_TEXT);
3760
3970
  case Regexp.Op.END_TEXT: return this.empty(Utils.EMPTY_END_TEXT);
3761
3971
  case Regexp.Op.WORD_BOUNDARY: return this.empty(Utils.EMPTY_WORD_BOUNDARY);
3762
3972
  case Regexp.Op.NO_WORD_BOUNDARY: return this.empty(Utils.EMPTY_NO_WORD_BOUNDARY);
3973
+ case Regexp.Op.CONCAT:
3974
+ case Regexp.Op.ALTERNATE: return this.nop();
3975
+ default: throw new RE2JSCompileException("regexp: unhandled case in compile");
3976
+ }
3977
+ }
3978
+ enter(re) {
3979
+ switch (re.op) {
3980
+ case Regexp.Op.PLB:
3981
+ case Regexp.Op.NLB:
3982
+ case Regexp.Op.STAR:
3983
+ case Regexp.Op.PLUS:
3984
+ case Regexp.Op.QUEST: return { sub: null };
3985
+ case Regexp.Op.CAPTURE: return {
3986
+ bra: this.cap(re.cap << 1),
3987
+ sub: null
3988
+ };
3989
+ case Regexp.Op.CONCAT:
3990
+ case Regexp.Op.ALTERNATE: return { f: null };
3991
+ default: throw new RE2JSCompileException("regexp: unhandled case in compile");
3992
+ }
3993
+ }
3994
+ child(re, state, f1) {
3995
+ if (re.op === Regexp.Op.CONCAT) state.f = state.f === null ? f1 : this.cat(state.f, f1);
3996
+ else if (re.op === Regexp.Op.ALTERNATE) state.f = state.f === null ? f1 : this.alt(state.f, f1);
3997
+ else state.sub = f1;
3998
+ }
3999
+ leave(re, state) {
4000
+ switch (re.op) {
3763
4001
  case Regexp.Op.PLB:
3764
- case Regexp.Op.NLB: return this.lookBehind(this.compile(re.subs[0]), re.lb);
4002
+ case Regexp.Op.NLB: return this.lookBehind(state.sub, re.lb);
3765
4003
  case Regexp.Op.CAPTURE: {
3766
- const bra = this.cap(re.cap << 1);
3767
- const sub = this.compile(re.subs[0]);
3768
4004
  const ket = this.cap(re.cap << 1 | 1);
3769
- return this.cat(this.cat(bra, sub), ket);
3770
- }
3771
- case Regexp.Op.STAR: return this.star(this.compile(re.subs[0]), (re.flags & RE2Flags.NON_GREEDY) !== 0);
3772
- case Regexp.Op.PLUS: return this.plus(this.compile(re.subs[0]), (re.flags & RE2Flags.NON_GREEDY) !== 0);
3773
- case Regexp.Op.QUEST: return this.quest(this.compile(re.subs[0]), (re.flags & RE2Flags.NON_GREEDY) !== 0);
3774
- case Regexp.Op.CONCAT: if (re.subs.length === 0) return this.nop();
3775
- else {
3776
- let f = null;
3777
- for (let sub of re.subs) {
3778
- const f1 = this.compile(sub);
3779
- f = f === null ? f1 : this.cat(f, f1);
3780
- }
3781
- return f;
3782
- }
3783
- case Regexp.Op.ALTERNATE: if (re.subs.length === 0) return this.nop();
3784
- else {
3785
- let f = null;
3786
- for (let sub of re.subs) {
3787
- const f1 = this.compile(sub);
3788
- f = f === null ? f1 : this.alt(f, f1);
3789
- }
3790
- return f;
4005
+ return this.cat(this.cat(state.bra, state.sub), ket);
3791
4006
  }
4007
+ case Regexp.Op.STAR: return this.star(state.sub, (re.flags & RE2Flags.NON_GREEDY) !== 0);
4008
+ case Regexp.Op.PLUS: return this.plus(state.sub, (re.flags & RE2Flags.NON_GREEDY) !== 0);
4009
+ case Regexp.Op.QUEST: return this.quest(state.sub, (re.flags & RE2Flags.NON_GREEDY) !== 0);
4010
+ case Regexp.Op.CONCAT:
4011
+ case Regexp.Op.ALTERNATE: return state.f;
3792
4012
  default: throw new RE2JSCompileException("regexp: unhandled case in compile");
3793
4013
  }
3794
4014
  }
@@ -3798,111 +4018,149 @@ var Compiler = class Compiler {
3798
4018
  var Simplify = class Simplify {
3799
4019
  static simplify(re) {
3800
4020
  if (re === null) return null;
4021
+ return RegexpWalker.walk(re, Simplify.WALKER);
4022
+ }
4023
+ static WALKER = {
4024
+ leaf: (re) => Simplify.leaf(re),
4025
+ enter: (re) => Simplify.enter(re),
4026
+ child: (re, state, result, i) => Simplify.child(re, state, result, i),
4027
+ leave: (re, state) => Simplify.leave(re, state)
4028
+ };
4029
+ static leaf(re) {
4030
+ if (re.op === Regexp.Op.CHAR_CLASS) {
4031
+ if (re.runes === null) return re;
4032
+ if (re.runes.length === 0) return new Regexp(Regexp.Op.NO_MATCH);
4033
+ if (re.runes.length === 2 && re.runes[0] === 0 && re.runes[1] === Unicode.MAX_RUNE) return new Regexp(Regexp.Op.ANY_CHAR);
4034
+ if (re.runes.length === 4 && re.runes[0] === 0 && re.runes[1] === Codepoint.CODES.get("\n") - 1 && re.runes[2] === Codepoint.CODES.get("\n") + 1 && re.runes[3] === Unicode.MAX_RUNE) return new Regexp(Regexp.Op.ANY_CHAR_NOT_NL);
4035
+ }
4036
+ return re;
4037
+ }
4038
+ static enter(re) {
4039
+ switch (re.op) {
4040
+ case Regexp.Op.PLB:
4041
+ case Regexp.Op.NLB:
4042
+ case Regexp.Op.CAPTURE:
4043
+ case Regexp.Op.STAR:
4044
+ case Regexp.Op.PLUS:
4045
+ case Regexp.Op.QUEST: return { sub: null };
4046
+ case Regexp.Op.CONCAT:
4047
+ case Regexp.Op.ALTERNATE: return {
4048
+ subs: [],
4049
+ changed: false
4050
+ };
4051
+ case Regexp.Op.REPEAT:
4052
+ if (re.min === 0 && re.max === 0) return { result: new Regexp(Regexp.Op.EMPTY_MATCH) };
4053
+ return { sub: null };
4054
+ default: return { result: re };
4055
+ }
4056
+ }
4057
+ static child(re, state, nsub, i) {
4058
+ if (state.subs === void 0) {
4059
+ state.sub = nsub;
4060
+ return;
4061
+ }
4062
+ if (nsub !== re.subs[i]) state.changed = true;
4063
+ if (re.op === Regexp.Op.CONCAT) {
4064
+ if (nsub.op === Regexp.Op.NO_MATCH) {
4065
+ state.result = new Regexp(Regexp.Op.NO_MATCH);
4066
+ return;
4067
+ }
4068
+ if (nsub.op === Regexp.Op.EMPTY_MATCH) {
4069
+ state.changed = true;
4070
+ return;
4071
+ }
4072
+ if (nsub.op === Regexp.Op.CONCAT) {
4073
+ state.changed = true;
4074
+ for (let j = 0; j < nsub.subs.length; j++) state.subs.push(nsub.subs[j]);
4075
+ return;
4076
+ }
4077
+ } else {
4078
+ if (nsub.op === Regexp.Op.NO_MATCH) {
4079
+ state.changed = true;
4080
+ return;
4081
+ }
4082
+ if (nsub.op === Regexp.Op.ALTERNATE) {
4083
+ state.changed = true;
4084
+ for (let j = 0; j < nsub.subs.length; j++) state.subs.push(nsub.subs[j]);
4085
+ return;
4086
+ }
4087
+ }
4088
+ state.subs.push(nsub);
4089
+ }
4090
+ static leave(re, state) {
3801
4091
  switch (re.op) {
3802
4092
  case Regexp.Op.PLB:
3803
4093
  case Regexp.Op.NLB:
3804
4094
  case Regexp.Op.CAPTURE: {
3805
- const sub = Simplify.simplify(re.subs[0]);
4095
+ const sub = state.sub;
3806
4096
  if (sub !== re.subs[0]) {
3807
4097
  const nre = Regexp.fromRegexp(re);
3808
- nre.runes = [];
4098
+ nre.runes = /* @__PURE__ */ new Int32Array(0);
3809
4099
  nre.subs = [sub];
3810
4100
  return nre;
3811
4101
  }
3812
4102
  return re;
3813
4103
  }
3814
4104
  case Regexp.Op.CONCAT:
3815
- case Regexp.Op.ALTERNATE: {
3816
- const newSubs = [];
3817
- let changed = false;
3818
- for (let i = 0; i < re.subs.length; i++) {
3819
- const sub = re.subs[i];
3820
- const nsub = Simplify.simplify(sub);
3821
- if (nsub !== sub) changed = true;
3822
- if (re.op === Regexp.Op.CONCAT) {
3823
- if (nsub.op === Regexp.Op.NO_MATCH) return new Regexp(Regexp.Op.NO_MATCH);
3824
- if (nsub.op === Regexp.Op.EMPTY_MATCH) {
3825
- changed = true;
3826
- continue;
3827
- }
3828
- if (nsub.op === Regexp.Op.CONCAT) {
3829
- changed = true;
3830
- for (let j = 0; j < nsub.subs.length; j++) newSubs.push(nsub.subs[j]);
3831
- continue;
3832
- }
3833
- } else if (re.op === Regexp.Op.ALTERNATE) {
3834
- if (nsub.op === Regexp.Op.NO_MATCH) {
3835
- changed = true;
3836
- continue;
3837
- }
3838
- if (nsub.op === Regexp.Op.ALTERNATE) {
3839
- changed = true;
3840
- for (let j = 0; j < nsub.subs.length; j++) newSubs.push(nsub.subs[j]);
3841
- continue;
3842
- }
3843
- }
3844
- newSubs.push(nsub);
3845
- }
3846
- if (changed) {
4105
+ case Regexp.Op.ALTERNATE:
4106
+ if (state.changed) {
4107
+ const newSubs = state.subs;
3847
4108
  if (newSubs.length === 0) return new Regexp(re.op === Regexp.Op.CONCAT ? Regexp.Op.EMPTY_MATCH : Regexp.Op.NO_MATCH);
3848
4109
  if (newSubs.length === 1) return newSubs[0];
3849
4110
  const nre = Regexp.fromRegexp(re);
3850
- nre.runes = [];
4111
+ nre.runes = /* @__PURE__ */ new Int32Array(0);
3851
4112
  nre.subs = newSubs;
3852
4113
  return nre;
3853
4114
  }
3854
4115
  return re;
3855
- }
3856
- case Regexp.Op.CHAR_CLASS:
3857
- if (re.runes === null) return re;
3858
- if (re.runes.length === 0) return new Regexp(Regexp.Op.NO_MATCH);
3859
- if (re.runes.length === 2 && re.runes[0] === 0 && re.runes[1] === Unicode.MAX_RUNE) return new Regexp(Regexp.Op.ANY_CHAR);
3860
- if (re.runes.length === 4 && re.runes[0] === 0 && re.runes[1] === Codepoint.CODES.get("\n") - 1 && re.runes[2] === Codepoint.CODES.get("\n") + 1 && re.runes[3] === Unicode.MAX_RUNE) return new Regexp(Regexp.Op.ANY_CHAR_NOT_NL);
3861
- return re;
3862
4116
  case Regexp.Op.STAR:
3863
4117
  case Regexp.Op.PLUS:
3864
- case Regexp.Op.QUEST: {
3865
- const sub = Simplify.simplify(re.subs[0]);
3866
- return Simplify.simplify1(re.op, re.flags, sub, re);
3867
- }
3868
- case Regexp.Op.REPEAT: {
3869
- if (re.min === 0 && re.max === 0) return new Regexp(Regexp.Op.EMPTY_MATCH);
3870
- const sub = Simplify.simplify(re.subs[0]);
3871
- if (re.max === -1) {
3872
- if (re.min === 0) return Simplify.simplify1(Regexp.Op.STAR, re.flags, sub, null);
3873
- if (re.min === 1) return Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null);
3874
- const nre = new Regexp(Regexp.Op.CONCAT);
3875
- const subs = [];
3876
- for (let i = 0; i < re.min - 1; i++) subs.push(sub);
3877
- subs.push(Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null));
3878
- nre.subs = subs.slice(0);
3879
- return Simplify.simplify(nre);
3880
- }
3881
- if (re.min === 1 && re.max === 1) return sub;
3882
- let prefixSubs = null;
3883
- if (re.min > 0) {
3884
- prefixSubs = [];
3885
- for (let i = 0; i < re.min; i++) prefixSubs.push(sub);
3886
- }
3887
- if (re.max > re.min) {
3888
- let suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, sub, null);
3889
- for (let i = re.min + 1; i < re.max; i++) {
3890
- const nre2 = new Regexp(Regexp.Op.CONCAT);
3891
- nre2.subs = [sub, suffix];
3892
- suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, nre2, null);
3893
- }
3894
- if (prefixSubs === null) return suffix;
3895
- prefixSubs.push(suffix);
3896
- }
3897
- if (prefixSubs !== null) {
3898
- const prefix = new Regexp(Regexp.Op.CONCAT);
3899
- prefix.subs = prefixSubs.slice(0);
3900
- return Simplify.simplify(prefix);
3901
- }
3902
- return new Regexp(Regexp.Op.NO_MATCH);
4118
+ case Regexp.Op.QUEST: return Simplify.simplify1(re.op, re.flags, state.sub, re);
4119
+ case Regexp.Op.REPEAT: return Simplify.simplifyRepeat(re, state.sub);
4120
+ default: return re;
4121
+ }
4122
+ }
4123
+ static simplifyRepeat(re, sub) {
4124
+ if (re.max === -1) {
4125
+ if (re.min === 0) return Simplify.simplify1(Regexp.Op.STAR, re.flags, sub, null);
4126
+ if (re.min === 1) return Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null);
4127
+ const subs = [];
4128
+ for (let i = 0; i < re.min - 1; i++) subs.push(sub);
4129
+ subs.push(Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null));
4130
+ return Simplify.concat(subs);
4131
+ }
4132
+ if (re.min === 1 && re.max === 1) return sub;
4133
+ let prefixSubs = null;
4134
+ if (re.min > 0) {
4135
+ prefixSubs = [];
4136
+ for (let i = 0; i < re.min; i++) prefixSubs.push(sub);
4137
+ }
4138
+ if (re.max > re.min) {
4139
+ let suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, sub, null);
4140
+ for (let i = re.min + 1; i < re.max; i++) suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, Simplify.concat([sub, suffix]), null);
4141
+ if (prefixSubs === null) return suffix;
4142
+ prefixSubs.push(suffix);
4143
+ }
4144
+ if (prefixSubs !== null) return Simplify.concat(prefixSubs);
4145
+ return new Regexp(Regexp.Op.NO_MATCH);
4146
+ }
4147
+ static concat(subs) {
4148
+ const newSubs = [];
4149
+ for (let i = 0; i < subs.length; i++) {
4150
+ const sub = subs[i];
4151
+ if (sub.op === Regexp.Op.NO_MATCH) return new Regexp(Regexp.Op.NO_MATCH);
4152
+ if (sub.op === Regexp.Op.EMPTY_MATCH) continue;
4153
+ if (sub.op === Regexp.Op.CONCAT) {
4154
+ for (let j = 0; j < sub.subs.length; j++) newSubs.push(sub.subs[j]);
4155
+ continue;
3903
4156
  }
4157
+ newSubs.push(sub);
3904
4158
  }
3905
- return re;
4159
+ if (newSubs.length === 0) return new Regexp(Regexp.Op.EMPTY_MATCH);
4160
+ if (newSubs.length === 1) return newSubs[0];
4161
+ const nre = new Regexp(Regexp.Op.CONCAT);
4162
+ nre.subs = newSubs;
4163
+ return nre;
3906
4164
  }
3907
4165
  static simplify1(op, flags, sub, re) {
3908
4166
  if (sub.op === Regexp.Op.EMPTY_MATCH) return sub;
@@ -4070,46 +4328,35 @@ var CharClass = class CharClass {
4070
4328
  result += "]";
4071
4329
  return result;
4072
4330
  }
4073
- static cmp(array, i, pivotFrom, pivotTo) {
4074
- const cmp = array[i] - pivotFrom;
4075
- return cmp !== 0 ? cmp : pivotTo - array[i + 1];
4076
- }
4077
- static qsortIntPair(array, left, right) {
4078
- const pivotIndex = ((left + right) / 2 | 0) & -2;
4079
- const pivotFrom = array[pivotIndex];
4080
- const pivotTo = array[pivotIndex + 1];
4081
- let i = left;
4082
- let j = right;
4083
- while (i <= j) {
4084
- while (i < right && CharClass.cmp(array, i, pivotFrom, pivotTo) < 0) i += 2;
4085
- while (j > left && CharClass.cmp(array, j, pivotFrom, pivotTo) > 0) j -= 2;
4086
- if (i <= j) {
4087
- if (i !== j) {
4088
- let temp = array[i];
4089
- array[i] = array[j];
4090
- array[j] = temp;
4091
- temp = array[i + 1];
4092
- array[i + 1] = array[j + 1];
4093
- array[j + 1] = temp;
4094
- }
4095
- i += 2;
4096
- j -= 2;
4097
- }
4331
+ static sortRanges(array, len) {
4332
+ const n = len >> 1;
4333
+ const order = new Array(n);
4334
+ for (let i = 0; i < n; i++) order[i] = i;
4335
+ order.sort((a, b) => array[2 * a] - array[2 * b] || array[2 * b + 1] - array[2 * a + 1]);
4336
+ const sorted = array.slice(0, len);
4337
+ for (let i = 0; i < n; i++) {
4338
+ array[2 * i] = sorted[2 * order[i]];
4339
+ array[2 * i + 1] = sorted[2 * order[i] + 1];
4098
4340
  }
4099
- if (left < j) CharClass.qsortIntPair(array, left, j);
4100
- if (i < right) CharClass.qsortIntPair(array, i, right);
4101
4341
  }
4102
4342
  constructor(r = Utils.emptyInts()) {
4103
4343
  this.r = r;
4104
4344
  this.len = r.length;
4105
4345
  }
4106
4346
  toArray() {
4107
- if (this.len === this.r.length) return this.r;
4108
- else return this.r.slice(0, this.len);
4347
+ if (this.r instanceof Int32Array) return this.len === this.r.length ? this.r : this.r.subarray(0, this.len);
4348
+ return new Int32Array(this.len === this.r.length ? this.r : this.r.slice(0, this.len));
4349
+ }
4350
+ toGrowableArray() {
4351
+ if (Array.isArray(this.r)) {
4352
+ this.r.length = this.len;
4353
+ return this.r;
4354
+ }
4355
+ return Array.from(this.r.subarray(0, this.len));
4109
4356
  }
4110
4357
  cleanClass() {
4111
4358
  if (this.len < 4) return this;
4112
- CharClass.qsortIntPair(this.r, 0, this.len - 2);
4359
+ CharClass.sortRanges(this.r, this.len);
4113
4360
  let w = 2;
4114
4361
  for (let i = 2; i < this.len; i += 2) {
4115
4362
  const lo = this.r[i];
@@ -4313,7 +4560,7 @@ var Parser = class Parser {
4313
4560
  static ERR_LARGE = "expression too large";
4314
4561
  static ERR_INVALID_CAPTURE_IN_LOOKBEHIND = "invalid capture in lookbehind";
4315
4562
  static MAX_HEIGHT = 1e3;
4316
- static MAX_SIZE = 3355443;
4563
+ static MAX_SIZE = 1398101;
4317
4564
  static MAX_RUNES = 33554432;
4318
4565
  static ANY_TABLE = new UnicodeRangeTable(new Uint32Array([
4319
4566
  0,
@@ -4455,15 +4702,17 @@ var Parser = class Parser {
4455
4702
  case Regexp.Op.ANY_CHAR_NOT_NL:
4456
4703
  if (Parser.matchRune(src, Codepoint.CODES.get("\n"))) dst.op = Regexp.Op.ANY_CHAR;
4457
4704
  break;
4458
- case Regexp.Op.CHAR_CLASS:
4459
- if (src.op === Regexp.Op.LITERAL) dst.runes = new CharClass(dst.runes).appendLiteral(src.runes[0], src.flags).toArray();
4460
- else dst.runes = new CharClass(dst.runes).appendClass(src.runes).toArray();
4705
+ case Regexp.Op.CHAR_CLASS: {
4706
+ const cc = new CharClass(Array.isArray(dst.runes) ? dst.runes : Array.from(dst.runes));
4707
+ if (src.op === Regexp.Op.LITERAL) cc.appendLiteral(src.runes[0], src.flags);
4708
+ else cc.appendClass(src.runes);
4709
+ dst.runes = cc.toGrowableArray();
4461
4710
  break;
4711
+ }
4462
4712
  case Regexp.Op.LITERAL:
4463
4713
  if (src.runes[0] === dst.runes[0] && src.flags === dst.flags) break;
4464
4714
  dst.op = Regexp.Op.CHAR_CLASS;
4465
- dst.runes = new CharClass().appendLiteral(dst.runes[0], dst.flags).appendLiteral(src.runes[0], src.flags).toArray();
4466
- break;
4715
+ dst.runes = new CharClass().appendLiteral(dst.runes[0], dst.flags).appendLiteral(src.runes[0], src.flags).toGrowableArray();
4467
4716
  }
4468
4717
  }
4469
4718
  static parseEscape(t) {
@@ -4520,9 +4769,7 @@ var Parser = class Parser {
4520
4769
  case Codepoint.CODES.get("r"): return Codepoint.CODES.get("\r");
4521
4770
  case Codepoint.CODES.get("t"): return Codepoint.CODES.get(" ");
4522
4771
  case Codepoint.CODES.get("v"): return Codepoint.CODES.get("\v");
4523
- default:
4524
- if (c <= Unicode.MAX_ASCII && !Utils.isalnum(c)) return c;
4525
- break;
4772
+ default: if (c <= Unicode.MAX_ASCII && !Utils.isalnum(c)) return c;
4526
4773
  }
4527
4774
  throw new RE2JSSyntaxException(Parser.ERR_INVALID_ESCAPE, t.from(startPos));
4528
4775
  }
@@ -4531,10 +4778,6 @@ var Parser = class Parser {
4531
4778
  if (t.lookingAt("\\")) return Parser.parseEscape(t);
4532
4779
  return t.pop();
4533
4780
  }
4534
- static concatRunes(x, y) {
4535
- for (let i = 0; i < y.length; i++) x.push(y[i]);
4536
- return x;
4537
- }
4538
4781
  static hasCapture(re) {
4539
4782
  if (re === null) return false;
4540
4783
  if (re.op === Regexp.Op.CAPTURE) return true;
@@ -4665,9 +4908,7 @@ var Parser = class Parser {
4665
4908
  const n = this.stack.length;
4666
4909
  let i = n;
4667
4910
  while (i > 0 && !Regexp.isPseudoOp(this.stack[i - 1].op)) i--;
4668
- const r = this.stack.slice(i, n);
4669
- this.stack = this.stack.slice(0, i);
4670
- return r;
4911
+ return this.stack.splice(i, n - i);
4671
4912
  }
4672
4913
  push(re) {
4673
4914
  this.numRunes += re.runes.length;
@@ -4692,7 +4933,8 @@ var Parser = class Parser {
4692
4933
  const re1 = this.stack[n - 1];
4693
4934
  const re2 = this.stack[n - 2];
4694
4935
  if (re1.op !== Regexp.Op.LITERAL || re2.op !== Regexp.Op.LITERAL || (re1.flags & RE2Flags.FOLD_CASE) !== (re2.flags & RE2Flags.FOLD_CASE)) return false;
4695
- re2.runes = Parser.concatRunes(re2.runes, re1.runes);
4936
+ if (!Array.isArray(re2.runes)) re2.runes = Array.from(re2.runes);
4937
+ for (let i = 0; i < re1.runes.length; i++) re2.runes.push(re1.runes[i]);
4696
4938
  if (r >= 0) {
4697
4939
  re1.runes = [r];
4698
4940
  re1.flags = flags;
@@ -4764,12 +5006,12 @@ var Parser = class Parser {
4764
5006
  }
4765
5007
  cleanAlt(re) {
4766
5008
  if (re.op === Regexp.Op.CHAR_CLASS) {
4767
- re.runes = new CharClass(re.runes).cleanClass().toArray();
5009
+ re.runes = new CharClass(Array.from(re.runes)).cleanClass().toArray();
4768
5010
  if (re.runes.length === 2 && re.runes[0] === 0 && re.runes[1] === Unicode.MAX_RUNE) {
4769
- re.runes = [];
5011
+ re.runes = /* @__PURE__ */ new Int32Array(0);
4770
5012
  re.op = Regexp.Op.ANY_CHAR;
4771
5013
  } else if (re.runes.length === 4 && re.runes[0] === 0 && re.runes[1] === Codepoint.CODES.get("\n") - 1 && re.runes[2] === Codepoint.CODES.get("\n") + 1 && re.runes[3] === Unicode.MAX_RUNE) {
4772
- re.runes = [];
5014
+ re.runes = /* @__PURE__ */ new Int32Array(0);
4773
5015
  re.op = Regexp.Op.ANY_CHAR_NOT_NL;
4774
5016
  }
4775
5017
  }
@@ -4929,9 +5171,7 @@ var Parser = class Parser {
4929
5171
  this.reuse(old);
4930
5172
  break;
4931
5173
  }
4932
- default:
4933
- re.subs = re.subs.slice(1, re.subs.length);
4934
- break;
5174
+ default: re.subs = re.subs.slice(1, re.subs.length);
4935
5175
  }
4936
5176
  }
4937
5177
  return re;
@@ -5030,9 +5270,7 @@ var Parser = class Parser {
5030
5270
  case Codepoint.CODES.get("+"):
5031
5271
  op = Regexp.Op.PLUS;
5032
5272
  break;
5033
- case Codepoint.CODES.get("?"):
5034
- op = Regexp.Op.QUEST;
5035
- break;
5273
+ case Codepoint.CODES.get("?"): op = Regexp.Op.QUEST;
5036
5274
  }
5037
5275
  this.repeat(op, min, max, repeatPos, t, lastRepeatPos);
5038
5276
  break;
@@ -5083,9 +5321,7 @@ var Parser = class Parser {
5083
5321
  case Codepoint.CODES.get("z"):
5084
5322
  this.op(Regexp.Op.END_TEXT);
5085
5323
  break bigswitch;
5086
- default:
5087
- t.rewindTo(savedPos);
5088
- break;
5324
+ default: t.rewindTo(savedPos);
5089
5325
  }
5090
5326
  else t.rewindTo(savedPos);
5091
5327
  const re = this.newRegexp(Regexp.Op.CHAR_CLASS);
@@ -5109,9 +5345,7 @@ var Parser = class Parser {
5109
5345
  this.literal(Parser.parseEscape(t));
5110
5346
  break;
5111
5347
  }
5112
- default:
5113
- this.literal(t.pop());
5114
- break;
5348
+ default: this.literal(t.pop());
5115
5349
  }
5116
5350
  lastRepeatPos = repeatPos;
5117
5351
  }
@@ -5309,7 +5543,7 @@ var Parser = class Parser {
5309
5543
  if ((this.flags & RE2Flags.FOLD_CASE) === 0 || fold === null) cc.appendTableWithSign(tab, sign);
5310
5544
  else {
5311
5545
  const tmp = new CharClass().appendTable(tab).appendTable(fold).cleanClass().toArray();
5312
- cc.appendClassWithSign(tmp, sign);
5546
+ cc.appendClassWithSign(Array.from(tmp), sign);
5313
5547
  }
5314
5548
  return true;
5315
5549
  }
@@ -5365,24 +5599,6 @@ var Parser = class Parser {
5365
5599
  };
5366
5600
  //#endregion
5367
5601
  //#region src/RE2.js
5368
- var AtomicReference = class {
5369
- constructor(initialValue) {
5370
- this.value = initialValue;
5371
- }
5372
- get() {
5373
- return this.value;
5374
- }
5375
- set(newValue) {
5376
- this.value = newValue;
5377
- }
5378
- compareAndSet(expect, update) {
5379
- if (this.value === expect) {
5380
- this.value = update;
5381
- return true;
5382
- }
5383
- return false;
5384
- }
5385
- };
5386
5602
  /**
5387
5603
  * An RE2 class instance is a compiled representation of an RE2 regular expression, independent of
5388
5604
  * the public Java-like Pattern/Matcher API.
@@ -5471,7 +5687,7 @@ var RE2 = class RE2 {
5471
5687
  this.prefixUTF8 = null;
5472
5688
  this.prefixComplete = false;
5473
5689
  this.prefixRune = 0;
5474
- this.pooled = new AtomicReference();
5690
+ this.machinePool = [];
5475
5691
  this.dfa = new DFA(this.prog);
5476
5692
  this.onepass = OnePass.compile(this.prog);
5477
5693
  this.prefilter = null;
@@ -5505,7 +5721,10 @@ var RE2 = class RE2 {
5505
5721
  }
5506
5722
  return [];
5507
5723
  }
5508
- executeEngine(input, pos, anchor, ncap) {
5724
+ createLookbehindState() {
5725
+ return this.prog.numLb > 0 ? new LookbehindState(this.prog) : null;
5726
+ }
5727
+ executeEngine(input, pos, anchor, ncap, lbState = null) {
5509
5728
  if (this.prefixComplete && (ncap === 0 || this.numSubexp === 0)) return this.matchPrefixComplete(input, pos, anchor, ncap);
5510
5729
  if (this.prefilter !== null && anchor === RE2Flags.UNANCHORED) {
5511
5730
  if (!this.prefilter.eval(input, pos)) return null;
@@ -5513,14 +5732,14 @@ var RE2 = class RE2 {
5513
5732
  if (this.onepass !== null) return OnePass.execute(this, input, pos, anchor, ncap);
5514
5733
  if (ncap > 0) {
5515
5734
  if (this.prog.numLb === 0 && input.endPos() <= Backtracker.maxBitStateLen(this.prog)) return Backtracker.execute(this, input, pos, anchor, ncap);
5516
- return this.doExecuteNFA(input, pos, anchor, ncap);
5735
+ return this.doExecuteNFA(input, pos, anchor, ncap, lbState);
5517
5736
  }
5518
5737
  if (this.prog.numLb === 0) {
5519
5738
  const dfaResult = this.dfa.match(input, pos, anchor);
5520
5739
  if (dfaResult !== null) return dfaResult ? [] : null;
5521
5740
  if (input.endPos() <= Backtracker.maxBitStateLen(this.prog)) return Backtracker.execute(this, input, pos, anchor, ncap);
5522
5741
  }
5523
- return this.doExecuteNFA(input, pos, anchor, ncap);
5742
+ return this.doExecuteNFA(input, pos, anchor, ncap, lbState);
5524
5743
  }
5525
5744
  /**
5526
5745
  * Returns the number of parenthesized subexpressions in this regular expression.
@@ -5535,42 +5754,23 @@ var RE2 = class RE2 {
5535
5754
  return this.prog.numInst();
5536
5755
  }
5537
5756
  get() {
5538
- let head;
5539
- do
5540
- head = this.pooled.get();
5541
- while (head && !this.pooled.compareAndSet(head, head.next));
5542
- return head;
5757
+ return this.machinePool.length > 0 ? this.machinePool.pop() : null;
5543
5758
  }
5544
5759
  reset() {
5545
- this.pooled.set(null);
5546
- }
5547
- put(m, isNew) {
5548
- let head = this.pooled.get();
5549
- do {
5550
- head = this.pooled.get();
5551
- if (!isNew && head) {
5552
- m = Machine.fromMachine(m);
5553
- isNew = true;
5554
- }
5555
- if (m.next !== head) m.next = head;
5556
- } while (!this.pooled.compareAndSet(head, m));
5760
+ this.machinePool.length = 0;
5761
+ }
5762
+ put(m) {
5763
+ this.machinePool.push(m);
5557
5764
  }
5558
5765
  toString() {
5559
5766
  return this.expr;
5560
5767
  }
5561
- doExecuteNFA(input, pos, anchor, ncap) {
5768
+ doExecuteNFA(input, pos, anchor, ncap, lbState = null) {
5562
5769
  let m = this.get();
5563
- let isNew = false;
5564
- if (!m) {
5565
- m = Machine.fromRE2(this);
5566
- isNew = true;
5567
- } else if (m.next !== null) {
5568
- m = Machine.fromMachine(m);
5569
- isNew = true;
5570
- }
5770
+ if (!m) m = Machine.fromRE2(this);
5571
5771
  m.init(ncap);
5572
- const cap = m.match(input, pos, anchor) ? m.submatches() : null;
5573
- this.put(m, isNew);
5772
+ const cap = m.match(input, pos, anchor, lbState) ? m.submatches() : null;
5773
+ this.put(m);
5574
5774
  return cap;
5575
5775
  }
5576
5776
  match(s) {
@@ -5588,17 +5788,21 @@ var RE2 = class RE2 {
5588
5788
  * @param anchor the anchoring flag (UNANCHORED, ANCHOR_START, ANCHOR_BOTH)
5589
5789
  * @param group the array to fill with submatch positions
5590
5790
  * @param ngroup the number of array pairs to fill in
5791
+ * @param lbState optional state carried between successive matches over the same input (see
5792
+ * createLookbehindState); omit it to evaluate lookbehinds from the beginning of the input
5591
5793
  * @returns true if a match was found
5592
5794
  */
5593
- matchWithGroup(input, start, end, anchor, ngroup) {
5594
- if (!(input instanceof MatcherInputBase)) if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
5595
- else input = MatcherInput.utf16(input);
5596
- return this.matchMachineInput(input, start, end, anchor, ngroup);
5795
+ matchWithGroup(input, start, end, anchor, ngroup, lbState = null) {
5796
+ if (!(input instanceof MatcherInputBase)) {
5797
+ if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
5798
+ else input = MatcherInput.utf16(input);
5799
+ }
5800
+ return this.matchMachineInput(input, start, end, anchor, ngroup, lbState);
5597
5801
  }
5598
- matchMachineInput(input, start, end, anchor, ngroup) {
5802
+ matchMachineInput(input, start, end, anchor, ngroup, lbState = null) {
5599
5803
  if (start > end) return [false, null];
5600
5804
  const machineInput = input.isUTF16Encoding() ? MachineInput.fromUTF16(input.asCharSequence(), 0, end) : MachineInput.fromUTF8(input.asBytes(), 0, end);
5601
- const groupMatch = this.executeEngine(machineInput, start, anchor, 2 * ngroup);
5805
+ const groupMatch = this.executeEngine(machineInput, start, anchor, 2 * ngroup, lbState);
5602
5806
  if (groupMatch === null) return [false, null];
5603
5807
  return [true, groupMatch];
5604
5808
  }
@@ -5635,9 +5839,10 @@ var RE2 = class RE2 {
5635
5839
  let searchPos = 0;
5636
5840
  let out = "";
5637
5841
  const input = MachineInput.fromUTF16(src);
5842
+ const lbState = this.createLookbehindState();
5638
5843
  let numReplaces = 0;
5639
5844
  while (searchPos <= src.length) {
5640
- const a = this.executeEngine(input, searchPos, RE2Flags.UNANCHORED, 2);
5845
+ const a = this.executeEngine(input, searchPos, RE2Flags.UNANCHORED, 2, lbState);
5641
5846
  if (a === null || a.length === 0) break;
5642
5847
  out += src.substring(lastMatchEnd, a[0]);
5643
5848
  if (a[1] > lastMatchEnd || a[0] === 0) {
@@ -5671,8 +5876,9 @@ var RE2 = class RE2 {
5671
5876
  let pos = 0;
5672
5877
  let i = 0;
5673
5878
  let prevMatchEnd = -1;
5879
+ const lbState = this.createLookbehindState();
5674
5880
  while (i < n && pos <= end) {
5675
- const matches = this.executeEngine(input, pos, RE2Flags.UNANCHORED, this.prog.numCap);
5881
+ const matches = this.executeEngine(input, pos, RE2Flags.UNANCHORED, this.prog.numCap, lbState);
5676
5882
  if (matches === null || matches.length === 0) break;
5677
5883
  let accept = true;
5678
5884
  if (matches[1] === pos) {
@@ -5942,7 +6148,9 @@ var RE2Set = class RE2Set {
5942
6148
  */
5943
6149
  compile() {
5944
6150
  if (this.prog) return;
5945
- this.prog = Compiler.compileSet(this.regexps);
6151
+ const prog = Compiler.compileSet(this.regexps);
6152
+ if (prog.numInst() > Parser.MAX_SIZE) throw new RE2JSCompileException(`${Parser.ERR_LARGE}: the set compiles to ${prog.numInst()} instructions, more than ${Parser.MAX_SIZE}`);
6153
+ this.prog = prog;
5946
6154
  this.dfa = new DFA(this.prog, this.maxMem);
5947
6155
  this.dummyRe2 = {
5948
6156
  prog: this.prog,
@@ -6436,7 +6644,7 @@ var RE2JS = class RE2JS {
6436
6644
  }
6437
6645
  result.push(m.substring(last, m.inputLength()));
6438
6646
  }
6439
- if (limit !== 0 || result.length === 0) result.push(m.substring(last, m.inputLength()));
6647
+ if (limit !== 0 || result.length === 0 && !(last === m.inputLength() && last > 0)) result.push(m.substring(last, m.inputLength()));
6440
6648
  return result;
6441
6649
  }
6442
6650
  /**