diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d807ea6..e17f0980 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ 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 + -std=c99 -pedantic -Wall -Wextra @@ -95,6 +95,11 @@ if (ENABLE_HIDDEN_SYMBOLS) add_definitions(-DCJSON_HIDE_SYMBOLS -UCJSON_API_VISIBILITY) endif() +option(NO_FLOATING_POINT "Build without Floating Foint support" OFF) +if (NO_FLOATING_POINT) + add_definitions(-DNO_FLOATING_POINT) +endif() + # apply custom compiler flags foreach(compiler_flag ${custom_compiler_flags}) #remove problematic characters @@ -242,7 +247,7 @@ if(ENABLE_TARGET_EXPORT) endif() option(ENABLE_CJSON_TEST "Enable building cJSON test" ON) -if(ENABLE_CJSON_TEST) +if (ENABLE_CJSON_TEST AND (NOT NO_FLOATING_POINT)) enable_testing() set(TEST_CJSON cJSON_test) @@ -264,6 +269,8 @@ if(ENABLE_CJSON_TEST) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure DEPENDS ${TEST_CJSON}) + + add_subdirectory(tests) endif() #Create the uninstall target @@ -279,5 +286,4 @@ if(ENABLE_LOCALES) add_definitions(-DENABLE_LOCALES) endif() -add_subdirectory(tests) add_subdirectory(fuzzing) diff --git a/cJSON.c b/cJSON.c index 4e4979e9..9fe378de 100644 --- a/cJSON.c +++ b/cJSON.c @@ -37,13 +37,25 @@ #pragma warning (disable : 4001) #endif +#include "cJSON.h" + +#ifdef __KERNEL__ +#include +#include + +#else + #include #include -#include #include #include #include +#endif + +#ifndef NO_FLOATING_POINT +#include #include +#endif #ifdef ENABLE_LOCALES #include @@ -56,8 +68,6 @@ #pragma GCC visibility pop #endif -#include "cJSON.h" - /* define our own boolean type */ #ifdef true #undef true @@ -69,6 +79,7 @@ #endif #define false ((cJSON_bool)0) +#ifndef NO_FLOATING_POINT /* 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)) @@ -85,6 +96,13 @@ #endif #endif +#define cJSON_NaN (double)NAN + +#else + +#define cJSON_NaN 0L +#endif + typedef struct { const unsigned char *json; size_t position; @@ -106,11 +124,11 @@ CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) return item->valuestring; } -CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +CJSON_PUBLIC(Numeric) cJSON_GetNumberValue(const cJSON * const item) { if (!cJSON_IsNumber(item)) { - return (double) NAN; + return cJSON_NaN; } return item->valuedouble; @@ -124,7 +142,7 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) CJSON_PUBLIC(const char*) cJSON_Version(void) { static char version[15]; - sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + snprintf(version, sizeof(version), "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); return version; } @@ -174,7 +192,24 @@ static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) { return realloc(pointer, size); } + +#elif defined(__KERNEL__) +/* Linux kernel */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return kmalloc(size, GFP_KERNEL); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + kfree(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return krealloc(pointer, size, GFP_KERNEL); +} + #else + #define internal_malloc malloc #define internal_free free #define internal_realloc realloc @@ -211,19 +246,19 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) if (hooks == NULL) { /* Reset hooks */ - global_hooks.allocate = malloc; - global_hooks.deallocate = free; - global_hooks.reallocate = realloc; + global_hooks.allocate = internal_malloc; + global_hooks.deallocate = internal_free; + global_hooks.reallocate = internal_realloc; return; } - global_hooks.allocate = malloc; + global_hooks.allocate = internal_malloc; if (hooks->malloc_fn != NULL) { global_hooks.allocate = hooks->malloc_fn; } - global_hooks.deallocate = free; + global_hooks.deallocate = internal_free; if (hooks->free_fn != NULL) { global_hooks.deallocate = hooks->free_fn; @@ -231,9 +266,9 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) /* use realloc only if both free and malloc are used */ global_hooks.reallocate = NULL; - if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + if ((global_hooks.allocate == internal_malloc) && (global_hooks.deallocate == internal_free)) { - global_hooks.reallocate = realloc; + global_hooks.reallocate = internal_realloc; } } @@ -301,61 +336,116 @@ typedef struct /* get a pointer to the buffer at the position */ #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) -/* 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; - unsigned char *after_end = NULL; - unsigned char number_c_string[64]; +/* + * Copy the number into a temporary buffer and replace '.' with the decimal point of the current locale. + * Return the length of the numeric value, i.e. the num. of bytes that can be skipped in the input, + * 0 stands for invalid (non-numeric) input. + */ +static size_t parse_numeric(const unsigned char *input, size_t insize, unsigned char *output, size_t outsize) +{ + size_t i; + int phase = 1; unsigned char decimal_point = get_decimal_point(); - size_t i = 0; - if ((input_buffer == NULL) || (input_buffer->content == NULL)) + for (i = 0; (i < outsize - 1) && (i < insize); ++i) { - return false; - } + unsigned char c = input[i]; + + /* identify 5 stages in FP values parsing: -1.2 / 12e-3 / .5 ... */ + switch (phase) + { + case 1: { + if (isdigit(c) || (c == '+') || (c == '-')) { + phase = 2; + } else if (c == '.') { + c = decimal_point; + phase = 3; + } else { + goto loop_end; + } + + output[i] = c; + break; + } + case 2: { + if (c == '.') { + c = decimal_point; + phase = 3; + } else if ((c == 'E') || (c == 'e')) { + phase = 4; + } else if (isdigit(c)) { + /* stay here */ + } else { + goto loop_end; + } - /* 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 '-': - case 'e': - case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; + output[i] = c; break; + } + case 3: { + if ((c == 'E') || (c == 'e')) { + phase = 4; + } else if (isdigit(c)) { + /* stay here */ + } else { + goto loop_end; + } + + output[i] = c; + break; + } + case 4: { + if (isdigit(c) || (c == '+') || (c == '-')) { + phase = 5; + } else { + goto loop_end; + } - case '.': - number_c_string[i] = decimal_point; + output[i] = c; break; + } + case 5: { + if (isdigit(c)) { + /* stay here */ + } else { + goto loop_end; + } + output[i] = c; + break; + } default: goto loop_end; } } + loop_end: - number_c_string[i] = '\0'; + for (; (i > 0) && !isdigit(output[i - 1]) && (output[i - 1] != '.'); --i) + output[i] = '\0'; + output[i] = '\0'; + + return i; +} - number = strtod((const char*)number_c_string, (char**)&after_end); - if (number_c_string == after_end) +/* 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) +{ + Numeric number = 0; + unsigned char number_c_string[64]; + size_t len; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { - return false; /* parse_error */ + return false; } + len = parse_numeric(&buffer_at_offset(input_buffer)[0], (input_buffer->length - input_buffer->offset), + number_c_string, sizeof(number_c_string)); + if (len == 0) + return false; /* parse_error */ + + strtonum((const char*)number_c_string, &number); + item->valuedouble = number; /* use saturation in case of overflow */ @@ -363,7 +453,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu { item->valueint = INT_MAX; } - else if (number <= (double)INT_MIN) + else if (number <= (Numeric)INT_MIN) { item->valueint = INT_MIN; } @@ -374,18 +464,18 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu item->type = cJSON_Number; - input_buffer->offset += (size_t)(after_end - number_c_string); + input_buffer->offset += len; return true; } /* 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(Numeric) cJSON_SetNumberHelper(cJSON *object, Numeric number) { if (number >= INT_MAX) { object->valueint = INT_MAX; } - else if (number <= (double)INT_MIN) + else if (number <= (Numeric)INT_MIN) { object->valueint = INT_MIN; } @@ -539,52 +629,65 @@ static void update_offset(printbuffer * const buffer) buffer->offset += strlen((const char*)buffer_pointer); } -/* securely comparison of floating-point variables */ +/* securely compare floating-point variables, return `true` if (a == b) */ +#ifndef NO_FLOATING_POINT static cJSON_bool compare_double(double a, double b) { double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); return (fabs(a - b) <= maxVal * DBL_EPSILON); } +#else + +#define compare_double(a, b) ((a) == (b)) +#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; + Numeric 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; if (output_buffer == NULL) { return false; } +#ifndef NO_FLOATING_POINT /* This checks for NaN and Infinity */ if (isnan(d) || isinf(d)) { - length = sprintf((char*)number_buffer, "null"); + length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); + } + else if(d == (Numeric)item->valueint) + { + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%d", item->valueint); } - else if(d == (double)item->valueint) - { - length = sprintf((char*)number_buffer, "%d", item->valueint); - } else { + double test = 0.0; + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ - length = sprintf((char*)number_buffer, "%1.15g", d); + length = snprintf((char*)number_buffer, sizeof(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); + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); } } - /* sprintf failed or buffer overrun occurred */ +#else + + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%ld", d); +#endif + + /* snprintf failed or buffer overrun occurred */ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; @@ -1013,8 +1116,7 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe break; default: /* escape and print as unicode codepoint */ - sprintf((char*)output_pointer, "u%04x", *input_pointer); - output_pointer += 4; + output_pointer += snprintf((char*)output_pointer, 6, "u%04x", *input_pointer); break; } } @@ -2129,7 +2231,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 Numeric number) { cJSON *number_item = cJSON_CreateNumber(number); if (add_item_to_object(object, name, number_item, &global_hooks, false)) @@ -2436,7 +2538,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(Numeric num) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) @@ -2449,7 +2551,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { item->valueint = INT_MAX; } - else if (num <= (double)INT_MIN) + else if (num <= (Numeric)INT_MIN) { item->valueint = INT_MIN; } @@ -2592,6 +2694,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) return a; } +#ifndef NO_FLOATING_POINT CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) { size_t i = 0; @@ -2671,6 +2774,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) return a; } +#endif CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { diff --git a/cJSON.h b/cJSON.h index 218cc9ea..5235752e 100644 --- a/cJSON.h +++ b/cJSON.h @@ -78,12 +78,32 @@ then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJ #endif #endif +#ifdef __KERNEL__ +#define NO_FLOATING_POINT +#endif + +#ifndef NO_FLOATING_POINT +typedef double Numeric; +#define strtonum(s, num) sscanf((s), "%lf", (num)) + +#else + +typedef long Numeric; +#define strtonum(s, num) sscanf((s), "%ld", (num)) +#endif + /* project version */ #define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MINOR 7 #define CJSON_VERSION_PATCH 17 +#ifdef __KERNEL__ +#include + +#else + #include +#endif /* cJSON Types: */ #define cJSON_Invalid (0) @@ -116,7 +136,7 @@ typedef struct cJSON /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; + Numeric 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; @@ -177,7 +197,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(Numeric) cJSON_GetNumberValue(const cJSON * const item); /* These functions check the type of an item */ CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); @@ -196,7 +216,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_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(Numeric num); CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); /* raw json */ CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); @@ -214,8 +234,10 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); /* These utilities create an Array of count items. * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +#ifndef NO_FLOATING_POINT CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +#endif CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); /* Append item to the specified array/object. */ @@ -265,7 +287,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 Numeric 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); @@ -274,8 +296,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)->valueint = (object)->valuedouble = (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(Numeric) cJSON_SetNumberHelper(cJSON *object, Numeric number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (Numeric)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);