re2js 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,7 +100,9 @@ RE2JS.LONGEST_MATCH
100
100
 
101
101
  ### Checking for Matches
102
102
 
103
- RE2JS allows you to check if a string matches a given regex pattern using the `matches()` function
103
+ RE2JS allows you to check if a string matches a given regex pattern using the `matches()` function.
104
+
105
+ ***Performance Note:** The `matches()` method is highly optimized. It performs a strict anchored check and runs directly on the high-speed DFA (Deterministic Finite Automaton) engine without tracking capture groups or instantiating a stateful `Matcher` object.*
104
106
 
105
107
  ```js
106
108
  import { RE2JS } from 're2js'
@@ -161,6 +163,42 @@ matchString.group() // 'e'
161
163
  matchString.find(7) // false
162
164
  ```
163
165
 
166
+ ### High-Performance Boolean Testing
167
+
168
+ If you only need to know **whether** a string matches a pattern (without extracting capture groups), you should use the `test()`, `testExact()`, or `matches()` methods. Unlike `.matcher()`, these methods do not instantiate stateful `Matcher` objects and request exactly `0` capture groups. This guarantees that execution is securely routed to the high-speed DFA (Deterministic Finite Automaton) engine whenever possible in linear `O(n)` time
169
+
170
+ #### `test(input)`
171
+
172
+ Tests if the regular expression matches **any part** of the provided input (unanchored). This method mirrors the standard JavaScript `RegExp.prototype.test()` API
173
+
174
+ ```js
175
+ import { RE2JS } from 're2js';
176
+
177
+ // Compile once, reuse often
178
+ const re = RE2JS.compile('error|warning|critical');
179
+
180
+ // Extremely fast, unanchored DFA search
181
+ if (re.test('The system encountered a critical failure')) {
182
+ console.log('Log needs attention!');
183
+ }
184
+ ```
185
+
186
+ #### `testExact(input)`
187
+
188
+ Tests if the regular expression matches the entire input string (anchored to both start and end).
189
+
190
+ *Note: `RE2JS.matches()` delegates to this method, so they provide the exact same performance and behavior.*
191
+
192
+ ```js
193
+ import { RE2JS } from 're2js';
194
+
195
+ const isHex = RE2JS.compile('[0-9A-Fa-f]+');
196
+
197
+ // Fast, anchored DFA validation
198
+ console.log(isHex.testExact('1A4F')); // true
199
+ console.log(isHex.testExact('1A4F-xyz')); // false
200
+ ```
201
+
164
202
  ### Checking Initial Match
165
203
 
166
204
  The `lookingAt()` method determines whether the start of the given string matches the pattern
@@ -2,7 +2,7 @@
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 v1.3.0
5
+ * @version v1.3.2
6
6
  * @author Alexey Vasiliev
7
7
  * @homepage https://github.com/le0pard/re2js#readme
8
8
  * @repository github:le0pard/re2js
@@ -843,6 +843,181 @@ class MatcherInput {
843
843
  }
844
844
  }
845
845
 
