From 87569a85870994ceb03ef5050b18d6f922a908e2 Mon Sep 17 00:00:00 2001 From: borne Date: Fri, 21 Jun 2024 11:50:00 +0800 Subject: [PATCH] feat: add int64 support Offering: Open Source Institute This commit adds int64 support for cjson. To enable int64, you need to define the macro ENABLE_INT64. To make it more clear, you need to build cjson like this if you are using makefile: mkdir build cd build cmake -DENABLE_INT64=ON .. make This implementation changed the type of valueint in struct cJSON: from int to long long(int64), and added a new flag cJSON_IsInt64. For a int64 cJSON item, the value of item->type would be cJSON_Number | cJSON_IsInt64. The existing functions parse_number and print_number can handle int64 now. Considering we have added a new type cJSON_IsInt64, some new functions have been added: CJSON_PUBLIC(long long *) cJSON_GetInt64NumberValue(cJSON * const item) CJSON_PUBLIC(cJSON_bool) cJSON_IsInt64Number(const cJSON * const item) CJSON_PUBLIC(cJSON *) cJSON_CreateInt64Number(long long integer) CJSON_PUBLIC(cJSON*) cJSON_AddInt64NumberToObject(cJSON * const object, const char * const name, const long long integer) CJSON_PUBLIC(long long) cJSON_SetInt64NumberValue(cJSON * const object, const long long integer) And some existing functions are also adjusted for int64, including parse_number, cJSON_SetNumberHelper, print_number, cJSON_CreateNumber, cJSON_Compare and compare_json(in cJSON_Utils). Besides the code changes, we have also added some testcases for int64. These tests will run if ENABLE_INT64 is turned on. Ref:PeterAlfredLee https://github.com/DaveGamble/cJSON/pull/689 Signed-off-by: borne --- CMakeLists.txt | 13 +- README.md | 1 + cJSON.c | 324 +++++++++++++++++++++++++++++++++++++++ cJSON.h | 28 ++++ cJSON_Utils.c | 17 ++ tests/CMakeLists.txt | 6 + tests/cjson_add.c | 47 ++++++ tests/compare_tests.c | 18 +++ tests/misc_int64_tests.c | 185 ++++++++++++++++++++++ tests/misc_tests.c | 10 ++ tests/parse_number.c | 52 +++++++ tests/print_number.c | 31 ++++ 12 files changed, 731 insertions(+), 1 deletion(-) create mode 100644 tests/misc_int64_tests.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f23ec63..0810aba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,12 +14,23 @@ set(CJSON_UTILS_VERSION_SO 1) set(custom_compiler_flags) +option(ENABLE_INT64 "Enable the use of int64(long long), please note this will use c99 instead of c89" OFF) +if (ENABLE_INT64) + add_definitions(-DENABLE_INT64) + list(APPEND custom_compiler_flags + -std=c99 + ) +else() + list(APPEND custom_compiler_flags + -std=c89 + ) +endif () + include(CheckCCompilerFlag) option(ENABLE_CUSTOM_COMPILER_FLAGS "Enables custom compiler flags" ON) if (ENABLE_CUSTOM_COMPILER_FLAGS) if (("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") OR ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")) list(APPEND custom_compiler_flags - -std=c89 -pedantic -Wall -Wextra diff --git a/README.md b/README.md index 99147af..9246e8b 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ You can change the build process with a list of different options that you can p * `-DENABLE_LOCALES=On`: Enable the usage of localeconv method. ( on by default ) * `-DCJSON_OVERRIDE_BUILD_SHARED_LIBS=On`: Enable overriding the value of `BUILD_SHARED_LIBS` with `-DCJSON_BUILD_SHARED_LIBS`. * `-DENABLE_CJSON_VERSION_SO`: Enable cJSON so version. ( on by default ) +* `-DENABLE_INT64`: Enable int64 support for cjson. Please note this will use c99 instead of c89. (off by default) If you are packaging cJSON for a distribution of Linux, you would probably take these steps for example: ``` diff --git a/cJSON.c b/cJSON.c index 8e62531..1293946 100644 --- a/cJSON.c +++ b/cJSON.c @@ -106,6 +106,18 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) return item->valuestring; } +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(long long *) cJSON_GetInt64NumberValue(cJSON * const item) +{ + if (!cJSON_IsInt64Number(item)) + { + return NULL; + } + + return &(item->valueint); +} +#endif + CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) { if (!cJSON_IsNumber(item)) @@ -301,6 +313,106 @@ typedef struct /* get a pointer to the buffer at the position */ #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) +#ifdef __CJSON_USE_INT64 +/* 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; + long long integer = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + cJSON_bool is_integer = true; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + is_integer = false; + break; + + case '.': + number_c_string[i] = decimal_point; + is_integer = false; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + /* use guard clause to process the int64 case first */ + if (is_integer) + { + /* for int64 values, set cJSON_IsInt64 */ + item->type = cJSON_Number | cJSON_IsInt64; + integer = strtoll((const char*)number_c_string, (char**)&after_end, 10); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + item->valueint = integer; + item->valuedouble = (double)integer; + goto parse_end; + } + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= (double)LLONG_MAX) + { + item->valueint = LLONG_MAX; + } + else if (number <= (double)LLONG_MIN) + { + item->valueint = LLONG_MIN; + } + else + { + item->valueint = (long long)number; + } + + item->type = cJSON_Number; + +parse_end: + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} +#else /* 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) { @@ -377,7 +489,49 @@ loop_end: input_buffer->offset += (size_t)(after_end - number_c_string); return true; } +#endif /* __CJSON_USE_INT64 */ +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(long long) cJSON_SetInt64NumberValue(cJSON * const object, const long long integer) +{ + if (object == NULL) + { + return integer; + } + + /* check the type before setting values */ + if (!(object->type & cJSON_Number) || !(object->type & cJSON_IsInt64)) + { + return integer; + } + + object->valueint = integer; + object->valuedouble = (double)integer; + + return integer; +} +#endif /* __CJSON_USE_INT64 */ + +#ifdef __CJSON_USE_INT64 +/* note that double max(DBL_MAX) is bigger than long long max(LLONG_MAX) */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= (double)LLONG_MAX) + { + object->valueint = LLONG_MAX; + } + else if (number <= (double)LLONG_MIN) + { + object->valueint = LLONG_MIN; + } + else + { + object->valueint = (long long)number; + } + + return object->valuedouble = number; +} +#else /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { @@ -396,6 +550,7 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) return object->valuedouble = number; } +#endif /* #ifdef __CJSON_USE_INT64 */ CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) { @@ -546,6 +701,82 @@ static cJSON_bool compare_double(double a, double b) return (fabs(a - b) <= maxVal * DBL_EPSILON); } +#ifdef __CJSON_USE_INT64 +/* 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; + long long integer = item->valueint; + 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; + + if (output_buffer == NULL) + { + return false; + } + + if (item->type & cJSON_IsInt64) + { + /* use lld to print the long long integer */ + length = sprintf((char*)number_buffer, "%lld", integer); + } + else /* item->type == cJSON_Number */ + { + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} +#else /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { @@ -615,6 +846,7 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out return true; } +#endif /* __CJSON_USE_INT64 */ /* parse 4 digit hexadecimal number */ static unsigned parse_hex4(const unsigned char * const input) @@ -2134,6 +2366,20 @@ CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * co return NULL; } +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(cJSON*) cJSON_AddInt64NumberToObject(cJSON * const object, const char * const name, const long long integer) +{ + cJSON *int_item = cJSON_CreateInt64Number(integer); + if (add_item_to_object(object, name, int_item, &global_hooks, false)) + { + return int_item; + } + + cJSON_Delete(int_item); + return NULL; +} +#endif /* __CJSON_USE_INT64 */ + CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) { cJSON *number_item = cJSON_CreateNumber(number); @@ -2441,6 +2687,49 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) return item; } +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(cJSON *) cJSON_CreateInt64Number(long long integer) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number | cJSON_IsInt64; + item->valueint = integer; + item->valuedouble = (double)integer; + } + + return item; +} +#endif /* __CJSON_USE_INT64 */ + +#ifdef __CJSON_USE_INT64 +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 */ + /* note that double max(DBL_MAX) is bigger than long long max(LLONG_MAX) */ + if (num >= (double)LLONG_MAX) + { + item->valueint = LLONG_MAX; + } + else if (num <= (double)LLONG_MIN) + { + item->valueint = LLONG_MIN; + } + else + { + item->valueint = (long long)num; + } + } + + return item; +} +#else CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { cJSON *item = cJSON_New_Item(&global_hooks); @@ -2466,6 +2755,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) return item; } +#endif /* __CJSON_USE_INT64 */ CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { @@ -2948,6 +3238,18 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) return (item->type & 0xFF) == cJSON_NULL; } +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(cJSON_bool) cJSON_IsInt64Number(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return cJSON_IsNumber(item) && (item->type & cJSON_IsInt64); +} +#endif /* __CJSON_USE_INT64 */ + CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) { if (item == NULL) @@ -3036,12 +3338,34 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons case cJSON_NULL: return true; +#ifdef __CJSON_USE_INT64 + case cJSON_Number: + if (!compare_double(a->valuedouble, b->valuedouble)) + { + return false; + } + + if ((a->type & cJSON_IsInt64) != (b->type & cJSON_IsInt64)) + { + /* cJSON_IsInt64 should also be considered */ + return false; + } + + if ((a->type & cJSON_IsInt64) && (b->type & cJSON_IsInt64)) + { + /* compare valueint if both of them are int64 */ + return a->valueint == b->valueint; + } + + return true; +#else case cJSON_Number: if (compare_double(a->valuedouble, b->valuedouble)) { return true; } return false; +#endif /* __CJSON_USE_INT64 */ case cJSON_String: case cJSON_Raw: diff --git a/cJSON.h b/cJSON.h index 2628d76..23617dc 100644 --- a/cJSON.h +++ b/cJSON.h @@ -85,6 +85,11 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #include +/* use int64 if it is enabled and supported */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && defined(ENABLE_INT64) +#define __CJSON_USE_INT64 +#endif + /* cJSON Types: */ #define cJSON_Invalid (0) #define cJSON_False (1 << 0) @@ -98,6 +103,9 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 +#ifdef __CJSON_USE_INT64 +#define cJSON_IsInt64 1024 +#endif /* The cJSON structure: */ typedef struct cJSON @@ -113,8 +121,13 @@ typedef struct cJSON /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; +#ifdef __CJSON_USE_INT64 + /* use long long if int64 is enabled */ + long long valueint; +#else /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ int valueint; +#endif /* __CJSON_USE_INT64 */ /* The item's number, if type==cJSON_Number */ double valuedouble; @@ -177,6 +190,9 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); /* Check item type and return its value */ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(long long *) cJSON_GetInt64NumberValue(cJSON * const item); +#endif CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); /* These functions check the type of an item */ @@ -185,6 +201,9 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); 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); +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(cJSON_bool) cJSON_IsInt64Number(const cJSON * const item); +#endif CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); @@ -197,6 +216,9 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(cJSON *) cJSON_CreateInt64Number(long long integer); +#endif CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); @@ -265,12 +287,18 @@ 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); +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(cJSON*) cJSON_AddInt64NumberToObject(cJSON * const object, const char * const name, const long long integer); +#endif CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double 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); CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); +#ifdef __CJSON_USE_INT64 +CJSON_PUBLIC(long long) cJSON_SetInt64NumberValue(cJSON * const object, const long long integer); +#endif /* 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)) /* helper for the cJSON_SetNumberValue macro */ diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 63651df..58c3308 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -610,6 +610,22 @@ static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensiti } switch (a->type & 0xFF) { +#ifdef __CJSON_USE_INT64 + case cJSON_Number: + /* numeric mismatch. */ + if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble))) + { + return false; + } + + if ((a->type & cJSON_IsInt64) != (b->type & cJSON_IsInt64)) + { + /* cJSON_IsInt64 should also be considered */ + return false; + } + + return true; +#else case cJSON_Number: /* numeric mismatch. */ if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble))) @@ -620,6 +636,7 @@ static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensiti { return true; } +#endif /* __CJSON_USE_INT64 */ case cJSON_String: /* string mismatch. */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c759221..3fe254f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -60,6 +60,12 @@ if(ENABLE_CJSON_TEST) minify_tests ) + if (ENABLE_INT64) + add_definitions(-DUNITY_SUPPORT_64) + list(APPEND unity_tests + misc_int64_tests + ) + endif() option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.") if (ENABLE_VALGRIND) find_program(MEMORYCHECK_COMMAND valgrind) diff --git a/tests/cjson_add.c b/tests/cjson_add.c index b02f1e2..1a20cf0 100644 --- a/tests/cjson_add.c +++ b/tests/cjson_add.c @@ -278,6 +278,47 @@ static void cjson_add_number_should_fail_on_allocation_failure(void) cJSON_Delete(root); } +#ifdef __CJSON_USE_INT64 +static void cjson_add_int64_number_should_add_int64_number(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *number = NULL; + + cJSON_AddInt64NumberToObject(root, "number", 42LL); + + TEST_ASSERT_NOT_NULL(number = cJSON_GetObjectItemCaseSensitive(root, "number")); + + TEST_ASSERT_EQUAL_INT(number->type, cJSON_Number | cJSON_IsInt64); + TEST_ASSERT_EQUAL_DOUBLE(number->valuedouble, 42); + TEST_ASSERT_EQUAL_INT64(number->valueint, 42LL); + + cJSON_Delete(root); +} + +static void cjson_add_int64_number_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddInt64NumberToObject(NULL, "number", 42LL)); + TEST_ASSERT_NULL(cJSON_AddInt64NumberToObject(root, NULL, 42LL)); + + cJSON_Delete(root); +} + +static void cjson_add_int64_number_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddInt64NumberToObject(root, "number", 42LL)); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} +#endif /* __CJSON_USE_INT64 */ + static void cjson_add_string_should_add_string(void) { cJSON *root = cJSON_CreateObject(); @@ -451,6 +492,12 @@ 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); +#ifdef __CJSON_USE_INT64 + RUN_TEST(cjson_add_int64_number_should_add_int64_number); + RUN_TEST(cjson_add_int64_number_should_fail_with_null_pointers); + RUN_TEST(cjson_add_int64_number_should_fail_on_allocation_failure); +#endif /* __CJSON_USE_INT64 */ + 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); diff --git a/tests/compare_tests.c b/tests/compare_tests.c index 797c774..ca10a50 100644 --- a/tests/compare_tests.c +++ b/tests/compare_tests.c @@ -72,6 +72,21 @@ static void cjson_compare_should_compare_numbers(void) TEST_ASSERT_FALSE(compare_from_string("1", "2", false)); } +#ifdef __CJSON_USE_INT64 +static void cjson_compare_should_compare_int64_numbers(void) +{ + TEST_ASSERT_TRUE(compare_from_string("1LL", "1LL", true)); + TEST_ASSERT_TRUE(compare_from_string("1LL", "1LL", false)); + TEST_ASSERT_TRUE(compare_from_string("9223372036854775807LL", "9223372036854775807LL", true)); + TEST_ASSERT_TRUE(compare_from_string("9223372036854775807LL", "9223372036854775807LL", false)); + TEST_ASSERT_TRUE(compare_from_string("-9223372036854775808LL", "-9223372036854775808LL", true)); + TEST_ASSERT_TRUE(compare_from_string("-9223372036854775808LL", "-9223372036854775808LL", false)); + + TEST_ASSERT_FALSE(compare_from_string("1LL", "2LL", true)); + TEST_ASSERT_FALSE(compare_from_string("1LL", "2LL", false)); +} +#endif /* __CJSON_USE_INT64 */ + static void cjson_compare_should_compare_booleans(void) { /* true */ @@ -196,6 +211,9 @@ int CJSON_CDECL main(void) RUN_TEST(cjson_compare_should_compare_null_pointer_as_not_equal); RUN_TEST(cjson_compare_should_compare_invalid_as_not_equal); RUN_TEST(cjson_compare_should_compare_numbers); +#ifdef __CJSON_USE_INT64 + RUN_TEST(cjson_compare_should_compare_int64_numbers); +#endif RUN_TEST(cjson_compare_should_compare_booleans); RUN_TEST(cjson_compare_should_compare_null); RUN_TEST(cjson_compare_should_not_accept_invalid_types); diff --git a/tests/misc_int64_tests.c b/tests/misc_int64_tests.c new file mode 100644 index 0000000..d2feae6 --- /dev/null +++ b/tests/misc_int64_tests.c @@ -0,0 +1,185 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void assert_is_int64(cJSON *int64_number_item) +{ + assert_has_type(int64_number_item, cJSON_Number); + TEST_ASSERT_BITS_MESSAGE(cJSON_IsInt64, cJSON_IsInt64, int64_number_item->type, "Item should be a int64 integer."); +} + +static void cjson_get_object_item_should_get_object_items_with_int64(void) +{ + cJSON *item = NULL; + cJSON *found = NULL; + + item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}"); + + found = cJSON_GetObjectItem(NULL, "test"); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer."); + + found = cJSON_GetObjectItem(item, NULL); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string."); + + found = cJSON_GetObjectItem(item, "one"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1); + assert_is_int64(found); + + found = cJSON_GetObjectItem(item, "tWo"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2); + assert_is_int64(found); + + found = cJSON_GetObjectItem(item, "three"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3); + assert_is_int64(found); + + found = cJSON_GetObjectItem(item, "four"); + TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there."); + + cJSON_Delete(item); +} + +static void cjson_get_object_item_case_sensitive_should_get_object_items_with_int64(void) +{ + cJSON *item = NULL; + cJSON *found = NULL; + + item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}"); + + found = cJSON_GetObjectItemCaseSensitive(NULL, "test"); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer."); + + found = cJSON_GetObjectItemCaseSensitive(item, NULL); + TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string."); + + found = cJSON_GetObjectItemCaseSensitive(item, "one"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1); + assert_is_int64(found); + + found = cJSON_GetObjectItemCaseSensitive(item, "Two"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2); + assert_is_int64(found); + + found = cJSON_GetObjectItemCaseSensitive(item, "tHree"); + TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item."); + TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3); + assert_is_int64(found); + + found = cJSON_GetObjectItemCaseSensitive(item, "One"); + TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there."); + + cJSON_Delete(item); +} + +static void cjson_set_number_value_should_set_numbers_with_int64(void) +{ + cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number | cJSON_IsInt64, NULL, 0, 0, NULL}}; + + cJSON_SetInt64NumberValue(number, 1LL); + TEST_ASSERT_EQUAL_INT64(1LL, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(1.0, number->valuedouble); + + cJSON_SetInt64NumberValue(number, -1LL); + TEST_ASSERT_EQUAL_INT64(-1LL, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(-1.0, number->valuedouble); + + cJSON_SetInt64NumberValue(number, 0LL); + TEST_ASSERT_EQUAL_INT64(0LL, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(0.0, number->valuedouble); + + cJSON_SetInt64NumberValue(number, LLONG_MAX); + TEST_ASSERT_EQUAL_INT64(LLONG_MAX, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE((double)LLONG_MAX, number->valuedouble); + + cJSON_SetInt64NumberValue(number, LLONG_MIN); + TEST_ASSERT_EQUAL_INT64(LLONG_MIN, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE((double)LLONG_MIN, number->valuedouble); +} + +static void typecheck_functions_should_check_type_with_int64(void) +{ + cJSON item[1]; + item->type = cJSON_Number; + TEST_ASSERT_FALSE(cJSON_IsInt64Number(item)); + + item->type = cJSON_IsInt64; + TEST_ASSERT_FALSE(cJSON_IsInt64Number(item)); + + item->type = cJSON_Number | cJSON_IsInt64; + TEST_ASSERT_TRUE(cJSON_IsInt64Number(item)); + + item->type = cJSON_False; + TEST_ASSERT_FALSE(cJSON_IsInt64Number(item)); +} + +static void cjson_functions_should_not_crash_with_null_pointers_with_int64(void) +{ + cJSON *item = cJSON_CreateString("item"); + + TEST_ASSERT_FALSE(cJSON_IsInt64Number(NULL)); + cJSON_AddInt64NumberToObject(NULL, "item", 0LL); + cJSON_AddInt64NumberToObject(item, NULL, 0LL); + cJSON_AddInt64NumberToObject(NULL, NULL, 0LL); + TEST_ASSERT_NULL(cJSON_GetInt64NumberValue(NULL)); + TEST_ASSERT_EQUAL_INT64(0LL, cJSON_SetInt64NumberValue(NULL, 0LL)); + + cJSON_Delete(item); +} + +static void cjson_get_number_value_should_get_a_number_with_int64(void) +{ + cJSON *string = cJSON_CreateString("test"); + cJSON *number = cJSON_CreateInt64Number(1LL); + + TEST_ASSERT_EQUAL_INT64(*cJSON_GetInt64NumberValue(number), number->valueint); + TEST_ASSERT_NULL(cJSON_GetInt64NumberValue(string)); + TEST_ASSERT_NULL(cJSON_GetInt64NumberValue(NULL)); + + cJSON_Delete(number); + cJSON_Delete(string); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_get_object_item_should_get_object_items_with_int64); + RUN_TEST(cjson_get_object_item_case_sensitive_should_get_object_items_with_int64); + RUN_TEST(cjson_set_number_value_should_set_numbers_with_int64); + RUN_TEST(typecheck_functions_should_check_type_with_int64); + RUN_TEST(cjson_functions_should_not_crash_with_null_pointers_with_int64); + RUN_TEST(cjson_get_number_value_should_get_a_number_with_int64); + + return UNITY_END(); +} diff --git a/tests/misc_tests.c b/tests/misc_tests.c index 48fb6ec..2a79dc0 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -231,6 +231,15 @@ static void cjson_set_number_value_should_set_numbers(void) TEST_ASSERT_EQUAL(-1, number->valueint); TEST_ASSERT_EQUAL_DOUBLE(-1.5, number->valuedouble); +#ifdef __CJSON_USE_INT64 + cJSON_SetNumberValue(number, 1 + (double)LLONG_MAX); + TEST_ASSERT_EQUAL(LLONG_MAX, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(1 + (double)LLONG_MAX, number->valuedouble); + + cJSON_SetNumberValue(number, -1 + (double)LLONG_MIN); + TEST_ASSERT_EQUAL(LLONG_MIN, number->valueint); + TEST_ASSERT_EQUAL_DOUBLE(-1 + (double)LLONG_MIN, number->valuedouble); +#else cJSON_SetNumberValue(number, 1 + (double)INT_MAX); TEST_ASSERT_EQUAL(INT_MAX, number->valueint); TEST_ASSERT_EQUAL_DOUBLE(1 + (double)INT_MAX, number->valuedouble); @@ -238,6 +247,7 @@ static void cjson_set_number_value_should_set_numbers(void) cJSON_SetNumberValue(number, -1 + (double)INT_MIN); TEST_ASSERT_EQUAL(INT_MIN, number->valueint); TEST_ASSERT_EQUAL_DOUBLE(-1 + (double)INT_MIN, number->valuedouble); +#endif /* __CJSON_USE_INT64 */ } 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..b92162b 100644 --- a/tests/parse_number.c +++ b/tests/parse_number.c @@ -55,6 +55,31 @@ static void assert_parse_number(const char *string, int integer, double real) TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); } +#ifdef __CJSON_USE_INT64 +static void assert_is_int64(cJSON *int64_number_item) +{ + assert_is_number(int64_number_item); + TEST_ASSERT_BITS_MESSAGE(cJSON_IsInt64, cJSON_IsInt64, int64_number_item->type, "Item should be a int64 integer"); +} + +static void assert_parse_int64_number(const char *string, long long integer, double real) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + buffer.content = (const unsigned char*)string; + buffer.length = strlen(string) + sizeof(""); + + TEST_ASSERT_TRUE(parse_number(item, &buffer)); + TEST_ASSERT_EQUAL_INT64(integer, item->valueint); + TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); +} + +static void assert_parse_int64_number_with_type(const char *string, long long integer, double real) +{ + assert_parse_int64_number(string, integer, real); + assert_is_int64(item); +} +#endif /* __CJSON_USE_INT64 */ + static void parse_number_should_parse_zero(void) { assert_parse_number("0", 0, 0); @@ -81,8 +106,13 @@ 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); +#ifdef __CJSON_USE_INT64 + assert_parse_int64_number("10e10", 100000000000LL, 10e10); + assert_parse_int64_number("123e+127", LLONG_MAX, 123e127); +#else assert_parse_number("10e10", INT_MAX, 10e10); assert_parse_number("123e+127", INT_MAX, 123e127); +#endif /* __CJSON_USE_INT64 */ assert_parse_number("123e-128", 0, 123e-128); } @@ -91,11 +121,30 @@ 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); +#ifdef __CJSON_USE_INT64 + assert_parse_int64_number("-10e20", LLONG_MIN, -10e20); + assert_parse_int64_number("-123e+127", LLONG_MIN, -123e127); +#else assert_parse_number("-10e20", INT_MIN, -10e20); assert_parse_number("-123e+127", INT_MIN, -123e127); +#endif /* __CJSON_USE_INT64 */ assert_parse_number("-123e-128", 0, -123e-128); } +#ifdef __CJSON_USE_INT64 +static void parse_number_should_parse_int64_numbers(void) +{ + assert_parse_int64_number_with_type("0", 0LL, 0); + assert_parse_int64_number_with_type("-1", -1LL, -1); + assert_parse_int64_number_with_type("-32768", -32768LL, -32768.0); + assert_parse_int64_number_with_type("-2147483648", -2147483648LL, -2147483648.0); + assert_parse_int64_number_with_type("2147483648", (long long)INT_MAX + 1, 2147483648.0); + assert_parse_int64_number_with_type("-2147483649", -2147483649LL, -2147483649.0); + assert_parse_int64_number_with_type("9223372036854775807", LLONG_MAX, 9223372036854775807.0); + assert_parse_int64_number_with_type("-9223372036854775808", LLONG_MIN, -9223372036854775808.0); +} +#endif /* __CJSON_USE_INT64 */ + int CJSON_CDECL main(void) { /* initialize cJSON item */ @@ -106,5 +155,8 @@ int CJSON_CDECL main(void) RUN_TEST(parse_number_should_parse_positive_integers); RUN_TEST(parse_number_should_parse_positive_reals); RUN_TEST(parse_number_should_parse_negative_reals); +#ifdef __CJSON_USE_INT64 + RUN_TEST(parse_number_should_parse_int64_numbers); +#endif return UNITY_END(); } diff --git a/tests/print_number.c b/tests/print_number.c index 3fbf9cb..cd91ee8 100644 --- a/tests/print_number.c +++ b/tests/print_number.c @@ -24,6 +24,25 @@ #include "unity/src/unity.h" #include "common.h" +#ifdef __CJSON_USE_INT64 +static void assert_print_int64_number(const char *expected, long long input) +{ + unsigned char printed[1024]; + cJSON item[1] = {{ NULL, NULL, NULL, cJSON_Number | cJSON_IsInt64, NULL, 0, 0, NULL }}; + 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; + + cJSON_SetInt64NumberValue(item, input); + TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer), "Failed to print number."); + + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected."); +} +#endif /* __CJSON_USE_INT64 */ + static void assert_print_number(const char *expected, double input) { unsigned char printed[1024]; @@ -72,6 +91,12 @@ 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); +#ifdef __CJSON_USE_INT64 + assert_print_int64_number("-1", -1LL); + assert_print_int64_number("-32768", -32768LL); + assert_print_int64_number("-2147483647", -2147483647LL); + assert_print_int64_number("-9223372036854775808", LLONG_MIN); +#endif /* __CJSON_USE_INT64 */ } static void print_number_should_print_positive_integers(void) @@ -79,6 +104,12 @@ 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); +#ifdef __CJSON_USE_INT64 + assert_print_int64_number("1", 1LL); + assert_print_int64_number("32767", 32767LL); + assert_print_int64_number("2147483647", 2147483647LL); + assert_print_int64_number("9223372036854775807", LLONG_MAX); +#endif /* __CJSON_USE_INT64 */ } static void print_number_should_print_positive_reals(void) -- Gitee