diff options
-rw-r--r-- | man/man3/cmark.3 | 81 | ||||
-rw-r--r-- | src/blocks.c | 110 | ||||
-rw-r--r-- | src/buffer.c | 19 | ||||
-rw-r--r-- | src/buffer.h | 9 | ||||
-rw-r--r-- | src/chunk.h | 17 | ||||
-rw-r--r-- | src/cmark.c | 30 | ||||
-rw-r--r-- | src/cmark.h | 38 | ||||
-rw-r--r-- | src/html.c | 2 | ||||
-rw-r--r-- | src/inlines.c | 158 | ||||
-rw-r--r-- | src/inlines.h | 8 | ||||
-rw-r--r-- | src/iterator.c | 10 | ||||
-rw-r--r-- | src/iterator.h | 2 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/memory.h | 12 | ||||
-rw-r--r-- | src/node.c | 63 | ||||
-rw-r--r-- | src/node.h | 10 | ||||
-rw-r--r-- | src/parser.h | 2 | ||||
-rwxr-xr-x | src/references.c | 35 | ||||
-rw-r--r-- | src/references.h | 4 | ||||
-rw-r--r-- | src/render.c | 12 | ||||
-rw-r--r-- | src/render.h | 2 | ||||
-rw-r--r-- | src/xml.c | 2 |
22 files changed, 343 insertions, 285 deletions
diff --git a/man/man3/cmark.3 b/man/man3/cmark.3 index 5642e59..ca304e6 100644 --- a/man/man3/cmark.3 +++ b/man/man3/cmark.3 @@ -1,4 +1,4 @@ -.TH cmark 3 "May 14, 2016" "LOCAL" "Library Functions Manual" +.TH cmark 3 "June 02, 2016" "LOCAL" "Library Functions Manual" .SH NAME .PP @@ -15,7 +15,7 @@ Simple Interface .PP Convert \f[I]text\f[] (assumed to be a UTF\-8 encoded string with length \f[I]len\f[]) from CommonMark Markdown to HTML, returning a -null\-terminated, UTF\-8\-encoded string. It is the caller's +null\-terminated, UTF\-8\-encoded string. It is the caller\[cq]s responsibility to free the returned buffer. .SS @@ -96,6 +96,25 @@ typedef enum { .SS +Custom memory allocator support + +.PP +.nf +\fC +.RS 0n +typedef struct cmark_mem { + void *(*calloc)(size_t, size_t); + void (*free)(void *); +} cmark_mem; +.RE +\f[] +.fi + +.PP +Defines the memory allocation functions to be used by CMark when parsing +and allocating a document tree + +.SS Creating and Destroying Nodes .PP @@ -103,8 +122,15 @@ Creating and Destroying Nodes .PP Creates a new node of type \f[I]type\f[]. Note that the node may have -other required properties, which it is the caller's responsibility to -assign. +other required properties, which it is the caller\[cq]s responsibility +to assign. + +.PP +\fIcmark_node *\f[] \fBcmark_node_new2\f[](\fIcmark_node_type type\f[], \fIcmark_mem *mem\f[]) + +.PP +Same as \f[C]cmark_node_new\f[], but explicitly listing the memory +allocator used to allocate the node .PP \fIvoid\f[] \fBcmark_node_free\f[](\fIcmark_node *node\f[]) @@ -378,7 +404,8 @@ Returns 1 if \f[I]node\f[] is a tight list, 0 otherwise. \fIint\f[] \fBcmark_node_set_list_tight\f[](\fIcmark_node *node\f[], \fIint tight\f[]) .PP -Sets the "tightness" of a list. Returns 1 on success, 0 on failure. +Sets the \[lq]tightness\[rq] of a list. Returns 1 on success, 0 on +failure. .PP \fIconst char *\f[] \fBcmark_node_get_fence_info\f[](\fIcmark_node *node\f[]) @@ -425,31 +452,31 @@ on failure. \fIconst char *\f[] \fBcmark_node_get_on_enter\f[](\fIcmark_node *node\f[]) .PP -Returns the literal "on enter" text for a custom \f[I]node\f[], or an -empty string if no on_enter is set. +Returns the literal \[lq]on enter\[rq] text for a custom \f[I]node\f[], +or an empty string if no on_enter is set. .PP \fIint\f[] \fBcmark_node_set_on_enter\f[](\fIcmark_node *node\f[], \fIconst char *on_enter\f[]) .PP -Sets the literal text to render "on enter" for a custom \f[I]node\f[]. -Any children of the node will be rendered after this text. Returns 1 on -success 0 on failure. +Sets the literal text to render \[lq]on enter\[rq] for a custom +\f[I]node\f[]. Any children of the node will be rendered after this +text. Returns 1 on success 0 on failure. .PP \fIconst char *\f[] \fBcmark_node_get_on_exit\f[](\fIcmark_node *node\f[]) .PP -Returns the literal "on exit" text for a custom \f[I]node\f[], or an -empty string if no on_exit is set. +Returns the literal \[lq]on exit\[rq] text for a custom \f[I]node\f[], +or an empty string if no on_exit is set. .PP \fIint\f[] \fBcmark_node_set_on_exit\f[](\fIcmark_node *node\f[], \fIconst char *on_exit\f[]) .PP -Sets the literal text to render "on exit" for a custom \f[I]node\f[]. -Any children of the node will be rendered before this text. Returns 1 on -success 0 on failure. +Sets the literal text to render \[lq]on exit\[rq] for a custom +\f[I]node\f[]. Any children of the node will be rendered before this +text. Returns 1 on success 0 on failure. .PP \fIint\f[] \fBcmark_node_get_start_line\f[](\fIcmark_node *node\f[]) @@ -563,6 +590,12 @@ cmark_parser_free(parser); Creates a new parser object. .PP +\fIcmark_parser *\f[] \fBcmark_parser_new2\f[](\fIint options\f[], \fIcmark_mem *mem\f[]) + +.PP +Creates a new parser object with the given memory allocator + +.PP \fIvoid\f[] \fBcmark_parser_free\f[](\fIcmark_parser *parser\f[]) .PP @@ -604,36 +637,36 @@ Rendering \fIchar *\f[] \fBcmark_render_xml\f[](\fIcmark_node *root\f[], \fIint options\f[]) .PP -Render a \f[I]node\f[] tree as XML. It is the caller's responsibility to -free the returned buffer. +Render a \f[I]node\f[] tree as XML. It is the caller\[cq]s +responsibility to free the returned buffer. .PP \fIchar *\f[] \fBcmark_render_html\f[](\fIcmark_node *root\f[], \fIint options\f[]) .PP Render a \f[I]node\f[] tree as an HTML fragment. It is up to the user to -add an appropriate header and footer. It is the caller's responsibility -to free the returned buffer. +add an appropriate header and footer. It is the caller\[cq]s +responsibility to free the returned buffer. .PP \fIchar *\f[] \fBcmark_render_man\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[]) .PP Render a \f[I]node\f[] tree as a groff man page, without the header. It -is the caller's responsibility to free the returned buffer. +is the caller\[cq]s responsibility to free the returned buffer. .PP \fIchar *\f[] \fBcmark_render_commonmark\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[]) .PP -Render a \f[I]node\f[] tree as a commonmark document. It is the caller's -responsibility to free the returned buffer. +Render a \f[I]node\f[] tree as a commonmark document. It is the +caller\[cq]s responsibility to free the returned buffer. .PP \fIchar *\f[] \fBcmark_render_latex\f[](\fIcmark_node *root\f[], \fIint options\f[], \fIint width\f[]) .PP -Render a \f[I]node\f[] tree as a LaTeX document. It is the caller's +Render a \f[I]node\f[] tree as a LaTeX document. It is the caller\[cq]s responsibility to free the returned buffer. .SS @@ -744,7 +777,7 @@ with the replacement character U+FFFD. .fi .PP -Convert straight quotes to curly, \-\-\- to em dashes, \-\- to en +Convert straight quotes to curly, \[em] to em dashes, \[en] to en dashes. .SS diff --git a/src/blocks.c b/src/blocks.c index 07eacc6..ef1790e 100644 --- a/src/blocks.c +++ b/src/blocks.c @@ -44,36 +44,38 @@ static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer, static void S_process_line(cmark_parser *parser, const unsigned char *buffer, bufsize_t bytes); -static cmark_node *make_block(cmark_node_type tag, int start_line, +static cmark_node *make_block(cmark_mem *mem, cmark_node_type tag, int start_line, int start_column) { cmark_node *e; - e = (cmark_node *)cmark_calloc(1, sizeof(*e)); + e = (cmark_node *)mem->calloc(1, sizeof(*e)); + cmark_strbuf_init(mem, &e->content, 32); e->type = tag; e->open = true; e->start_line = start_line; e->start_column = start_column; e->end_line = start_line; - cmark_strbuf_init(&e->string_content, 32); return e; } // Create a root document node. -static cmark_node *make_document() { - cmark_node *e = make_block(CMARK_NODE_DOCUMENT, 1, 1); +static cmark_node *make_document(cmark_mem *mem) { + cmark_node *e = make_block(mem, CMARK_NODE_DOCUMENT, 1, 1); return e; } -cmark_parser *cmark_parser_new(int options) { - cmark_parser *parser = (cmark_parser *)cmark_calloc(1, sizeof(cmark_parser)); - cmark_node *document = make_document(); - cmark_strbuf *line = (cmark_strbuf *)cmark_calloc(1, sizeof(cmark_strbuf)); - cmark_strbuf *buf = (cmark_strbuf *)cmark_calloc(1, sizeof(cmark_strbuf)); - cmark_strbuf_init(line, 256); - cmark_strbuf_init(buf, 0); - - parser->refmap = cmark_reference_map_new(); +cmark_parser *cmark_parser_new2(int options, cmark_mem *mem) { + cmark_parser *parser = mem->calloc(1, sizeof(cmark_parser)); + parser->mem = mem; + + cmark_node *document = make_document(mem); + cmark_strbuf *line = mem->calloc(1, sizeof(cmark_strbuf)); + cmark_strbuf *buf = mem->calloc(1, sizeof(cmark_strbuf)); + cmark_strbuf_init(mem, line, 256); + cmark_strbuf_init(mem, buf, 0); + + parser->refmap = cmark_reference_map_new(mem); parser->root = document; parser->current = document; parser->line_number = 0; @@ -93,13 +95,30 @@ cmark_parser *cmark_parser_new(int options) { return parser; } +static void *xcalloc(size_t nmem, size_t size) { + void *ptr = calloc(nmem, size); + if (!ptr) abort(); + return ptr; +} + +static void xfree(void *ptr) { + free(ptr); +} + +cmark_mem DEFAULT_MEM_ALLOCATOR = { xcalloc, xfree }; + +cmark_parser *cmark_parser_new(int options) { + return cmark_parser_new2(options, &DEFAULT_MEM_ALLOCATOR); +} + void cmark_parser_free(cmark_parser *parser) { + cmark_mem *mem = parser->mem; cmark_strbuf_free(parser->curline); - free(parser->curline); + mem->free(parser->curline); cmark_strbuf_free(parser->linebuf); - free(parser->linebuf); + mem->free(parser->linebuf); cmark_reference_map_free(parser->refmap); - free(parser); + mem->free(parser); } static cmark_node *finalize(cmark_parser *parser, cmark_node *b); @@ -153,10 +172,10 @@ static void add_line(cmark_node *node, cmark_chunk *ch, cmark_parser *parser) { // add space characters: chars_to_tab = TAB_STOP - (parser->column % TAB_STOP); for (i = 0; i < chars_to_tab; i++) { - cmark_strbuf_putc(&node->string_content, ' '); + cmark_strbuf_putc(&node->content, ' '); } } - cmark_strbuf_put(&node->string_content, ch->data + parser->offset, + cmark_strbuf_put(&node->content, ch->data + parser->offset, ch->len - parser->offset); } @@ -229,7 +248,6 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) { cmark_node *parent; parent = b->parent; - assert(b->open); // shouldn't call finalize on closed blocks b->open = false; @@ -251,15 +269,17 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) { b->end_column = parser->last_line_length; } + cmark_strbuf *node_content = &b->content; + switch (b->type) { case CMARK_NODE_PARAGRAPH: - while (cmark_strbuf_at(&b->string_content, 0) == '[' && - (pos = cmark_parse_reference_inline(&b->string_content, + while (cmark_strbuf_at(node_content, 0) == '[' && + (pos = cmark_parse_reference_inline(parser->mem, node_content, parser->refmap))) { - cmark_strbuf_drop(&b->string_content, pos); + cmark_strbuf_drop(node_content, pos); } - if (is_blank(&b->string_content, 0)) { + if (is_blank(node_content, 0)) { // remove blank node (former reference def) cmark_node_free(b); } @@ -267,34 +287,33 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) { case CMARK_NODE_CODE_BLOCK: if (!b->as.code.fenced) { // indented code - remove_trailing_blank_lines(&b->string_content); - cmark_strbuf_putc(&b->string_content, '\n'); + remove_trailing_blank_lines(node_content); + cmark_strbuf_putc(node_content, '\n'); } else { - // first line of contents becomes info - for (pos = 0; pos < b->string_content.size; ++pos) { - if (S_is_line_end_char(b->string_content.ptr[pos])) + for (pos = 0; pos < node_content->size; ++pos) { + if (S_is_line_end_char(node_content->ptr[pos])) break; } - assert(pos < b->string_content.size); + assert(pos < node_content->size); - cmark_strbuf tmp = GH_BUF_INIT; - houdini_unescape_html_f(&tmp, b->string_content.ptr, pos); + cmark_strbuf tmp = CMARK_BUF_INIT(parser->mem); + houdini_unescape_html_f(&tmp, node_content->ptr, pos); cmark_strbuf_trim(&tmp); cmark_strbuf_unescape(&tmp); b->as.code.info = cmark_chunk_buf_detach(&tmp); - if (b->string_content.ptr[pos] == '\r') + if (node_content->ptr[pos] == '\r') pos += 1; - if (b->string_content.ptr[pos] == '\n') + if (node_content->ptr[pos] == '\n') pos += 1; - cmark_strbuf_drop(&b->string_content, pos); + cmark_strbuf_drop(node_content, pos); } - b->as.code.literal = cmark_chunk_buf_detach(&b->string_content); + b->as.code.literal = cmark_chunk_buf_detach(node_content); break; case CMARK_NODE_HTML_BLOCK: - b->as.literal = cmark_chunk_buf_detach(&b->string_content); + b->as.literal = cmark_chunk_buf_detach(node_content); break; case CMARK_NODE_LIST: // determine tight/loose status @@ -328,6 +347,7 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) { default: break; } + return parent; } @@ -342,7 +362,7 @@ static cmark_node *add_child(cmark_parser *parser, cmark_node *parent, parent = finalize(parser, parent); } - cmark_node *child = make_block(block_type, parser->line_number, start_column); + cmark_node *child = make_block(parser->mem, block_type, parser->line_number, start_column); child->parent = parent; if (parent->last_child) { @@ -358,7 +378,7 @@ static cmark_node *add_child(cmark_parser *parser, cmark_node *parent, // Walk through node and all children, recursively, parsing // string content into inline content where appropriate. -static void process_inlines(cmark_node *root, cmark_reference_map *refmap, +static void process_inlines(cmark_mem *mem, cmark_node *root, cmark_reference_map *refmap, int options) { cmark_iter *iter = cmark_iter_new(root); cmark_node *cur; @@ -368,7 +388,7 @@ static void process_inlines(cmark_node *root, cmark_reference_map *refmap, cur = cmark_iter_get_node(iter); if (ev_type == CMARK_EVENT_ENTER) { if (contains_inlines(cur->type)) { - cmark_parse_inlines(cur, refmap, options); + cmark_parse_inlines(mem, cur, refmap, options); } } } @@ -379,7 +399,7 @@ static void process_inlines(cmark_node *root, cmark_reference_map *refmap, // Attempts to parse a list item marker (bullet or enumerated). // On success, returns length of the marker, and populates // data with the details. On failure, returns 0. -static bufsize_t parse_list_marker(cmark_chunk *input, bufsize_t pos, +static bufsize_t parse_list_marker(cmark_mem *mem, cmark_chunk *input, bufsize_t pos, cmark_list **dataptr) { unsigned char c; bufsize_t startpos; @@ -393,7 +413,7 @@ static bufsize_t parse_list_marker(cmark_chunk *input, bufsize_t pos, if (!cmark_isspace(peek_at(input, pos))) { return 0; } - data = (cmark_list *)cmark_calloc(1, sizeof(*data)); + data = (cmark_list *)mem->calloc(1, sizeof(*data)); data->marker_offset = 0; // will be adjusted later data->list_type = CMARK_BULLET_LIST; data->bullet_char = c; @@ -419,7 +439,7 @@ static bufsize_t parse_list_marker(cmark_chunk *input, bufsize_t pos, if (!cmark_isspace(peek_at(input, pos))) { return 0; } - data = (cmark_list *)cmark_calloc(1, sizeof(*data)); + data = (cmark_list *)mem->calloc(1, sizeof(*data)); data->marker_offset = 0; // will be adjusted later data->list_type = CMARK_ORDERED_LIST; data->bullet_char = 0; @@ -451,7 +471,7 @@ static cmark_node *finalize_document(cmark_parser *parser) { } finalize(parser, parser->root); - process_inlines(parser->root, parser->refmap, parser->options); + process_inlines(parser->mem, parser->root, parser->refmap, parser->options); return parser->root; } @@ -899,7 +919,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container, parser->first_nonspace + 1); S_advance_offset(parser, input, input->len - 1 - parser->offset, false); } else if ((matched = - parse_list_marker(input, parser->first_nonspace, &data)) && + parse_list_marker(parser->mem, input, parser->first_nonspace, &data)) && (!indented || cont_type == CMARK_NODE_LIST)) { // Note that we can have new list items starting with >= 4 // spaces indent, as long as the list container is still open. diff --git a/src/buffer.c b/src/buffer.c index 2ac3b04..b879f0c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -21,7 +21,8 @@ unsigned char cmark_strbuf__initbuf[1]; #define MIN(x, y) ((x < y) ? x : y) #endif -void cmark_strbuf_init(cmark_strbuf *buf, bufsize_t initial_size) { +void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf, bufsize_t initial_size) { + buf->mem = mem; buf->asize = 0; buf->size = 0; buf->ptr = cmark_strbuf__initbuf; @@ -41,7 +42,7 @@ void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) { return; if (target_size > (bufsize_t)(SIZE_MAX / 4)) - cmark_trigger_oom(); + abort(); /* Oversize the buffer by 50% to guarantee amortized linear time * complexity on append operations. */ @@ -49,7 +50,11 @@ void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) { new_size += 1; new_size = (new_size + 7) & ~7; - unsigned char *new_ptr = cmark_realloc(buf->asize ? buf->ptr : NULL, new_size); + unsigned char *new_ptr = buf->mem->calloc(new_size, 1); + if (buf->ptr != cmark_strbuf__initbuf) { + memcpy(new_ptr, buf->ptr, buf->size); + buf->mem->free(buf->ptr); + } buf->asize = new_size; buf->ptr = new_ptr; @@ -62,9 +67,9 @@ void cmark_strbuf_free(cmark_strbuf *buf) { return; if (buf->ptr != cmark_strbuf__initbuf) - free(buf->ptr); + buf->mem->free(buf->ptr); - cmark_strbuf_init(buf, 0); + cmark_strbuf_init(buf->mem, buf, 0); } void cmark_strbuf_clear(cmark_strbuf *buf) { @@ -147,10 +152,10 @@ unsigned char *cmark_strbuf_detach(cmark_strbuf *buf) { if (buf->asize == 0) { /* return an empty string */ - return cmark_calloc(1, 1); + return buf->mem->calloc(1, 1); } - cmark_strbuf_init(buf, 0); + cmark_strbuf_init(buf->mem, buf, 0); return data; } diff --git a/src/buffer.h b/src/buffer.h index 89328ac..96b2704 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -7,6 +7,7 @@ #include <limits.h> #include <stdbool.h> #include "config.h" +#include "cmark.h" #ifdef __cplusplus extern "C" { @@ -15,22 +16,22 @@ extern "C" { typedef ssize_t bufsize_t; typedef struct { + cmark_mem *mem; unsigned char *ptr; bufsize_t asize, size; } cmark_strbuf; extern unsigned char cmark_strbuf__initbuf[]; -#define GH_BUF_INIT \ - { cmark_strbuf__initbuf, 0, 0 } +#define CMARK_BUF_INIT(mem) {mem, cmark_strbuf__initbuf, 0, 0} /** * Initialize a cmark_strbuf structure. * - * For the cases where GH_BUF_INIT cannot be used to do static + * For the cases where CMARK_BUF_INIT cannot be used to do static * initialization. */ -void cmark_strbuf_init(cmark_strbuf *buf, bufsize_t initial_size); +void cmark_strbuf_init(cmark_mem *mem, cmark_strbuf *buf, bufsize_t initial_size); /** * Grow the buffer to hold at least `target_size` bytes. diff --git a/src/chunk.h b/src/chunk.h index acceaa1..4dfc698 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -4,9 +4,10 @@ #include <string.h> #include <stdlib.h> #include <assert.h> +#include "cmark.h" +#include "buffer.h" #include "memory.h" #include "cmark_ctype.h" -#include "buffer.h" #define CMARK_CHUNK_EMPTY \ { NULL, 0, 0 } @@ -17,9 +18,9 @@ typedef struct { bufsize_t alloc; // also implies a NULL-terminated string } cmark_chunk; -static CMARK_INLINE void cmark_chunk_free(cmark_chunk *c) { +static CMARK_INLINE void cmark_chunk_free(cmark_mem *mem, cmark_chunk *c) { if (c->alloc) - free(c->data); + mem->free(c->data); c->data = NULL; c->alloc = 0; @@ -56,13 +57,13 @@ static CMARK_INLINE bufsize_t return p ? (bufsize_t)(p - ch->data) : ch->len; } -static CMARK_INLINE const char *cmark_chunk_to_cstr(cmark_chunk *c) { +static CMARK_INLINE const char *cmark_chunk_to_cstr(cmark_mem *mem, cmark_chunk *c) { unsigned char *str; if (c->alloc) { return (char *)c->data; } - str = (unsigned char *)cmark_calloc(c->len + 1, 1); + str = (unsigned char *)mem->calloc(c->len + 1, 1); if (c->len > 0) { memcpy(str, c->data, c->len); } @@ -73,9 +74,9 @@ static CMARK_INLINE const char *cmark_chunk_to_cstr(cmark_chunk *c) { return (char *)str; } -static CMARK_INLINE void cmark_chunk_set_cstr(cmark_chunk *c, const char *str) { +static CMARK_INLINE void cmark_chunk_set_cstr(cmark_mem *mem, cmark_chunk *c, const char *str) { if (c->alloc) { - free(c->data); + mem->free(c->data); } if (str == NULL) { c->len = 0; @@ -83,7 +84,7 @@ static CMARK_INLINE void cmark_chunk_set_cstr(cmark_chunk *c, const char *str) { c->alloc = 0; } else { c->len = strlen(str); - c->data = (unsigned char *)cmark_calloc(c->len + 1, 1); + c->data = (unsigned char *)mem->calloc(c->len + 1, 1); c->alloc = 1; memcpy(c->data, str, c->len + 1); } diff --git a/src/cmark.c b/src/cmark.c index 3288308..3491199 100644 --- a/src/cmark.c +++ b/src/cmark.c @@ -10,36 +10,6 @@ int cmark_version() { return CMARK_VERSION; } const char *cmark_version_string() { return CMARK_VERSION_STRING; } -void (*_cmark_on_oom)(void) = NULL; - -void cmark_trigger_oom(void) -{ - if (_cmark_on_oom) - _cmark_on_oom(); - abort(); -} - -void cmark_set_oom_handler(void (*handler)(void)) -{ - _cmark_on_oom = handler; -} - -void *cmark_calloc(size_t nmem, size_t size) -{ - void *ptr = calloc(nmem, size); - if (!ptr) - cmark_trigger_oom(); - return ptr; -} - -void *cmark_realloc(void *ptr, size_t size) -{ - void *ptr_new = realloc(ptr, size); - if (!ptr_new) - cmark_trigger_oom(); - return ptr_new; -} - char *cmark_markdown_to_html(const char *text, size_t len, int options) { cmark_node *doc; char *result; diff --git a/src/cmark.h b/src/cmark.h index a43011b..293919d 100644 --- a/src/cmark.h +++ b/src/cmark.h @@ -4,6 +4,7 @@ #include <stdio.h> #include <cmark_export.h> #include <cmark_version.h> +#include "memory.h" #ifdef __cplusplus extern "C" { @@ -88,6 +89,19 @@ typedef struct cmark_parser cmark_parser; typedef struct cmark_iter cmark_iter; /** + * ## Custom memory allocator support + */ + +/** Defines the memory allocation functions to be used by CMark + * when parsing and allocating a document tree + */ +typedef struct cmark_mem { + void *(*calloc)(size_t, size_t); + void (*free)(void *); +} cmark_mem; + + +/** * ## Creating and Destroying Nodes */ @@ -97,6 +111,11 @@ typedef struct cmark_iter cmark_iter; */ CMARK_EXPORT cmark_node *cmark_node_new(cmark_node_type type); +/** Same as `cmark_node_new`, but explicitly listing the memory + * allocator used to allocate the node + */ +CMARK_EXPORT cmark_node *cmark_node_new2(cmark_node_type type, cmark_mem *mem); + /** Frees the memory allocated for a node and any children. */ CMARK_EXPORT void cmark_node_free(cmark_node *node); @@ -437,6 +456,11 @@ CMARK_EXPORT void cmark_consolidate_text_nodes(cmark_node *root); CMARK_EXPORT cmark_parser *cmark_parser_new(int options); +/** Creates a new parser object with the given memory allocator + */ +CMARK_EXPORT +cmark_parser *cmark_parser_new2(int options, cmark_mem *mem); + /** Frees memory allocated for a parser object. */ CMARK_EXPORT @@ -573,20 +597,6 @@ int cmark_version(); CMARK_EXPORT const char *cmark_version_string(); -/** Set the callback function that will be issued whenever the - * library hits an out of memory situation. - * - * This can happen when the heap memory allocator fails to allocate - * a block of memory, or when the index of an in-memory buffer overflows - * - * If no OOM handler is set, the library will call `abort` and - * terminate itself and the running process. If the custom OOM handler - * you set does return (i.e. it does not gracefully terminate the - * application), the behavior of the library will be unspecified. - */ -CMARK_EXPORT -void cmark_set_oom_handler(void (*handler)(void)); - /** # AUTHORS * * John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer. @@ -324,7 +324,7 @@ static int S_render_node(cmark_node *node, cmark_event_type ev_type, char *cmark_render_html(cmark_node *root, int options) { char *result; - cmark_strbuf html = GH_BUF_INIT; + cmark_strbuf html = CMARK_BUF_INIT(cmark_node_mem(root)); cmark_event_type ev_type; cmark_node *cur; struct render_state state = {&html, NULL}; diff --git a/src/inlines.c b/src/inlines.c index f2e7cf1..8f18e6c 100644 --- a/src/inlines.c +++ b/src/inlines.c @@ -22,13 +22,13 @@ static const char *LEFTSINGLEQUOTE = "\xE2\x80\x98"; static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99"; // Macros for creating various kinds of simple. -#define make_str(s) make_literal(CMARK_NODE_TEXT, s) -#define make_code(s) make_literal(CMARK_NODE_CODE, s) -#define make_raw_html(s) make_literal(CMARK_NODE_HTML_INLINE, s) -#define make_linebreak() make_simple(CMARK_NODE_LINEBREAK) -#define make_softbreak() make_simple(CMARK_NODE_SOFTBREAK) -#define make_emph() make_simple(CMARK_NODE_EMPH) -#define make_strong() make_simple(CMARK_NODE_STRONG) +#define make_str(mem, s) make_literal(mem, CMARK_NODE_TEXT, s) +#define make_code(mem, s) make_literal(mem, CMARK_NODE_CODE, s) +#define make_raw_html(mem, s) make_literal(mem, CMARK_NODE_HTML_INLINE, s) +#define make_linebreak(mem) make_simple(mem, CMARK_NODE_LINEBREAK) +#define make_softbreak(mem) make_simple(mem, CMARK_NODE_SOFTBREAK) +#define make_emph(mem) make_simple(mem, CMARK_NODE_EMPH) +#define make_strong(mem) make_simple(mem, CMARK_NODE_STRONG) typedef struct delimiter { struct delimiter *previous; @@ -42,6 +42,7 @@ typedef struct delimiter { } delimiter; typedef struct { + cmark_mem *mem; cmark_chunk input; bufsize_t pos; cmark_reference_map *refmap; @@ -57,44 +58,46 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener, static int parse_inline(subject *subj, cmark_node *parent, int options); -static void subject_from_buf(subject *e, cmark_strbuf *buffer, +static void subject_from_buf(cmark_mem *mem, subject *e, cmark_strbuf *buffer, cmark_reference_map *refmap); static bufsize_t subject_find_special_char(subject *subj, int options); // Create an inline with a literal string value. -static CMARK_INLINE cmark_node *make_literal(cmark_node_type t, cmark_chunk s) { - cmark_node *e = (cmark_node *)cmark_calloc(1, sizeof(*e)); +static CMARK_INLINE cmark_node *make_literal(cmark_mem *mem, cmark_node_type t, cmark_chunk s) { + cmark_node *e = (cmark_node *)mem->calloc(1, sizeof(*e)); + cmark_strbuf_init(mem, &e->content, 0); e->type = t; e->as.literal = s; return e; } // Create an inline with no value. -static CMARK_INLINE cmark_node *make_simple(cmark_node_type t) { - cmark_node *e = (cmark_node *)cmark_calloc(1, sizeof(*e)); +static CMARK_INLINE cmark_node *make_simple(cmark_mem *mem, cmark_node_type t) { + cmark_node *e = (cmark_node *)mem->calloc(1, sizeof(*e)); + cmark_strbuf_init(mem, &e->content, 0); e->type = t; return e; } // Like make_str, but parses entities. -static cmark_node *make_str_with_entities(cmark_chunk *content) { - cmark_strbuf unescaped = GH_BUF_INIT; +static cmark_node *make_str_with_entities(cmark_mem *mem, cmark_chunk *content) { + cmark_strbuf unescaped = CMARK_BUF_INIT(mem); if (houdini_unescape_html(&unescaped, content->data, content->len)) { - return make_str(cmark_chunk_buf_detach(&unescaped)); + return make_str(mem, cmark_chunk_buf_detach(&unescaped)); } else { - return make_str(*content); + return make_str(mem, *content); } } // Duplicate a chunk by creating a copy of the buffer not by reusing the // buffer like cmark_chunk_dup does. -static cmark_chunk chunk_clone(cmark_chunk *src) { +static cmark_chunk chunk_clone(cmark_mem *mem, cmark_chunk *src) { cmark_chunk c; bufsize_t len = src->len; c.len = len; - c.data = (unsigned char *)cmark_calloc(len + 1, 1); + c.data = (unsigned char *)mem->calloc(len + 1, 1); c.alloc = 1; memcpy(c.data, src->data, len); c.data[len] = '\0'; @@ -102,8 +105,8 @@ static cmark_chunk chunk_clone(cmark_chunk *src) { return c; } -static cmark_chunk cmark_clean_autolink(cmark_chunk *url, int is_email) { - cmark_strbuf buf = GH_BUF_INIT; +static cmark_chunk cmark_clean_autolink(cmark_mem *mem, cmark_chunk *url, int is_email) { + cmark_strbuf buf = CMARK_BUF_INIT(mem); cmark_chunk_trim(url); @@ -119,16 +122,17 @@ static cmark_chunk cmark_clean_autolink(cmark_chunk *url, int is_email) { return cmark_chunk_buf_detach(&buf); } -static CMARK_INLINE cmark_node *make_autolink(cmark_chunk url, int is_email) { - cmark_node *link = make_simple(CMARK_NODE_LINK); - link->as.link.url = cmark_clean_autolink(&url, is_email); +static CMARK_INLINE cmark_node *make_autolink(cmark_mem *mem, cmark_chunk url, int is_email) { + cmark_node *link = make_simple(mem, CMARK_NODE_LINK); + link->as.link.url = cmark_clean_autolink(mem, &url, is_email); link->as.link.title = cmark_chunk_literal(""); - cmark_node_append_child(link, make_str_with_entities(&url)); + cmark_node_append_child(link, make_str_with_entities(mem, &url)); return link; } -static void subject_from_buf(subject *e, cmark_strbuf *buffer, +static void subject_from_buf(cmark_mem *mem, subject *e, cmark_strbuf *buffer, cmark_reference_map *refmap) { + e->mem = mem; e->input.data = buffer->ptr; e->input.len = buffer->size; e->input.alloc = 0; @@ -229,16 +233,16 @@ static cmark_node *handle_backticks(subject *subj) { if (endpos == 0) { // not found subj->pos = startpos; // rewind - return make_str(openticks); + return make_str(subj->mem, openticks); } else { - cmark_strbuf buf = GH_BUF_INIT; + cmark_strbuf buf = CMARK_BUF_INIT(subj->mem); cmark_strbuf_set(&buf, subj->input.data + startpos, endpos - startpos - openticks.len); cmark_strbuf_trim(&buf); cmark_strbuf_normalize_whitespace(&buf); - return make_code(cmark_chunk_buf_detach(&buf)); + return make_code(subj->mem, cmark_chunk_buf_detach(&buf)); } } @@ -340,7 +344,7 @@ static void remove_delimiter(subject *subj, delimiter *delim) { static void push_delimiter(subject *subj, unsigned char c, bool can_open, bool can_close, cmark_node *inl_text) { - delimiter *delim = (delimiter *)cmark_calloc(1, sizeof(delimiter)); + delimiter *delim = (delimiter *)subj->mem->calloc(1, sizeof(delimiter)); delim->delim_char = c; delim->can_open = can_open; delim->can_close = can_close; @@ -373,7 +377,7 @@ static cmark_node *handle_delim(subject *subj, unsigned char c, bool smart) { contents = cmark_chunk_dup(&subj->input, subj->pos - numdelims, numdelims); } - inl_text = make_str(contents); + inl_text = make_str(subj->mem, contents); if ((can_open || can_close) && (!(c == '\'' || c == '"') || smart)) { push_delimiter(subj, c, can_open, can_close, inl_text); @@ -389,7 +393,7 @@ static cmark_node *handle_hyphen(subject *subj, bool smart) { advance(subj); if (!smart || peek_char(subj) != '-') { - return make_str(cmark_chunk_literal("-")); + return make_str(subj->mem, cmark_chunk_literal("-")); } while (smart && peek_char(subj) == '-') { @@ -400,7 +404,7 @@ static cmark_node *handle_hyphen(subject *subj, bool smart) { int en_count = 0; int em_count = 0; int i; - cmark_strbuf buf = GH_BUF_INIT; + cmark_strbuf buf = CMARK_BUF_INIT(subj->mem); if (numhyphens % 3 == 0) { // if divisible by 3, use all em dashes em_count = numhyphens / 3; @@ -422,7 +426,7 @@ static cmark_node *handle_hyphen(subject *subj, bool smart) { cmark_strbuf_puts(&buf, ENDASH); } - return make_str(cmark_chunk_buf_detach(&buf)); + return make_str(subj->mem, cmark_chunk_buf_detach(&buf)); } // Assumes we have a period at the current position. @@ -432,12 +436,12 @@ static cmark_node *handle_period(subject *subj, bool smart) { advance(subj); if (peek_char(subj) == '.') { advance(subj); - return make_str(cmark_chunk_literal(ELLIPSES)); + return make_str(subj->mem, cmark_chunk_literal(ELLIPSES)); } else { - return make_str(cmark_chunk_literal("..")); + return make_str(subj->mem, cmark_chunk_literal("..")); } } else { - return make_str(cmark_chunk_literal(".")); + return make_str(subj->mem, cmark_chunk_literal(".")); } } @@ -483,18 +487,18 @@ static void process_emphasis(subject *subj, delimiter *stack_bottom) { closer = closer->next; } } else if (closer->delim_char == '\'') { - cmark_chunk_free(&closer->inl_text->as.literal); + cmark_chunk_free(subj->mem, &closer->inl_text->as.literal); closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE); if (opener_found) { - cmark_chunk_free(&opener->inl_text->as.literal); + cmark_chunk_free(subj->mem, &opener->inl_text->as.literal); opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE); } closer = closer->next; } else if (closer->delim_char == '"') { - cmark_chunk_free(&closer->inl_text->as.literal); + cmark_chunk_free(subj->mem, &closer->inl_text->as.literal); closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE); if (opener_found) { - cmark_chunk_free(&opener->inl_text->as.literal); + cmark_chunk_free(subj->mem, &opener->inl_text->as.literal); opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE); } closer = closer->next; @@ -553,7 +557,7 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener, // create new emph or strong, and splice it in to our inlines // between the opener and closer - emph = use_delims == 1 ? make_emph() : make_strong(); + emph = use_delims == 1 ? make_emph(subj->mem) : make_strong(subj->mem); tmp = opener_inl->next; while (tmp && tmp != closer_inl) { @@ -589,18 +593,18 @@ static cmark_node *handle_backslash(subject *subj) { if (cmark_ispunct( nextchar)) { // only ascii symbols and newline can be escaped advance(subj); - return make_str(cmark_chunk_dup(&subj->input, subj->pos - 1, 1)); + return make_str(subj->mem, cmark_chunk_dup(&subj->input, subj->pos - 1, 1)); } else if (!is_eof(subj) && skip_line_end(subj)) { - return make_linebreak(); + return make_linebreak(subj->mem); } else { - return make_str(cmark_chunk_literal("\\")); + return make_str(subj->mem, cmark_chunk_literal("\\")); } } // Parse an entity or a regular "&" string. // Assumes the subject has an '&' character at the current position. static cmark_node *handle_entity(subject *subj) { - cmark_strbuf ent = GH_BUF_INIT; + cmark_strbuf ent = CMARK_BUF_INIT(subj->mem); bufsize_t len; advance(subj); @@ -609,16 +613,16 @@ static cmark_node *handle_entity(subject *subj) { subj->input.len - subj->pos); if (len == 0) - return make_str(cmark_chunk_literal("&")); + return make_str(subj->mem, cmark_chunk_literal("&")); subj->pos += len; - return make_str(cmark_chunk_buf_detach(&ent)); + return make_str(subj->mem, cmark_chunk_buf_detach(&ent)); } // Clean a URL: remove surrounding whitespace and surrounding <>, // and remove \ that escape punctuation. -cmark_chunk cmark_clean_url(cmark_chunk *url) { - cmark_strbuf buf = GH_BUF_INIT; +cmark_chunk cmark_clean_url(cmark_mem *mem, cmark_chunk *url) { + cmark_strbuf buf = CMARK_BUF_INIT(mem); cmark_chunk_trim(url); @@ -637,8 +641,8 @@ cmark_chunk cmark_clean_url(cmark_chunk *url) { return cmark_chunk_buf_detach(&buf); } -cmark_chunk cmark_clean_title(cmark_chunk *title) { - cmark_strbuf buf = GH_BUF_INIT; +cmark_chunk cmark_clean_title(cmark_mem *mem, cmark_chunk *title) { + cmark_strbuf buf = CMARK_BUF_INIT(mem); unsigned char first, last; if (title->len == 0) { @@ -675,7 +679,7 @@ static cmark_node *handle_pointy_brace(subject *subj) { contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1); subj->pos += matchlen; - return make_autolink(contents, 0); + return make_autolink(subj->mem, contents, 0); } // next try to match an email autolink @@ -684,7 +688,7 @@ static cmark_node *handle_pointy_brace(subject *subj) { contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1); subj->pos += matchlen; - return make_autolink(contents, 1); + return make_autolink(subj->mem, contents, 1); } // finally, try to match an html tag @@ -692,11 +696,11 @@ static cmark_node *handle_pointy_brace(subject *subj) { if (matchlen > 0) { contents = cmark_chunk_dup(&subj->input, subj->pos - 1, matchlen + 1); subj->pos += matchlen; - return make_raw_html(contents); + return make_raw_html(subj->mem, contents); } // if nothing matches, just return the opening <: - return make_str(cmark_chunk_literal("<")); + return make_str(subj->mem, cmark_chunk_literal("<")); } // Parse a link label. Returns 1 if successful. @@ -774,13 +778,13 @@ static cmark_node *handle_close_bracket(subject *subj) { } if (opener == NULL) { - return make_str(cmark_chunk_literal("]")); + return make_str(subj->mem, cmark_chunk_literal("]")); } if (!opener->active) { // take delimiter off stack remove_delimiter(subj, opener); - return make_str(cmark_chunk_literal("]")); + return make_str(subj->mem, cmark_chunk_literal("]")); } // If we got here, we matched a potential link/image text. @@ -811,10 +815,10 @@ static cmark_node *handle_close_bracket(subject *subj) { url_chunk = cmark_chunk_dup(&subj->input, starturl, endurl - starturl); title_chunk = cmark_chunk_dup(&subj->input, starttitle, endtitle - starttitle); - url = cmark_clean_url(&url_chunk); - title = cmark_clean_title(&title_chunk); - cmark_chunk_free(&url_chunk); - cmark_chunk_free(&title_chunk); + url = cmark_clean_url(subj->mem, &url_chunk); + title = cmark_clean_title(subj->mem, &title_chunk); + cmark_chunk_free(subj->mem, &url_chunk); + cmark_chunk_free(subj->mem, &title_chunk); goto match; } else { @@ -827,7 +831,7 @@ static cmark_node *handle_close_bracket(subject *subj) { raw_label = cmark_chunk_literal(""); found_label = link_label(subj, &raw_label); if (!found_label || raw_label.len == 0) { - cmark_chunk_free(&raw_label); + cmark_chunk_free(subj->mem, &raw_label); raw_label = cmark_chunk_dup(&subj->input, opener->position, initial_pos - opener->position - 1); } @@ -839,11 +843,11 @@ static cmark_node *handle_close_bracket(subject *subj) { } ref = cmark_reference_lookup(subj->refmap, &raw_label); - cmark_chunk_free(&raw_label); + cmark_chunk_free(subj->mem, &raw_label); if (ref != NULL) { // found - url = chunk_clone(&ref->url); - title = chunk_clone(&ref->title); + url = chunk_clone(subj->mem, &ref->url); + title = chunk_clone(subj->mem, &ref->title); goto match; } else { goto noMatch; @@ -853,10 +857,10 @@ noMatch: // If we fall through to here, it means we didn't match a link: remove_delimiter(subj, opener); // remove this opener from delimiter list subj->pos = initial_pos; - return make_str(cmark_chunk_literal("]")); + return make_str(subj->mem, cmark_chunk_literal("]")); match: - inl = make_simple(is_image ? CMARK_NODE_IMAGE : CMARK_NODE_LINK); + inl = make_simple(subj->mem, is_image ? CMARK_NODE_IMAGE : CMARK_NODE_LINK); inl->as.link.url = url; inl->as.link.title = title; cmark_node_insert_before(opener->inl_text, inl); @@ -909,9 +913,9 @@ static cmark_node *handle_newline(subject *subj) { skip_spaces(subj); if (nlpos > 1 && peek_at(subj, nlpos - 1) == ' ' && peek_at(subj, nlpos - 2) == ' ') { - return make_linebreak(); + return make_linebreak(subj->mem); } else { - return make_softbreak(); + return make_softbreak(subj->mem); } } @@ -1000,7 +1004,7 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) { break; case '[': advance(subj); - new_inl = make_str(cmark_chunk_literal("[")); + new_inl = make_str(subj->mem, cmark_chunk_literal("[")); push_delimiter(subj, '[', true, false, new_inl); break; case ']': @@ -1010,10 +1014,10 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) { advance(subj); if (peek_char(subj) == '[') { advance(subj); - new_inl = make_str(cmark_chunk_literal("![")); + new_inl = make_str(subj->mem, cmark_chunk_literal("![")); push_delimiter(subj, '!', false, true, new_inl); } else { - new_inl = make_str(cmark_chunk_literal("!")); + new_inl = make_str(subj->mem, cmark_chunk_literal("!")); } break; default: @@ -1026,7 +1030,7 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) { cmark_chunk_rtrim(&contents); } - new_inl = make_str(contents); + new_inl = make_str(subj->mem, contents); } if (new_inl != NULL) { cmark_node_append_child(parent, new_inl); @@ -1036,10 +1040,10 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) { } // Parse inlines from parent's string_content, adding as children of parent. -extern void cmark_parse_inlines(cmark_node *parent, cmark_reference_map *refmap, +extern void cmark_parse_inlines(cmark_mem *mem, cmark_node *parent, cmark_reference_map *refmap, int options) { subject subj; - subject_from_buf(&subj, &parent->string_content, refmap); + subject_from_buf(mem, &subj, &parent->content, refmap); cmark_chunk_rtrim(&subj.input); while (!is_eof(&subj) && parse_inline(&subj, parent, options)) @@ -1060,7 +1064,7 @@ static void spnl(subject *subj) { // Modify refmap if a reference is encountered. // Return 0 if no reference found, otherwise position of subject // after reference is parsed. -bufsize_t cmark_parse_reference_inline(cmark_strbuf *input, +bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_strbuf *input, cmark_reference_map *refmap) { subject subj; @@ -1071,7 +1075,7 @@ bufsize_t cmark_parse_reference_inline(cmark_strbuf *input, bufsize_t matchlen = 0; bufsize_t beforetitle; - subject_from_buf(&subj, input, NULL); + subject_from_buf(mem, &subj, input, NULL); // parse label: if (!link_label(&subj, &lab) || lab.len == 0) diff --git a/src/inlines.h b/src/inlines.h index 7e7ee45..cbe7830 100644 --- a/src/inlines.h +++ b/src/inlines.h @@ -5,13 +5,13 @@ extern "C" { #endif -cmark_chunk cmark_clean_url(cmark_chunk *url); -cmark_chunk cmark_clean_title(cmark_chunk *title); +cmark_chunk cmark_clean_url(cmark_mem *mem, cmark_chunk *url); +cmark_chunk cmark_clean_title(cmark_mem *mem, cmark_chunk *title); -void cmark_parse_inlines(cmark_node *parent, cmark_reference_map *refmap, +void cmark_parse_inlines(cmark_mem *mem, cmark_node *parent, cmark_reference_map *refmap, int options); -bufsize_t cmark_parse_reference_inline(cmark_strbuf *input, +bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_strbuf *input, cmark_reference_map *refmap); #ifdef __cplusplus diff --git a/src/iterator.c b/src/iterator.c index 40287f5..24423a2 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -16,7 +16,9 @@ cmark_iter *cmark_iter_new(cmark_node *root) { if (root == NULL) { return NULL; } - cmark_iter *iter = (cmark_iter *)cmark_calloc(1, sizeof(cmark_iter)); + cmark_mem *mem = root->content.mem; + cmark_iter *iter = (cmark_iter *)mem->calloc(1, sizeof(cmark_iter)); + iter->mem = mem; iter->root = root; iter->cur.ev_type = CMARK_EVENT_NONE; iter->cur.node = NULL; @@ -25,7 +27,7 @@ cmark_iter *cmark_iter_new(cmark_node *root) { return iter; } -void cmark_iter_free(cmark_iter *iter) { free(iter); } +void cmark_iter_free(cmark_iter *iter) { iter->mem->free(iter); } static bool S_is_leaf(cmark_node *node) { return ((1 << node->type) & S_leaf_mask) != 0; @@ -90,7 +92,7 @@ void cmark_consolidate_text_nodes(cmark_node *root) { return; } cmark_iter *iter = cmark_iter_new(root); - cmark_strbuf buf = GH_BUF_INIT; + cmark_strbuf buf = CMARK_BUF_INIT(iter->mem); cmark_event_type ev_type; cmark_node *cur, *tmp, *next; @@ -108,7 +110,7 @@ void cmark_consolidate_text_nodes(cmark_node *root) { cmark_node_free(tmp); tmp = next; } - cmark_chunk_free(&cur->as.literal); + cmark_chunk_free(iter->mem, &cur->as.literal); cur->as.literal = cmark_chunk_buf_detach(&buf); } } diff --git a/src/iterator.h b/src/iterator.h index 9c6bca6..fc745df 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -6,6 +6,7 @@ extern "C" { #endif #include "cmark.h" +#include "memory.h" typedef struct { cmark_event_type ev_type; @@ -13,6 +14,7 @@ typedef struct { } cmark_iter_state; struct cmark_iter { + cmark_mem *mem; cmark_node *root; cmark_iter_state cur; cmark_iter_state next; @@ -81,7 +81,7 @@ int main(int argc, char *argv[]) { _setmode(_fileno(stdout), _O_BINARY); #endif - files = (int *)cmark_calloc(argc, sizeof(*files)); + files = (int *)calloc(argc, sizeof(*files)); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--version") == 0) { diff --git a/src/memory.h b/src/memory.h deleted file mode 100644 index f05d566..0000000 --- a/src/memory.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CMARK_MEM_H -#define CMARK_MEM_H - -#ifdef __cplusplus -extern "C" { -#endif - -void *cmark_calloc(size_t nmem, size_t size); -void *cmark_realloc(void *ptr, size_t size); -void cmark_trigger_oom(void); - -#endif @@ -6,6 +6,8 @@ static void S_node_unlink(cmark_node *node); +#define NODE_MEM(node) cmark_node_mem(node) + static CMARK_INLINE bool S_is_block(cmark_node *node) { if (node == NULL) { return false; @@ -28,6 +30,9 @@ static bool S_can_contain(cmark_node *node, cmark_node *child) { if (node == NULL || child == NULL) { return false; } + if (NODE_MEM(node) != NODE_MEM(child)) { + return 0; + } // Verify that child is not an ancestor of node or equal to node. cur = node; @@ -70,8 +75,9 @@ static bool S_can_contain(cmark_node *node, cmark_node *child) { return false; } -cmark_node *cmark_node_new(cmark_node_type type) { - cmark_node *node = (cmark_node *)cmark_calloc(1, sizeof(*node)); +cmark_node *cmark_node_new2(cmark_node_type type, cmark_mem *mem) { + cmark_node *node = (cmark_node *)mem->calloc(1, sizeof(*node)); + cmark_strbuf_init(mem, &node->content, 0); node->type = type; switch (node->type) { @@ -94,33 +100,36 @@ cmark_node *cmark_node_new(cmark_node_type type) { return node; } +cmark_node *cmark_node_new(cmark_node_type type) { + extern cmark_mem DEFAULT_MEM_ALLOCATOR; + return cmark_node_new2(type, &DEFAULT_MEM_ALLOCATOR); +} + // Free a cmark_node list and any children. static void S_free_nodes(cmark_node *e) { cmark_node *next; while (e != NULL) { - if (S_is_block(e)) { - cmark_strbuf_free(&e->string_content); - } + cmark_strbuf_free(&e->content); switch (e->type) { case CMARK_NODE_CODE_BLOCK: - cmark_chunk_free(&e->as.code.info); - cmark_chunk_free(&e->as.code.literal); + cmark_chunk_free(NODE_MEM(e), &e->as.code.info); + cmark_chunk_free(NODE_MEM(e), &e->as.code.literal); break; case CMARK_NODE_TEXT: case CMARK_NODE_HTML_INLINE: case CMARK_NODE_CODE: case CMARK_NODE_HTML_BLOCK: - cmark_chunk_free(&e->as.literal); + cmark_chunk_free(NODE_MEM(e), &e->as.literal); break; case CMARK_NODE_LINK: case CMARK_NODE_IMAGE: - cmark_chunk_free(&e->as.link.url); - cmark_chunk_free(&e->as.link.title); + cmark_chunk_free(NODE_MEM(e), &e->as.link.url); + cmark_chunk_free(NODE_MEM(e), &e->as.link.title); break; case CMARK_NODE_CUSTOM_BLOCK: case CMARK_NODE_CUSTOM_INLINE: - cmark_chunk_free(&e->as.custom.on_enter); - cmark_chunk_free(&e->as.custom.on_exit); + cmark_chunk_free(NODE_MEM(e), &e->as.custom.on_enter); + cmark_chunk_free(NODE_MEM(e), &e->as.custom.on_exit); break; default: break; @@ -131,7 +140,7 @@ static void S_free_nodes(cmark_node *e) { e->next = e->first_child; } next = e->next; - free(e); + NODE_MEM(e)->free(e); e = next; } } @@ -269,10 +278,10 @@ const char *cmark_node_get_literal(cmark_node *node) { case CMARK_NODE_TEXT: case CMARK_NODE_HTML_INLINE: case CMARK_NODE_CODE: - return cmark_chunk_to_cstr(&node->as.literal); + return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.literal); case CMARK_NODE_CODE_BLOCK: - return cmark_chunk_to_cstr(&node->as.code.literal); + return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.code.literal); default: break; @@ -291,11 +300,11 @@ int cmark_node_set_literal(cmark_node *node, const char *content) { case CMARK_NODE_TEXT: case CMARK_NODE_HTML_INLINE: case CMARK_NODE_CODE: - cmark_chunk_set_cstr(&node->as.literal, content); + cmark_chunk_set_cstr(NODE_MEM(node), &node->as.literal, content); return 1; case CMARK_NODE_CODE_BLOCK: - cmark_chunk_set_cstr(&node->as.code.literal, content); + cmark_chunk_set_cstr(NODE_MEM(node), &node->as.code.literal, content); return 1; default: @@ -452,7 +461,7 @@ const char *cmark_node_get_fence_info(cmark_node *node) { } if (node->type == CMARK_NODE_CODE_BLOCK) { - return cmark_chunk_to_cstr(&node->as.code.info); + return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.code.info); } else { return NULL; } @@ -464,7 +473,7 @@ int cmark_node_set_fence_info(cmark_node *node, const char *info) { } if (node->type == CMARK_NODE_CODE_BLOCK) { - cmark_chunk_set_cstr(&node->as.code.info, info); + cmark_chunk_set_cstr(NODE_MEM(node), &node->as.code.info, info); return 1; } else { return 0; @@ -479,7 +488,7 @@ const char *cmark_node_get_url(cmark_node *node) { switch (node->type) { case CMARK_NODE_LINK: case CMARK_NODE_IMAGE: - return cmark_chunk_to_cstr(&node->as.link.url); + return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.link.url); default: break; } @@ -495,7 +504,7 @@ int cmark_node_set_url(cmark_node *node, const char *url) { switch (node->type) { case CMARK_NODE_LINK: case CMARK_NODE_IMAGE: - cmark_chunk_set_cstr(&node->as.link.url, url); + cmark_chunk_set_cstr(NODE_MEM(node), &node->as.link.url, url); return 1; default: break; @@ -512,7 +521,7 @@ const char *cmark_node_get_title(cmark_node *node) { switch (node->type) { case CMARK_NODE_LINK: case CMARK_NODE_IMAGE: - return cmark_chunk_to_cstr(&node->as.link.title); + return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.link.title); default: break; } @@ -528,7 +537,7 @@ int cmark_node_set_title(cmark_node *node, const char *title) { switch (node->type) { case CMARK_NODE_LINK: case CMARK_NODE_IMAGE: - cmark_chunk_set_cstr(&node->as.link.title, title); + cmark_chunk_set_cstr(NODE_MEM(node), &node->as.link.title, title); return 1; default: break; @@ -545,7 +554,7 @@ const char *cmark_node_get_on_enter(cmark_node *node) { switch (node->type) { case CMARK_NODE_CUSTOM_INLINE: case CMARK_NODE_CUSTOM_BLOCK: - return cmark_chunk_to_cstr(&node->as.custom.on_enter); + return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.custom.on_enter); default: break; } @@ -561,7 +570,7 @@ int cmark_node_set_on_enter(cmark_node *node, const char *on_enter) { switch (node->type) { case CMARK_NODE_CUSTOM_INLINE: case CMARK_NODE_CUSTOM_BLOCK: - cmark_chunk_set_cstr(&node->as.custom.on_enter, on_enter); + cmark_chunk_set_cstr(NODE_MEM(node), &node->as.custom.on_enter, on_enter); return 1; default: break; @@ -578,7 +587,7 @@ const char *cmark_node_get_on_exit(cmark_node *node) { switch (node->type) { case CMARK_NODE_CUSTOM_INLINE: case CMARK_NODE_CUSTOM_BLOCK: - return cmark_chunk_to_cstr(&node->as.custom.on_exit); + return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.custom.on_exit); default: break; } @@ -594,7 +603,7 @@ int cmark_node_set_on_exit(cmark_node *node, const char *on_exit) { switch (node->type) { case CMARK_NODE_CUSTOM_INLINE: case CMARK_NODE_CUSTOM_BLOCK: - cmark_chunk_set_cstr(&node->as.custom.on_exit, on_exit); + cmark_chunk_set_cstr(NODE_MEM(node), &node->as.custom.on_exit, on_exit); return 1; default: break; @@ -48,6 +48,8 @@ typedef struct { } cmark_custom; struct cmark_node { + cmark_strbuf content; + struct cmark_node *next; struct cmark_node *prev; struct cmark_node *parent; @@ -62,11 +64,8 @@ struct cmark_node { int end_column; cmark_node_type type; - - bool open; bool last_line_blank; - - cmark_strbuf string_content; + bool open; union { cmark_chunk literal; @@ -79,6 +78,9 @@ struct cmark_node { } as; }; +static CMARK_INLINE cmark_mem *cmark_node_mem(cmark_node *node) { + return node->content.mem; +} CMARK_EXPORT int cmark_node_check(cmark_node *node, FILE *out); #ifdef __cplusplus diff --git a/src/parser.h b/src/parser.h index ab21d0f..1309420 100644 --- a/src/parser.h +++ b/src/parser.h @@ -4,6 +4,7 @@ #include <stdio.h> #include "node.h" #include "buffer.h" +#include "memory.h" #ifdef __cplusplus extern "C" { @@ -12,6 +13,7 @@ extern "C" { #define MAX_LINK_LABEL_LENGTH 1000 struct cmark_parser { + struct cmark_mem *mem; struct cmark_reference_map *refmap; struct cmark_node *root; struct cmark_node *current; diff --git a/src/references.c b/src/references.c index 168bd89..235e0af 100755 --- a/src/references.c +++ b/src/references.c @@ -14,12 +14,13 @@ static unsigned int refhash(const unsigned char *link_ref) { return hash; } -static void reference_free(cmark_reference *ref) { +static void reference_free(cmark_reference_map *map, cmark_reference *ref) { + cmark_mem *mem = map->mem; if (ref != NULL) { - free(ref->label); - cmark_chunk_free(&ref->url); - cmark_chunk_free(&ref->title); - free(ref); + mem->free(ref->label); + cmark_chunk_free(mem, &ref->url); + cmark_chunk_free(mem, &ref->title); + mem->free(ref); } } @@ -27,8 +28,8 @@ static void reference_free(cmark_reference *ref) { // remove leading/trailing whitespace, case fold // Return NULL if the reference name is actually empty (i.e. composed // solely from whitespace) -static unsigned char *normalize_reference(cmark_chunk *ref) { - cmark_strbuf normalized = GH_BUF_INIT; +static unsigned char *normalize_reference(cmark_mem *mem, cmark_chunk *ref) { + cmark_strbuf normalized = CMARK_BUF_INIT(mem); unsigned char *result; if (ref == NULL) @@ -57,7 +58,7 @@ static void add_reference(cmark_reference_map *map, cmark_reference *ref) { while (t) { if (t->hash == ref->hash && !strcmp((char *)t->label, (char *)ref->label)) { - reference_free(ref); + reference_free(map, ref); return; } @@ -70,17 +71,17 @@ static void add_reference(cmark_reference_map *map, cmark_reference *ref) { void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label, cmark_chunk *url, cmark_chunk *title) { cmark_reference *ref; - unsigned char *reflabel = normalize_reference(label); + unsigned char *reflabel = normalize_reference(map->mem, label); /* empty reference name, or composed from only whitespace */ if (reflabel == NULL) return; - ref = (cmark_reference *)cmark_calloc(1, sizeof(*ref)); + ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref)); ref->label = reflabel; ref->hash = refhash(ref->label); - ref->url = cmark_clean_url(url); - ref->title = cmark_clean_title(title); + ref->url = cmark_clean_url(map->mem, url); + ref->title = cmark_clean_title(map->mem, title); ref->next = NULL; add_reference(map, ref); @@ -100,7 +101,7 @@ cmark_reference *cmark_reference_lookup(cmark_reference_map *map, if (map == NULL) return NULL; - norm = normalize_reference(label); + norm = normalize_reference(map->mem, label); if (norm == NULL) return NULL; @@ -129,7 +130,7 @@ void cmark_reference_map_free(cmark_reference_map *map) { while (ref) { next = ref->next; - reference_free(ref); + reference_free(map, ref); ref = next; } } @@ -137,6 +138,8 @@ void cmark_reference_map_free(cmark_reference_map *map) { free(map); } -cmark_reference_map *cmark_reference_map_new(void) { - return (cmark_reference_map *)cmark_calloc(1, sizeof(cmark_reference_map)); +cmark_reference_map *cmark_reference_map_new(cmark_mem *mem) { + cmark_reference_map *map = mem->calloc(1, sizeof(cmark_reference_map)); + map->mem = mem; + return map; } diff --git a/src/references.h b/src/references.h index 310ff9f..f075bbb 100644 --- a/src/references.h +++ b/src/references.h @@ -1,6 +1,7 @@ #ifndef CMARK_REFERENCES_H #define CMARK_REFERENCES_H +#include "memory.h" #include "chunk.h" #ifdef __cplusplus @@ -20,12 +21,13 @@ struct cmark_reference { typedef struct cmark_reference cmark_reference; struct cmark_reference_map { + cmark_mem *mem; cmark_reference *table[REFMAP_SIZE]; }; typedef struct cmark_reference_map cmark_reference_map; -cmark_reference_map *cmark_reference_map_new(void); +cmark_reference_map *cmark_reference_map_new(cmark_mem *mem); void cmark_reference_map_free(cmark_reference_map *map); cmark_reference *cmark_reference_lookup(cmark_reference_map *map, cmark_chunk *label); diff --git a/src/render.c b/src/render.c index b1ed2e4..0f88007 100644 --- a/src/render.c +++ b/src/render.c @@ -4,6 +4,7 @@ #include "cmark.h" #include "utf8.h" #include "render.h" +#include "node.h" static CMARK_INLINE void S_cr(cmark_renderer *renderer) { if (renderer->need_cr < 1) { @@ -108,7 +109,7 @@ static void S_out(cmark_renderer *renderer, const char *source, bool wrap, !renderer->begin_line && renderer->last_breakable > 0) { // copy from last_breakable to remainder - cmark_chunk_set_cstr(&remainder, (char *)renderer->buffer->ptr + + cmark_chunk_set_cstr(renderer->mem, &remainder, (char *)renderer->buffer->ptr + renderer->last_breakable + 1); // truncate at last_breakable cmark_strbuf_truncate(renderer->buffer, renderer->last_breakable); @@ -118,7 +119,7 @@ static void S_out(cmark_renderer *renderer, const char *source, bool wrap, renderer->prefix->size); cmark_strbuf_put(renderer->buffer, remainder.data, remainder.len); renderer->column = renderer->prefix->size + remainder.len; - cmark_chunk_free(&remainder); + cmark_chunk_free(renderer->mem, &remainder); renderer->last_breakable = 0; renderer->begin_line = false; renderer->begin_content = false; @@ -146,14 +147,15 @@ char *cmark_render(cmark_node *root, int options, int width, int (*render_node)(cmark_renderer *renderer, cmark_node *node, cmark_event_type ev_type, int options)) { - cmark_strbuf pref = GH_BUF_INIT; - cmark_strbuf buf = GH_BUF_INIT; + cmark_mem *mem = cmark_node_mem(root); + cmark_strbuf pref = CMARK_BUF_INIT(mem); + cmark_strbuf buf = CMARK_BUF_INIT(mem); cmark_node *cur; cmark_event_type ev_type; char *result; cmark_iter *iter = cmark_iter_new(root); - cmark_renderer renderer = {&buf, &pref, 0, width, 0, + cmark_renderer renderer = {mem, &buf, &pref, 0, width, 0, 0, true, true, false, false, outc, S_cr, S_blankline, S_out}; diff --git a/src/render.h b/src/render.h index c3acf6b..35eb0a6 100644 --- a/src/render.h +++ b/src/render.h @@ -8,10 +8,12 @@ extern "C" { #include <stdlib.h> #include "buffer.h" #include "chunk.h" +#include "memory.h" typedef enum { LITERAL, NORMAL, TITLE, URL } cmark_escaping; struct cmark_renderer { + cmark_mem *mem; cmark_strbuf *buffer; cmark_strbuf *prefix; int column; @@ -149,7 +149,7 @@ static int S_render_node(cmark_node *node, cmark_event_type ev_type, char *cmark_render_xml(cmark_node *root, int options) { char *result; - cmark_strbuf xml = GH_BUF_INIT; + cmark_strbuf xml = CMARK_BUF_INIT(cmark_node_mem(root)); cmark_event_type ev_type; cmark_node *cur; struct render_state state = {&xml, 0}; |