ext/redcarpet/markdown.c in redcarpet-2.0.0b vs ext/redcarpet/markdown.c in redcarpet-2.0.0b3

- old
+ new

@@ -92,11 +92,13 @@ &char_superscript, }; /* render • structure containing one particular render */ struct render { - struct mkd_renderer make; + struct sd_callbacks cb; + void *opaque; + struct array refs; char active_char[256]; struct parray work_bufs[2]; unsigned int ext_flags; size_t max_nesting; @@ -352,14 +354,14 @@ /* copying inactive chars into the output */ while (end < size && (action = rndr->active_char[(unsigned char)data[end]]) == 0) { end++; } - if (rndr->make.normal_text) { + if (rndr->cb.normal_text) { work.data = data + i; work.size = end - i; - rndr->make.normal_text(ob, &work, rndr->make.opaque); + rndr->cb.normal_text(ob, &work, rndr->opaque); } else bufput(ob, data + i, end - i); if (end >= size) break; @@ -459,11 +461,11 @@ { size_t i = 0, len; struct buf *work = 0; int r; - if (!rndr->make.emphasis) return 0; + if (!rndr->cb.emphasis) return 0; /* skipping one symbol if coming from emph3 */ if (size > 1 && data[0] == c && data[1] == c) i = 1; while (i < size) { @@ -484,11 +486,11 @@ continue; } work = rndr_newbuf(rndr, BUFFER_SPAN); parse_inline(work, rndr, data, i); - r = rndr->make.emphasis(ob, work, rndr->make.opaque); + r = rndr->cb.emphasis(ob, work, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); return r ? i + 1 : 0; } } @@ -502,11 +504,11 @@ int (*render_method)(struct buf *ob, struct buf *text, void *opaque); size_t i = 0, len; struct buf *work = 0; int r; - render_method = (c == '~') ? rndr->make.strikethrough : rndr->make.double_emphasis; + render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis; if (!render_method) return 0; while (i < size) { @@ -515,11 +517,11 @@ i += len; if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !isspace(data[i - 1])) { work = rndr_newbuf(rndr, BUFFER_SPAN); parse_inline(work, rndr, data, i); - r = render_method(ob, work, rndr->make.opaque); + r = render_method(ob, work, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); return r ? i + 2 : 0; } i++; } @@ -541,16 +543,16 @@ /* skip whitespace preceded symbols */ if (data[i] != c || isspace(data[i - 1])) continue; - if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->make.triple_emphasis) { + if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) { /* triple symbol found */ struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN); parse_inline(work, rndr, data, i); - r = rndr->make.triple_emphasis(ob, work, rndr->make.opaque); + r = rndr->cb.triple_emphasis(ob, work, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); return r ? i + 3 : 0; } else if (i + 1 < size && data[i + 1] == c) { /* double symbol found, handing over to emph1 */ @@ -611,11 +613,11 @@ /* removing the last space from ob and rendering */ while (ob->size && ob->data[ob->size - 1] == ' ') ob->size--; - return rndr->make.linebreak(ob, rndr->make.opaque) ? 1 : 0; + return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0; } /* char_codespan • '`' parsing a code span (assuming codespan != 0) */ static size_t @@ -647,14 +649,14 @@ f_end--; /* real code span */ if (f_begin < f_end) { struct buf work = { data + f_begin, f_end - f_begin, 0, 0, 0 }; - if (!rndr->make.codespan(ob, &work, rndr->make.opaque)) + if (!rndr->cb.codespan(ob, &work, rndr->opaque)) end = 0; } else { - if (!rndr->make.codespan(ob, 0, rndr->make.opaque)) + if (!rndr->cb.codespan(ob, 0, rndr->opaque)) end = 0; } return end; } @@ -669,14 +671,14 @@ if (size > 1) { if (strchr(escape_chars, data[1]) == NULL) return 0; - if (rndr->make.normal_text) { + if (rndr->cb.normal_text) { work.data = data + 1; work.size = 1; - rndr->make.normal_text(ob, &work, rndr->make.opaque); + rndr->cb.normal_text(ob, &work, rndr->opaque); } else bufputc(ob, data[1]); } return 2; @@ -699,14 +701,14 @@ if (end < size && data[end] == ';') end++; /* real entity */ else return 0; /* lone '&' */ - if (rndr->make.entity) { + if (rndr->cb.entity) { work.data = data; work.size = end; - rndr->make.entity(ob, &work, rndr->make.opaque); + rndr->cb.entity(ob, &work, rndr->opaque); } else bufput(ob, data, end); return end; } @@ -719,20 +721,20 @@ size_t end = tag_length(data, size, &altype); struct buf work = { data, end, 0, 0, 0 }; int ret = 0; if (end > 2) { - if (rndr->make.autolink && altype != MKDA_NOT_AUTOLINK) { + if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) { struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN); work.data = data + 1; work.size = end - 2; unscape_text(u_link, &work); - ret = rndr->make.autolink(ob, u_link, altype, rndr->make.opaque); + ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); } - else if (rndr->make.raw_html_tag) - ret = rndr->make.raw_html_tag(ob, &work, rndr->make.opaque); + else if (rndr->cb.raw_html_tag) + ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque); } if (!ret) return 0; else return end; } @@ -741,22 +743,22 @@ char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size) { struct buf *link, *link_url; size_t link_len, rewind; - if (!rndr->make.link) + if (!rndr->cb.link) return 0; link = rndr_newbuf(rndr, BUFFER_SPAN); if ((link_len = sd_autolink__www(&rewind, link, data, offset, size)) > 0) { link_url = rndr_newbuf(rndr, BUFFER_SPAN); BUFPUTSL(link_url, "http://"); bufput(link_url, link->data, link->size); ob->size -= rewind; - rndr->make.link(ob, link_url, NULL, link, rndr->make.opaque); + rndr->cb.link(ob, link_url, NULL, link, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); } rndr_popbuf(rndr, BUFFER_SPAN); return link_len; @@ -766,18 +768,18 @@ char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size) { struct buf *link; size_t link_len, rewind; - if (!rndr->make.autolink) + if (!rndr->cb.autolink) return 0; link = rndr_newbuf(rndr, BUFFER_SPAN); if ((link_len = sd_autolink__email(&rewind, link, data, offset, size)) > 0) { ob->size -= rewind; - rndr->make.autolink(ob, link, MKDA_EMAIL, rndr->make.opaque); + rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque); } rndr_popbuf(rndr, BUFFER_SPAN); return link_len; } @@ -786,18 +788,18 @@ char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size) { struct buf *link; size_t link_len, rewind; - if (!rndr->make.autolink) + if (!rndr->cb.autolink) return 0; link = rndr_newbuf(rndr, BUFFER_SPAN); if ((link_len = sd_autolink__url(&rewind, link, data, offset, size)) > 0) { ob->size -= rewind; - rndr->make.autolink(ob, link, MKDA_NORMAL, rndr->make.opaque); + rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque); } rndr_popbuf(rndr, BUFFER_SPAN); return link_len; } @@ -814,11 +816,11 @@ struct buf *u_link = 0; size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size; int text_has_nl = 0, ret = 0; /* checking whether the correct renderer exists */ - if ((is_img && !rndr->make.image) || (!is_img && !rndr->make.link)) + if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link)) goto cleanup; /* looking for the matching closing bracket */ for (level = 1; i < size; i++) { if (data[i] == '\n') @@ -1011,13 +1013,13 @@ /* calling the relevant rendering function */ if (is_img) { if (ob->size && ob->data[ob->size - 1] == '!') ob->size -= 1; - ret = rndr->make.image(ob, u_link, title, content, rndr->make.opaque); + ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque); } else { - ret = rndr->make.link(ob, u_link, title, content, rndr->make.opaque); + ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque); } /* cleanup */ cleanup: rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size; @@ -1028,11 +1030,11 @@ char_superscript(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size) { size_t sup_start, sup_len; struct buf *sup; - if (!rndr->make.superscript) + if (!rndr->cb.superscript) return 0; if (size < 2) return 0; @@ -1054,11 +1056,11 @@ if (sup_len - sup_start == 0) return (sup_start == 2) ? 3 : 0; sup = rndr_newbuf(rndr, BUFFER_SPAN); parse_inline(sup, rndr, data + sup_start, sup_len - sup_start); - rndr->make.superscript(ob, sup, rndr->make.opaque); + rndr->cb.superscript(ob, sup, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); return (sup_start == 2) ? sup_len + 1 : sup_len; } @@ -1315,12 +1317,12 @@ } beg = end; } parse_block(out, rndr, work_data, work_size); - if (rndr->make.blockquote) - rndr->make.blockquote(ob, out, rndr->make.opaque); + if (rndr->cb.blockquote) + rndr->cb.blockquote(ob, out, rndr->opaque); rndr_popbuf(rndr, BUFFER_BLOCK); return end; } static size_t @@ -1339,11 +1341,11 @@ if (is_empty(data + i, size - i) || (level = is_headerline(data + i, size - i)) != 0) break; if (rndr->ext_flags & MKDEXT_LAX_HTML_BLOCKS) { - if (data[i] == '<' && rndr->make.blockhtml && parse_htmlblock(ob, rndr, data + i, size - i, 0)) { + if (data[i] == '<' && rndr->cb.blockhtml && parse_htmlblock(ob, rndr, data + i, size - i, 0)) { end = i; break; } } @@ -1360,12 +1362,12 @@ work.size--; if (!level) { struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK); parse_inline(tmp, rndr, work.data, work.size); - if (rndr->make.paragraph) - rndr->make.paragraph(ob, tmp, rndr->make.opaque); + if (rndr->cb.paragraph) + rndr->cb.paragraph(ob, tmp, rndr->opaque); rndr_popbuf(rndr, BUFFER_BLOCK); } else { struct buf *header_work; if (work.size) { @@ -1382,12 +1384,12 @@ if (work.size > 0) { struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK); parse_inline(tmp, rndr, work.data, work.size); - if (rndr->make.paragraph) - rndr->make.paragraph(ob, tmp, rndr->make.opaque); + if (rndr->cb.paragraph) + rndr->cb.paragraph(ob, tmp, rndr->opaque); rndr_popbuf(rndr, BUFFER_BLOCK); work.data += beg; work.size = i - beg; } @@ -1395,12 +1397,12 @@ } header_work = rndr_newbuf(rndr, BUFFER_SPAN); parse_inline(header_work, rndr, work.data, work.size); - if (rndr->make.header) - rndr->make.header(ob, header_work, (int)level, rndr->make.opaque); + if (rndr->cb.header) + rndr->cb.header(ob, header_work, (int)level, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); } return end; @@ -1441,12 +1443,12 @@ } if (work->size && work->data[work->size - 1] != '\n') bufputc(work, '\n'); - if (rndr->make.blockcode) - rndr->make.blockcode(ob, work, lang.size ? &lang : NULL, rndr->make.opaque); + if (rndr->cb.blockcode) + rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque); rndr_popbuf(rndr, BUFFER_BLOCK); return beg; } @@ -1482,12 +1484,12 @@ while (work->size && work->data[work->size - 1] == '\n') work->size -= 1; bufputc(work, '\n'); - if (rndr->make.blockcode) - rndr->make.blockcode(ob, work, NULL, rndr->make.opaque); + if (rndr->cb.blockcode) + rndr->cb.blockcode(ob, work, NULL, rndr->opaque); rndr_popbuf(rndr, BUFFER_BLOCK); return beg; } @@ -1597,12 +1599,12 @@ else parse_inline(inter, rndr, work->data, work->size); } /* render of li itself */ - if (rndr->make.listitem) - rndr->make.listitem(ob, inter, *flags, rndr->make.opaque); + if (rndr->cb.listitem) + rndr->cb.listitem(ob, inter, *flags, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); rndr_popbuf(rndr, BUFFER_SPAN); return beg; } @@ -1623,12 +1625,12 @@ if (!j || (flags & MKD_LI_END)) break; } - if (rndr->make.list) - rndr->make.list(ob, work, flags, rndr->make.opaque); + if (rndr->cb.list) + rndr->cb.list(ob, work, flags, rndr->opaque); rndr_popbuf(rndr, BUFFER_BLOCK); return i; } /* parse_atxheader • parsing of atx-style headers */ @@ -1655,12 +1657,12 @@ if (end > i) { struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN); parse_inline(work, rndr, data + i, end - i); - if (rndr->make.header) - rndr->make.header(ob, work, (int)level, rndr->make.opaque); + if (rndr->cb.header) + rndr->cb.header(ob, work, (int)level, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); } return skip; @@ -1730,12 +1732,12 @@ if (i < size) j = is_empty(data + i, size - i); if (j) { work.size = i + j; - if (do_render && rndr->make.blockhtml) - rndr->make.blockhtml(ob, &work, rndr->make.opaque); + if (do_render && rndr->cb.blockhtml) + rndr->cb.blockhtml(ob, &work, rndr->opaque); return work.size; } } /* HR, which is the only self-closing block tag considered */ @@ -1747,12 +1749,12 @@ if (i + 1 < size) { i++; j = is_empty(data + i, size - i); if (j) { work.size = i + j; - if (do_render && rndr->make.blockhtml) - rndr->make.blockhtml(ob, &work, rndr->make.opaque); + if (do_render && rndr->cb.blockhtml) + rndr->cb.blockhtml(ob, &work, rndr->opaque); return work.size; } } } @@ -1789,12 +1791,12 @@ if (!found) return 0; /* the end of the block has been found */ work.size = i; - if (do_render && rndr->make.blockhtml) - rndr->make.blockhtml(ob, &work, rndr->make.opaque); + if (do_render && rndr->cb.blockhtml) + rndr->cb.blockhtml(ob, &work, rndr->opaque); return i; } static void @@ -1826,25 +1828,25 @@ while (cell_end > cell_start && isspace(data[cell_end])) cell_end--; parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start); - if (rndr->make.table_cell) - rndr->make.table_cell(row_work, cell_work, col_data ? col_data[col] : 0, rndr->make.opaque); + if (rndr->cb.table_cell) + rndr->cb.table_cell(row_work, cell_work, col_data ? col_data[col] : 0, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); i++; } for (; col < columns; ++col) { struct buf empty_cell = {0, 0, 0, 0, 0}; - if (rndr->make.table_cell) - rndr->make.table_cell(row_work, &empty_cell, col_data ? col_data[col] : 0, rndr->make.opaque); + if (rndr->cb.table_cell) + rndr->cb.table_cell(row_work, &empty_cell, col_data ? col_data[col] : 0, rndr->opaque); } - if (rndr->make.table_row) - rndr->make.table_row(ob, row_work, rndr->make.opaque); + if (rndr->cb.table_row) + rndr->cb.table_row(ob, row_work, rndr->opaque); rndr_popbuf(rndr, BUFFER_SPAN); } static size_t @@ -1954,12 +1956,12 @@ parse_table_row(body_work, rndr, data + row_start, i - row_start, columns, col_data); i++; } - if (rndr->make.table) - rndr->make.table(ob, header_work, body_work, rndr->make.opaque); + if (rndr->cb.table) + rndr->cb.table(ob, header_work, body_work, rndr->opaque); } free(col_data); rndr_popbuf(rndr, BUFFER_SPAN); rndr_popbuf(rndr, BUFFER_BLOCK); @@ -1983,20 +1985,20 @@ end = size - beg; if (is_atxheader(rndr, txt_data, end)) beg += parse_atxheader(ob, rndr, txt_data, end); - else if (data[beg] == '<' && rndr->make.blockhtml && + else if (data[beg] == '<' && rndr->cb.blockhtml && (i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0) beg += i; else if ((i = is_empty(txt_data, end)) != 0) beg += i; else if (is_hrule(txt_data, end)) { - if (rndr->make.hrule) - rndr->make.hrule(ob, rndr->make.opaque); + if (rndr->cb.hrule) + rndr->cb.hrule(ob, rndr->opaque); while (beg < size && data[beg] != '\n') beg++; beg++; @@ -2165,49 +2167,58 @@ * EXPORTED FUNCTIONS * **********************/ /* markdown • parses the input buffer and renders it into the output buffer */ void -sd_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer, unsigned int extensions) { +sd_markdown(struct buf *ob, + const struct buf *ib, + unsigned int extensions, + const struct sd_callbacks *callbacks, + void *opaque) { + + static const float MARKDOWN_GROW_FACTOR = 1.4f; + struct link_ref *lr; struct buf *text; size_t i, beg, end; struct render rndr; /* filling the render structure */ - if (!rndrer) + if (!callbacks) return; text = bufnew(64); if (!text) return; /* Preallocate enough space for our buffer to avoid expanding while copying */ bufgrow(text, ib->size); - memcpy(&rndr.make, rndrer, sizeof(struct mkd_renderer)); + memcpy(&rndr.cb, callbacks, sizeof(struct sd_callbacks)); arr_init(&rndr.refs, sizeof (struct link_ref)); parr_init(&rndr.work_bufs[BUFFER_BLOCK]); parr_init(&rndr.work_bufs[BUFFER_SPAN]); - for (i = 0; i < 256; i++) - rndr.active_char[i] = 0; +/* for (i = 0; i < 256; i++) + rndr.active_char[i] = 0; */ - if (rndr.make.emphasis || rndr.make.double_emphasis || rndr.make.triple_emphasis) { + memset(rndr.active_char, 0x0, 256); + + if (rndr.cb.emphasis || rndr.cb.double_emphasis || rndr.cb.triple_emphasis) { rndr.active_char['*'] = MD_CHAR_EMPHASIS; rndr.active_char['_'] = MD_CHAR_EMPHASIS; if (extensions & MKDEXT_STRIKETHROUGH) rndr.active_char['~'] = MD_CHAR_EMPHASIS; } - if (rndr.make.codespan) + if (rndr.cb.codespan) rndr.active_char['`'] = MD_CHAR_CODESPAN; - if (rndr.make.linebreak) + if (rndr.cb.linebreak) rndr.active_char['\n'] = MD_CHAR_LINEBREAK; - if (rndr.make.image || rndr.make.link) + if (rndr.cb.image || rndr.cb.link) rndr.active_char['['] = MD_CHAR_LINK; rndr.active_char['<'] = MD_CHAR_LANGLE; rndr.active_char['\\'] = MD_CHAR_ESCAPE; rndr.active_char['&'] = MD_CHAR_ENTITITY; @@ -2221,10 +2232,11 @@ if (extensions & MKDEXT_SUPERSCRIPT) rndr.active_char['^'] = MD_CHAR_SUPERSCRIPT; /* Extension data */ rndr.ext_flags = extensions; + rndr.opaque = opaque; rndr.max_nesting = 16; /* first pass: looking for references, copying everything else */ beg = 0; while (beg < ib->size) /* iterating over lines */ @@ -2251,23 +2263,26 @@ /* sorting the reference array */ if (rndr.refs.size) qsort(rndr.refs.base, rndr.refs.size, rndr.refs.unit, cmp_link_ref_sort); + /* pre-grow the output buffer to minimize allocations */ + bufgrow(ob, text->size * MARKDOWN_GROW_FACTOR); + /* second pass: actual rendering */ - if (rndr.make.doc_header) - rndr.make.doc_header(ob, rndr.make.opaque); + if (rndr.cb.doc_header) + rndr.cb.doc_header(ob, rndr.opaque); if (text->size) { /* adding a final newline if not already present */ if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') bufputc(text, '\n'); parse_block(ob, &rndr, text->data, text->size); } - if (rndr.make.doc_footer) - rndr.make.doc_footer(ob, rndr.make.opaque); + if (rndr.cb.doc_footer) + rndr.cb.doc_footer(ob, rndr.opaque); /* clean-up */ bufrelease(text); lr = rndr.refs.base; for (i = 0; i < (size_t)rndr.refs.size; i++) {