re2js 2.8.6 → 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.
@@ -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.6
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
  (function(global, factory) {
@@ -503,6 +503,10 @@
503
503
  */
504
504
  var Unicode = class Unicode {
505
505
  static MAX_RUNE = 1114111;
506
+ static RUNE_ERROR = 65533;
507
+ static isUnsearchable(r) {
508
+ return r === Unicode.RUNE_ERROR || r >= Unicode.MIN_HIGH_SURROGATE && r <= Unicode.MAX_LOW_SURROGATE;
509
+ }
506
510
  static MAX_ASCII = 127;
507
511
  static MAX_LATIN1 = 255;
508
512
  static MAX_BMP = 65535;
@@ -605,7 +609,7 @@
605
609
  return [];
606
610
  }
607
611
  static isByteArray(input) {
608
- return Array.isArray(input) || input instanceof Uint8Array;
612
+ return Array.isArray(input) || Object.prototype.toString.call(input) === "[object Uint8Array]";
609
613
  }
610
614
  static isalnum(c) {
611
615
  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");
@@ -664,7 +668,7 @@
664
668
  runes.push(cp);
665
669
  i += cp > Unicode.MAX_BMP ? 2 : 1;
666
670
  }
667
- return runes;
671
+ return new Int32Array(runes);
668
672
  }
669
673
  static runeToString(r) {
670
674
  return String.fromCodePoint(r);
@@ -791,7 +795,7 @@
791
795
  asCharSequence() {
792
796
  throw Error("not implemented");
793
797
  }
794
- /** @returns {Uint8Array|number[]} */
798
+ /** @returns {Uint8Array|number[]|null} */
795
799
  asBytes() {
796
800
  throw Error("not implemented");
797
801
  }
@@ -922,7 +926,8 @@
922
926
  return 0;
923
927
  }
924
928
  };
