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