Repository navigation
Conversation
dafili
force-pushed
the
fix-quadratic-unclosed-brackets
branch
from
November 9, 2026 14:15
c23c244 to
34f2814
Compare
`LinkInlineProcessor.getText` counts brackets from the opening bracket of each `[` the link pattern matches until the count returns to zero. When a bracket is never closed the count never returns to zero and the same walk to the end of the buffer runs again for the next `[`, so a run of n unclosed brackets costs O(n^2). The buffer does not change while matches keep being rejected, so the `]` which matches each `[` is now computed once per buffer with a stack and cached on the processor; `getText` becomes a dictionary lookup. Which text and index are returned for a handled and for an unhandled match is unchanged, so the rendered output is unchanged. Refs Python-Markdown#798.
`docs/changelog.md` only; the code and test changes are in the parent commit and are untouched here.
dafili
force-pushed
the
fix-quadratic-unclosed-brackets
branch
from
November 9, 2026 14:19
34f2814 to
b22c46c
Compare
Member
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.
Description
LinkInlineProcessor.getTextcounts brackets from the opening bracket of every[the link pattern matches until the count returns to zero. When a bracket is never closed the count never returns to zero, the walk runs to the end of the buffer, and the next[walks the same span again. A run of n unclosed brackets therefore costs O(n^2) with no extensions enabled:'[' * 2000(2 KB)'[' * 4000(4 KB)'[' * 8000(8 KB)'[' * 13000(13 KB)'[' * 20000(20 KB)'[' * 52000(52 KB)The buffer does not change while matches keep being rejected, so the
]which matches each[is now computed once per buffer with a stack and cached on the processor, andgetTextbecomes a dictionary lookup. A]with no[before it is still ignored, as it was before, so unpaired closers behave exactly as they did.Nothing about which text and index are returned changes: for a matched bracket pair
getTextreturns the text up to the]and the index just past it, and for an unclosed bracket it returns the rest of the buffer and the end of the buffer. That is the same output the loop produced.Output is unchanged. I compared the rendered HTML with master on 1 945 documents: 400 random mixes of brackets, parentheses, backticks, quotes and escapes, 1 500 longer random documents over the same alphabet plus link and image fragments, and 45 hand-written nesting shapes (matched and unmatched runs at depths 1, 2, 3 and 7, closers before openers, 200-deep runs). The md5 of the concatenated output is identical in both runs:
51cb0be444d0524fd90ae0c983141369andff96a25b5fee5033bbef14f0dc071d3b. The full test suite passes (1 188 tests, 111 skipped) and flake8 is clean. The new test takes 78 s on master and 0.08 s with this change.getLink, which walks(/)the same way, is untouched: this only covers the[direction reported in #798.AI Assistance Disclosure
No AI tools were used in preparing this PR.
AI tools were used and I have disclosed which ones, and have fully reviewed and verified their output.
AI tools were use to assist with the following:
An AI agent (nanobot, running on my machine) drafted the stack-based pairing above, the wording of this description and the commit message. I read the diff line by line, ran the timing and rendering comparisons myself on this machine, and can explain the change or rework it in the review.
Checklist