From aa73711d67f0230b3cd5cc0b2c17a672d82f59dd Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 6 Mar 2023 22:15:58 +0000 Subject: [PATCH] Early-terminate the loop in check_open_blocks when the current line is blank. --- src/blocks.c | 32 +++++++++++++++++++++++++++++--- src/parser.h | 10 ++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/blocks.c b/src/blocks.c index 63741594e..2c70b21c9 100644 --- a/src/blocks.c +++ b/src/blocks.c @@ -409,9 +409,11 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) { // in parser. (Used to check that the counts in parser, which are updated incrementally, are // correct.) bool check_open_block_counts(cmark_parser *parser) { - cmark_parser tmp_parser = {0}; // Only used for its open_block_counts field. + cmark_parser tmp_parser = {0}; // Only used for its open_block_counts and total_open_blocks fields. add_open_block_counts(&tmp_parser, parser->root); - return memcmp(tmp_parser.open_block_counts, parser->open_block_counts, sizeof(parser->open_block_counts)) == 0; + return + tmp_parser.total_open_blocks == parser->total_open_blocks && + memcmp(tmp_parser.open_block_counts, parser->open_block_counts, sizeof(parser->open_block_counts)) == 0; } // Add a node as child of another. Return pointer to child. @@ -1082,10 +1084,14 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input, *all_matched = false; cmark_node *container = parser->root; cmark_node_type cont_type; + cmark_parser tmp_parser; // Only used for its open_block_counts and total_open_blocks fields. + memcpy(tmp_parser.open_block_counts, parser->open_block_counts, sizeof(parser->open_block_counts)); + tmp_parser.total_open_blocks = parser->total_open_blocks; assert(check_open_block_counts(parser)); while (S_last_child_is_open(container)) { + decr_open_block_count(&tmp_parser, S_type(container)); container = container->last_child; cont_type = S_type(container); @@ -1097,6 +1103,26 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input, continue; } + if (parser->blank) { + const size_t n_list = read_open_block_count(&tmp_parser, CMARK_NODE_LIST); + const size_t n_item = read_open_block_count(&tmp_parser, CMARK_NODE_ITEM); + const size_t n_para = read_open_block_count(&tmp_parser, CMARK_NODE_PARAGRAPH); + if (n_list + n_item + n_para == tmp_parser.total_open_blocks) { + if (parser->current->flags & CMARK_NODE__OPEN_BLOCK) { + if (S_type(parser->current) == CMARK_NODE_PARAGRAPH) { + container = parser->current; + goto done; + } + if (S_type(parser->current) == CMARK_NODE_ITEM) { + if (parser->current->flags & CMARK_NODE__OPEN) { + container = parser->current; + cont_type = S_type(container); + } + } + } + } + } + switch (cont_type) { case CMARK_NODE_BLOCK_QUOTE: if (!parse_block_quote_prefix(parser, input)) @@ -1387,7 +1413,7 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container, S_set_last_line_blank(container, last_line_blank); tmp = container; - while (tmp->parent) { + while (tmp->parent && S_last_line_blank(tmp->parent)) { S_set_last_line_blank(tmp->parent, false); tmp = tmp->parent; } diff --git a/src/parser.h b/src/parser.h index 5020aa20b..05403fe3d 100644 --- a/src/parser.h +++ b/src/parser.h @@ -66,12 +66,14 @@ struct cmark_parser { * For example, CMARK_NODE_LIST (0x8003) is stored at offset 2. */ size_t open_block_counts[CMARK_NODE_TYPE_BLOCK_LIMIT]; + size_t total_open_blocks; }; static CMARK_INLINE void incr_open_block_count(cmark_parser *parser, cmark_node_type type) { assert(type > CMARK_NODE_TYPE_BLOCK); assert(type <= CMARK_NODE_TYPE_BLOCK + CMARK_NODE_TYPE_BLOCK_LIMIT); parser->open_block_counts[type - CMARK_NODE_TYPE_BLOCK - 1]++; + parser->total_open_blocks++; } static CMARK_INLINE void decr_open_block_count(cmark_parser *parser, cmark_node_type type) { @@ -79,6 +81,14 @@ static CMARK_INLINE void decr_open_block_count(cmark_parser *parser, cmark_node_ assert(type <= CMARK_NODE_TYPE_BLOCK + CMARK_NODE_TYPE_BLOCK_LIMIT); assert(parser->open_block_counts[type - CMARK_NODE_TYPE_BLOCK - 1] > 0); parser->open_block_counts[type - CMARK_NODE_TYPE_BLOCK - 1]--; + assert(parser->total_open_blocks > 0); + parser->total_open_blocks--; +} + +static CMARK_INLINE size_t read_open_block_count(cmark_parser *parser, cmark_node_type type) { + assert(type > CMARK_NODE_TYPE_BLOCK); + assert(type <= CMARK_NODE_TYPE_BLOCK + CMARK_NODE_TYPE_BLOCK_LIMIT); + return parser->open_block_counts[type - CMARK_NODE_TYPE_BLOCK - 1]; } #ifdef __cplusplus