925
- var MachineUTF8Input = class extends MachineInputBase {
929
+ var MachineUTF8Input = class MachineUTF8Input extends MachineInputBase {
930
+ static RUNE_ERROR_STEP = Unicode.RUNE_ERROR << 3 | 1;
926
931
  constructor(bytes, start = 0, end = bytes.length) {
927
932
  super();
928
933
  this.bytes = bytes;
@@ -936,33 +941,46 @@
936
941
  return idx !== -1 && idx <= this.end - target.length;
937
942
  }
938
943
  hasAnyString(prefilter, pos) {
939
- if (!prefilter.ac8) return false;
940
- return prefilter.ac8.searchUTF8(this.bytes, this.start + pos, this.end);
944
+ return prefilter.automaton8().searchUTF8(this.bytes, this.start + pos, this.end);
941
945
  }
942
946
  step(pos) {
943
947
  pos += this.start;
944
948
  if (pos >= this.end) return MachineInputBase.EOF();
945
949
  const c = this.bytes[pos] & 255;
946
950
  if (c < 128) return c << 3 | 1;
947
- else if (c >= 194 && c <= 223 && pos + 1 < this.end) {
948
- const c1 = this.bytes[pos + 1] & 255;
949
- if ((c1 & 192) !== 128) return c << 3 | 1;
950
- return ((c & 31) << 6 | c1 & 63) << 3 | 2;
951
- } else if (c >= 224 && c <= 239 && pos + 2 < this.end) {
952
- const c1 = this.bytes[pos + 1] & 255;
953
- if ((c1 & 192) !== 128) return c << 3 | 1;
954
- const c2 = this.bytes[pos + 2] & 255;
955
- if ((c2 & 192) !== 128) return c << 3 | 1;
956
- return ((c & 15) << 12 | (c1 & 63) << 6 | c2 & 63) << 3 | 3;
957
- } else if (c >= 240 && c <= 244 && pos + 3 < this.end) {
958
- const c1 = this.bytes[pos + 1] & 255;
959
- if ((c1 & 192) !== 128) return c << 3 | 1;
951
+ let size;
952
+ let rune;
953
+ let lo = 128;
954
+ let hi = 191;
955
+ if (c >= 194 && c <= 223) {
956
+ size = 2;
957
+ rune = c & 31;
958
+ } else if (c >= 224 && c <= 239) {
959
+ size = 3;
960
+ rune = c & 15;
961
+ if (c === 224) lo = 160;
962
+ else if (c === 237) hi = 159;
963
+ } else if (c >= 240 && c <= 244) {
964
+ size = 4;
965
+ rune = c & 7;
966
+ if (c === 240) lo = 144;
967
+ else if (c === 244) hi = 143;
968
+ } else return MachineUTF8Input.RUNE_ERROR_STEP;
969
+ if (pos + size > this.end) return MachineUTF8Input.RUNE_ERROR_STEP;
970
+ const c1 = this.bytes[pos + 1] & 255;
971
+ if (c1 < lo || c1 > hi) return MachineUTF8Input.RUNE_ERROR_STEP;
972
+ rune = rune << 6 | c1 & 63;
973
+ if (size > 2) {
960
974
  const c2 = this.bytes[pos + 2] & 255;
961
- if ((c2 & 192) !== 128) return c << 3 | 1;
962
- const c3 = this.bytes[pos + 3] & 255;
963
- if ((c3 & 192) !== 128) return c << 3 | 1;
964
- return ((c & 7) << 18 | (c1 & 63) << 12 | (c2 & 63) << 6 | c3 & 63) << 3 | 4;
965
- } else return c << 3 | 1;
975
+ if ((c2 & 192) !== 128) return MachineUTF8Input.RUNE_ERROR_STEP;
976
+ rune = rune << 6 | c2 & 63;
977
+ if (size > 3) {
978
+ const c3 = this.bytes[pos + 3] & 255;
979
+ if ((c3 & 192) !== 128) return MachineUTF8Input.RUNE_ERROR_STEP;
980
+ rune = rune << 6 | c3 & 63;
981
+ }
982
+ }
983
+ return rune << 3 | size;
966
984
  }
967
985
  index(re2, pos) {
968
986
  pos += this.start;
@@ -1027,8 +1045,7 @@
1027
1045
  return idx !== -1 && idx <= this.end - prefilter.str.length;
1028
1046
  }
1029
1047
  hasAnyString(prefilter, pos) {
1030
- if (!prefilter.ac16) return false;
1031
- return prefilter.ac16.searchUTF16(this.charSequence, this.start + pos, this.end);
1048
+ return prefilter.automaton16().searchUTF16(this.charSequence, this.start + pos, this.end);
1032
1049
  }
1033
1050
  step(pos) {
1034
1051
  pos += this.start;
@@ -1221,6 +1238,8 @@
1221
1238
  this.namedGroups = re2.namedGroups;
1222
1239
  /** @type {number} */
1223
1240
  this.numberOfInstructions = re2.numberOfInstructions();
1241
+ /** @type {import('./Machine.js').LookbehindState|null} */
1242
+ this.lookbehindState = null;
1224
1243
  if (input instanceof MatcherInputBase) this.resetMatcherInput(input);
1225
1244
  else if (Utils.isByteArray(input)) this.resetMatcherInput(MatcherInput.utf8(input));
1226
1245
  else this.resetMatcherInput(MatcherInput.utf16(input));
@@ -1254,9 +1273,13 @@
1254
1273
  */
1255
1274
  resetMatcherInput(input) {
1256
1275
  if (input === null) throw new Error("input is null");
1257
- if (!(input instanceof MatcherInputBase)) if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
1258
- else input = MatcherInput.utf16(input);
1276
+ if (!(input instanceof MatcherInputBase)) {
1277
+ if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
1278
+ else input = MatcherInput.utf16(input);
1279
+ }
1259
1280
  this.matcherInput = input;
1281
+ if (this.lookbehindState !== null) this.lookbehindState.reset();
1282
+ else this.lookbehindState = this.patternInput.re2().createLookbehindState();
1260
1283
  this.reset();
1261
1284
  return this;
1262
1285
  }
@@ -1344,11 +1367,11 @@
1344
1367
  * @private
1345
1368
  */
1346
1369
  loadGroup(group) {
1347
- if (group < 0 || group > this.patternGroupCount) throw new RE2JSGroupException(`Group index out of bounds: ${group}`);
1370
+ if (!Number.isInteger(group) || group < 0 || group > this.patternGroupCount) throw new RE2JSGroupException(`Group index out of bounds: ${group}`);
1348
1371
  if (!this.hasMatch) throw new RE2JSGroupException("perhaps no match attempted");
1349
1372
  if (group === 0 || this.hasGroups) return;
1350
1373
  const end = this.matcherInputLength;
1351
- const res = this.patternInput.re2().matchMachineInput(this.matcherInput, this.groups[0], end, this.anchorFlag, 1 + this.patternGroupCount);
1374
+ const res = this.patternInput.re2().matchMachineInput(this.matcherInput, this.groups[0], end, this.anchorFlag, 1 + this.patternGroupCount, this.lookbehindState);
1352
1375
  if (!res[0]) throw new RE2JSGroupException("inconsistency in matching group data");
1353
1376
  this.groups = res[1];
1354
1377
  this.hasGroups = true;
@@ -1381,7 +1404,7 @@
1381
1404
  */
1382
1405
  find(start = null) {
1383
1406
  if (start !== null) {
1384
- if (start < 0 || start > this.matcherInputLength) throw new RE2JSGroupException(`start index out of bounds: ${start}`);
1407
+ if (!Number.isInteger(start) || start < 0 || start > this.matcherInputLength) throw new RE2JSGroupException(`start index out of bounds: ${start}`);
1385
1408
  this.reset();
1386
1409
  return this.genMatch(start, 0);
1387
1410
  }
@@ -1404,7 +1427,7 @@
1404
1427
  * @private
1405
1428
  */
1406
1429
  genMatch(startByte, anchor) {
1407
- const res = this.patternInput.re2().matchMachineInput(this.matcherInput, startByte, this.matcherInputLength, anchor, 1);
1430
+ const res = this.patternInput.re2().matchMachineInput(this.matcherInput, startByte, this.matcherInputLength, anchor, 1, this.lookbehindState);
1408
1431
  if (!res[0]) {
1409
1432
  this.hasMatch = false;
1410
1433
  return false;
@@ -1733,7 +1756,8 @@
1733
1756
  this.op = op;
1734
1757
  this.out = 0;
1735
1758
  this.arg = 0;
1736
- this.runes = [];
1759
+ /** @type {Int32Array} */
1760
+ this.runes = /* @__PURE__ */ new Int32Array(0);
1737
1761
  /** @type {Uint32Array | number[] | null} */
1738
1762
  this.next = null;
1739
1763
  }
@@ -1822,19 +1846,27 @@
1822
1846
  };
1823
1847
  //#endregion
1824
1848
  //#region src/Machine.js
1825
- var Queue = class {
1849
+ var Queue = class Queue {
1826
1850
  constructor(numInst) {
1827
1851
  this.sparse = new Int32Array(numInst);
1828
1852
  this.densePcs = new Int32Array(numInst);
1829
1853
  this.denseCaps = null;
1830
1854
  this.size = 0;
1831
1855
  this.ncap = 0;
1856
+ this.mainThreads = 0;
1832
1857
  }
1858
+ static INITIAL_THREADS = 32;
1833
1859
  init(ncap) {
1834
1860
  this.ncap = ncap;
1835
- const needed = this.densePcs.length * ncap;
1861
+ const needed = Math.min(this.densePcs.length, Queue.INITIAL_THREADS) * ncap;
1836
1862
  if (!this.denseCaps || this.denseCaps.length < needed) this.denseCaps = new Int32Array(needed);
1837
1863
  }
1864
+ growCaps(needed) {
1865
+ const limit = this.densePcs.length * this.ncap;
1866
+ const grown = new Int32Array(Math.min(limit, Math.max(needed, 2 * this.denseCaps.length)));
1867
+ grown.set(this.denseCaps);
1868
+ this.denseCaps = grown;
1869
+ }
1838
1870
  contains(pc) {
1839
1871
  const j = this.sparse[pc];
1840
1872
  return j < this.size && this.densePcs[j] === pc;
@@ -1850,6 +1882,7 @@
1850
1882
  }
1851
1883
  clear() {
1852
1884
  this.size = 0;
1885
+ this.mainThreads = 0;
1853
1886
  }
1854
1887
  toString() {
1855
1888
  let out = "{";
@@ -1861,6 +1894,18 @@
1861
1894
  return out;
1862
1895
  }
1863
1896
  };
1897
+ var LookbehindState = class {
1898
+ constructor(prog) {
1899
+ this.pos = -1;
1900
+ this.pcs = new Int32Array(prog.numInst());
1901
+ this.size = 0;
1902
+ this.lbTable = new Int32Array(prog.numLb + 1);
1903
+ }
1904
+ reset() {
1905
+ this.pos = -1;
1906
+ this.size = 0;
1907
+ }
1908
+ };
1864
1909
  var Machine = class Machine {
1865
1910
  static fromRE2(re2) {
1866
1911
  const m = new Machine();
@@ -1868,19 +1913,18 @@
1868
1913
  m.re2 = re2;
1869
1914
  m.q0 = new Queue(m.prog.numInst());
1870
1915
  m.q1 = new Queue(m.prog.numInst());
1916
+ m.addStack = new Int32Array(2 * (m.prog.numInst() + 1));
1871
1917
  m.matched = false;
1872
1918
  m.matchcap = new Int32Array(m.prog.numCap < 2 ? 2 : m.prog.numCap);
1873
1919
  m.ncap = 0;
1874
1920
  return m;
1875
1921
  }
1876
- static fromMachine(machine) {
1877
- return Machine.fromRE2(machine.re2);
1878
- }
1879
1922
  constructor() {
1880
1923
  this.prog = null;
1881
1924
  this.re2 = null;
1882
1925
  this.q0 = null;
1883
1926
  this.q1 = null;
1927
+ this.addStack = null;
1884
1928
  this.matched = false;
1885
1929
  this.matchcap = null;
1886
1930
  this.ncap = 0;
@@ -1901,14 +1945,23 @@
1901
1945
  if (this.ncap === 0) return Utils.emptyInts();
1902
1946
  return Utils.toArray(this.matchcap.subarray(0, this.ncap));
1903
1947
  }
1904
- match(input, pos, anchor) {
1948
+ match(input, pos, anchor, lbState = null) {
1905
1949
  const startCond = this.re2.cond;
1906
1950
  if (startCond === Utils.EMPTY_ALL) return false;
1907
1951
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return false;
1908
1952
  this.matched = false;
1909
1953
  this.matchcap.fill(-1);
1910
- let currentPos = this.prog.numLb > 0 ? 0 : pos;
1911
- let matchStartPos = pos;
1954
+ const hasLookbehinds = this.prog.numLb > 0;
1955
+ const matchStartPos = pos;
1956
+ let currentPos = pos;
1957
+ let resumeLookbehinds = false;
1958
+ if (hasLookbehinds) {
1959
+ if (lbState !== null && lbState.pos >= 0 && lbState.pos <= pos) {
1960
+ currentPos = lbState.pos;
1961
+ resumeLookbehinds = true;
1962
+ } else currentPos = 0;
1963
+ }
1964
+ let saveLookbehinds = hasLookbehinds && lbState !== null;
1912
1965
  let runq = this.q0;
1913
1966
  let nextq = this.q1;
1914
1967
  let r = input.step(currentPos);
@@ -1924,12 +1977,16 @@
1924
1977
  let flag;
1925
1978
  if (currentPos === 0) flag = Utils.emptyOpContext(-1, rune);
1926
1979
  else flag = input.context(currentPos);
1980
+ if (hasLookbehinds) {
1981
+ if (resumeLookbehinds) this.restoreLookbehinds(lbState, runq, flag);
1982
+ else this.spawnLookbehinds(runq, currentPos, flag);
1983
+ }
1927
1984
  while (true) {
1928
- if (runq.isEmpty()) {
1985
+ if (runq.mainThreads === 0) {
1929
1986
  if ((startCond & Utils.EMPTY_BEGIN_TEXT) !== 0 && currentPos !== 0) break;
1930
1987
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && currentPos !== 0) break;
1931
1988
  if (this.matched) break;
1932
- if (this.prog.numLb === 0 && !(this.re2.prefix.length === 0) && rune1 !== this.re2.prefixRune && input.canCheckPrefix()) {
1989
+ if (this.prog.numLb === 0 && runq.isEmpty() && !(this.re2.prefix.length === 0) && rune1 !== this.re2.prefixRune && input.canCheckPrefix()) {
1933
1990
  const advance = input.index(this.re2, currentPos);
1934
1991
  if (advance < 0) break;
1935
1992
  currentPos += advance;
@@ -1942,9 +1999,12 @@
1942
1999
  flag = input.context(currentPos);
1943
2000
  }
1944
2001
  }
1945
- 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, 0, flag);
1946
- if (!this.matched && (currentPos === 0 || anchor === RE2Flags.UNANCHORED)) {
1947
- if (currentPos >= matchStartPos) {
2002
+ if (currentPos >= matchStartPos) {
2003
+ if (saveLookbehinds) {
2004
+ this.saveLookbehinds(lbState, runq, currentPos);
2005
+ saveLookbehinds = false;
2006
+ }
2007
+ if (!this.matched && (currentPos === 0 || anchor === RE2Flags.UNANCHORED)) {
1948
2008
  if (this.ncap > 0) this.matchcap[0] = currentPos;
1949
2009
  this.add(runq, this.prog.start, currentPos, this.matchcap, 0, flag);
1950
2010
  }
@@ -1966,6 +2026,7 @@
1966
2026
  runq = nextq;
1967
2027
  nextq = tmpq;
1968
2028
  }
2029
+ runq.clear();
1969
2030
  nextq.clear();
1970
2031
  return this.matched;
1971
2032
  }
@@ -1989,12 +2050,12 @@
1989
2050
  }
1990
2051
  let flag = currentPos === 0 ? Utils.emptyOpContext(-1, rune) : input.context(currentPos);
1991
2052
  const matches = /* @__PURE__ */ new Set();
2053
+ if (this.prog.numLb > 0) this.spawnLookbehinds(runq, currentPos, flag);
1992
2054
  while (true) {
1993
- if (runq.isEmpty()) {
2055
+ if (runq.mainThreads === 0) {
1994
2056
  if ((startCond & Utils.EMPTY_BEGIN_TEXT) !== 0 && currentPos !== 0) break;
1995
2057
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && currentPos !== 0) break;
1996
2058
  }
1997
- 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, 0, flag);
1998
2059
  if (currentPos === 0 || anchor === RE2Flags.UNANCHORED) {
1999
2060
  if (currentPos >= matchStartPos) this.add(runq, this.prog.start, currentPos, this.matchcap, 0, flag);
2000
2061
  }
@@ -2079,65 +2140,85 @@
2079
2140
  }
2080
2141
  runq.clear();
2081
2142
  }
2143
+ spawnLookbehinds(q, pos, flag) {
2144
+ for (let i = 0; i < this.prog.lbStarts.length; i++) this.add(q, this.prog.lbStarts[i], pos, this.matchcap, 0, flag);
2145
+ }
2146
+ restoreLookbehinds(lbState, q, flag) {
2147
+ this.lbTable.set(lbState.lbTable);
2148
+ for (let j = 0; j < lbState.size; j++) this.add(q, lbState.pcs[j], lbState.pos, this.matchcap, 0, flag);
2149
+ }
2150
+ saveLookbehinds(lbState, q, pos) {
2151
+ let size = 0;
2152
+ for (let j = 0; j < q.size; j++) {
2153
+ const pc = q.densePcs[j];
2154
+ if (this.prog.lbInst[pc] === 1 && Inst.isRuneOp(this.prog.inst[pc].op)) lbState.pcs[size++] = pc;
2155
+ }
2156
+ lbState.size = size;
2157
+ lbState.pos = pos;
2158
+ lbState.lbTable.set(this.lbTable);
2159
+ }
2082
2160
  add(q, pc, pos, capArray, capOffset, cond) {
2083
- while (true) {
2084
- if (pc === 0) return;
2085
- if (q.contains(pc)) return;
2086
- const d = q.add(pc);
2087
- const inst = this.prog.inst[pc];
2088
- switch (inst.op) {
2089
- case Inst.FAIL: return;
2090
- case Inst.ALT:
2091
- case Inst.ALT_MATCH:
2092
- this.add(q, inst.out, pos, capArray, capOffset, cond);
2093
- pc = inst.arg;
2094
- continue;
2095
- case Inst.EMPTY_WIDTH:
2096
- if ((inst.arg & ~cond) === 0) {
2161
+ const stk = this.addStack;
2162
+ let nstk = 0;
2163
+ for (;;) {
2164
+ while (pc !== 0 && !q.contains(pc)) {
2165
+ const d = q.add(pc);
2166
+ const inst = this.prog.inst[pc];
2167
+ switch (inst.op) {
2168
+ case Inst.FAIL:
2169
+ pc = 0;
2170
+ break;
2171
+ case Inst.ALT:
2172
+ case Inst.ALT_MATCH:
2173
+ stk[nstk++] = inst.arg;
2174
+ stk[nstk++] = 0;
2097
2175
  pc = inst.out;
2098
- continue;
2099
- }
2100
- return;
2101
- case Inst.NOP:
2102
- pc = inst.out;
2103
- continue;
2104
- case Inst.CAPTURE: if (inst.arg < this.ncap) {
2105
- const opos = capArray[capOffset + inst.arg];
2106
- capArray[capOffset + inst.arg] = pos;
2107
- this.add(q, inst.out, pos, capArray, capOffset, cond);
2108
- capArray[capOffset + inst.arg] = opos;
2109
- return;
2110
- } else {
2111
- pc = inst.out;
2112
- continue;
2113
- }
2114
- case Inst.LB_WRITE:
2115
- this.lbTable[Math.abs(inst.arg)] = pos;
2116
- pc = inst.out;
2117
- continue;
2118
- case Inst.LB_CHECK:
2119
- if (inst.arg > 0) {
2120
- if (this.lbTable[inst.arg] === pos) {
2121
- pc = inst.out;
2122
- continue;
2176
+ break;
2177
+ case Inst.EMPTY_WIDTH:
2178
+ pc = (inst.arg & ~cond) === 0 ? inst.out : 0;
2179
+ break;
2180
+ case Inst.NOP:
2181
+ pc = inst.out;
2182
+ break;
2183
+ case Inst.CAPTURE:
2184
+ if (inst.arg < this.ncap) {
2185
+ stk[nstk++] = -inst.arg - 1;
2186
+ stk[nstk++] = capArray[capOffset + inst.arg];
2187
+ capArray[capOffset + inst.arg] = pos;
2123
2188
  }
2124
- } else if (this.lbTable[-inst.arg] !== pos) {
2125
2189
  pc = inst.out;
2126
- continue;
2127
- }
2128
- return;
2129
- case Inst.MATCH:
2130
- case Inst.RUNE:
2131
- case Inst.RUNE1:
2132
- case Inst.RUNE_ANY:
2133
- case Inst.RUNE_ANY_NOT_NL:
2134
- if (this.ncap > 0) {
2135
- const destOffset = d * this.ncap;
2136
- for (let c = 0; c < this.ncap; c++) q.denseCaps[destOffset + c] = capArray[capOffset + c];
2137
- }
2138
- return;
2139
- default: throw new RE2JSInternalException("unhandled");
2190
+ break;
2191
+ case Inst.LB_WRITE:
2192
+ this.lbTable[Math.abs(inst.arg)] = pos;
2193
+ pc = inst.out;
2194
+ break;
2195
+ case Inst.LB_CHECK:
2196
+ if (inst.arg > 0) pc = this.lbTable[inst.arg] === pos ? inst.out : 0;
2197
+ else pc = this.lbTable[-inst.arg] !== pos ? inst.out : 0;
2198
+ break;
2199
+ case Inst.MATCH:
2200
+ case Inst.RUNE:
2201
+ case Inst.RUNE1:
2202
+ case Inst.RUNE_ANY:
2203
+ case Inst.RUNE_ANY_NOT_NL:
2204
+ if (this.prog.lbInst[pc] === 0) q.mainThreads++;
2205
+ if (this.ncap > 0) {
2206
+ const destOffset = d * this.ncap;
2207
+ if (destOffset + this.ncap > q.denseCaps.length) q.growCaps(destOffset + this.ncap);
2208
+ for (let c = 0; c < this.ncap; c++) q.denseCaps[destOffset + c] = capArray[capOffset + c];
2209
+ }
2210
+ pc = 0;
2211
+ break;
2212
+ default: throw new RE2JSInternalException("unhandled");
2213
+ }
2140
2214
  }
2215
+ if (nstk === 0) return;
2216
+ nstk -= 2;
2217
+ const entry = stk[nstk];
2218
+ if (entry < 0) {
2219
+ capArray[capOffset - entry - 1] = stk[nstk + 1];
2220
+ pc = 0;
2221
+ } else pc = entry;
2141
2222
  }
2142
2223
  }
2143
2224
  };
@@ -2157,35 +2238,85 @@
2157
2238
  return true;
2158
2239
  };
2159
2240
  var DFAState = class {
2160
- constructor(nfaStates, isMatch, matchIDs = []) {
2241
+ constructor(nfaStates, isMatch, matchIDs) {
2161
2242
  this.nfaStates = nfaStates;
2162
2243
  this.isMatch = isMatch;
2163
2244
  this.matchIDs = matchIDs;
2164
- this.nextLatin1 = new Array(Unicode.MAX_LATIN1 + 1).fill(null);
2165
- this.nextLatin1Anchored = new Array(Unicode.MAX_LATIN1 + 1).fill(null);
2166
- this.transKeys = [];
2167
- this.transVals = [];
2168
- this.lastSeen = 0;
2245
+ this.next = null;
2246
+ this.nextAnchored = null;
2169
2247
  }
2170
2248
  };
2171
2249
  var DFA = class DFA {
2172
- static MAX_CACHE_CLEARS = 5;
2173
- static STATE_MEMORY_ESTIMATE = 838;
2250
+ static STATE_OVERHEAD = 160;
2251
+ static TABLE_OVERHEAD = 16;
2252
+ static PROGRESS_PER_STATE = 10;
2174
2253
  constructor(prog, maxMem = 8388608) {
2175
2254
  this.prog = prog;
2255
+ this.maxMem = maxMem;
2176
2256
  this.stateCache = /* @__PURE__ */ new Map();
2177
2257
  this.stateCount = 0;
2178
2258
  this.startState = null;
2179
- this.stateLimit = Math.max(1, Math.floor(maxMem / DFA.STATE_MEMORY_ESTIMATE));
2180
- this.cacheClears = 0;
2181
2259
  this.failed = false;
2182
- this.clock = 0;
2260
+ for (let pc = 0; pc < prog.numInst(); pc++) {
2261
+ const op = prog.getInst(pc).op;
2262
+ if (op === Inst.EMPTY_WIDTH || op === Inst.LB_WRITE || op === Inst.LB_CHECK) {
2263
+ this.failed = true;
2264
+ break;
2265
+ }
2266
+ }
2267
+ this.computeRuneClasses();
2268
+ this.stateBudget = maxMem;
2269
+ this.memBudget = maxMem;
2270
+ }
2271
+ computeRuneClasses() {
2272
+ const bounds = /* @__PURE__ */ new Set();
2273
+ const addRune = (r) => {
2274
+ bounds.add(r);
2275
+ bounds.add(r + 1);
2276
+ };
2277
+ for (let pc = 0; pc < this.prog.numInst(); pc++) {
2278
+ const inst = this.prog.getInst(pc);
2279
+ switch (inst.op) {
2280
+ case Inst.RUNE:
2281
+ case Inst.RUNE1:
2282
+ if (inst.runes.length === 1) {
2283
+ addRune(inst.runes[0]);
2284
+ 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);
2285
+ } else for (let j = 0; j < inst.runes.length; j += 2) {
2286
+ bounds.add(inst.runes[j]);
2287
+ bounds.add(inst.runes[j + 1] + 1);
2288
+ }
2289
+ break;
2290
+ case Inst.RUNE_ANY_NOT_NL: addRune(10);
2291
+ }
2292
+ }
2293
+ bounds.delete(0);
2294
+ this.classBounds = Int32Array.from(bounds).sort();
2295
+ this.numClasses = this.classBounds.length + 1;
2296
+ const latin1Class = new Int32Array(Unicode.MAX_LATIN1 + 1);
2297
+ for (let r = 0; r <= Unicode.MAX_LATIN1; r++) latin1Class[r] = this.searchClass(r);
2298
+ this.latin1Class = latin1Class;
2299
+ }
2300
+ runeClass(r) {
2301
+ if (r <= Unicode.MAX_LATIN1) return this.latin1Class[r];
2302
+ return this.searchClass(r);
2303
+ }
2304
+ searchClass(r) {
2305
+ const bounds = this.classBounds;
2306
+ let lo = 0;
2307
+ let hi = bounds.length;
2308
+ while (lo < hi) {
2309
+ const mid = lo + hi >> 1;
2310
+ if (bounds[mid] <= r) lo = mid + 1;
2311
+ else hi = mid;
2312
+ }
2313
+ return lo;
2183
2314
  }
2184
2315
  computeClosure(pcs) {
2185
2316
  const closure = /* @__PURE__ */ new Set();
2186
2317
  const stack = [...pcs];
2187
2318
  let isMatch = false;
2188
- const matchIDs = [];
2319
+ const matchIDs = /* @__PURE__ */ new Set();
2189
2320
  while (stack.length > 0) {
2190
2321
  const pc = stack.pop();
2191
2322
  if (closure.has(pc)) continue;
@@ -2194,7 +2325,7 @@
2194
2325
  switch (inst.op) {
2195
2326
  case Inst.MATCH:
2196
2327
  isMatch = true;
2197
- if (!matchIDs.includes(inst.arg)) matchIDs.push(inst.arg);
2328
+ matchIDs.add(inst.arg);
2198
2329
  break;
2199
2330
  case Inst.ALT:
2200
2331
  case Inst.ALT_MATCH:
@@ -2211,11 +2342,11 @@
2211
2342
  }
2212
2343
  }
2213
2344
  const sortedPCs = Int32Array.from(closure).sort();
2214
- matchIDs.sort((a, b) => a - b);
2345
+ const ids = Array.from(matchIDs).sort((a, b) => a - b);
2215
2346
  return {
2216
2347
  pcs: sortedPCs,
2217
2348
  isMatch,
2218
- matchIDs
2349
+ matchIDs: ids
2219
2350
  };
2220
2351
  }
2221
2352
  getState(pcs) {
@@ -2226,156 +2357,126 @@
2226
2357
  let bucket = this.stateCache.get(hash);
2227
2358
  if (bucket) for (let i = 0; i < bucket.length; i++) {
2228
2359
  const state = bucket[i];
2229
- if (arraysEqual(state.nfaStates, sortedPCs)) {
2230
- state.lastSeen = ++this.clock;
2231
- return state;
2232
- }
2360
+ if (arraysEqual(state.nfaStates, sortedPCs)) return state;
2233
2361
  }
2234
- else {
2362
+ const cost = DFA.STATE_OVERHEAD + 4 * sortedPCs.length + 8 * closureResult.matchIDs.length;
2363
+ if (this.memBudget < cost) return null;
2364
+ this.memBudget -= cost;
2365
+ if (!bucket) {
2235
2366
  bucket = [];
2236
2367
  this.stateCache.set(hash, bucket);
2237
2368
  }
2238
- if (this.failed) return null;
2239
- if (this.stateCount >= this.stateLimit) {
2240
- this.cacheClears++;
2241
- if (this.cacheClears >= DFA.MAX_CACHE_CLEARS) {
2242
- this.failed = true;
2243
- this.stateCache.clear();
2244
- this.stateCount = 0;
2245
- this.startState = null;
2246
- return null;
2247
- }
2248
- this.evictCache();
2249
- bucket = this.stateCache.get(hash);
2250
- if (!bucket) {
2251
- bucket = [];
2252
- this.stateCache.set(hash, bucket);
2253
- }
2254
- }
2255
2369
  const state = new DFAState(sortedPCs, closureResult.isMatch, closureResult.matchIDs);
2256
- state.lastSeen = ++this.clock;
2257
2370
  bucket.push(state);
2258
2371
  this.stateCount++;
2259
2372
  return state;
2260
2373
  }
2261
- evictCache() {
2262
- const allStates = [];
2263
- for (const bucket of this.stateCache.values()) for (let i = 0; i < bucket.length; i++) allStates.push(bucket[i]);
2264
- allStates.sort((a, b) => a.lastSeen - b.lastSeen);
2265
- const keepCount = Math.max(1, Math.floor(this.stateLimit / 2));
2266
- const startIndex = allStates.length - keepCount;
2267
- const survivorsArray = allStates.slice(startIndex);
2268
- const survivors = new Set(survivorsArray);
2374
+ allocTable(state, anchor) {
2375
+ const cost = DFA.TABLE_OVERHEAD + 8 * this.numClasses;
2376
+ if (this.memBudget < cost) return null;
2377
+ this.memBudget -= cost;
2378
+ const table = new Array(this.numClasses).fill(null);
2379
+ if (anchor === RE2Flags.UNANCHORED) state.next = table;
2380
+ else state.nextAnchored = table;
2381
+ return table;
2382
+ }
2383
+ clearCache() {
2269
2384
  this.stateCache.clear();
2270
2385
  this.stateCount = 0;
2271
- for (let i = 0; i < survivorsArray.length; i++) {
2272
- const state = survivorsArray[i];
2273
- state.nextLatin1.fill(null);
2274
- state.nextLatin1Anchored.fill(null);
2275
- state.transKeys.length = 0;
2276
- state.transVals.length = 0;
2277
- const hash = hashPCs(state.nfaStates);
2278
- let bucket = this.stateCache.get(hash);
2279
- if (!bucket) {
2280
- bucket = [];
2281
- this.stateCache.set(hash, bucket);
2282
- }
2283
- bucket.push(state);
2284
- this.stateCount++;
2285
- }
2286
- if (this.startState && !survivors.has(this.startState)) this.startState = null;
2386
+ this.startState = null;
2387
+ this.memBudget = this.stateBudget;
2287
2388
  }
2288
- step(state, charCode, anchor) {
2289
- if (charCode <= Unicode.MAX_LATIN1) if (anchor === RE2Flags.UNANCHORED) {
2290
- const next = state.nextLatin1[charCode];
2291
- if (next !== null) return next;
2389
+ resetCache(current) {
2390
+ this.clearCache();
2391
+ return this.getState(current.nfaStates);
2392
+ }
2393
+ step(state, cls, rune, anchor) {
2394
+ let table = anchor === RE2Flags.UNANCHORED ? state.next : state.nextAnchored;
2395
+ if (table !== null) {
2396
+ const cached = table[cls];
2397
+ if (cached !== null) return cached;
2292
2398
  } else {
2293
- const next = state.nextLatin1Anchored[charCode];
2294
- if (next !== null) return next;
2295
- }
2296
- else {
2297
- const key = charCode + (anchor === RE2Flags.UNANCHORED ? 0 : Unicode.MAX_RUNE + 1);
2298
- const keys = state.transKeys;
2299
- const len = keys.length;
2300
- for (let i = 0; i < len; i++) if (keys[i] === key) return state.transVals[i];
2399
+ table = this.allocTable(state, anchor);
2400
+ if (table === null) return null;
2301
2401
  }
2302
2402
  const nextPCs = [];
2303
2403
  for (let i = 0; i < state.nfaStates.length; i++) {
2304
2404
  const pc = state.nfaStates[i];
2305
2405
  const inst = this.prog.getInst(pc);
2306
- if (Inst.isRuneOp(inst.op) && inst.matchRune(charCode)) nextPCs.push(inst.out);
2406
+ if (Inst.isRuneOp(inst.op) && inst.matchRune(rune)) nextPCs.push(inst.out);
2307
2407
  }
2308
2408
  if (anchor === RE2Flags.UNANCHORED) nextPCs.push(this.prog.start);
2309
2409
  const nextState = this.getState(nextPCs);
2310
- if (charCode <= Unicode.MAX_LATIN1) if (anchor === RE2Flags.UNANCHORED) state.nextLatin1[charCode] = nextState;
2311
- else state.nextLatin1Anchored[charCode] = nextState;
2312
- else {
2313
- const key = charCode + (anchor === RE2Flags.UNANCHORED ? 0 : Unicode.MAX_RUNE + 1);
2314
- state.transKeys.push(key);
2315
- state.transVals.push(nextState);
2316
- }
2410
+ if (nextState === null) return null;
2411
+ table[cls] = nextState;
2317
2412
  return nextState;
2318
2413
  }
2319
- match(input, pos, anchor) {
2320
- if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return false;
2321
- if (!this.startState) {
2414
+ start() {
2415
+ if (this.startState === null) {
2322
2416
  this.startState = this.getState([this.prog.start]);
2323
- if (!this.startState) return null;
2417
+ if (this.startState === null && !this.failed) {
2418
+ this.clearCache();
2419
+ this.startState = this.getState([this.prog.start]);
2420
+ }
2324
2421
  }
2325
- let endPos = input.endPos();
2326
- let currentState = this.startState;
2327
- if (currentState.isMatch) if (anchor === RE2Flags.ANCHOR_BOTH) {
2328
- if (pos === endPos) return true;
2329
- } else return true;
2422
+ return this.startState;
2423
+ }
2424
+ run(input, pos, anchor, state, onState, noMatch) {
2425
+ const endPos = input.endPos();
2426
+ const latin1Class = this.latin1Class;
2427
+ let resetPos = -1;
2330
2428
  let i = pos;
2331
2429
  while (i < endPos) {
2332
2430
  const r = input.step(i);
2333
2431
  const rune = r >> 3;
2334
2432
  const width = r & 7;
2335
2433
  if (width === 0) break;
2336
- currentState = anchor === RE2Flags.UNANCHORED && rune <= Unicode.MAX_LATIN1 && currentState.nextLatin1[rune] || this.step(currentState, rune, anchor);
2337
- if (currentState === null) return null;
2338
- currentState.lastSeen = ++this.clock;
2339
- if (currentState.isMatch) if (anchor === RE2Flags.ANCHOR_BOTH) {
2340
- if (i + width === endPos) return true;
2341
- } else return true;
2342
- if (currentState.nfaStates.length === 0) {
2343
- if (anchor !== RE2Flags.UNANCHORED) return false;
2434
+ const cls = rune <= Unicode.MAX_LATIN1 ? latin1Class[rune] : this.runeClass(rune);
2435
+ let next = anchor === RE2Flags.UNANCHORED && state.next !== null ? state.next[cls] : null;
2436
+ if (next === null) {
2437
+ next = this.step(state, cls, rune, anchor);
2438
+ if (next === null) {
2439
+ if (resetPos >= 0 && i - resetPos < DFA.PROGRESS_PER_STATE * this.stateCount) return null;
2440
+ resetPos = i;
2441
+ state = this.resetCache(state);
2442
+ if (state === null) return null;
2443
+ next = this.step(state, cls, rune, anchor);
2444
+ if (next === null) return null;
2445
+ }
2344
2446
  }
2447
+ state = next;
2345
2448
  i += width;
2449
+ const result = onState(state, i);
2450
+ if (result !== null) return result;
2451
+ if (state.nfaStates.length === 0 && anchor !== RE2Flags.UNANCHORED) return noMatch;
2346
2452
  }
2347
- return false;
2453
+ return noMatch;
2454
+ }
2455
+ match(input, pos, anchor) {
2456
+ if (this.failed) return null;
2457
+ if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return false;
2458
+ const startState = this.start();
2459
+ if (startState === null) return null;
2460
+ const endPos = input.endPos();
2461
+ if (startState.isMatch && (anchor !== RE2Flags.ANCHOR_BOTH || pos === endPos)) return true;
2462
+ return this.run(input, pos, anchor, startState, (state, nextPos) => {
2463
+ if (state.isMatch && (anchor !== RE2Flags.ANCHOR_BOTH || nextPos === endPos)) return true;
2464
+ return null;
2465
+ }, false);
2348
2466
  }
2349
2467
  matchSet(input, pos, anchor) {
2468
+ if (this.failed) return null;
2350
2469
  if ((anchor === RE2Flags.ANCHOR_START || anchor === RE2Flags.ANCHOR_BOTH) && pos !== 0) return [];
2351
- if (!this.startState) {
2352
- this.startState = this.getState([this.prog.start]);
2353
- if (!this.startState) return null;
2354
- }
2355
- let endPos = input.endPos();
2356
- let currentState = this.startState;
2470
+ const startState = this.start();
2471
+ if (startState === null) return null;
2472
+ const endPos = input.endPos();
2357
2473
  const matches = /* @__PURE__ */ new Set();
2358
- const checkMatch = (state, currentPos) => {
2359
- if (state.isMatch) if (anchor === RE2Flags.ANCHOR_BOTH) {
2360
- if (currentPos === endPos) state.matchIDs.forEach((id) => matches.add(id));
2361
- } else state.matchIDs.forEach((id) => matches.add(id));
2474
+ const collect = (state, currentPos) => {
2475
+ if (state.isMatch && (anchor !== RE2Flags.ANCHOR_BOTH || currentPos === endPos)) for (let k = 0; k < state.matchIDs.length; k++) matches.add(state.matchIDs[k]);
2476
+ return null;
2362
2477
  };
2363
- checkMatch(currentState, pos);
2364
- let i = pos;
2365
- while (i < endPos) {
2366
- const r = input.step(i);
2367
- const rune = r >> 3;
2368
- const width = r & 7;
2369
- if (width === 0) break;
2370
- currentState = anchor === RE2Flags.UNANCHORED && rune <= Unicode.MAX_LATIN1 && currentState.nextLatin1[rune] || this.step(currentState, rune, anchor);
2371
- if (currentState === null) return null;
2372
- currentState.lastSeen = ++this.clock;
2373
- i += width;
2374
- checkMatch(currentState, i);
2375
- if (currentState.nfaStates.length === 0) {
2376
- if (anchor !== RE2Flags.UNANCHORED) break;
2377
- }
2378
- }
2478
+ collect(startState, pos);
2479
+ if (this.run(input, pos, anchor, startState, collect, false) === null) return null;
2379
2480
  return Array.from(matches).sort((a, b) => a - b);
2380
2481
  }
2381
2482
  };
@@ -2384,7 +2485,7 @@
2384
2485
  const VISITED_BITS = 32;
2385
2486
  const MAX_BACKTRACK_PROG = 500;
2386
2487
  const INITIAL_JOB_CAPACITY = 256;
2387
- const MAX_BACKTRACK_VECTOR = 256 * 1024;
2488
+ const MAX_BACKTRACK_VECTOR = 262144;
2388
2489
  var BitState = class {
2389
2490
  constructor() {
2390
2491
  this.end = 0;
@@ -2667,7 +2768,7 @@
2667
2768
  const inst = new Inst(orig.op);
2668
2769
  inst.out = orig.out;
2669
2770
  inst.arg = orig.arg;
2670
- inst.runes = orig.runes ? orig.runes.slice() : [];
2771
+ inst.runes = orig.runes ? new Int32Array(orig.runes) : /* @__PURE__ */ new Int32Array(0);
2671
2772
  inst.next = null;
2672
2773
  this.inst[i] = inst;
2673
2774
  }
@@ -2737,7 +2838,7 @@
2737
2838
  const rightRunes = onePassRunes[inst.arg] || [];
2738
2839
  const mergeRes = mergeRuneSets(leftRunes, rightRunes, inst.out, inst.arg);
2739
2840
  if (!mergeRes) return false;
2740
- onePassRunes[pc] = mergeRes.merged;
2841
+ onePassRunes[pc] = new Int32Array(mergeRes.merged);
2741
2842
  inst.next = new Uint32Array(mergeRes.next);
2742
2843
  break;
2743
2844
  }
@@ -2746,7 +2847,7 @@
2746
2847
  case Inst.NOP:
2747
2848
  ok = check(inst.out);
2748
2849
  m[pc] = m[inst.out];
2749
- onePassRunes[pc] = onePassRunes[inst.out] ? onePassRunes[inst.out].slice() : [];
2850
+ onePassRunes[pc] = onePassRunes[inst.out] ? new Int32Array(onePassRunes[inst.out]) : /* @__PURE__ */ new Int32Array(0);
2750
2851
  inst.next = new Uint32Array(Math.floor(onePassRunes[pc].length / 2) + 1).fill(inst.out);
2751
2852
  break;
2752
2853
  case Inst.MATCH:
@@ -2758,7 +2859,7 @@
2758
2859
  if (inst.next && inst.next.length > 0) break;
2759
2860
  instQueue.insert(inst.out);
2760
2861
  if (!inst.runes || inst.runes.length === 0) {
2761
- onePassRunes[pc] = [];
2862
+ onePassRunes[pc] = /* @__PURE__ */ new Int32Array(0);
2762
2863
  inst.next = new Uint32Array([inst.out]);
2763
2864
  break;
2764
2865
  }
@@ -2769,7 +2870,7 @@
2769
2870
  for (let r1 = Unicode.simpleFold(r0); r1 !== r0; r1 = Unicode.simpleFold(r1)) runes.push(r1, r1);
2770
2871
  runes.sort((a, b) => a - b);
2771
2872
  } else for (let j = 0; j < inst.runes.length; j++) runes.push(inst.runes[j]);
2772
- onePassRunes[pc] = runes;
2873
+ onePassRunes[pc] = new Int32Array(runes);
2773
2874
  inst.next = new Uint32Array(Math.floor(runes.length / 2) + 1).fill(inst.out);
2774
2875
  inst.op = Inst.RUNE;
2775
2876
  break;
@@ -2785,7 +2886,7 @@
2785
2886
  for (let r1 = Unicode.simpleFold(r0); r1 !== r0; r1 = Unicode.simpleFold(r1)) runes.push(r1, r1);
2786
2887
  runes.sort((a, b) => a - b);
2787
2888
  } else runes.push(inst.runes[0], inst.runes[0]);
2788
- onePassRunes[pc] = runes;
2889
+ onePassRunes[pc] = new Int32Array(runes);
2789
2890
  inst.next = new Uint32Array(Math.floor(runes.length / 2) + 1).fill(inst.out);
2790
2891
  inst.op = Inst.RUNE;
2791
2892
  break;
@@ -2794,21 +2895,20 @@
2794
2895
  m[pc] = false;
2795
2896
  if (inst.next && inst.next.length > 0) break;
2796
2897
  instQueue.insert(inst.out);
2797
- onePassRunes[pc] = [0, Unicode.MAX_RUNE];
2898
+ onePassRunes[pc] = new Int32Array([0, Unicode.MAX_RUNE]);
2798
2899
  inst.next = new Uint32Array([inst.out]);
2799
2900
  break;
2800
2901
  case Inst.RUNE_ANY_NOT_NL:
2801
2902
  m[pc] = false;
2802
2903
  if (inst.next && inst.next.length > 0) break;
2803
2904
  instQueue.insert(inst.out);
2804
- onePassRunes[pc] = [
2905
+ onePassRunes[pc] = new Int32Array([
2805
2906
  0,
2806
2907
  9,
2807
2908
  11,
2808
2909
  Unicode.MAX_RUNE
2809
- ];
2910
+ ]);
2810
2911
  inst.next = new Uint32Array(Math.floor(onePassRunes[pc].length / 2) + 1).fill(inst.out);
2811
- break;
2812
2912
  }
2813
2913
  return ok;
2814
2914
  };
@@ -2840,8 +2940,7 @@
2840
2940
  case Inst.RUNE_ANY_NOT_NL:
2841
2941
  p.inst[ix].next = null;
2842
2942
  p.inst[ix].op = instOriginal.op;
2843
- p.inst[ix].runes = instOriginal.runes ? instOriginal.runes.slice() : [];
2844
- break;
2943
+ p.inst[ix].runes = instOriginal.runes ? new Int32Array(instOriginal.runes) : /* @__PURE__ */ new Int32Array(0);
2845
2944
  }
2846
2945
  }
2847
2946
  };
@@ -2870,9 +2969,7 @@
2870
2969
  return null;
2871
2970
  }
2872
2971
  break;
2873
- default:
2874
- if (opOut === Inst.MATCH && hasAlt) return null;
2875
- break;
2972
+ default: if (opOut === Inst.MATCH && hasAlt) return null;
2876
2973
  }
2877
2974
  }
2878
2975
  let p = onePassCopy(prog);
@@ -3023,7 +3120,7 @@
3023
3120
  this.op = op;
3024
3121
  this.flags = 0;
3025
3122
  this.subs = Regexp.emptySubs();
3026
- this.runes = [];
3123
+ this.runes = /* @__PURE__ */ new Int32Array(0);
3027
3124
  this.min = 0;
3028
3125
  this.max = 0;
3029
3126
  this.cap = 0;
@@ -3034,7 +3131,7 @@
3034
3131
  reinit() {
3035
3132
  this.flags = 0;
3036
3133
  this.subs = Regexp.emptySubs();
3037
- this.runes = [];
3134
+ this.runes = /* @__PURE__ */ new Int32Array(0);
3038
3135
  this.cap = 0;
3039
3136
  this.min = 0;
3040
3137
  this.max = 0;
@@ -3078,7 +3175,6 @@
3078
3175
  if (this.max >= 0) out += this.max;
3079
3176
  }
3080
3177
  out += "}";
3081
- break;
3082
3178
  }
3083
3179
  if ((this.flags & RE2Flags.NON_GREEDY) !== 0) out += "?";
3084
3180
  break;
@@ -3171,9 +3267,7 @@
3171
3267
  }
3172
3268
  out += "]";
3173
3269
  break;
3174
- default:
3175
- out += this.op;
3176
- break;
3270
+ default: out += this.op;
3177
3271
  }
3178
3272
  return out;
3179
3273
  }
@@ -3217,70 +3311,158 @@
3217
3311
  if (this.cap !== that.cap || (this.name === null ? that.name !== null : this.name !== that.name) || !this.subs[0].equals(that.subs[0])) return false;
3218
3312
  break;
3219
3313
  case Regexp.Op.PLB:
3220
- case Regexp.Op.NLB:
3221
- if (this.lb !== that.lb || !this.subs[0].equals(that.subs[0])) return false;
3222
- break;
3314
+ case Regexp.Op.NLB: if (this.lb !== that.lb || !this.subs[0].equals(that.subs[0])) return false;
3223
3315
  }
3224
3316
  return true;
3225
3317
  }
3226
3318
  };
