tag:github.com,2008:https://github.com/coregx/coregex/releases
Release notes from coregex
2026-02-06T01:06:53Z
tag:github.com,2008:Repository/1105180758/v0.12.0
2026-02-06T01:15:05Z
v0.12.0: Rust-inspired optimizations
<h2>Performance</h2>
<ul>
<li><strong>Anti-quadratic guard</strong> for reverse suffix/inner/suffix-set searches — prevents O(n²) degradation on high false-positive suffix workloads, falls back to PikeVM when quadratic detected</li>
<li><strong>Lazy DFA 4x loop unrolling</strong> — process 4 state transitions per inner loop iteration, check special states between batches</li>
<li><strong>Prefilter <code>IsFast()</code> gate</strong> — skip reverse search optimizations when fast SIMD-backed prefix prefilter already exists</li>
<li><strong>DFA cache clear & continue</strong> — on cache overflow, clear and fall back to PikeVM for current search instead of permanently disabling DFA</li>
</ul>
<h2>Fixed</h2>
<ul>
<li><strong>OnePass DFA capture limit</strong> — tighten from 17 to 16 capture groups (<code>uint32</code> slot mask = 32 bits)</li>
</ul>
<h2>Benchmark (AMD EPYC, regex-bench)</h2>
<table>
<thead>
<tr>
<th>Pattern</th>
<th>coregex</th>
<th>vs stdlib</th>
<th>vs Rust</th>
</tr>
</thead>
<tbody>
<tr>
<td>suffix</td>
<td>0.91ms</td>
<td><strong>257x</strong></td>
<td><strong>1.4x faster</strong></td>
</tr>
<tr>
<td>email</td>
<td>0.70ms</td>
<td><strong>383x</strong></td>
<td><strong>1.9x faster</strong></td>
</tr>
<tr>
<td>ip</td>
<td>2.19ms</td>
<td><strong>225x</strong></td>
<td><strong>5.5x faster</strong></td>
</tr>
<tr>
<td>uri</td>
<td>0.76ms</td>
<td><strong>340x</strong></td>
<td><strong>1.2x faster</strong></td>
</tr>
<tr>
<td>multiline_php</td>
<td>0.60ms</td>
<td><strong>171x</strong></td>
<td><strong>1.2x faster</strong></td>
</tr>
<tr>
<td>anchored_php</td>
<td>0.03ms</td>
<td>~1x</td>
<td><strong>12.0x faster</strong></td>
</tr>
</tbody>
</table>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.9
2026-02-01T21:33:29Z
v0.11.9: Fix missing first-byte prefilter in FindAll
<h2>Fixed</h2>
<ul>
<li><strong>Missing first-byte prefilter in FindAll state-reusing path</strong> (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3881752926" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/107" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/107/hovercard" href="https://github.com/coregx/coregex/issues/107">#107</a>)
<ul>
<li><code>findIndicesBoundedBacktrackerAtWithState</code> was missing <code>anchoredFirstBytes</code> O(1) check</li>
<li>Pattern <code>^/.*[\w-]+\.php</code> (without <code>$</code>) took 377ms instead of 40µs on 6MB input</li>
<li>Fix: <strong>377ms → 40µs</strong> (9000x improvement for non-matching anchored patterns)</li>
</ul>
</li>
</ul>
<h2>Full Changelog</h2>
<p><a class="commit-link" href="https://github.com/coregx/coregex/compare/v0.11.8...v0.11.9"><tt>v0.11.8...v0.11.9</tt></a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.8
2026-02-01T20:41:29Z
v0.11.8: Fix UseAnchoredLiteral regression
<h2>Fixed</h2>
<ul>
<li><strong>Critical regression in UseAnchoredLiteral strategy</strong> (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3881752926" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/107" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/107/hovercard" href="https://github.com/coregx/coregex/issues/107">#107</a>)
<ul>
<li><code>FindIndices*</code> and <code>findIndicesAtWithState</code> were missing <code>UseAnchoredLiteral</code> case</li>
<li>Pattern <code>^/.*[\w-]+\.php$</code> fell through to slow NFA path</li>
<li><strong>Regression</strong>: 0.01ms → 408ms (40,000x slower)</li>
<li><strong>Fix</strong>: 408ms → 0.5ms (O(1) anchored literal matching restored)</li>
</ul>
</li>
</ul>
<h2>Full Changelog</h2>
<p><a class="commit-link" href="https://github.com/coregx/coregex/compare/v0.11.7...v0.11.8"><tt>v0.11.7...v0.11.8</tt></a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.7
2026-02-01T19:50:49Z
v0.11.7: FindAll optimization - 1.08x faster than stdlib
<h2>Fixed</h2>
<p><strong>FindAll now uses optimized state-reusing path</strong></p>
<ul>
<li>FindAll was using slow per-match loop instead of optimized findAllIndicesStreaming</li>
<li>Results for <code>(\w{2,8})+</code> on 6MB: 2179ms → 834ms (<strong>2.6x faster</strong>)</li>
<li>Now <strong>1.08x faster than stdlib</strong> (was 2.4x slower in regex-bench)</li>
</ul>
<h3>Full Changelog</h3>
<p>See <a href="https://github.com/coregx/coregex/blob/main/CHANGELOG.md#0117---2026-02-01">CHANGELOG.md</a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.6
2026-02-01T18:56:45Z
v0.11.6: PikeVM 6MB optimization - 1.68x faster than stdlib
<h2>Performance</h2>
<p>Major PikeVM optimization achieving <strong>1.68x speedup over stdlib</strong> for large inputs (was 2.2x slower).</p>
<h3>Key Changes</h3>
<ul>
<li><strong>Windowed BoundedBacktracker (V12)</strong>: Search in 914KB windows before PikeVM fallback</li>
<li><strong>SlotTable architecture</strong>: Rust-style per-state slot storage</li>
<li><strong>Dynamic slot sizing</strong>: 0 (IsMatch), 2 (Find), full (Captures)</li>
<li><strong>Lightweight searchThread</strong>: 16 bytes (was 40+ bytes)</li>
</ul>
<h3>Benchmark Results</h3>
<p>Pattern <code>(\w{2,8})+</code> vs stdlib:</p>
<table>
<thead>
<tr>
<th>Size</th>
<th>Speedup</th>
</tr>
</thead>
<tbody>
<tr>
<td>10KB</td>
<td><strong>1.68x faster</strong></td>
</tr>
<tr>
<td>50KB</td>
<td><strong>1.88x faster</strong></td>
</tr>
<tr>
<td>100KB</td>
<td><strong>2.04x faster</strong></td>
</tr>
<tr>
<td>1MB</td>
<td><strong>1.67x faster</strong></td>
</tr>
<tr>
<td><strong>6MB</strong></td>
<td><strong>1.68x faster</strong></td>
</tr>
</tbody>
</table>
<p><strong>6MB improvement: 1900ms → 628ms (3x faster)</strong></p>
<h3>Full Changelog</h3>
<p>See <a href="https://github.com/coregx/coregex/blob/main/CHANGELOG.md#0116---2026-02-01">CHANGELOG.md</a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.5
2026-02-01T09:46:49Z
v0.11.5: Fix checkHasWordBoundary catastrophic slowdown
<h2>Summary</h2>
<p>Fixes catastrophic performance regression in patterns with <code>\w{n,m}</code> quantifiers (Issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3880363266" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/105" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/105/hovercard" href="https://github.com/coregx/coregex/issues/105">#105</a>).</p>
<p><strong>Before:</strong> 3 minutes 22 seconds on 79KB input (7,000,000x slower than stdlib)<br>
<strong>After:</strong> 3.6 µs on 79KB input (<strong>8.6x faster than stdlib</strong>)</p>
<h2>Changes</h2>
<h3>Fixed</h3>
<ul>
<li><strong>checkHasWordBoundary catastrophic slowdown</strong> (Issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3880363266" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/105" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/105/hovercard" href="https://github.com/coregx/coregex/issues/105">#105</a>)
<ul>
<li>Root cause: O(N*M) complexity from scanning all NFA states per byte</li>
<li>Fix: Use <code>NewBuilderWithWordBoundary()</code>, add <code>hasWordBoundary</code> guards, anchored prefilter verification</li>
</ul>
</li>
</ul>
<h3>Performance</h3>
<ul>
<li><strong>DFA state lookup: map → slice</strong> — 42% CPU time eliminated</li>
<li><strong>Literal extraction from capture/repeat groups</strong> — better prefilters
<ul>
<li><code>=($\w...){2}</code> now extracts <code>=$</code> (2 bytes) instead of just <code>=</code></li>
</ul>
</li>
</ul>
<h2>Benchmarks (79KB input)</h2>
<table>
<thead>
<tr>
<th>Stage</th>
<th>Time</th>
<th>vs stdlib</th>
</tr>
</thead>
<tbody>
<tr>
<td>Before fix</td>
<td>3m 22s</td>
<td>7,000,000x slower</td>
</tr>
<tr>
<td><strong>After fix</strong></td>
<td><strong>3.6 µs</strong></td>
<td><strong>8.6x faster</strong></td>
</tr>
</tbody>
</table>
<h2>Credits</h2>
<p><a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/danslo/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/danslo">@danslo</a> for root cause analysis and fix suggestions</p>
<p><strong>Full Changelog</strong>: <a class="commit-link" href="https://github.com/coregx/coregex/compare/v0.11.4...v0.11.5"><tt>v0.11.4...v0.11.5</tt></a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.4
2026-01-16T15:59:53Z
v0.11.4: FindAll multiline optimization
<h2>Fixed</h2>
<ul>
<li><strong>FindAll/FindAllIndex now use UseMultilineReverseSuffix strategy</strong> (Issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3822484285" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/102" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/102/hovercard" href="https://github.com/coregx/coregex/issues/102">#102</a>)
<ul>
<li><code>FindIndicesAt()</code> was missing dispatch for <code>UseMultilineReverseSuffix</code></li>
<li><code>IsMatch</code>/<code>Find</code> were fast (1µs), but <code>FindAll</code> was slow (78ms) — <strong>100x gap vs Rust</strong></li>
<li>After fix: <code>FindAll</code> on 6MB with 2000 matches: <strong>~1ms</strong> (was 78ms)</li>
</ul>
</li>
</ul>
<h2>Performance</h2>
<table>
<thead>
<tr>
<th>Operation</th>
<th>Before</th>
<th>After</th>
<th>Improvement</th>
</tr>
</thead>
<tbody>
<tr>
<td>FindAll (6MB, 2000 matches)</td>
<td>78ms</td>
<td>~1ms</td>
<td><strong>78x faster</strong></td>
</tr>
<tr>
<td>vs Rust gap</td>
<td>100x slower</td>
<td>~1.3x slower</td>
<td><strong>Near parity!</strong></td>
</tr>
</tbody>
</table>
<h2>Changed</h2>
<ul>
<li>Updated <code>golang.org/x/sys</code> v0.39.0 → v0.40.0</li>
</ul>
<p><strong>Full Changelog</strong>: <a class="commit-link" href="https://github.com/coregx/coregex/compare/v0.11.3...v0.11.4"><tt>v0.11.3...v0.11.4</tt></a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.3
2026-01-16T14:45:53Z
v0.11.3: Prefix fast path 319-552x speedup
<h2>Performance</h2>
<p>Pattern <code>(?m)^/.*\.php</code> now <strong>319-552x faster</strong> than stdlib (was 3.5-5.7x in v0.11.1)</p>
<table>
<thead>
<tr>
<th>Operation</th>
<th>coregex</th>
<th>stdlib</th>
<th>Speedup</th>
</tr>
</thead>
<tbody>
<tr>
<td>IsMatch</td>
<td>182 ns</td>
<td>100 µs</td>
<td><strong>552x</strong></td>
</tr>
<tr>
<td>Find</td>
<td>240 ns</td>
<td>81 µs</td>
<td><strong>338x</strong></td>
</tr>
<tr>
<td>CountAll</td>
<td>58 µs</td>
<td>18.7 ms</td>
<td><strong>319x</strong></td>
</tr>
</tbody>
</table>
<h2>Algorithm</h2>
<ol>
<li>Suffix prefilter finds <code>.php</code> candidates (SIMD memmem)</li>
<li>SIMD backward scan to find line start (<code>bytes.LastIndexByte</code>)</li>
<li>O(1) prefix byte check (<code>/</code> at line start)</li>
<li>Skip-to-next-line on mismatch (avoids O(n²) worst case)</li>
<li>DFA fallback for complex patterns without extractable prefix</li>
</ol>
<h2>Changes</h2>
<ul>
<li><code>MultilineReverseSuffixSearcher.prefixBytes</code> for O(1) verification</li>
<li><code>SetPrefixLiterals()</code> extracts prefix from pattern</li>
<li><code>findLineStart()</code> uses SIMD <code>bytes.LastIndexByte</code></li>
<li>Skip-to-next-line: on prefix mismatch, jump to next <code>\n</code> position</li>
</ul>
<p>Fixes <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3819410877" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/99" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/99/hovercard" href="https://github.com/coregx/coregex/issues/99">#99</a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.2
2026-01-16T13:46:52Z
v0.11.2: DFA verification for UseMultilineReverseSuffix
<h2>Performance Improvement</h2>
<p>Replace O(n*m) PikeVM verification with O(n) DFA verification for multiline suffix patterns.</p>
<p><strong>Issue</strong>: <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3819410877" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/99" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/99/hovercard" href="https://github.com/coregx/coregex/issues/99">#99</a> (Rust regex 84x faster on <code>(?m)^/.*\.php</code>)</p>
<h3>Benchmark Results</h3>
<table>
<thead>
<tr>
<th>Case</th>
<th>Before</th>
<th>After</th>
<th>Speedup</th>
</tr>
</thead>
<tbody>
<tr>
<td>No-match (2KB)</td>
<td>1136 ns</td>
<td>108 ns</td>
<td><strong>10.5x</strong></td>
</tr>
<tr>
<td>Long no-match</td>
<td>25937 ns</td>
<td>197 ns</td>
<td><strong>131x</strong></td>
</tr>
<tr>
<td>Large input (6MB)</td>
<td>66 ms</td>
<td>~5-10 ms</td>
<td><strong>10-30x</strong> (expected)</td>
</tr>
</tbody>
</table>
<h3>Changes</h3>
<ul>
<li><code>MultilineReverseSuffixSearcher.forwardDFA</code> replaces <code>pikevm</code> field</li>
<li>Uses <code>lazy.DFA.SearchAtAnchored()</code> for linear-time anchored matching</li>
<li><code>lazy.CompileWithConfig()</code> creates forward DFA with proper config</li>
</ul>
<h3>Research Insight</h3>
<p>Analysis of Rust regex-automata revealed that the hybrid (lazy) DFA does <strong>NOT</strong> use per-state acceleration — only the dense (pre-compiled) DFA does. The real performance difference comes from using DFA vs NFA/PikeVM for verification.</p>
<p>coregex already has partial state acceleration in <code>dfa/lazy/</code>. The main fix was switching from PikeVM to DFA verification.</p>
<hr>
<p><strong>Full Changelog</strong>: <a class="commit-link" href="https://github.com/coregx/coregex/compare/v0.11.1...v0.11.2"><tt>v0.11.1...v0.11.2</tt></a></p>
kolkov
tag:github.com,2008:Repository/1105180758/v0.11.1
2026-01-15T21:56:33Z
v0.11.1: UseMultilineReverseSuffix 3.5-5.7x speedup
<h2>What's New</h2>
<p>New <strong>18th strategy</strong> <code>UseMultilineReverseSuffix</code> for multiline suffix patterns like <code>(?m)^/.*\.php</code>.</p>
<h3>Performance (Issue <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3819071280" data-permission-text="Title is private" data-url="https://github.com/coregx/coregex/issues/97" data-hovercard-type="issue" data-hovercard-url="/coregx/coregex/issues/97/hovercard" href="https://github.com/coregx/coregex/issues/97">#97</a>)</h3>
<p><strong>Before</strong>: coregex was 24% slower than stdlib<br>
<strong>After</strong>: coregex is <strong>3.5-5.7x faster</strong> than stdlib</p>
<table>
<thead>
<tr>
<th>Operation</th>
<th>coregex</th>
<th>stdlib</th>
<th>Speedup</th>
</tr>
</thead>
<tbody>
<tr>
<td>IsMatch (0.5MB)</td>
<td>20.6 µs</td>
<td>72.2 µs</td>
<td><strong>3.5x</strong></td>
</tr>
<tr>
<td>Find (0.5MB)</td>
<td>15.3 µs</td>
<td>68.7 µs</td>
<td><strong>4.5x</strong></td>
</tr>
<tr>
<td>CountAll (200 matches)</td>
<td>2.56 ms</td>
<td>14.6 ms</td>
<td><strong>5.7x</strong></td>
</tr>
<tr>
<td>No-match (small)</td>
<td>90 ns</td>
<td>1.1 µs</td>
<td><strong>12x</strong></td>
</tr>
<tr>
<td>No-match (2KB)</td>
<td>184 ns</td>
<td>24 µs</td>
<td><strong>130x</strong></td>
</tr>
</tbody>
</table>
<h3>Algorithm</h3>
<ol>
<li>Suffix prefilter finds <code>.php</code> candidates</li>
<li>Backward scan to line start (<code>\n</code> or pos 0)</li>
<li>Forward PikeVM verification</li>
</ol>
<h3>Files</h3>
<ul>
<li><code>meta/reverse_suffix_multiline.go</code> (NEW)</li>
<li><code>meta/reverse_suffix_multiline_test.go</code> (NEW)</li>
</ul>
<p><strong>Full Changelog</strong>: <a class="commit-link" href="https://github.com/coregx/coregex/compare/v0.11.0...v0.11.1"><tt>v0.11.0...v0.11.1</tt></a></p>
kolkov