Add cJSON_Allocators new style allocator struct

This commit is contained in:
Max Bruckner 2018-02-01 10:26:58 +01:00
parent dd1ba72ce2
commit 98e0b586ca
5 changed files with 89 additions and 51 deletions

120
cJSON.c
View File

@ -122,9 +122,10 @@ typedef struct internal_configuration
cJSON_bool format; cJSON_bool format;
cJSON_bool allow_data_after_json; cJSON_bool allow_data_after_json;
cJSON_bool case_sensitive; cJSON_bool case_sensitive;
void *(*allocate)(size_t size); cJSON_Allocators allocators;
void (*deallocate)(void *pointer); void *userdata;
void *(*reallocate)(void *pointer, size_t size);
} internal_configuration; } internal_configuration;
#if defined(_MSC_VER) #if defined(_MSC_VER)
@ -137,24 +138,45 @@ static void internal_free(void *pointer)
{ {
free(pointer); free(pointer);
} }
static void *internal_realloc(void *pointer, size_t size)
{
return realloc(pointer, size);
}
#else #else
#define internal_malloc malloc #define internal_malloc malloc
#define internal_free free #define internal_free free
#define internal_realloc realloc
#endif #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 {\ #define default_configuration {\
256, /* default buffer size */\ 256, /* default buffer size */\
true, /* enable formatting by default */\ true, /* enable formatting by default */\
true, /* allow data after the JSON by default */\ true, /* allow data after the JSON by default */\
true, /* case sensitive by default */\ true, /* case sensitive by default */\
internal_malloc,\ {\
internal_free,\ global_allocate_wrapper,\
internal_realloc\ global_deallocate_wrapper,\
global_reallocate_wrapper\
},\
NULL /* no userdata */\
} }
static internal_configuration global_configuration = default_configuration; static internal_configuration global_configuration = default_configuration;
@ -170,7 +192,7 @@ static unsigned char* custom_strdup(const unsigned char* string, const internal_
} }
length = strlen((const char*)string) + sizeof(""); length = strlen((const char*)string) + sizeof("");
copy = (unsigned char*)configuration->allocate(length); copy = (unsigned char*)configuration->allocators.allocate(length, configuration->userdata);
if (copy == NULL) if (copy == NULL)
{ {
return NULL; return NULL;
@ -182,39 +204,45 @@ static unsigned char* custom_strdup(const unsigned char* string, const internal_
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) 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) if (hooks == NULL)
{ {
/* Reset hooks */ /* reset global allocators */
global_configuration.allocate = malloc; global_allocators.malloc_fn = internal_malloc;
global_configuration.deallocate = free; global_allocators.free_fn = internal_free;
global_configuration.reallocate = realloc;
return; return;
} }
global_configuration.allocate = malloc; global_allocators.malloc_fn = internal_malloc;
if (hooks->malloc_fn != NULL) 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) 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 */ /* use realloc only if both free and malloc are used */
global_configuration.reallocate = NULL; global_configuration.allocators.reallocate = NULL;
if ((global_configuration.allocate == malloc) && (global_configuration.deallocate == free)) if ((hooks->malloc_fn == malloc) && (hooks->free_fn == free))
{ {
global_configuration.reallocate = realloc; global_configuration.allocators.reallocate = global_reallocate_wrapper;
} }
} }
/* Internal constructor. */ /* Internal constructor. */
static cJSON *create_item(const internal_configuration * const configuration) 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) if (node)
{ {
memset(node, '\0', sizeof(cJSON)); memset(node, '\0', sizeof(cJSON));
@ -236,13 +264,13 @@ static void delete_item(cJSON *item, const internal_configuration * const config
} }
if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) 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)) 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; item = next;
} }
} }
@ -427,13 +455,13 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
newsize = needed * 2; newsize = needed * 2;
} }
if (p->configuration.reallocate != NULL) if (*p->configuration.allocators.reallocate != NULL)
{ {
/* reallocate with realloc if available */ /* 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) if (newbuffer == NULL)
{ {
p->configuration.deallocate(p->buffer); p->configuration.allocators.deallocate(p->buffer, p->configuration.userdata);
p->length = 0; p->length = 0;
p->buffer = NULL; p->buffer = NULL;
@ -443,10 +471,10 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
else else
{ {
/* otherwise reallocate manually */ /* otherwise reallocate manually */
newbuffer = (unsigned char*)p->configuration.allocate(newsize); newbuffer = (unsigned char*)p->configuration.allocators.allocate(newsize, p->configuration.userdata);
if (!newbuffer) if (!newbuffer)
{ {
p->configuration.deallocate(p->buffer); p->configuration.allocators.deallocate(p->buffer, p->configuration.userdata);
p->length = 0; p->length = 0;
p->buffer = NULL; p->buffer = NULL;
@ -456,7 +484,7 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
{ {
memcpy(newbuffer, p->buffer, p->offset + 1); 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->length = newsize;
p->buffer = newbuffer; p->buffer = newbuffer;
@ -748,7 +776,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 */ /* This is at most how much we need for the output */
allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; 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) if (output == NULL)
{ {
goto fail; /* allocation failure */ goto fail; /* allocation failure */
@ -826,7 +854,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu
fail: fail:
if (output != NULL) if (output != NULL)
{ {
input_buffer->configuration.deallocate(output); input_buffer->configuration.allocators.deallocate(output, input_buffer->configuration.userdata);
} }
if (input_pointer != NULL) if (input_pointer != NULL)
@ -1117,7 +1145,7 @@ static unsigned char *print(const cJSON * const item, const internal_configurati
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
/* create 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->length = configuration->buffer_size;
buffer->configuration = *configuration; buffer->configuration = *configuration;
if (buffer->buffer == NULL) if (buffer->buffer == NULL)
@ -1135,9 +1163,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. /* 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 */ This can save up to 50% because ensure increases the buffer size by a factor of 2 */
/* check if reallocate is available */ /* 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; buffer->buffer = NULL;
if (printed == NULL) { if (printed == NULL) {
goto fail; goto fail;
@ -1145,7 +1173,7 @@ static unsigned char *print(const cJSON * const item, const internal_configurati
} }
else /* otherwise copy the JSON over to a new buffer */ 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) if (printed == NULL)
{ {
goto fail; goto fail;
@ -1154,7 +1182,7 @@ static unsigned char *print(const cJSON * const item, const internal_configurati
printed[buffer->offset] = '\0'; /* just to be sure */ printed[buffer->offset] = '\0'; /* just to be sure */
/* free the buffer */ /* free the buffer */
configuration->deallocate(buffer->buffer); configuration->allocators.deallocate(buffer->buffer, configuration->userdata);
} }
return printed; return printed;
@ -1162,12 +1190,12 @@ static unsigned char *print(const cJSON * const item, const internal_configurati
fail: fail:
if (buffer->buffer != NULL) if (buffer->buffer != NULL)
{ {
configuration->deallocate(buffer->buffer); configuration->allocators.deallocate(buffer->buffer, configuration->userdata);
} }
if (printed != NULL) if (printed != NULL)
{ {
configuration->deallocate(printed); configuration->allocators.deallocate(printed, configuration->userdata);
} }
return NULL; return NULL;
@ -1203,7 +1231,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) 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)) if ((length < 0) || (buffer == NULL))
{ {
@ -1971,7 +1999,7 @@ static cJSON_bool add_item_to_object(cJSON * const object, const char * const st
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{ {
configuration->deallocate(item->string); configuration->allocators.deallocate(item->string, configuration->userdata);
} }
item->string = new_key; item->string = new_key;
@ -2964,10 +2992,10 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons
CJSON_PUBLIC(void *) cJSON_malloc(size_t size) 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) CJSON_PUBLIC(void) cJSON_free(void *object)
{ {
global_configuration.deallocate(object); global_configuration.allocators.deallocate(object, global_configuration.userdata);
} }

View File

@ -78,6 +78,14 @@ typedef struct cJSON_Hooks
void (*free_fn)(void *ptr); void (*free_fn)(void *ptr);
} cJSON_Hooks; } 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; typedef int cJSON_bool;
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))

View File

@ -33,11 +33,11 @@ void reset(cJSON *item) {
} }
if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference)) 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)) 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)); memset(item, 0, sizeof(cJSON));

View File

@ -409,16 +409,18 @@ static void cjson_functions_shouldnt_crash_with_null_pointers(void)
cJSON_Delete(item); 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)size;
(void)pointer; (void)pointer;
(void)userdata;
return NULL; return NULL;
} }
static void ensure_should_fail_on_failed_realloc(void) 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); buffer.buffer = (unsigned char*)malloc(100);
TEST_ASSERT_NOT_NULL(buffer.buffer); TEST_ASSERT_NOT_NULL(buffer.buffer);

View File

@ -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."); TEST_ASSERT_TRUE_MESSAGE(parse_string(item, &buffer), "Couldn't parse string.");
assert_is_string(item); assert_is_string(item);
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected."); 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; item->valuestring = NULL;
} }