From c21efcbaee57e31461a445d0db94f9d0c2ac3f6f Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Fri, 19 Jan 2018 00:11:07 +0100 Subject: [PATCH 01/18] print: Comment about why the buffer is reallocated --- cJSON.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cJSON.c b/cJSON.c index cbdec413..3c936184 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1109,6 +1109,8 @@ 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) { From f520fdd4325da0cd76f9afb95ad494fb6d657563 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sat, 20 Jan 2018 14:43:50 +0100 Subject: [PATCH 02/18] Fix #234: Different argument names between declaration and definition --- cJSON.c | 14 +++++++------- cJSON.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cJSON.c b/cJSON.c index 3c936184..d832f857 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1191,20 +1191,20 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON return (char*)p.buffer; } -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 } }; - 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.format = format; p.hooks = global_hooks; return print_value(item, &p); @@ -2278,12 +2278,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); if(item) { - item->type = b ? cJSON_True : cJSON_False; + item->type = boolean ? cJSON_True : cJSON_False; } return item; diff --git a/cJSON.h b/cJSON.h index 6e0bde93..5ffa7f79 100644 --- a/cJSON.h +++ b/cJSON.h @@ -152,7 +152,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON /* 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); From 1e95d4fe9a7f48a43528728d021a2c03834e6b41 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sat, 20 Jan 2018 15:43:56 +0100 Subject: [PATCH 03/18] CMake: Remove -fsanitize=float-divide-by-zero This is so that NaN and INFINITY values can be produced. --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b347ac91..06b61605 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 From 9000f08b17d5c7701bbdc306f067ab88a4b3f15e Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sat, 20 Jan 2018 15:14:27 +0100 Subject: [PATCH 04/18] is_nan and is_infinity macros --- cJSON.c | 6 ++++-- tests/misc_tests.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/cJSON.c b/cJSON.c index d832f857..53bd877a 100644 --- a/cJSON.c +++ b/cJSON.c @@ -470,6 +470,9 @@ 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) { @@ -486,8 +489,7 @@ 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(d) || is_infinity(d)) { length = sprintf((char*)number_buffer, "null"); } diff --git a/tests/misc_tests.c b/tests/misc_tests.c index a0b4f7eb..aae52052 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -527,6 +527,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 +568,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(); } From cfee6a731836a061887990fa0d774d4c6b0e5118 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Mon, 29 Jan 2018 20:12:12 +0100 Subject: [PATCH 05/18] Extract helper: double_to_saturated_integer --- cJSON.c | 58 +++++++++++++++++---------------------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/cJSON.c b/cJSON.c index 53bd877a..5f45b084 100644 --- a/cJSON.c +++ b/cJSON.c @@ -234,6 +234,20 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) } } +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) { @@ -318,21 +332,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); @@ -342,18 +342,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; } @@ -2298,20 +2287,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { 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; From a2ede77ee01691d56d5fcc07d33594e325c18930 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Mon, 29 Jan 2018 20:24:42 +0100 Subject: [PATCH 06/18] print_number: Introduce fast path for integers. Thanks @Tangerino for suggesting this optimisation. --- cJSON.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cJSON.c b/cJSON.c index 5f45b084..8b601960 100644 --- a/cJSON.c +++ b/cJSON.c @@ -466,7 +466,8 @@ static void update_offset(printbuffer * const buffer) 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 */ @@ -478,20 +479,24 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out return false; } - if (is_nan(d) || is_infinity(d)) + 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); } } From 4e9154458dce824216820fd49bfe8bd97bc1e287 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Wed, 31 Jan 2018 10:31:14 +0100 Subject: [PATCH 07/18] parse_value: Check only first character at first This should improve performance --- cJSON.c | 108 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 42 deletions(-) diff --git a/cJSON.c b/cJSON.c index 8b601960..01ca07ee 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1214,51 +1214,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)) + if (!can_read(input_buffer, 1)) { - 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] == '{')) - { - 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. */ From bd307ec3b5f1c0555976c4b0118621e485367aa2 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sat, 3 Feb 2018 15:54:47 +0100 Subject: [PATCH 08/18] cJSON_Compare: Performance improvement for objects Check the size to prevent comparing objects equal if they are prefixes of each other. --- cJSON.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/cJSON.c b/cJSON.c index 01ca07ee..d8ea4d33 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1738,8 +1738,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; @@ -1751,13 +1750,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; } @@ -2891,6 +2902,14 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons { cJSON *a_element = NULL; cJSON *b_element = NULL; + size_t a_size = get_array_size(a); + size_t b_size = get_array_size(b); + + if (a_size != b_size) + { + return false; + } + cJSON_ArrayForEach(a_element, a) { /* TODO This has O(n^2) runtime, which is horrible! */ @@ -2906,22 +2925,6 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons } } - /* 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) - { - a_element = get_object_item(a, b_element->string, case_sensitive); - if (a_element == NULL) - { - return false; - } - - if (!cJSON_Compare(b_element, a_element, case_sensitive)) - { - return false; - } - } - return true; } From dcfa1618bb72682847bc444d9c8184e0b74205c1 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sat, 3 Feb 2018 19:14:19 +0100 Subject: [PATCH 09/18] Remove superfluous null checks in can_read/access_at_index macros --- cJSON.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cJSON.c b/cJSON.c index d8ea4d33..85024231 100644 --- a/cJSON.c +++ b/cJSON.c @@ -269,9 +269,9 @@ typedef struct } 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) From cb5bd2c97b04985d89c9ca66d30896d13e9f1710 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sun, 25 Mar 2018 14:20:48 +0200 Subject: [PATCH 10/18] cJSON.c: Remove unnecessary includes --- cJSON.c | 1 - 1 file changed, 1 deletion(-) diff --git a/cJSON.c b/cJSON.c index 85024231..48862944 100644 --- a/cJSON.c +++ b/cJSON.c @@ -39,7 +39,6 @@ #include #include -#include #include #include #include From d06baf7052e25b5c22424afe4cad341a56e2e829 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sun, 25 Mar 2018 14:25:46 +0200 Subject: [PATCH 11/18] is_{nan,infinity}: Wrap macro arguments in parentheses --- cJSON.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cJSON.c b/cJSON.c index 48862944..681a3484 100644 --- a/cJSON.c +++ b/cJSON.c @@ -458,8 +458,8 @@ 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) +#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) From 3bd3b7aae7f947237ca8a949a8f281e3ae3d6fb2 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sun, 25 Mar 2018 15:11:56 +0200 Subject: [PATCH 12/18] cJSON.c: Remove unused cast --- cJSON.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cJSON.c b/cJSON.c index 681a3484..89de6f6e 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1007,7 +1007,7 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return } buffer.content = (const unsigned char*)value; - buffer.length = strlen((const char*)value) + sizeof(""); + buffer.length = strlen(value) + sizeof(""); buffer.offset = 0; buffer.hooks = global_hooks; From ddd93934e6fc8afa9047333b43b4ef67e4afb764 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sun, 25 Mar 2018 15:12:15 +0200 Subject: [PATCH 13/18] cJSON: cjson_min: Wrap arguments in parentheses --- cJSON.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cJSON.c b/cJSON.c index 89de6f6e..412a2505 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1077,7 +1077,7 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) return cJSON_ParseWithOpts(value, 0, 0); } -#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) { From 054b4d146d4e22b98ab6dd33b0a64416cb05672b Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Sun, 25 Mar 2018 23:31:36 +0200 Subject: [PATCH 14/18] Gitignore: add CLion files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) 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 From 0b20df9ecf06319aca645876bdb6cdec78b340f2 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Fri, 3 Aug 2018 07:39:17 +0200 Subject: [PATCH 15/18] Replace strcpy with memcpy and remove magic numbers for string sizes --- cJSON.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/cJSON.c b/cJSON.c index 412a2505..0428451c 100644 --- a/cJSON.c +++ b/cJSON.c @@ -836,12 +836,13 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe /* empty string */ if (input == NULL) { - output = ensure(output_buffer, sizeof("\"\"")); + const char quotes[] = "\"\""; + output = ensure(output_buffer, sizeof(quotes)); if (output == NULL) { return false; } - strcpy((char*)output, "\"\""); + memcpy(output, quotes, sizeof(quotes)); return true; } @@ -1297,31 +1298,40 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp switch ((item->type) & 0xFF) { case cJSON_NULL: - output = ensure(output_buffer, 5); + { + const char null_string[] = "null"; + output = ensure(output_buffer, sizeof(null_string)); if (output == NULL) { return false; } - strcpy((char*)output, "null"); + memcpy(output, null_string, sizeof(null_string)); return true; + } case cJSON_False: - output = ensure(output_buffer, 6); + { + const char false_string[] = "false"; + output = ensure(output_buffer, sizeof(false_string)); if (output == NULL) { return false; } - strcpy((char*)output, "false"); + memcpy(output, false_string, sizeof(false_string)); return true; + } case cJSON_True: - output = ensure(output_buffer, 5); + { + const char true_string[] = "true"; + output = ensure(output_buffer, sizeof(true_string)); if (output == NULL) { return false; } - strcpy((char*)output, "true"); + memcpy(output, true_string, sizeof(true_string)); return true; + } case cJSON_Number: return print_number(item, output_buffer); From 2371b7bc66d54cff95bc9970d725465eb9bd96a7 Mon Sep 17 00:00:00 2001 From: caglarivriz Date: Thu, 2 Apr 2020 11:59:19 +0300 Subject: [PATCH 16/18] Added cJSON_ParseWithLength (#358) Co-authored-by: Caglar Ivriz --- README.md | 8 ++++++ cJSON.c | 31 ++++++++++++++++++++--- cJSON.h | 2 ++ tests/parse_examples.c | 57 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c6b788f9..dac835b8 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,12 @@ Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse` cJSON *json = cJSON_Parse(string); ``` +Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`. + +```c +cJSON *json = cJSON_ParseWithLength(string, buffer_length); +``` + It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`. The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`. @@ -255,6 +261,8 @@ By default, characters in the input string that follow the parsed JSON will not If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`. `return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON. +If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`. + ### Printing JSON Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`. diff --git a/cJSON.c b/cJSON.c index 0428451c..1873af96 100644 --- a/cJSON.c +++ b/cJSON.c @@ -963,6 +963,11 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) return NULL; } + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { buffer->offset++; @@ -992,8 +997,23 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) return 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) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) { parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; cJSON *item = NULL; @@ -1002,13 +1022,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return global_error.json = NULL; global_error.position = 0; - if (value == NULL) + if (value == NULL || 0 == buffer_length) { goto fail; } buffer.content = (const unsigned char*)value; - buffer.length = strlen(value) + sizeof(""); + buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; @@ -1078,6 +1098,11 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) return cJSON_ParseWithOpts(value, 0, 0); } +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + #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) diff --git a/cJSON.h b/cJSON.h index 5ffa7f79..63518f99 100644 --- a/cJSON.h +++ b/cJSON.h @@ -138,9 +138,11 @@ 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_ParseWithLength(const char *value, size_t buffer_length); /* 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_ParseWithLengthOpts(const char *value, size_t buffer_length, 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); diff --git a/tests/parse_examples.c b/tests/parse_examples.c index 76215bbc..7e957f17 100644 --- a/tests/parse_examples.c +++ b/tests/parse_examples.c @@ -195,6 +195,61 @@ static void test12_should_not_be_parsed(void) } } +static void test13_should_be_parsed_without_null_termination(void) +{ + cJSON *tree = NULL; + const char test_13[] = "{" \ + "\"Image\":{" \ + "\"Width\":800," \ + "\"Height\":600," \ + "\"Title\":\"Viewfrom15thFloor\"," \ + "\"Thumbnail\":{" \ + "\"Url\":\"http:/*www.example.com/image/481989943\"," \ + "\"Height\":125," \ + "\"Width\":\"100\"" \ + "}," \ + "\"IDs\":[116,943,234,38793]" \ + "}" \ + "}"; + + char test_13_wo_null[sizeof(test_13) - 1]; + memcpy(test_13_wo_null, test_13, sizeof(test_13) - 1); + + tree = cJSON_ParseWithLength(test_13_wo_null, sizeof(test_13) - 1); + TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to parse valid json."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +static void test14_should_not_be_parsed(void) +{ + cJSON *tree = NULL; + const char test_14[] = "{" \ + "\"Image\":{" \ + "\"Width\":800," \ + "\"Height\":600," \ + "\"Title\":\"Viewfrom15thFloor\"," \ + "\"Thumbnail\":{" \ + "\"Url\":\"http:/*www.example.com/image/481989943\"," \ + "\"Height\":125," \ + "\"Width\":\"100\"" \ + "}," \ + "\"IDs\":[116,943,234,38793]" \ + "}" \ + "}"; + + tree = cJSON_ParseWithLength(test_14, sizeof(test_14) - 2); + TEST_ASSERT_NULL_MESSAGE(tree, "Should not continue after buffer_length is reached."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + int main(void) { UNITY_BEGIN(); @@ -210,5 +265,7 @@ int main(void) RUN_TEST(file_test10_should_be_parsed_and_printed); RUN_TEST(file_test11_should_be_parsed_and_printed); RUN_TEST(test12_should_not_be_parsed); + RUN_TEST(test13_should_be_parsed_without_null_termination); + RUN_TEST(test14_should_not_be_parsed); return UNITY_END(); } From 5437b7908651c6b0828a82b21b4baf98846c0d01 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Thu, 2 Apr 2020 18:06:56 +0900 Subject: [PATCH 17/18] Add getNumberValue function * Add GetNumberValue function and testcase Co-authored-by: Alan Wang --- cJSON.c | 16 ++++++++++++++-- cJSON.h | 3 ++- tests/misc_tests.c | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/cJSON.c b/cJSON.c index 1873af96..9bc8961e 100644 --- a/cJSON.c +++ b/cJSON.c @@ -71,14 +71,26 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) return (const char*) (global_error.json + global_error.position); } -CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { - if (!cJSON_IsString(item)) { +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) +{ + if (!cJSON_IsString(item)) + { return NULL; } return item->valuestring; } +CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item) +{ + if (!cJSON_IsNumber(item)) + { + return 0.0/0.0; + } + + return item->valuedouble; +} + /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 7) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. diff --git a/cJSON.h b/cJSON.h index 63518f99..9267c93c 100644 --- a/cJSON.h +++ b/cJSON.h @@ -167,8 +167,9 @@ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *st /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); -/* Check if the item is a string and return its valuestring */ +/* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); diff --git a/tests/misc_tests.c b/tests/misc_tests.c index aae52052..b0b7528a 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -463,6 +463,19 @@ static void cjson_get_string_value_should_get_a_string(void) cJSON_Delete(string); } +static void cjson_get_number_value_should_get_a_number(void) +{ + cJSON *string = cJSON_CreateString("test"); + cJSON *number = cJSON_CreateNumber(1); + + TEST_ASSERT_EQUAL_DOUBLE(cJSON_GetNumberValue(number), number->valuedouble); + TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(string)); + TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(NULL)); + + cJSON_Delete(number); + cJSON_Delete(string); +} + static void cjson_create_string_reference_should_create_a_string_reference(void) { const char *string = "I am a string!"; @@ -564,6 +577,7 @@ int main(void) RUN_TEST(skip_utf8_bom_should_skip_bom); RUN_TEST(skip_utf8_bom_should_not_skip_bom_if_not_at_beginning); RUN_TEST(cjson_get_string_value_should_get_a_string); + RUN_TEST(cjson_get_number_value_should_get_a_number); RUN_TEST(cjson_create_string_reference_should_create_a_string_reference); RUN_TEST(cjson_create_object_reference_should_create_an_object_reference); RUN_TEST(cjson_create_array_reference_should_create_an_array_reference); From 1ac905d3b2359f48a0463afe8e16a080f0e1e03c Mon Sep 17 00:00:00 2001 From: maebex Date: Sat, 30 Mar 2024 10:42:22 +0100 Subject: [PATCH 18/18] Set free'd pointers to NULL whenever they are not reassigned immediately after --- cJSON.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cJSON.c b/cJSON.c index 9bc8961e..727e1827 100644 --- a/cJSON.c +++ b/cJSON.c @@ -235,10 +235,12 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { global_hooks.deallocate(item->string); + item->string = NULL; } global_hooks.deallocate(item); item = next; @@ -820,6 +822,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu if (output != NULL) { input_buffer->hooks.deallocate(output); + output = NULL; } if (input_pointer != NULL) @@ -1165,6 +1168,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* free the buffer */ hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } return printed; @@ -1173,11 +1177,13 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } if (printed != NULL) { hooks->deallocate(printed); + printed = NULL; } return NULL; @@ -1218,6 +1224,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON if (!print_value(item, &p)) { global_hooks.deallocate(p.buffer); + p.buffer = NULL; return NULL; } @@ -2987,4 +2994,5 @@ CJSON_PUBLIC(void *) cJSON_malloc(size_t size) CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); + object = NULL; }