diff --git a/CMakeLists.txt b/CMakeLists.txt index e40ca1633..4aa739c99 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,12 @@ if(CMARK_THREADING) include(FindThreads) endif() include(GNUInstallDirs) +include(CheckCCompilerFlag) + +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES Clang) + check_c_compiler_flag(-Wallocator-wrappers HAS_WARN_ALLOCATOR_WRAPPERS) + check_c_compiler_flag(-ftyped-memory-operations HAS_TYPED_MEMORY_OPERATIONS) +endif() if(NOT MSVC OR CMAKE_HOST_SYSTEM_NAME STREQUAL Windows) set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_NO_WARNINGS ON) @@ -67,6 +73,13 @@ elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES Clang) add_compile_options($<$:-pedantic>) endif() +if(HAS_WARN_ALLOCATOR_WRAPPERS) + add_compile_options($<$:-Wallocator-wrappers>) +endif() +if(HAS_TYPED_MEMORY_OPERATIONS) + add_compile_options($<$:-ftyped-memory-operations>) +endif() + if(CMAKE_BUILD_TYPE STREQUAL "Ubsan") add_compile_options($<$:-fsanitize=undefined>) endif() diff --git a/Package@swift-6.3.swift b/Package@swift-6.3.swift new file mode 100644 index 000000000..a971260e6 --- /dev/null +++ b/Package@swift-6.3.swift @@ -0,0 +1,81 @@ +// swift-tools-version:6.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +var cSettings: [CSetting] = [ + .unsafeFlags(["-ftyped-memory-operations"]), + .enableWarning("allocator-wrappers"), +] + +#if os(Windows) + // When building the library on Windows, we do not have proper control of + // whether it is being built statically or dynamically. However, given the + // current default of static, we will assume that we are building + // statically. More importantly, should this not hold, this will fail at + // link time. + cSettings.append( + .define("CMARK_GFM_STATIC_DEFINE", .when(platforms: [.windows])) + ) +#endif + +let package = Package( + name: "cmark-gfm", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "cmark-gfm", + targets: ["cmark-gfm"]), + .library( + name: "cmark-gfm-extensions", + targets: ["cmark-gfm-extensions"]), + .executable( + name: "cmark-gfm-bin", + targets: ["cmark-gfm-bin"]), + .executable(name: "api_test", + targets: ["api_test"]) + ], + targets: [ + .target(name: "cmark-gfm", + path: "src", + exclude: [ + "scanners.re", + "libcmark-gfm.pc.in", + "config.h.in", + "CMakeLists.txt", + ], + cSettings: cSettings + ), + .target(name: "cmark-gfm-extensions", + dependencies: [ + "cmark-gfm", + ], + path: "extensions", + exclude: [ + "CMakeLists.txt", + "ext_scanners.re", + ], + cSettings: cSettings + ), + .executableTarget(name: "cmark-gfm-bin", + dependencies: [ + "cmark-gfm", + "cmark-gfm-extensions", + ], + path: "bin", + sources: [ + "main.c", + ] + ), + .executableTarget(name: "api_test", + dependencies: [ + "cmark-gfm", + "cmark-gfm-extensions", + ], + path: "api_test", + exclude: [ + "CMakeLists.txt", + ] + ) + ] +) diff --git a/bin/main.c b/bin/main.c index 46c54bb33..a0ba1229d 100644 --- a/bin/main.c +++ b/bin/main.c @@ -11,6 +11,7 @@ #include "syntax_extension.h" #include "parser.h" #include "registry.h" +#include "mem.h" #include @@ -104,7 +105,7 @@ static bool print_document(cmark_node *document, writer_format writer, return false; } printf("%s", result); - mem->free(result); + cmark_mem_free(mem, result); return true; } diff --git a/extensions/table.c b/extensions/table.c index c84f863e7..8ac67a875 100644 --- a/extensions/table.c +++ b/extensions/table.c @@ -12,6 +12,7 @@ #include "strikethrough.h" #include "table.h" #include "cmark-gfm-core-extensions.h" +#include "mem.h" // Limit to prevent a malicious input from causing a denial of service. #define MAX_AUTOCOMPLETED_CELLS 0x80000 @@ -52,16 +53,16 @@ typedef struct { static void free_table_cell(cmark_mem *mem, node_cell *cell) { cmark_strbuf_free((cmark_strbuf *)cell->buf); - mem->free(cell->buf); + cmark_mem_free(mem, cell->buf); if (cell->cell_data) - mem->free(cell->cell_data); + cmark_mem_free(mem, cell->cell_data); } static void free_row_cells(cmark_mem *mem, table_row *row) { while (row->n_columns > 0) { free_table_cell(mem, &row->cells[--row->n_columns]); } - mem->free(row->cells); + cmark_mem_free(mem, row->cells); row->cells = NULL; } @@ -70,21 +71,21 @@ static void free_table_row(cmark_mem *mem, table_row *row) { return; free_row_cells(mem, row); - mem->free(row); + cmark_mem_free(mem, row); } static void free_node_table(cmark_mem *mem, void *ptr) { node_table *t = (node_table *)ptr; - mem->free(t->alignments); - mem->free(t); + cmark_mem_free(mem, t->alignments); + cmark_mem_free(mem, t); } static void free_node_table_row(cmark_mem *mem, void *ptr) { - mem->free(ptr); + cmark_mem_free(mem, ptr); } static void free_node_table_cell_data(cmark_mem *mem, void *data) { - mem->free(data); + cmark_mem_free(mem, data); } static int get_n_table_columns(cmark_node *node) { @@ -222,7 +223,7 @@ static int increment_cell_rowspan(cmark_node *node) { static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len) { - cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf)); + cmark_strbuf *res = CMARK_CALLOC_ONE(mem, cmark_strbuf); bufsize_t r, w; cmark_strbuf_init(mem, res, len + 1); @@ -253,7 +254,7 @@ static node_cell* append_row_cell(cmark_mem *mem, table_row *row) { return NULL; } // Use realloc to double the size of the buffer. - row->cells = (node_cell *)mem->realloc(row->cells, (2 * n_columns - 1) * sizeof(node_cell)); + row->cells = CMARK_REALLOC(mem, row->cells, node_cell, 2 * n_columns - 1); } row->n_columns = (uint16_t)n_columns; return &row->cells[n_columns-1]; @@ -279,7 +280,7 @@ static table_row *row_from_string(cmark_syntax_extension *self, int row_end_offset = 0; int int_overflow_abort = 0; - row = (table_row *)parser->mem->calloc(1, sizeof(table_row)); + row = CMARK_CALLOC_ONE(parser->mem, table_row); row->n_columns = 0; row->cells = NULL; @@ -304,7 +305,7 @@ static table_row *row_from_string(cmark_syntax_extension *self, if (!cell) { int_overflow_abort = 1; cmark_strbuf_free(cell_buf); - parser->mem->free(cell_buf); + cmark_mem_free(parser->mem, cell_buf); break; } cell->buf = cell_buf; @@ -319,7 +320,7 @@ static table_row *row_from_string(cmark_syntax_extension *self, --cell->start_offset; ++cell->internal_offset; } - cell->cell_data = (node_cell_data *)parser->mem->calloc(1, sizeof(node_cell_data)); + cell->cell_data = CMARK_CALLOC_ONE(parser->mem, node_cell_data); if (parser->options & CMARK_OPT_TABLE_SPANS) { // Check for a column-spanning cell @@ -411,10 +412,10 @@ static void try_inserting_table_header_paragraph(cmark_parser *parser, cmark_strbuf_trim(paragraph_content); cmark_node_set_string_content(paragraph, (char *) paragraph_content->ptr); cmark_strbuf_free(paragraph_content); - parser->mem->free(paragraph_content); + cmark_mem_free(parser->mem, paragraph_content); if (!cmark_node_insert_before(parent_container, paragraph)) { - parser->mem->free(paragraph); + cmark_mem_free(parser->mem, paragraph); } } @@ -490,13 +491,13 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self, } cmark_node_set_syntax_extension(parent_container, self); - parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table)); + parent_container->as.opaque = CMARK_CALLOC_ONE(parser->mem, node_table); set_n_table_columns(parent_container, header_row->n_columns); // allocate alignments based on delimiter_row->n_columns // since we populate the alignments array based on delimiter_row->cells uint8_t *alignments = - (uint8_t *)parser->mem->calloc(delimiter_row->n_columns, sizeof(uint8_t)); + CMARK_CALLOC(parser->mem, uint8_t, delimiter_row->n_columns); for (i = 0; i < delimiter_row->n_columns; ++i) { node_cell *node = &delimiter_row->cells[i]; bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':'; @@ -517,7 +518,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self, table_header->end_column = parent_container->start_column + (int)strlen(parent_string) - 2; table_header->start_line = table_header->end_line = parent_container->start_line; - table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row)); + table_header->as.opaque = ntr = CMARK_CALLOC_ONE(parser->mem, node_table_row); ntr->is_header = true; for (i = 0; i < header_row->n_columns; ++i) { @@ -564,7 +565,7 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self, parent_container->start_column); cmark_node_set_syntax_extension(table_row_block, self); table_row_block->end_column = parent_container->end_column; - table_row_block->as.opaque = parser->mem->calloc(1, sizeof(node_table_row)); + table_row_block->as.opaque = CMARK_CALLOC_ONE(parser->mem, node_table_row); row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser), len - cmark_parser_get_first_nonspace(parser)); @@ -1032,11 +1033,11 @@ static void html_render(cmark_syntax_extension *extension, static void opaque_alloc(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) { if (node->type == CMARK_NODE_TABLE) { - node->as.opaque = mem->calloc(1, sizeof(node_table)); + node->as.opaque = CMARK_CALLOC_ONE(mem, node_table); } else if (node->type == CMARK_NODE_TABLE_ROW) { - node->as.opaque = mem->calloc(1, sizeof(node_table_row)); + node->as.opaque = CMARK_CALLOC_ONE(mem, node_table_row); } else if (node->type == CMARK_NODE_TABLE_CELL) { - node->as.opaque = mem->calloc(1, sizeof(node_cell_data)); + node->as.opaque = CMARK_CALLOC_ONE(mem, node_cell_data); } } @@ -1106,7 +1107,7 @@ int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns) CMARK_GFM_EXPORT int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments) { - uint8_t *a = (uint8_t *)cmark_node_mem(node)->calloc(1, ncols); + uint8_t *a = CMARK_CALLOC(cmark_node_mem(node), uint8_t, ncols); memcpy(a, alignments, ncols); return set_table_alignments(node, a); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index deee8dcbb..960670dba 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(libcmark-gfm linked_list.c man.c map.c + mem.c node.c plaintext.c plugin.c @@ -81,6 +82,7 @@ install(FILES include/inlines.h include/iterator.h include/map.h + include/mem.h include/node.h include/parser.h include/plugin.h diff --git a/src/arena.c b/src/arena.c index 86bf5b749..d8adf1e17 100644 --- a/src/arena.c +++ b/src/arena.c @@ -116,7 +116,15 @@ static void arena_free(void *ptr) { /* no-op */ } -cmark_mem CMARK_ARENA_MEM_ALLOCATOR = {arena_calloc, arena_realloc, arena_free}; +static void *arena_calloc_typed(size_t nmem, size_t size, cmark_malloc_type_id type_id) { + return arena_calloc(nmem, size); +} + +static void *arena_realloc_typed(void *ptr, size_t size, cmark_malloc_type_id type_id) { + return arena_realloc(ptr, size); +} + +cmark_mem CMARK_ARENA_MEM_ALLOCATOR = {arena_calloc, arena_realloc, arena_free, arena_calloc_typed, arena_realloc_typed}; cmark_mem *cmark_get_arena_mem_allocator(void) { return &CMARK_ARENA_MEM_ALLOCATOR; diff --git a/src/blocks.c b/src/blocks.c index 1efd8fd01..658bc0905 100644 --- a/src/blocks.c +++ b/src/blocks.c @@ -24,6 +24,7 @@ #include "houdini.h" #include "buffer.h" #include "footnotes.h" +#include "mem.h" #define CODE_INDENT 4 #define TAB_STOP 4 @@ -83,7 +84,7 @@ static cmark_node *make_block(cmark_mem *mem, cmark_node_type tag, int start_line, int start_column) { cmark_node *e; - e = (cmark_node *)mem->calloc(1, sizeof(*e)); + e = CMARK_CALLOC_ONE(mem, cmark_node); cmark_strbuf_init(mem, &e->content, 32); e->type = (uint16_t)tag; e->flags = CMARK_NODE__OPEN; @@ -107,10 +108,10 @@ int cmark_parser_attach_syntax_extension(cmark_parser *parser, if (!parser->inline_syntax_extensions) { // if we're loading an inline extension into this parser for the first time, // allocate new buffers for the inline parser character arrays - parser->skip_chars = (int8_t *)parser->mem->calloc(sizeof(int8_t), 256); + parser->skip_chars = CMARK_CALLOC(parser->mem, int8_t, 256); cmark_set_default_skip_chars(&parser->skip_chars, true); - parser->special_chars = (int8_t *)parser->mem->calloc(sizeof(int8_t), 256); + parser->special_chars = CMARK_CALLOC(parser->mem, int8_t, 256); cmark_set_default_special_chars(&parser->special_chars, true); } @@ -160,7 +161,7 @@ static void cmark_parser_reset(cmark_parser *parser) { } cmark_parser *cmark_parser_new_with_mem(int options, cmark_mem *mem) { - cmark_parser *parser = (cmark_parser *)mem->calloc(1, sizeof(cmark_parser)); + cmark_parser *parser = CMARK_CALLOC_ONE(mem, cmark_parser); parser->mem = mem; parser->options = options; cmark_set_default_skip_chars(&parser->skip_chars, false); @@ -179,8 +180,8 @@ void cmark_parser_free(cmark_parser *parser) { // If any inline syntax extensions were added, free the memory allocated for the special-chars arrays if (parser->inline_syntax_extensions) { - mem->free(parser->special_chars); - mem->free(parser->skip_chars); + cmark_mem_free(mem, parser->special_chars); + cmark_mem_free(mem, parser->skip_chars); } cmark_parser_dispose(parser); @@ -188,7 +189,7 @@ void cmark_parser_free(cmark_parser *parser) { cmark_strbuf_free(&parser->linebuf); cmark_llist_free(parser->mem, parser->syntax_extensions); cmark_llist_free(parser->mem, parser->inline_syntax_extensions); - mem->free(parser); + cmark_mem_free(mem, parser); } static cmark_node *finalize(cmark_parser *parser, cmark_node *b); @@ -537,7 +538,7 @@ static void process_footnotes(cmark_parser *parser) { cur->as.literal = cmark_chunk_buf_detach(&buf); } else { - cmark_node *text = (cmark_node *)parser->mem->calloc(1, sizeof(*text)); + cmark_node *text = CMARK_CALLOC_ONE(parser->mem, cmark_node); cmark_strbuf_init(parser->mem, &text->content, 0); text->type = (uint16_t) CMARK_NODE_TEXT; @@ -603,7 +604,7 @@ static bufsize_t parse_list_marker(cmark_mem *mem, cmark_chunk *input, } } - data = (cmark_list *)mem->calloc(1, sizeof(*data)); + data = CMARK_CALLOC_ONE(mem, cmark_list); data->marker_offset = 0; // will be adjusted later data->list_type = CMARK_BULLET_LIST; data->bullet_char = c; @@ -643,7 +644,7 @@ static bufsize_t parse_list_marker(cmark_mem *mem, cmark_chunk *input, } } - data = (cmark_list *)mem->calloc(1, sizeof(*data)); + data = CMARK_CALLOC_ONE(mem, cmark_list); data->marker_offset = 0; // will be adjusted later data->list_type = CMARK_ORDERED_LIST; data->bullet_char = 0; @@ -1321,7 +1322,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container, parser->first_nonspace + 1); /* TODO: static */ memcpy(&((*container)->as.list), data, sizeof(*data)); - parser->mem->free(data); + cmark_mem_free(parser->mem, data); } else if (indented && !maybe_lazy && !parser->blank) { S_advance_offset(parser, input, CODE_INDENT, true); *container = add_child(parser, *container, CMARK_NODE_CODE_BLOCK, diff --git a/src/buffer.c b/src/buffer.c index 5adfa3054..ec2946108 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -10,6 +10,7 @@ #include "cmark-gfm_config.h" #include "cmark_ctype.h" #include "buffer.h" +#include "mem.h" /* Used as default value for cmark_strbuf->ptr so that people can always * assume ptr is non-NULL and zero terminated even for new cmark_strbufs. @@ -54,8 +55,8 @@ void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) { new_size += 1; new_size = (new_size + 7) & ~7; - buf->ptr = (unsigned char *)buf->mem->realloc(buf->asize ? buf->ptr : NULL, - new_size); + unsigned char *ptr = buf->asize ? buf->ptr : NULL; + buf->ptr = CMARK_REALLOC(buf->mem, ptr, unsigned char, new_size); buf->asize = new_size; } @@ -66,7 +67,7 @@ void cmark_strbuf_free(cmark_strbuf *buf) { return; if (buf->ptr != cmark_strbuf__initbuf) - buf->mem->free(buf->ptr); + cmark_mem_free(buf->mem, buf->ptr); cmark_strbuf_init(buf->mem, buf, 0); } @@ -150,7 +151,7 @@ unsigned char *cmark_strbuf_detach(cmark_strbuf *buf) { if (buf->asize == 0) { /* return an empty string */ - return (unsigned char *)buf->mem->calloc(1, 1); + return CMARK_CALLOC_ONE(buf->mem, unsigned char); } cmark_strbuf_init(buf->mem, buf, 0); diff --git a/src/cmark.c b/src/cmark.c index f2619f429..9ec3b0617 100644 --- a/src/cmark.c +++ b/src/cmark.c @@ -6,6 +6,13 @@ #include "houdini.h" #include "cmark-gfm.h" #include "buffer.h" +#include "mem.h" + +#define START_TYPE_ALLOCATOR_IMPL \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wallocator-wrappers\"") +#define END_TYPE_ALLOCATOR_IMPL \ + _Pragma("clang diagnostic pop") cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FOOTNOTE_DEFINITION; cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_ATTRIBUTE; @@ -14,6 +21,80 @@ int cmark_version(void) { return CMARK_GFM_VERSION; } const char *cmark_version_string(void) { return CMARK_GFM_VERSION_STRING; } +#if defined(_MALLOC_TYPE_ENABLED) && _MALLOC_TYPE_ENABLED + +// Native typed-memory allocators are only available on Apple platforms starting at macOS 14+, +// so weak-import them here. These definitions are taken from the back-deploy definitions in +// the macOS system headers +__attribute__((weak_import)) void * __sized_by_or_null(count * size) malloc_type_calloc(size_t count, size_t size, malloc_type_id_t type_id) __result_use_check __alloc_size(1,2); +__attribute__((weak_import)) void * __sized_by_or_null(size) malloc_type_realloc(void * __unsafe_indexable ptr, size_t size, malloc_type_id_t type_id) __result_use_check __alloc_size(2); + +// These are the typed allocator versions of the allocator wrappers. +// The typed-operation behavior is managed in mem.c +START_TYPE_ALLOCATOR_IMPL + +static void *xcalloc_typed(size_t nmem, size_t size, cmark_malloc_type_id type_id) { + void *ptr; + if (malloc_type_calloc) + ptr = malloc_type_calloc(nmem, size, type_id); + else { + #if defined(_MALLOC_TYPE_MALLOC_IS_BACKDEPLOYING) && _MALLOC_TYPE_MALLOC_IS_BACKDEPLOYING + ptr = malloc_type_calloc_backdeploy(nmem, size, type_id); + #else + ptr = calloc(nmem, size); + #endif + } + if (!ptr) { + fprintf(stderr, "[cmark] calloc returned null pointer, aborting\n"); + abort(); + } + return ptr; +} + +// xcalloc_typed handles the back-deploy logic itself, so treat them the same +#define xcalloc_typed_backdeploy xcalloc_typed + +static void *xrealloc_typed(void *ptr, size_t size, cmark_malloc_type_id type_id) { + void *new_ptr; + if (malloc_type_realloc) { + new_ptr = malloc_type_realloc(ptr, size, type_id); + } else { + #if defined(_MALLOC_TYPE_MALLOC_IS_BACKDEPLOYING) && _MALLOC_TYPE_MALLOC_IS_BACKDEPLOYING + new_ptr = malloc_type_realloc_backdeploy(ptr, size, type_id); + #else + new_ptr = realloc(ptr, size); + #endif + } + if (!new_ptr) { + fprintf(stderr, "[cmark] realloc returned null pointer, aborting\n"); + abort(); + } + return new_ptr; +} + +#define xrealloc_typed_backdeploy xrealloc_typed + +END_TYPE_ALLOCATOR_IMPL + +#else + +static void *xcalloc(size_t nmem, size_t size); +static void *xrealloc(void *ptr, size_t size); + +static void *xcalloc_typed(size_t nmem, size_t size, cmark_malloc_type_id type_id) { + return xcalloc(nmem, size); +} + +static void *xrealloc_typed(void *ptr, size_t size, cmark_malloc_type_id type_id) { + return xrealloc(ptr, size); +} + +#endif + +// When type-aware allocators are available, these functions participate in the +// type-aware allocations machinery. This behavior is managed in mem.c +START_TYPE_ALLOCATOR_IMPL + static void *xcalloc(size_t nmem, size_t size) { void *ptr = calloc(nmem, size); if (!ptr) { @@ -36,7 +117,9 @@ static void xfree(void *ptr) { free(ptr); } -cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, xfree}; +END_TYPE_ALLOCATOR_IMPL + +cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, xfree, xcalloc_typed, xrealloc_typed}; cmark_mem *cmark_get_default_mem_allocator(void) { return &CMARK_DEFAULT_MEM_ALLOCATOR; diff --git a/src/commonmark.c b/src/commonmark.c index dee18b4d9..9c21de7f9 100644 --- a/src/commonmark.c +++ b/src/commonmark.c @@ -13,6 +13,7 @@ #include "scanners.h" #include "render.h" #include "syntax_extension.h" +#include "mem.h" #define OUT(s, wrap, escaping) renderer->out(renderer, node, s, wrap, escaping) #define LIT(s) renderer->out(renderer, node, s, false, LITERAL) @@ -474,11 +475,11 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node, if (entering) { LIT("[^"); - char *footnote_label = renderer->mem->calloc(node->parent_footnote_def->as.literal.len + 1, sizeof(char)); + char *footnote_label = CMARK_CALLOC(renderer->mem, char, node->parent_footnote_def->as.literal.len + 1); memmove(footnote_label, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len); OUT(footnote_label, false, LITERAL); - renderer->mem->free(footnote_label); + cmark_mem_free(renderer->mem, footnote_label); LIT("]"); } @@ -489,11 +490,11 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node, renderer->footnote_ix += 1; LIT("[^"); - char *footnote_label = renderer->mem->calloc(node->as.literal.len + 1, sizeof(char)); + char *footnote_label = CMARK_CALLOC(renderer->mem, char, node->as.literal.len + 1); memmove(footnote_label, node->as.literal.data, node->as.literal.len); OUT(footnote_label, false, LITERAL); - renderer->mem->free(footnote_label); + cmark_mem_free(renderer->mem, footnote_label); LIT("]:\n"); diff --git a/src/footnotes.c b/src/footnotes.c index c2b745f79..58b963e18 100644 --- a/src/footnotes.c +++ b/src/footnotes.c @@ -3,15 +3,16 @@ #include "footnotes.h" #include "inlines.h" #include "chunk.h" +#include "mem.h" static void footnote_free(cmark_map *map, cmark_map_entry *_ref) { cmark_footnote *ref = (cmark_footnote *)_ref; cmark_mem *mem = map->mem; if (ref != NULL) { - mem->free(ref->entry.label); + cmark_mem_free(mem, ref->entry.label); if (ref->node) cmark_node_free(ref->node); - mem->free(ref); + cmark_mem_free(mem, ref); } } @@ -25,7 +26,7 @@ void cmark_footnote_create(cmark_map *map, cmark_node *node) { assert(map->sorted == NULL); - ref = (cmark_footnote *)map->mem->calloc(1, sizeof(*ref)); + ref = CMARK_CALLOC_ONE(map->mem, cmark_footnote); ref->entry.label = reflabel; ref->node = node; ref->entry.age = map->size; diff --git a/src/include/chunk.h b/src/include/chunk.h index bc93a8753..d8a0f0090 100644 --- a/src/include/chunk.h +++ b/src/include/chunk.h @@ -7,6 +7,7 @@ #include "cmark-gfm.h" #include "buffer.h" #include "cmark_ctype.h" +#include "mem.h" #define CMARK_CHUNK_EMPTY \ { NULL, 0, 0 } @@ -19,7 +20,7 @@ typedef struct cmark_chunk { static inline void cmark_chunk_free(cmark_mem *mem, cmark_chunk *c) { if (c->alloc) - mem->free(c->data); + cmark_mem_free(mem, c->data); c->data = NULL; c->alloc = 0; @@ -64,7 +65,7 @@ static inline const char *cmark_chunk_to_cstr(cmark_mem *mem, cmark_chunk *c) { if (c->alloc) { return (char *)c->data; } - str = (unsigned char *)mem->calloc(c->len + 1, 1); + str = CMARK_CALLOC(mem, unsigned char, c->len + 1); if (c->len > 0) { memcpy(str, c->data, c->len); } @@ -84,12 +85,12 @@ static inline void cmark_chunk_set_cstr(cmark_mem *mem, cmark_chunk *c, c->alloc = 0; } else { c->len = (bufsize_t)strlen(str); - c->data = (unsigned char *)mem->calloc(c->len + 1, 1); + c->data = CMARK_CALLOC(mem, unsigned char, c->len + 1); c->alloc = 1; memcpy(c->data, str, c->len + 1); } if (old != NULL) { - mem->free(old); + cmark_mem_free(mem, old); } } diff --git a/src/include/cmark-gfm.h b/src/include/cmark-gfm.h index ce2c3b113..71874cb19 100644 --- a/src/include/cmark-gfm.h +++ b/src/include/cmark-gfm.h @@ -3,6 +3,7 @@ #include #include +#include #include "export.h" #include "cmark-gfm_version.h" @@ -106,6 +107,12 @@ typedef struct cmark_syntax_extension cmark_syntax_extension; * ## Custom memory allocator support */ +#if defined(_MALLOC_TYPE_ENABLED) && _MALLOC_TYPE_ENABLED +typedef malloc_type_id_t cmark_malloc_type_id; +#else +typedef unsigned long long cmark_malloc_type_id; +#endif + /** Defines the memory allocation functions to be used by CMark * when parsing and allocating a document tree */ @@ -113,6 +120,8 @@ typedef struct cmark_mem { void *(*calloc)(size_t, size_t); void *(*realloc)(void *, size_t); void (*free)(void *); + void *(*calloc_typed)(size_t, size_t, cmark_malloc_type_id); + void *(*realloc_typed)(void *, size_t, cmark_malloc_type_id); } cmark_mem; /** The default memory allocator; uses the system's calloc, diff --git a/src/include/mem.h b/src/include/mem.h new file mode 100644 index 000000000..d3c0d4b04 --- /dev/null +++ b/src/include/mem.h @@ -0,0 +1,45 @@ +#ifndef CMARK_MEM_H +#define CMARK_MEM_H + +#include +#include "cmark-gfm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MALLOC_TYPE_ENABLED) && _MALLOC_TYPE_ENABLED +# define CMARK_MALLOC_TYPED(F,N) _MALLOC_TYPED(F,N) +#else +# define CMARK_MALLOC_TYPED(F,N) +#endif + +CMARK_GFM_EXPORT +void *cmark_mem_calloc_typed(cmark_mem *mem, size_t count, size_t size, cmark_malloc_type_id type_id); + +#define cmark_mem_calloc_typed_backdeploy cmark_mem_calloc_typed + +CMARK_GFM_EXPORT +void *cmark_mem_realloc_typed(cmark_mem *mem, void *ptr, size_t size, cmark_malloc_type_id type_id); + +#define cmark_mem_realloc_typed_backdeploy cmark_mem_realloc_typed + +CMARK_GFM_EXPORT +void *cmark_mem_calloc(cmark_mem *mem, size_t count, size_t size) CMARK_MALLOC_TYPED(cmark_mem_calloc_typed, 3); + +#define CMARK_CALLOC(MEM, TYPE, COUNT) (TYPE *)cmark_mem_calloc(MEM, COUNT, sizeof(TYPE)) +#define CMARK_CALLOC_ONE(MEM, TYPE) CMARK_CALLOC(MEM, TYPE, 1) + +CMARK_GFM_EXPORT +void *cmark_mem_realloc(cmark_mem *mem, void *ptr, size_t size) CMARK_MALLOC_TYPED(cmark_mem_realloc_typed, 3); + +#define CMARK_REALLOC(MEM, PTR, TYPE, COUNT) (TYPE *)cmark_mem_realloc(MEM, PTR, (COUNT) * sizeof(TYPE)) + +CMARK_GFM_EXPORT +void cmark_mem_free(cmark_mem *mem, void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/inlines.c b/src/inlines.c index 5fee895d6..b709b31ca 100644 --- a/src/inlines.c +++ b/src/inlines.c @@ -14,6 +14,7 @@ #include "scanners.h" #include "inlines.h" #include "syntax_extension.h" +#include "mem.h" static const char *EMDASH = "\xE2\x80\x94"; static const char *ENDASH = "\xE2\x80\x93"; @@ -97,7 +98,7 @@ static bufsize_t subject_find_special_char(cmark_parser *parser, subject *subj, static inline cmark_node *make_literal(subject *subj, cmark_node_type t, int start_column, int end_column, cmark_chunk s) { - cmark_node *e = (cmark_node *)subj->mem->calloc(1, sizeof(*e)); + cmark_node *e = CMARK_CALLOC_ONE(subj->mem, cmark_node); cmark_strbuf_init(subj->mem, &e->content, 0); e->type = (uint16_t)t; e->as.literal = s; @@ -110,7 +111,7 @@ static inline cmark_node *make_literal(subject *subj, cmark_node_type t, // Create an inline with no value. static inline cmark_node *make_simple(cmark_mem *mem, cmark_node_type t) { - cmark_node *e = (cmark_node *)mem->calloc(1, sizeof(*e)); + cmark_node *e = CMARK_CALLOC_ONE(mem, cmark_node); cmark_strbuf_init(mem, &e->content, 0); e->type = (uint16_t)t; return e; @@ -154,7 +155,7 @@ static cmark_chunk chunk_clone(cmark_mem *mem, cmark_chunk *src) { bufsize_t len = src->len; c.len = len; - c.data = (unsigned char *)mem->calloc(len + 1, 1); + c.data = CMARK_CALLOC(mem, unsigned char, len + 1); c.alloc = 1; if (len) memcpy(c.data, src->data, len); @@ -528,7 +529,7 @@ static void remove_delimiter(subject *subj, delimiter *delim) { if (delim->previous != NULL) { delim->previous->next = delim->next; } - subj->mem->free(delim); + cmark_mem_free(subj->mem, delim); } static void pop_bracket(subject *subj) { @@ -537,12 +538,12 @@ static void pop_bracket(subject *subj) { return; b = subj->last_bracket; subj->last_bracket = subj->last_bracket->previous; - subj->mem->free(b); + cmark_mem_free(subj->mem, b); } static void push_delimiter(subject *subj, unsigned char c, bool can_open, bool can_close, cmark_node *inl_text) { - delimiter *delim = (delimiter *)subj->mem->calloc(1, sizeof(delimiter)); + delimiter *delim = CMARK_CALLOC_ONE(subj->mem, delimiter); delim->delim_char = c; delim->can_open = can_open; delim->can_close = can_close; @@ -558,7 +559,7 @@ static void push_delimiter(subject *subj, unsigned char c, bool can_open, } static void push_bracket(subject *subj, bracket_type type, cmark_node *inl_text) { - bracket *b = (bracket *)subj->mem->calloc(1, sizeof(bracket)); + bracket *b = CMARK_CALLOC_ONE(subj->mem, bracket); if (subj->last_bracket != NULL) { subj->last_bracket->bracket_after = true; memcpy(b->in_bracket, subj->last_bracket->in_bracket, sizeof(b->in_bracket)); diff --git a/src/iterator.c b/src/iterator.c index d8c0eade5..dc2d473ed 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -6,13 +6,14 @@ #include "node.h" #include "cmark-gfm.h" #include "iterator.h" +#include "mem.h" cmark_iter *cmark_iter_new(cmark_node *root) { if (root == NULL) { return NULL; } cmark_mem *mem = root->content.mem; - cmark_iter *iter = (cmark_iter *)mem->calloc(1, sizeof(cmark_iter)); + cmark_iter *iter = CMARK_CALLOC_ONE(mem, cmark_iter); iter->mem = mem; iter->root = root; iter->cur.ev_type = CMARK_EVENT_NONE; @@ -22,7 +23,7 @@ cmark_iter *cmark_iter_new(cmark_node *root) { return iter; } -void cmark_iter_free(cmark_iter *iter) { iter->mem->free(iter); } +void cmark_iter_free(cmark_iter *iter) { cmark_mem_free(iter->mem, iter); } static bool S_is_leaf(cmark_node *node) { switch (node->type) { diff --git a/src/linked_list.c b/src/linked_list.c index 8c26dc557..855eee863 100644 --- a/src/linked_list.c +++ b/src/linked_list.c @@ -1,10 +1,11 @@ #include #include "cmark-gfm.h" +#include "mem.h" cmark_llist *cmark_llist_append(cmark_mem *mem, cmark_llist *head, void *data) { cmark_llist *tmp; - cmark_llist *new_node = (cmark_llist *) mem->calloc(1, sizeof(cmark_llist)); + cmark_llist *new_node = CMARK_CALLOC_ONE(mem, cmark_llist); new_node->data = data; new_node->next = NULL; @@ -28,7 +29,7 @@ void cmark_llist_free_full(cmark_mem *mem, cmark_llist *head, cmark_free_func fr prev = tmp; tmp = tmp->next; - mem->free(prev); + cmark_mem_free(mem, prev); } } diff --git a/src/map.c b/src/map.c index f60f8fc60..0c9003966 100644 --- a/src/map.c +++ b/src/map.c @@ -1,6 +1,7 @@ #include "map.h" #include "utf8.h" #include "parser.h" +#include "mem.h" // normalize map label: collapse internal whitespace to single space, // remove leading/trailing whitespace, case fold @@ -24,7 +25,7 @@ unsigned char *normalize_map_label(cmark_mem *mem, cmark_chunk *ref) { assert(result); if (result[0] == '\0') { - mem->free(result); + cmark_mem_free(mem, result); return NULL; } @@ -54,7 +55,7 @@ static void sort_map(cmark_map *map) { size_t i = 0, last = 0, size = map->size; cmark_map_entry *r = map->refs, **sorted = NULL; - sorted = (cmark_map_entry **)map->mem->calloc(size, sizeof(cmark_map_entry *)); + sorted = CMARK_CALLOC(map->mem, cmark_map_entry *, size); while (r) { sorted[i++] = r; r = r->next; @@ -90,7 +91,7 @@ cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label) { sort_map(map); ref = (cmark_map_entry **)bsearch(norm, map->sorted, map->size, sizeof(cmark_map_entry *), refsearch); - map->mem->free(norm); + cmark_mem_free(map->mem, norm); if (ref != NULL) { r = ref[0]; @@ -116,12 +117,12 @@ void cmark_map_free(cmark_map *map) { ref = next; } - map->mem->free(map->sorted); - map->mem->free(map); + cmark_mem_free(map->mem, map->sorted); + cmark_mem_free(map->mem, map); } cmark_map *cmark_map_new(cmark_mem *mem, cmark_map_free_f free) { - cmark_map *map = (cmark_map *)mem->calloc(1, sizeof(cmark_map)); + cmark_map *map = CMARK_CALLOC_ONE(mem, cmark_map); map->mem = mem; map->free = free; map->max_ref_size = UINT_MAX; diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 000000000..839bff667 --- /dev/null +++ b/src/mem.c @@ -0,0 +1,42 @@ +#include "cmark-gfm.h" +#include "mem.h" + +void *cmark_mem_calloc(cmark_mem *mem, size_t count, size_t size) { + return mem->calloc(count, size); +} + +void *cmark_mem_realloc(cmark_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size); +} + +void cmark_mem_free(cmark_mem *mem, void *ptr) { + mem->free(ptr); +} + +#if defined(_MALLOC_TYPE_ENABLED) && _MALLOC_TYPE_ENABLED + +void *cmark_mem_calloc_typed(cmark_mem *mem, size_t count, size_t size, cmark_malloc_type_id type_id) { + if (mem->calloc_typed) + return mem->calloc_typed(count, size, type_id); + + return mem->calloc(count, size); +} + +void *cmark_mem_realloc_typed(cmark_mem *mem, void *ptr, size_t size, cmark_malloc_type_id type_id) { + if (mem->realloc_typed) + return mem->realloc_typed(ptr, size, type_id); + + return mem->realloc(ptr, size); +} + +#else + +void *cmark_mem_calloc_typed(cmark_mem *mem, size_t count, size_t size, cmark_malloc_type_id type_id) { + return cmark_mem_calloc(mem, count, size); +} + +void *cmark_mem_realloc_typed(cmark_mem *mem, void *ptr, size_t size, cmark_malloc_type_id type_id) { + return cmark_mem_realloc(mem, ptr, size); +} + +#endif diff --git a/src/node.c b/src/node.c index 967b1b900..889cde2e6 100644 --- a/src/node.c +++ b/src/node.c @@ -3,6 +3,7 @@ #include #include "cmark-gfm_config.h" +#include "mem.h" #include "mutex.h" #include "node.h" #include "syntax_extension.h" @@ -125,7 +126,7 @@ static bool S_can_contain(cmark_node *node, cmark_node *child) { } cmark_node *cmark_node_new_with_mem_and_ext(cmark_node_type type, cmark_mem *mem, cmark_syntax_extension *extension) { - cmark_node *node = (cmark_node *)mem->calloc(1, sizeof(*node)); + cmark_node *node = CMARK_CALLOC_ONE(mem, cmark_node); cmark_strbuf_init(mem, &node->content, 0); node->type = (uint16_t)type; node->extension = extension; @@ -220,7 +221,7 @@ static void S_free_nodes(cmark_node *e) { e->next = e->first_child; } next = e->next; - NODE_MEM(e)->free(e); + cmark_mem_free(NODE_MEM(e), e); e = next; } } diff --git a/src/plugin.c b/src/plugin.c index 3992fe197..84db02579 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1,5 +1,6 @@ #include +#include "mem.h" #include "plugin.h" extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR; @@ -12,7 +13,7 @@ int cmark_plugin_register_syntax_extension(cmark_plugin * plugin, cmark_plugin * cmark_plugin_new(void) { - cmark_plugin *res = (cmark_plugin *) CMARK_DEFAULT_MEM_ALLOCATOR.calloc(1, sizeof(cmark_plugin)); + cmark_plugin *res = CMARK_CALLOC_ONE(&CMARK_DEFAULT_MEM_ALLOCATOR, cmark_plugin); res->syntax_extensions = NULL; @@ -24,7 +25,7 @@ cmark_plugin_free(cmark_plugin *plugin) { cmark_llist_free_full(&CMARK_DEFAULT_MEM_ALLOCATOR, plugin->syntax_extensions, (cmark_free_func) cmark_syntax_extension_free); - CMARK_DEFAULT_MEM_ALLOCATOR.free(plugin); + cmark_mem_free(&CMARK_DEFAULT_MEM_ALLOCATOR, plugin); } cmark_llist * diff --git a/src/references.c b/src/references.c index 6dec0637a..54e407a2f 100644 --- a/src/references.c +++ b/src/references.c @@ -5,16 +5,17 @@ #include "references.h" #include "inlines.h" #include "chunk.h" +#include "mem.h" static void reference_free(cmark_map *map, cmark_map_entry *_ref) { cmark_reference *ref = (cmark_reference *)_ref; cmark_mem *mem = map->mem; if (ref != NULL) { - mem->free(ref->entry.label); + cmark_mem_free(mem, ref->entry.label); cmark_chunk_free(mem, &ref->url); cmark_chunk_free(mem, &ref->title); cmark_chunk_free(mem, &ref->attributes); - mem->free(ref); + cmark_mem_free(mem, ref); } } @@ -29,7 +30,7 @@ void cmark_reference_create(cmark_map *map, cmark_chunk *label, assert(map->sorted == NULL); - ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref)); + ref = CMARK_CALLOC_ONE(map->mem, cmark_reference); ref->entry.label = reflabel; ref->is_attributes_reference = false; ref->url = cmark_clean_url(map->mem, url); @@ -54,7 +55,7 @@ void cmark_reference_create_attributes(cmark_map *map, cmark_chunk *label, assert(map->sorted == NULL); - ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref)); + ref = CMARK_CALLOC_ONE(map->mem, cmark_reference); ref->entry.label = reflabel; ref->is_attributes_reference = true; ref->url = cmark_chunk_literal(""); diff --git a/src/syntax_extension.c b/src/syntax_extension.c index 361c99333..66c4e594c 100644 --- a/src/syntax_extension.c +++ b/src/syntax_extension.c @@ -5,6 +5,7 @@ #include "cmark-gfm.h" #include "syntax_extension.h" #include "buffer.h" +#include "mem.h" extern cmark_mem CMARK_DEFAULT_MEM_ALLOCATOR; @@ -16,14 +17,14 @@ void cmark_syntax_extension_free(cmark_mem *mem, cmark_syntax_extension *extensi } cmark_llist_free(mem, extension->special_inline_chars); - mem->free(extension->name); - mem->free(extension); + cmark_mem_free(mem, extension->name); + cmark_mem_free(mem, extension); } cmark_syntax_extension *cmark_syntax_extension_new(const char *name) { - cmark_syntax_extension *res = (cmark_syntax_extension *) _mem->calloc(1, sizeof(cmark_syntax_extension)); + cmark_syntax_extension *res = CMARK_CALLOC_ONE(_mem, cmark_syntax_extension); size_t size = strlen(name) + 1; - res->name = (char *) _mem->calloc(size, sizeof(char)); + res->name = CMARK_CALLOC(_mem, char, size); #if defined(_WIN32) strcpy_s(res->name, size, name); #else