Skip to content

Commit d4e788d

Browse files
committed
fix: prevent quadratic complexity in emStrongLDelim regex
Make suffix groups optional to eliminate O(n) backtracking per exec(). Skip entire delimiter run as text when no valid suffix is found to avoid the O(n^2) amplification loop.
1 parent 22f0c55 commit d4e788d

File tree

3 files changed

+16
-4
lines changed

3 files changed

+16
-4
lines changed

src/Tokenizer.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -726,14 +726,22 @@ export class _Tokenizer<ParserOutput = string, RendererOutput = string> {
726726
}
727727
}
728728

729-
emStrong(src: string, maskedSrc: string, prevChar = ''): Tokens.Em | Tokens.Strong | undefined {
729+
emStrong(src: string, maskedSrc: string, prevChar = ''): Tokens.Em | Tokens.Strong | Tokens.Text | undefined {
730730
let match = this.rules.inline.emStrongLDelim.exec(src);
731731
if (!match) return;
732+
if (!match[1] && !match[2] && !match[3] && !match[4]) {
733+
// Delimiter run has no valid suffix — skip entire run as text
734+
return {
735+
type: 'text',
736+
raw: match[0],
737+
text: match[0],
738+
};
739+
}
732740

733741
// _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
734-
if (match[3] && prevChar.match(this.rules.other.unicodeAlphaNumeric)) return;
742+
if (match[4] && prevChar.match(this.rules.other.unicodeAlphaNumeric)) return;
735743

736-
const nextChar = match[1] || match[2] || '';
744+
const nextChar = match[1] || match[3] || '';
737745

738746
if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
739747
// unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)

src/rules.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ const blockSkip = edit(/link|precode-code|html/, 'g')
290290
.replace('html', /<(?! )[^<>]*?>/)
291291
.getRegex();
292292

293-
const emStrongLDelimCore = /^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/;
293+
const emStrongLDelimCore = /^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/;
294294

295295
const emStrongLDelim = edit(emStrongLDelimCore, 'u')
296296
.replace(/punct/g, _punctuation)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = {
2+
markdown: '_'.repeat(10000) + ' a',
3+
html: `<p>${'_'.repeat(10000)} a</p>\n`,
4+
};

0 commit comments

Comments
 (0)