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