diff --git a/.gitignore b/.gitignore index e2e312b5..58edf92c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ libcjson.so.* libcjson_utils.so.* *.orig .vscode +.idea +cmake-build-debug diff --git a/CMakeLists.txt b/CMakeLists.txt index 09039f62..2062f97f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,6 @@ if (ENABLE_SANITIZERS) -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined - -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize-address-use-after-scope -fsanitize=integer diff --git a/cJSON.c b/cJSON.c index 7e71ea9e..2862fd48 100644 --- a/cJSON.c +++ b/cJSON.c @@ -39,9 +39,7 @@ #include #include -#include #include -#include #include #include @@ -118,12 +116,17 @@ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned return tolower(*string1) - tolower(*string2); } -typedef struct internal_hooks +typedef struct internal_context { - void *(*allocate)(size_t size); - void (*deallocate)(void *pointer); - void *(*reallocate)(void *pointer, size_t size); -} internal_hooks; + size_t buffer_size; + size_t end_position; + void *userdata; + cJSON_Allocators allocators; + cJSON_bool format; + cJSON_bool allow_data_after_json; + cJSON_bool case_sensitive; + cJSON_bool duplicate_recursive; +} internal_context; #if defined(_MSC_VER) /* work around MSVC error C2322: '...' address of dillimport '...' is not static */ @@ -135,19 +138,81 @@ 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 -static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; +/* 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(size_t size, void *userdata) +{ + (void)userdata; + return global_allocators.malloc_fn(size); +} +static void global_deallocate(void *pointer, void *userdata) +{ + (void)userdata; + free(pointer); +} + +/* wrappers around standard allocators */ +static void *malloc_wrapper(size_t size, void *userdata) +{ + (void)userdata; + return malloc(size); +} +static void *realloc_wrapper(void *pointer, size_t size, void *userdata) +{ + (void)userdata; + return realloc(pointer, size); +} +static void free_wrapper(void *pointer, void *userdata) +{ + (void)userdata; + free(pointer); +} + +/* helpers to allocate memory with the allocators in a context */ +static void *allocate(const internal_context * const context, size_t size) +{ + return context->allocators.allocate(size, context->userdata); +} +static void *reallocate(const internal_context * const context, void *pointer, size_t size) +{ + return context->allocators.reallocate(pointer, size, context->userdata); +} +static void deallocate(const internal_context * const context, void *pointer) +{ + context->allocators.deallocate(pointer, context->userdata); +} + +#define default_context {\ + 256, /* default buffer size */\ + 0, /* default end position */\ + NULL, /* no userdata */\ + {\ + malloc_wrapper,\ + free_wrapper,\ + realloc_wrapper\ + },\ + true, /* enable formatting by default */\ + true, /* allow data after the JSON by default */\ + true, /* case sensitive by default */\ + true /* Do cJSON_Duplicate recursively by default */\ +} + +/* this is necessary to assign the default context after initialization */ +static internal_context global_default_context = default_context; + +static internal_context global_context = default_context; -static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +static unsigned char* custom_strdup(const unsigned char* string, const internal_context * const context) { size_t length = 0; unsigned char *copy = NULL; @@ -158,7 +223,7 @@ static unsigned char* cJSON_strdup(const unsigned char* string, const internal_h } length = strlen((const char*)string) + sizeof(""); - copy = (unsigned char*)hooks->allocate(length); + copy = (unsigned char*)allocate(context, length); if (copy == NULL) { return NULL; @@ -172,37 +237,36 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { if (hooks == NULL) { - /* Reset hooks */ - global_hooks.allocate = malloc; - global_hooks.deallocate = free; - global_hooks.reallocate = realloc; + /* reset global context */ + global_context.allocators.allocate = malloc_wrapper; + global_context.allocators.deallocate = free_wrapper; + global_context.allocators.reallocate = realloc_wrapper; + return; } - global_hooks.allocate = malloc; + global_allocators.malloc_fn = internal_malloc; if (hooks->malloc_fn != NULL) { - global_hooks.allocate = hooks->malloc_fn; + global_allocators.malloc_fn = hooks->malloc_fn; } - global_hooks.deallocate = free; + global_allocators.free_fn = internal_free; if (hooks->free_fn != NULL) { - global_hooks.deallocate = hooks->free_fn; + global_allocators.free_fn = hooks->free_fn; } - /* use realloc only if both free and malloc are used */ - global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) - { - global_hooks.reallocate = realloc; - } + /* set the wrappers in the global context */ + global_context.allocators.allocate = global_allocate; + global_context.allocators.deallocate = global_deallocate; + global_context.allocators.reallocate = NULL; } /* Internal constructor. */ -static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +static cJSON *create_item(const internal_context * const context) { - cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + cJSON* node = (cJSON*)allocate(context, sizeof(cJSON)); if (node) { memset(node, '\0', sizeof(cJSON)); @@ -212,7 +276,7 @@ static cJSON *cJSON_New_Item(const internal_hooks * const hooks) } /* Delete a cJSON structure. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +static void delete_item(cJSON *item, const internal_context * const context) { cJSON *next = NULL; while (item != NULL) @@ -220,21 +284,41 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) next = item->next; if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { - cJSON_Delete(item->child); + delete_item(item->child, context); } if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { - global_hooks.deallocate(item->valuestring); + deallocate(context, item->valuestring); } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { - global_hooks.deallocate(item->string); + deallocate(context, item->string); } - global_hooks.deallocate(item); + deallocate(context, item); item = next; } } +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + delete_item(item, &global_context); +} + +static int double_to_saturated_integer(double number) +{ + if (number >= INT_MAX) + { + return INT_MAX; + } + else if (number <= INT_MIN) + { + return INT_MIN; + } + + return (int)number; +} + /* get the decimal point character of the current locale */ static unsigned char get_decimal_point(void) { @@ -252,13 +336,13 @@ typedef struct size_t length; size_t offset; size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ - internal_hooks hooks; + internal_context context; } parse_buffer; /* check if the given size is left to read in a given parse buffer (starting with 1) */ -#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +#define can_read(buffer, size) (((buffer)->offset + (size)) <= (buffer)->length) /* check if the buffer can be accessed at the given index (starting with 0) */ -#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define can_access_at_index(buffer, index) (((buffer)->offset + (index)) < (buffer)->length) #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) /* get a pointer to the buffer at the position */ #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) @@ -319,21 +403,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu } item->valuedouble = number; - - /* use saturation in case of overflow */ - if (number >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (number <= INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)number; - } - + item->valueint = double_to_saturated_integer(number); item->type = cJSON_Number; input_buffer->offset += (size_t)(after_end - number_c_string); @@ -343,18 +413,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { - if (number >= INT_MAX) - { - object->valueint = INT_MAX; - } - else if (number <= INT_MIN) - { - object->valueint = INT_MIN; - } - else - { - object->valueint = (int)number; - } + object->valueint = double_to_saturated_integer(number); return object->valuedouble = number; } @@ -366,8 +425,7 @@ typedef struct size_t offset; size_t depth; /* current nesting depth (for formatted printing) */ cJSON_bool noalloc; - cJSON_bool format; /* is this print a formatted print */ - internal_hooks hooks; + internal_context context; } printbuffer; /* realloc printbuffer if necessary to have at least "needed" bytes more */ @@ -421,13 +479,13 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) newsize = needed * 2; } - if (p->hooks.reallocate != NULL) + if (p->context.allocators.reallocate != NULL) { /* reallocate with realloc if available */ - newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + newbuffer = (unsigned char*)reallocate(&p->context, p->buffer, newsize); if (newbuffer == NULL) { - p->hooks.deallocate(p->buffer); + deallocate(&p->context, p->buffer); p->length = 0; p->buffer = NULL; @@ -437,10 +495,10 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) else { /* otherwise reallocate manually */ - newbuffer = (unsigned char*)p->hooks.allocate(newsize); + newbuffer = (unsigned char*)allocate(&p->context, newsize); if (!newbuffer) { - p->hooks.deallocate(p->buffer); + deallocate(&p->context, p->buffer); p->length = 0; p->buffer = NULL; @@ -450,7 +508,7 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) { memcpy(newbuffer, p->buffer, p->offset + 1); } - p->hooks.deallocate(p->buffer); + deallocate(&p->context, p->buffer); } p->length = newsize; p->buffer = newbuffer; @@ -471,11 +529,15 @@ static void update_offset(printbuffer * const buffer) buffer->offset += strlen((const char*)buffer_pointer); } +#define is_nan(number) ((number) != (number)) +#define is_infinity(number) (!is_nan(number) && ((number) * 0) != 0) + /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { unsigned char *output_pointer = NULL; - double d = item->valuedouble; + double number = item->valuedouble; + int integer = double_to_saturated_integer(number); int length = 0; size_t i = 0; unsigned char number_buffer[26]; /* temporary buffer to print the number into */ @@ -487,21 +549,24 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out return false; } - /* This checks for NaN and Infinity */ - if ((d * 0) != 0) + if (is_nan(number) || is_infinity(number)) { length = sprintf((char*)number_buffer, "null"); } + else if (number == integer) /* avoid overhead for integers */ + { + length = sprintf((char*)number_buffer, "%d", integer); + } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = sprintf((char*)number_buffer, "%1.15g", d); + length = sprintf((char*)number_buffer, "%1.15g", number); /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != number)) { /* If not, print with 17 decimal places of precision */ - length = sprintf((char*)number_buffer, "%1.17g", d); + length = sprintf((char*)number_buffer, "%1.17g", number); } } @@ -735,7 +800,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->hooks.allocate(allocation_length + sizeof("")); + output = (unsigned char*)allocate(&input_buffer->context, allocation_length + sizeof("")); if (output == NULL) { goto fail; /* allocation failure */ @@ -813,7 +878,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu fail: if (output != NULL) { - input_buffer->hooks.deallocate(output); + deallocate(&input_buffer->context, output); } if (input_pointer != NULL) @@ -973,6 +1038,7 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) buffer->offset++; } + /* step back if we went over the end */ if (buffer->offset == buffer->length) { buffer->offset--; @@ -998,39 +1064,38 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) } /* Parse an object - create a new root, and populate. */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +static cJSON *parse(const char * const json, const size_t length, internal_context * const context) { - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; cJSON *item = NULL; - /* reset error position */ + /* reset global error position */ global_error.json = NULL; global_error.position = 0; - if (value == NULL) + if (json == NULL) { goto fail; } - buffer.content = (const unsigned char*)value; - buffer.length = strlen((const char*)value) + sizeof(""); + buffer.content = (const unsigned char*)json; + buffer.length = length; buffer.offset = 0; - buffer.hooks = global_hooks; + buffer.context = *context; - item = cJSON_New_Item(&global_hooks); - if (item == NULL) /* memory fail */ + item = create_item(context); + if (item == NULL) { goto fail; } if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { - /* parse failure. ep is set. */ + /* parse failure. error position is set. */ goto fail; } - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) + if (!context->allow_data_after_json) { buffer_skip_whitespace(&buffer); if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') @@ -1038,23 +1103,20 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return goto fail; } } - if (return_parse_end) - { - *return_parse_end = (const char*)buffer_at_offset(&buffer); - } + + context->end_position = buffer.offset; return item; fail: if (item != NULL) { - cJSON_Delete(item); + delete_item(item, context); } - if (value != NULL) { error local_error; - local_error.json = (const unsigned char*)value; + local_error.json = (const unsigned char*)json; local_error.position = 0; if (buffer.offset < buffer.length) @@ -1066,38 +1128,59 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return local_error.position = buffer.length - 1; } - if (return_parse_end != NULL) - { - *return_parse_end = (const char*)local_error.json + local_error.position; - } - + context->end_position = local_error.position; global_error = local_error; } return NULL; } +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *json, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + internal_context context = global_context; + cJSON *item = NULL; + + if (json == NULL) + { + return NULL; + } + + context.allow_data_after_json = !require_null_terminated; + item = parse(json, strlen((const char*)json) + sizeof(""), &context); + + if (return_parse_end != NULL) + { + *return_parse_end = json + context.end_position; + } + + return item; +} + /* Default options for cJSON_Parse */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *json) { - return cJSON_ParseWithOpts(value, 0, 0); + if (json == NULL) + { + return NULL; + } + + return parse(json, strlen((const char*)json) + sizeof(""), &global_context); } -#define cjson_min(a, b) ((a < b) ? a : b) +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) -static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +static unsigned char *print(const cJSON * const item, const internal_context * const context) { - static const size_t default_buffer_size = 256; printbuffer buffer[1]; unsigned char *printed = NULL; memset(buffer, 0, sizeof(buffer)); /* create buffer */ - buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); - buffer->length = default_buffer_size; - buffer->format = format; - buffer->hooks = *hooks; + buffer->buffer = (unsigned char*)allocate(context, context->buffer_size); + buffer->length = context->buffer_size; + buffer->context = *context; if (buffer->buffer == NULL) { goto fail; @@ -1110,10 +1193,12 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i } update_offset(buffer); + /* 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 (hooks->reallocate != NULL) + if (context->allocators.reallocate != NULL) { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + printed = (unsigned char*)reallocate(context, buffer->buffer, buffer->offset + 1); buffer->buffer = NULL; if (printed == NULL) { goto fail; @@ -1121,7 +1206,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i } else /* otherwise copy the JSON over to a new buffer */ { - printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + printed = (unsigned char*)allocate(context, buffer->offset + 1); if (printed == NULL) { goto fail; @@ -1130,7 +1215,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i printed[buffer->offset] = '\0'; /* just to be sure */ /* free the buffer */ - hooks->deallocate(buffer->buffer); + deallocate(context, buffer->buffer); } return printed; @@ -1138,12 +1223,12 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i fail: if (buffer->buffer != NULL) { - hooks->deallocate(buffer->buffer); + deallocate(context, buffer->buffer); } if (printed != NULL) { - hooks->deallocate(printed); + deallocate(context, printed); } return NULL; @@ -1152,59 +1237,46 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* Render a cJSON item/entity/structure to text. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { - return (char*)print(item, true, &global_hooks); + return (char*)print(item, &global_context); } CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { - return (char*)print(item, false, &global_hooks); + internal_context context = global_context; + context.format = false; + return (char*)print(item, &context); } -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool format) { - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + internal_context context = global_context; if (prebuffer < 0) { return NULL; } - p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); - if (!p.buffer) - { - return NULL; - } - - p.length = (size_t)prebuffer; - p.offset = 0; - p.noalloc = false; - p.format = fmt; - p.hooks = global_hooks; - - if (!print_value(item, &p)) - { - global_hooks.deallocate(p.buffer); - return NULL; - } + context.buffer_size = (size_t)prebuffer; + context.format = format; - return (char*)p.buffer; + return (char*)print(item, &context); } -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { - printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer p = { 0, 0, 0, 0, 0, default_context }; - if ((len < 0) || (buf == NULL)) + if ((length < 0) || (buffer == NULL)) { return false; } - p.buffer = (unsigned char*)buf; - p.length = (size_t)len; + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; p.offset = 0; p.noalloc = true; - p.format = fmt; - p.hooks = global_hooks; + p.context = global_context; + p.context.format = format; return print_value(item, &p); } @@ -1217,51 +1289,75 @@ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buf return false; /* no input */ } - /* parse the different types of values */ - /* null */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) - { - item->type = cJSON_NULL; - input_buffer->offset += 4; - return true; - } - /* false */ - if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) - { - item->type = cJSON_False; - input_buffer->offset += 5; - return true; - } - /* true */ - if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) - { - item->type = cJSON_True; - item->valueint = 1; - input_buffer->offset += 4; - return true; - } - /* string */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) - { - return parse_string(item, input_buffer); - } - /* number */ - if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) - { - return parse_number(item, input_buffer); - } - /* array */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) - { - return parse_array(item, input_buffer); - } - /* object */ - if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + if (!can_read(input_buffer, 1)) { - return parse_object(item, input_buffer); + return false; } - return false; + /* parse the different types of values */ + switch (buffer_at_offset(input_buffer)[0]) + { + /* number */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parse_number(item, input_buffer); + + /* string */ + case '\"': + return parse_string(item, input_buffer); + + /* array */ + case '[': + return parse_array(item, input_buffer); + + /* object */ + case '{': + return parse_object(item, input_buffer); + + /* null */ + case 'n': + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + return false; + + /* true */ + case 't': + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + return false; + + /* false */ + case 'f': + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + return false; + + + default: + return false; + } } /* Render a value to text. */ @@ -1377,7 +1473,7 @@ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buf do { /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + cJSON *new_item = create_item(&(input_buffer->context)); if (new_item == NULL) { goto fail; /* allocation failure */ @@ -1426,7 +1522,7 @@ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buf fail: if (head != NULL) { - cJSON_Delete(head); + delete_item(head, &input_buffer->context); } return false; @@ -1465,14 +1561,14 @@ static cJSON_bool print_array(const cJSON * const item, printbuffer * const outp update_offset(output_buffer); if (current_element->next) { - length = (size_t) (output_buffer->format ? 2 : 1); + length = (size_t) (output_buffer->context.format ? 2 : 1); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { return false; } *output_pointer++ = ','; - if(output_buffer->format) + if(output_buffer->context.format) { *output_pointer++ = ' '; } @@ -1531,7 +1627,7 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu do { /* allocate next item */ - cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + cJSON *new_item = create_item(&(input_buffer->context)); if (new_item == NULL) { goto fail; /* allocation failure */ @@ -1597,7 +1693,7 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu fail: if (head != NULL) { - cJSON_Delete(head); + delete_item(head, &input_buffer->context); } return false; @@ -1616,7 +1712,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out } /* Compose the output: */ - length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + length = (size_t) (output_buffer->context.format ? 2 : 1); /* fmt: {\n */ output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { @@ -1625,7 +1721,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out *output_pointer++ = '{'; output_buffer->depth++; - if (output_buffer->format) + if (output_buffer->context.format) { *output_pointer++ = '\n'; } @@ -1633,7 +1729,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out while (current_item) { - if (output_buffer->format) + if (output_buffer->context.format) { size_t i; output_pointer = ensure(output_buffer, output_buffer->depth); @@ -1655,14 +1751,14 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out } update_offset(output_buffer); - length = (size_t) (output_buffer->format ? 2 : 1); + length = (size_t) (output_buffer->context.format ? 2 : 1); output_pointer = ensure(output_buffer, length); if (output_pointer == NULL) { return false; } *output_pointer++ = ':'; - if (output_buffer->format) + if (output_buffer->context.format) { *output_pointer++ = '\t'; } @@ -1676,7 +1772,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out update_offset(output_buffer); /* print comma if not last */ - length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + length = (size_t) ((output_buffer->context.format ? 1 : 0) + (current_item->next ? 1 : 0)); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { @@ -1687,7 +1783,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out *output_pointer++ = ','; } - if (output_buffer->format) + if (output_buffer->context.format) { *output_pointer++ = '\n'; } @@ -1697,12 +1793,12 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out current_item = current_item->next; } - output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + output_pointer = ensure(output_buffer, output_buffer->context.format ? (output_buffer->depth + 1) : 2); if (output_pointer == NULL) { return false; } - if (output_buffer->format) + if (output_buffer->context.format) { size_t i; for (i = 0; i < (output_buffer->depth - 1); i++) @@ -1717,8 +1813,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out return true; } -/* Get Array size/item / object item. */ -CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +static size_t get_array_size(const cJSON * const array) { cJSON *child = NULL; size_t size = 0; @@ -1730,13 +1825,25 @@ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) child = array->child; - while(child != NULL) + while (child != NULL) { size++; child = child->next; } - /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + return size; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + size_t size = get_array_size(array); + + if (size > INT_MAX) + { + /* This is incorrect but can't be fixed without breaking the API */ + return INT_MAX; + } return (int)size; } @@ -1770,7 +1877,7 @@ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) return get_array_item(array, (size_t)index); } -static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +static cJSON *get_object_item(const cJSON * const object, const char * const name, const internal_context * const context) { cJSON *current_element = NULL; @@ -1780,7 +1887,7 @@ static cJSON *get_object_item(const cJSON * const object, const char * const nam } current_element = object->child; - if (case_sensitive) + if (context->case_sensitive) { while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) { @@ -1800,12 +1907,16 @@ static cJSON *get_object_item(const cJSON * const object, const char * const nam CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) { - return get_object_item(object, string, false); + internal_context context = default_context; + context.case_sensitive = false; + return get_object_item(object, string, &context); } CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) { - return get_object_item(object, string, true); + internal_context context = default_context; + context.case_sensitive = true; + return get_object_item(object, string, &context); } CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) @@ -1821,7 +1932,7 @@ static void suffix_object(cJSON *prev, cJSON *item) } /* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +static cJSON *create_reference(const cJSON *item, const internal_context * const context) { cJSON *reference = NULL; if (item == NULL) @@ -1829,7 +1940,7 @@ static cJSON *create_reference(const cJSON *item, const internal_hooks * const h return NULL; } - reference = cJSON_New_Item(hooks); + reference = create_item(context); if (reference == NULL) { return NULL; @@ -1893,7 +2004,7 @@ static void* cast_away_const(const void* string) #endif -static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_context * const context, const cJSON_bool constant_key) { char *new_key = NULL; int new_type = cJSON_Invalid; @@ -1910,7 +2021,7 @@ static cJSON_bool add_item_to_object(cJSON * const object, const char * const st } else { - new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + new_key = (char*)custom_strdup((const unsigned char*)string, context); if (new_key == NULL) { return false; @@ -1921,7 +2032,7 @@ static cJSON_bool add_item_to_object(cJSON * const object, const char * const st if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { - hooks->deallocate(item->string); + deallocate(context, item->string); } item->string = new_key; @@ -1932,13 +2043,13 @@ static cJSON_bool add_item_to_object(cJSON * const object, const char * const st CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { - add_item_to_object(object, string, item, &global_hooks, false); + add_item_to_object(object, string, item, &global_context, false); } /* Add an item to an object with constant string as key */ CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { - add_item_to_object(object, string, item, &global_hooks, true); + add_item_to_object(object, string, item, &global_context, true); } CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) @@ -1948,7 +2059,7 @@ CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) return; } - add_item_to_array(array, create_reference(item, &global_hooks)); + add_item_to_array(array, create_reference(item, &global_context)); } CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) @@ -1958,114 +2069,114 @@ CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *str return; } - add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); + add_item_to_object(object, string, create_reference(item, &global_context), &global_context, false); } CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) { cJSON *null = cJSON_CreateNull(); - if (add_item_to_object(object, name, null, &global_hooks, false)) + if (add_item_to_object(object, name, null, &global_context, false)) { return null; } - cJSON_Delete(null); + delete_item(null, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) { cJSON *true_item = cJSON_CreateTrue(); - if (add_item_to_object(object, name, true_item, &global_hooks, false)) + if (add_item_to_object(object, name, true_item, &global_context, false)) { return true_item; } - cJSON_Delete(true_item); + delete_item(true_item, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) { cJSON *false_item = cJSON_CreateFalse(); - if (add_item_to_object(object, name, false_item, &global_hooks, false)) + if (add_item_to_object(object, name, false_item, &global_context, false)) { return false_item; } - cJSON_Delete(false_item); + delete_item(false_item, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) { cJSON *bool_item = cJSON_CreateBool(boolean); - if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + if (add_item_to_object(object, name, bool_item, &global_context, false)) { return bool_item; } - cJSON_Delete(bool_item); + delete_item(bool_item, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) { cJSON *number_item = cJSON_CreateNumber(number); - if (add_item_to_object(object, name, number_item, &global_hooks, false)) + if (add_item_to_object(object, name, number_item, &global_context, false)) { return number_item; } - cJSON_Delete(number_item); + delete_item(number_item, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) { cJSON *string_item = cJSON_CreateString(string); - if (add_item_to_object(object, name, string_item, &global_hooks, false)) + if (add_item_to_object(object, name, string_item, &global_context, false)) { return string_item; } - cJSON_Delete(string_item); + delete_item(string_item, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) { cJSON *raw_item = cJSON_CreateRaw(raw); - if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + if (add_item_to_object(object, name, raw_item, &global_context, false)) { return raw_item; } - cJSON_Delete(raw_item); + delete_item(raw_item, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) { cJSON *object_item = cJSON_CreateObject(); - if (add_item_to_object(object, name, object_item, &global_hooks, false)) + if (add_item_to_object(object, name, object_item, &global_context, false)) { return object_item; } - cJSON_Delete(object_item); + delete_item(object_item, &global_context); return NULL; } CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) { cJSON *array = cJSON_CreateArray(); - if (add_item_to_object(object, name, array, &global_hooks, false)) + if (add_item_to_object(object, name, array, &global_context, false)) { return array; } - cJSON_Delete(array); + delete_item(array, &global_context); return NULL; } @@ -2111,7 +2222,7 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) { - cJSON_Delete(cJSON_DetachItemFromArray(array, which)); + delete_item(cJSON_DetachItemFromArray(array, which), &global_context); } CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) @@ -2130,12 +2241,12 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, con CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) { - cJSON_Delete(cJSON_DetachItemFromObject(object, string)); + delete_item(cJSON_DetachItemFromObject(object, string), &global_context); } CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) { - cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); + delete_item(cJSON_DetachItemFromObjectCaseSensitive(object, string), &global_context); } /* Replace array/object items with new ones. */ @@ -2198,7 +2309,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON item->next = NULL; item->prev = NULL; - cJSON_Delete(item); + delete_item(item, &global_context); return true; } @@ -2213,7 +2324,7 @@ CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newi cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } -static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, const internal_context * const context) { if ((replacement == NULL) || (string == NULL)) { @@ -2225,28 +2336,32 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO { cJSON_free(replacement->string); } - replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->string = (char*)custom_strdup((const unsigned char*)string, &global_context); replacement->type &= ~cJSON_StringIsConst; - cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, context), replacement); return true; } CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { - replace_item_in_object(object, string, newitem, false); + internal_context context = global_context; + context.case_sensitive = false; + replace_item_in_object(object, string, newitem, &context); } CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { - replace_item_in_object(object, string, newitem, true); + internal_context context = global_context; + context.case_sensitive = true; + replace_item_in_object(object, string, newitem, &context); } /* Create basic types: */ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { item->type = cJSON_NULL; @@ -2257,7 +2372,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { item->type = cJSON_True; @@ -2268,7 +2383,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { item->type = cJSON_False; @@ -2277,12 +2392,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { - item->type = b ? cJSON_True : cJSON_False; + item->type = boolean ? cJSON_True : cJSON_False; } return item; @@ -2290,25 +2405,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { item->type = cJSON_Number; item->valuedouble = num; - - /* use saturation in case of overflow */ - if (num >= INT_MAX) - { - item->valueint = INT_MAX; - } - else if (num <= INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)num; - } + item->valueint = double_to_saturated_integer(num); } return item; @@ -2316,14 +2418,14 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { item->type = cJSON_String; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + item->valuestring = (char*)custom_strdup((const unsigned char*)string, &global_context); if(!item->valuestring) { - cJSON_Delete(item); + delete_item(item, &global_context); return NULL; } } @@ -2333,7 +2435,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if (item != NULL) { item->type = cJSON_String | cJSON_IsReference; @@ -2345,7 +2447,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if (item != NULL) { item->type = cJSON_Object | cJSON_IsReference; item->child = (cJSON*)cast_away_const(child); @@ -2355,7 +2457,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) } CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if (item != NULL) { item->type = cJSON_Array | cJSON_IsReference; item->child = (cJSON*)cast_away_const(child); @@ -2366,14 +2468,14 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { item->type = cJSON_Raw; - item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + item->valuestring = (char*)custom_strdup((const unsigned char*)raw, &global_context); if(!item->valuestring) { - cJSON_Delete(item); + delete_item(item, &global_context); return NULL; } } @@ -2383,7 +2485,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if(item) { item->type=cJSON_Array; @@ -2394,7 +2496,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) { - cJSON *item = cJSON_New_Item(&global_hooks); + cJSON *item = create_item(&global_context); if (item) { item->type = cJSON_Object; @@ -2422,7 +2524,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) n = cJSON_CreateNumber(numbers[i]); if (!n) { - cJSON_Delete(a); + delete_item(a, &global_context); return NULL; } if(!i) @@ -2458,7 +2560,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) n = cJSON_CreateNumber((double)numbers[i]); if(!n) { - cJSON_Delete(a); + delete_item(a, &global_context); return NULL; } if(!i) @@ -2494,7 +2596,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) n = cJSON_CreateNumber(numbers[i]); if(!n) { - cJSON_Delete(a); + delete_item(a, &global_context); return NULL; } if(!i) @@ -2530,7 +2632,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) n = cJSON_CreateString(strings[i]); if(!n) { - cJSON_Delete(a); + delete_item(a, &global_context); return NULL; } if(!i) @@ -2547,56 +2649,60 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) return a; } -/* Duplication */ -CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +static cJSON *duplicate_json(const cJSON *item, const internal_context * const context) { cJSON *newitem = NULL; cJSON *child = NULL; cJSON *next = NULL; cJSON *newchild = NULL; - /* Bail on bad ptr */ - if (!item) + if (item == NULL) { goto fail; } - /* Create new item */ - newitem = cJSON_New_Item(&global_hooks); - if (!newitem) + + newitem = create_item(context); + if (newitem == NULL) { goto fail; } + /* Copy over all vars */ newitem->type = item->type & (~cJSON_IsReference); newitem->valueint = item->valueint; newitem->valuedouble = item->valuedouble; - if (item->valuestring) + + if (item->valuestring != NULL) { - newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); - if (!newitem->valuestring) + newitem->valuestring = (char*)custom_strdup((unsigned char*)item->valuestring, context); + if (newitem->valuestring == NULL) { goto fail; } } - if (item->string) + + if (item->string != NULL) { - newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)custom_strdup((unsigned char*)item->string, context); if (!newitem->string) { goto fail; } } + /* If non-recursive, then we're done! */ - if (!recurse) + if (!context->duplicate_recursive) { return newitem; } + /* Walk the ->next chain for the child. */ child = item->child; while (child != NULL) { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) + /* Each item in the ->next chain */ + newchild = duplicate_json(child, context); + if (newchild == NULL) { goto fail; } @@ -2621,12 +2727,19 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) fail: if (newitem != NULL) { - cJSON_Delete(newitem); + delete_item(newitem, context); } return NULL; } +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + internal_context context = global_context; + context.duplicate_recursive = recurse; + return duplicate_json(item, &context); +} + CJSON_PUBLIC(void) cJSON_Minify(char *json) { unsigned char *into = (unsigned char*)json; @@ -2797,7 +2910,141 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) return (item->type & 0xFF) == cJSON_Raw; } -CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +CJSON_PUBLIC(cJSON_Context) cJSON_DuplicateContext(const cJSON_Context context, const cJSON_Allocators * const allocators, void *allocator_userdata) +{ + internal_context *duplicate = NULL; + const cJSON_Allocators *local_allocators = &global_default_context.allocators; + + if (allocators != NULL) + { + if ((allocators->allocate == NULL) || (allocators->deallocate == NULL)) + { + return NULL; + } + + local_allocators = allocators; + } + + duplicate = (internal_context*)local_allocators->allocate(sizeof(internal_context), allocator_userdata); + if (duplicate == NULL) + { + return NULL; + } + + memcpy(duplicate, context, sizeof(internal_context)); + + return duplicate; +} + +CJSON_PUBLIC(cJSON_Context) cJSON_CreateContext(const cJSON_Allocators * const allocators, void *allocator_userdata) +{ + return cJSON_DuplicateContext((cJSON_Context)&global_default_context, allocators, allocator_userdata); +} + +CJSON_PUBLIC(cJSON_Context) cJSON_SetAllocators(cJSON_Context context, const cJSON_Allocators allocators) +{ + if ((context == NULL) || (allocators.allocate == NULL) || (allocators.deallocate == NULL)) + { + return NULL; + } + + ((internal_context*)context)->allocators = allocators; + ((internal_context*)context)->userdata = NULL; + + return context; +} + +/* Change the allocator userdata attached to a cJSON_Context */ +CJSON_PUBLIC(cJSON_Context) cJSON_SetUserdata(cJSON_Context context, void *userdata) +{ + if (context == NULL) + { + return NULL; + } + + ((internal_context*)context)->userdata = userdata; + return context; +} + +CJSON_PUBLIC(size_t) cJSON_GetParseEnd(cJSON_Context context) +{ + if (context == NULL) + { + return 0; + } + + return ((internal_context*)context)->end_position; +} + +CJSON_PUBLIC(cJSON_Context) cJSON_SetPrebufferSize(cJSON_Context context, const size_t buffer_size) +{ + if ((context == NULL) || (buffer_size == 0)) + { + return NULL; + } + + ((internal_context*)context)->buffer_size = buffer_size; + return context; +} + +CJSON_PUBLIC(cJSON_Context) cJSON_SetFormat(cJSON_Context context, cJSON_Format format) +{ + if (context == NULL) + { + return NULL; + } + + switch (format) + { + case CJSON_FORMAT_MINIFIED: + ((internal_context*)context)->format = false; + break; + + case CJSON_FORMAT_DEFAULT: + ((internal_context*)context)->format = true; + break; + + default: + return NULL; + } + + return context; +} + +CJSON_PUBLIC(cJSON_Context) cJSON_MakeCaseSensitive(cJSON_Context context, cJSON_bool case_sensitive) +{ + if (context == NULL) + { + return NULL; + } + + ((internal_context*)context)->case_sensitive = case_sensitive; + return context; +} + +CJSON_PUBLIC(cJSON_Context) cJSON_AllowDataAfterJson(cJSON_Context context, cJSON_bool allow_data_after_json) +{ + if (context == NULL) + { + return NULL; + } + + ((internal_context*)context)->allow_data_after_json = allow_data_after_json; + return context; +} + +CJSON_PUBLIC(cJSON_Context) cJSON_MakeDuplicateRecursive(cJSON_Context context, cJSON_bool recursive) +{ + if (context == NULL) + { + return NULL; + } + + ((internal_context*)context)->duplicate_recursive = recursive; + return context; +} + +static cJSON_bool compare(const cJSON * const a, const cJSON * const b, const internal_context * const context) { if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) { @@ -2862,7 +3109,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons for (; (a_element != NULL) && (b_element != NULL);) { - if (!cJSON_Compare(a_element, b_element, case_sensitive)) + if (!compare(a_element, b_element, context)) { return false; } @@ -2883,32 +3130,24 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons { cJSON *a_element = NULL; cJSON *b_element = NULL; - cJSON_ArrayForEach(a_element, a) - { - /* TODO This has O(n^2) runtime, which is horrible! */ - b_element = get_object_item(b, a_element->string, case_sensitive); - if (b_element == NULL) - { - return false; - } + size_t a_size = get_array_size(a); + size_t b_size = get_array_size(b); - if (!cJSON_Compare(a_element, b_element, case_sensitive)) - { - return false; - } + if (a_size != b_size) + { + return false; } - /* doing this twice, once on a and b to prevent true comparison if a subset of b - * TODO: Do this the proper way, this is just a fix for now */ - cJSON_ArrayForEach(b_element, b) + cJSON_ArrayForEach(a_element, a) { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, context); + if (b_element == NULL) { return false; } - if (!cJSON_Compare(b_element, a_element, case_sensitive)) + if (!compare(a_element, b_element, context)) { return false; } @@ -2922,12 +3161,19 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons } } +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + internal_context context = global_context; + context.case_sensitive = case_sensitive; + return compare(a, b, &context); +} + CJSON_PUBLIC(void *) cJSON_malloc(size_t size) { - return global_hooks.allocate(size); + return global_allocators.malloc_fn(size); } CJSON_PUBLIC(void) cJSON_free(void *object) { - global_hooks.deallocate(object); + global_allocators.free_fn(object); } diff --git a/cJSON.h b/cJSON.h index a9c68fa2..094785d7 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)) @@ -132,27 +140,64 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ /* returns the version of cJSON as a string */ CJSON_PUBLIC(const char*) cJSON_Version(void); -/* Supply malloc, realloc and free functions to cJSON */ +typedef void* cJSON_Context; +/* + * Create a context object that can be passed to several functions + * to configure their behavior and/or take their output. It will be set to the default values + * initially, they can be changed later using the builder pattern by passing it to functions + * that change one setting. + * + * A cJSON_Context object is dynamically allocated and you are responsible to free it + * after use. + * + * If allocators is a NULL pointer, malloc and free are used. + * + * allocator_userdata can be used to pass custom data to your allocator (e.g. for pool allocators). + * */ +CJSON_PUBLIC(cJSON_Context) cJSON_CreateContext(const cJSON_Allocators * const allocators, void *allocator_userdata); +/* Create a copy of an existing context */ +CJSON_PUBLIC(cJSON_Context) cJSON_DuplicateContext(const cJSON_Context, const cJSON_Allocators * const allocators, void *allocator_userdata); + +/* The following functions work on a context in order to set and retrieve data: */ +/* Change the allocators of a cJSON_Context and reset the userdata */ +CJSON_PUBLIC(cJSON_Context) cJSON_SetAllocators(cJSON_Context context, const cJSON_Allocators allocators); +/* Change the allocator userdata attached to a cJSON_Context */ +CJSON_PUBLIC(cJSON_Context) cJSON_SetUserdata(cJSON_Context context, void *userdata); +/* Get the position relative to the JSON where the parser stopped, return 0 if invalid. */ +CJSON_PUBLIC(size_t) cJSON_GetParseEnd(cJSON_Context context); +/* Set how many bytes should be initially allocated for printing */ +CJSON_PUBLIC(cJSON_Context) cJSON_SetPrebufferSize(cJSON_Context context, const size_t buffer_size); +typedef enum { CJSON_FORMAT_MINIFIED = 0, CJSON_FORMAT_DEFAULT = 1 } cJSON_Format; +/* Change the format for printing */ +CJSON_PUBLIC(cJSON_Context) cJSON_SetFormat(cJSON_Context context, cJSON_Format format); +/* Change the case sensitivity */ +CJSON_PUBLIC(cJSON_Context) cJSON_MakeCaseSensitive(cJSON_Context context, cJSON_bool case_sensitive); +/* Change if data is allowed after the JSON */ +CJSON_PUBLIC(cJSON_Context) cJSON_AllowDataAfterJson(cJSON_Context context, cJSON_bool allow_data_after_json); +/* Change if cJSON_Duplicate copies recursively */ +CJSON_PUBLIC(cJSON_Context) cJSON_MakeDuplicateRecursive(cJSON_Context context, cJSON_bool recursive); + +/* Supply malloc and free functions to cJSON globally */ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ -CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *json); /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *json, const char **return_parse_end, cJSON_bool require_null_terminated); /* Render a cJSON entity to text for transfer/storage. */ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. */ CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ -CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool format); /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 79672927..f7d0180a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -57,6 +57,7 @@ if(ENABLE_CJSON_TEST) compare_tests cjson_add readme_examples + context_tests ) option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.") diff --git a/tests/common.h b/tests/common.h index 4db6bf8c..0853fb01 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_hooks.deallocate(item->valuestring); + global_context.allocators.deallocate(item->valuestring, global_context.userdata); } if ((item->string != NULL) && !(item->type & cJSON_StringIsConst)) { - global_hooks.deallocate(item->string); + global_context.allocators.deallocate(item->string, global_context.userdata); } memset(item, 0, sizeof(cJSON)); diff --git a/tests/context_tests.c b/tests/context_tests.c new file mode 100644 index 00000000..cbc3d6a5 --- /dev/null +++ b/tests/context_tests.c @@ -0,0 +1,278 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void create_context_should_create_a_context(void) +{ + internal_context *context = NULL; + + context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + TEST_ASSERT_EQUAL_MESSAGE(context->buffer_size, 256, "buffer_size has an incorrect value."); + TEST_ASSERT_TRUE_MESSAGE(context->format, "format has an incorrect value."); + TEST_ASSERT_TRUE_MESSAGE(context->case_sensitive, "case_sensitive has an incorrect value."); + TEST_ASSERT_TRUE_MESSAGE(context->allow_data_after_json, "allow_data_after_json has an incorrect value."); + TEST_ASSERT_NULL_MESSAGE(context->userdata, "Userdata should be NULL"); + TEST_ASSERT_TRUE_MESSAGE(context->duplicate_recursive, "Duplicating is not recursive."); + TEST_ASSERT_TRUE_MESSAGE(malloc_wrapper == context->allocators.allocate, "Wrong malloc."); + TEST_ASSERT_TRUE_MESSAGE(realloc_wrapper == context->allocators.reallocate, "Wrong realloc."); + TEST_ASSERT_TRUE_MESSAGE(free_wrapper == context->allocators.deallocate, "Wrong free."); + + free(context); +} + +static void* custom_allocator(size_t size, void *userdata) +{ + *((size_t*)userdata) = size; + return malloc(size); +} +static void custom_deallocator(void *pointer, void *userdata) +{ + *((size_t*)userdata) = (size_t)pointer; + free(pointer); +} + +static void create_context_should_take_custom_allocators(void) +{ + internal_context *context = NULL; + cJSON_Allocators allocators = {custom_allocator, custom_deallocator, NULL}; + size_t userdata = 0; + + context = (internal_context*)cJSON_CreateContext(&allocators, &userdata); + TEST_ASSERT_NOT_NULL(context); + TEST_ASSERT_EQUAL_MESSAGE(userdata, sizeof(internal_context), "custom allocator wasn't run properly."); + TEST_ASSERT_TRUE_MESSAGE(global_default_context.allocators.allocate == context->allocators.allocate, "Wrong allocator."); + TEST_ASSERT_TRUE_MESSAGE(global_default_context.allocators.deallocate == context->allocators.deallocate, "Wrong deallocator."); + TEST_ASSERT_TRUE_MESSAGE(global_default_context.allocators.reallocate == context->allocators.reallocate, "Wrong reallocator."); + + custom_deallocator(context, &userdata); +} + +static void create_context_should_not_take_incomplete_allocators(void) +{ + cJSON_Allocators allocators1 = {custom_allocator, NULL, NULL}; + cJSON_Allocators allocators2 = {NULL, custom_deallocator, NULL}; + size_t userdata = 0; + + TEST_ASSERT_NULL(cJSON_CreateContext(&allocators1, &userdata)); + TEST_ASSERT_NULL(cJSON_CreateContext(&allocators2, &userdata)); +} + +static void duplicate_context_should_duplicate_a_context(void) +{ + internal_context *context = NULL; + + context = (internal_context*)cJSON_DuplicateContext(&global_context, NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + TEST_ASSERT_EQUAL_MEMORY(&global_context, context, sizeof(internal_context)); + + free(context); +} + +static void duplicate_context_should_take_custom_allocators(void) +{ + internal_context *context = NULL; + cJSON_Allocators allocators = {custom_allocator, custom_deallocator, NULL}; + size_t userdata = 0; + + context = (internal_context*)cJSON_DuplicateContext(&global_context, &allocators, &userdata); + TEST_ASSERT_NOT_NULL(context); + TEST_ASSERT_EQUAL_MESSAGE(userdata, sizeof(internal_context), "custom allocator wasn't run properly"); + + TEST_ASSERT_EQUAL_MEMORY(&global_context, context, sizeof(internal_context)); + free(context); +} + +static void duplicate_context_should_not_take_incomplete_allocators(void) +{ + cJSON_Allocators allocators1 = {custom_allocator, NULL, NULL}; + cJSON_Allocators allocators2 = {NULL, custom_deallocator, NULL}; + size_t userdata = 0; + + TEST_ASSERT_NULL(cJSON_DuplicateContext(&global_context, &allocators1, &userdata)); + TEST_ASSERT_NULL(cJSON_DuplicateContext(&global_context, &allocators2, &userdata)); +} + +static void set_allocators_should_set_allocators(void) +{ + internal_context *context = NULL; + cJSON_Allocators allocators = {custom_allocator, custom_deallocator, NULL}; + size_t userdata = 0; + + context = (internal_context*)cJSON_CreateContext(&allocators, &userdata); + TEST_ASSERT_NOT_NULL(context); + + context = (internal_context*)cJSON_SetAllocators(context, allocators); + TEST_ASSERT_NOT_NULL(context); + TEST_ASSERT_TRUE_MESSAGE(custom_allocator == context->allocators.allocate, "Wrong allocator."); + TEST_ASSERT_TRUE_MESSAGE(custom_deallocator == context->allocators.deallocate, "Wrong deallocator."); + TEST_ASSERT_NULL_MESSAGE(context->allocators.reallocate, "Reallocator is not null"); + + custom_deallocator(context, &userdata); +} + +static void set_allocators_should_not_set_incomplete_allocators(void) +{ + internal_context *context = NULL; + cJSON_Allocators allocators1 = {custom_allocator, NULL, NULL}; + cJSON_Allocators allocators2 = {NULL, custom_deallocator, NULL}; + + context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + TEST_ASSERT_NULL(cJSON_SetAllocators(context, allocators1)); + TEST_ASSERT_NULL(cJSON_SetAllocators(context, allocators2)); + + free(context); +} + +static void set_userdata_should_set_userdata(void) +{ + internal_context *context = NULL; + size_t userdata = 0; + context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + context = (internal_context*)cJSON_SetUserdata(context, &userdata); + TEST_ASSERT_TRUE_MESSAGE(context->userdata == &userdata, "Userdata is incorrect."); + + free(context); +} + +static void get_parse_end_should_get_the_parse_end(void) +{ + internal_context context = global_default_context; + context.end_position = 42; + + TEST_ASSERT_EQUAL_MESSAGE(cJSON_GetParseEnd(&context), 42, "Failed to get parse end."); +} + +static void set_prebuffer_size_should_set_buffer_size(void) +{ + internal_context *context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + context = (internal_context*)cJSON_SetPrebufferSize(context, 1024); + TEST_ASSERT_NOT_NULL(context); + + TEST_ASSERT_EQUAL_MESSAGE(context->buffer_size, 1024, "Didn't set the buffer size correctly."); + + free(context); +} + +static void set_prebuffer_size_should_not_allow_empty_sizes(void) +{ + internal_context *context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + TEST_ASSERT_NULL(cJSON_SetPrebufferSize(context, 0)); + + free(context); +} + +static void set_format_should_set_format(void) +{ + internal_context *context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + context = (internal_context*)cJSON_SetFormat(context, CJSON_FORMAT_MINIFIED); + TEST_ASSERT_NOT_NULL(context); + TEST_ASSERT_FALSE_MESSAGE(context->format, "Failed to set CJSON_FORMAT_MINIFIED."); + + context = (internal_context*)cJSON_SetFormat(context, CJSON_FORMAT_DEFAULT); + TEST_ASSERT_NOT_NULL(context); + TEST_ASSERT_TRUE_MESSAGE(context->format, "Failed to set CJSON_FORMAT_DEFAULT."); + + TEST_ASSERT_NULL_MESSAGE(cJSON_SetFormat(context, (cJSON_Format)3), "Failed to detect invalid format."); + + free(context); +} + +static void make_case_sensitive_should_change_case_sensitivity(void) +{ + internal_context *context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + context = (internal_context*)cJSON_MakeCaseSensitive(context, false); + TEST_ASSERT_NOT_NULL(context); + + TEST_ASSERT_FALSE_MESSAGE(context->case_sensitive, "Didn't set the case sensitivity correctly."); + + free(context); +} + +static void allow_data_after_json_should_change_allow_data_after_json(void) +{ + internal_context *context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + context = (internal_context*)cJSON_AllowDataAfterJson(context, false); + TEST_ASSERT_NOT_NULL(context); + + TEST_ASSERT_FALSE_MESSAGE(context->allow_data_after_json, "Didn't set allow_data_after_json property correctly."); + + free(context); +} + +static void make_duplicate_recursive_should_make_duplicate_recursive(void) +{ + internal_context *context = (internal_context*)cJSON_CreateContext(NULL, NULL); + TEST_ASSERT_NOT_NULL(context); + + context = (internal_context*)cJSON_MakeDuplicateRecursive(context, false); + TEST_ASSERT_NOT_NULL(context); + TEST_ASSERT_FALSE_MESSAGE(context->duplicate_recursive, "Duplicating is not set correctly."); + + free(context); +} + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(create_context_should_create_a_context); + RUN_TEST(create_context_should_take_custom_allocators); + RUN_TEST(create_context_should_not_take_incomplete_allocators); + RUN_TEST(duplicate_context_should_duplicate_a_context); + RUN_TEST(duplicate_context_should_take_custom_allocators); + RUN_TEST(duplicate_context_should_not_take_incomplete_allocators); + RUN_TEST(set_allocators_should_set_allocators); + RUN_TEST(set_allocators_should_not_set_incomplete_allocators); + RUN_TEST(set_userdata_should_set_userdata); + RUN_TEST(get_parse_end_should_get_the_parse_end); + RUN_TEST(set_prebuffer_size_should_set_buffer_size); + RUN_TEST(set_prebuffer_size_should_not_allow_empty_sizes); + RUN_TEST(set_format_should_set_format); + RUN_TEST(make_case_sensitive_should_change_case_sensitivity); + RUN_TEST(allow_data_after_json_should_change_allow_data_after_json); + RUN_TEST(make_duplicate_recursive_should_make_duplicate_recursive); + + return UNITY_END(); +} diff --git a/tests/misc_tests.c b/tests/misc_tests.c index a0b4f7eb..8cb4f882 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -10,8 +10,7 @@ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER @@ -410,16 +409,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, false, {&malloc, &free, &failing_realloc}}; + printbuffer buffer = {NULL, 10, 0, 0, false, {256, 0, NULL, {malloc_wrapper, free_wrapper, failing_realloc}, false, true, true, true} }; + buffer.context.userdata = &buffer; buffer.buffer = (unsigned char*)malloc(100); TEST_ASSERT_NOT_NULL(buffer.buffer); @@ -429,10 +430,10 @@ static void ensure_should_fail_on_failed_realloc(void) static void skip_utf8_bom_should_skip_bom(void) { const unsigned char string[] = "\xEF\xBB\xBF{}"; - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = string; buffer.length = sizeof(string); - buffer.hooks = global_hooks; + buffer.context = global_context; TEST_ASSERT_TRUE(skip_utf8_bom(&buffer) == &buffer); TEST_ASSERT_EQUAL_UINT(3U, (unsigned int)buffer.offset); @@ -441,10 +442,10 @@ static void skip_utf8_bom_should_skip_bom(void) static void skip_utf8_bom_should_not_skip_bom_if_not_at_beginning(void) { const unsigned char string[] = " \xEF\xBB\xBF{}"; - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = string; buffer.length = sizeof(string); - buffer.hooks = global_hooks; + buffer.context = global_context; buffer.offset = 1; TEST_ASSERT_NULL(skip_utf8_bom(&buffer)); @@ -512,7 +513,7 @@ static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_al { cJSON *object = cJSON_CreateObject(); cJSON *number = cJSON_CreateNumber(42); - char *name = (char*)cJSON_strdup((const unsigned char*)"number", &global_hooks); + char *name = (char*)custom_strdup((const unsigned char*)"number", &global_context); TEST_ASSERT_NOT_NULL(object); TEST_ASSERT_NOT_NULL(number); @@ -527,6 +528,24 @@ static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_al cJSON_Delete(object); } +static void is_nan_should_detect_nan(void) +{ + double nan = 0.0/0.0; + + TEST_ASSERT_TRUE(is_nan(nan)); + TEST_ASSERT_FALSE(is_nan(1)); +} + +static void is_infinity_should_detect_infinity(void) +{ + double negative_infinity = -1.0/0.0; + double positive_infinity = 1.0/0.0; + + TEST_ASSERT_TRUE(is_infinity(negative_infinity)); + TEST_ASSERT_TRUE(is_infinity(positive_infinity)); + TEST_ASSERT_FALSE(is_infinity(1)); +} + int main(void) { UNITY_BEGIN(); @@ -550,6 +569,8 @@ int main(void) RUN_TEST(cjson_create_object_reference_should_create_an_object_reference); RUN_TEST(cjson_create_array_reference_should_create_an_array_reference); RUN_TEST(cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased); + RUN_TEST(is_nan_should_detect_nan); + RUN_TEST(is_infinity_should_detect_infinity); return UNITY_END(); } diff --git a/tests/parse_array.c b/tests/parse_array.c index 69e0f411..f41721d1 100644 --- a/tests/parse_array.c +++ b/tests/parse_array.c @@ -44,10 +44,10 @@ static void assert_is_array(cJSON *array_item) static void assert_not_array(const char *json) { - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = (const unsigned char*)json; buffer.length = strlen(json) + sizeof(""); - buffer.hooks = global_hooks; + buffer.context = global_context; TEST_ASSERT_FALSE(parse_array(item, &buffer)); assert_is_invalid(item); @@ -55,10 +55,10 @@ static void assert_not_array(const char *json) static void assert_parse_array(const char *json) { - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = (const unsigned char*)json; buffer.length = strlen(json) + sizeof(""); - buffer.hooks = global_hooks; + buffer.context = global_context; TEST_ASSERT_TRUE(parse_array(item, &buffer)); assert_is_array(item); diff --git a/tests/parse_number.c b/tests/parse_number.c index f499ab65..04fa218f 100644 --- a/tests/parse_number.c +++ b/tests/parse_number.c @@ -45,7 +45,7 @@ static void assert_is_number(cJSON *number_item) static void assert_parse_number(const char *string, int integer, double real) { - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = (const unsigned char*)string; buffer.length = strlen(string) + sizeof(""); diff --git a/tests/parse_object.c b/tests/parse_object.c index 09858455..ca9902e2 100644 --- a/tests/parse_object.c +++ b/tests/parse_object.c @@ -52,10 +52,10 @@ static void assert_is_child(cJSON *child_item, const char *name, int type) static void assert_not_object(const char *json) { - parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer parsebuffer = { 0, 0, 0, 0, default_context }; parsebuffer.content = (const unsigned char*)json; parsebuffer.length = strlen(json) + sizeof(""); - parsebuffer.hooks = global_hooks; + parsebuffer.context = global_context; TEST_ASSERT_FALSE(parse_object(item, &parsebuffer)); assert_is_invalid(item); @@ -64,10 +64,10 @@ static void assert_not_object(const char *json) static void assert_parse_object(const char *json) { - parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer parsebuffer = { 0, 0, 0, 0, default_context }; parsebuffer.content = (const unsigned char*)json; parsebuffer.length = strlen(json) + sizeof(""); - parsebuffer.hooks = global_hooks; + parsebuffer.context = global_context; TEST_ASSERT_TRUE(parse_object(item, &parsebuffer)); assert_is_object(item); diff --git a/tests/parse_string.c b/tests/parse_string.c index ceb1a896..5dc13128 100644 --- a/tests/parse_string.c +++ b/tests/parse_string.c @@ -45,24 +45,24 @@ static void assert_is_string(cJSON *string_item) static void assert_parse_string(const char *string, const char *expected) { - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = (const unsigned char*)string; buffer.length = strlen(string) + sizeof(""); - buffer.hooks = global_hooks; + buffer.context = global_context; 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_hooks.deallocate(item->valuestring); + global_context.allocators.deallocate(item->valuestring, global_context.userdata); item->valuestring = NULL; } static void assert_not_parse_string(const char * const string) { - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = (const unsigned char*)string; buffer.length = strlen(string) + sizeof(""); - buffer.hooks = global_hooks; + buffer.context = global_context; TEST_ASSERT_FALSE_MESSAGE(parse_string(item, &buffer), "Malformed string should not be accepted."); assert_is_invalid(item); diff --git a/tests/parse_value.c b/tests/parse_value.c index 08ec3e7a..a42832a9 100644 --- a/tests/parse_value.c +++ b/tests/parse_value.c @@ -43,10 +43,10 @@ static void assert_is_value(cJSON *value_item, int type) static void assert_parse_value(const char *string, int type) { - parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer buffer = { 0, 0, 0, 0, default_context }; buffer.content = (const unsigned char*) string; buffer.length = strlen(string) + sizeof(""); - buffer.hooks = global_hooks; + buffer.context = global_context; TEST_ASSERT_TRUE(parse_value(item, &buffer)); assert_is_value(item, type); diff --git a/tests/print_array.c b/tests/print_array.c index 4bee17f9..a8dac86d 100644 --- a/tests/print_array.c +++ b/tests/print_array.c @@ -31,36 +31,36 @@ static void assert_print_array(const char * const expected, const char * const i cJSON item[1]; - printbuffer formatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer formatted_buffer = { 0, 0, 0, 0, 0, default_context }; + printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, default_context }; - parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + parse_buffer parsebuffer = { 0, 0, 0, 0, default_context }; parsebuffer.content = (const unsigned char*)input; parsebuffer.length = strlen(input) + sizeof(""); - parsebuffer.hooks = global_hooks; + parsebuffer.context = global_context; /* buffer for formatted printing */ formatted_buffer.buffer = printed_formatted; formatted_buffer.length = sizeof(printed_formatted); formatted_buffer.offset = 0; formatted_buffer.noalloc = true; - formatted_buffer.hooks = global_hooks; + formatted_buffer.context = global_context; /* buffer for unformatted printing */ unformatted_buffer.buffer = printed_unformatted; unformatted_buffer.length = sizeof(printed_unformatted); unformatted_buffer.offset = 0; unformatted_buffer.noalloc = true; - unformatted_buffer.hooks = global_hooks; + unformatted_buffer.context = global_context; memset(item, 0, sizeof(item)); TEST_ASSERT_TRUE_MESSAGE(parse_array(item, &parsebuffer), "Failed to parse array."); - unformatted_buffer.format = false; + unformatted_buffer.context.format = false; TEST_ASSERT_TRUE_MESSAGE(print_array(item, &unformatted_buffer), "Failed to print unformatted string."); TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted array is not correct."); - formatted_buffer.format = true; + formatted_buffer.context.format = true; TEST_ASSERT_TRUE_MESSAGE(print_array(item, &formatted_buffer), "Failed to print formatted string."); TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted array is not correct."); diff --git a/tests/print_number.c b/tests/print_number.c index 5ebb3489..6963a390 100644 --- a/tests/print_number.c +++ b/tests/print_number.c @@ -28,12 +28,12 @@ static void assert_print_number(const char *expected, double input) { unsigned char printed[1024]; cJSON item[1]; - printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer buffer = { 0, 0, 0, 0, 0, default_context }; buffer.buffer = printed; buffer.length = sizeof(printed); buffer.offset = 0; buffer.noalloc = true; - buffer.hooks = global_hooks; + buffer.context = global_context; memset(item, 0, sizeof(item)); cJSON_SetNumberValue(item, input); diff --git a/tests/print_object.c b/tests/print_object.c index 70bbb43c..b1337ecd 100644 --- a/tests/print_object.c +++ b/tests/print_object.c @@ -31,37 +31,37 @@ static void assert_print_object(const char * const expected, const char * const cJSON item[1]; - printbuffer formatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer formatted_buffer = { 0, 0, 0, 0, 0, default_context }; + printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, default_context }; + parse_buffer parsebuffer = { 0, 0, 0, 0, default_context }; /* buffer for parsing */ parsebuffer.content = (const unsigned char*)input; parsebuffer.length = strlen(input) + sizeof(""); - parsebuffer.hooks = global_hooks; + parsebuffer.context = global_context; /* buffer for formatted printing */ formatted_buffer.buffer = printed_formatted; formatted_buffer.length = sizeof(printed_formatted); formatted_buffer.offset = 0; formatted_buffer.noalloc = true; - formatted_buffer.hooks = global_hooks; + formatted_buffer.context = global_context; /* buffer for unformatted printing */ unformatted_buffer.buffer = printed_unformatted; unformatted_buffer.length = sizeof(printed_unformatted); unformatted_buffer.offset = 0; unformatted_buffer.noalloc = true; - unformatted_buffer.hooks = global_hooks; + unformatted_buffer.context = global_context; memset(item, 0, sizeof(item)); TEST_ASSERT_TRUE_MESSAGE(parse_object(item, &parsebuffer), "Failed to parse object."); - unformatted_buffer.format = false; + unformatted_buffer.context.format = false; TEST_ASSERT_TRUE_MESSAGE(print_object(item, &unformatted_buffer), "Failed to print unformatted string."); TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted object is not correct."); - formatted_buffer.format = true; + formatted_buffer.context.format = true; TEST_ASSERT_TRUE_MESSAGE(print_object(item, &formatted_buffer), "Failed to print formatted string."); TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted ojbect is not correct."); diff --git a/tests/print_string.c b/tests/print_string.c index 83de1e7a..f855ce64 100644 --- a/tests/print_string.c +++ b/tests/print_string.c @@ -27,12 +27,12 @@ static void assert_print_string(const char *expected, const char *input) { unsigned char printed[1024]; - printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer buffer = { 0, 0, 0, 0, 0, default_context }; buffer.buffer = printed; buffer.length = sizeof(printed); buffer.offset = 0; buffer.noalloc = true; - buffer.hooks = global_hooks; + buffer.context = global_context; TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer), "Failed to print string."); TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed, "The printed string isn't as expected."); diff --git a/tests/print_value.c b/tests/print_value.c index d5908232..8453d618 100644 --- a/tests/print_value.c +++ b/tests/print_value.c @@ -32,17 +32,18 @@ static void assert_print_value(const char *input) { unsigned char printed[1024]; cJSON item[1]; - printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + printbuffer buffer = { 0, 0, 0, 0, 0, default_context }; + parse_buffer parsebuffer = { 0, 0, 0, 0, default_context }; buffer.buffer = printed; buffer.length = sizeof(printed); buffer.offset = 0; buffer.noalloc = true; - buffer.hooks = global_hooks; + buffer.context = global_context; + buffer.context.format = false; parsebuffer.content = (const unsigned char*)input; parsebuffer.length = strlen(input) + sizeof(""); - parsebuffer.hooks = global_hooks; + parsebuffer.context = global_context; memset(item, 0, sizeof(item));