mirror of
https://github.com/DaveGamble/cJSON.git
synced 2023-08-10 21:13:26 +03:00
Make integers be first-class citizens, including full precision in longlong+float case
This commit is contained in:
parent
882361c428
commit
e79376f2d9
33
cJSON.c
33
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))
|
||||
{
|
||||
|
28
cJSON.h
28
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 <sourcedir>
|
||||
*/
|
||||
#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 <sourcedir>
|
||||
*/
|
||||
#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))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user