markly 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/conduct.md +133 -0
  4. data/ext/markly/arena.c +9 -8
  5. data/ext/markly/autolink.c +217 -134
  6. data/ext/markly/blocks.c +27 -2
  7. data/ext/markly/cmark-gfm-core-extensions.h +11 -11
  8. data/ext/markly/cmark-gfm-extension_api.h +1 -0
  9. data/ext/markly/cmark-gfm.h +18 -2
  10. data/ext/markly/cmark.c +3 -3
  11. data/ext/markly/commonmark.c +19 -34
  12. data/ext/markly/extconf.rb +8 -1
  13. data/ext/markly/html.c +22 -6
  14. data/ext/markly/inlines.c +148 -51
  15. data/ext/markly/latex.c +6 -4
  16. data/ext/markly/man.c +7 -11
  17. data/ext/markly/map.c +11 -4
  18. data/ext/markly/map.h +5 -2
  19. data/ext/markly/markly.c +582 -586
  20. data/ext/markly/markly.h +1 -1
  21. data/ext/markly/node.c +76 -10
  22. data/ext/markly/node.h +42 -1
  23. data/ext/markly/parser.h +1 -0
  24. data/ext/markly/plaintext.c +12 -29
  25. data/ext/markly/references.c +1 -0
  26. data/ext/markly/render.c +15 -7
  27. data/ext/markly/scanners.c +13916 -10380
  28. data/ext/markly/scanners.h +8 -0
  29. data/ext/markly/scanners.re +47 -8
  30. data/ext/markly/strikethrough.c +1 -1
  31. data/ext/markly/table.c +81 -31
  32. data/ext/markly/xml.c +2 -1
  33. data/lib/markly/flags.rb +16 -0
  34. data/lib/markly/node/inspect.rb +59 -53
  35. data/lib/markly/node.rb +125 -58
  36. data/lib/markly/renderer/generic.rb +129 -124
  37. data/lib/markly/renderer/html.rb +294 -275
  38. data/lib/markly/version.rb +7 -1
  39. data/lib/markly.rb +36 -30
  40. data/license.md +39 -0
  41. data/readme.md +36 -0
  42. data.tar.gz.sig +0 -0
  43. metadata +61 -29
  44. metadata.gz.sig +0 -0
  45. data/bin/markly +0 -94
  46. data/lib/markly/markly.bundle +0 -0
data/ext/markly/inlines.c CHANGED
@@ -35,17 +35,24 @@ static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";
35
35
 
36
36
  typedef struct bracket {
37
37
  struct bracket *previous;
38
- struct delimiter *previous_delimiter;
39
38
  cmark_node *inl_text;
40
39
  bufsize_t position;
41
40
  bool image;
42
41
  bool active;
43
42
  bool bracket_after;
43
+ bool in_bracket_image0;
44
+ bool in_bracket_image1;
44
45
  } bracket;
45
46
 
47
+ #define FLAG_SKIP_HTML_CDATA (1u << 0)
48
+ #define FLAG_SKIP_HTML_DECLARATION (1u << 1)
49
+ #define FLAG_SKIP_HTML_PI (1u << 2)
50
+ #define FLAG_SKIP_HTML_COMMENT (1u << 3)
51
+
46
52
  typedef struct subject{
47
53
  cmark_mem *mem;
48
54
  cmark_chunk input;
55
+ unsigned flags;
49
56
  int line;
50
57
  bufsize_t pos;
51
58
  int block_offset;
@@ -55,6 +62,7 @@ typedef struct subject{
55
62
  bracket *last_bracket;
56
63
  bufsize_t backticks[MAXBACKTICKS + 1];
57
64
  bool scanned_for_backticks;
65
+ bool no_link_openers;
58
66
  } subject;
59
67
 
60
68
  // Extensions may populate this.
@@ -109,6 +117,24 @@ static cmark_node *make_str_with_entities(subject *subj,
109
117
  }
110
118
  }
111
119
 