3227
3319
  //#endregion
3320
+ //#region src/RegexpWalker.js
3321
+ /**
3322
+ * Walks a Regexp tree without recursion, visiting nodes in the same order a recursive walk
3323
+ * would (RE2: re2/walker-inl.h, "Helper class for traversing Regexps without recursion").
3324
+ *
3325
+ * The handler sees a node without subexpressions once, through `leaf(re)`, which returns its
3326
+ * result. Any other node is seen up to three times:
3327
+ * - `enter(re)` before its children. It returns the state kept for `re` while its children are
3328
+ * visited; a state whose `result` is already set skips the children.
3329
+ * - `child(re, state, result, i)` after child `i` produced `result`. Setting `state.result`
3330
+ * stops visiting the remaining children.
3331
+ * - `leave(re, state)` after the last child; it returns the result for `re`.
3332
+ *
3333
+ * Counted repetitions expand into one nesting level per copy, so the simplified tree can be
3334
+ * thousands of levels deep; recursion over it would overflow the JS stack, which has no fixed
3335
+ * relation to the parser's nesting limit. This walk keeps its frames on the heap instead.
3336
+ */
3337
+ var RegexpWalker = class {
3338
+ static walk(root, handler) {
3339
+ const stack = [];
3340
+ let re = root;
3341
+ let result;
3342
+ for (;;) {
3343
+ if (re.subs.length === 0) result = handler.leaf(re);
3344
+ else {
3345
+ const state = handler.enter(re);
3346
+ if (state.result === void 0) {
3347
+ stack.push({
3348
+ re,
3349
+ state,
3350
+ i: 0
3351
+ });
3352
+ re = re.subs[0];
3353
+ continue;
3354
+ }
3355
+ result = state.result;
3356
+ }
3357
+ for (;;) {
3358
+ if (stack.length === 0) return result;
3359
+ const frame = stack[stack.length - 1];
3360
+ handler.child(frame.re, frame.state, result, frame.i);
3361
+ frame.i++;
3362
+ if (frame.state.result === void 0 && frame.i < frame.re.subs.length) {
3363
+ re = frame.re.subs[frame.i];
3364
+ break;
3365
+ }
3366
+ stack.pop();
3367
+ result = frame.state.result !== void 0 ? frame.state.result : handler.leave(frame.re, frame.state);
3368
+ }
3369
+ }
3370
+ }
3371
+ };
3372
+ //#endregion
3228
3373
  //#region src/Prefilter.js
