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); }