120
+ // Like cmark_node_append_child but without costly sanity checks.
121
+ // Assumes that child was newly created.
122
+ static void append_child(cmark_node *node, cmark_node *child) {
123
+ cmark_node *old_last_child = node->last_child;
124
+
125
+ child->next = NULL;
126
+ child->prev = old_last_child;
127
+ child->parent = node;
128
+ node->last_child = child;
129
+
130
+ if (old_last_child) {
131
+ old_last_child->next = child;
132
+ } else {
133
+ // Also set first_child if node previously had no children.
134
+ node->first_child = child;
135
+ }
136
+ }
137
+
112
138
  // Duplicate a chunk by creating a copy of the buffer not by reusing the
113
139
  // buffer like cmark_chunk_dup does.
114
140
  static cmark_chunk chunk_clone(cmark_mem *mem, cmark_chunk *src) {
@@ -152,7 +178,7 @@ static CMARK_INLINE cmark_node *make_autolink(subject *subj,
152
178
  link->start_line = link->end_line = subj->line;
153
179
  link->start_column = start_column + 1;
154
180
  link->end_column = end_column + 1;
155
- cmark_node_append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
181
+ append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
156
182
  return link;
157
183
  }
158
184
 
@@ -161,6 +187,7 @@ static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset,
161
187
  int i;
162
188
  e->mem = mem;
163
189
  e->input = *chunk;
190
+ e->flags = 0;
164
191
  e->line = line_number;
165
192
  e->pos = 0;
166
193
  e->block_offset = block_offset;
@@ -172,6 +199,7 @@ static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset,
172
199
  e->backticks[i] = 0;
173
200
  }
174
201
  e->scanned_for_backticks = false;
202
+ e->no_link_openers = true;
175
203
  }
176
204
 
177
205
  static CMARK_INLINE int isbacktick(int c) { return (c == '`'); }
@@ -503,6 +531,7 @@ static void push_delimiter(subject *subj, unsigned char c, bool can_open,
503
531
  delim->can_open = can_open;
504
532
  delim->can_close = can_close;
505
533
  delim->inl_text = inl_text;
534
+ delim->position = subj->pos;
506
535
  delim->length = inl_text->as.literal.len;
507
536
  delim->previous = subj->last_delim;
508
537
  delim->next = NULL;
@@ -516,15 +545,24 @@ static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
516
545
  bracket *b = (bracket *)subj->mem->calloc(1, sizeof(bracket));
517
546
  if (subj->last_bracket != NULL) {
518
547
  subj->last_bracket->bracket_after = true;
548
+ b->in_bracket_image0 = subj->last_bracket->in_bracket_image0;
549
+ b->in_bracket_image1 = subj->last_bracket->in_bracket_image1;
519
550
  }
520
551
  b->image = image;
521
552
  b->active = true;
522
553
  b->inl_text = inl_text;
523
554
  b->previous = subj->last_bracket;
524
- b->previous_delimiter = subj->last_delim;
525
555
  b->position = subj->pos;
526
556
  b->bracket_after = false;
557
+ if (image) {
558
+ b->in_bracket_image1 = true;
559
+ } else {
560
+ b->in_bracket_image0 = true;
561
+ }
527
562
  subj->last_bracket = b;
563
+ if (!image) {
564
+ subj->no_link_openers = false;
565
+ }
528
566
  }
529
567
 
530
568
  // Assumes the subject has a c at the current position.
@@ -631,12 +669,13 @@ static cmark_syntax_extension *get_extension_for_special_char(cmark_parser *pars
631
669
  return NULL;
632
670
  }
633
671
 
