Repository navigation
Fix O(n²) performance regression in spoiler span analysis - #312
Merged
Merged
Conversation
The spoiler analysis loop in md_analyze_link_contents() was called
recursively for every resolved link pair (by md_resolve_links()), but
lacked the skip-resolved-spans optimization present in md_analyze_marks().
With deeply nested bracket structures (e.g. the OSS-Fuzz testcase
clusterfuzz-testcase-fuzz-mdhtml-5579634954797056 with 37,034 nesting
levels), the loop iterated O(n^2) over the same marks, causing 13+
second parse times for a 661 kB input.
Fix: when the loop encounters a resolved link or image opener ('[', '!'),
jump directly to mark->next (the closer) instead of walking through
every mark inside the span one by one. This mirrors the skip logic in
md_analyze_marks() and reduces total iterations from O(n^2) to O(n).
Closes mity#311.
Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #312 +/- ##
==========================================
+ Coverage 78.94% 78.95% +0.01%
==========================================
Files 5 5
Lines 3401 3403 +2
Branches 1138 1139 +1
==========================================
+ Hits 2685 2687 +2
Misses 275 275
Partials 441 441 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…coverable branch ISANYOF_ expands to ((ch) != '\0' && ...), which introduces an impossible branch because no mark ever has ch == '\0'. Replace with an explicit (mark->ch == '[' || mark->ch == '!') check for full branch coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
Owner
|
Yep, great job. Thx. I've somewhat refactored |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
md_analyze_link_contents()is called recursively for every resolved linkpair. Each call runs the spoiler loop over its mark range, but unlike
md_analyze_marks(), the loop had no skip optimization for resolved spans— it walked through every inner mark one by one.
With 64,135 resolved links in the OSS-Fuzz testcase, this caused
4.86 billion loop iterations instead of 268,499 — O(n²) behavior.
Fix: when the loop hits a resolved
[or!opener, jump tomark->nextdirectly, mirroring whatmd_analyze_marks()already doesFixes: #311