Skip to content

Commit e27a52f

Browse files
Also handle CMARK_NODE_CODE_BLOCK
1 parent 27ba3a4 commit e27a52f

File tree

1 file changed

+32
-8
lines changed

1 file changed

+32
-8
lines changed

src/blocks.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,17 +1103,41 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
11031103
continue;
11041104
}
11051105

1106-
if (parser->blank) {
1107-
const size_t n_list = read_open_block_count(&tmp_parser, CMARK_NODE_LIST);
1108-
const size_t n_item = read_open_block_count(&tmp_parser, CMARK_NODE_ITEM);
1109-
const size_t n_para = read_open_block_count(&tmp_parser, CMARK_NODE_PARAGRAPH);
1110-
if (n_list + n_item + n_para == tmp_parser.total_open_blocks) {
1111-
if (parser->current->flags & CMARK_NODE__OPEN_BLOCK) {
1112-
if (parser->current->flags & CMARK_NODE__OPEN) {
1106+
// This block of code is a workaround for the quadratic performance
1107+
// issue described here (issue 2):
1108+
//
1109+
// https://github.com/github/cmark-gfm/security/advisories/GHSA-66g8-4hjf-77xh
1110+
//
1111+
// If the current line is empty then we might be able to skip directly
1112+
// to the end of the list of open blocks. To determine whether this is
1113+
// possible, we have been maintaining a count of the number of
1114+
// different types of open blocks. The main criterium is that every
1115+
// remaining block, except the last element of the list, is a LIST or
1116+
// ITEM. The code below checks the conditions, and if they're ok, skips
1117+
// forward to parser->current.
1118+
if (parser->blank && parser->indent == 0) { // Current line is empty
1119+
// Make sure that parser->current doesn't point to a closed block.
1120+
if (parser->current->flags & CMARK_NODE__OPEN_BLOCK) {
1121+
if (parser->current->flags & CMARK_NODE__OPEN) {
1122+
const size_t n_list = read_open_block_count(&tmp_parser, CMARK_NODE_LIST);
1123+
const size_t n_item = read_open_block_count(&tmp_parser, CMARK_NODE_ITEM);
1124+
// At most one block can be something other than a LIST or ITEM.
1125+
if (n_list + n_item + 1 >= tmp_parser.total_open_blocks) {
1126+
// Check that parser->current is suitable for jumping to.
11131127
switch (S_type(parser->current)) {
1114-
case CMARK_NODE_PARAGRAPH:
11151128
case CMARK_NODE_LIST:
11161129
case CMARK_NODE_ITEM:
1130+
if (n_list + n_item != tmp_parser.total_open_blocks) {
1131+
if (parser->current->last_child == NULL) {
1132+
// There's another node type somewhere in the middle of
1133+
// the list, so don't attempt the optimization.
1134+
break;
1135+
}
1136+
}
1137+
// fall through
1138+
case CMARK_NODE_CODE_BLOCK:
1139+
case CMARK_NODE_PARAGRAPH:
1140+
// Jump to parser->current
11171141
container = parser->current;
11181142
cont_type = S_type(container);
11191143
break;

0 commit comments

Comments
 (0)