634
- static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *stack_bottom) {
635
- delimiter *closer = subj->last_delim;
672
+ static void process_emphasis(cmark_parser *parser, subject *subj, bufsize_t stack_bottom) {
673
+ delimiter *candidate;
674
+ delimiter *closer = NULL;
636
675
  delimiter *opener;
637
676
  delimiter *old_closer;
638
677
  bool opener_found;
639
- delimiter *openers_bottom[3][128];
678
+ bufsize_t openers_bottom[3][128];
640
679
  int i;
641
680
 
642
681
  // initialize openers_bottom:
@@ -649,8 +688,10 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
649
688
  }
650
689
 
651
690
  // move back to first relevant delim.
652
- while (closer != NULL && closer->previous != stack_bottom) {
653
- closer = closer->previous;
691
+ candidate = subj->last_delim;
692
+ while (candidate != NULL && candidate->position >= stack_bottom) {
693
+ closer = candidate;
694
+ candidate = candidate->previous;
654
695
  }
655
696
 
656
697
  // now move forward, looking for closers, and handling each
@@ -660,8 +701,8 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
660
701
  // Now look backwards for first matching opener:
661
702
  opener = closer->previous;
662
703
  opener_found = false;
663
- while (opener != NULL && opener != stack_bottom &&
664
- opener != openers_bottom[closer->length % 3][closer->delim_char]) {
704
+ while (opener != NULL && opener->position >= stack_bottom &&
705
+ opener->position >= openers_bottom[closer->length % 3][closer->delim_char]) {
665
706
  if (opener->can_open && opener->delim_char == closer->delim_char) {
666
707
  // interior closer of size 2 can't match opener of size 1
667
708
  // or of size 1 can't match 2
@@ -687,27 +728,29 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
687
728
  } else {
688
729
  closer = closer->next;
689
730
  }
690
- } else if (closer->delim_char == '\'') {
731
+ } else if (closer->delim_char == '\'' || closer->delim_char == '"') {
691
732
  cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
692
- closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE);
693
- if (opener_found) {
694
- cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
695
- opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE);
733
+ if (closer->delim_char == '\'') {
734
+ closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE);
735
+ } else {
736
+ closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE);
696
737
  }
697
738
  closer = closer->next;
698
- } else if (closer->delim_char == '"') {
699
- cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
700
- closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE);
701
739
  if (opener_found) {
702
740
  cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
703
- opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE);
741
+ if (old_closer->delim_char == '\'') {
742
+ opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE);
743
+ } else {
744
+ opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE);
745
+ }
746
+ remove_delimiter(subj, opener);
747
+ remove_delimiter(subj, old_closer);
704
748
  }
705
- closer = closer->next;
706
749
  }
707
750
  if (!opener_found) {
708
751
  // set lower bound for future searches for openers
709
752
  openers_bottom[old_closer->length % 3][old_closer->delim_char] =
710
- old_closer->previous;
753
+ old_closer->position;
711
754
  if (!old_closer->can_open) {
712
755
  // we can remove a closer that can't be an
713
756
  // opener, once we've seen there's no
@@ -720,7 +763,8 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
720
763
  }
721
764
  }
722
765
  // free all delimiters in list until stack_bottom:
723
- while (subj->last_delim != NULL && subj->last_delim != stack_bottom) {
766
+ while (subj->last_delim != NULL &&
767
+ subj->last_delim->position >= stack_bottom) {
724
768
  remove_delimiter(subj, subj->last_delim);
725
769
  }
726
770
  }
@@ -759,7 +803,8 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener,
759
803
  tmp = opener_inl->next;
760
804
  while (tmp && tmp != closer_inl) {
761
805
  tmpnext = tmp->next;
762
- cmark_node_append_child(emph, tmp);
806
+ cmark_node_unlink(tmp);
807
+ append_child(emph, tmp);
763
808
  tmp = tmpnext;
764
809
  }
765
810
  cmark_node_insert_after(opener_inl, emph);
@@ -890,7 +935,63 @@ static cmark_node *handle_pointy_brace(subject *subj, int options) {
890
935
  }
891
936
 
892
937
  // finally, try to match an html tag
