1111#include "table.h"
1212#include "cmark-gfm-core-extensions.h"
1313
14+ // Custom node flag, initialized in `create_table_extension`.
15+ static cmark_node__internal_flags CMARK_NODE__TABLE_VISITED ;
16+
1417cmark_node_type CMARK_NODE_TABLE , CMARK_NODE_TABLE_ROW ,
1518 CMARK_NODE_TABLE_CELL ;
1619
20+ typedef struct {
21+ cmark_strbuf * buf ;
22+ int start_offset , end_offset , internal_offset ;
23+ } node_cell ;
24+
1725typedef struct {
1826 uint16_t n_columns ;
1927 int paragraph_offset ;
20- cmark_llist * cells ;
28+ node_cell * cells ;
2129} table_row ;
2230
2331typedef struct {
@@ -29,24 +37,24 @@ typedef struct {
2937 bool is_header ;
3038} node_table_row ;
3139
32- typedef struct {
33- cmark_strbuf * buf ;
34- int start_offset , end_offset , internal_offset ;
35- } node_cell ;
36-
37- static void free_table_cell (cmark_mem * mem , void * data ) {
38- node_cell * cell = (node_cell * )data ;
40+ static void free_table_cell (cmark_mem * mem , node_cell * cell ) {
3941 cmark_strbuf_free ((cmark_strbuf * )cell -> buf );
4042 mem -> free (cell -> buf );
41- mem -> free (cell );
43+ }
44+
45+ static void free_row_cells (cmark_mem * mem , table_row * row ) {
46+ while (row -> n_columns > 0 ) {
47+ free_table_cell (mem , & row -> cells [-- row -> n_columns ]);
48+ }
49+ mem -> free (row -> cells );
50+ row -> cells = NULL ;
4251}
4352
4453static void free_table_row (cmark_mem * mem , table_row * row ) {
4554 if (!row )
4655 return ;
4756
48- cmark_llist_free_full (mem , row -> cells , (cmark_free_func )free_table_cell );
49-
57+ free_row_cells (mem , row );
5058 mem -> free (row );
5159}
5260
@@ -111,6 +119,24 @@ static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsi
111119 return res ;
112120}
113121
122+ // Adds a new cell to the end of the row. A pointer to the new cell is returned
123+ // for the caller to initialize.
124+ static node_cell * append_row_cell (cmark_mem * mem , table_row * row ) {
125+ const uint32_t n_columns = row -> n_columns + 1 ;
126+ // realloc when n_columns is a power of 2
127+ if ((n_columns & (n_columns - 1 )) == 0 ) {
128+ // make sure we never wrap row->n_columns
129+ // offset will != len and our exit will clean up as intended
130+ if (n_columns > UINT16_MAX ) {
131+ return NULL ;
132+ }
133+ // Use realloc to double the size of the buffer.
134+ row -> cells = (node_cell * )mem -> realloc (row -> cells , (2 * n_columns - 1 ) * sizeof (node_cell ));
135+ }
136+ row -> n_columns = n_columns ;
137+ return & row -> cells [n_columns - 1 ];
138+ }
139+
114140static table_row * row_from_string (cmark_syntax_extension * self ,
115141 cmark_parser * parser , unsigned char * string ,
116142 int len ) {
@@ -152,24 +178,22 @@ static table_row *row_from_string(cmark_syntax_extension *self,
152178 cell_matched );
153179 cmark_strbuf_trim (cell_buf );
154180
155- node_cell * cell = (node_cell * )parser -> mem -> calloc (1 , sizeof (* cell ));
181+ node_cell * cell = append_row_cell (parser -> mem , row );
182+ if (!cell ) {
183+ int_overflow_abort = 1 ;
184+ cmark_strbuf_free (cell_buf );
185+ parser -> mem -> free (cell_buf );
186+ break ;
187+ }
156188 cell -> buf = cell_buf ;
157189 cell -> start_offset = offset ;
158190 cell -> end_offset = offset + cell_matched - 1 ;
191+ cell -> internal_offset = 0 ;
159192
160- while (cell -> start_offset > 0 && string [cell -> start_offset - 1 ] != '|' ) {
193+ while (cell -> start_offset > row -> paragraph_offset && string [cell -> start_offset - 1 ] != '|' ) {
161194 -- cell -> start_offset ;
162195 ++ cell -> internal_offset ;
163196 }
164-
165- // make sure we never wrap row->n_columns
166- // offset will != len and our exit will clean up as intended
167- if (row -> n_columns == UINT16_MAX ) {
168- int_overflow_abort = 1 ;
169- break ;
170- }
171- row -> n_columns += 1 ;
172- row -> cells = cmark_llist_append (parser -> mem , row -> cells , cell );
173197 }
174198
175199 offset += cell_matched + pipe_matched ;
@@ -187,9 +211,7 @@ static table_row *row_from_string(cmark_syntax_extension *self,
187211 if (row_end_offset && offset != len ) {
188212 row -> paragraph_offset = offset ;
189213
190- cmark_llist_free_full (parser -> mem , row -> cells , (cmark_free_func )free_table_cell );
191- row -> cells = NULL ;
192- row -> n_columns = 0 ;
214+ free_row_cells (parser -> mem , row );
193215
194216 // Scan past the (optional) leading pipe.
195217 offset += scan_table_cell_end (string , len , offset );
@@ -240,6 +262,10 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
240262 const char * parent_string ;
241263 uint16_t i ;
242264
265+ if (parent_container -> flags & CMARK_NODE__TABLE_VISITED ) {
266+ return parent_container ;
267+ }
268+
243269 if (!scan_table_start (input , len , cmark_parser_get_first_nonspace (parser ))) {
244270 return parent_container ;
245271 }
@@ -267,6 +293,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
267293 free_table_row (parser -> mem , marker_row );
268294 free_table_row (parser -> mem , header_row );
269295 cmark_arena_pop ();
296+ parent_container -> flags |= CMARK_NODE__TABLE_VISITED ;
270297 return parent_container ;
271298 }
272299
@@ -303,9 +330,8 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
303330 // since we populate the alignments array based on marker_row->cells
304331 uint8_t * alignments =
305332 (uint8_t * )parser -> mem -> calloc (marker_row -> n_columns , sizeof (uint8_t ));
306- cmark_llist * it = marker_row -> cells ;
307- for (i = 0 ; it ; it = it -> next , ++ i ) {
308- node_cell * node = (node_cell * )it -> data ;
333+ for (i = 0 ; i < marker_row -> n_columns ; ++ i ) {
334+ node_cell * node = & marker_row -> cells [i ];
309335 bool left = node -> buf -> ptr [0 ] == ':' , right = node -> buf -> ptr [node -> buf -> size - 1 ] == ':' ;
310336
311337 if (left && right )
@@ -328,10 +354,8 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
328354 ntr -> is_header = true;
329355
330356 {
331- cmark_llist * tmp ;
332-
333- for (tmp = header_row -> cells ; tmp ; tmp = tmp -> next ) {
334- node_cell * cell = (node_cell * ) tmp -> data ;
357+ for (i = 0 ; i < header_row -> n_columns ; ++ i ) {
358+ node_cell * cell = & header_row -> cells [i ];
335359 cmark_node * header_cell = cmark_parser_add_child (parser , table_header ,
336360 CMARK_NODE_TABLE_CELL , parent_container -> start_column + cell -> start_offset );
337361 header_cell -> start_line = header_cell -> end_line = parent_container -> start_line ;
@@ -378,11 +402,10 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
378402 }
379403
380404 {
381- cmark_llist * tmp ;
382405 int i , table_columns = get_n_table_columns (parent_container );
383406
384- for (tmp = row -> cells , i = 0 ; tmp && i < table_columns ; tmp = tmp -> next , ++ i ) {
385- node_cell * cell = ( node_cell * ) tmp -> data ;
407+ for (i = 0 ; i < row -> n_columns && i < table_columns ; ++ i ) {
408+ node_cell * cell = & row -> cells [ i ] ;
386409 cmark_node * node = cmark_parser_add_child (parser , table_row_block ,
387410 CMARK_NODE_TABLE_CELL , parent_container -> start_column + cell -> start_offset );
388411 node -> internal_offset = cell -> internal_offset ;
@@ -785,6 +808,7 @@ static int escape(cmark_syntax_extension *self, cmark_node *node, int c) {
785808cmark_syntax_extension * create_table_extension (void ) {
786809 cmark_syntax_extension * self = cmark_syntax_extension_new ("table" );
787810
811+ cmark_register_node_flag (& CMARK_NODE__TABLE_VISITED );
788812 cmark_syntax_extension_set_match_block_func (self , matches );
789813 cmark_syntax_extension_set_open_block_func (self , try_opening_table_block );
790814 cmark_syntax_extension_set_get_type_string_func (self , get_type_string );
0 commit comments