re2js 2.8.5 → 2.8.7

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