893
- matchlen = scan_html_tag(&subj->input, subj->pos);
938
+ if (subj->pos + 2 <= subj->input.len) {
939
+ int c = subj->input.data[subj->pos];
940
+ if (c == '!' && (subj->flags & FLAG_SKIP_HTML_COMMENT) == 0) {
941
+ c = subj->input.data[subj->pos+1];
942
+ if (c == '-' && subj->input.data[subj->pos+2] == '-') {
943
+ if (subj->input.data[subj->pos+3] == '>') {
944
+ matchlen = 4;
945
+ } else if (subj->input.data[subj->pos+3] == '-' &&
946
+ subj->input.data[subj->pos+4] == '>') {
947
+ matchlen = 5;
948
+ } else {
949
+ matchlen = scan_html_comment(&subj->input, subj->pos + 1);
950
+ if (matchlen > 0) {
951
+ matchlen += 1; // prefix "<"
952
+ } else { // no match through end of input: set a flag so
953
+ // we don't reparse looking for -->:
954
+ subj->flags |= FLAG_SKIP_HTML_COMMENT;
955
+ }
956
+ }
957
+ } else if (c == '[') {
958
+ if ((subj->flags & FLAG_SKIP_HTML_CDATA) == 0) {
959
+ matchlen = scan_html_cdata(&subj->input, subj->pos + 2);
960
+ if (matchlen > 0) {
961
+ // The regex doesn't require the final "]]>". But if we're not at
962
+ // the end of input, it must come after the match. Otherwise,
963
+ // disable subsequent scans to avoid quadratic behavior.
964
+ matchlen += 5; // prefix "![", suffix "]]>"
965
+ if (subj->pos + matchlen > subj->input.len) {
966
+ subj->flags |= FLAG_SKIP_HTML_CDATA;
967
+ matchlen = 0;
968
+ }
969
+ }
970
+ }
971
+ } else if ((subj->flags & FLAG_SKIP_HTML_DECLARATION) == 0) {
972
+ matchlen = scan_html_declaration(&subj->input, subj->pos + 1);
973
+ if (matchlen > 0) {
974
+ matchlen += 2; // prefix "!", suffix ">"
975
+ if (subj->pos + matchlen > subj->input.len) {
976
+ subj->flags |= FLAG_SKIP_HTML_DECLARATION;
977
+ matchlen = 0;
978
+ }
979
+ }
980
+ }
981
+ } else if (c == '?') {
982
+ if ((subj->flags & FLAG_SKIP_HTML_PI) == 0) {
983
+ // Note that we allow an empty match.
984
+ matchlen = scan_html_pi(&subj->input, subj->pos + 1);
985
+ matchlen += 3; // prefix "?", suffix "?>"
986
+ if (subj->pos + matchlen > subj->input.len) {
987
+ subj->flags |= FLAG_SKIP_HTML_PI;
988
+ matchlen = 0;
989
+ }
990
+ }
991
+ } else {
992
+ matchlen = scan_html_tag(&subj->input, subj->pos);
993
+ }
994
+ }
894
995
  if (matchlen > 0) {
895
996
  contents = cmark_chunk_dup(&subj->input, subj->pos - 1, matchlen + 1);
896
997
  subj->pos += matchlen;
@@ -1056,16 +1157,16 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
1056
1157
  return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
1057
1158
  }
1058
1159
 
