Skip to content

Commit ece074c

Browse files
jgmkevinbackhouse
authored andcommitted
Fix quadratic complexity bug.
Previously in parsing the repeated pattern ![[]() the parser repeatedly scanned to the beginning of the increasingly large stack of bracketed delimiters, trying to set the link delimiters to "inactive" to prevent links inside links. This commit removes the `active` flag from the bracketed delimiters, and adds a new boolean flag `no_link_openers` on subject. This can be set to true after a link is matched, telling the parser not to bother forming a link if it matches an open bracket. It is set to false when a new link open bracket is added to the stack of openers. This new approach avoids the need to traverse the stack of open brackets.
1 parent 9d57d8a commit ece074c

File tree

1 file changed

+13
-29
lines changed

1 file changed

+13
-29
lines changed

src/inlines.c

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ typedef struct subject{
5757
bracket *last_bracket;
5858
bufsize_t backticks[MAXBACKTICKS + 1];
5959
bool scanned_for_backticks;
60+
bool no_link_openers;
6061
} subject;
6162

6263
// Extensions may populate this.
@@ -174,6 +175,7 @@ static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset,
174175
e->backticks[i] = 0;
175176
}
176177
e->scanned_for_backticks = false;
178+
e->no_link_openers = true;
177179
}
178180

179181
static CMARK_INLINE int isbacktick(int c) { return (c == '`'); }
@@ -534,6 +536,9 @@ static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
534536
b->in_bracket_image0 = true;
535537
}
536538
subj->last_bracket = b;
539+
if (!image) {
540+
subj->no_link_openers = false;
541+
}
537542
}
538543

539544
// Assumes the subject has a c at the current position.
@@ -1065,16 +1070,16 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
10651070
return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
10661071
}
10671072

1068-
if (!opener->active) {
1073+
// If we got here, we matched a potential link/image text.
1074+
// Now we check to see if it's a link/image.
1075+
is_image = opener->image;
1076+
1077+
if (!is_image && subj->no_link_openers) {
10691078
// take delimiter off stack
10701079
pop_bracket(subj);
10711080
return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
10721081
}
10731082

1074-
// If we got here, we matched a potential link/image text.
1075-
// Now we check to see if it's a link/image.
1076-
is_image = opener->image;
1077-
10781083
after_link_text_pos = subj->pos;
10791084

10801085
// First, look for an inline link.
@@ -1248,32 +1253,11 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
12481253
process_emphasis(parser, subj, opener->previous_delimiter);
12491254
pop_bracket(subj);
12501255

1251-
// Now, if we have a link, we also want to deactivate earlier link
1252-
// delimiters. (This code can be removed if we decide to allow links
1256+
// Now, if we have a link, we also want to deactivate links until
1257+
// we get a new opener. (This code can be removed if we decide to allow links
12531258
// inside links.)
12541259
if (!is_image) {
1255-
opener = subj->last_bracket;
1256-
while (opener != NULL) {
1257-
if (!opener->image) {
1258-
if (!opener->active) {
1259-
break;
1260-
} else {
1261-
opener->active = false;
1262-
}
1263-
}
1264-
opener = opener->previous;
1265-
}
1266-
bool in_bracket_image1 = false;
1267-
if (opener) {
1268-
in_bracket_image1 = opener->in_bracket_image1;
1269-
}
1270-
bracket *opener2 = subj->last_bracket;
1271-
while (opener2 != opener) {
1272-
if (opener2->image) {
1273-
opener2->in_bracket_image1 = in_bracket_image1;
1274-
}
1275-
opener2 = opener2->previous;
1276-
}
1260+
subj->no_link_openers = true;
12771261
}
12781262

12791263
return NULL;

0 commit comments

Comments
 (0)