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