1059
- if (!opener->active) {
1160
+ // If we got here, we matched a potential link/image text.
1161
+ // Now we check to see if it's a link/image.
1162
+ is_image = opener->image;
1163
+
1164
+ if (!is_image && subj->no_link_openers) {
1060
1165
  // take delimiter off stack
1061
1166
  pop_bracket(subj);
1062
1167
  return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
1063
1168
  }
1064
1169
 
1065
- // If we got here, we matched a potential link/image text.
1066
- // Now we check to see if it's a link/image.
1067
- is_image = opener->image;
1068
-
1069
1170
  after_link_text_pos = subj->pos;
1070
1171
 
1071
1172
  // First, look for an inline link.
@@ -1184,7 +1285,7 @@ noMatch:
1184
1285
  // being replacing the opening '[' text node with a `^footnote-ref]` node.
1185
1286
  cmark_node_insert_before(opener->inl_text, fnref);
1186
1287
 
1187
- process_emphasis(parser, subj, opener->previous_delimiter);
1288
+ process_emphasis(parser, subj, opener->position);
1188
1289
  // sometimes, the footnote reference text gets parsed into multiple nodes
1189
1290
  // i.e. '[^example]' parsed into '[', '^exam', 'ple]'.
1190
1291
  // this happens for ex with the autolink extension. when the autolinker
@@ -1229,31 +1330,22 @@ match:
1229
1330
  tmp = opener->inl_text->next;
1230
1331
  while (tmp) {
1231
1332
  tmpnext = tmp->next;
1232
- cmark_node_append_child(inl, tmp);
1333
+ cmark_node_unlink(tmp);
1334
+ append_child(inl, tmp);
1233
1335
  tmp = tmpnext;
1234
1336
  }
1235
1337
 
1236
1338
  // Free the bracket [:
1237
1339
  cmark_node_free(opener->inl_text);
1238
1340
 
1239
- process_emphasis(parser, subj, opener->previous_delimiter);
1341
+ process_emphasis(parser, subj, opener->position);
1240
1342
  pop_bracket(subj);
1241
1343
 
1242
- // Now, if we have a link, we also want to deactivate earlier link
1243
- // delimiters. (This code can be removed if we decide to allow links
1344
+ // Now, if we have a link, we also want to deactivate links until
1345
+ // we get a new opener. (This code can be removed if we decide to allow links
1244
1346
  // inside links.)
1245
1347
  if (!is_image) {
1246
- opener = subj->last_bracket;
1247
- while (opener != NULL) {
1248
- if (!opener->image) {
1249
- if (!opener->active) {
1250
- break;
1251
- } else {
1252
- opener->active = false;
1253
- }
1254
- }
1255
- opener = opener->previous;
1256
- }
1348
+ subj->no_link_openers = true;
1257
1349
  }
1258
1350
 
1259
1351
  return NULL;
@@ -1431,7 +1523,7 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
1431
1523
  new_inl = make_str(subj, startpos, endpos - 1, contents);
1432
1524
  }
1433
1525
  if (new_inl != NULL) {
1434
- cmark_node_append_child(parent, new_inl);
1526
+ append_child(parent, new_inl);
1435
1527
  }
1436
1528
 
1437
1529
  return 1;
@@ -1450,7 +1542,7 @@ void cmark_parse_inlines(cmark_parser *parser,
1450
1542
  while (!is_eof(&subj) && parse_inline(parser, &subj, parent, options))
1451
1543
  ;
1452
1544
 
1453
- process_emphasis(parser, &subj, NULL);
1545
+ process_emphasis(parser, &subj, 0);
1454
1546
  // free bracket and delim stack
1455
1547
  while (subj.last_delim) {
1456
1548
  remove_delimiter(&subj, subj.last_delim);
@@ -1662,10 +1754,15 @@ cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser) {
1662
1754
  }
1663
1755
 
1664
1756
  int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int image) {
1665
- for (bracket *b = parser->last_bracket; b; b = b->previous)
1666
- if (b->active && b->image == (image != 0))
1667
- return 1;
1668
- return 0;
1757
+ bracket *b = parser->last_bracket;
1758
+ if (!b) {
1759
+ return 0;
1760
+ }
1761
+ if (image != 0) {
1762
+ return b->in_bracket_image1;
1763
+ } else {
1764
+ return b->in_bracket_image0;
1765
+ }
1669
1766
  }
1670
1767
 
1671
1768
  void cmark_node_unput(cmark_node *node, int n) {
data/ext/markly/latex.c CHANGED
@@ -385,10 +385,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
385
385
  break;
386
386
 
387
387
  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
+ }
392
394
  }
393
395
  break;
394
396
 
