long long fixes, enhancements, tests

This commit is contained in:
No Default Name 2022-04-24 20:13:29 +02:00
parent 586762189b
commit a1efa1e77f
4 changed files with 49 additions and 25 deletions

23
cJSON.c
View File

@ -70,7 +70,7 @@
#define false ((cJSON_bool)0) #define false ((cJSON_bool)0)
/* define our own int max and min */ /* 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_MAX LLONG_MAX
#define CJSON_INT_MIN LLONG_MIN #define CJSON_INT_MIN LLONG_MIN
#define strtoint(s) strtoll((const char*)(s), NULL, 0) #define strtoint(s) strtoll((const char*)(s), NULL, 0)
@ -82,7 +82,7 @@
#define intfmt "%d" #define intfmt "%d"
#endif #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 */ /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
#ifndef isinf #ifndef isinf
@ -320,7 +320,7 @@ typedef struct
static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
{ {
double number = 0; double number = 0;
cJSON_bool integer = cJSON_True; cJSON_bool is_integer = cJSON_True;
unsigned char *after_end = NULL; unsigned char *after_end = NULL;
unsigned char number_c_string[64]; unsigned char number_c_string[64];
unsigned char decimal_point = get_decimal_point(); 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':
case 'E': case 'E':
integer = cJSON_False; is_integer = cJSON_False;
number_c_string[i] = buffer_at_offset(input_buffer)[i]; number_c_string[i] = buffer_at_offset(input_buffer)[i];
break; break;
case '.': case '.':
integer = cJSON_False; is_integer = cJSON_False;
number_c_string[i] = decimal_point; number_c_string[i] = decimal_point;
break; break;
@ -380,7 +380,8 @@ loop_end:
item->valuedouble = number; item->valuedouble = number;
/* use saturation in case of overflow */ /* 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; item->valueint = CJSON_INT_MAX;
} }
@ -388,9 +389,9 @@ loop_end:
{ {
item->valueint = CJSON_INT_MIN; 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); item->valueint = strtoint(number_c_string);
} }
else else
@ -407,7 +408,7 @@ loop_end:
/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ /* 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(double) cJSON_SetNumberHelper(cJSON *object, double number)
{ {
if (number >= CJSON_INT_MAX) if (number >= (double)CJSON_INT_MAX)
{ {
object->valueint = 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"); length = sprintf((char*)number_buffer, "null");
} }
else if (isint(d) && ((item->valueint != CJSON_INT_MAX && 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 == (double)item->valueint)
{ {
length = sprintf((char*)number_buffer, intfmt, item->valueint); length = sprintf((char*)number_buffer, intfmt, item->valueint);
} }

View File

@ -100,8 +100,11 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ
#define cJSON_StringIsConst 512 #define cJSON_StringIsConst 512
#define notCJSON_INT_USE_LONGLONG #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 <sourcedir>
#if defined(CJSON_INT_USE_LONGLONG) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #ifdef CJSON_INT_USE_LONGLONG
typedef long long cJSON_int; typedef long long cJSON_int;
#else #else
typedef int cJSON_int; typedef int cJSON_int;

View File

@ -231,13 +231,13 @@ static void cjson_set_number_value_should_set_numbers(void)
TEST_ASSERT_EQUAL(-1, number->valueint); TEST_ASSERT_EQUAL(-1, number->valueint);
TEST_ASSERT_EQUAL_DOUBLE(-1.5, number->valuedouble); TEST_ASSERT_EQUAL_DOUBLE(-1.5, number->valuedouble);
cJSON_SetNumberValue(number, 1 + (double)INT_MAX); cJSON_SetNumberValue(number, 1.0 + (double)CJSON_INT_MAX);
TEST_ASSERT_EQUAL(INT_MAX, number->valueint); TEST_ASSERT_EQUAL(CJSON_INT_MAX, number->valueint);
TEST_ASSERT_EQUAL_DOUBLE(1 + (double)INT_MAX, number->valuedouble); TEST_ASSERT_EQUAL_DOUBLE(1.0 + (double)CJSON_INT_MAX, number->valuedouble);
cJSON_SetNumberValue(number, -1 + (double)INT_MIN); cJSON_SetNumberValue(number, -1.0 + (double)CJSON_INT_MIN);
TEST_ASSERT_EQUAL(INT_MIN, number->valueint); TEST_ASSERT_EQUAL(CJSON_INT_MIN, number->valueint);
TEST_ASSERT_EQUAL_DOUBLE(-1 + (double)INT_MIN, number->valuedouble); TEST_ASSERT_EQUAL_DOUBLE(-1.0 + (double)CJSON_INT_MIN, number->valuedouble);
} }
static void cjson_detach_item_via_pointer_should_detach_items(void) static void cjson_detach_item_via_pointer_should_detach_items(void)

View File

@ -43,7 +43,7 @@ static void assert_is_number(cJSON *number_item)
assert_has_no_string(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 } }; parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
buffer.content = (const unsigned char*)string; 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)); TEST_ASSERT_TRUE(parse_number(item, &buffer));
assert_is_number(item); assert_is_number(item);
#ifdef CJSON_INT_USE_LONGLONG
TEST_ASSERT_EQUAL_INT64(integer, item->valueint);
#else
TEST_ASSERT_EQUAL_INT(integer, item->valueint); TEST_ASSERT_EQUAL_INT(integer, item->valueint);
#endif
TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); 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("-1", -1, -1);
assert_parse_number("-32768", -32768, -32768.0); 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) static void parse_number_should_parse_positive_integers(void)
{ {
assert_parse_number("1", 1, 1); assert_parse_number("1", 1, 1);
assert_parse_number("32767", 32767, 32767.0); 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) 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("0.001", 0, 0.001);
assert_parse_number("10e-10", 0, 10e-10); assert_parse_number("10e-10", 0, 10e-10);
assert_parse_number("10E-10", 0, 10e-10); assert_parse_number("10E-10", 0, 10e-10);
assert_parse_number("10e10", INT_MAX, 10e10); #ifdef CJSON_INT_USE_LONGLONG
assert_parse_number("123e+127", INT_MAX, 123e127); 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); 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("-0.001", 0, -0.001);
assert_parse_number("-10e-10", 0, -10e-10); assert_parse_number("-10e-10", 0, -10e-10);
assert_parse_number("-10E-10", 0, -10e-10); assert_parse_number("-10E-10", 0, -10e-10);
assert_parse_number("-10e20", INT_MIN, -10e20); #ifdef CJSON_INT_USE_LONGLONG
assert_parse_number("-123e+127", INT_MIN, -123e127); 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); assert_parse_number("-123e-128", 0, -123e-128);
} }