@@ -135,40 +135,28 @@ func (e *Engine) isMatchNFA(haystack []byte) bool {
135135func (e * Engine ) isMatchDFA (haystack []byte ) bool {
136136 atomic .AddUint64 (& e .stats .DFASearches , 1 )
137137
138- // Prefilter-accelerated matching: use prefilter to skip non-matching regions,
139- // then verify each candidate with anchored DFA or PikeVM.
140- // This is critical for case-insensitive patterns with large NFAs (Issue #137).
138+ // Prefilter fast rejection: if prefilter finds no candidates, no match.
139+ // For incomplete prefilters with candidates, use pooled PikeVM for
140+ // thread-safe verification. Shared lazy DFA (e.dfa) is NOT thread-safe
141+ // for ANY concurrent access — even DFA.IsMatch causes data races on
142+ // lazy state construction. Issue #137 (ARM64 M2 Max: 1.7GB allocs).
141143 if e .prefilter != nil {
142- pos := 0
143- for pos < len (haystack ) {
144- candidate := e .prefilter .Find (haystack , pos )
145- if candidate == - 1 {
146- return false // No more candidates
147- }
148- atomic .AddUint64 (& e .stats .PrefilterHits , 1 )
149- // For complete prefilters, the find is sufficient
150- if e .prefilter .IsComplete () {
151- return true
152- }
153- // Verify candidate with anchored DFA (O(pattern_len) per candidate).
154- // Unanchored verification would scan to end of input = O(n) per candidate.
155- if e .dfa != nil {
156- if e .dfa .SearchAtAnchored (haystack , candidate ) != - 1 {
157- return true
158- }
159- } else {
160- // PikeVM fallback — check if match starts at candidate position
161- start , _ , found := e .pikevm .SearchAt (haystack , candidate )
162- if found && start == candidate {
163- return true
164- }
165- }
166- pos = candidate + 1
144+ pos := e .prefilter .Find (haystack , 0 )
145+ if pos == - 1 {
146+ return false // Prefilter says no candidates — fast rejection
167147 }
168- return false
148+ atomic .AddUint64 (& e .stats .PrefilterHits , 1 )
149+ if e .prefilter .IsComplete () {
150+ return true // Complete prefilter — find is sufficient
151+ }
152+ // Prefilter found candidate but isn't complete — verify with pooled PikeVM
153+ state := e .getSearchState ()
154+ _ , _ , found := state .pikevm .SearchAt (haystack , pos )
155+ e .putSearchState (state )
156+ return found
169157 }
170158
171- // Use DFA.IsMatch which has early termination optimization
159+ // No prefilter: use DFA.IsMatch (single-thread path, safe)
172160 return e .dfa .IsMatch (haystack )
173161}
174162
0 commit comments