data/ext/markly/man.c CHANGED
@@ -74,7 +74,6 @@ static void S_outc(cmark_renderer *renderer, cmark_node *node,
74
74
 
75
75
  static int S_render_node(cmark_renderer *renderer, cmark_node *node,
76
76
  cmark_event_type ev_type, int options) {
77
- cmark_node *tmp;
78
77
  int list_number;
79
78
  bool entering = (ev_type == CMARK_EVENT_ENTER);
80
79
  bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
@@ -123,12 +122,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
123
122
  if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
124
123
  LIT("\\[bu] 2");
125
124
  } 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
- }
125
+ list_number = cmark_node_get_item_index(node);
132
126
  char list_number_s[LIST_NUMBER_SIZE];
133
127
  snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
134
128
  LIT(list_number_s);
@@ -225,10 +219,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
225
219
  break;
226
220
 
227
221
  case CMARK_NODE_STRONG:
228
- if (entering) {
229
- LIT("\\f[B]");
230
- } else {
231
- LIT("\\f[]");
222
+ if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
223
+ if (entering) {
224
+ LIT("\\f[B]");
225
+ } else {
226
+ LIT("\\f[]");
227
+ }
232
228
  }
233
229
  break;
234
230
 
data/ext/markly/map.c CHANGED
@@ -51,7 +51,7 @@ refsearch(const void *label, const void *p2) {
51
51
  }
52
52
 
53
53
  static void sort_map(cmark_map *map) {
54
- unsigned int i = 0, last = 0, size = map->size;
54
+ size_t i = 0, last = 0, size = map->size;
55
55
  cmark_map_entry *r = map->refs, **sorted = NULL;
56
56
 
57
57
  sorted = (cmark_map_entry **)map->mem->calloc(size, sizeof(cmark_map_entry *));
@@ -73,6 +73,7 @@ static void sort_map(cmark_map *map) {
73
73
 
74
74
  cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label) {
75
75
  cmark_map_entry **ref = NULL;
76
+ cmark_map_entry *r = NULL;
76
77
  unsigned char *norm;
77
78
 
78
79
  if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
@@ -91,10 +92,15 @@ cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label) {
91
92
  ref = (cmark_map_entry **)bsearch(norm, map->sorted, map->size, sizeof(cmark_map_entry *), refsearch);
92
93
  map->mem->free(norm);
93
94
 
94
- if (!ref)
95
- return NULL;
95
+ if (ref != NULL) {
96
+ r = ref[0];
97
+ /* Check for expansion limit */
98
+ if (r->size > map->max_ref_size - map->ref_size)
99
+ return NULL;
100
+ map->ref_size += r->size;
101
+ }
96
102
 
97
- return ref[0];
103
+ return r;
98
104
  }
99
105
 
100
106
  void cmark_map_free(cmark_map *map) {
@@ -118,5 +124,6 @@ cmark_map *cmark_map_new(cmark_mem *mem, cmark_map_free_f free) {
118
124
  cmark_map *map = (cmark_map *)mem->calloc(1, sizeof(cmark_map));
119
125
  map->mem = mem;
120
126
  map->free = free;
127
+ map->max_ref_size = UINT_MAX;
121
128
  return map;
122
129
  }
data/ext/markly/map.h CHANGED
@@ -10,7 +10,8 @@ extern "C" {
10
10
  struct cmark_map_entry {
11
11
  struct cmark_map_entry *next;
12
12
  unsigned char *label;
13
- unsigned int age;
13
+ size_t age;
14
+ size_t size;
14
15
  };
15
16
 
16
17
  typedef struct cmark_map_entry cmark_map_entry;
@@ -23,7 +24,9 @@ struct cmark_map {
23
24
  cmark_mem *mem;
24
25
  cmark_map_entry *refs;
25
26
  cmark_map_entry **sorted;
26
- unsigned int size;
27
+ size_t size;
28
+ size_t ref_size;
29
+ size_t max_ref_size;
27
30
  cmark_map_free_f free;
28
31
  };
29
32