846
+ /**
847
+ * MachineInput abstracts different representations of the input text supplied to the Machine. It
848
+ * provides one-character lookahead.
849
+ */
850
+ class MachineInputBase {
851
+ static EOF() {
852
+ return -1 << 3;
853
+ }
854
+
855
+ // can we look ahead without losing info?
856
+ canCheckPrefix() {
857
+ return true;
858
+ }
859
+
860
+ // Returns the end position in the same units as step().
861
+ endPos() {
862
+ return this.end;
863
+ }
864
+ }
865
+
866
+ // An implementation of MachineInput for UTF-8 byte arrays.
867
+ // |pos| and |width| are byte indices.
868
+ class MachineUTF8Input extends MachineInputBase {
869
+ constructor(bytes, start = 0, end = bytes.length) {
870
+ super();
871
+ this.bytes = bytes;
872
+ this.start = start;
873
+ this.end = end;
874
+ }
875
+
876
+ // Returns the rune at the specified index; the units are
877
+ // unspecified, but could be UTF-8 byte, UTF-16 char, or rune
878
+ // indices. Returns the width (in the same units) of the rune in
879
+ // the lower 3 bits, and the rune (Unicode code point) in the high
880
+ // bits. Never negative, except for EOF which is represented as -1
881
+ // << 3 | 0.
882
+ step(i) {
883
+ i += this.start;
884
+ if (i >= this.end) {
885
+ return MachineInputBase.EOF();
886
+ }
887
+ let x = this.bytes[i++] & 255;
888
+ if ((x & 128) === 0) {
889
+ return x << 3 | 1;
890
+ } else if ((x & 224) === 192) {
891
+ x = x & 31;
892
+ if (i >= this.end) {
893
+ return MachineInputBase.EOF();
894
+ }
895
+ x = x << 6 | this.bytes[i++] & 63;
896
+ return x << 3 | 2;
897
+ } else if ((x & 240) === 224) {
898
+ x = x & 15;
899
+ if (i + 1 >= this.end) {
900
+ return MachineInputBase.EOF();
901
+ }
902
+ x = x << 6 | this.bytes[i++] & 63;
903
+ x = x << 6 | this.bytes[i++] & 63;
904
+ return x << 3 | 3;
905
+ } else {
906
+ x = x & 7;
907
+ if (i + 2 >= this.end) {
908
+ return MachineInputBase.EOF();
909
+ }
910
+ x = x << 6 | this.bytes[i++] & 63;
911
+ x = x << 6 | this.bytes[i++] & 63;
912
+ x = x << 6 | this.bytes[i++] & 63;
913
+ return x << 3 | 4;
914
+ }
915
+ }
916
+
917
+ // Returns the index relative to |pos| at which |re2.prefix| is found
918
+ // in this input stream, or a negative value if not found.
919
+ index(re2, pos) {
920
+ pos += this.start;
921
+ const i = this.indexOf(this.bytes, re2.prefixUTF8, pos);
922
+ return i < 0 ? i : i - pos;
923
+ }
924
+
925
+ // Returns a bitmask of EMPTY_* flags.
926
+ context(pos) {
927
+ pos += this.start;
928
+ let r1 = -1;
929
+ if (pos > this.start && pos <= this.end) {
930
+ let start = pos - 1;
931
+ r1 = this.bytes[start--];
932
+ if (r1 >= 128) {
933
+ let lim = pos - 4;
934
+ if (lim < this.start) {
935
+ lim = this.start;
936
+ }
937
+ while (start >= lim && (this.bytes[start] & 192) === 128) {
938
+ start--;
939
+ }
940
+ if (start < this.start) {
941
+ start = this.start;
942
+ }
943
+ r1 = this.step(start) >> 3;
944
+ }
945
+ }
946
+ const r2 = pos < this.end ? this.step(pos) >> 3 : -1;
947
+ return Utils.emptyOpContext(r1, r2);
948
+ }
949
+
950
+ // Returns the index of the first occurrence of array |target| within
951
+ // array |source| after |fromIndex|, or -1 if not found.
952
+ indexOf(source, target, fromIndex = 0) {
953
+ let targetLength = target.length;
954
+ if (targetLength === 0) {
955
+ return -1;
956
+ }
957
+ let sourceLength = source.length;
958
+ for (let i = fromIndex; i <= sourceLength - targetLength; i++) {
959
+ for (let j = 0; j < targetLength; j++) {
960
+ if (source[i + j] !== target[j]) {
961
+ break;
962
+ } else if (j === targetLength - 1) {
963
+ return i;
964
+ }
965
+ }
966
+ }
967
+ return -1;
968
+ }
969
+ }
970
+
971
+ // |pos| and |width| are in JS "char" units.
972
+ class MachineUTF16Input extends MachineInputBase {
973
+ constructor(charSequence, start = 0, end = charSequence.length) {
974
+ super();
975
+ this.charSequence = charSequence;
976
+ this.start = start;
977
+ this.end = end;
978
+ }
979
+
980
+ // Returns the rune at the specified index; the units are
981
+ // unspecified, but could be UTF-8 byte, UTF-16 char, or rune
982
+ // indices. Returns the width (in the same units) of the rune in
983
+ // the lower 3 bits, and the rune (Unicode code point) in the high
984
+ // bits. Never negative, except for EOF which is represented as -1
985
+ // << 3 | 0.
986
+ step(pos) {
987
+ pos += this.start;
988
+ if (pos < this.end) {
989
+ const rune = this.charSequence.codePointAt(pos);
990
+ return rune << 3 | Utils.charCount(rune);
991
+ } else {
992
+ return MachineInputBase.EOF();
993
+ }
994
+ }
995
+
996
+ // Returns the index relative to |pos| at which |re2.prefix| is found
997
+ // in this input stream, or a negative value if not found.
998
+ index(re2, pos) {
999
+ pos += this.start;
1000
+ const i = this.charSequence.indexOf(re2.prefix, pos);
1001
+ return i < 0 ? i : i - pos;
1002
+ }
1003
+
1004
+ // Returns a bitmask of EMPTY_* flags.
1005
+ context(pos) {
1006
+ pos += this.start;
1007
+ const r1 = pos > 0 && pos <= this.charSequence.length ? this.charSequence.codePointAt(pos - 1) : -1;
1008
+ const r2 = pos < this.charSequence.length ? this.charSequence.codePointAt(pos) : -1;
1009
+ return Utils.emptyOpContext(r1, r2);
1010
+ }
1011
+ }
1012
+ class MachineInput {
1013
+ static fromUTF8(bytes, start = 0, end = bytes.length) {
1014
+ return new MachineUTF8Input(bytes, start, end);
1015
+ }
1016
+ static fromUTF16(charSequence, start = 0, end = charSequence.length) {
1017
+ return new MachineUTF16Input(charSequence, start, end);
1018
+ }
1019
+ }
1020
+
846
1021
  class RE2JSException extends Error {
847
1022
  /** @param {string} message */
848
1023
  constructor(message) {
@@ -923,17 +1098,6 @@ class RE2JSFlagsException extends RE2JSException {
923
1098
  }
924
1099
  }
925
1100
 
926
- /**
927
- * An exception thrown by DFA
928
- */
929
- class RE2JSDfaMemoryException extends RE2JSException {
930
- /** @param {string} message */
931
- constructor(message) {
932
- super(message);
933
- this.name = 'RE2JSDfaMemoryException';
934
- }
935
- }
936
-
937
1101
  /**
938
1102
  * A stateful iterator that interprets a regex {@code RE2JS} on a specific input.
939
1103
  *
@@ -1490,181 +1654,6 @@ class Matcher {
1490
1654
  }
1491
1655
  }
1492
1656
 
1493
- /**
1494
- * MachineInput abstracts different representations of the input text supplied to the Machine. It
1495
- * provides one-character lookahead.
1496
- */
1497
- class MachineInputBase {
1498
- static EOF() {
1499
- return -1 << 3;
1500
- }
1501
-
1502
- // can we look ahead without losing info?
1503
- canCheckPrefix() {
1504
- return true;
1505
- }
1506
-
1507
- // Returns the end position in the same units as step().
1508
- endPos() {
1509
- return this.end;
1510
- }
1511
- }
1512
-
1513
- // An implementation of MachineInput for UTF-8 byte arrays.
1514
- // |pos| and |width| are byte indices.
1515
- class MachineUTF8Input extends MachineInputBase {
1516
- constructor(bytes, start = 0, end = bytes.length) {
1517
- super();
1518
- this.bytes = bytes;
1519
- this.start = start;
1520
- this.end = end;
1521
- }
1522
-
1523
- // Returns the rune at the specified index; the units are
1524
- // unspecified, but could be UTF-8 byte, UTF-16 char, or rune
1525
- // indices. Returns the width (in the same units) of the rune in
1526
- // the lower 3 bits, and the rune (Unicode code point) in the high
1527
- // bits. Never negative, except for EOF which is represented as -1
1528
- // << 3 | 0.
1529
- step(i) {
1530
- i += this.start;
1531
- if (i >= this.end) {
1532
- return MachineInputBase.EOF();
1533
- }
1534
- let x = this.bytes[i++] & 255;
1535
- if ((x & 128) === 0) {
1536
- return x << 3 | 1;
1537
- } else if ((x & 224) === 192) {
1538
- x = x & 31;
1539
- if (i >= this.end) {
1540
- return MachineInputBase.EOF();
1541
- }
1542
- x = x << 6 | this.bytes[i++] & 63;
1543
- return x << 3 | 2;
1544
- } else if ((x & 240) === 224) {
1545
- x = x & 15;
1546
- if (i + 1 >= this.end) {
1547
- return MachineInputBase.EOF();
1548
- }
1549
- x = x << 6 | this.bytes[i++] & 63;
1550
- x = x << 6 | this.bytes[i++] & 63;
1551
- return x << 3 | 3;
1552
- } else {
1553
- x = x & 7;
1554
- if (i + 2 >= this.end) {
1555
- return MachineInputBase.EOF();
1556
- }
1557
- x = x << 6 | this.bytes[i++] & 63;
1558
- x = x << 6 | this.bytes[i++] & 63;
1559
- x = x << 6 | this.bytes[i++] & 63;
1560
- return x << 3 | 4;
1561
- }
1562
- }
1563
-
1564
- // Returns the index relative to |pos| at which |re2.prefix| is found
1565
- // in this input stream, or a negative value if not found.
1566
- index(re2, pos) {
1567
- pos += this.start;
1568
- const i = this.indexOf(this.bytes, re2.prefixUTF8, pos);
1569
- return i < 0 ? i : i - pos;
1570
- }
1571
-
1572
- // Returns a bitmask of EMPTY_* flags.
1573
- context(pos) {
1574
- pos += this.start;
1575
- let r1 = -1;
1576
- if (pos > this.start && pos <= this.end) {
1577
- let start = pos - 1;
1578
- r1 = this.bytes[start--];
1579
- if (r1 >= 128) {
1580
- let lim = pos - 4;
1581
- if (lim < this.start) {
1582
- lim = this.start;
1583
- }
1584
- while (start >= lim && (this.bytes[start] & 192) === 128) {
1585
- start--;
1586
- }
1587
- if (start < this.start) {
1588
- start = this.start;
1589
- }
1590
- r1 = this.step(start) >> 3;
1591
- }
1592
- }
1593
- const r2 = pos < this.end ? this.step(pos) >> 3 : -1;
1594
- return Utils.emptyOpContext(r1, r2);
1595
- }
1596
-
1597
- // Returns the index of the first occurrence of array |target| within
1598
- // array |source| after |fromIndex|, or -1 if not found.
1599
- indexOf(source, target, fromIndex = 0) {
1600
- let targetLength = target.length;
1601
- if (targetLength === 0) {
1602
- return -1;
1603
- }
1604
- let sourceLength = source.length;
1605
- for (let i = fromIndex; i <= sourceLength - targetLength; i++) {
1606
- for (let j = 0; j < targetLength; j++) {
1607
- if (source[i + j] !== target[j]) {
1608
- break;
1609
- } else if (j === targetLength - 1) {
1610
- return i;
1611
- }
1612
- }
1613
- }
1614
- return -1;
1615
- }
1616
- }
1617
-
1618
- // |pos| and |width| are in JS "char" units.
1619
- class MachineUTF16Input extends MachineInputBase {
1620
- constructor(charSequence, start = 0, end = charSequence.length) {
1621
- super();
1622
- this.charSequence = charSequence;
1623
- this.start = start;
1624
- this.end = end;
1625
- }
1626
-
1627
- // Returns the rune at the specified index; the units are
1628
- // unspecified, but could be UTF-8 byte, UTF-16 char, or rune
1629
- // indices. Returns the width (in the same units) of the rune in
1630
- // the lower 3 bits, and the rune (Unicode code point) in the high
1631
- // bits. Never negative, except for EOF which is represented as -1
1632
- // << 3 | 0.
1633
- step(pos) {
1634
- pos += this.start;
1635
- if (pos < this.end) {
1636
- const rune = this.charSequence.codePointAt(pos);
1637
- return rune << 3 | Utils.charCount(rune);
1638
- } else {
1639
- return MachineInputBase.EOF();
1640
- }
1641
- }
1642
-
1643
- // Returns the index relative to |pos| at which |re2.prefix| is found
1644
- // in this input stream, or a negative value if not found.
1645
- index(re2, pos) {
1646
- pos += this.start;
1647
- const i = this.charSequence.indexOf(re2.prefix, pos);
1648
- return i < 0 ? i : i - pos;
1649
- }
1650
-
1651
- // Returns a bitmask of EMPTY_* flags.
1652
- context(pos) {
1653
- pos += this.start;
1654
- const r1 = pos > 0 && pos <= this.charSequence.length ? this.charSequence.codePointAt(pos - 1) : -1;
1655
- const r2 = pos < this.charSequence.length ? this.charSequence.codePointAt(pos) : -1;
1656
- return Utils.emptyOpContext(r1, r2);
1657
- }
1658
- }
1659
- class MachineInput {
1660
- static fromUTF8(bytes, start = 0, end = bytes.length) {
1661
- return new MachineUTF8Input(bytes, start, end);
1662
- }
1663
- static fromUTF16(charSequence, start = 0, end = charSequence.length) {
1664
- return new MachineUTF16Input(charSequence, start, end);
1665
- }
1666
- }
1667
-
1668
1657
  /**
1669
1658
  * A single instruction in the regular expression virtual machine.
1670
1659
  *
@@ -2123,21 +2112,40 @@ class Machine {
2123
2112
  }
2124
2113
  }
2125
2114
 
2115
+ // FNV-1a 32-bit hash for an array of integers.
2116
+ // Extremely fast, allocates no memory, and produces good distribution.
2117
+ const hashPCs = pcs => {
2118
+ let h = -2128831035; // 0x811c9dc5 (32-bit signed offset basis)
2119
+ for (let i = 0; i < pcs.length; i++) {
2120
+ h ^= pcs[i];
2121
+ h = Math.imul(h, 16777619); // 0x01000193 (FNV prime)
2122
+ }
2123
+ return h;
2124
+ };
2125
+
2126
+ // Zero-allocation array comparison for hash collision resolution
2127
+ const arraysEqual = (a, b) => {
2128
+ if (a.length !== b.length) return false;
2129
+ for (let i = 0; i < a.length; i++) {
2130
+ if (a[i] !== b[i]) return false;
2131
+ }
2132
+ return true;
2133
+ };
2126
2134
  class DFAState {
2127
- constructor(id, nfaStates, isMatch) {
2128
- this.id = id; // Stringified NFA state list (e.g., "1,4,7")
2129
- this.nfaStates = nfaStates; // Array of Instruction PCs
2135
+ constructor(nfaStates, isMatch) {
2136
+ this.nfaStates = nfaStates; // Int32Array of Instruction PCs
2130
2137
  this.isMatch = isMatch; // Boolean
2131
- this.nextAscii = new Array(Unicode.MAX_ASCII + 1).fill(null); // Flat array for blisteringly fast ASCII lookups (unanchored)
2138
+ this.nextAscii = new Array(Unicode.MAX_ASCII + 1).fill(null); // Flat array for blisteringly fast ASCII lookups
2132
2139
  this.nextMap = new Map(); // Cache of Char -> DFAState
2133
2140
  }
2134
2141
  }
2135
2142
  class DFA {
2136
2143
  constructor(prog) {
2137
2144
  this.prog = prog;
2138
- this.stateCache = new Map(); // id -> DFAState
2145
+ this.stateCache = new Map(); // hash(number) -> DFAState[]
2146
+ this.stateCount = 0; // Tracks total states for memory limits
2139
2147
  this.startState = null;
2140
- this.stateLimit = 10000; // Prevent memory explosion (ReDoS protection), like RE2 max_mem
2148
+ this.stateLimit = 10000; // Prevent memory explosion (ReDoS protection)
2141
2149
  }
2142
2150
 
2143
2151
  // Follows epsilon (empty) transitions to find all reachable states without consuming a char
@@ -2181,17 +2189,37 @@ class DFA {
2181
2189
  const closureResult = this.computeClosure(pcs);
2182
2190
  if (!closureResult) return null; // Bailout to NFA required
2183
2191
 
2184
- const id = closureResult.pcs.join(',');
2185
- if (this.stateCache.has(id)) {
2186
- return this.stateCache.get(id);
2192
+ const sortedPCs = closureResult.pcs;
2193
+ const hash = hashPCs(sortedPCs);
2194
+
2195
+ // Lookup hash bucket
2196
+ let bucket = this.stateCache.get(hash);
2197
+ if (bucket) {
2198
+ // Resolve potential hash collisions
2199
+ for (let i = 0; i < bucket.length; i++) {
2200
+ const state = bucket[i];
2201
+ if (arraysEqual(state.nfaStates, sortedPCs)) {
2202
+ return state;
2203
+ }
2204
+ }
2205
+ } else {
2206
+ bucket = [];
2207
+ this.stateCache.set(hash, bucket);
2187
2208
  }
2188
2209
 
2189
2210
  // Safety: prevent memory exhaustion from state explosion
2190
- if (this.stateCache.size > this.stateLimit) {
2191
- throw new RE2JSDfaMemoryException('dfa error: Out of memory exception');
2211
+ // We flush the cache and return null, which seamlessly routes execution to the NFA
2212
+ if (this.stateCount >= this.stateLimit) {
2213
+ this.stateCache.clear();
2214
+ this.stateCount = 0;
2215
+ this.startState = null;
2216
+ return null;
2192
2217
  }
2193
- const state = new DFAState(id, closureResult.pcs, closureResult.isMatch);
2194
- this.stateCache.set(id, state);
2218
+
2219
+ // State not found, create it and add to bucket
2220
+ const state = new DFAState(sortedPCs, closureResult.isMatch);
2221
+ bucket.push(state);
2222
+ this.stateCount++;
2195
2223
  return state;
2196
2224
  }
2197
2225
 
@@ -2255,6 +2283,11 @@ class DFA {
2255
2283
  const r = input.step(i);
2256
2284
  const rune = r >> 3;
2257
2285
  const width = r & 7;
2286
+
2287
+ // prevent infinite loop on EOF
2288
+ if (width === 0) {
2289
+ break;
2290
+ }
2258
2291
  currentState = this.step(currentState, rune, anchor);
2259
2292
 
2260
2293
  // If we hit an unrecoverable DFA error or bailout, signal fallback
@@ -5417,18 +5450,10 @@ class RE2 {
5417
5450
  if (ncap > 0) {
5418
5451
  return this.doExecuteNFA(input, pos, anchor, ncap);
5419
5452
  }
5420
- try {
5421
- const dfaResult = this.dfa.match(input, pos, anchor);
5422
- if (dfaResult !== null) {
5423
- // DFA succeeded (returned true or false)
5424
- return dfaResult ? [] : null; // Return empty array to signify "matched but no captures"
5425
- }
5426
- } catch (e) {
5427
- if (e instanceof RE2JSDfaMemoryException) {
5428
- this.dfa = new DFA(this.prog); // flush cache
5429
- } else {
5430
- throw e;
5431
- }
5453
+ const dfaResult = this.dfa.match(input, pos, anchor);
5454
+ if (dfaResult !== null) {
5455
+ // DFA succeeded (returned true or false)
5456
+ return dfaResult ? [] : null; // Return empty array to signify "matched but no captures"
5432
5457
  }
5433
5458
 
5434
5459
  // Fallback to NFA
@@ -6230,7 +6255,7 @@ class RE2JS {
6230
6255
  * @throws RE2JSSyntaxException if the regular expression is malformed
6231
6256
  */
6232
6257
  static matches(regex, input) {
6233
- return RE2JS.compile(regex).matcher(input).matches();
6258
+ return RE2JS.compile(regex).testExact(input);
6234
6259
  }
6235
6260
 
6236
6261
  /**
@@ -6296,7 +6321,7 @@ class RE2JS {
6296
6321
  * @returns {boolean} true if the regular expression matches the entire input
6297
6322
  */
6298
6323
  matches(input) {
6299
- return this.matcher(input).matches();
6324
+ return this.testExact(input);
6300
6325
  }
6301
6326
 
6302
6327
  /**
@@ -6312,6 +6337,39 @@ class RE2JS {
6312
6337
  return new Matcher(this, input);
6313
6338
  }
6314
6339
 
6340
+ /**
6341
+ * Tests whether the regular expression matches any part of the input string.
6342
+ * Performance Note: This method is highly optimized. Because it only returns
6343
+ * a boolean and does not extract capture groups, it bypasses the `Matcher` overhead
6344
+ * and guarantees execution on the high-speed DFA engine whenever possible.
6345
+ *
6346
+ * @param {string|number[]} input - The input string or UTF-8 byte array to test against.
6347
+ * @returns {boolean} `true` if the pattern is found anywhere in the input, `false` otherwise.
6348
+ */
6349
+ test(input) {
6350
+ if (Array.isArray(input)) {
6351
+ // Reuse the existing UTF-8 fast-path method
6352
+ return this.re2Input.matchUTF8(input);
6353
+ }
6354
+
6355
+ // Reuse the existing UTF-16 fast-path method
6356
+ return this.re2Input.match(input);
6357
+ }
6358
+
6359
+ /**
6360
+ * Tests whether the regular expression matches the ENTIRE input string.
6361
+ * * **Performance Note:** This operates identically to `.matches()`, but is significantly
6362
+ * faster because it does not request capture group data. By requesting 0 capture groups,
6363
+ * it securely routes execution through the DFA fast-path.
6364
+ *
6365
+ * @param {string|number[]} input - The input string or UTF-8 byte array to test against.
6366
+ * @returns {boolean} `true` if the exact input string fully matches the pattern, `false` otherwise.
6367
+ */
6368
+ testExact(input) {
6369
+ const machineInput = Array.isArray(input) ? MachineInput.fromUTF8(input) : MachineInput.fromUTF16(input);
6370
+ return this.re2Input.executeEngine(machineInput, 0, RE2Flags.ANCHOR_BOTH, 0) !== null;
6371
+ }
6372
+
6315
6373
  /**
6316
6374
  * Splits input around instances of the regular expression. It returns an array giving the strings
6317
6375
  * that occur before, between, and after instances of the regular expression.
@@ -6433,7 +6491,6 @@ class RE2JS {
6433
6491
  exports.Matcher = Matcher;
6434
6492
  exports.RE2JS = RE2JS;
6435
6493
  exports.RE2JSCompileException = RE2JSCompileException;
6436
- exports.RE2JSDfaMemoryException = RE2JSDfaMemoryException;
6437
6494
  exports.RE2JSException = RE2JSException;
6438
6495
  exports.RE2JSFlagsException = RE2JSFlagsException;
6439
6496
  exports.RE2JSGroupException = RE2JSGroupException;