From ec898e01433c63b7408d28cdcbbccee45ab56ad6 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Sun, 24 Apr 2022 16:40:31 +0200 Subject: [PATCH 01/14] Github pullreq 556: long long ints --- cJSON.c | 83 +++++++++++++++++++++++++++++++++++++-------------------- cJSON.h | 11 ++++++-- 2 files changed, 63 insertions(+), 31 deletions(-) diff --git a/cJSON.c b/cJSON.c index 524ba46..cf7cbe9 100644 --- a/cJSON.c +++ b/cJSON.c @@ -69,6 +69,21 @@ #endif #define false ((cJSON_bool)0) +/* define our own int max and min */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define CJSON_INT_MAX LLONG_MAX +#define CJSON_INT_MIN LLONG_MIN +#define strtoint(s) strtoll((const char*)(s), NULL, 0) +#define intfmt "%lld" +#else +#define CJSON_INT_MAX INT_MAX +#define CJSON_INT_MIN INT_MIN +#define strtoint(s) atoi((const char*)(s)) +#define intfmt "%d" +#endif + +#define isint(d) (d == floor(d)) + /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ #ifndef isinf #define isinf(d) (isnan((d - d)) && !isnan(d)) @@ -305,6 +320,7 @@ typedef struct static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { double number = 0; + cJSON_bool integer = cJSON_True; unsigned char *after_end = NULL; unsigned char number_c_string[64]; unsigned char decimal_point = get_decimal_point(); @@ -334,12 +350,17 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu case '9': case '+': case '-': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + case 'e': case 'E': + integer = cJSON_False; number_c_string[i] = buffer_at_offset(input_buffer)[i]; break; case '.': + integer = cJSON_False; number_c_string[i] = decimal_point; break; @@ -359,17 +380,22 @@ loop_end: item->valuedouble = number; /* use saturation in case of overflow */ - if (number >= INT_MAX) + if (number >= CJSON_INT_MAX) { - item->valueint = INT_MAX; + item->valueint = CJSON_INT_MAX; } - else if (number <= (double)INT_MIN) + else if (number <= (double)CJSON_INT_MIN) { - item->valueint = INT_MIN; + item->valueint = CJSON_INT_MIN; + } + else if (integer == cJSON_True) + { + /* parse again to handle the very big integer */ + item->valueint = strtoint(number_c_string); } else { - item->valueint = (int)number; + item->valueint = (cJSON_int)number; } item->type = cJSON_Number; @@ -381,13 +407,13 @@ loop_end: /* 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) + if (number >= CJSON_INT_MAX) { - object->valueint = INT_MAX; + object->valueint = CJSON_INT_MAX; } - else if (number <= (double)INT_MIN) + else if (number <= (double)CJSON_INT_MIN) { - object->valueint = INT_MIN; + object->valueint = CJSON_INT_MIN; } else { @@ -562,10 +588,11 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out { length = sprintf((char*)number_buffer, "null"); } - else if(d == (double)item->valueint) - { - length = sprintf((char*)number_buffer, "%d", item->valueint); - } + else if (isint(d) && ((item->valueint != CJSON_INT_MAX && + item->valueint != CJSON_INT_MIN) || d == (double)item->valueint)) + { + length = sprintf((char*)number_buffer, intfmt, item->valueint); + } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ @@ -2426,27 +2453,25 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) return item; } +CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + cJSON_SetIntValue(item, num); + } + + return item; +} + CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { cJSON *item = cJSON_New_Item(&global_hooks); 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 <= (double)INT_MIN) - { - item->valueint = INT_MIN; - } - else - { - item->valueint = (int)num; - } + cJSON_SetNumberValue(item, num); } return item; @@ -3022,7 +3047,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons return true; case cJSON_Number: - if (compare_double(a->valuedouble, b->valuedouble)) + if ((a->valueint == b->valueint) && compare_double(a->valuedouble, b->valuedouble)) { return true; } diff --git a/cJSON.h b/cJSON.h index 95a9cf6..3edfe6a 100644 --- a/cJSON.h +++ b/cJSON.h @@ -99,6 +99,12 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +typedef long long cJSON_int; +#else +typedef int cJSON_int; +#endif + /* The cJSON structure: */ typedef struct cJSON { @@ -114,7 +120,7 @@ typedef struct cJSON /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ - int valueint; + cJSON_int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; @@ -196,6 +202,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num); CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ @@ -272,7 +279,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +#define cJSON_SetIntValue(object, number) ((object) ? ((object)->valuedouble = (number), (object)->valueint = (number)) : (number)) /* helper for the cJSON_SetNumberValue macro */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) From 3506c17e572b96b35db66b2dc89f057d866a304e Mon Sep 17 00:00:00 2001 From: No Default Name Date: Sun, 24 Apr 2022 16:47:29 +0200 Subject: [PATCH 02/14] Long long ints: optional compile choice --- cJSON.c | 2 +- cJSON.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cJSON.c b/cJSON.c index cf7cbe9..d0f9964 100644 --- a/cJSON.c +++ b/cJSON.c @@ -70,7 +70,7 @@ #define false ((cJSON_bool)0) /* define our own int max and min */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#if defined(CJSON_INT_USE_LONGLONG) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define CJSON_INT_MAX LLONG_MAX #define CJSON_INT_MIN LLONG_MIN #define strtoint(s) strtoll((const char*)(s), NULL, 0) diff --git a/cJSON.h b/cJSON.h index 3edfe6a..630b1e8 100644 --- a/cJSON.h +++ b/cJSON.h @@ -99,7 +99,9 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define notCJSON_INT_USE_LONGLONG + +#if defined(CJSON_INT_USE_LONGLONG) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L typedef long long cJSON_int; #else typedef int cJSON_int; From 586762189b8dd142858b155b8076248859253aa5 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Sun, 24 Apr 2022 18:31:45 +0200 Subject: [PATCH 03/14] Add tests for large integers representable in double --- tests/print_number.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/print_number.c b/tests/print_number.c index 3fbf9cb..2ebca03 100644 --- a/tests/print_number.c +++ b/tests/print_number.c @@ -72,6 +72,11 @@ static void print_number_should_print_negative_integers(void) assert_print_number("-1", -1.0); assert_print_number("-32768", -32768.0); assert_print_number("-2147483648", -2147483648.0); + assert_print_number("-2147483649", -2147483649.0); + assert_print_number("-4294967296", -4294967296.0); + assert_print_number("-4294967297", -4294967297.0); + /* Approx lowest integer exactly representable in double */ + assert_print_number("-8765432101234567", -8765432101234567.0); } static void print_number_should_print_positive_integers(void) @@ -79,6 +84,11 @@ static void print_number_should_print_positive_integers(void) assert_print_number("1", 1.0); assert_print_number("32767", 32767.0); assert_print_number("2147483647", 2147483647.0); + assert_print_number("2147483648", 2147483648.0); + assert_print_number("4294967295", 4294967295.0); + assert_print_number("4294967296", 4294967296.0); + /* Approx highest integer exactly representable in double */ + assert_print_number("8765432101234567", 8765432101234567.0); } static void print_number_should_print_positive_reals(void) From a1efa1e77f54e08268af41599a49af58e97674e1 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Sun, 24 Apr 2022 20:13:29 +0200 Subject: [PATCH 04/14] long long fixes, enhancements, tests --- cJSON.c | 23 ++++++++++++----------- cJSON.h | 5 ++++- tests/misc_tests.c | 12 ++++++------ tests/parse_number.c | 34 +++++++++++++++++++++++++++------- 4 files changed, 49 insertions(+), 25 deletions(-) diff --git a/cJSON.c b/cJSON.c index d0f9964..1b23b65 100644 --- a/cJSON.c +++ b/cJSON.c @@ -70,7 +70,7 @@ #define false ((cJSON_bool)0) /* define our own int max and min */ -#if defined(CJSON_INT_USE_LONGLONG) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#ifdef CJSON_INT_USE_LONGLONG #define CJSON_INT_MAX LLONG_MAX #define CJSON_INT_MIN LLONG_MIN #define strtoint(s) strtoll((const char*)(s), NULL, 0) @@ -82,7 +82,7 @@ #define intfmt "%d" #endif -#define isint(d) (d == floor(d)) +#define has_no_decimals(d) (d == floor(d)) /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ #ifndef isinf @@ -320,7 +320,7 @@ typedef struct static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { double number = 0; - cJSON_bool integer = cJSON_True; + cJSON_bool is_integer = cJSON_True; unsigned char *after_end = NULL; unsigned char number_c_string[64]; unsigned char decimal_point = get_decimal_point(); @@ -355,12 +355,12 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu case 'e': case 'E': - integer = cJSON_False; + is_integer = cJSON_False; number_c_string[i] = buffer_at_offset(input_buffer)[i]; break; case '.': - integer = cJSON_False; + is_integer = cJSON_False; number_c_string[i] = decimal_point; break; @@ -380,7 +380,8 @@ loop_end: item->valuedouble = number; /* use saturation in case of overflow */ - if (number >= CJSON_INT_MAX) + /* note that even float has range beyond long long (though inexactly) */ + if (number >= (double)CJSON_INT_MAX) { item->valueint = CJSON_INT_MAX; } @@ -388,9 +389,9 @@ loop_end: { item->valueint = CJSON_INT_MIN; } - else if (integer == cJSON_True) + else if (is_integer == cJSON_True) { - /* parse again to handle the very big integer */ + /* integer is in range, parse as integer to prevent float inexactness */ item->valueint = strtoint(number_c_string); } else @@ -407,7 +408,7 @@ loop_end: /* 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 >= CJSON_INT_MAX) + if (number >= (double)CJSON_INT_MAX) { object->valueint = CJSON_INT_MAX; } @@ -588,8 +589,8 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out { length = sprintf((char*)number_buffer, "null"); } - else if (isint(d) && ((item->valueint != CJSON_INT_MAX && - item->valueint != CJSON_INT_MIN) || d == (double)item->valueint)) + else if (has_no_decimals(d) && item->valueint != CJSON_INT_MAX && + item->valueint != CJSON_INT_MIN && d == (double)item->valueint) { length = sprintf((char*)number_buffer, intfmt, item->valueint); } diff --git a/cJSON.h b/cJSON.h index 630b1e8..b2c99de 100644 --- a/cJSON.h +++ b/cJSON.h @@ -100,8 +100,11 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define cJSON_StringIsConst 512 #define notCJSON_INT_USE_LONGLONG +// Note: Default cmake rules will force C89 and prevent long long. +// To use long long, delete build tree, then run: +// CFLAGS="-Wall -Werror" cmake -DENABLE_CUSTOM_COMPILER_FLAGS=Off -#if defined(CJSON_INT_USE_LONGLONG) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#ifdef CJSON_INT_USE_LONGLONG typedef long long cJSON_int; #else typedef int cJSON_int; diff --git a/tests/misc_tests.c b/tests/misc_tests.c index 19b7c85..3080dee 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -231,13 +231,13 @@ static void cjson_set_number_value_should_set_numbers(void) TEST_ASSERT_EQUAL(-1, number->valueint); TEST_ASSERT_EQUAL_DOUBLE(-1.5, number->valuedouble); - cJSON_SetNumberValue(number, 1 + (double)INT_MAX); - TEST_ASSERT_EQUAL(INT_MAX, number->valueint); - TEST_ASSERT_EQUAL_DOUBLE(1 + (double)INT_MAX, number->valuedouble); + cJSON_SetNumberValue(number, 1.0 + (double)CJSON_INT_MAX); + TEST_ASSERT_EQUAL(CJSON_INT_MAX, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(1.0 + (double)CJSON_INT_MAX, number->valuedouble); - cJSON_SetNumberValue(number, -1 + (double)INT_MIN); - TEST_ASSERT_EQUAL(INT_MIN, number->valueint); - TEST_ASSERT_EQUAL_DOUBLE(-1 + (double)INT_MIN, number->valuedouble); + cJSON_SetNumberValue(number, -1.0 + (double)CJSON_INT_MIN); + TEST_ASSERT_EQUAL(CJSON_INT_MIN, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(-1.0 + (double)CJSON_INT_MIN, number->valuedouble); } static void cjson_detach_item_via_pointer_should_detach_items(void) diff --git a/tests/parse_number.c b/tests/parse_number.c index 4cb72ec..502f10b 100644 --- a/tests/parse_number.c +++ b/tests/parse_number.c @@ -43,7 +43,7 @@ static void assert_is_number(cJSON *number_item) assert_has_no_string(number_item); } -static void assert_parse_number(const char *string, int integer, double real) +static void assert_parse_number(const char *string, cJSON_int integer, double real) { parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; buffer.content = (const unsigned char*)string; @@ -51,7 +51,11 @@ static void assert_parse_number(const char *string, int integer, double real) TEST_ASSERT_TRUE(parse_number(item, &buffer)); assert_is_number(item); +#ifdef CJSON_INT_USE_LONGLONG + TEST_ASSERT_EQUAL_INT64(integer, item->valueint); +#else TEST_ASSERT_EQUAL_INT(integer, item->valueint); +#endif TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); } @@ -66,14 +70,24 @@ static void parse_number_should_parse_negative_integers(void) { assert_parse_number("-1", -1, -1); assert_parse_number("-32768", -32768, -32768.0); - assert_parse_number("-2147483648", (int)-2147483648.0, -2147483648.0); + assert_parse_number("-2147483648", -2147483648, -2147483648.0); +#ifdef CJSON_INT_USE_LONGLONG + assert_parse_number("-8765432101234567", -8765432101234567LL, -8765432101234567.0); +#else + assert_parse_number("-8765432101234567", CJSON_INT_MIN, -8765432101234567.0); +#endif } static void parse_number_should_parse_positive_integers(void) { assert_parse_number("1", 1, 1); assert_parse_number("32767", 32767, 32767.0); - assert_parse_number("2147483647", (int)2147483647.0, 2147483647.0); + assert_parse_number("2147483647", 2147483647, 2147483647.0); +#ifdef CJSON_INT_USE_LONGLONG + assert_parse_number("8765432101234567", 8765432101234567LL, 8765432101234567.0); +#else + assert_parse_number("8765432101234567", CJSON_INT_MAX, 8765432101234567.0); +#endif } static void parse_number_should_parse_positive_reals(void) @@ -81,8 +95,11 @@ static void parse_number_should_parse_positive_reals(void) assert_parse_number("0.001", 0, 0.001); assert_parse_number("10e-10", 0, 10e-10); assert_parse_number("10E-10", 0, 10e-10); - assert_parse_number("10e10", INT_MAX, 10e10); - assert_parse_number("123e+127", INT_MAX, 123e127); +#ifdef CJSON_INT_USE_LONGLONG + assert_parse_number("10e10", 100000000000LL, 10e10); +#endif + assert_parse_number("10e20", CJSON_INT_MAX, 10e20); + assert_parse_number("123e+127", CJSON_INT_MAX, 123e127); assert_parse_number("123e-128", 0, 123e-128); } @@ -91,8 +108,11 @@ static void parse_number_should_parse_negative_reals(void) assert_parse_number("-0.001", 0, -0.001); assert_parse_number("-10e-10", 0, -10e-10); assert_parse_number("-10E-10", 0, -10e-10); - assert_parse_number("-10e20", INT_MIN, -10e20); - assert_parse_number("-123e+127", INT_MIN, -123e127); +#ifdef CJSON_INT_USE_LONGLONG + assert_parse_number("-10e10", -100000000000LL, -10e10); +#endif + assert_parse_number("-10e20", CJSON_INT_MIN, -10e20); + assert_parse_number("-123e+127", CJSON_INT_MIN, -123e127); assert_parse_number("-123e-128", 0, -123e-128); } From 5cdc8d4c272c6d53ada067b9f2fba7cd50a36990 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Thu, 28 Apr 2022 21:18:28 +0200 Subject: [PATCH 05/14] Floats choosable between double and float --- cJSON.c | 71 ++++++++++++++++++++++++++--------------- cJSON.h | 20 ++++++++---- tests/readme_examples.c | 2 +- 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/cJSON.c b/cJSON.c index 1b23b65..6f5fa73 100644 --- a/cJSON.c +++ b/cJSON.c @@ -69,7 +69,6 @@ #endif #define false ((cJSON_bool)0) -/* define our own int max and min */ #ifdef CJSON_INT_USE_LONGLONG #define CJSON_INT_MAX LLONG_MAX #define CJSON_INT_MIN LLONG_MIN @@ -82,7 +81,17 @@ #define intfmt "%d" #endif +#ifdef CJSON_FLOAT_USE_FLOAT +#define strtofloat strtof +#define has_no_decimals(d) (d == floorf(d)) +#define floatfmt_shorter "%1.6g" +#define floatfmt_longer "%1.9g" +#else +#define strtofloat strtod #define has_no_decimals(d) (d == floor(d)) +#define floatfmt_shorter "%1.15g" +#define floatfmt_longer "%1.17g" +#endif /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ #ifndef isinf @@ -121,11 +130,11 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) return item->valuestring; } -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +CJSON_PUBLIC(cJSON_float) cJSON_GetNumberValue(const cJSON * const item) { if (!cJSON_IsNumber(item)) { - return (double) NAN; + return (cJSON_float) NAN; } return item->valuedouble; @@ -319,7 +328,7 @@ typedef struct /* Parse the input text to generate a number, and populate the result into item. */ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { - double number = 0; + cJSON_float number; cJSON_bool is_integer = cJSON_True; unsigned char *after_end = NULL; unsigned char number_c_string[64]; @@ -371,7 +380,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu loop_end: number_c_string[i] = '\0'; - number = strtod((const char*)number_c_string, (char**)&after_end); + number = strtofloat((const char*)number_c_string, (char**)&after_end); if (number_c_string == after_end) { return false; /* parse_error */ @@ -381,11 +390,11 @@ loop_end: /* use saturation in case of overflow */ /* note that even float has range beyond long long (though inexactly) */ - if (number >= (double)CJSON_INT_MAX) + if (number >= (cJSON_float)CJSON_INT_MAX) { item->valueint = CJSON_INT_MAX; } - else if (number <= (double)CJSON_INT_MIN) + else if (number <= (cJSON_float)CJSON_INT_MIN) { item->valueint = CJSON_INT_MIN; } @@ -406,19 +415,19 @@ loop_end: } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +CJSON_PUBLIC(cJSON_float) cJSON_SetNumberHelper(cJSON *object, cJSON_float number) { - if (number >= (double)CJSON_INT_MAX) + if (number >= (cJSON_float)CJSON_INT_MAX) { object->valueint = CJSON_INT_MAX; } - else if (number <= (double)CJSON_INT_MIN) + else if (number <= (cJSON_float)CJSON_INT_MIN) { object->valueint = CJSON_INT_MIN; } else { - object->valueint = (int)number; + object->valueint = (cJSON_int)number; } return object->valuedouble = number; @@ -562,22 +571,31 @@ static void update_offset(printbuffer * const buffer) } /* securely comparison of floating-point variables */ -static cJSON_bool compare_double(double a, double b) +#ifdef CJSON_FLOAT_USE_FLOAT +static cJSON_bool compare_cJSON_float(float a, float b) +{ + float maxVal = fabsf(a) > fabsf(b) ? fabsf(a) : fabsf(b); + return (fabsf(a - b) <= maxVal * FLT_EPSILON); +} +#else +static cJSON_bool compare_cJSON_float(double a, double b) { double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } +#endif /* 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; + cJSON_float d = item->valuedouble; int length = 0; size_t i = 0; unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); - double test = 0.0; + cJSON_float test; + char *test_endptr = NULL; if (output_buffer == NULL) { @@ -590,20 +608,21 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out length = sprintf((char*)number_buffer, "null"); } else if (has_no_decimals(d) && item->valueint != CJSON_INT_MAX && - item->valueint != CJSON_INT_MIN && d == (double)item->valueint) + item->valueint != CJSON_INT_MIN && d == (cJSON_float)item->valueint) { length = sprintf((char*)number_buffer, intfmt, item->valueint); } else { - /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = sprintf((char*)number_buffer, "%1.15g", d); + /* Try 15 (6) decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, floatfmt_shorter, d); /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + test = strtofloat((char*)number_buffer, &test_endptr); + if (test_endptr == NULL || test_endptr == (char *)number_buffer || *test_endptr != '\0' || !compare_cJSON_float((cJSON_float)test, d)) { - /* If not, print with 17 decimal places of precision */ - length = sprintf((char*)number_buffer, "%1.17g", d); + /* If not, print with 17 (9) decimal places of precision */ + length = sprintf((char*)number_buffer, floatfmt_longer, d); } } @@ -2152,7 +2171,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * co return NULL; } -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const cJSON_float number) { cJSON *number_item = cJSON_CreateNumber(number); if (add_item_to_object(object, name, number_item, &global_hooks, false)) @@ -2466,7 +2485,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(cJSON_float num) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) @@ -2584,7 +2603,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) for(i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber(numbers[i]); + n = cJSON_CreateInt(numbers[i]); if (!n) { cJSON_Delete(a); @@ -2624,7 +2643,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) for(i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber((double)numbers[i]); + n = cJSON_CreateNumber((cJSON_float)numbers[i]); if(!n) { cJSON_Delete(a); @@ -2664,7 +2683,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) for(i = 0; a && (i < (size_t)count); i++) { - n = cJSON_CreateNumber(numbers[i]); + n = cJSON_CreateNumber((cJSON_float)numbers[i]); if(!n) { cJSON_Delete(a); @@ -3048,7 +3067,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons return true; case cJSON_Number: - if ((a->valueint == b->valueint) && compare_double(a->valuedouble, b->valuedouble)) + if ((a->valueint == b->valueint) && compare_cJSON_float(a->valuedouble, b->valuedouble)) { return true; } diff --git a/cJSON.h b/cJSON.h index b2c99de..fa0372a 100644 --- a/cJSON.h +++ b/cJSON.h @@ -110,6 +110,14 @@ typedef long long cJSON_int; typedef int cJSON_int; #endif +#define notCJSON_FLOAT_USE_FLOAT + +#ifdef CJSON_FLOAT_USE_FLOAT +typedef float cJSON_float; +#else +typedef double cJSON_float; +#endif + /* The cJSON structure: */ typedef struct cJSON { @@ -127,7 +135,7 @@ typedef struct cJSON /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ cJSON_int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; + cJSON_float valuedouble; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; @@ -188,7 +196,7 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); +CJSON_PUBLIC(cJSON_float) cJSON_GetNumberValue(const cJSON * const item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); @@ -208,7 +216,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num); -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(cJSON_float num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); @@ -277,7 +285,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * co CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); -CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const cJSON_float number); CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); @@ -286,8 +294,8 @@ CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * c /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #define cJSON_SetIntValue(object, number) ((object) ? ((object)->valuedouble = (number), (object)->valueint = (number)) : (number)) /* helper for the cJSON_SetNumberValue macro */ -CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); -#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +CJSON_PUBLIC(cJSON_float) cJSON_SetNumberHelper(cJSON *object, cJSON_float number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (cJSON_float)number) : (number)) /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); diff --git a/tests/readme_examples.c b/tests/readme_examples.c index 09850cd..691cebc 100644 --- a/tests/readme_examples.c +++ b/tests/readme_examples.c @@ -202,7 +202,7 @@ static int supports_full_hd(const char * const monitor) goto end; } - if (compare_double(width->valuedouble, 1920) && compare_double(height->valuedouble, 1080)) + if (compare_cJSON_float(width->valuedouble, 1920) && compare_cJSON_float(height->valuedouble, 1080)) { status = 1; goto end; From 882361c42876ae23fed5d9549e3bd652086b2c18 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Thu, 28 Apr 2022 22:05:41 +0200 Subject: [PATCH 06/14] Fix tests for all combinations of int/longlong and double/float --- cJSON.h | 7 ++++--- tests/compare_tests.c | 7 ++++++- tests/inputs/test7float | 22 ++++++++++++++++++++++ tests/inputs/test7float.expected | 19 +++++++++++++++++++ tests/misc_tests.c | 5 +++++ tests/parse_examples.c | 4 ++++ tests/parse_number.c | 14 +++++++++++++- tests/print_number.c | 11 ++++++++++- 8 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 tests/inputs/test7float create mode 100644 tests/inputs/test7float.expected diff --git a/cJSON.h b/cJSON.h index fa0372a..6279654 100644 --- a/cJSON.h +++ b/cJSON.h @@ -100,9 +100,10 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define cJSON_StringIsConst 512 #define notCJSON_INT_USE_LONGLONG -// Note: Default cmake rules will force C89 and prevent long long. -// To use long long, delete build tree, then run: -// CFLAGS="-Wall -Werror" cmake -DENABLE_CUSTOM_COMPILER_FLAGS=Off +/* Note: Default cmake rules will force C89 and prevent long long. + To use long long, delete build tree, then run: + CFLAGS="-Wall -Werror" cmake -DENABLE_CUSTOM_COMPILER_FLAGS=Off + */ #ifdef CJSON_INT_USE_LONGLONG typedef long long cJSON_int; diff --git a/tests/compare_tests.c b/tests/compare_tests.c index 797c774..8db00ac 100644 --- a/tests/compare_tests.c +++ b/tests/compare_tests.c @@ -64,9 +64,14 @@ static void cjson_compare_should_compare_numbers(void) TEST_ASSERT_TRUE(compare_from_string("1", "1", false)); TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", true)); TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", false)); - TEST_ASSERT_TRUE(compare_from_string("1E100", "10E99", false)); + TEST_ASSERT_TRUE(compare_from_string("1E20", "10E19", false)); + TEST_ASSERT_FALSE(compare_from_string("0.5E-20", "0.5E-21", false)); + +#ifndef CJSON_FLOAT_USE_FLOAT + TEST_ASSERT_TRUE(compare_from_string("1E100", "10E99", false)); TEST_ASSERT_FALSE(compare_from_string("0.5E-100", "0.5E-101", false)); +#endif TEST_ASSERT_FALSE(compare_from_string("1", "2", true)); TEST_ASSERT_FALSE(compare_from_string("1", "2", false)); diff --git a/tests/inputs/test7float b/tests/inputs/test7float new file mode 100644 index 0000000..1f6c99a --- /dev/null +++ b/tests/inputs/test7float @@ -0,0 +1,22 @@ +[ + { + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.395, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, + { + "precision": "zip", + "Latitude": 37.3719, + "Longitude": -122.0260, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + } + ] diff --git a/tests/inputs/test7float.expected b/tests/inputs/test7float.expected new file mode 100644 index 0000000..e3cf6fa --- /dev/null +++ b/tests/inputs/test7float.expected @@ -0,0 +1,19 @@ +[{ + "precision": "zip", + "Latitude": 37.7668, + "Longitude": -122.395, + "Address": "", + "City": "SAN FRANCISCO", + "State": "CA", + "Zip": "94107", + "Country": "US" + }, { + "precision": "zip", + "Latitude": 37.3719, + "Longitude": -122.026, + "Address": "", + "City": "SUNNYVALE", + "State": "CA", + "Zip": "94085", + "Country": "US" + }] \ No newline at end of file diff --git a/tests/misc_tests.c b/tests/misc_tests.c index 3080dee..b375847 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -28,6 +28,11 @@ #include "unity/src/unity.h" #include "common.h" +#ifdef CJSON_FLOAT_USE_FLOAT +#undef TEST_ASSERT_EQUAL_DOUBLE +#define TEST_ASSERT_EQUAL_DOUBLE TEST_ASSERT_EQUAL_FLOAT +#endif + static void cjson_array_foreach_should_loop_over_arrays(void) { cJSON array[1]; diff --git a/tests/parse_examples.c b/tests/parse_examples.c index 95a0959..402b225 100644 --- a/tests/parse_examples.c +++ b/tests/parse_examples.c @@ -156,7 +156,11 @@ static void file_test6_should_not_be_parsed(void) static void file_test7_should_be_parsed_and_printed(void) { +#ifdef CJSON_FLOAT_USE_FLOAT + do_test("test7float"); +#else do_test("test7"); +#endif } static void file_test8_should_be_parsed_and_printed(void) diff --git a/tests/parse_number.c b/tests/parse_number.c index 502f10b..d0d04f5 100644 --- a/tests/parse_number.c +++ b/tests/parse_number.c @@ -43,7 +43,7 @@ static void assert_is_number(cJSON *number_item) assert_has_no_string(number_item); } -static void assert_parse_number(const char *string, cJSON_int integer, double real) +static void assert_parse_number(const char *string, cJSON_int integer, cJSON_float real) { parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; buffer.content = (const unsigned char*)string; @@ -56,7 +56,11 @@ static void assert_parse_number(const char *string, cJSON_int integer, double re #else TEST_ASSERT_EQUAL_INT(integer, item->valueint); #endif +#ifdef CJSON_FLOAT_USE_FLOAT + TEST_ASSERT_EQUAL_FLOAT(real, item->valuedouble); +#else TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); +#endif } static void parse_number_should_parse_zero(void) @@ -96,11 +100,15 @@ static void parse_number_should_parse_positive_reals(void) assert_parse_number("10e-10", 0, 10e-10); assert_parse_number("10E-10", 0, 10e-10); #ifdef CJSON_INT_USE_LONGLONG +#ifndef CJSON_FLOAT_USE_FLOAT assert_parse_number("10e10", 100000000000LL, 10e10); +#endif #endif assert_parse_number("10e20", CJSON_INT_MAX, 10e20); +#ifndef CJSON_FLOAT_USE_FLOAT assert_parse_number("123e+127", CJSON_INT_MAX, 123e127); assert_parse_number("123e-128", 0, 123e-128); +#endif } static void parse_number_should_parse_negative_reals(void) @@ -109,11 +117,15 @@ static void parse_number_should_parse_negative_reals(void) assert_parse_number("-10e-10", 0, -10e-10); assert_parse_number("-10E-10", 0, -10e-10); #ifdef CJSON_INT_USE_LONGLONG +#ifndef CJSON_FLOAT_USE_FLOAT assert_parse_number("-10e10", -100000000000LL, -10e10); +#endif #endif assert_parse_number("-10e20", CJSON_INT_MIN, -10e20); +#ifndef CJSON_FLOAT_USE_FLOAT assert_parse_number("-123e+127", CJSON_INT_MIN, -123e127); assert_parse_number("-123e-128", 0, -123e-128); +#endif } int CJSON_CDECL main(void) diff --git a/tests/print_number.c b/tests/print_number.c index 2ebca03..617bed1 100644 --- a/tests/print_number.c +++ b/tests/print_number.c @@ -24,7 +24,7 @@ #include "unity/src/unity.h" #include "common.h" -static void assert_print_number(const char *expected, double input) +static void assert_print_number(const char *expected, cJSON_float input) { unsigned char printed[1024]; unsigned char new_buffer[26]; @@ -71,34 +71,41 @@ static void print_number_should_print_negative_integers(void) { assert_print_number("-1", -1.0); assert_print_number("-32768", -32768.0); +#ifndef CJSON_FLOAT_USE_FLOAT assert_print_number("-2147483648", -2147483648.0); assert_print_number("-2147483649", -2147483649.0); assert_print_number("-4294967296", -4294967296.0); assert_print_number("-4294967297", -4294967297.0); /* Approx lowest integer exactly representable in double */ assert_print_number("-8765432101234567", -8765432101234567.0); +#endif } static void print_number_should_print_positive_integers(void) { assert_print_number("1", 1.0); assert_print_number("32767", 32767.0); +#ifndef CJSON_FLOAT_USE_FLOAT assert_print_number("2147483647", 2147483647.0); assert_print_number("2147483648", 2147483648.0); assert_print_number("4294967295", 4294967295.0); assert_print_number("4294967296", 4294967296.0); /* Approx highest integer exactly representable in double */ assert_print_number("8765432101234567", 8765432101234567.0); +#endif } static void print_number_should_print_positive_reals(void) { assert_print_number("0.123", 0.123); assert_print_number("1e-09", 10e-10); + assert_print_number("1e+21", 10e20); +#ifndef CJSON_FLOAT_USE_FLOAT assert_print_number("1000000000000", 10e11); assert_print_number("1.23e+129", 123e+127); assert_print_number("1.23e-126", 123e-128); assert_print_number("3.1415926535897931", 3.1415926535897931); +#endif } static void print_number_should_print_negative_reals(void) @@ -106,8 +113,10 @@ static void print_number_should_print_negative_reals(void) assert_print_number("-0.0123", -0.0123); assert_print_number("-1e-09", -10e-10); assert_print_number("-1e+21", -10e20); +#ifndef CJSON_FLOAT_USE_FLOAT assert_print_number("-1.23e+129", -123e+127); assert_print_number("-1.23e-126", -123e-128); +#endif } static void print_number_should_print_non_number(void) From e79376f2d9c08a0d15268f480c9ae9fe42366b97 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Fri, 29 Apr 2022 23:11:06 +0200 Subject: [PATCH 07/14] Make integers be first-class citizens, including full precision in longlong+float case --- cJSON.c | 33 ++++++++++++++++++---------- cJSON.h | 28 +++++++++++++++--------- tests/parse_number.c | 14 +++++++++--- tests/print_number.c | 52 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 97 insertions(+), 30 deletions(-) diff --git a/cJSON.c b/cJSON.c index 6f5fa73..57b3166 100644 --- a/cJSON.c +++ b/cJSON.c @@ -105,7 +105,7 @@ #ifdef _WIN32 #define NAN sqrt(-1.0) #else -#define NAN 0.0/0.0 +#define NAN (0.0/0.0) #endif #endif @@ -140,6 +140,16 @@ CJSON_PUBLIC(cJSON_float) cJSON_GetNumberValue(const cJSON * const item) return item->valuedouble; } +CJSON_PUBLIC(cJSON_int) cJSON_GetIntValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return 0; + } + + return item->valueint; +} + /* 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 != 15) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. @@ -387,6 +397,7 @@ loop_end: } item->valuedouble = number; + item->type = cJSON_Number; /* use saturation in case of overflow */ /* note that even float has range beyond long long (though inexactly) */ @@ -402,14 +413,13 @@ loop_end: { /* integer is in range, parse as integer to prevent float inexactness */ item->valueint = strtoint(number_c_string); + item->type |= cJSON_PreferInt; } else { item->valueint = (cJSON_int)number; } - item->type = cJSON_Number; - input_buffer->offset += (size_t)(after_end - number_c_string); return true; } @@ -429,7 +439,7 @@ CJSON_PUBLIC(cJSON_float) cJSON_SetNumberHelper(cJSON *object, cJSON_float numbe { object->valueint = (cJSON_int)number; } - + object->type &= ~cJSON_PreferInt; return object->valuedouble = number; } @@ -602,22 +612,21 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out return false; } - /* This checks for NaN and Infinity */ - if (isnan(d) || isinf(d)) - { - length = sprintf((char*)number_buffer, "null"); - } - else if (has_no_decimals(d) && item->valueint != CJSON_INT_MAX && - item->valueint != CJSON_INT_MIN && d == (cJSON_float)item->valueint) + if (item->type & cJSON_PreferInt) { length = sprintf((char*)number_buffer, intfmt, item->valueint); } + else if (isnan(d) || isinf(d)) + /* note that isnan/isinf will never work when compiling with -ffast-math */ + { + length = sprintf((char*)number_buffer, "null"); + } else { /* Try 15 (6) decimal places of precision to avoid nonsignificant nonzero digits */ length = sprintf((char*)number_buffer, floatfmt_shorter, d); - /* Check whether the original double can be recovered */ + /* Check whether the original value can be recovered */ test = strtofloat((char*)number_buffer, &test_endptr); if (test_endptr == NULL || test_endptr == (char *)number_buffer || *test_endptr != '\0' || !compare_cJSON_float((cJSON_float)test, d)) { diff --git a/cJSON.h b/cJSON.h index 6279654..7cdcf4f 100644 --- a/cJSON.h +++ b/cJSON.h @@ -28,6 +28,19 @@ extern "C" { #endif +/* CJSON_INT_USE_LONGLONG + Compile-time option to use "long long" instead of default "int". + Note: Default cmake rules will force C89 and prevent long long. + To use long long, delete build tree, then run: + CFLAGS="-Wall -Werror" cmake -DENABLE_CUSTOM_COMPILER_FLAGS=Off + */ +#define notCJSON_INT_USE_LONGLONG + +/* CJSON_FLOAT_USE_FLOAT + Compile-time option to use "float" instead of default "double". + */ +#define notCJSON_FLOAT_USE_FLOAT + #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) #define __WINDOWS__ #endif @@ -98,12 +111,7 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 - -#define notCJSON_INT_USE_LONGLONG -/* Note: Default cmake rules will force C89 and prevent long long. - To use long long, delete build tree, then run: - CFLAGS="-Wall -Werror" cmake -DENABLE_CUSTOM_COMPILER_FLAGS=Off - */ +#define cJSON_PreferInt 1024 #ifdef CJSON_INT_USE_LONGLONG typedef long long cJSON_int; @@ -111,8 +119,6 @@ typedef long long cJSON_int; typedef int cJSON_int; #endif -#define notCJSON_FLOAT_USE_FLOAT - #ifdef CJSON_FLOAT_USE_FLOAT typedef float cJSON_float; #else @@ -133,9 +139,10 @@ typedef struct cJSON /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; - /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + /* writing to valueint is DEPRECATED, use cJSON_SetIntValue instead */ cJSON_int valueint; /* The item's number, if type==cJSON_Number */ + /* writing to valuedouble is DEPRECATED, use cJSON_SetNumberValue instead */ cJSON_float valuedouble; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ @@ -198,6 +205,7 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); CJSON_PUBLIC(cJSON_float) cJSON_GetNumberValue(const cJSON * const item); +CJSON_PUBLIC(cJSON_int) cJSON_GetIntValue(const cJSON * const item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); @@ -293,7 +301,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object, number) ((object) ? ((object)->valuedouble = (number), (object)->valueint = (number)) : (number)) +#define cJSON_SetIntValue(object, number) ((object != NULL) ? ((object)->valuedouble = (number), (object)->type |= cJSON_PreferInt, (object)->valueint = (number)) : (number)) /* helper for the cJSON_SetNumberValue macro */ CJSON_PUBLIC(cJSON_float) cJSON_SetNumberHelper(cJSON *object, cJSON_float number); #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (cJSON_float)number) : (number)) diff --git a/tests/parse_number.c b/tests/parse_number.c index d0d04f5..14bc431 100644 --- a/tests/parse_number.c +++ b/tests/parse_number.c @@ -73,8 +73,13 @@ static void parse_number_should_parse_zero(void) static void parse_number_should_parse_negative_integers(void) { assert_parse_number("-1", -1, -1); - assert_parse_number("-32768", -32768, -32768.0); - assert_parse_number("-2147483648", -2147483648, -2147483648.0); + + /* not -32768: C allows int as 15bit + signbit, or one's complement */ + assert_parse_number("-32767", -32767, -32767.0); + + if (sizeof(cJSON_int) >= 4) + assert_parse_number("-2147483648", -2147483648, -2147483648.0); + #ifdef CJSON_INT_USE_LONGLONG assert_parse_number("-8765432101234567", -8765432101234567LL, -8765432101234567.0); #else @@ -86,7 +91,10 @@ static void parse_number_should_parse_positive_integers(void) { assert_parse_number("1", 1, 1); assert_parse_number("32767", 32767, 32767.0); - assert_parse_number("2147483647", 2147483647, 2147483647.0); + + if (sizeof(cJSON_int) >= 4) + assert_parse_number("2147483647", 2147483647, 2147483647.0); + #ifdef CJSON_INT_USE_LONGLONG assert_parse_number("8765432101234567", 8765432101234567LL, 8765432101234567.0); #else diff --git a/tests/print_number.c b/tests/print_number.c index 617bed1..ae76745 100644 --- a/tests/print_number.c +++ b/tests/print_number.c @@ -62,9 +62,30 @@ static void assert_print_number(const char *expected, cJSON_float input) TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected."); } +static void assert_print_integer(const char *expected, cJSON_int input) +{ + unsigned char printed[1024]; + unsigned char new_buffer[26]; + cJSON item[1]; + printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.buffer = printed; + buffer.length = sizeof(printed); + buffer.offset = 0; + buffer.noalloc = true; + buffer.hooks = global_hooks; + buffer.buffer = new_buffer; + + memset(item, 0, sizeof(item)); + memset(new_buffer, 0, sizeof(new_buffer)); + cJSON_SetIntValue(item, input); + TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer), "Failed to print integer."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed integer is not as expected."); +} + static void print_number_should_print_zero(void) { assert_print_number("0", 0); + assert_print_integer("0", 0); } static void print_number_should_print_negative_integers(void) @@ -79,6 +100,18 @@ static void print_number_should_print_negative_integers(void) /* Approx lowest integer exactly representable in double */ assert_print_number("-8765432101234567", -8765432101234567.0); #endif + + assert_print_integer("-1", -1); + + /* not -32768: C allows int as 15bit + signbit, or one's complement */ + assert_print_integer("-32767", -32767); + + if (sizeof(cJSON_int) >= 4) + assert_print_integer("-2147483647", -2147483647); + +#ifdef CJSON_INT_USE_LONGLONG + assert_print_integer("-9223372036854775807", -9223372036854775807LL); +#endif } static void print_number_should_print_positive_integers(void) @@ -93,6 +126,16 @@ static void print_number_should_print_positive_integers(void) /* Approx highest integer exactly representable in double */ assert_print_number("8765432101234567", 8765432101234567.0); #endif + + assert_print_integer("1", 1); + assert_print_integer("32767", 32767); + + if (sizeof(cJSON_int) >= 4) + assert_print_integer("2147483647", 2147483647); + +#ifdef CJSON_INT_USE_LONGLONG + assert_print_integer("9223372036854775807", 9223372036854775807LL); +#endif } static void print_number_should_print_positive_reals(void) @@ -121,11 +164,10 @@ static void print_number_should_print_negative_reals(void) static void print_number_should_print_non_number(void) { - TEST_IGNORE(); - /* FIXME: Cannot test this easily in C89! */ - /* assert_print_number("null", NaN); */ - /* assert_print_number("null", INFTY); */ - /* assert_print_number("null", -INFTY); */ + assert_print_number("null", 0.0/0.0); + assert_print_number("null", -(0.0/0.0)); + assert_print_number("null", 1.0/0.0); + assert_print_number("null", (-1.0)/0.0); } int CJSON_CDECL main(void) From 4c2b3d8949a939e0520e22354aeb106611ea0446 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Sun, 1 May 2022 13:33:21 +0200 Subject: [PATCH 08/14] Fix float comparison also in cJSON_Utils --- cJSON.c | 8 ++++---- cJSON_Utils.c | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cJSON.c b/cJSON.c index 57b3166..e82d605 100644 --- a/cJSON.c +++ b/cJSON.c @@ -582,15 +582,15 @@ static void update_offset(printbuffer * const buffer) /* securely comparison of floating-point variables */ #ifdef CJSON_FLOAT_USE_FLOAT -static cJSON_bool compare_cJSON_float(float a, float b) +static cJSON_bool compare_cJSON_float(cJSON_float a, cJSON_float b) { - float maxVal = fabsf(a) > fabsf(b) ? fabsf(a) : fabsf(b); + cJSON_float maxVal = fabsf(a) > fabsf(b) ? fabsf(a) : fabsf(b); return (fabsf(a - b) <= maxVal * FLT_EPSILON); } #else -static cJSON_bool compare_cJSON_float(double a, double b) +static cJSON_bool compare_cJSON_float(cJSON_float a, cJSON_float b) { - double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + cJSON_float maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } #endif diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 63651df..8f3ac2a 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -109,12 +109,19 @@ static int compare_strings(const unsigned char *string1, const unsigned char *st } /* securely comparison of floating-point variables */ -static cJSON_bool compare_double(double a, double b) +#ifdef CJSON_FLOAT_USE_FLOAT +static cJSON_bool compare_cJSON_float(cJSON_float a, cJSON_float b) { - double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + cJSON_float maxVal = fabsf(a) > fabsf(b) ? fabsf(a) : fabsf(b); + return (fabsf(a - b) <= maxVal * FLT_EPSILON); +} +#else +static cJSON_bool compare_cJSON_float(cJSON_float a, cJSON_float b) +{ + cJSON_float maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } - +#endif /* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */ static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive) @@ -612,7 +619,7 @@ static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensiti { case cJSON_Number: /* numeric mismatch. */ - if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble))) + if ((a->valueint != b->valueint) || (!compare_cJSON_float(a->valuedouble, b->valuedouble))) { return false; } @@ -1154,7 +1161,7 @@ static void create_patches(cJSON * const patches, const unsigned char * const pa switch (from->type & 0xFF) { case cJSON_Number: - if ((from->valueint != to->valueint) || !compare_double(from->valuedouble, to->valuedouble)) + if ((from->valueint != to->valueint) || !compare_cJSON_float(from->valuedouble, to->valuedouble)) { compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); } From 1208b35e037d5f847046893d17f65f2cd90816a0 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Sun, 1 May 2022 20:22:38 +0200 Subject: [PATCH 09/14] Add cJSON_AddItemReferenceToObjectCS constant-string variant This can not be implemented externally, since create_reference() is not exposed. --- cJSON.c | 10 ++++++++++ cJSON.h | 1 + tests/misc_tests.c | 3 +++ 3 files changed, 14 insertions(+) diff --git a/cJSON.c b/cJSON.c index e82d605..391ae7e 100644 --- a/cJSON.c +++ b/cJSON.c @@ -2132,6 +2132,16 @@ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const cha return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, true); +} + CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) { cJSON *null = cJSON_CreateNull(); diff --git a/cJSON.h b/cJSON.h index 7cdcf4f..c38827f 100644 --- a/cJSON.h +++ b/cJSON.h @@ -257,6 +257,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *stri /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObjectCS(cJSON *object, const char *string, cJSON *item); /* Remove/Detach items from Arrays/Objects. */ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); diff --git a/tests/misc_tests.c b/tests/misc_tests.c index b375847..979770f 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -404,6 +404,9 @@ static void cjson_functions_should_not_crash_with_null_pointers(void) cJSON_AddItemReferenceToObject(item, "item", NULL); cJSON_AddItemReferenceToObject(item, NULL, item); cJSON_AddItemReferenceToObject(NULL, "item", item); + cJSON_AddItemReferenceToObjectCS(item, "item", NULL); + cJSON_AddItemReferenceToObjectCS(item, NULL, item); + cJSON_AddItemReferenceToObjectCS(NULL, "item", item); TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(NULL, item)); TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(item, NULL)); TEST_ASSERT_NULL(cJSON_DetachItemFromArray(NULL, 0)); From 4bafe51b12348a28320494eb36514434f5108fe9 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Tue, 24 May 2022 20:36:24 +0200 Subject: [PATCH 10/14] Add cJSON_IsInt() --- cJSON.c | 10 ++++++++++ cJSON.h | 1 + tests/misc_tests.c | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/cJSON.c b/cJSON.c index 391ae7e..a1abf5c 100644 --- a/cJSON.c +++ b/cJSON.c @@ -3007,6 +3007,16 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) return (item->type & 0xFF) == cJSON_Number; } +CJSON_PUBLIC(cJSON_bool) cJSON_IsInt(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return ((item->type & 0xFF) == cJSON_Number) && (item->type & cJSON_PreferInt); +} + CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) { if (item == NULL) diff --git a/cJSON.h b/cJSON.h index c38827f..24e9c15 100644 --- a/cJSON.h +++ b/cJSON.h @@ -214,6 +214,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsInt(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); diff --git a/tests/misc_tests.c b/tests/misc_tests.c index 979770f..df8e242 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -188,6 +188,12 @@ static void typecheck_functions_should_check_type(void) TEST_ASSERT_FALSE(cJSON_IsNumber(NULL)); TEST_ASSERT_FALSE(cJSON_IsNumber(invalid)); TEST_ASSERT_TRUE(cJSON_IsNumber(item)); + TEST_ASSERT_FALSE(cJSON_IsInt(item)); + + item->type = cJSON_Number | cJSON_StringIsConst | cJSON_PreferInt; + TEST_ASSERT_FALSE(cJSON_IsInt(NULL)); + TEST_ASSERT_FALSE(cJSON_IsInt(invalid)); + TEST_ASSERT_TRUE(cJSON_IsInt(item)); item->type = cJSON_String | cJSON_StringIsConst; TEST_ASSERT_FALSE(cJSON_IsString(NULL)); @@ -381,6 +387,7 @@ static void cjson_functions_should_not_crash_with_null_pointers(void) TEST_ASSERT_FALSE(cJSON_IsBool(NULL)); TEST_ASSERT_FALSE(cJSON_IsNull(NULL)); TEST_ASSERT_FALSE(cJSON_IsNumber(NULL)); + TEST_ASSERT_FALSE(cJSON_IsInt(NULL)); TEST_ASSERT_FALSE(cJSON_IsString(NULL)); TEST_ASSERT_FALSE(cJSON_IsArray(NULL)); TEST_ASSERT_FALSE(cJSON_IsObject(NULL)); From a5961c66132e6bd2083feb1cbb9a860bf2cb0701 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Tue, 24 May 2022 21:32:04 +0200 Subject: [PATCH 11/14] Update README.md for integer vs floating point --- README.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ebd32c4..7f1808c 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Ultralightweight JSON parser in ANSI C. * [Character Encoding](#character-encoding) * [C Standard](#c-standard) * [Floating Point Numbers](#floating-point-numbers) + * [Integer numbers](#integer-numbers) * [Deep Nesting Of Arrays And Objects](#deep-nesting-of-arrays-and-objects) * [Thread Safety](#thread-safety) * [Case Sensitivity](#case-sensitivity) @@ -179,9 +180,10 @@ typedef struct cJSON struct cJSON *child; int type; char *valuestring; - /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ - int valueint; - double valuedouble; + /* writing to valueint is DEPRECATED, use cJSON_SetIntValue instead */ + cJSON_int valueint; + /* writing to valuedouble is DEPRECATED, use cJSON_SetNumberValue instead */ + cJSON_float valuedouble; char *string; } cJSON; ``` @@ -196,7 +198,8 @@ The type can be one of the following: * `cJSON_False` (check with `cJSON_IsFalse`): Represents a `false` boolean value. You can also check for boolean values in general with `cJSON_IsBool`. * `cJSON_True` (check with `cJSON_IsTrue`): Represents a `true` boolean value. You can also check for boolean values in general with `cJSON_IsBool`. * `cJSON_NULL` (check with `cJSON_IsNull`): Represents a `null` value. -* `cJSON_Number` (check with `cJSON_IsNumber`): Represents a number value. The value is stored as a double in `valuedouble` and also in `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`. +* `cJSON_Number` (check with `cJSON_IsNumber`): Represents a number value, which may be integer. The value is stored in `valuedouble`, and also in `valueint` with possibly some loss of precision. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`. +* `cJSON_Number` with `cJSON_PreferInt` flag (check with `cJSON_IsInt`): Represents an integer number value that is within the range representable by `valueint`. The value is stored in `valueint`, and also in `valuedouble` with possibly some loss of precision. * `cJSON_String` (check with `cJSON_IsString`): Represents a string value. It is stored in the form of a zero terminated string in `valuestring`. * `cJSON_Array` (check with `cJSON_IsArray`): Represent an array value. This is implemented by pointing `child` to a linked list of `cJSON` items that represent the values in the array. The elements are linked together using `next` and `prev`, where the first element has `prev.next == NULL` and the last element `next == NULL`. * `cJSON_Object` (check with `cJSON_IsObject`): Represents an object value. Objects are stored same way as an array, the only difference is that the items in the object store their keys in `string`. @@ -219,7 +222,8 @@ it gets deleted as well. You also could use `cJSON_SetValuestring` to change a ` * **null** is created with `cJSON_CreateNull` * **booleans** are created with `cJSON_CreateTrue`, `cJSON_CreateFalse` or `cJSON_CreateBool` -* **numbers** are created with `cJSON_CreateNumber`. This will set both `valuedouble` and `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint` +* (floating point) **numbers** are created with `cJSON_CreateNumber`. This will set both `valuedouble` and `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` will be used for `valueint` +* **integer numbers** are created with `cJSON_CreateInt`. This will set both `valuedouble` and `valueint`, and will remember that the value represents an integer number. * **strings** are created with `cJSON_CreateString` (copies the string) or with `cJSON_CreateStringReference` (directly points to the string. This means that `valuestring` won't be deleted by `cJSON_Delete` and you are responsible for its lifetime, useful for constants) #### Arrays @@ -543,6 +547,16 @@ cJSON does not officially support any `double` implementations other than IEEE75 The maximum length of a floating point literal that cJSON supports is currently 63 characters. +The floating point type defaults to `double`, and can be switched to `float` by defining `CJSON_FLOAT_USE_FLOAT` in `cJSON.h`. + +#### Integer numbers + +While the JSON standard only specifies floating point numbers, cJSON also supports really-integer numbers when explicitly created as such. Also during parsing, integer numbers are recognized when a value does not contain a decimal digit or exponential notation, and is exactly representable by the integer data type. In some cases, the integer data type may be able to exactly represent larger integer values, where the floating point data type would lose precision. + +cJSON number items can be accessed using either the floating point or the integer function calls. Especially **writing** to the `valuedouble` or `valueint` members is deprecated, since writing to either one will not update the other, and will not update the integer representation flag. + +The integer type defaults to `int`, and can be switched to `long long` by defining `CJSON_INT_USE_LONGLONG` in `cJSON.h`. + #### Deep Nesting Of Arrays And Objects cJSON doesn't support arrays and objects that are nested too deeply because this would result in a stack overflow. To prevent this cJSON limits the depth to `CJSON_NESTING_LIMIT` which is 1000 by default but can be changed at compile time. From bf7a73fa652561b302e857ab21a6c6bbc68e661c Mon Sep 17 00:00:00 2001 From: No Default Name Date: Tue, 24 May 2022 21:42:03 +0200 Subject: [PATCH 12/14] README.md: add compile details when using long long type --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7f1808c..c20ac4d 100644 --- a/README.md +++ b/README.md @@ -555,7 +555,13 @@ While the JSON standard only specifies floating point numbers, cJSON also suppor cJSON number items can be accessed using either the floating point or the integer function calls. Especially **writing** to the `valuedouble` or `valueint` members is deprecated, since writing to either one will not update the other, and will not update the integer representation flag. -The integer type defaults to `int`, and can be switched to `long long` by defining `CJSON_INT_USE_LONGLONG` in `cJSON.h`. +The integer type defaults to `int`, and can be switched to `long long` by defining `CJSON_INT_USE_LONGLONG` in `cJSON.h`. Since `long long` is not a C89 data type, a default cmake compile will fail. Either remove `-std=c89` from `CMakeLists.txt`, or force compiler flags e.g.: + +``` +mkdir build +cd build +CFLAGS="-Wall -Werror" cmake -DENABLE_CUSTOM_COMPILER_FLAGS=Off .. +``` #### Deep Nesting Of Arrays And Objects From 2ff0894010dc68a8e675aa1ddc3de321545f1094 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Tue, 24 May 2022 21:57:11 +0200 Subject: [PATCH 13/14] Add cJSON_AddIntToObject() --- cJSON.c | 36 ++++++++++++++++++++++++------------ cJSON.h | 3 ++- tests/cjson_add.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/cJSON.c b/cJSON.c index a1abf5c..ec8bc3a 100644 --- a/cJSON.c +++ b/cJSON.c @@ -2202,6 +2202,18 @@ CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * return NULL; } +CJSON_PUBLIC(cJSON*) cJSON_AddIntToObject(cJSON * const object, const char * const name, const cJSON_int number) +{ + cJSON *number_item = cJSON_CreateInt(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) { cJSON *string_item = cJSON_CreateString(string); @@ -2492,18 +2504,6 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num) -{ - cJSON *item = cJSON_New_Item(&global_hooks); - if(item) - { - item->type = cJSON_Number; - cJSON_SetIntValue(item, num); - } - - return item; -} - CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(cJSON_float num) { cJSON *item = cJSON_New_Item(&global_hooks); @@ -2516,6 +2516,18 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(cJSON_float num) return item; } +CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + cJSON_SetIntValue(item, num); + } + + return item; +} + CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { cJSON *item = cJSON_New_Item(&global_hooks); diff --git a/cJSON.h b/cJSON.h index 24e9c15..1f1b679 100644 --- a/cJSON.h +++ b/cJSON.h @@ -225,8 +225,8 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); -CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num); CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(cJSON_float num); +CJSON_PUBLIC(cJSON *) cJSON_CreateInt(cJSON_int num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); @@ -297,6 +297,7 @@ CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * co CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const cJSON_float number); +CJSON_PUBLIC(cJSON*) cJSON_AddIntToObject(cJSON * const object, const char * const name, const cJSON_int number); CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); diff --git a/tests/cjson_add.c b/tests/cjson_add.c index b02f1e2..bc7bf59 100644 --- a/tests/cjson_add.c +++ b/tests/cjson_add.c @@ -278,6 +278,45 @@ static void cjson_add_number_should_fail_on_allocation_failure(void) cJSON_Delete(root); } +static void cjson_add_int_should_add_integer(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *number = NULL; + + cJSON_AddIntToObject(root, "number", 42); + + TEST_ASSERT_NOT_NULL(number = cJSON_GetObjectItemCaseSensitive(root, "number")); + + TEST_ASSERT_EQUAL_INT(number->type, cJSON_Number | cJSON_PreferInt); + TEST_ASSERT_EQUAL_DOUBLE(number->valuedouble, 42); + TEST_ASSERT_EQUAL_INT(number->valueint, 42); + + cJSON_Delete(root); +} + +static void cjson_add_int_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddIntToObject(NULL, "number", 42)); + TEST_ASSERT_NULL(cJSON_AddIntToObject(root, NULL, 42)); + + cJSON_Delete(root); +} + +static void cjson_add_int_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddIntToObject(root, "number", 42)); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + static void cjson_add_string_should_add_string(void) { cJSON *root = cJSON_CreateObject(); @@ -451,6 +490,10 @@ int CJSON_CDECL main(void) RUN_TEST(cjson_add_number_should_fail_with_null_pointers); RUN_TEST(cjson_add_number_should_fail_on_allocation_failure); + RUN_TEST(cjson_add_int_should_add_integer); + RUN_TEST(cjson_add_int_should_fail_with_null_pointers); + RUN_TEST(cjson_add_int_should_fail_on_allocation_failure); + RUN_TEST(cjson_add_string_should_add_string); RUN_TEST(cjson_add_string_should_fail_with_null_pointers); RUN_TEST(cjson_add_string_should_fail_on_allocation_failure); From c4429d580ec51b501ee773f42d0f3d6d6c35ffe1 Mon Sep 17 00:00:00 2001 From: No Default Name Date: Tue, 24 May 2022 22:23:46 +0200 Subject: [PATCH 14/14] Revert cJSON_AddItemReferenceToObjectCS to keep branch clean This reverts commit 1208b35e037d5f847046893d17f65f2cd90816a0. --- cJSON.c | 10 ---------- cJSON.h | 1 - tests/misc_tests.c | 3 --- 3 files changed, 14 deletions(-) diff --git a/cJSON.c b/cJSON.c index ec8bc3a..f626b57 100644 --- a/cJSON.c +++ b/cJSON.c @@ -2132,16 +2132,6 @@ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const cha return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); } -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObjectCS(cJSON *object, const char *string, cJSON *item) -{ - if ((object == NULL) || (string == NULL)) - { - return false; - } - - return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, true); -} - CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) { cJSON *null = cJSON_CreateNull(); diff --git a/cJSON.h b/cJSON.h index 1f1b679..351a22e 100644 --- a/cJSON.h +++ b/cJSON.h @@ -258,7 +258,6 @@ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *stri /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); -CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObjectCS(cJSON *object, const char *string, cJSON *item); /* Remove/Detach items from Arrays/Objects. */ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); diff --git a/tests/misc_tests.c b/tests/misc_tests.c index df8e242..d8556fd 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -411,9 +411,6 @@ static void cjson_functions_should_not_crash_with_null_pointers(void) cJSON_AddItemReferenceToObject(item, "item", NULL); cJSON_AddItemReferenceToObject(item, NULL, item); cJSON_AddItemReferenceToObject(NULL, "item", item); - cJSON_AddItemReferenceToObjectCS(item, "item", NULL); - cJSON_AddItemReferenceToObjectCS(item, NULL, item); - cJSON_AddItemReferenceToObjectCS(NULL, "item", item); TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(NULL, item)); TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(item, NULL)); TEST_ASSERT_NULL(cJSON_DetachItemFromArray(NULL, 0));