3229
3374
  var AhoCorasick = class {
3230
- constructor(wordArrays) {
3231
- this.next = [Object.create(null)];
3232
- this.fail = [0];
3233
- this.match = [false];
3234
- for (const word of wordArrays) {
3375
+ constructor(words, alphabetSize) {
3376
+ const edges = /* @__PURE__ */ new Map();
3377
+ const parent = [0];
3378
+ const unit = [0];
3379
+ const depth = [0];
3380
+ const terminal = [0];
3381
+ for (const word of words) {
3235
3382
  let node = 0;
3236
3383
  for (let i = 0; i < word.length; i++) {
3237
- const val = word[i];
3238
- if (!(val in this.next[node])) {
3239
- this.next.push(Object.create(null));
3240
- this.fail.push(0);
3241
- this.match.push(false);
3242
- this.next[node][val] = this.next.length - 1;
3384
+ const key = node * alphabetSize + word[i];
3385
+ let child = edges.get(key);
3386
+ if (child === void 0) {
3387
+ child = parent.length;
3388
+ edges.set(key, child);
3389
+ parent.push(node);
3390
+ unit.push(word[i]);
3391
+ depth.push(depth[node] + 1);
3392
+ terminal.push(0);
3243
3393
  }
3244
- node = this.next[node][val];
3394
+ node = child;
3245
3395
  }
3246
- this.match[node] = true;
3247
- }
3248
- const queue = [];
3249
- for (const val in this.next[0]) if (Object.prototype.hasOwnProperty.call(this.next[0], val)) {
3250
- const child = this.next[0][val];
3251
- this.fail[child] = 0;
3252
- queue.push(child);
3253
- }
3254
- while (queue.length > 0) {
3255
- const curr = queue.shift();
3256
- for (const val in this.next[curr]) if (Object.prototype.hasOwnProperty.call(this.next[curr], val)) {
3257
- const child = this.next[curr][val];
3258
- let failNode = this.fail[curr];
3259
- while (failNode !== 0 && !(val in this.next[failNode])) failNode = this.fail[failNode];
3260
- if (val in this.next[failNode]) this.fail[child] = this.next[failNode][val];
3261
- else this.fail[child] = 0;
3262
- this.match[child] = this.match[child] || this.match[this.fail[child]];
3263
- queue.push(child);
3396
+ terminal[node] = 1;
3397
+ }
3398
+ const numNodes = parent.length;
3399
+ const children = new Int32Array(numNodes - 1);
3400
+ for (let c = 1; c < numNodes; c++) children[c - 1] = c;
3401
+ children.sort((a, b) => parent[a] - parent[b] || unit[a] - unit[b]);
3402
+ const childStart = new Int32Array(numNodes + 1);
3403
+ for (let c = 1; c < numNodes; c++) childStart[parent[c] + 1]++;
3404
+ for (let n = 0; n < numNodes; n++) childStart[n + 1] += childStart[n];
3405
+ const childUnit = new Int32Array(numNodes - 1);
3406
+ for (let k = 0; k < children.length; k++) childUnit[k] = unit[children[k]];
3407
+ this.childStart = childStart;
3408
+ this.childUnit = childUnit;
3409
+ this.childNode = children;
3410
+ const byDepth = new Int32Array(numNodes);
3411
+ for (let n = 0; n < numNodes; n++) byDepth[n] = n;
3412
+ byDepth.sort((a, b) => depth[a] - depth[b]);
3413
+ const fail = new Int32Array(numNodes);
3414
+ const match = Uint8Array.from(terminal);
3415
+ for (let k = 1; k < numNodes; k++) {
3416
+ const c = byDepth[k];
3417
+ const p = parent[c];
3418
+ if (p !== 0) {
3419
+ let f = fail[p];
3420
+ let next = this.next(f, unit[c]);
3421
+ while (next < 0 && f !== 0) {
3422
+ f = fail[f];
3423
+ next = this.next(f, unit[c]);
3424
+ }
3425
+ fail[c] = next >= 0 ? next : 0;
3264
3426
  }
3427
+ match[c] |= match[fail[c]];
3428
+ }
3429
+ this.fail = fail;
3430
+ this.match = match;
3431
+ }
3432
+ next(node, u) {
3433
+ let lo = this.childStart[node];
3434
+ let hi = this.childStart[node + 1];
3435
+ const units = this.childUnit;
3436
+ while (lo < hi) {
3437
+ const mid = lo + hi >> 1;
3438
+ const m = units[mid];
3439
+ if (m === u) return this.childNode[mid];
3440
+ if (m < u) lo = mid + 1;
3441
+ else hi = mid;
3442
+ }
3443
+ return -1;
3444
+ }
3445
+ advance(node, u) {
3446
+ for (;;) {
3447
+ const next = this.next(node, u);
3448
+ if (next >= 0) return next;
3449
+ if (node === 0) return 0;
3450
+ node = this.fail[node];
3265
3451
  }
3266
3452
  }
3267
3453
  searchUTF16(charSeq, start, end) {
3268
3454
  let node = 0;
3269
3455
  for (let i = start; i < end; i++) {
3270
- const val = charSeq.charCodeAt(i);
3271
- while (node !== 0 && !(val in this.next[node])) node = this.fail[node];
3272
- if (val in this.next[node]) node = this.next[node][val];
3273
- if (this.match[node]) return true;
3456
+ node = this.advance(node, charSeq.charCodeAt(i));
3457
+ if (this.match[node] === 1) return true;
3274
3458
  }
3275
3459
  return false;
3276
3460
  }
3277
3461
  searchUTF8(bytes, start, end) {
3278
3462
  let node = 0;
3279
3463
  for (let i = start; i < end; i++) {
3280
- const val = bytes[i];
3281
- while (node !== 0 && !(val in this.next[node])) node = this.fail[node];
3282
- if (val in this.next[node]) node = this.next[node][val];
3283
- if (this.match[node]) return true;
3464
+ node = this.advance(node, bytes[i]);
3465
+ if (this.match[node] === 1) return true;
3284
3466
  }
3285
3467
  return false;
3286
3468
  }
@@ -3292,14 +3474,33 @@
3292
3474
  AND: 2,
3293
3475
  OR: 3
3294
3476
  };
3477
+ static MAX_SCANS = 32;
3478
+ static MAX_AC_UNITS = 1 << 18;
3295
3479
  constructor(type) {
3296
3480
  this.type = type;
3297
3481
  this.subs = [];
3298
3482
  this.str = "";
3299
3483
  this.bytes = null;
3484
+ this.cost = type === Prefilter.Type.EXACT ? 1 : 0;
3485
+ this.allExact = false;
3300
3486
  this.ac16 = null;
3301
3487
  this.ac8 = null;
3302
3488
  }
3489
+ automaton16() {
3490
+ if (this.ac16 === null) {
3491
+ const words = this.subs.map((s) => {
3492
+ const arr = new Array(s.str.length);
3493
+ for (let i = 0; i < s.str.length; i++) arr[i] = s.str.charCodeAt(i);
3494
+ return arr;
3495
+ });
3496
+ this.ac16 = new AhoCorasick(words, 65536);
3497
+ }
3498
+ return this.ac16;
3499
+ }
3500
+ automaton8() {
3501
+ if (this.ac8 === null) this.ac8 = new AhoCorasick(this.subs.map((s) => s.bytes), 256);
3502
+ return this.ac8;
3503
+ }
3303
3504
  eval(input, pos) {
3304
3505
  switch (this.type) {
3305
3506
  case Prefilter.Type.NONE: return true;
@@ -3308,7 +3509,7 @@
3308
3509
  for (let i = 0; i < this.subs.length; i++) if (!this.subs[i].eval(input, pos)) return false;
3309
3510
  return true;
3310
3511
  case Prefilter.Type.OR:
3311
- if (this.ac16 && this.ac8) return input.hasAnyString(this, pos);
3512
+ if (this.allExact) return input.hasAnyString(this, pos);
3312
3513
  for (let i = 0; i < this.subs.length; i++) if (this.subs[i].eval(input, pos)) return true;
3313
3514
  return false;
3314
3515
  default: return true;
@@ -3317,100 +3518,115 @@
3317
3518
  };
3318
3519
  var PrefilterTree = class PrefilterTree {
3319
3520
  static build(re) {
3320
- const pf = PrefilterTree.fromRegexp(re);
3321
- return PrefilterTree.simplify(pf);
3322
- }
3323
- static fromRegexp(re) {
3324
3521
  if (!re) return new Prefilter(Prefilter.Type.NONE);
3522
+ return RegexpWalker.walk(re, PrefilterTree.WALKER);
3523
+ }
3524
+ static WALKER = {
3525
+ leaf: (re) => PrefilterTree.leaf(re),
3526
+ enter: (re) => PrefilterTree.enter(re),
3527
+ child: (re, state, result) => PrefilterTree.child(re, state, result),
3528
+ leave: (re, state) => PrefilterTree.leave(re, state)
3529
+ };
3530
+ static leaf(re) {
3531
+ if (re.op !== Regexp.Op.LITERAL) return new Prefilter(Prefilter.Type.NONE);
3532
+ if (re.runes.length === 0 || (re.flags & RE2Flags.FOLD_CASE) !== 0) return new Prefilter(Prefilter.Type.NONE);
3533
+ let str = "";
3534
+ for (let i = 0; i < re.runes.length; i++) {
3535
+ if (Unicode.isUnsearchable(re.runes[i])) return new Prefilter(Prefilter.Type.NONE);
3536
+ str += String.fromCodePoint(re.runes[i]);
3537
+ }
3538
+ const pf = new Prefilter(Prefilter.Type.EXACT);
3539
+ pf.str = str;
3540
+ pf.bytes = Utils.stringToUtf8ByteArray(pf.str);
3541
+ return pf;
3542
+ }
3543
+ static enter(re) {
3325
3544
  switch (re.op) {
3326
- case Regexp.Op.PLB:
3327
- case Regexp.Op.NLB:
3328
- case Regexp.Op.NO_MATCH:
3329
- case Regexp.Op.EMPTY_MATCH:
3330
- case Regexp.Op.BEGIN_LINE:
3331
- case Regexp.Op.END_LINE:
3332
- case Regexp.Op.BEGIN_TEXT:
3333
- case Regexp.Op.END_TEXT:
3334
- case Regexp.Op.WORD_BOUNDARY:
3335
- case Regexp.Op.NO_WORD_BOUNDARY:
3336
- case Regexp.Op.CHAR_CLASS:
3337
- case Regexp.Op.ANY_CHAR_NOT_NL:
3338
- case Regexp.Op.ANY_CHAR: return new Prefilter(Prefilter.Type.NONE);
3339
- case Regexp.Op.LITERAL: {
3340
- if (re.runes.length === 0 || (re.flags & RE2Flags.FOLD_CASE) !== 0) return new Prefilter(Prefilter.Type.NONE);
3341
- const pf = new Prefilter(Prefilter.Type.EXACT);
3342
- let str = "";
3343
- for (let i = 0; i < re.runes.length; i++) str += String.fromCodePoint(re.runes[i]);
3344
- pf.str = str;
3345
- pf.bytes = Utils.stringToUtf8ByteArray(pf.str);
3346
- return pf;
3347
- }
3348
3545
  case Regexp.Op.CAPTURE:
3349
- case Regexp.Op.PLUS: return PrefilterTree.fromRegexp(re.subs[0]);
3546
+ case Regexp.Op.PLUS: return { sub: null };
3350
3547
  case Regexp.Op.REPEAT:
3351
- if (re.min >= 1) return PrefilterTree.fromRegexp(re.subs[0]);
3352
- return new Prefilter(Prefilter.Type.NONE);
3353
- case Regexp.Op.CONCAT: {
3354
- const pf = new Prefilter(Prefilter.Type.AND);
3355
- for (const sub of re.subs) pf.subs.push(PrefilterTree.fromRegexp(sub));
3356
- return pf;
3548
+ if (re.min >= 1) return { sub: null };
3549
+ return { result: new Prefilter(Prefilter.Type.NONE) };
3550
+ case Regexp.Op.CONCAT: return {
3551
+ pf: new Prefilter(Prefilter.Type.AND),
3552
+ subs: []
3553
+ };
3554
+ case Regexp.Op.ALTERNATE: return {
3555
+ pf: new Prefilter(Prefilter.Type.OR),
3556
+ subs: []
3557
+ };
3558
+ default: return { result: new Prefilter(Prefilter.Type.NONE) };
3559
+ }
3560
+ }
3561
+ static child(re, state, s) {
3562
+ if (state.subs === void 0) {
3563
+ state.sub = s;
3564
+ return;
3565
+ }
3566
+ if (state.pf.type === Prefilter.Type.AND) {
3567
+ if (s.type === Prefilter.Type.NONE) return;
3568
+ if (s.type === Prefilter.Type.AND) {
3569
+ for (let j = 0; j < s.subs.length; j++) state.subs.push(s.subs[j]);
3570
+ return;
3357
3571
  }
3358
- case Regexp.Op.ALTERNATE: {
3359
- const pf = new Prefilter(Prefilter.Type.OR);
3360
- for (const sub of re.subs) pf.subs.push(PrefilterTree.fromRegexp(sub));
3361
- return pf;
3572
+ state.subs.push(s);
3573
+ return;
3574
+ }
3575
+ if (s.type === Prefilter.Type.NONE) {
3576
+ state.result = new Prefilter(Prefilter.Type.NONE);
3577
+ return;
3578
+ }
3579
+ if (s.type === Prefilter.Type.OR) {
3580
+ for (let j = 0; j < s.subs.length; j++) state.subs.push(s.subs[j]);
3581
+ return;
3582
+ }
3583
+ state.subs.push(s);
3584
+ }
3585
+ static leave(re, state) {
3586
+ if (state.subs === void 0) return state.sub;
3587
+ const pf = state.pf;
3588
+ const seen = /* @__PURE__ */ new Set();
3589
+ const subs = [];
3590
+ for (const sub of state.subs) {
3591
+ if (sub.type === Prefilter.Type.EXACT) {
3592
+ if (seen.has(sub.str)) continue;
3593
+ seen.add(sub.str);
3362
3594
  }
3363
- default: return new Prefilter(Prefilter.Type.NONE);
3595
+ subs.push(sub);
3364
3596
  }
3365
- }
3366
- static simplify(pf) {
3367
- if (pf.type === Prefilter.Type.EXACT || pf.type === Prefilter.Type.NONE) return pf;
3368
3597
  if (pf.type === Prefilter.Type.AND) {
3369
- const newSubs = [];
3370
- for (const sub of pf.subs) {
3371
- const s = PrefilterTree.simplify(sub);
3372
- 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]);
3373
- else newSubs.push(s);
3598
+ const kept = [];
3599
+ let cost = 0;
3600
+ for (const sub of subs) {
3601
+ if (cost + sub.cost > Prefilter.MAX_SCANS) continue;
3602
+ kept.push(sub);
3603
+ cost += sub.cost;
3374
3604
  }
3375
- if (newSubs.length === 0) return new Prefilter(Prefilter.Type.NONE);
3376
- if (newSubs.length === 1) return newSubs[0];
3377
- pf.subs = newSubs;
3605
+ if (kept.length === 0) return new Prefilter(Prefilter.Type.NONE);
3606
+ if (kept.length === 1) return kept[0];
3607
+ pf.subs = kept;
3608
+ pf.cost = cost;
3378
3609
  return pf;
3379
3610
  }
3380
- if (pf.type === Prefilter.Type.OR) {
3381
- const newSubs = [];
3382
- for (const sub of pf.subs) {
3383
- const s = PrefilterTree.simplify(sub);
3384
- if (s.type === Prefilter.Type.NONE) return new Prefilter(Prefilter.Type.NONE);
3385
- if (s.type === Prefilter.Type.OR) for (let j = 0; j < s.subs.length; j++) newSubs.push(s.subs[j]);
3386
- else newSubs.push(s);
3387
- }
3388
- if (newSubs.length === 0) return new Prefilter(Prefilter.Type.NONE);
3389
- if (newSubs.length === 1) return newSubs[0];
3390
- const seen = /* @__PURE__ */ new Set();
3391
- const uniqueSubs = [];
3392
- for (const sub of newSubs) if (sub.type === Prefilter.Type.EXACT) {
3393
- if (!seen.has(sub.str)) {
3394
- seen.add(sub.str);
3395
- uniqueSubs.push(sub);
3396
- }
3397
- } else uniqueSubs.push(sub);
3398
- pf.subs = uniqueSubs;
3399
- let allExact = true;
3400
- for (const sub of uniqueSubs) if (sub.type !== Prefilter.Type.EXACT) {
3401
- allExact = false;
3402
- break;
3403
- }
3404
- if (allExact && uniqueSubs.length > 1) {
3405
- pf.ac16 = new AhoCorasick(uniqueSubs.map((s) => {
3406
- const arr = [];
3407
- for (let i = 0; i < s.str.length; i++) arr.push(s.str.charCodeAt(i));
3408
- return arr;
3409
- }));
3410
- pf.ac8 = new AhoCorasick(uniqueSubs.map((s) => s.bytes));
3411
- }
3611
+ if (subs.length === 0) return new Prefilter(Prefilter.Type.NONE);
3612
+ if (subs.length === 1) return subs[0];
3613
+ pf.subs = subs;
3614
+ let allExact = true;
3615
+ let cost = 0;
3616
+ let units = 0;
3617
+ for (const sub of subs) {
3618
+ if (sub.type !== Prefilter.Type.EXACT) allExact = false;
3619
+ else units += sub.bytes.length;
3620
+ cost += sub.cost;
3621
+ }
3622
+ if (allExact) {
3623
+ if (units > Prefilter.MAX_AC_UNITS) return new Prefilter(Prefilter.Type.NONE);
3624
+ pf.allExact = true;
3625
+ pf.cost = 1;
3412
3626
  return pf;
3413
3627
  }
3628
+ if (cost > Prefilter.MAX_SCANS) return new Prefilter(Prefilter.Type.NONE);
3629
+ pf.cost = cost;
3414
3630
  return pf;
3415
3631
  }
3416
3632
  };
@@ -3446,6 +3662,7 @@
3446
3662
  this.numCap = 2;
3447
3663
  this.lbStarts = [];
3448
3664
  this.numLb = 0;
3665
+ this.lbInst = null;
3449
3666
  }
3450
3667
  getInst(pc) {
3451
3668
  return this.inst[pc];
@@ -3456,6 +3673,26 @@
3456
3673
  addInst(op) {
3457
3674
  this.inst.push(new Inst(op));
3458
3675
  }
3676
+ markLookbehindInsts() {
3677
+ const lbInst = new Uint8Array(this.inst.length);
3678
+ const pending = this.lbStarts.slice();
3679
+ while (pending.length > 0) {
3680
+ const pc = pending.pop();
3681
+ if (pc === 0 || lbInst[pc] === 1) continue;
3682
+ lbInst[pc] = 1;
3683
+ const i = this.inst[pc];
3684
+ switch (i.op) {
3685
+ case Inst.ALT:
3686
+ case Inst.ALT_MATCH:
3687
+ pending.push(i.out, i.arg);
3688
+ break;
3689
+ case Inst.MATCH:
3690
+ case Inst.FAIL: break;
3691
+ default: pending.push(i.out);
3692
+ }
3693
+ }
3694
+ this.lbInst = lbInst;
3695
+ }
3459
3696
  skipNop(pc) {
3460
3697
  let i = this.inst[pc];
3461
3698
  while (i.op === Inst.NOP || i.op === Inst.CAPTURE) {
@@ -3468,7 +3705,7 @@
3468
3705
  let prefix = "";
3469
3706
  let i = this.skipNop(this.start);
3470
3707
  if (!Inst.isRuneOp(i.op) || i.runes.length !== 1) return [i.op === Inst.MATCH, prefix];
3471
- while (Inst.isRuneOp(i.op) && i.runes.length === 1 && (i.arg & RE2Flags.FOLD_CASE) === 0) {
3708
+ while (Inst.isRuneOp(i.op) && i.runes.length === 1 && (i.arg & RE2Flags.FOLD_CASE) === 0 && !Unicode.isUnsearchable(i.runes[0])) {
3472
3709
  prefix += String.fromCodePoint(i.runes[0]);
3473
3710
  i = this.skipNop(i.out);
3474
3711
  }
@@ -3532,6 +3769,13 @@
3532
3769
  };
3533
3770
  //#endregion
3534
3771
  //#region src/Compiler.js
3772
+ const ANY_RUNE_NOT_NL = new Int32Array([
3773
+ 0,
3774
+ Codepoint.CODES.get("\n") - 1,
3775
+ Codepoint.CODES.get("\n") + 1,
3776
+ Unicode.MAX_RUNE
3777
+ ]);
3778
+ const ANY_RUNE = new Int32Array([0, Unicode.MAX_RUNE]);
3535
3779
  /**
3536
3780
  * A fragment of a compiled regular expression program.
3537
3781
  *
@@ -3551,28 +3795,19 @@
3551
3795
  * The only entry point is {@link #compileRegexp}.
3552
3796
  */
3553
3797
  var Compiler = class Compiler {
3554
- static ANY_RUNE_NOT_NL() {
3555
- return [
3556
- 0,
3557
- Codepoint.CODES.get("\n") - 1,
3558
- Codepoint.CODES.get("\n") + 1,
3559
- Unicode.MAX_RUNE
3560
- ];
3561
- }
3562
- static ANY_RUNE() {
3563
- return [0, Unicode.MAX_RUNE];
3564
- }
3565
3798
  static compileRegexp(re) {
3566
3799
  const c = new Compiler();
3567
3800
  const f = c.compile(re);
3568
3801
  c.prog.patch(f.out, c.newInst(Inst.MATCH).i);
3569
3802
  c.prog.start = f.i;
3803
+ c.prog.markLookbehindInsts();
3570
3804
  return c.prog;
3571
3805
  }
3572
3806
  static compileSet(regexps) {
3573
3807
  const c = new Compiler();
3574
3808
  if (regexps.length === 0) {
3575
3809
  c.prog.start = c.newInst(Inst.FAIL).i;
3810
+ c.prog.markLookbehindInsts();
3576
3811
  return c.prog;
3577
3812
  }
3578
3813
  let starts = [];
@@ -3592,11 +3827,19 @@
3592
3827
  start = f.i;
3593
3828
  }
3594
3829
  c.prog.start = start;
3830
+ c.prog.markLookbehindInsts();
3595
3831
  return c.prog;
3596
3832
  }
3597
3833
  constructor() {
3598
3834
  this.prog = new Prog();
3599
3835
  this.newInst(Inst.FAIL);
3836
+ this.runeCache = /* @__PURE__ */ new Map();
3837
+ this.walker = {
3838
+ leaf: (re) => this.leaf(re),
3839
+ enter: (re) => this.enter(re),
3840
+ child: (re, state, result) => this.child(re, state, result),
3841
+ leave: (re, state) => this.leave(re, state)
3842
+ };
3600
3843
  }
3601
3844
  newInst(op) {
3602
3845
  this.prog.addInst(op);
@@ -3689,7 +3932,7 @@
3689
3932
  lookBehind(a, lb) {
3690
3933
  const id = this.newInst(Inst.LB_WRITE);
3691
3934
  this.prog.getInst(id.i).arg = lb;
3692
- const any = this.rune(Compiler.ANY_RUNE(), 0);
3935
+ const any = this.rune(ANY_RUNE, 0);
3693
3936
  const dotStar = this.star(any, true);
3694
3937
  const lbAutomaton = this.cat(dotStar, a);
3695
3938
  this.prog.patch(lbAutomaton.out, id.i);
@@ -3701,56 +3944,74 @@
3701
3944
  return checkId;
3702
3945
  }
3703
3946
  compile(re) {
3947
+ return RegexpWalker.walk(re, this.walker);
3948
+ }
3949
+ leaf(re) {
3704
3950
  switch (re.op) {
3705
3951
  case Regexp.Op.NO_MATCH: return this.fail();
3706
3952
  case Regexp.Op.EMPTY_MATCH: return this.nop();
3707
- case Regexp.Op.LITERAL: if (re.runes.length === 0) return this.nop();
3708
- else {
3953
+ case Regexp.Op.LITERAL: {
3954
+ if (re.runes.length === 0) return this.nop();
3709
3955
  let f = null;
3710
3956
  for (let r of re.runes) {
3711
- const f1 = this.rune([r], re.flags);
3957
+ let singleRune = this.runeCache.get(r);
3958
+ if (singleRune === void 0) {
3959
+ singleRune = new Int32Array([r]);
3960
+ this.runeCache.set(r, singleRune);
3961
+ }
3962
+ const f1 = this.rune(singleRune, re.flags);
3712
3963
  f = f === null ? f1 : this.cat(f, f1);
3713
3964
  }
3714
3965
  return f;
3715
3966
  }
3716
3967
  case Regexp.Op.CHAR_CLASS: return this.rune(re.runes, re.flags);
3717
- case Regexp.Op.ANY_CHAR_NOT_NL: return this.rune(Compiler.ANY_RUNE_NOT_NL(), 0);
3718
- case Regexp.Op.ANY_CHAR: return this.rune(Compiler.ANY_RUNE(), 0);
3968
+ case Regexp.Op.ANY_CHAR_NOT_NL: return this.rune(ANY_RUNE_NOT_NL, 0);
3969
+ case Regexp.Op.ANY_CHAR: return this.rune(ANY_RUNE, 0);
3719
3970
  case Regexp.Op.BEGIN_LINE: return this.empty(Utils.EMPTY_BEGIN_LINE);
3720
3971
  case Regexp.Op.END_LINE: return this.empty(Utils.EMPTY_END_LINE);
3721
3972
  case Regexp.Op.BEGIN_TEXT: return this.empty(Utils.EMPTY_BEGIN_TEXT);
3722
3973
  case Regexp.Op.END_TEXT: return this.empty(Utils.EMPTY_END_TEXT);
3723
3974
  case Regexp.Op.WORD_BOUNDARY: return this.empty(Utils.EMPTY_WORD_BOUNDARY);
3724
3975
  case Regexp.Op.NO_WORD_BOUNDARY: return this.empty(Utils.EMPTY_NO_WORD_BOUNDARY);
3976
+ case Regexp.Op.CONCAT:
3977
+ case Regexp.Op.ALTERNATE: return this.nop();
3978
+ default: throw new RE2JSCompileException("regexp: unhandled case in compile");
3979
+ }
3980
+ }
3981
+ enter(re) {
3982
+ switch (re.op) {
3983
+ case Regexp.Op.PLB:
3984
+ case Regexp.Op.NLB:
3985
+ case Regexp.Op.STAR:
3986
+ case Regexp.Op.PLUS:
3987
+ case Regexp.Op.QUEST: return { sub: null };
3988
+ case Regexp.Op.CAPTURE: return {
3989
+ bra: this.cap(re.cap << 1),
3990
+ sub: null
3991
+ };
3992
+ case Regexp.Op.CONCAT:
3993
+ case Regexp.Op.ALTERNATE: return { f: null };
3994
+ default: throw new RE2JSCompileException("regexp: unhandled case in compile");
3995
+ }
3996
+ }
3997
+ child(re, state, f1) {
3998
+ if (re.op === Regexp.Op.CONCAT) state.f = state.f === null ? f1 : this.cat(state.f, f1);
3999
+ else if (re.op === Regexp.Op.ALTERNATE) state.f = state.f === null ? f1 : this.alt(state.f, f1);
4000
+ else state.sub = f1;
4001
+ }
4002
+ leave(re, state) {
4003
+ switch (re.op) {
3725
4004
  case Regexp.Op.PLB:
3726
- case Regexp.Op.NLB: return this.lookBehind(this.compile(re.subs[0]), re.lb);
4005
+ case Regexp.Op.NLB: return this.lookBehind(state.sub, re.lb);
3727
4006
  case Regexp.Op.CAPTURE: {
3728
- const bra = this.cap(re.cap << 1);
3729
- const sub = this.compile(re.subs[0]);
3730
4007
  const ket = this.cap(re.cap << 1 | 1);
3731
- return this.cat(this.cat(bra, sub), ket);
3732
- }
3733
- case Regexp.Op.STAR: return this.star(this.compile(re.subs[0]), (re.flags & RE2Flags.NON_GREEDY) !== 0);
3734
- case Regexp.Op.PLUS: return this.plus(this.compile(re.subs[0]), (re.flags & RE2Flags.NON_GREEDY) !== 0);
3735
- case Regexp.Op.QUEST: return this.quest(this.compile(re.subs[0]), (re.flags & RE2Flags.NON_GREEDY) !== 0);
3736
- case Regexp.Op.CONCAT: if (re.subs.length === 0) return this.nop();
3737
- else {
3738
- let f = null;
3739
- for (let sub of re.subs) {
3740
- const f1 = this.compile(sub);
3741
- f = f === null ? f1 : this.cat(f, f1);
3742
- }
3743
- return f;
3744
- }
3745
- case Regexp.Op.ALTERNATE: if (re.subs.length === 0) return this.nop();
3746
- else {
3747
- let f = null;
3748
- for (let sub of re.subs) {
3749
- const f1 = this.compile(sub);
3750
- f = f === null ? f1 : this.alt(f, f1);
3751
- }
3752
- return f;
4008
+ return this.cat(this.cat(state.bra, state.sub), ket);
3753
4009
  }
4010
+ case Regexp.Op.STAR: return this.star(state.sub, (re.flags & RE2Flags.NON_GREEDY) !== 0);
4011
+ case Regexp.Op.PLUS: return this.plus(state.sub, (re.flags & RE2Flags.NON_GREEDY) !== 0);
4012
+ case Regexp.Op.QUEST: return this.quest(state.sub, (re.flags & RE2Flags.NON_GREEDY) !== 0);
4013
+ case Regexp.Op.CONCAT:
4014
+ case Regexp.Op.ALTERNATE: return state.f;
3754
4015
  default: throw new RE2JSCompileException("regexp: unhandled case in compile");
3755
4016
  }
3756
4017
  }
@@ -3760,111 +4021,149 @@
3760
4021
  var Simplify = class Simplify {
3761
4022
  static simplify(re) {
3762
4023
  if (re === null) return null;
4024
+ return RegexpWalker.walk(re, Simplify.WALKER);
4025
+ }
4026
+ static WALKER = {
4027
+ leaf: (re) => Simplify.leaf(re),
4028
+ enter: (re) => Simplify.enter(re),
4029
+ child: (re, state, result, i) => Simplify.child(re, state, result, i),
4030
+ leave: (re, state) => Simplify.leave(re, state)
4031
+ };
4032
+ static leaf(re) {
4033
+ if (re.op === Regexp.Op.CHAR_CLASS) {
4034
+ if (re.runes === null) return re;
4035
+ if (re.runes.length === 0) return new Regexp(Regexp.Op.NO_MATCH);
4036
+ if (re.runes.length === 2 && re.runes[0] === 0 && re.runes[1] === Unicode.MAX_RUNE) return new Regexp(Regexp.Op.ANY_CHAR);
4037
+ 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);
4038
+ }
4039
+ return re;
4040
+ }
4041
+ static enter(re) {
4042
+ switch (re.op) {
4043
+ case Regexp.Op.PLB:
4044
+ case Regexp.Op.NLB:
4045
+ case Regexp.Op.CAPTURE:
4046
+ case Regexp.Op.STAR:
4047
+ case Regexp.Op.PLUS:
4048
+ case Regexp.Op.QUEST: return { sub: null };
4049
+ case Regexp.Op.CONCAT:
4050
+ case Regexp.Op.ALTERNATE: return {
4051
+ subs: [],
4052
+ changed: false
4053
+ };
4054
+ case Regexp.Op.REPEAT:
4055
+ if (re.min === 0 && re.max === 0) return { result: new Regexp(Regexp.Op.EMPTY_MATCH) };
4056
+ return { sub: null };
4057
+ default: return { result: re };
4058
+ }
4059
+ }
4060
+ static child(re, state, nsub, i) {
4061
+ if (state.subs === void 0) {
4062
+ state.sub = nsub;
4063
+ return;
4064
+ }
4065
+ if (nsub !== re.subs[i]) state.changed = true;
4066
+ if (re.op === Regexp.Op.CONCAT) {
4067
+ if (nsub.op === Regexp.Op.NO_MATCH) {
4068
+ state.result = new Regexp(Regexp.Op.NO_MATCH);
4069
+ return;
4070
+ }
4071
+ if (nsub.op === Regexp.Op.EMPTY_MATCH) {
4072
+ state.changed = true;
4073
+ return;
4074
+ }
4075
+ if (nsub.op === Regexp.Op.CONCAT) {
4076
+ state.changed = true;
4077
+ for (let j = 0; j < nsub.subs.length; j++) state.subs.push(nsub.subs[j]);
4078
+ return;
4079
+ }
4080
+ } else {
4081
+ if (nsub.op === Regexp.Op.NO_MATCH) {
4082
+ state.changed = true;
4083
+ return;
4084
+ }
4085
+ if (nsub.op === Regexp.Op.ALTERNATE) {
4086
+ state.changed = true;
4087
+ for (let j = 0; j < nsub.subs.length; j++) state.subs.push(nsub.subs[j]);
4088
+ return;
4089
+ }
4090
+ }
4091
+ state.subs.push(nsub);
4092
+ }
4093
+ static leave(re, state) {
3763
4094
  switch (re.op) {
3764
4095
  case Regexp.Op.PLB:
3765
4096
  case Regexp.Op.NLB:
3766
4097
  case Regexp.Op.CAPTURE: {
3767
- const sub = Simplify.simplify(re.subs[0]);
4098
+ const sub = state.sub;
3768
4099
  if (sub !== re.subs[0]) {
3769
4100
  const nre = Regexp.fromRegexp(re);
3770
- nre.runes = [];
4101
+ nre.runes = /* @__PURE__ */ new Int32Array(0);
3771
4102
  nre.subs = [sub];
3772
4103
  return nre;
3773
4104
  }
3774
4105
  return re;
3775
4106
  }
3776
4107
  case Regexp.Op.CONCAT:
3777
- case Regexp.Op.ALTERNATE: {
3778
- const newSubs = [];
3779
- let changed = false;
3780
- for (let i = 0; i < re.subs.length; i++) {
3781
- const sub = re.subs[i];
3782
- const nsub = Simplify.simplify(sub);
3783
- if (nsub !== sub) changed = true;
3784
- if (re.op === Regexp.Op.CONCAT) {
3785
- if (nsub.op === Regexp.Op.NO_MATCH) return new Regexp(Regexp.Op.NO_MATCH);
3786
- if (nsub.op === Regexp.Op.EMPTY_MATCH) {
3787
- changed = true;
3788
- continue;
3789
- }
3790
- if (nsub.op === Regexp.Op.CONCAT) {
3791
- changed = true;
3792
- for (let j = 0; j < nsub.subs.length; j++) newSubs.push(nsub.subs[j]);
3793
- continue;
3794
- }
3795
- } else if (re.op === Regexp.Op.ALTERNATE) {
3796
- if (nsub.op === Regexp.Op.NO_MATCH) {
3797
- changed = true;
3798
- continue;
3799
- }
3800
- if (nsub.op === Regexp.Op.ALTERNATE) {
3801
- changed = true;
3802
- for (let j = 0; j < nsub.subs.length; j++) newSubs.push(nsub.subs[j]);
3803
- continue;
3804
- }
3805
- }
3806
- newSubs.push(nsub);
3807
- }
3808
- if (changed) {
4108
+ case Regexp.Op.ALTERNATE:
4109
+ if (state.changed) {
4110
+ const newSubs = state.subs;
3809
4111
  if (newSubs.length === 0) return new Regexp(re.op === Regexp.Op.CONCAT ? Regexp.Op.EMPTY_MATCH : Regexp.Op.NO_MATCH);
3810
4112
  if (newSubs.length === 1) return newSubs[0];
3811
4113
  const nre = Regexp.fromRegexp(re);
3812
- nre.runes = [];
4114
+ nre.runes = /* @__PURE__ */ new Int32Array(0);
3813
4115
  nre.subs = newSubs;
3814
4116
  return nre;
3815
4117
  }
3816
4118
  return re;
3817
- }
3818
- case Regexp.Op.CHAR_CLASS:
3819
- if (re.runes === null) return re;
3820
- if (re.runes.length === 0) return new Regexp(Regexp.Op.NO_MATCH);
3821
- if (re.runes.length === 2 && re.runes[0] === 0 && re.runes[1] === Unicode.MAX_RUNE) return new Regexp(Regexp.Op.ANY_CHAR);
3822
- 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);
3823
- return re;
3824
4119
  case Regexp.Op.STAR:
3825
4120
  case Regexp.Op.PLUS:
3826
- case Regexp.Op.QUEST: {
3827
- const sub = Simplify.simplify(re.subs[0]);
3828
- return Simplify.simplify1(re.op, re.flags, sub, re);
3829
- }
3830
- case Regexp.Op.REPEAT: {
3831
- if (re.min === 0 && re.max === 0) return new Regexp(Regexp.Op.EMPTY_MATCH);
3832
- const sub = Simplify.simplify(re.subs[0]);
3833
- if (re.max === -1) {
3834
- if (re.min === 0) return Simplify.simplify1(Regexp.Op.STAR, re.flags, sub, null);
3835
- if (re.min === 1) return Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null);
3836
- const nre = new Regexp(Regexp.Op.CONCAT);
3837
- const subs = [];
3838
- for (let i = 0; i < re.min - 1; i++) subs.push(sub);
3839
- subs.push(Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null));
3840
- nre.subs = subs.slice(0);
3841
- return Simplify.simplify(nre);
3842
- }
3843
- if (re.min === 1 && re.max === 1) return sub;
3844
- let prefixSubs = null;
3845
- if (re.min > 0) {
3846
- prefixSubs = [];
3847
- for (let i = 0; i < re.min; i++) prefixSubs.push(sub);
3848
- }
3849
- if (re.max > re.min) {
3850
- let suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, sub, null);
3851
- for (let i = re.min + 1; i < re.max; i++) {
3852
- const nre2 = new Regexp(Regexp.Op.CONCAT);
3853
- nre2.subs = [sub, suffix];
3854
- suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, nre2, null);
3855
- }
3856
- if (prefixSubs === null) return suffix;
3857
- prefixSubs.push(suffix);
3858
- }
3859
- if (prefixSubs !== null) {
3860
- const prefix = new Regexp(Regexp.Op.CONCAT);
3861
- prefix.subs = prefixSubs.slice(0);
3862
- return Simplify.simplify(prefix);
3863
- }
3864
- return new Regexp(Regexp.Op.NO_MATCH);
4121
+ case Regexp.Op.QUEST: return Simplify.simplify1(re.op, re.flags, state.sub, re);
4122
+ case Regexp.Op.REPEAT: return Simplify.simplifyRepeat(re, state.sub);
4123
+ default: return re;
4124
+ }
4125
+ }
4126
+ static simplifyRepeat(re, sub) {
4127
+ if (re.max === -1) {
4128
+ if (re.min === 0) return Simplify.simplify1(Regexp.Op.STAR, re.flags, sub, null);
4129
+ if (re.min === 1) return Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null);
4130
+ const subs = [];
4131
+ for (let i = 0; i < re.min - 1; i++) subs.push(sub);
4132
+ subs.push(Simplify.simplify1(Regexp.Op.PLUS, re.flags, sub, null));
4133
+ return Simplify.concat(subs);
4134
+ }
4135
+ if (re.min === 1 && re.max === 1) return sub;
4136
+ let prefixSubs = null;
4137
+ if (re.min > 0) {
4138
+ prefixSubs = [];
4139
+ for (let i = 0; i < re.min; i++) prefixSubs.push(sub);
4140
+ }
4141
+ if (re.max > re.min) {
4142
+ let suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, sub, null);
4143
+ for (let i = re.min + 1; i < re.max; i++) suffix = Simplify.simplify1(Regexp.Op.QUEST, re.flags, Simplify.concat([sub, suffix]), null);
4144
+ if (prefixSubs === null) return suffix;
4145
+ prefixSubs.push(suffix);
4146
+ }
4147
+ if (prefixSubs !== null) return Simplify.concat(prefixSubs);
4148
+ return new Regexp(Regexp.Op.NO_MATCH);
4149
+ }
4150
+ static concat(subs) {
4151
+ const newSubs = [];
4152
+ for (let i = 0; i < subs.length; i++) {
4153
+ const sub = subs[i];
4154
+ if (sub.op === Regexp.Op.NO_MATCH) return new Regexp(Regexp.Op.NO_MATCH);
4155
+ if (sub.op === Regexp.Op.EMPTY_MATCH) continue;
4156
+ if (sub.op === Regexp.Op.CONCAT) {
4157
+ for (let j = 0; j < sub.subs.length; j++) newSubs.push(sub.subs[j]);
4158
+ continue;
3865
4159
  }
4160
+ newSubs.push(sub);
3866
4161
  }
3867
- return re;
4162
+ if (newSubs.length === 0) return new Regexp(Regexp.Op.EMPTY_MATCH);
4163
+ if (newSubs.length === 1) return newSubs[0];
4164
+ const nre = new Regexp(Regexp.Op.CONCAT);
4165
+ nre.subs = newSubs;
4166
+ return nre;
3868
4167
  }
3869
4168
  static simplify1(op, flags, sub, re) {
3870
4169
  if (sub.op === Regexp.Op.EMPTY_MATCH) return sub;
@@ -4032,46 +4331,35 @@
4032
4331
  result += "]";
4033
4332
  return result;
4034
4333
  }
4035
- static cmp(array, i, pivotFrom, pivotTo) {
4036
- const cmp = array[i] - pivotFrom;
4037
- return cmp !== 0 ? cmp : pivotTo - array[i + 1];
4038
- }
4039
- static qsortIntPair(array, left, right) {
4040
- const pivotIndex = ((left + right) / 2 | 0) & -2;
4041
- const pivotFrom = array[pivotIndex];
4042
- const pivotTo = array[pivotIndex + 1];
4043
- let i = left;
4044
- let j = right;
4045
- while (i <= j) {
4046
- while (i < right && CharClass.cmp(array, i, pivotFrom, pivotTo) < 0) i += 2;
4047
- while (j > left && CharClass.cmp(array, j, pivotFrom, pivotTo) > 0) j -= 2;
4048
- if (i <= j) {
4049
- if (i !== j) {
4050
- let temp = array[i];
4051
- array[i] = array[j];
4052
- array[j] = temp;
4053
- temp = array[i + 1];
4054
- array[i + 1] = array[j + 1];
4055
- array[j + 1] = temp;
4056
- }
4057
- i += 2;
4058
- j -= 2;
4059
- }
4334
+ static sortRanges(array, len) {
4335
+ const n = len >> 1;
4336
+ const order = new Array(n);
4337
+ for (let i = 0; i < n; i++) order[i] = i;
4338
+ order.sort((a, b) => array[2 * a] - array[2 * b] || array[2 * b + 1] - array[2 * a + 1]);
4339
+ const sorted = array.slice(0, len);
4340
+ for (let i = 0; i < n; i++) {
4341
+ array[2 * i] = sorted[2 * order[i]];
4342
+ array[2 * i + 1] = sorted[2 * order[i] + 1];
4060
4343
  }
4061
- if (left < j) CharClass.qsortIntPair(array, left, j);
4062
- if (i < right) CharClass.qsortIntPair(array, i, right);
4063
4344
  }
4064
4345
  constructor(r = Utils.emptyInts()) {
4065
4346
  this.r = r;
4066
4347
  this.len = r.length;
4067
4348
  }
4068
4349
  toArray() {
4069
- if (this.len === this.r.length) return this.r;
4070
- else return this.r.slice(0, this.len);
4350
+ if (this.r instanceof Int32Array) return this.len === this.r.length ? this.r : this.r.subarray(0, this.len);
4351
+ return new Int32Array(this.len === this.r.length ? this.r : this.r.slice(0, this.len));
4352
+ }
4353
+ toGrowableArray() {
4354
+ if (Array.isArray(this.r)) {
4355
+ this.r.length = this.len;
4356
+ return this.r;
4357
+ }
4358
+ return Array.from(this.r.subarray(0, this.len));
4071
4359
  }
4072
4360
  cleanClass() {
4073
4361
  if (this.len < 4) return this;
4074
- CharClass.qsortIntPair(this.r, 0, this.len - 2);
4362
+ CharClass.sortRanges(this.r, this.len);
4075
4363
  let w = 2;
4076
4364
  for (let i = 2; i < this.len; i += 2) {
4077
4365
  const lo = this.r[i];
@@ -4275,7 +4563,7 @@
4275
4563
  static ERR_LARGE = "expression too large";
4276
4564
  static ERR_INVALID_CAPTURE_IN_LOOKBEHIND = "invalid capture in lookbehind";
4277
4565
  static MAX_HEIGHT = 1e3;
4278
- static MAX_SIZE = 3355443;
4566
+ static MAX_SIZE = 1398101;
4279
4567
  static MAX_RUNES = 33554432;
4280
4568
  static ANY_TABLE = new UnicodeRangeTable(new Uint32Array([
4281
4569
  0,
@@ -4417,15 +4705,17 @@
4417
4705
  case Regexp.Op.ANY_CHAR_NOT_NL:
4418
4706
  if (Parser.matchRune(src, Codepoint.CODES.get("\n"))) dst.op = Regexp.Op.ANY_CHAR;
4419
4707
  break;
4420
- case Regexp.Op.CHAR_CLASS:
4421
- if (src.op === Regexp.Op.LITERAL) dst.runes = new CharClass(dst.runes).appendLiteral(src.runes[0], src.flags).toArray();
4422
- else dst.runes = new CharClass(dst.runes).appendClass(src.runes).toArray();
4708
+ case Regexp.Op.CHAR_CLASS: {
4709
+ const cc = new CharClass(Array.isArray(dst.runes) ? dst.runes : Array.from(dst.runes));
4710
+ if (src.op === Regexp.Op.LITERAL) cc.appendLiteral(src.runes[0], src.flags);
4711
+ else cc.appendClass(src.runes);
4712
+ dst.runes = cc.toGrowableArray();
4423
4713
  break;
4714
+ }
4424
4715
  case Regexp.Op.LITERAL:
4425
4716
  if (src.runes[0] === dst.runes[0] && src.flags === dst.flags) break;
4426
4717
  dst.op = Regexp.Op.CHAR_CLASS;
4427
- dst.runes = new CharClass().appendLiteral(dst.runes[0], dst.flags).appendLiteral(src.runes[0], src.flags).toArray();
4428
- break;
4718
+ dst.runes = new CharClass().appendLiteral(dst.runes[0], dst.flags).appendLiteral(src.runes[0], src.flags).toGrowableArray();
4429
4719
  }
4430
4720
  }
4431
4721
  static parseEscape(t) {
@@ -4482,9 +4772,7 @@
4482
4772
  case Codepoint.CODES.get("r"): return Codepoint.CODES.get("\r");
4483
4773
  case Codepoint.CODES.get("t"): return Codepoint.CODES.get(" ");
4484
4774
  case Codepoint.CODES.get("v"): return Codepoint.CODES.get("\v");
4485
- default:
4486
- if (c <= Unicode.MAX_ASCII && !Utils.isalnum(c)) return c;
4487
- break;
4775
+ default: if (c <= Unicode.MAX_ASCII && !Utils.isalnum(c)) return c;
4488
4776
  }
4489
4777
  throw new RE2JSSyntaxException(Parser.ERR_INVALID_ESCAPE, t.from(startPos));
4490
4778
  }
@@ -4493,10 +4781,6 @@
4493
4781
  if (t.lookingAt("\\")) return Parser.parseEscape(t);
4494
4782
  return t.pop();
4495
4783
  }
4496
- static concatRunes(x, y) {
4497
- for (let i = 0; i < y.length; i++) x.push(y[i]);
4498
- return x;
4499
- }
4500
4784
  static hasCapture(re) {
4501
4785
  if (re === null) return false;
4502
4786
  if (re.op === Regexp.Op.CAPTURE) return true;
@@ -4627,9 +4911,7 @@
4627
4911
  const n = this.stack.length;
4628
4912
  let i = n;
4629
4913
  while (i > 0 && !Regexp.isPseudoOp(this.stack[i - 1].op)) i--;
4630
- const r = this.stack.slice(i, n);
4631
- this.stack = this.stack.slice(0, i);
4632
- return r;
4914
+ return this.stack.splice(i, n - i);
4633
4915
  }
4634
4916
  push(re) {
4635
4917
  this.numRunes += re.runes.length;
@@ -4654,7 +4936,8 @@
4654
4936
  const re1 = this.stack[n - 1];
4655
4937
  const re2 = this.stack[n - 2];
4656
4938
  if (re1.op !== Regexp.Op.LITERAL || re2.op !== Regexp.Op.LITERAL || (re1.flags & RE2Flags.FOLD_CASE) !== (re2.flags & RE2Flags.FOLD_CASE)) return false;
4657
- re2.runes = Parser.concatRunes(re2.runes, re1.runes);
4939
+ if (!Array.isArray(re2.runes)) re2.runes = Array.from(re2.runes);
4940
+ for (let i = 0; i < re1.runes.length; i++) re2.runes.push(re1.runes[i]);
4658
4941
  if (r >= 0) {
4659
4942
  re1.runes = [r];
4660
4943
  re1.flags = flags;
@@ -4726,12 +5009,12 @@
4726
5009
  }
4727
5010
  cleanAlt(re) {
4728
5011
  if (re.op === Regexp.Op.CHAR_CLASS) {
4729
- re.runes = new CharClass(re.runes).cleanClass().toArray();
5012
+ re.runes = new CharClass(Array.from(re.runes)).cleanClass().toArray();
4730
5013
  if (re.runes.length === 2 && re.runes[0] === 0 && re.runes[1] === Unicode.MAX_RUNE) {
4731
- re.runes = [];
5014
+ re.runes = /* @__PURE__ */ new Int32Array(0);
4732
5015
  re.op = Regexp.Op.ANY_CHAR;
4733
5016
  } 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) {
4734
- re.runes = [];
5017
+ re.runes = /* @__PURE__ */ new Int32Array(0);
4735
5018
  re.op = Regexp.Op.ANY_CHAR_NOT_NL;
4736
5019
  }
4737
5020
  }
@@ -4891,9 +5174,7 @@
4891
5174
  this.reuse(old);
4892
5175
  break;
4893
5176
  }
4894
- default:
4895
- re.subs = re.subs.slice(1, re.subs.length);
4896
- break;
5177
+ default: re.subs = re.subs.slice(1, re.subs.length);
4897
5178
  }
4898
5179
  }
4899
5180
  return re;
@@ -4992,9 +5273,7 @@
4992
5273
  case Codepoint.CODES.get("+"):
4993
5274
  op = Regexp.Op.PLUS;
4994
5275
  break;
4995
- case Codepoint.CODES.get("?"):
4996
- op = Regexp.Op.QUEST;
4997
- break;
5276
+ case Codepoint.CODES.get("?"): op = Regexp.Op.QUEST;
4998
5277
  }
4999
5278
  this.repeat(op, min, max, repeatPos, t, lastRepeatPos);
5000
5279
  break;
@@ -5045,9 +5324,7 @@
5045
5324
  case Codepoint.CODES.get("z"):
5046
5325
  this.op(Regexp.Op.END_TEXT);
5047
5326
  break bigswitch;
5048
- default:
5049
- t.rewindTo(savedPos);
5050
- break;
5327
+ default: t.rewindTo(savedPos);
5051
5328
  }
5052
5329
  else t.rewindTo(savedPos);
5053
5330
  const re = this.newRegexp(Regexp.Op.CHAR_CLASS);
@@ -5071,9 +5348,7 @@
5071
5348
  this.literal(Parser.parseEscape(t));
5072
5349
  break;
5073
5350
  }
5074
- default:
5075
- this.literal(t.pop());
5076
- break;
5351
+ default: this.literal(t.pop());
5077
5352
  }
5078
5353
  lastRepeatPos = repeatPos;
5079
5354
  }
@@ -5271,7 +5546,7 @@
5271
5546
  if ((this.flags & RE2Flags.FOLD_CASE) === 0 || fold === null) cc.appendTableWithSign(tab, sign);
5272
5547
  else {
5273
5548
  const tmp = new CharClass().appendTable(tab).appendTable(fold).cleanClass().toArray();
5274
- cc.appendClassWithSign(tmp, sign);
5549
+ cc.appendClassWithSign(Array.from(tmp), sign);
5275
5550
  }
5276
5551
  return true;
5277
5552
  }
@@ -5449,7 +5724,10 @@
5449
5724
  }
