fix: Avoid O(n^2) backtracking in HTML block close and tilde interrupt regexes#4014
Open
hong4rc wants to merge 2 commits into
Open
fix: Avoid O(n^2) backtracking in HTML block close and tilde interrupt regexes#4014hong4rc wants to merge 2 commits into
hong4rc wants to merge 2 commits into
Conversation
The close branches end in `[^\n]*\n+`; the trailing `\n+` requires a newline, so at EOF the close can't match and the engine retries every split of the lazy `[\s\S]*?` before falling through to `$`, which is O(n^2). `\n*` closes on first match and consumes identical text whenever a trailing newline is present (the `[^\n]*` was added in markedjs#3991).
The backtick branch is guarded by a lookahead but `~{3,}` isn't, and it
overlaps the following `[^\n]*`, so a long newline-less tilde run
backtracks quadratically. Since `~{3,}` is always followed by `[^\n]*`,
`~~~` matches the same strings without the overlap. The real fences
tokenizer is left untouched.
|
@hong4rc is attempting to deploy a commit to the MarkedJS Team on Vercel. A member of the Team first needs to authorize it. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Follow-up to #4013 — two more spots in
src/rules.tswhere a block-level regex backtracks quadratically on a long single line. Split into one commit each.The first is the HTML block close. #3991 rewrote the close of the closing-tag, processing-instruction, declaration, and CDATA branches to
[^\n]*\n+so trailing text on the close line is kept. Keeping the trailing text is right, but the\n+isn't: it requires at least one newline, so when the input ends without a trailing newline the close can never match and the engine retries every split point of the preceding lazy[\s\S]*?before finally falling through to$. That's O(n²):Switching
\n+back to\n*closes on the first match. Whenever a trailing newline is present (the normal case)\n*and\n+consume exactly the same text, so output is unchanged — #3991's own trailing-text fixtures still pass.The second is the paragraph/table/blockquote interrupt check. Its fence sub-pattern is
(?:`{3,}(?=[^`\n]*\n)|~{3,})[^\n]*\n. The backtick branch is guarded by a lookahead, but~{3,}isn't, and it overlaps with the following[^\n]*, so a long run of tildes with no newline backtracks quadratically:Because
~{3,}is always immediately followed by[^\n]*,~~~[^\n]*matches exactly the same strings, so replacing~{3,}with~~~removes the overlap without changing what matches. The real fenced-code tokenizer (which captures the fence length via a group and a$alternative) is left untouched — only the three interrupt copies change.Both changes are behavior-preserving: the full spec and unit suites pass with identical output, and I added a
quadratic_*.cjsguard next to #4013's for each.