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++) {