From 440ba84d08fcfda976dcdbbfedb926c87cc56145 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 1 Feb 2018 10:26:58 +0100 Subject: [PATCH] Add cJSON_Allocators new style allocator struct --- cJSON.c | 122 ++++++++++++++++++++++++++----------------- cJSON.h | 8 +++ tests/common.h | 4 +- tests/misc_tests.c | 6 ++- tests/parse_string.c | 2 +- 5 files changed, 90 insertions(+), 52 deletions(-) diff --git a/cJSON.c b/cJSON.c index ffe7909..fb1ff36 100644 --- a/cJSON.c +++ b/cJSON.c @@ -124,9 +124,10 @@ typedef struct internal_configuration cJSON_bool format; cJSON_bool allow_data_after_json; cJSON_bool case_sensitive; - void *(*allocate)(size_t size); - void (*deallocate)(void *pointer); - void *(*reallocate)(void *pointer, size_t size); + cJSON_Allocators allocators; + void *userdata; + + } internal_configuration; #if defined(_MSC_VER) @@ -139,24 +140,45 @@ static void internal_free(void *pointer) { free(pointer); } -static void *internal_realloc(void *pointer, size_t size) -{ - return realloc(pointer, size); -} #else #define internal_malloc malloc #define internal_free free -#define internal_realloc realloc #endif +/* old style allocators for cJSON_InitHooks */ +static cJSON_Hooks global_allocators = { + internal_malloc, + internal_free +}; + +/* wrappers around global old style allocators */ +static void *global_allocate_wrapper(size_t size, void *userdata) +{ + (void)userdata; + return global_allocators.malloc_fn(size); +} +static void *global_reallocate_wrapper(void *pointer, size_t size, void *userdata) +{ + (void)userdata; + return realloc(pointer, size); +} +static void global_deallocate_wrapper(void *pointer, void *userdata) +{ + (void)userdata; + global_allocators.free_fn(pointer); +} + #define default_configuration {\ 256, /* default buffer size */\ true, /* enable formatting by default */\ true, /* allow data after the JSON by default */\ true, /* case sensitive by default */\ - internal_malloc,\ - internal_free,\ - internal_realloc\ + {\ + global_allocate_wrapper,\ + global_deallocate_wrapper,\ + global_reallocate_wrapper\ + },\ + NULL /* no userdata */\ } static internal_configuration global_configuration = default_configuration; @@ -172,7 +194,7 @@ static unsigned char* custom_strdup(const unsigned char* string, const internal_ } length = strlen((const char*)string) + sizeof(""); - copy = (unsigned char*)configuration->allocate(length); + copy = (unsigned char*)configuration->allocators.allocate(length, configuration->userdata); if (copy == NULL) { return NULL; @@ -184,39 +206,45 @@ static unsigned char* custom_strdup(const unsigned char* string, const internal_ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { + /* set the wrappers in the global configuration */ + global_configuration.userdata = NULL; + global_configuration.allocators.allocate = global_allocate_wrapper; + global_configuration.allocators.deallocate = global_deallocate_wrapper; + global_configuration.allocators.reallocate = global_reallocate_wrapper; + if (hooks == NULL) { - /* Reset hooks */ - global_configuration.allocate = malloc; - global_configuration.deallocate = free; - global_configuration.reallocate = realloc; + /* reset global allocators */ + global_allocators.malloc_fn = internal_malloc; + global_allocators.free_fn = internal_free; + return; } - global_configuration.allocate = malloc; + global_allocators.malloc_fn = internal_malloc; if (hooks->malloc_fn != NULL) { - global_configuration.allocate = hooks->malloc_fn; + global_allocators.malloc_fn = hooks->malloc_fn; } - global_configuration.deallocate = free; + global_allocators.free_fn = internal_free; if (hooks->free_fn != NULL) { - global_configuration.deallocate = hooks->free_fn; + global_allocators.free_fn = hooks->free_fn; } /* use realloc only if both free and malloc are used */ - global_configuration.reallocate = NULL; - if ((global_configuration.allocate == malloc) && (global_configuration.deallocate == free)) + global_configuration.allocators.reallocate = NULL; + if ((hooks->malloc_fn == malloc) && (hooks->free_fn == free)) { - global_configuration.reallocate = realloc; + global_configuration.allocators.reallocate = global_reallocate_wrapper; } } /* Internal constructor. */ static cJSON *create_item(const internal_configuration * const configuration) { - cJSON* node = (cJSON*)configuration->allocate(sizeof(cJSON)); + cJSON* node = (cJSON*)configuration->allocators.allocate(sizeof(cJSON), configuration->userdata); if (node) { memset(node, '\0', sizeof(cJSON)); @@ -238,13 +266,13 @@ static void delete_item(cJSON *item, const internal_configuration * const config } if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { - configuration->deallocate(item->valuestring); + configuration->allocators.deallocate(item->valuestring, configuration->userdata); } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { - configuration->deallocate(item->string); + configuration->allocators.deallocate(item->string, configuration->userdata); } - configuration->deallocate(item); + configuration->allocators.deallocate(item, configuration->userdata); item = next; } } @@ -429,13 +457,13 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) newsize = needed * 2; } - if (p->configuration.reallocate != NULL) + if (*p->configuration.allocators.reallocate != NULL) { /* reallocate with realloc if available */ - newbuffer = (unsigned char*)p->configuration.reallocate(p->buffer, newsize); + newbuffer = (unsigned char*)p->configuration.allocators.reallocate(p->buffer, newsize, p->configuration.userdata); if (newbuffer == NULL) { - p->configuration.deallocate(p->buffer); + p->configuration.allocators.deallocate(p->buffer, p->configuration.userdata); p->length = 0; p->buffer = NULL; @@ -445,10 +473,10 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) else { /* otherwise reallocate manually */ - newbuffer = (unsigned char*)p->configuration.allocate(newsize); + newbuffer = (unsigned char*)p->configuration.allocators.allocate(newsize, p->configuration.userdata); if (!newbuffer) { - p->configuration.deallocate(p->buffer); + p->configuration.allocators.deallocate(p->buffer, p->configuration.userdata); p->length = 0; p->buffer = NULL; @@ -458,7 +486,7 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) { memcpy(newbuffer, p->buffer, p->offset + 1); } - p->configuration.deallocate(p->buffer); + p->configuration.allocators.deallocate(p->buffer, p->configuration.userdata); } p->length = newsize; p->buffer = newbuffer; @@ -750,7 +778,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu /* This is at most how much we need for the output */ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; - output = (unsigned char*)input_buffer->configuration.allocate(allocation_length + sizeof("")); + output = (unsigned char*)input_buffer->configuration.allocators.allocate(allocation_length + sizeof(""), input_buffer->configuration.userdata); if (output == NULL) { goto fail; /* allocation failure */ @@ -828,7 +856,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu fail: if (output != NULL) { - input_buffer->configuration.deallocate(output); + input_buffer->configuration.allocators.deallocate(output, input_buffer->configuration.userdata); } if (input_pointer != NULL) @@ -1119,7 +1147,7 @@ static unsigned char *print(const cJSON * const item, const internal_configurati memset(buffer, 0, sizeof(buffer)); /* create buffer */ - buffer->buffer = (unsigned char*) configuration->allocate(configuration->buffer_size); + buffer->buffer = (unsigned char*)configuration->allocators.allocate(configuration->buffer_size, configuration->userdata); buffer->length = configuration->buffer_size; buffer->configuration = *configuration; if (buffer->buffer == NULL) @@ -1137,9 +1165,9 @@ static unsigned char *print(const cJSON * const item, const internal_configurati /* Reallocate the buffer so that it only uses as much as it needs. This can save up to 50% because ensure increases the buffer size by a factor of 2 */ /* check if reallocate is available */ - if (configuration->reallocate != NULL) + if (configuration->allocators.reallocate != NULL) { - printed = (unsigned char*) configuration->reallocate(buffer->buffer, buffer->offset + 1); + printed = (unsigned char*)configuration->allocators.reallocate(buffer->buffer, buffer->offset + 1, configuration->userdata); buffer->buffer = NULL; if (printed == NULL) { goto fail; @@ -1147,7 +1175,7 @@ static unsigned char *print(const cJSON * const item, const internal_configurati } else /* otherwise copy the JSON over to a new buffer */ { - printed = (unsigned char*) configuration->allocate(buffer->offset + 1); + printed = (unsigned char*)configuration->allocators.allocate(buffer->offset + 1, configuration->userdata); if (printed == NULL) { goto fail; @@ -1156,7 +1184,7 @@ static unsigned char *print(const cJSON * const item, const internal_configurati printed[buffer->offset] = '\0'; /* just to be sure */ /* free the buffer */ - configuration->deallocate(buffer->buffer); + configuration->allocators.deallocate(buffer->buffer, configuration->userdata); } return printed; @@ -1164,12 +1192,12 @@ static unsigned char *print(const cJSON * const item, const internal_configurati fail: if (buffer->buffer != NULL) { - configuration->deallocate(buffer->buffer); + configuration->allocators.deallocate(buffer->buffer, configuration->userdata); } if (printed != NULL) { - configuration->deallocate(printed); + configuration->allocators.deallocate(printed, configuration->userdata); } return NULL; @@ -1205,7 +1233,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { - printbuffer p = { 0, 0, 0, 0, 0, default_configuration}; + printbuffer p = { 0, 0, 0, 0, 0, default_configuration }; if ((length < 0) || (buffer == NULL)) { @@ -1350,7 +1378,7 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp { if (!output_buffer->noalloc) { - output_buffer->configuration.deallocate(output_buffer->buffer); + output_buffer->configuration.allocators.deallocate(output_buffer->buffer, output_buffer->configuration.userdata); } return false; } @@ -1958,7 +1986,7 @@ static cJSON_bool add_item_to_object(cJSON * const object, const char * const st if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { - configuration->deallocate(item->string); + configuration->allocators.deallocate(item->string, configuration->userdata); } if (constant_key) @@ -2965,10 +2993,10 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons CJSON_PUBLIC(void *) cJSON_malloc(size_t size) { - return global_configuration.allocate(size); + return global_configuration.allocators.allocate(size, global_configuration.userdata); } CJSON_PUBLIC(void) cJSON_free(void *object) { - global_configuration.deallocate(object); + global_configuration.allocators.deallocate(object, global_configuration.userdata); } diff --git a/cJSON.h b/cJSON.h index 980d9b9..5637d2d 100644 --- a/cJSON.h +++ b/cJSON.h @@ -78,6 +78,14 @@ typedef struct cJSON_Hooks void (*free_fn)(void *ptr); } cJSON_Hooks; +/* new style allocators with userdata (e.g. for pool allocators) */ +typedef struct cJSON_Allocators +{ + void *(*allocate)(size_t size, void *userdata); + void (*deallocate)(void *pointer, void *userdata); + void *(*reallocate)(void *pointer, size_t size, void *userdata); /* optional */ +} cJSON_Allocators; + typedef int cJSON_bool; #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) diff --git a/tests/common.h b/tests/common.h index caf0e50..27c7587 100644 --- a/tests/common.h +++ b/tests/common.h @@ -33,11 +33,11 @@ void reset(cJSON *item) { } if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference)) { - global_configuration.deallocate(item->valuestring); + global_configuration.allocators.deallocate(item->valuestring, global_configuration.userdata); } if ((item->string != NULL) && !(item->type & cJSON_StringIsConst)) { - global_configuration.deallocate(item->string); + global_configuration.allocators.deallocate(item->string, global_configuration.userdata); } memset(item, 0, sizeof(cJSON)); diff --git a/tests/misc_tests.c b/tests/misc_tests.c index e0a129d..f4aad6e 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -410,16 +410,18 @@ static void cjson_functions_shouldnt_crash_with_null_pointers(void) cJSON_Delete(item); } -static void *failing_realloc(void *pointer, size_t size) +static void *failing_realloc(void *pointer, size_t size, void *userdata) { (void)size; (void)pointer; + (void)userdata; return NULL; } static void ensure_should_fail_on_failed_realloc(void) { - printbuffer buffer = {NULL, 10, 0, 0, false, {256, false, true, true, &malloc, &free, &failing_realloc}}; + printbuffer buffer = {NULL, 10, 0, 0, false, {256, false, true, true, {global_allocate_wrapper, global_deallocate_wrapper, failing_realloc}, NULL } }; + buffer.configuration.userdata = &buffer; buffer.buffer = (unsigned char*)malloc(100); TEST_ASSERT_NOT_NULL(buffer.buffer); diff --git a/tests/parse_string.c b/tests/parse_string.c index d22f7c8..39955e5 100644 --- a/tests/parse_string.c +++ b/tests/parse_string.c @@ -53,7 +53,7 @@ static void assert_parse_string(const char *string, const char *expected) TEST_ASSERT_TRUE_MESSAGE(parse_string(item, &buffer), "Couldn't parse string."); assert_is_string(item); TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected."); - global_configuration.deallocate(item->valuestring); + global_configuration.allocators.deallocate(item->valuestring, global_configuration.userdata); item->valuestring = NULL; }