From 99a12144fe3cea90eece1a1b6da564299d3a430c Mon Sep 17 00:00:00 2001 From: Pawel Winogrodzki Date: Wed, 29 Mar 2017 12:24:38 -0700 Subject: [PATCH] IOT-1583: Updating cJSON version to remove /W4 warnings. The code is updated to commit b0db7a6308f8948dd4a68f06b787cb425ce7254d from the main cJSON repo: https://github.com/DaveGamble/cJSON. Change-Id: I43b2bae1fa4fde5eee12f7d74990d5263f4ec51b Signed-off-by: Pawel Winogrodzki Reviewed-on: https://gerrit.iotivity.org/gerrit/18381 Tested-by: jenkins-iotivity Reviewed-by: Dan Mihai Reviewed-by: Dave Thaler --- bridging/plugins/hue_plugin/SConscript | 2 - build_common/android/SConscript | 1 - build_common/external_libs.scons | 3 + build_common/linux/SConscript | 1 - extlibs/android/gradle/SConscript | 2 - extlibs/android/sdk/SConscript | 1 - extlibs/arduino/SConscript | 1 - extlibs/cjson/README-cJSON.txt | 7 +- extlibs/cjson/SConscript | 12 +- extlibs/cjson/cJSON.c | 2689 +++++++++++++++++-------------- extlibs/cjson/cJSON.h | 159 +- resource/csdk/security/tool/SConscript | 4 +- service/coap-http-proxy/SConscript | 1 - service/simulator/ramlparser/SConscript | 1 - 14 files changed, 1596 insertions(+), 1288 deletions(-) diff --git a/bridging/plugins/hue_plugin/SConscript b/bridging/plugins/hue_plugin/SConscript index 01d7f5d..7c75284 100644 --- a/bridging/plugins/hue_plugin/SConscript +++ b/bridging/plugins/hue_plugin/SConscript @@ -32,8 +32,6 @@ bridging_path = os.path.join(src_dir, 'bridging') hue_env = env.Clone() -SConscript('#/extlibs/cjson/SConscript') - print "Reading Hue Plugin script" ###################################################################### diff --git a/build_common/android/SConscript b/build_common/android/SConscript index a2e8a1f..034bdf6 100644 --- a/build_common/android/SConscript +++ b/build_common/android/SConscript @@ -7,7 +7,6 @@ import subprocess Import('env') -SConscript('../external_libs.scons') help_vars = Variables() if not env.get('ANDROID_NDK'): SConscript('../../extlibs/android/ndk/SConscript') diff --git a/build_common/external_libs.scons b/build_common/external_libs.scons index 22a6c1a..c43a29e 100644 --- a/build_common/external_libs.scons +++ b/build_common/external_libs.scons @@ -138,6 +138,9 @@ SConscript('tools/UnpackAll.py') # tinycbor build/fetch SConscript(os.path.join(env.get('SRC_DIR'), 'extlibs', 'tinycbor', 'SConscript')) +#cJSON lib +SConscript(os.path.join(env.get('SRC_DIR'), 'extlibs', 'cjson', 'SConscript')) + with_ra = env.get('WITH_RA') if with_ra: SConscript(os.path.join(env.get('SRC_DIR'), 'extlibs', 'raxmpp', 'SConscript')) diff --git a/build_common/linux/SConscript b/build_common/linux/SConscript index eeea809..198118f 100644 --- a/build_common/linux/SConscript +++ b/build_common/linux/SConscript @@ -8,7 +8,6 @@ Import('env') print "Reading linux configuration script" -SConscript('../external_libs.scons') help_vars = Variables() if env.get('BUILD_JAVA') == True: if not env.get('ANDROID_GRADLE'): diff --git a/extlibs/android/gradle/SConscript b/extlibs/android/gradle/SConscript index 9cece4b..208f223 100644 --- a/extlibs/android/gradle/SConscript +++ b/extlibs/android/gradle/SConscript @@ -4,12 +4,10 @@ Import('env') env = env.Clone() ndk_env = env.Clone() -SConscript('../../../build_common/external_libs.scons') target_os = env.get('TARGET_OS') host_os = sys.platform print host_os -SConscript('../../../build_common/external_libs.scons') ###################################################################### # Build flags ###################################################################### diff --git a/extlibs/android/sdk/SConscript b/extlibs/android/sdk/SConscript index 7b7b1ba..a06adf6 100644 --- a/extlibs/android/sdk/SConscript +++ b/extlibs/android/sdk/SConscript @@ -13,7 +13,6 @@ target_os = env.get('TARGET_OS') src_dir = env.get('SRC_DIR') SConscript(src_dir + '/build_common/tools/UnpackAll.py') -SConscript(src_dir + '/build_common/external_libs.scons') # Download if target_os == 'android': diff --git a/extlibs/arduino/SConscript b/extlibs/arduino/SConscript index 879fe2c..fcd5cb7 100644 --- a/extlibs/arduino/SConscript +++ b/extlibs/arduino/SConscript @@ -14,7 +14,6 @@ target_os = env.get('TARGET_OS') src_dir = env.get('SRC_DIR') SConscript(src_dir + '/build_common/tools/UnpackAll.py') -SConscript(src_dir + '/build_common/external_libs.scons') # Download if target_os == 'arduino': diff --git a/extlibs/cjson/README-cJSON.txt b/extlibs/cjson/README-cJSON.txt index 7e3a0b9..64ed9f8 100644 --- a/extlibs/cjson/README-cJSON.txt +++ b/extlibs/cjson/README-cJSON.txt @@ -1,10 +1,9 @@ This dependency comes from: https://github.com/DaveGamble/cJSON.git. The cJSON.c and cJSON.h files are copied from that repo. -Commit ID from when files were last copied: 11fd27ade73c5049295a58eb0587e283241d0e70 -Release tag when files were last copied (if applicable): v1.0.1 +Commit ID from when files were last copied: b0db7a6308f8948dd4a68f06b787cb425ce7254d +Release tag when files were last copied (if applicable): With the following changes: -* git revert 679004914fceb1c418545fb33c707fb454cbc4bb (Define a boolean type) - * Causes a build break on Arduino because it declares 'bool' to be 'int' +* b0db7a6 cJSON_ParseWidthOpts: Fix -Wmissing-field-initializers diff --git a/extlibs/cjson/SConscript b/extlibs/cjson/SConscript index 7329925..8365b70 100644 --- a/extlibs/cjson/SConscript +++ b/extlibs/cjson/SConscript @@ -20,6 +20,14 @@ Import('env') -libcjson = env.StaticLibrary('cjson', ['cJSON.c'], OBJPREFIX='libcjson_') -env.InstallTarget(libcjson, 'cjson'); +cjson_env = env.Clone() + +if cjson_env.get('TARGET_OS') in ['windows', 'msys_nt']: + # Macro needed for Windows builds to avoid __declspec(dllexport) and __declspec(dllimport) for cJSON APIs. + cjson_env.AppendUnique(CPPDEFINES = ['CJSON_HIDE_SYMBOLS']) + if cjson_env['CC'] == 'cl': + cjson_env.AppendUnique(CCFLAGS = ['/W4', '/WX']) + +libcjson = cjson_env.StaticLibrary('cjson', ['cJSON.c'], OBJPREFIX='libcjson_') +cjson_env.InstallTarget(libcjson, 'cjson'); diff --git a/extlibs/cjson/cJSON.c b/extlibs/cjson/cJSON.c index 079027c..75f900d 100644 --- a/extlibs/cjson/cJSON.c +++ b/extlibs/cjson/cJSON.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble + 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 @@ -23,6 +23,10 @@ /* cJSON */ /* JSON parser in C. */ +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif + #include #include #include @@ -30,87 +34,135 @@ #include #include #include -#include "cJSON.h" -/* Determine the number of bits that an integer has using the preprocessor */ -#if INT_MAX == 32767 - /* 16 bits */ - #define INTEGER_SIZE 0x0010 -#elif INT_MAX == 2147483647 - /* 32 bits */ - #define INTEGER_SIZE 0x0100 -#elif INT_MAX == 9223372036854775807 - /* 64 bits */ - #define INTEGER_SIZE 0x1000 -#else - #error "Failed to determine the size of an integer" +#ifdef __GNUC__ +#pragma GCC visibility pop #endif -static const char *global_ep = NULL; +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 4) || (CJSON_VERSION_PATCH != 5) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif -const char *cJSON_GetErrorPtr(void) +CJSON_PUBLIC(const char*) cJSON_Version(void) { - return global_ep; + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; } /* case insensitive strcmp */ -static int cJSON_strcasecmp(const char *s1, const char *s2) +static int cJSON_strcasecmp(const unsigned char *string1, const unsigned char *string2) { - if (!s1) + if (string1 == NULL) { - return (s1 == s2) ? 0 : 1; /* both NULL? */ + if (string2 == NULL) + { + /* both NULL */ + return 0; + } + + return 1; } - if (!s2) + + if (string2 == NULL) { return 1; } - for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2) + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { - if (*s1 == '\0') + if (*string1 == '\0') { return 0; } } - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); + return tolower(string1[0]) - tolower(string2[0]); } -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +static internal_hooks global_hooks = { malloc, free, realloc }; -static char* cJSON_strdup(const char* str) +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) { - size_t len = 0; - char *copy = NULL; + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) + length = strlen((const char*)string) + sizeof(""); + if (!(copy = (unsigned char*)hooks->allocate(length))) { return NULL; } - memcpy(copy, str, len); + memcpy(copy, string, length); return copy; } -void cJSON_InitHooks(cJSON_Hooks* hooks) +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) { - if (!hooks) + if (hooks == NULL) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; return; } - cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; - cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } } /* Internal constructor. */ -static cJSON *cJSON_New_Item(void) +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) { - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); if (node) { memset(node, '\0', sizeof(cJSON)); @@ -120,1512 +172,1485 @@ static cJSON *cJSON_New_Item(void) } /* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { cJSON *next = NULL; - while (c) + while (item != NULL) { - next = c->next; - if (!(c->type & cJSON_IsReference) && c->child) + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { - cJSON_Delete(c->child); + cJSON_Delete(item->child); } - if (!(c->type & cJSON_IsReference) && c->valuestring) + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { - cJSON_free(c->valuestring); + global_hooks.deallocate(item->valuestring); } - if (!(c->type & cJSON_StringIsConst) && c->string) + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { - cJSON_free(c->string); + global_hooks.deallocate(item->string); } - cJSON_free(c); - c = next; + global_hooks.deallocate(item); + item = next; } } +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +#define cannot_read(buffer, size) (!can_read(buffer, size)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* 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 const char *parse_number(cJSON *item, const char *num) +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) { - double n = 0; - double sign = 1; - double scale = 0; - int subscale = 0; - int signsubscale = 1; + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and zero terminate the string + * because the number in the input buffer is not necessariliy zero terminated + * and strtod only works with zero terminated strings */ + 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': + case '.': + break; - /* Has sign? */ - if (*num == '-') - { - sign = -1; - num++; + default: + goto loop_end; + } + + number_c_string[i] = buffer_at_offset(input_buffer)[i]; } - /* is zero */ - if (*num == '0') + loop_end: + number_c_string[i] = '\0'; + + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) { - num++; + return false; /* parse_error */ } - /* Number? */ - if ((*num >= '1') && (*num <= '9')) + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) { - do - { - n = (n * 10.0) + (*num++ - '0'); - } - while ((*num >= '0') && (*num<='9')); + item->valueint = INT_MAX; } - /* Fractional part? */ - if ((*num == '.') && (num[1] >= '0') && (num[1] <= '9')) + else if (number <= INT_MIN) { - num++; - do - { - n = (n *10.0) + (*num++ - '0'); - scale--; - } while ((*num >= '0') && (*num <= '9')); + item->valueint = INT_MIN; } - /* Exponent? */ - if ((*num == 'e') || (*num == 'E')) + else { - num++; - /* With sign? */ - if (*num == '+') - { - num++; - } - else if (*num == '-') - { - signsubscale = -1; - num++; - } - /* Number? */ - while ((*num>='0') && (*num<='9')) - { - subscale = (subscale * 10) + (*num++ - '0'); - } + item->valueint = (int)number; } - /* number = +/- number.fraction * 10^+/- exponent */ - n = sign * n * pow(10.0, (scale + subscale * signsubscale)); - - item->valuedouble = n; - item->valueint = (int)n; item->type = cJSON_Number; - return num; + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; } -/* calculate the next largest power of 2 */ -static int pow2gt (int x) +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { - --x; - - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; -#if INTEGER_SIZE & 0x1110 /* at least 16 bit */ - x |= x >> 8; -#endif -#if INTEGER_SIZE & 0x1100 /* at least 32 bit */ - x |= x >> 16; -#endif -#if INT_SIZE & 0x1000 /* 64 bit */ - x |= x >> 32; -#endif + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } - return x + 1; + return object->valuedouble = number; } typedef struct { - char *buffer; - int length; - int offset; + unsigned char *buffer; + size_t length; + size_t offset; + cJSON_bool noalloc; } printbuffer; /* realloc printbuffer if necessary to have at least "needed" bytes more */ -static char* ensure(printbuffer *p, int needed) +static unsigned char* ensure(printbuffer * const p, size_t needed, const internal_hooks * const hooks) { - char *newbuffer = NULL; - int newsize = 0; - if (!p || !p->buffer) + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ return NULL; } - needed += p->offset; + + needed += p->offset + 1; if (needed <= p->length) { return p->buffer + p->offset; } - newsize = pow2gt(needed); - newbuffer = (char*)cJSON_malloc(newsize); - if (!newbuffer) + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (newsize > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else { - cJSON_free(p->buffer); - p->length = 0; - p->buffer = NULL; + newsize = needed * 2; + } - return NULL; + if (hooks->reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)hooks->reallocate(p->buffer, newsize); } - if (newbuffer) + else { - memcpy(newbuffer, p->buffer, p->length); + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)hooks->allocate(newsize); + if (!newbuffer) + { + hooks->deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + hooks->deallocate(p->buffer); } - cJSON_free(p->buffer); p->length = newsize; p->buffer = newbuffer; return newbuffer + p->offset; } -/* calculate the new length of the string in a printbuffer */ -static int update(const printbuffer *p) +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Removes trailing zeroes from the end of a printed number */ +static cJSON_bool trim_trailing_zeroes(printbuffer * const buffer) { - char *str = NULL; - if (!p || !p->buffer) + size_t offset = 0; + unsigned char *content = NULL; + + if ((buffer == NULL) || (buffer->buffer == NULL) || (buffer->offset < 1)) + { + return false; + } + + offset = buffer->offset - 1; + content = buffer->buffer; + + while ((offset > 0) && (content[offset] == '0')) + { + offset--; + } + if ((offset > 0) && (content[offset] == '.')) { - return 0; + offset--; } - str = p->buffer + p->offset; - return p->offset + strlen(str); + offset++; + content[offset] = '\0'; + + buffer->offset = offset; + + return true; } /* Render the number nicely from the given item into a string. */ -static char *print_number(const cJSON *item, printbuffer *p) +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer, const internal_hooks * const hooks) { - char *str = NULL; + unsigned char *output_pointer = NULL; double d = item->valuedouble; - /* special case for 0. */ - if (d == 0) + int length = 0; + cJSON_bool trim_zeroes = true; /* should at the end be removed? */ + + if (output_buffer == NULL) { - if (p) - { - str = ensure(p, 2); - } - else - { - str = (char*)cJSON_malloc(2); - } - if (str) - { - strcpy(str,"0"); - } + return false; } - /* value is an int */ - else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) + + /* This is a nice tradeoff. */ + output_pointer = ensure(output_buffer, 64, hooks); + if (output_pointer == NULL) { - if (p) - { - str = ensure(p, 21); - } - else + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)output_pointer, "null"); + } + else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) + { + /* integer */ + length = sprintf((char*)output_pointer, "%.0f", d); + trim_zeroes = false; /* don't remove zeroes for "big integers" */ + } + else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) + { + length = sprintf((char*)output_pointer, "%e", d); + trim_zeroes = false; /* don't remove zeroes in engineering notation */ + } + else + { + length = sprintf((char*)output_pointer, "%f", d); + } + + /* sprintf failed */ + if (length < 0) + { + return false; + } + + output_buffer->offset += (size_t)length; + + if (trim_zeroes) + { + return trim_trailing_zeroes(output_buffer); + } + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) { - /* 2^64+1 can be represented in 21 chars. */ - str = (char*)cJSON_malloc(21); + h += (unsigned int) input[i] - '0'; } - if (str) + else if ((input[i] >= 'A') && (input[i] <= 'F')) { - sprintf(str, "%d", item->valueint); + h += (unsigned int) 10 + input[i] - 'A'; } - } - /* value is a floating point number */ - else - { - if (p) + else if ((input[i] >= 'a') && (input[i] <= 'f')) { - /* This is a nice tradeoff. */ - str = ensure(p, 64); + h += (unsigned int) 10 + input[i] - 'a'; } - else + else /* invalid */ { - /* This is a nice tradeoff. */ - str=(char*)cJSON_malloc(64); + return 0; } - if (str) + + if (i < 3) { - /* This checks for NaN and Infinity */ - if ((d * 0) != 0) - { - sprintf(str, "null"); - } - else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) - { - sprintf(str, "%.0f", d); - } - else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) - { - sprintf(str, "%e", d); - } - else - { - sprintf(str, "%f", d); - } + /* shift left to make place for the next nibble */ + h = h << 4; } } - return str; + + return h; } -/* parse 4 digit hexadecimal number */ -static unsigned parse_hex4(const char *str) +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) { - unsigned h = 0; - /* first digit */ - if ((*str >= '0') && (*str <= '9')) - { - h += (*str) - '0'; - } - else if ((*str >= 'A') && (*str <= 'F')) - { - h += 10 + (*str) - 'A'; - } - else if ((*str >= 'a') && (*str <= 'f')) - { - h += 10 + (*str) - 'a'; - } - else /* invalid */ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) { - return 0; + /* input ends unexpectedly */ + goto fail; } + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); - /* second digit */ - h = h << 4; - str++; - if ((*str >= '0') && (*str <= '9')) - { - h += (*str) - '0'; - } - else if ((*str >= 'A') && (*str <= 'F')) + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { - h += 10 + (*str) - 'A'; + goto fail; } - else if ((*str >= 'a') && (*str <= 'f')) + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { - h += 10 + (*str) - 'a'; + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); } - else /* invalid */ + else { - return 0; + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; } - /* third digit */ - h = h << 4; - str++; - if ((*str >= '0') && (*str <= '9')) + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) { - h += (*str) - '0'; + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; } - else if ((*str >= 'A') && (*str <= 'F')) + else if (codepoint < 0x800) { - h += 10 + (*str) - 'A'; + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ } - else if ((*str >= 'a') && (*str <= 'f')) + else if (codepoint < 0x10000) { - h += 10 + (*str) - 'a'; + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ } - else /* invalid */ + else if (codepoint <= 0x10FFFF) { - return 0; + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ } - - /* fourth digit */ - h = h << 4; - str++; - if ((*str >= '0') && (*str <= '9')) + else { - h += (*str) - '0'; + /* invalid unicode codepoint */ + goto fail; } - else if ((*str >= 'A') && (*str <= 'F')) + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { - h += 10 + (*str) - 'A'; + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; } - else if ((*str >= 'a') && (*str <= 'f')) + /* encode first byte */ + if (utf8_length > 1) { - h += 10 + (*str) - 'a'; + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); } - else /* invalid */ + else { - return 0; + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); } - return h; -} + *output_pointer += utf8_length; -/* first bytes of UTF8 encoding for a given length in bytes */ -static const unsigned char firstByteMark[7] = -{ - 0x00, /* should never happen */ - 0x00, /* 0xxxxxxx */ - 0xC0, /* 110xxxxx */ - 0xE0, /* 1110xxxx */ - 0xF0, /* 11110xxx */ - 0xF8, - 0xFC -}; + return sequence_length; + +fail: + return 0; +} -/* Parse the input text into an unescaped cstring, and populate item. */ -static const char *parse_string(cJSON *item, const char *str, const char **ep) +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks) { - const char *ptr = str + 1; - const char *end_ptr =str + 1; - char *ptr2 = NULL; - char *out = NULL; - int len = 0; - unsigned uc = 0; - unsigned uc2 = 0; + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; - /* not a string! */ - if (*str != '\"') + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') { - *ep = str; - return NULL; + goto fail; } - while ((*end_ptr != '\"') && *end_ptr && ++len) { - if (*end_ptr++ == '\\') + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while ((*input_end != '\"') && ((size_t)(input_end - input_buffer->content) < input_buffer->length)) { - if (*end_ptr == '\0') + /* is escape sequence */ + if (input_end[0] == '\\') { - /* prevent buffer overflow when last input character is a backslash */ - return NULL; + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; } - /* Skip escaped quotes. */ - end_ptr++; + input_end++; + } + if (*input_end != '\"') + { + goto fail; /* string ended unexpectedly */ } - } - /* This is at most how long we need for the string, roughly. */ - out = (char*)cJSON_malloc(len + 1); - if (!out) - { - return NULL; + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)hooks->allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } } - item->valuestring = out; /* assign here so out will be deleted during cJSON_Delete() later */ - item->type = cJSON_String; - ptr = str + 1; - ptr2 = out; + output_pointer = output; /* loop through the string literal */ - while (ptr < end_ptr) + while (input_pointer < input_end) { - if (*ptr != '\\') + if (*input_pointer != '\\') { - *ptr2++ = *ptr++; + *output_pointer++ = *input_pointer++; } /* escape sequence */ else { - ptr++; - switch (*ptr) + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) { case 'b': - *ptr2++ = '\b'; + *output_pointer++ = '\b'; break; case 'f': - *ptr2++ = '\f'; + *output_pointer++ = '\f'; break; case 'n': - *ptr2++ = '\n'; + *output_pointer++ = '\n'; break; case 'r': - *ptr2++ = '\r'; + *output_pointer++ = '\r'; break; case 't': - *ptr2++ = '\t'; + *output_pointer++ = '\t'; break; case '\"': case '\\': case '/': - *ptr2++ = *ptr; + *output_pointer++ = input_pointer[1]; break; - case 'u': - /* transcode utf16 to utf8. See RFC2781 and RFC3629. */ - uc = parse_hex4(ptr + 1); /* get the unicode char. */ - ptr += 4; - if (ptr >= end_ptr) - { - /* invalid */ - *ep = str; - return NULL; - } - /* check for invalid. */ - if (((uc >= 0xDC00) && (uc <= 0xDFFF)) || (uc == 0)) - { - *ep = str; - return NULL; - } - - /* UTF16 surrogate pairs. */ - if ((uc >= 0xD800) && (uc<=0xDBFF)) - { - if ((ptr + 6) > end_ptr) - { - /* invalid */ - *ep = str; - return NULL; - } - if ((ptr[1] != '\\') || (ptr[2] != 'u')) - { - /* missing second-half of surrogate. */ - *ep = str; - return NULL; - } - uc2 = parse_hex4(ptr + 3); - ptr += 6; /* \uXXXX */ - if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) - { - /* invalid second-half of surrogate. */ - *ep = str; - return NULL; - } - /* calculate unicode codepoint from the surrogate pair */ - uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); - } - /* encode as UTF8 - * takes at maximum 4 bytes to encode: - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - len = 4; - if (uc < 0x80) - { - /* normal ascii, encoding 0xxxxxxx */ - len = 1; - } - else if (uc < 0x800) - { - /* two bytes, encoding 110xxxxx 10xxxxxx */ - len = 2; - } - else if (uc < 0x10000) + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) { - /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ - len = 3; + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; } - ptr2 += len; - - switch (len) { - case 4: - /* 10xxxxxx */ - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 3: - /* 10xxxxxx */ - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 2: - /* 10xxxxxx */ - *--ptr2 = ((uc | 0x80) & 0xBF); - uc >>= 6; - case 1: - /* depending on the length in bytes this determines the - * encoding ofthe first UTF8 byte */ - *--ptr2 = (uc | firstByteMark[len]); - } - ptr2 += len; break; + default: - *ep = str; - return NULL; + goto fail; } - ptr++; + input_pointer += sequence_length; } } - *ptr2 = '\0'; - if (*ptr == '\"') + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + hooks->deallocate(output); + } + + if (input_pointer != NULL) { - ptr++; + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); } - return ptr; + return false; } /* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str, printbuffer *p) +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer, const internal_hooks * const hooks) { - const char *ptr = NULL; - char *ptr2 = NULL; - char *out = NULL; - int len = 0; - int flag = 0; - unsigned char token = '\0'; + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } /* empty string */ - if (!str) + if (input == NULL) { - if (p) + output = ensure(output_buffer, sizeof("\"\""), hooks); + if (output == NULL) { - out = ensure(p, 3); + return false; } - else - { - out = (char*)cJSON_malloc(3); - } - if (!out) - { - return NULL; - } - strcpy(out, "\"\""); + strcpy((char*)output, "\"\""); - return out; + return true; } /* set "flag" to 1 if something needs to be escaped */ - for (ptr = str; *ptr; ptr++) + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\""), hooks); + if (output == NULL) { - flag |= (((*ptr > 0) && (*ptr < 32)) /* unprintable characters */ - || (*ptr == '\"') /* double quote */ - || (*ptr == '\\')) /* backslash */ - ? 1 - : 0; + return false; } + /* no characters have to be escaped */ - if (!flag) + if (escape_characters == 0) { - len = ptr - str; - if (p) - { - out = ensure(p, len + 3); - } - else - { - out = (char*)cJSON_malloc(len + 3); - } - if (!out) - { - return NULL; - } - - ptr2 = out; - *ptr2++ = '\"'; - strcpy(ptr2, str); - ptr2[len] = '\"'; - ptr2[len + 1] = '\0'; - - return out; - } + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; - ptr = str; - /* calculate additional space that is needed for escaping */ - while ((token = *ptr) && ++len) - { - if (strchr("\"\\\b\f\n\r\t", token)) - { - len++; /* +1 for the backslash */ - } - else if (token < 32) - { - len += 5; /* +5 for \uXXXX */ - } - ptr++; + return true; } - if (p) - { - out = ensure(p, len + 3); - } - else - { - out = (char*)cJSON_malloc(len + 3); - } - if (!out) - { - return NULL; - } - - ptr2 = out; - ptr = str; - *ptr2++ = '\"'; + output[0] = '\"'; + output_pointer = output + 1; /* copy the string */ - while (*ptr) + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { - if (((unsigned char)*ptr > 31) && (*ptr != '\"') && (*ptr != '\\')) + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { /* normal character, copy */ - *ptr2++ = *ptr++; + *output_pointer = *input_pointer; } else { /* character needs to be escaped */ - *ptr2++ = '\\'; - switch (token = *ptr++) + *output_pointer++ = '\\'; + switch (*input_pointer) { case '\\': - *ptr2++ = '\\'; + *output_pointer = '\\'; break; case '\"': - *ptr2++ = '\"'; + *output_pointer = '\"'; break; case '\b': - *ptr2++ = 'b'; + *output_pointer = 'b'; break; case '\f': - *ptr2++ = 'f'; + *output_pointer = 'f'; break; case '\n': - *ptr2++ = 'n'; + *output_pointer = 'n'; break; case '\r': - *ptr2++ = 'r'; + *output_pointer = 'r'; break; case '\t': - *ptr2++ = 't'; + *output_pointer = 't'; break; default: /* escape and print as unicode codepoint */ - sprintf(ptr2, "u%04x", token); - ptr2 += 5; + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; break; } } } - *ptr2++ = '\"'; - *ptr2++ = '\0'; + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; - return out; + return true; } /* Invoke print_string_ptr (which is useful) on an item. */ -static char *print_string(const cJSON *item, printbuffer *p) +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p, const internal_hooks * const hooks) { - return print_string_ptr(item->valuestring, p); + return print_string_ptr((unsigned char*)item->valuestring, p, hooks); } /* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item, const char *value, const char **ep); -static char *print_value(const cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_array(cJSON *item, const char *value, const char **ep); -static char *print_array(const cJSON *item, int depth, int fmt, printbuffer *p); -static const char *parse_object(cJSON *item, const char *value, const char **ep); -static char *print_object(const cJSON *item, int depth, int fmt, printbuffer *p); +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks); +static cJSON_bool print_value(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks); +static cJSON_bool print_array(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks); +static cJSON_bool print_object(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks); /* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) { - while (in && *in && ((unsigned char)*in<=32)) + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { - in++; + buffer->offset++; } - return in; + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; } /* Parse an object - create a new root, and populate. */ -cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated) +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { - const char *end = NULL; - /* use global error pointer if no specific one was given */ - const char **ep = return_parse_end ? return_parse_end : &global_ep; - cJSON *c = cJSON_New_Item(); - *ep = NULL; - if (!c) /* memory fail */ + parse_buffer buffer = { 0, 0, 0 }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) { - return NULL; + goto fail; } - end = parse_value(c, skip(value), ep); - if (!end) + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(&buffer), &global_hooks)) { /* parse failure. ep is set. */ - cJSON_Delete(c); - return NULL; + goto fail; } /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) { - end = skip(end); - if (*end) + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { - cJSON_Delete(c); - *ep = end; - return NULL; + goto fail; } } if (return_parse_end) { - *return_parse_end = end; + *return_parse_end = (const char*)buffer_at_offset(&buffer); } - return c; + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + else + { + global_error = local_error; + } + } + + return NULL; } /* Default options for cJSON_Parse */ -cJSON *cJSON_Parse(const char *value) +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) { return cJSON_ParseWithOpts(value, 0, 0); } +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(256); + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, 0, format, buffer, hooks)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + /* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(const cJSON *item) +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) { - return print_value(item, 0, 1, 0); + return (char*)print(item, true, &global_hooks); } -char *cJSON_PrintUnformatted(const cJSON *item) +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) { - return print_value(item, 0, 0, 0); + return (char*)print(item, false, &global_hooks); } -char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt) +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) { printbuffer p; - p.buffer = (char*)cJSON_malloc(prebuffer); + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); if (!p.buffer) { return NULL; } - p.length = prebuffer; + + p.length = (size_t)prebuffer; p.offset = 0; + p.noalloc = false; - return print_value(item, 0, fmt, &p); + if (!print_value(item, 0, fmt, &p, &global_hooks)) + { + return NULL; + } + + return (char*)p.buffer; } +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p; + + if (len < 0) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + return print_value(item, 0, fmt, &p, &global_hooks); +} /* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item, const char *value, const char **ep) +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks) { - if (!value) + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { - /* Fail on null. */ - return NULL; + return false; /* no input */ } /* parse the different types of values */ - if (!strncmp(value, "null", 4)) + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) { item->type = cJSON_NULL; - return value + 4; + input_buffer->offset += 4; + return true; } - if (!strncmp(value, "false", 5)) + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) { item->type = cJSON_False; - return value + 5; + input_buffer->offset += 5; + return true; } - if (!strncmp(value, "true", 4)) + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) { item->type = cJSON_True; item->valueint = 1; - return value + 4; + input_buffer->offset += 4; + return true; } - if (*value == '\"') + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { - return parse_string(item, value, ep); + return parse_string(item, input_buffer, hooks); } - if ((*value == '-') || ((*value >= '0') && (*value <= '9'))) + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) { - return parse_number(item, value); + return parse_number(item, input_buffer); } - if (*value == '[') + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { - return parse_array(item, value, ep); + return parse_array(item, input_buffer, hooks); } - if (*value == '{') + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { - return parse_object(item, value, ep); + return parse_object(item, input_buffer, hooks); } - /* failure. */ - *ep = value; - return NULL; + + return false; } /* Render a value to text. */ -static char *print_value(const cJSON *item, int depth, int fmt, printbuffer *p) +static cJSON_bool print_value(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks) { - char *out = NULL; + unsigned char *output = NULL; - if (!item) + if ((item == NULL) || (output_buffer == NULL)) { - return NULL; + return false; } - if (p) + + switch ((item->type) & 0xFF) { - switch ((item->type) & 0xFF) + case cJSON_NULL: + output = ensure(output_buffer, 5, hooks); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6, hooks); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5, hooks); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer, hooks); + + case cJSON_Raw: { - case cJSON_NULL: - out = ensure(p, 5); - if (out) - { - strcpy(out, "null"); - } - break; - case cJSON_False: - out = ensure(p, 6); - if (out) - { - strcpy(out, "false"); - } - break; - case cJSON_True: - out = ensure(p, 5); - if (out) + size_t raw_length = 0; + if (item->valuestring == NULL) + { + if (!output_buffer->noalloc) { - strcpy(out, "true"); + hooks->deallocate(output_buffer->buffer); } - break; - case cJSON_Number: - out = print_number(item, p); - break; - case cJSON_String: - out = print_string(item, p); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, p); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, p); - break; - } - } - else - { - switch ((item->type) & 0xFF) - { - case cJSON_NULL: - out = cJSON_strdup("null"); - break; - case cJSON_False: - out = cJSON_strdup("false"); - break; - case cJSON_True: - out = cJSON_strdup("true"); - break; - case cJSON_Number: - out = print_number(item, 0); - break; - case cJSON_String: - out = print_string(item, 0); - break; - case cJSON_Array: - out = print_array(item, depth, fmt, 0); - break; - case cJSON_Object: - out = print_object(item, depth, fmt, 0); - break; + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length, hooks); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; } - } - return out; + case cJSON_String: + return print_string(item, output_buffer, hooks); + + case cJSON_Array: + return print_array(item, depth, format, output_buffer, hooks); + + case cJSON_Object: + return print_object(item, depth, format, output_buffer, hooks); + + default: + return false; + } } /* Build an array from input text. */ -static const char *parse_array(cJSON *item,const char *value,const char **ep) +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks) { - cJSON *child = NULL; - if (*value != '[') - { - /* not an array! */ - *ep = value; - return NULL; - } + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; - item->type = cJSON_Array; - value = skip(value + 1); - if (*value == ']') + if (buffer_at_offset(input_buffer)[0] != '[') { - /* empty array. */ - return value + 1; + /* not an array */ + goto fail; } - item->child = child = cJSON_New_Item(); - if (!item->child) + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { - /* memory fail */ - return NULL; + /* empty array */ + goto success; } - /* skip any spacing, get the value. */ - value = skip(parse_value(child, skip(value), ep)); - if (!value) + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { - return NULL; + input_buffer->offset--; + goto fail; } + /* step back to character in front of the first element */ + input_buffer->offset--; /* loop through the comma separated array elements */ - while (*value == ',') + do { - cJSON *new_item = NULL; - if (!(new_item = cJSON_New_Item())) + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(hooks); + if (new_item == NULL) { - /* memory fail */ - return NULL; + goto fail; /* allocation failure */ } - /* add new item to end of the linked list */ - child->next = new_item; - new_item->prev = child; - child = new_item; - /* go to the next comma */ - value = skip(parse_value(child, skip(value + 1), ep)); - if (!value) + /* attach next item to list */ + if (head == NULL) { - /* memory fail */ - return NULL; + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer, hooks)) + { + goto fail; /* failed to parse value */ } + buffer_skip_whitespace(input_buffer); } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - if (*value == ']') + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { - /* end of array */ - return value + 1; + goto fail; /* expected end of array */ } - /* malformed. */ - *ep = value; +success: + item->type = cJSON_Array; + item->child = head; - return NULL; + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; } /* Render an array to text */ -static char *print_array(const cJSON *item, int depth, int fmt, printbuffer *p) -{ - char **entries; - char *out = NULL; - char *ptr = NULL; - char *ret = NULL; - int len = 5; - cJSON *child = item->child; - int numentries = 0; - int i = 0; - int fail = 0; - size_t tmplen = 0; - - /* How many entries in the array? */ - while (child) - { - numentries++; - child = child->next; - } +static cJSON_bool print_array(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; - /* Explicitly handle numentries == 0 */ - if (!numentries) + if (output_buffer == NULL) { - if (p) - { - out = ensure(p, 3); - } - else - { - out = (char*)cJSON_malloc(3); - } - if (out) - { - strcpy(out,"[]"); - } - - return out; + return false; } - if (p) + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1, hooks); + if (output_pointer == NULL) { - /* Compose the output array. */ - /* opening square bracket */ - i = p->offset; - ptr = ensure(p, 1); - if (!ptr) - { - return NULL; - } - *ptr = '['; - p->offset++; - - child = item->child; - while (child && !fail) - { - print_value(child, depth + 1, fmt, p); - p->offset = update(p); - if (child->next) - { - len = fmt ? 2 : 1; - ptr = ensure(p, len + 1); - if (!ptr) - { - return NULL; - } - *ptr++ = ','; - if(fmt) - { - *ptr++ = ' '; - } - *ptr = '\0'; - p->offset += len; - } - child = child->next; - } - ptr = ensure(p, 2); - if (!ptr) - { - return NULL; - } - *ptr++ = ']'; - *ptr = '\0'; - out = (p->buffer) + i; + return false; } - else - { - /* Allocate an array to hold the pointers to all printed values */ - entries = (char**)cJSON_malloc(numentries * sizeof(char*)); - if (!entries) - { - return NULL; - } - memset(entries, '\0', numentries * sizeof(char*)); - /* Retrieve all the results: */ - child = item->child; - while (child && !fail) - { - ret = print_value(child, depth + 1, fmt, 0); - entries[i++] = ret; - if (ret) - { - len += strlen(ret) + 2 + (fmt ? 1 : 0); - } - else - { - fail = 1; - } - child = child->next; - } + *output_pointer = '['; + output_buffer->offset++; - /* If we didn't fail, try to malloc the output string */ - if (!fail) - { - out = (char*)cJSON_malloc(len); - } - /* If that fails, we fail. */ - if (!out) + while (current_element != NULL) + { + if (!print_value(current_element, depth + 1, format, output_buffer, hooks)) { - fail = 1; + return false; } - - /* Handle failure. */ - if (fail) + update_offset(output_buffer); + if (current_element->next) { - /* free all the entries in the array */ - for (i = 0; i < numentries; i++) + length = (size_t) (format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1, hooks); + if (output_pointer == NULL) { - if (entries[i]) - { - cJSON_free(entries[i]); - } + return false; } - cJSON_free(entries); - return NULL; - } - - /* Compose the output array. */ - *out='['; - ptr = out + 1; - *ptr = '\0'; - for (i = 0; i < numentries; i++) - { - tmplen = strlen(entries[i]); - memcpy(ptr, entries[i], tmplen); - ptr += tmplen; - if (i != (numentries - 1)) + *output_pointer++ = ','; + if(format) { - *ptr++ = ','; - if(fmt) - { - *ptr++ = ' '; - } - *ptr = '\0'; + *output_pointer++ = ' '; } - cJSON_free(entries[i]); + *output_pointer = '\0'; + output_buffer->offset += length; } - cJSON_free(entries); - *ptr++ = ']'; - *ptr++ = '\0'; + current_element = current_element->next; } - return out; + output_pointer = ensure(output_buffer, 2, hooks); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + + return true; } /* Build an object from the text. */ -static const char *parse_object(cJSON *item, const char *value, const char **ep) +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks) { - cJSON *child = NULL; - if (*value != '{') - { - /* not an object! */ - *ep = value; - return NULL; - } - - item->type = cJSON_Object; - value = skip(value + 1); - if (*value == '}') - { - /* empty object. */ - return value + 1; - } + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; - child = cJSON_New_Item(); - item->child = child; - if (!item->child) + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { - return NULL; - } - /* parse first key */ - value = skip(parse_string(child, skip(value), ep)); - if (!value) - { - return NULL; + goto fail; /* not an object */ } - /* use string as key, not value */ - child->string = child->valuestring; - child->valuestring = NULL; - if (*value != ':') + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { - /* invalid object. */ - *ep = value; - return NULL; - } - /* skip any spacing, get the value. */ - value = skip(parse_value(child, skip(value + 1), ep)); - if (!value) - { - return NULL; + goto success; /* empty object */ } - while (*value == ',') - { - cJSON *new_item = NULL; - if (!(new_item = cJSON_New_Item())) - { - /* memory fail */ - return NULL; - } - /* add to linked list */ - child->next = new_item; - new_item->prev = child; - - child = new_item; - value = skip(parse_string(child, skip(value + 1), ep)); - if (!value) - { - return NULL; - } - - /* use string as key, not value */ - child->string = child->valuestring; - child->valuestring = NULL; - - if (*value != ':') - { - /* invalid object. */ - *ep = value; - return NULL; - } - /* skip any spacing, get the value. */ - value = skip(parse_value(child, skip(value + 1), ep)); - if (!value) - { - return NULL; - } - } - /* end of object */ - if (*value == '}') + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { - return value + 1; + input_buffer->offset--; + goto fail; } - /* malformed */ - *ep = value; - return NULL; -} - -/* Render an object to text. */ -static char *print_object(const cJSON *item, int depth, int fmt, printbuffer *p) -{ - char **entries = NULL; - char **names = NULL; - char *out = NULL; - char *ptr = NULL; - char *ret = NULL; - char *str = NULL; - int len = 7; - int i = 0; - int j = 0; - cJSON *child = item->child; - int numentries = 0; - int fail = 0; - size_t tmplen = 0; - - /* Count the number of entries. */ - while (child) - { - numentries++; - child = child->next; - } - - /* Explicitly handle empty object case */ - if (!numentries) + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { - if (p) + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(hooks); + if (new_item == NULL) { - out = ensure(p, fmt ? depth + 4 : 3); + goto fail; /* allocation failure */ } - else + + /* attach next item to list */ + if (head == NULL) { - out = (char*)cJSON_malloc(fmt ? depth + 4 : 3); + /* start the linked list */ + current_item = head = new_item; } - if (!out) + else { - return NULL; + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; } - ptr = out; - *ptr++ = '{'; - if (fmt) { - *ptr++ = '\n'; - for (i = 0; i < depth; i++) - { - *ptr++ = '\t'; - } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer, hooks)) + { + goto fail; /* faile to parse name */ } - *ptr++ = '}'; - *ptr++ = '\0'; + buffer_skip_whitespace(input_buffer); - return out; - } + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; - if (p) - { - /* Compose the output: */ - i = p->offset; - len = fmt ? 2 : 1; /* fmt: {\n */ - ptr = ensure(p, len + 1); - if (!ptr) + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { - return NULL; + goto fail; /* invalid object */ } - *ptr++ = '{'; - if (fmt) + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer, hooks)) { - *ptr++ = '\n'; + goto fail; /* failed to parse value */ } - *ptr = '\0'; - p->offset += len; + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); - child = item->child; - depth++; - while (child) - { - if (fmt) - { - ptr = ensure(p, depth); - if (!ptr) - { - return NULL; - } - for (j = 0; j < depth; j++) - { - *ptr++ = '\t'; - } - p->offset += depth; - } + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } - /* print key */ - print_string_ptr(child->string, p); - p->offset = update(p); +success: + item->type = cJSON_Object; + item->child = head; - len = fmt ? 2 : 1; - ptr = ensure(p, len); - if (!ptr) - { - return NULL; - } - *ptr++ = ':'; - if (fmt) - { - *ptr++ = '\t'; - } - p->offset+=len; + input_buffer->offset++; + return true; - /* print value */ - print_value(child, depth, fmt, p); - p->offset = update(p); +fail: + if (head != NULL) + { + cJSON_Delete(head); + } - /* print comma if not last */ - len = (fmt ? 1 : 0) + (child->next ? 1 : 0); - ptr = ensure(p, len + 1); - if (!ptr) - { - return NULL; - } - if (child->next) - { - *ptr++ = ','; - } + return false; +} - if (fmt) - { - *ptr++ = '\n'; - } - *ptr = '\0'; - p->offset += len; +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; - child = child->next; - } + if (output_buffer == NULL) + { + return false; + } - ptr = ensure(p, fmt ? (depth + 1) : 2); - if (!ptr) - { - return NULL; - } - if (fmt) - { - for (i = 0; i < (depth - 1); i++) - { - *ptr++ = '\t'; - } - } - *ptr++ = '}'; - *ptr = '\0'; - out = (p->buffer) + i; + /* Compose the output: */ + length = (size_t) (format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1, hooks); + if (output_pointer == NULL) + { + return false; } - else + + *output_pointer++ = '{'; + if (format) { - /* Allocate space for the names and the objects */ - entries = (char**)cJSON_malloc(numentries * sizeof(char*)); - if (!entries) - { - return NULL; - } - names = (char**)cJSON_malloc(numentries * sizeof(char*)); - if (!names) - { - cJSON_free(entries); - return NULL; - } - memset(entries, '\0', sizeof(char*) * numentries); - memset(names, '\0', sizeof(char*) * numentries); + *output_pointer++ = '\n'; + } + output_buffer->offset += length; - /* Collect all the results into our arrays: */ - child = item->child; - depth++; - if (fmt) - { - len += depth; - } - while (child && !fail) + while (current_item) + { + if (format) { - names[i] = str = print_string_ptr(child->string, 0); /* print key */ - entries[i++] = ret = print_value(child, depth, fmt, 0); - if (str && ret) + size_t i; + output_pointer = ensure(output_buffer, depth + 1, hooks); + if (output_pointer == NULL) { - len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); + return false; } - else + for (i = 0; i < depth + 1; i++) { - fail = 1; + *output_pointer++ = '\t'; } - child = child->next; + output_buffer->offset += depth + 1; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer, hooks)) + { + return false; } + update_offset(output_buffer); - /* Try to allocate the output string */ - if (!fail) + length = (size_t) (format ? 2 : 1); + output_pointer = ensure(output_buffer, length, hooks); + if (output_pointer == NULL) { - out = (char*)cJSON_malloc(len); + return false; } - if (!out) + *output_pointer++ = ':'; + if (format) { - fail = 1; + *output_pointer++ = '\t'; } + output_buffer->offset += length; - /* Handle failure */ - if (fail) + /* print value */ + if (!print_value(current_item, depth + 1, format, output_buffer, hooks)) { - /* free all the printed keys and values */ - for (i = 0; i < numentries; i++) - { - if (names[i]) - { - cJSON_free(names[i]); - } - if (entries[i]) - { - cJSON_free(entries[i]); - } - } - cJSON_free(names); - cJSON_free(entries); - return NULL; + return false; } + update_offset(output_buffer); - /* Compose the output: */ - *out = '{'; - ptr = out + 1; - if (fmt) + /* print comma if not last */ + length = (size_t) ((format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1, hooks); + if (output_pointer == NULL) { - *ptr++ = '\n'; + return false; } - *ptr = '\0'; - for (i = 0; i < numentries; i++) + if (current_item->next) { - if (fmt) - { - for (j = 0; j < depth; j++) - { - *ptr++='\t'; - } - } - tmplen = strlen(names[i]); - memcpy(ptr, names[i], tmplen); - ptr += tmplen; - *ptr++ = ':'; - if (fmt) - { - *ptr++ = '\t'; - } - strcpy(ptr, entries[i]); - ptr += strlen(entries[i]); - if (i != (numentries - 1)) - { - *ptr++ = ','; - } - if (fmt) - { - *ptr++ = '\n'; - } - *ptr = '\0'; - cJSON_free(names[i]); - cJSON_free(entries[i]); + *output_pointer++ = ','; } - cJSON_free(names); - cJSON_free(entries); - if (fmt) + if (format) { - for (i = 0; i < (depth - 1); i++) - { - *ptr++ = '\t'; - } + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, format ? (depth + 2) : 2, hooks); + if (output_pointer == NULL) + { + return false; + } + if (format) + { + size_t i; + for (i = 0; i < (depth); i++) + { + *output_pointer++ = '\t'; } - *ptr++ = '}'; - *ptr++ = '\0'; } + *output_pointer++ = '}'; + *output_pointer = '\0'; - return out; + return true; } /* Get Array size/item / object item. */ -int cJSON_GetArraySize(const cJSON *array) +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { cJSON *c = array->child; - int i = 0; + size_t i = 0; while(c) { i++; c = c->next; } - return i; + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)i; } -cJSON *cJSON_GetArrayItem(const cJSON *array, int item) +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item) { cJSON *c = array ? array->child : NULL; while (c && item > 0) @@ -1637,17 +1662,35 @@ cJSON *cJSON_GetArrayItem(const cJSON *array, int item) return c; } -cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string) +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string) { cJSON *c = object ? object->child : NULL; - while (c && cJSON_strcasecmp(c->string, string)) + while (c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string)) { c = c->next; } return c; } -int cJSON_HasObjectItem(const cJSON *object,const char *string) +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (string == NULL)) + { + return NULL; + } + + current_element = object->child; + while ((current_element != NULL) && (strcmp(string, current_element->string) != 0)) + { + current_element = current_element->next; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) { return cJSON_GetObjectItem(object, string) ? 1 : 0; } @@ -1660,9 +1703,9 @@ static void suffix_object(cJSON *prev, cJSON *item) } /* Utility for handling references. */ -static cJSON *create_reference(const cJSON *item) +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) { - cJSON *ref = cJSON_New_Item(); + cJSON *ref = cJSON_New_Item(hooks); if (!ref) { return NULL; @@ -1675,14 +1718,18 @@ static cJSON *create_reference(const cJSON *item) } /* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) { - cJSON *c = array->child; - if (!item) + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) { return; } - if (!c) + + child = array->child; + + if (child == NULL) { /* list is empty, start new one */ array->child = item; @@ -1690,33 +1737,31 @@ void cJSON_AddItemToArray(cJSON *array, cJSON *item) else { /* append to the end */ - while (c->next) + while (child->next) { - c = c->next; + child = child->next; } - suffix_object(c, item); + suffix_object(child, item); } } -void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) { - if (!item) - { - return; - } - - /* free old key and set new one */ - if (item->string) - { - cJSON_free(item->string); - } - item->string = cJSON_strdup(string); - - cJSON_AddItemToArray(object,item); + /* call cJSON_AddItemToObjectCS for code reuse */ + cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); + /* remove cJSON_StringIsConst flag */ + item->type &= ~cJSON_StringIsConst; } +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + /* Add an item to an object with constant string as key */ -void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { if (!item) { @@ -1724,24 +1769,27 @@ void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) } if (!(item->type & cJSON_StringIsConst) && item->string) { - cJSON_free(item->string); + global_hooks.deallocate(item->string); } item->string = (char*)string; item->type |= cJSON_StringIsConst; cJSON_AddItemToArray(object, item); } +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) { - cJSON_AddItemToArray(array, create_reference(item)); + cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); } -void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) { - cJSON_AddItemToObject(object, string, create_reference(item)); + cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); } -cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) +static cJSON *DetachItemFromArray(cJSON *array, size_t which) { cJSON *c = array->child; while (c && (which > 0)) @@ -1772,36 +1820,45 @@ cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) return c; } +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return DetachItemFromArray(array, (size_t)which); +} -void cJSON_DeleteItemFromArray(cJSON *array, int which) +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) { cJSON_Delete(cJSON_DetachItemFromArray(array, which)); } -cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) { - int i = 0; + size_t i = 0; cJSON *c = object->child; - while (c && cJSON_strcasecmp(c->string,string)) + while (c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string)) { i++; c = c->next; } if (c) { - return cJSON_DetachItemFromArray(object, i); + return DetachItemFromArray(object, i); } return NULL; } -void cJSON_DeleteItemFromObject(cJSON *object, const char *string) +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) { cJSON_Delete(cJSON_DetachItemFromObject(object, string)); } /* Replace array/object items with new ones. */ -void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { cJSON *c = array->child; while (c && (which > 0)) @@ -1827,7 +1884,7 @@ void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) } } -void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +static void ReplaceItemInArray(cJSON *array, size_t which, cJSON *newitem) { cJSON *c = array->child; while (c && (which > 0)) @@ -1856,12 +1913,21 @@ void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) c->next = c->prev = NULL; cJSON_Delete(c); } +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + ReplaceItemInArray(array, (size_t)which, newitem); +} -void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { - int i = 0; + size_t i = 0; cJSON *c = object->child; - while(c && cJSON_strcasecmp(c->string, string)) + while(c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string)) { i++; c = c->next; @@ -1871,18 +1937,18 @@ void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem /* free the old string if not const */ if (!(newitem->type & cJSON_StringIsConst) && newitem->string) { - cJSON_free(newitem->string); + global_hooks.deallocate(newitem->string); } - newitem->string = cJSON_strdup(string); - cJSON_ReplaceItemInArray(object, i, newitem); + newitem->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + ReplaceItemInArray(object, i, newitem); } } /* Create basic types: */ -cJSON *cJSON_CreateNull(void) +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_NULL; @@ -1891,9 +1957,9 @@ cJSON *cJSON_CreateNull(void) return item; } -cJSON *cJSON_CreateTrue(void) +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_True; @@ -1902,9 +1968,9 @@ cJSON *cJSON_CreateTrue(void) return item; } -cJSON *cJSON_CreateFalse(void) +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_False; @@ -1913,9 +1979,9 @@ cJSON *cJSON_CreateFalse(void) return item; } -cJSON *cJSON_CreateBool(int b) +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = b ? cJSON_True : cJSON_False; @@ -1924,26 +1990,56 @@ cJSON *cJSON_CreateBool(int b) return item; } -cJSON *cJSON_CreateNumber(double num) +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_Number; item->valuedouble = num; - item->valueint = (int)num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } } return item; } -cJSON *cJSON_CreateString(const char *string) +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type = cJSON_String; - item->valuestring = cJSON_strdup(string); + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); if(!item->valuestring) { cJSON_Delete(item); @@ -1954,9 +2050,9 @@ cJSON *cJSON_CreateString(const char *string) return item; } -cJSON *cJSON_CreateArray(void) +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if(item) { item->type=cJSON_Array; @@ -1965,9 +2061,9 @@ cJSON *cJSON_CreateArray(void) return item; } -cJSON *cJSON_CreateObject(void) +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) { - cJSON *item = cJSON_New_Item(); + cJSON *item = cJSON_New_Item(&global_hooks); if (item) { item->type = cJSON_Object; @@ -1977,13 +2073,20 @@ cJSON *cJSON_CreateObject(void) } /* Create Arrays: */ -cJSON *cJSON_CreateIntArray(const int *numbers, int count) +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) { - int i = 0; + size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; - cJSON *a = cJSON_CreateArray(); - for(i = 0; a && (i < count); i++) + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if (!n) @@ -2005,15 +2108,23 @@ cJSON *cJSON_CreateIntArray(const int *numbers, int count) return a; } -cJSON *cJSON_CreateFloatArray(const float *numbers, int count) +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) { - int i = 0; + size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; - cJSON *a = cJSON_CreateArray(); - for(i = 0; a && (i < count); i++) + cJSON *a = NULL; + + if (count < 0) { - n = cJSON_CreateNumber(numbers[i]); + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); if(!n) { cJSON_Delete(a); @@ -2033,13 +2144,21 @@ cJSON *cJSON_CreateFloatArray(const float *numbers, int count) return a; } -cJSON *cJSON_CreateDoubleArray(const double *numbers, int count) +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) { - int i = 0; + size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; - cJSON *a = cJSON_CreateArray(); - for(i = 0;a && (i < count); i++) + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if(!n) @@ -2061,13 +2180,21 @@ cJSON *cJSON_CreateDoubleArray(const double *numbers, int count) return a; } -cJSON *cJSON_CreateStringArray(const char **strings, int count) +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) { - int i = 0; + size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; - cJSON *a = cJSON_CreateArray(); - for (i = 0; a && (i < count); i++) + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateString(strings[i]); if(!n) @@ -2090,23 +2217,23 @@ cJSON *cJSON_CreateStringArray(const char **strings, int count) } /* Duplication */ -cJSON *cJSON_Duplicate(const cJSON *item, int recurse) +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { cJSON *newitem = NULL; - cJSON *cptr = NULL; - cJSON *nptr = NULL; + cJSON *child = NULL; + cJSON *next = NULL; cJSON *newchild = NULL; /* Bail on bad ptr */ if (!item) { - return NULL; + goto fail; } /* Create new item */ - newitem = cJSON_New_Item(); + newitem = cJSON_New_Item(&global_hooks); if (!newitem) { - return NULL; + goto fail; } /* Copy over all vars */ newitem->type = item->type & (~cJSON_IsReference); @@ -2114,20 +2241,18 @@ cJSON *cJSON_Duplicate(const cJSON *item, int recurse) newitem->valuedouble = item->valuedouble; if (item->valuestring) { - newitem->valuestring = cJSON_strdup(item->valuestring); + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); if (!newitem->valuestring) { - cJSON_Delete(newitem); - return NULL; + goto fail; } } if (item->string) { - newitem->string = cJSON_strdup(item->string); + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); if (!newitem->string) { - cJSON_Delete(newitem); - return NULL; + goto fail; } } /* If non-recursive, then we're done! */ @@ -2136,36 +2261,44 @@ cJSON *cJSON_Duplicate(const cJSON *item, int recurse) return newitem; } /* Walk the ->next chain for the child. */ - cptr = item->child; - while (cptr) + child = item->child; + while (child != NULL) { - newchild = cJSON_Duplicate(cptr, 1); /* Duplicate (with recurse) each item in the ->next chain */ + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ if (!newchild) { - cJSON_Delete(newitem); - return NULL; + goto fail; } - if (nptr) + if (next != NULL) { /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - nptr->next = newchild; - newchild->prev = nptr; - nptr = newchild; + next->next = newchild; + newchild->prev = next; + next = newchild; } else { /* Set newitem->child and move to it */ - newitem->child = newchild; nptr = newchild; + newitem->child = newchild; + next = newchild; } - cptr = cptr->next; + child = child->next; } return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; } -void cJSON_Minify(char *json) +CJSON_PUBLIC(void) cJSON_Minify(char *json) { - char *into = json; + unsigned char *into = (unsigned char*)json; while (*json) { if (*json == ' ') @@ -2205,24 +2338,124 @@ void cJSON_Minify(char *json) else if (*json == '\"') { /* string literals, which are \" sensitive. */ - *into++ = *json++; + *into++ = (unsigned char)*json++; while (*json && (*json != '\"')) { if (*json == '\\') { - *into++=*json++; + *into++ = (unsigned char)*json++; } - *into++ = *json++; + *into++ = (unsigned char)*json++; } - *into++ = *json++; + *into++ = (unsigned char)*json++; } else { /* All other characters. */ - *into++ = *json++; + *into++ = (unsigned char)*json++; } } /* and null-terminate. */ *into = '\0'; } + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} diff --git a/extlibs/cjson/cJSON.h b/extlibs/cjson/cJSON.h index dbbb739..56e6c60 100644 --- a/extlibs/cjson/cJSON.h +++ b/extlibs/cjson/cJSON.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble + 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 @@ -28,9 +28,15 @@ extern "C" { #endif +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 4 +#define CJSON_VERSION_PATCH 5 + #include /* cJSON Types: */ +#define cJSON_Invalid (0) #define cJSON_False (1 << 0) #define cJSON_True (1 << 1) #define cJSON_NULL (1 << 2) @@ -38,6 +44,7 @@ extern "C" #define cJSON_String (1 << 4) #define cJSON_Array (1 << 5) #define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_IsReference 256 #define cJSON_StringIsConst 512 @@ -54,7 +61,7 @@ typedef struct cJSON /* The type of the item, as above. */ int type; - /* The item's string, if type==cJSON_String */ + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; /* The item's number, if type==cJSON_Number */ int valueint; @@ -71,77 +78,140 @@ typedef struct cJSON_Hooks void (*free_fn)(void *ptr); } cJSON_Hooks; +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#else +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + /* Supply malloc, realloc and free functions to cJSON */ -extern void cJSON_InitHooks(cJSON_Hooks* hooks); +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern char *cJSON_Print(const cJSON *item); +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ -extern char *cJSON_PrintUnformatted(const cJSON *item); +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ -extern char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt); +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: If you are printing numbers, the buffer hat to be 63 bytes bigger then the printed JSON (worst case) */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -extern void cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); /* Returns the number of items in an array (or object). */ -extern int cJSON_GetArraySize(const cJSON *array); +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -extern cJSON *cJSON_GetArrayItem(const cJSON *array, int item); +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item); /* Get item "string" from object. Case insensitive. */ -extern cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string); -extern int cJSON_HasObjectItem(const cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern const char *cJSON_GetErrorPtr(void); +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +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); +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); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); /* These calls create a cJSON item of the appropriate type. */ -extern cJSON *cJSON_CreateNull(void); -extern cJSON *cJSON_CreateTrue(void); -extern cJSON *cJSON_CreateFalse(void); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(void); -extern cJSON *cJSON_CreateObject(void); +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_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); /* These utilities create an Array of count items. */ -extern cJSON *cJSON_CreateIntArray(const int *numbers, int count); -extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count); -extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count); -extern cJSON *cJSON_CreateStringArray(const char **strings, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); /* Append item to the specified array/object. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); -extern void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); /* Remove/Detatch items from Arrays/Objects. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which); -extern void cJSON_DeleteItemFromArray(cJSON *array, int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); /* Update array items. */ -extern void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ -extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ -extern cJSON *cJSON_Duplicate(const cJSON *item, int recurse); +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will need to be released. With recurse!=0, it will duplicate any children connected to the item. The item->next and ->prev pointers are always zero on return from Duplicate. */ /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */ -extern cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); -extern void cJSON_Minify(char *json); +CJSON_PUBLIC(void) cJSON_Minify(char *json); /* Macros for creating things quickly. */ #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) @@ -150,13 +220,16 @@ extern void cJSON_Minify(char *json); #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) +#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s)) /* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object,val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) -#define cJSON_SetNumberValue(object,val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val)) +#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)) /* Macro for iterating over an array */ -#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next) +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) #ifdef __cplusplus } diff --git a/resource/csdk/security/tool/SConscript b/resource/csdk/security/tool/SConscript index a4cd300..8c0289a 100644 --- a/resource/csdk/security/tool/SConscript +++ b/resource/csdk/security/tool/SConscript @@ -50,7 +50,9 @@ tools_env.PrependUnique(CPPPATH = ['#/extlibs/cjson', tools_env.AppendUnique(CXXFLAGS = ['-O2', '-g', '-Wall', '-Wextra', '-std=c++0x']) tools_env.AppendUnique(RPATH = [tools_env.get('BUILD_DIR')]) -SConscript('#/extlibs/cjson/SConscript') +if target_os in ['windows', 'msys_nt']: + # Macro needed for Windows builds to avoid __declspec(dllexport) and __declspec(dllimport) for cJSON APIs. + tools_env.AppendUnique(CPPDEFINES = ['CJSON_HIDE_SYMBOLS']) if target_os in ['msys_nt', 'windows']: # octbstack.dll doesn't export all the functions called by this app, so use static LIBs instead. diff --git a/service/coap-http-proxy/SConscript b/service/coap-http-proxy/SConscript index d1407b8..65419f6 100644 --- a/service/coap-http-proxy/SConscript +++ b/service/coap-http-proxy/SConscript @@ -54,7 +54,6 @@ if with_upstream_libcoap == '1': env.AppendUnique(CPPPATH = ['#extlibs/libcoap/libcoap/include']) else: env.AppendUnique(CPPPATH = ['#resource/csdk/connectivity/lib/libcoap-4.1.1/include']) -SConscript('#/extlibs/cjson/SConscript') local_env.PrependUnique(LIBS = ['cjson', 'oc', 'octbstack', 'oc_logger', 'connectivity_abstraction', 'coap']) if target_os not in ['windows']: diff --git a/service/simulator/ramlparser/SConscript b/service/simulator/ramlparser/SConscript index 1cee926..59ff637 100755 --- a/service/simulator/ramlparser/SConscript +++ b/service/simulator/ramlparser/SConscript @@ -24,7 +24,6 @@ lib_env = env.Clone() SConscript('#service/third_party_libs.scons', 'lib_env') raml_env = lib_env.Clone() -SConscript('#/extlibs/cjson/SConscript') SConscript('#/extlibs/yaml/SConscript') ###################################################################### # Build flags -- 2.7.4