Skip to content

Commit 07a66c9

Browse files
authored
Merge pull request from GHSA-r8vr-c48j-fcc5
Fix quadratic behavior in rendering
2 parents 2300c1b + bd4f96e commit 07a66c9

File tree

10 files changed

+80
-100
lines changed

10 files changed

+80
-100
lines changed

src/commonmark.c

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -153,23 +153,8 @@ static bool is_autolink(cmark_node *node) {
153153
link_text->as.literal.len) == 0);
154154
}
155155

156-
// if node is a block node, returns node.
157-
// otherwise returns first block-level node that is an ancestor of node.
158-
// if there is no block-level ancestor, returns NULL.
159-
static cmark_node *get_containing_block(cmark_node *node) {
160-
while (node) {
161-
if (CMARK_NODE_BLOCK_P(node)) {
162-
return node;
163-
} else {
164-
node = node->parent;
165-
}
166-
}
167-
return NULL;
168-
}
169-
170156
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
171157
cmark_event_type ev_type, int options) {
172-
cmark_node *tmp;
173158
int list_number;
174159
cmark_delim_type list_delim;
175160
int numticks;
@@ -189,14 +174,17 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
189174
// Don't adjust tight list status til we've started the list.
190175
// Otherwise we loose the blank line between a paragraph and
191176
// a following list.
192-
if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {
193-
tmp = get_containing_block(node);
194-
renderer->in_tight_list_item =
195-
tmp && // tmp might be NULL if there is no containing block
196-
((tmp->type == CMARK_NODE_ITEM &&
197-
cmark_node_get_list_tight(tmp->parent)) ||
198-
(tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM &&
199-
cmark_node_get_list_tight(tmp->parent->parent)));
177+
if (entering) {
178+
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
179+
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
180+
}
181+
} else {
182+
if (node->type == CMARK_NODE_LIST) {
183+
renderer->in_tight_list_item =
184+
node->parent &&
185+
node->parent->type == CMARK_NODE_ITEM &&
186+
node->parent->parent->as.list.tight;
187+
}
200188
}
201189

202190
if (node->extension && node->extension->commonmark_render_func) {
@@ -228,19 +216,15 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
228216
LIT("<!-- end list -->");
229217
BLANKLINE();
230218
}
219+
renderer->list_number = cmark_node_get_list_start(node);
231220
break;
232221

233222
case CMARK_NODE_ITEM:
234223
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
235224
marker_width = 4;
236225
} else {
237-
list_number = cmark_node_get_list_start(node->parent);
226+
list_number = renderer->list_number++;
238227
list_delim = cmark_node_get_list_delim(node->parent);
239-
tmp = node;
240-
while (tmp->prev) {
241-
tmp = tmp->prev;
242-
list_number += 1;
243-
}
244228
// we ensure a width of at least 4 so
245229
// we get nice transition from single digits
246230
// to double
@@ -405,10 +389,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
405389
break;
406390

407391
case CMARK_NODE_STRONG:
408-
if (entering) {
409-
LIT("**");
410-
} else {
411-
LIT("**");
392+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
393+
if (entering) {
394+
LIT("**");
395+
} else {
396+
LIT("**");
397+
}
412398
}
413399
break;
414400

src/html.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,10 +364,12 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
364364
break;
365365

366366
case CMARK_NODE_STRONG:
367-
if (entering) {
368-
cmark_strbuf_puts(html, "<strong>");
369-
} else {
370-
cmark_strbuf_puts(html, "</strong>");
367+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
368+
if (entering) {
369+
cmark_strbuf_puts(html, "<strong>");
370+
} else {
371+
cmark_strbuf_puts(html, "</strong>");
372+
}
371373
}
372374
break;
373375

src/latex.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,10 +385,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
385385
break;
386386

387387
case CMARK_NODE_STRONG:
388-
if (entering) {
389-
LIT("\\textbf{");
390-
} else {
391-
LIT("}");
388+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
389+
if (entering) {
390+
LIT("\\textbf{");
391+
} else {
392+
LIT("}");
393+
}
392394
}
393395
break;
394396

src/man.c

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ static void S_outc(cmark_renderer *renderer, cmark_node *node,
7474

7575
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
7676
cmark_event_type ev_type, int options) {
77-
cmark_node *tmp;
7877
int list_number;
7978
bool entering = (ev_type == CMARK_EVENT_ENTER);
8079
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
@@ -114,6 +113,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
114113
break;
115114

116115
case CMARK_NODE_LIST:
116+
renderer->list_number = cmark_node_get_list_start(node);
117117
break;
118118

119119
case CMARK_NODE_ITEM:
@@ -123,12 +123,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
123123
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
124124
LIT("\\[bu] 2");
125125
} else {
126-
list_number = cmark_node_get_list_start(node->parent);
127-
tmp = node;
128-
while (tmp->prev) {
129-
tmp = tmp->prev;
130-
list_number += 1;
131-
}
126+
list_number = renderer->list_number++;
132127
char list_number_s[LIST_NUMBER_SIZE];
133128
snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
134129
LIT(list_number_s);
@@ -225,10 +220,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
225220
break;
226221

227222
case CMARK_NODE_STRONG:
228-
if (entering) {
229-
LIT("\\f[B]");
230-
} else {
231-
LIT("\\f[]");
223+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
224+
if (entering) {
225+
LIT("\\f[B]");
226+
} else {
227+
LIT("\\f[]");
228+
}
232229
}
233230
break;
234231

src/node.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ struct cmark_node {
8383

8484
cmark_syntax_extension *extension;
8585

86+
/**
87+
* Used during cmark_render() to cache the most recent non-NULL
88+
* extension, if you go up the parent chain like this:
89+
*
90+
* node->parent->...parent->extension
91+
*/
92+
cmark_syntax_extension *ancestor_extension;
93+
8694
union {
8795
int ref_ix;
8896
int def_count;

src/plaintext.c

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,8 @@ static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node,
1616
cmark_render_code_point(renderer, c);
1717
}
1818

19-
// if node is a block node, returns node.
20-
// otherwise returns first block-level node that is an ancestor of node.
21-
// if there is no block-level ancestor, returns NULL.
22-
static cmark_node *get_containing_block(cmark_node *node) {
23-
while (node) {
24-
if (CMARK_NODE_BLOCK_P(node)) {
25-
return node;
26-
} else {
27-
node = node->parent;
28-
}
29-
}
30-
return NULL;
31-
}
32-
3319
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
3420
cmark_event_type ev_type, int options) {
35-
cmark_node *tmp;
3621
int list_number;
3722
cmark_delim_type list_delim;
3823
int i;
@@ -46,14 +31,17 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
4631
// Don't adjust tight list status til we've started the list.
4732
// Otherwise we loose the blank line between a paragraph and
4833
// a following list.
49-
if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) {
50-
tmp = get_containing_block(node);
51-
renderer->in_tight_list_item =
52-
tmp && // tmp might be NULL if there is no containing block
53-
((tmp->type == CMARK_NODE_ITEM &&
54-
cmark_node_get_list_tight(tmp->parent)) ||
55-
(tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM &&
56-
cmark_node_get_list_tight(tmp->parent->parent)));
34+
if (entering) {
35+
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
36+
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
37+
}
38+
} else {
39+
if (node->type == CMARK_NODE_LIST) {
40+
renderer->in_tight_list_item =
41+
node->parent &&
42+
node->parent->type == CMARK_NODE_ITEM &&
43+
node->parent->parent->as.list.tight;
44+
}
5745
}
5846

5947
if (node->extension && node->extension->plaintext_render_func) {
@@ -73,19 +61,15 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
7361
node->next->type == CMARK_NODE_LIST)) {
7462
CR();
7563
}
64+
renderer->list_number = cmark_node_get_list_start(node);
7665
break;
7766

7867
case CMARK_NODE_ITEM:
7968
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
8069
marker_width = 4;
8170
} else {
82-
list_number = cmark_node_get_list_start(node->parent);
71+
list_number = renderer->list_number++;
8372
list_delim = cmark_node_get_list_delim(node->parent);
84-
tmp = node;
85-
while (tmp->prev) {
86-
tmp = tmp->prev;
87-
list_number += 1;
88-
}
8973
// we ensure a width of at least 4 so
9074
// we get nice transition from single digits
9175
// to double

src/render.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,7 @@ static void S_out(cmark_renderer *renderer, cmark_node *node,
3131
cmark_chunk remainder = cmark_chunk_literal("");
3232
int k = renderer->buffer->size - 1;
3333

34-
cmark_syntax_extension *ext = NULL;
35-
cmark_node *n = node;
36-
while (n && !ext) {
37-
ext = n->extension;
38-
if (!ext)
39-
n = n->parent;
40-
}
34+
cmark_syntax_extension *ext = node->ancestor_extension;
4135
if (ext && !ext->commonmark_escape_func)
4236
ext = NULL;
4337

@@ -177,11 +171,16 @@ char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width,
177171

178172
cmark_renderer renderer = {mem, &buf, &pref, 0, width,
179173
0, 0, true, true, false,
180-
false, outc, S_cr, S_blankline, S_out,
181-
0};
174+
false, 0, outc, S_cr, S_blankline,
175+
S_out, 0};
182176

183177
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
184178
cur = cmark_iter_get_node(iter);
179+
if (cur->extension) {
180+
cur->ancestor_extension = cur->extension;
181+
} else if (cur->parent) {
182+
cur->ancestor_extension = cur->parent->ancestor_extension;
183+
}
185184
if (!render_node(&renderer, cur, ev_type, options)) {
186185
// a false value causes us to skip processing
187186
// the node's contents. this is used for

src/render.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct cmark_renderer {
2323
bool begin_content;
2424
bool no_linebreaks;
2525
bool in_tight_list_item;
26+
int list_number;
2627
void (*outc)(struct cmark_renderer *, cmark_node *, cmark_escaping, int32_t, unsigned char);
2728
void (*cr)(struct cmark_renderer *);
2829
void (*blankline)(struct cmark_renderer *);

src/xml.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "syntax_extension.h"
1212

1313
#define BUFFER_SIZE 100
14+
#define MAX_INDENT 40
1415

1516
// Functions to convert cmark_nodes to XML strings.
1617

@@ -26,7 +27,7 @@ struct render_state {
2627

2728
static CMARK_INLINE void indent(struct render_state *state) {
2829
int i;
29-
for (i = 0; i < state->indent; i++) {
30+
for (i = 0; i < state->indent && i < MAX_INDENT; i++) {
3031
cmark_strbuf_putc(state->xml, ' ');
3132
}
3233
}

test/spec.txt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6926,7 +6926,7 @@ foo__bar__
69266926
```````````````````````````````` example
69276927
__foo, __bar__, baz__
69286928
.
6929-
<p><strong>foo, <strong>bar</strong>, baz</strong></p>
6929+
<p><strong>foo, bar, baz</strong></p>
69306930
````````````````````````````````
69316931

69326932

@@ -7197,7 +7197,7 @@ foo***bar***baz
71977197
```````````````````````````````` example
71987198
foo******bar*********baz
71997199
.
7200-
<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>
7200+
<p>foo<strong>bar</strong>***baz</p>
72017201
````````````````````````````````
72027202

72037203

@@ -7268,21 +7268,21 @@ __foo _bar_ baz__
72687268
```````````````````````````````` example
72697269
__foo __bar__ baz__
72707270
.
7271-
<p><strong>foo <strong>bar</strong> baz</strong></p>
7271+
<p><strong>foo bar baz</strong></p>
72727272
````````````````````````````````
72737273

72747274

72757275
```````````````````````````````` example
72767276
____foo__ bar__
72777277
.
7278-
<p><strong><strong>foo</strong> bar</strong></p>
7278+
<p><strong>foo bar</strong></p>
72797279
````````````````````````````````
72807280

72817281

72827282
```````````````````````````````` example
72837283
**foo **bar****
72847284
.
7285-
<p><strong>foo <strong>bar</strong></strong></p>
7285+
<p><strong>foo bar</strong></p>
72867286
````````````````````````````````
72877287

72887288

@@ -7567,14 +7567,14 @@ switching delimiters:
75677567
```````````````````````````````` example
75687568
****foo****
75697569
.
7570-
<p><strong><strong>foo</strong></strong></p>
7570+
<p><strong>foo</strong></p>
75717571
````````````````````````````````
75727572

75737573

75747574
```````````````````````````````` example
75757575
____foo____
75767576
.
7577-
<p><strong><strong>foo</strong></strong></p>
7577+
<p><strong>foo</strong></p>
75787578
````````````````````````````````
75797579

75807580

@@ -7585,7 +7585,7 @@ delimiters:
75857585
```````````````````````````````` example
75867586
******foo******
75877587
.
7588-
<p><strong><strong><strong>foo</strong></strong></strong></p>
7588+
<p><strong>foo</strong></p>
75897589
````````````````````````````````
75907590

75917591

@@ -7601,7 +7601,7 @@ Rule 14:
76017601
```````````````````````````````` example
76027602
_____foo_____
76037603
.
7604-
<p><em><strong><strong>foo</strong></strong></em></p>
7604+
<p><em><strong>foo</strong></em></p>
76057605
````````````````````````````````
76067606

76077607

0 commit comments

Comments
 (0)