5450
5725
  return [];
5451
5726
  }
5452
- executeEngine(input, pos, anchor, ncap) {
5727
+ createLookbehindState() {
5728
+ return this.prog.numLb > 0 ? new LookbehindState(this.prog) : null;
5729
+ }
5730
+ executeEngine(input, pos, anchor, ncap, lbState = null) {
5453
5731
  if (this.prefixComplete && (ncap === 0 || this.numSubexp === 0)) return this.matchPrefixComplete(input, pos, anchor, ncap);
5454
5732
  if (this.prefilter !== null && anchor === RE2Flags.UNANCHORED) {
5455
5733
  if (!this.prefilter.eval(input, pos)) return null;
@@ -5457,14 +5735,14 @@
5457
5735
  if (this.onepass !== null) return OnePass.execute(this, input, pos, anchor, ncap);
5458
5736
  if (ncap > 0) {
5459
5737
  if (this.prog.numLb === 0 && input.endPos() <= Backtracker.maxBitStateLen(this.prog)) return Backtracker.execute(this, input, pos, anchor, ncap);
5460
- return this.doExecuteNFA(input, pos, anchor, ncap);
5738
+ return this.doExecuteNFA(input, pos, anchor, ncap, lbState);
5461
5739
  }
5462
5740
  if (this.prog.numLb === 0) {
5463
5741
  const dfaResult = this.dfa.match(input, pos, anchor);
5464
5742
  if (dfaResult !== null) return dfaResult ? [] : null;
5465
5743
  if (input.endPos() <= Backtracker.maxBitStateLen(this.prog)) return Backtracker.execute(this, input, pos, anchor, ncap);
5466
5744
  }
5467
- return this.doExecuteNFA(input, pos, anchor, ncap);
5745
+ return this.doExecuteNFA(input, pos, anchor, ncap, lbState);
5468
5746
  }
5469
5747
  /**
5470
5748
  * Returns the number of parenthesized subexpressions in this regular expression.
@@ -5490,11 +5768,11 @@
5490
5768
  toString() {
5491
5769
  return this.expr;
5492
5770
  }
5493
- doExecuteNFA(input, pos, anchor, ncap) {
5771
+ doExecuteNFA(input, pos, anchor, ncap, lbState = null) {
5494
5772
  let m = this.get();
5495
5773
  if (!m) m = Machine.fromRE2(this);
5496
5774
  m.init(ncap);
5497
- const cap = m.match(input, pos, anchor) ? m.submatches() : null;
5775
+ const cap = m.match(input, pos, anchor, lbState) ? m.submatches() : null;
5498
5776
  this.put(m);
5499
5777
  return cap;
5500
5778
  }
@@ -5513,17 +5791,21 @@
5513
5791
  * @param anchor the anchoring flag (UNANCHORED, ANCHOR_START, ANCHOR_BOTH)
5514
5792
  * @param group the array to fill with submatch positions
5515
5793
  * @param ngroup the number of array pairs to fill in
5794
+ * @param lbState optional state carried between successive matches over the same input (see
5795
+ * createLookbehindState); omit it to evaluate lookbehinds from the beginning of the input
5516
5796
  * @returns true if a match was found
5517
5797
  */
5518
- matchWithGroup(input, start, end, anchor, ngroup) {
5519
- if (!(input instanceof MatcherInputBase)) if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
5520
- else input = MatcherInput.utf16(input);
5521
- return this.matchMachineInput(input, start, end, anchor, ngroup);
5798
+ matchWithGroup(input, start, end, anchor, ngroup, lbState = null) {
5799
+ if (!(input instanceof MatcherInputBase)) {
5800
+ if (Utils.isByteArray(input)) input = MatcherInput.utf8(input);
5801
+ else input = MatcherInput.utf16(input);
5802
+ }
5803
+ return this.matchMachineInput(input, start, end, anchor, ngroup, lbState);
5522
5804
  }
5523
- matchMachineInput(input, start, end, anchor, ngroup) {
5805
+ matchMachineInput(input, start, end, anchor, ngroup, lbState = null) {
5524
5806
  if (start > end) return [false, null];
5525
5807
  const machineInput = input.isUTF16Encoding() ? MachineInput.fromUTF16(input.asCharSequence(), 0, end) : MachineInput.fromUTF8(input.asBytes(), 0, end);
5526
- const groupMatch = this.executeEngine(machineInput, start, anchor, 2 * ngroup);
5808
+ const groupMatch = this.executeEngine(machineInput, start, anchor, 2 * ngroup, lbState);
5527
5809
  if (groupMatch === null) return [false, null];
5528
5810
  return [true, groupMatch];
5529
5811
  }
@@ -5560,9 +5842,10 @@
5560
5842
  let searchPos = 0;
5561
5843
  let out = "";
5562
5844
  const input = MachineInput.fromUTF16(src);
5845
+ const lbState = this.createLookbehindState();
5563
5846
  let numReplaces = 0;
5564
5847
  while (searchPos <= src.length) {
5565
- const a = this.executeEngine(input, searchPos, RE2Flags.UNANCHORED, 2);
5848
+ const a = this.executeEngine(input, searchPos, RE2Flags.UNANCHORED, 2, lbState);
5566
5849
  if (a === null || a.length === 0) break;
5567
5850
  out += src.substring(lastMatchEnd, a[0]);
5568
5851
  if (a[1] > lastMatchEnd || a[0] === 0) {
@@ -5596,8 +5879,9 @@
5596
5879
  let pos = 0;
5597
5880
  let i = 0;
5598
5881
  let prevMatchEnd = -1;
5882
+ const lbState = this.createLookbehindState();
5599
5883
  while (i < n && pos <= end) {
5600
- const matches = this.executeEngine(input, pos, RE2Flags.UNANCHORED, this.prog.numCap);
5884
+ const matches = this.executeEngine(input, pos, RE2Flags.UNANCHORED, this.prog.numCap, lbState);
5601
5885
  if (matches === null || matches.length === 0) break;
5602
5886
  let accept = true;
5603
5887
  if (matches[1] === pos) {
@@ -5867,7 +6151,9 @@
5867
6151
  */
5868
6152
  compile() {
5869
6153
  if (this.prog) return;
5870
- this.prog = Compiler.compileSet(this.regexps);
6154
+ const prog = Compiler.compileSet(this.regexps);
6155
+ if (prog.numInst() > Parser.MAX_SIZE) throw new RE2JSCompileException(`${Parser.ERR_LARGE}: the set compiles to ${prog.numInst()} instructions, more than ${Parser.MAX_SIZE}`);
6156
+ this.prog = prog;
5871
6157
  this.dfa = new DFA(this.prog, this.maxMem);
5872
6158
  this.dummyRe2 = {
5873
6159
  prog: this.prog,