189928cc5SHong Zhang /* 289928cc5SHong Zhang Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 389928cc5SHong Zhang 489928cc5SHong Zhang Permission is hereby granted, free of charge, to any person obtaining a copy 589928cc5SHong Zhang of this software and associated documentation files (the "Software"), to deal 689928cc5SHong Zhang in the Software without restriction, including without limitation the rights 789928cc5SHong Zhang to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 889928cc5SHong Zhang copies of the Software, and to permit persons to whom the Software is 989928cc5SHong Zhang furnished to do so, subject to the following conditions: 1089928cc5SHong Zhang 1189928cc5SHong Zhang The above copyright notice and this permission notice shall be included in 1289928cc5SHong Zhang all copies or substantial portions of the Software. 1389928cc5SHong Zhang 1489928cc5SHong Zhang THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1589928cc5SHong Zhang IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1689928cc5SHong Zhang FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1789928cc5SHong Zhang AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1889928cc5SHong Zhang LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1989928cc5SHong Zhang OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2089928cc5SHong Zhang THE SOFTWARE. 2189928cc5SHong Zhang */ 2289928cc5SHong Zhang 2389928cc5SHong Zhang /* cJSON */ 2489928cc5SHong Zhang /* JSON parser in C. */ 2589928cc5SHong Zhang 2689928cc5SHong Zhang /* disable warnings about old C89 functions in MSVC */ 2789928cc5SHong Zhang #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) 2889928cc5SHong Zhang #define _CRT_SECURE_NO_DEPRECATE 2989928cc5SHong Zhang #endif 3089928cc5SHong Zhang 3189928cc5SHong Zhang #ifdef __GNUC__ 3289928cc5SHong Zhang #pragma GCC visibility push(default) 3389928cc5SHong Zhang #endif 3489928cc5SHong Zhang #if defined(_MSC_VER) 3589928cc5SHong Zhang #pragma warning(push) 3689928cc5SHong Zhang /* disable warning about single line comments in system headers */ 3789928cc5SHong Zhang #pragma warning(disable : 4001) 3889928cc5SHong Zhang #endif 3989928cc5SHong Zhang 4089928cc5SHong Zhang #include <string.h> 4189928cc5SHong Zhang #include <stdio.h> 4289928cc5SHong Zhang #include <math.h> 4389928cc5SHong Zhang #include <stdlib.h> 4489928cc5SHong Zhang #include <limits.h> 4589928cc5SHong Zhang #include <ctype.h> 4689928cc5SHong Zhang #include <float.h> 4789928cc5SHong Zhang 4889928cc5SHong Zhang #ifdef ENABLE_LOCALES 4989928cc5SHong Zhang #include <locale.h> 5089928cc5SHong Zhang #endif 5189928cc5SHong Zhang 5289928cc5SHong Zhang #if defined(_MSC_VER) 5389928cc5SHong Zhang #pragma warning(pop) 5489928cc5SHong Zhang #endif 5589928cc5SHong Zhang #ifdef __GNUC__ 5689928cc5SHong Zhang #pragma GCC visibility pop 5789928cc5SHong Zhang #endif 5889928cc5SHong Zhang 5989928cc5SHong Zhang #include "cJSON.h" 6089928cc5SHong Zhang 6189928cc5SHong Zhang /* define our own boolean type */ 6289928cc5SHong Zhang #ifdef true 6389928cc5SHong Zhang #undef true 6489928cc5SHong Zhang #endif 6589928cc5SHong Zhang #define true ((cJSON_bool)1) 6689928cc5SHong Zhang 6789928cc5SHong Zhang #ifdef false 6889928cc5SHong Zhang #undef false 6989928cc5SHong Zhang #endif 7089928cc5SHong Zhang #define false ((cJSON_bool)0) 7189928cc5SHong Zhang 7289928cc5SHong Zhang /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ 7389928cc5SHong Zhang #ifndef isinf 74*f4f49eeaSPierre Jolivet #define isinf(d) (isnan(d - d) && !isnan(d)) 7589928cc5SHong Zhang #endif 7689928cc5SHong Zhang #ifndef isnan 7789928cc5SHong Zhang #define isnan(d) (d != d) 7889928cc5SHong Zhang #endif 7989928cc5SHong Zhang 8089928cc5SHong Zhang #ifndef NAN 8189928cc5SHong Zhang #ifdef _WIN32 8289928cc5SHong Zhang #define NAN sqrt(-1.0) 8389928cc5SHong Zhang #else 8489928cc5SHong Zhang #define NAN 0.0 / 0.0 8589928cc5SHong Zhang #endif 8689928cc5SHong Zhang #endif 8789928cc5SHong Zhang 8889928cc5SHong Zhang typedef struct { 8989928cc5SHong Zhang const unsigned char *json; 9089928cc5SHong Zhang size_t position; 9189928cc5SHong Zhang } error; 9289928cc5SHong Zhang static error global_error = {NULL, 0}; 9389928cc5SHong Zhang 9489928cc5SHong Zhang CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) 9589928cc5SHong Zhang { 9689928cc5SHong Zhang return (const char *)(global_error.json + global_error.position); 9789928cc5SHong Zhang } 9889928cc5SHong Zhang 9989928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item) 10089928cc5SHong Zhang { 10189928cc5SHong Zhang if (!cJSON_IsString(item)) { return NULL; } 10289928cc5SHong Zhang 10389928cc5SHong Zhang return item->valuestring; 10489928cc5SHong Zhang } 10589928cc5SHong Zhang 10689928cc5SHong Zhang CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item) 10789928cc5SHong Zhang { 10889928cc5SHong Zhang if (!cJSON_IsNumber(item)) { return (double)NAN; } 10989928cc5SHong Zhang 11089928cc5SHong Zhang return item->valuedouble; 11189928cc5SHong Zhang } 11289928cc5SHong Zhang 11389928cc5SHong Zhang /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ 11489928cc5SHong Zhang #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15) 11589928cc5SHong Zhang #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. 11689928cc5SHong Zhang #endif 11789928cc5SHong Zhang 11889928cc5SHong Zhang CJSON_PUBLIC(const char *) cJSON_Version(void) 11989928cc5SHong Zhang { 12089928cc5SHong Zhang static char version[15]; 121ae1eecf8SHong Zhang snprintf(version, sizeof(version) / sizeof(version[0]), "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); 12289928cc5SHong Zhang 12389928cc5SHong Zhang return version; 12489928cc5SHong Zhang } 12589928cc5SHong Zhang 12689928cc5SHong Zhang /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ 12789928cc5SHong Zhang static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) 12889928cc5SHong Zhang { 12989928cc5SHong Zhang if ((string1 == NULL) || (string2 == NULL)) { return 1; } 13089928cc5SHong Zhang 13189928cc5SHong Zhang if (string1 == string2) { return 0; } 13289928cc5SHong Zhang 13389928cc5SHong Zhang for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++) { 13489928cc5SHong Zhang if (*string1 == '\0') { return 0; } 13589928cc5SHong Zhang } 13689928cc5SHong Zhang 13789928cc5SHong Zhang return tolower(*string1) - tolower(*string2); 13889928cc5SHong Zhang } 13989928cc5SHong Zhang 14089928cc5SHong Zhang typedef struct internal_hooks { 14189928cc5SHong Zhang void *(CJSON_CDECL *allocate)(size_t size); 14289928cc5SHong Zhang void(CJSON_CDECL *deallocate)(void *pointer); 14389928cc5SHong Zhang void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); 14489928cc5SHong Zhang } internal_hooks; 14589928cc5SHong Zhang 14689928cc5SHong Zhang #if defined(_MSC_VER) 14789928cc5SHong Zhang /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ 14889928cc5SHong Zhang static void *CJSON_CDECL internal_malloc(size_t size) 14989928cc5SHong Zhang { 15089928cc5SHong Zhang return malloc(size); 15189928cc5SHong Zhang } 15289928cc5SHong Zhang static void CJSON_CDECL internal_free(void *pointer) 15389928cc5SHong Zhang { 15489928cc5SHong Zhang free(pointer); 15589928cc5SHong Zhang } 15689928cc5SHong Zhang static void *CJSON_CDECL internal_realloc(void *pointer, size_t size) 15789928cc5SHong Zhang { 15889928cc5SHong Zhang return realloc(pointer, size); 15989928cc5SHong Zhang } 16089928cc5SHong Zhang #else 16189928cc5SHong Zhang #define internal_malloc malloc 16289928cc5SHong Zhang #define internal_free free 16389928cc5SHong Zhang #define internal_realloc realloc 16489928cc5SHong Zhang #endif 16589928cc5SHong Zhang 16689928cc5SHong Zhang /* strlen of character literals resolved at compile time */ 16789928cc5SHong Zhang #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) 16889928cc5SHong Zhang 16989928cc5SHong Zhang static internal_hooks global_hooks = {internal_malloc, internal_free, internal_realloc}; 17089928cc5SHong Zhang 17189928cc5SHong Zhang static unsigned char *cJSON_strdup(const unsigned char *string, const internal_hooks *const hooks) 17289928cc5SHong Zhang { 17389928cc5SHong Zhang size_t length = 0; 17489928cc5SHong Zhang unsigned char *copy = NULL; 17589928cc5SHong Zhang 17689928cc5SHong Zhang if (string == NULL) { return NULL; } 17789928cc5SHong Zhang 17889928cc5SHong Zhang length = strlen((const char *)string) + sizeof(""); 17989928cc5SHong Zhang copy = (unsigned char *)hooks->allocate(length); 18089928cc5SHong Zhang if (copy == NULL) { return NULL; } 18189928cc5SHong Zhang memcpy(copy, string, length); 18289928cc5SHong Zhang 18389928cc5SHong Zhang return copy; 18489928cc5SHong Zhang } 18589928cc5SHong Zhang 18689928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks *hooks) 18789928cc5SHong Zhang { 18889928cc5SHong Zhang if (hooks == NULL) { 18989928cc5SHong Zhang /* Reset hooks */ 19089928cc5SHong Zhang global_hooks.allocate = malloc; 19189928cc5SHong Zhang global_hooks.deallocate = free; 19289928cc5SHong Zhang global_hooks.reallocate = realloc; 19389928cc5SHong Zhang return; 19489928cc5SHong Zhang } 19589928cc5SHong Zhang 19689928cc5SHong Zhang global_hooks.allocate = malloc; 19789928cc5SHong Zhang if (hooks->malloc_fn != NULL) { global_hooks.allocate = hooks->malloc_fn; } 19889928cc5SHong Zhang 19989928cc5SHong Zhang global_hooks.deallocate = free; 20089928cc5SHong Zhang if (hooks->free_fn != NULL) { global_hooks.deallocate = hooks->free_fn; } 20189928cc5SHong Zhang 20289928cc5SHong Zhang /* use realloc only if both free and malloc are used */ 20389928cc5SHong Zhang global_hooks.reallocate = NULL; 20489928cc5SHong Zhang if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) { global_hooks.reallocate = realloc; } 20589928cc5SHong Zhang } 20689928cc5SHong Zhang 20789928cc5SHong Zhang /* Internal constructor. */ 20889928cc5SHong Zhang static cJSON *cJSON_New_Item(const internal_hooks *const hooks) 20989928cc5SHong Zhang { 21089928cc5SHong Zhang cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON)); 21189928cc5SHong Zhang if (node) { memset(node, '\0', sizeof(cJSON)); } 21289928cc5SHong Zhang 21389928cc5SHong Zhang return node; 21489928cc5SHong Zhang } 21589928cc5SHong Zhang 21689928cc5SHong Zhang /* Delete a cJSON structure. */ 21789928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) 21889928cc5SHong Zhang { 21989928cc5SHong Zhang cJSON *next = NULL; 22089928cc5SHong Zhang while (item != NULL) { 22189928cc5SHong Zhang next = item->next; 22289928cc5SHong Zhang if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { cJSON_Delete(item->child); } 22389928cc5SHong Zhang if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { global_hooks.deallocate(item->valuestring); } 22489928cc5SHong Zhang if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { global_hooks.deallocate(item->string); } 22589928cc5SHong Zhang global_hooks.deallocate(item); 22689928cc5SHong Zhang item = next; 22789928cc5SHong Zhang } 22889928cc5SHong Zhang } 22989928cc5SHong Zhang 23089928cc5SHong Zhang /* get the decimal point character of the current locale */ 23189928cc5SHong Zhang static unsigned char get_decimal_point(void) 23289928cc5SHong Zhang { 23389928cc5SHong Zhang #ifdef ENABLE_LOCALES 23489928cc5SHong Zhang struct lconv *lconv = localeconv(); 23589928cc5SHong Zhang return (unsigned char)lconv->decimal_point[0]; 23689928cc5SHong Zhang #else 23789928cc5SHong Zhang return '.'; 23889928cc5SHong Zhang #endif 23989928cc5SHong Zhang } 24089928cc5SHong Zhang 24189928cc5SHong Zhang typedef struct { 24289928cc5SHong Zhang const unsigned char *content; 24389928cc5SHong Zhang size_t length; 24489928cc5SHong Zhang size_t offset; 24589928cc5SHong Zhang size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ 24689928cc5SHong Zhang internal_hooks hooks; 24789928cc5SHong Zhang } parse_buffer; 24889928cc5SHong Zhang 24989928cc5SHong Zhang /* check if the given size is left to read in a given parse buffer (starting with 1) */ 25089928cc5SHong Zhang #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) 25189928cc5SHong Zhang /* check if the buffer can be accessed at the given index (starting with 0) */ 25289928cc5SHong Zhang #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) 25389928cc5SHong Zhang #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) 25489928cc5SHong Zhang /* get a pointer to the buffer at the position */ 25589928cc5SHong Zhang #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) 25689928cc5SHong Zhang 25789928cc5SHong Zhang /* Parse the input text to generate a number, and populate the result into item. */ 25889928cc5SHong Zhang static cJSON_bool parse_number(cJSON *const item, parse_buffer *const input_buffer) 25989928cc5SHong Zhang { 26089928cc5SHong Zhang double number = 0; 26189928cc5SHong Zhang unsigned char *after_end = NULL; 26289928cc5SHong Zhang unsigned char number_c_string[64]; 26389928cc5SHong Zhang unsigned char decimal_point = get_decimal_point(); 26489928cc5SHong Zhang size_t i = 0; 26589928cc5SHong Zhang 26689928cc5SHong Zhang if ((input_buffer == NULL) || (input_buffer->content == NULL)) { return false; } 26789928cc5SHong Zhang 26889928cc5SHong Zhang /* copy the number into a temporary buffer and replace '.' with the decimal point 26989928cc5SHong Zhang * of the current locale (for strtod) 27089928cc5SHong Zhang * This also takes care of '\0' not necessarily being available for marking the end of the input */ 27189928cc5SHong Zhang for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) { 27289928cc5SHong Zhang switch (buffer_at_offset(input_buffer)[i]) { 27389928cc5SHong Zhang case '0': 27489928cc5SHong Zhang case '1': 27589928cc5SHong Zhang case '2': 27689928cc5SHong Zhang case '3': 27789928cc5SHong Zhang case '4': 27889928cc5SHong Zhang case '5': 27989928cc5SHong Zhang case '6': 28089928cc5SHong Zhang case '7': 28189928cc5SHong Zhang case '8': 28289928cc5SHong Zhang case '9': 28389928cc5SHong Zhang case '+': 28489928cc5SHong Zhang case '-': 28589928cc5SHong Zhang case 'e': 28689928cc5SHong Zhang case 'E': 28789928cc5SHong Zhang number_c_string[i] = buffer_at_offset(input_buffer)[i]; 28889928cc5SHong Zhang break; 28989928cc5SHong Zhang 29089928cc5SHong Zhang case '.': 29189928cc5SHong Zhang number_c_string[i] = decimal_point; 29289928cc5SHong Zhang break; 29389928cc5SHong Zhang 29489928cc5SHong Zhang default: 29589928cc5SHong Zhang goto loop_end; 29689928cc5SHong Zhang } 29789928cc5SHong Zhang } 29889928cc5SHong Zhang loop_end: 29989928cc5SHong Zhang number_c_string[i] = '\0'; 30089928cc5SHong Zhang 30189928cc5SHong Zhang number = strtod((const char *)number_c_string, (char **)&after_end); 30289928cc5SHong Zhang if (number_c_string == after_end) { return false; /* parse_error */ } 30389928cc5SHong Zhang 30489928cc5SHong Zhang item->valuedouble = number; 30589928cc5SHong Zhang 30689928cc5SHong Zhang /* use saturation in case of overflow */ 30789928cc5SHong Zhang if (number >= INT_MAX) { 30889928cc5SHong Zhang item->valueint = INT_MAX; 30989928cc5SHong Zhang } else if (number <= (double)INT_MIN) { 31089928cc5SHong Zhang item->valueint = INT_MIN; 31189928cc5SHong Zhang } else { 31289928cc5SHong Zhang item->valueint = (int)number; 31389928cc5SHong Zhang } 31489928cc5SHong Zhang 31589928cc5SHong Zhang item->type = cJSON_Number; 31689928cc5SHong Zhang 31789928cc5SHong Zhang input_buffer->offset += (size_t)(after_end - number_c_string); 31889928cc5SHong Zhang return true; 31989928cc5SHong Zhang } 32089928cc5SHong Zhang 32189928cc5SHong Zhang /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ 32289928cc5SHong Zhang CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) 32389928cc5SHong Zhang { 32489928cc5SHong Zhang if (number >= INT_MAX) { 32589928cc5SHong Zhang object->valueint = INT_MAX; 32689928cc5SHong Zhang } else if (number <= (double)INT_MIN) { 32789928cc5SHong Zhang object->valueint = INT_MIN; 32889928cc5SHong Zhang } else { 32989928cc5SHong Zhang object->valueint = (int)number; 33089928cc5SHong Zhang } 33189928cc5SHong Zhang 33289928cc5SHong Zhang return object->valuedouble = number; 33389928cc5SHong Zhang } 33489928cc5SHong Zhang 33589928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_SetValuestring(cJSON *object, const char *valuestring) 33689928cc5SHong Zhang { 33789928cc5SHong Zhang char *copy = NULL; 33889928cc5SHong Zhang /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ 33989928cc5SHong Zhang if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) { return NULL; } 34089928cc5SHong Zhang if (strlen(valuestring) <= strlen(object->valuestring)) { 34189928cc5SHong Zhang strcpy(object->valuestring, valuestring); 34289928cc5SHong Zhang return object->valuestring; 34389928cc5SHong Zhang } 34489928cc5SHong Zhang copy = (char *)cJSON_strdup((const unsigned char *)valuestring, &global_hooks); 34589928cc5SHong Zhang if (copy == NULL) { return NULL; } 34689928cc5SHong Zhang if (object->valuestring != NULL) { cJSON_free(object->valuestring); } 34789928cc5SHong Zhang object->valuestring = copy; 34889928cc5SHong Zhang 34989928cc5SHong Zhang return copy; 35089928cc5SHong Zhang } 35189928cc5SHong Zhang 35289928cc5SHong Zhang typedef struct { 35389928cc5SHong Zhang unsigned char *buffer; 35489928cc5SHong Zhang size_t length; 35589928cc5SHong Zhang size_t offset; 35689928cc5SHong Zhang size_t depth; /* current nesting depth (for formatted printing) */ 35789928cc5SHong Zhang cJSON_bool noalloc; 35889928cc5SHong Zhang cJSON_bool format; /* is this print a formatted print */ 35989928cc5SHong Zhang internal_hooks hooks; 36089928cc5SHong Zhang } printbuffer; 36189928cc5SHong Zhang 36289928cc5SHong Zhang /* realloc printbuffer if necessary to have at least "needed" bytes more */ 36389928cc5SHong Zhang static unsigned char *ensure(printbuffer *const p, size_t needed) 36489928cc5SHong Zhang { 36589928cc5SHong Zhang unsigned char *newbuffer = NULL; 36689928cc5SHong Zhang size_t newsize = 0; 36789928cc5SHong Zhang 36889928cc5SHong Zhang if ((p == NULL) || (p->buffer == NULL)) { return NULL; } 36989928cc5SHong Zhang 37089928cc5SHong Zhang if ((p->length > 0) && (p->offset >= p->length)) { 37189928cc5SHong Zhang /* make sure that offset is valid */ 37289928cc5SHong Zhang return NULL; 37389928cc5SHong Zhang } 37489928cc5SHong Zhang 37589928cc5SHong Zhang if (needed > INT_MAX) { 37689928cc5SHong Zhang /* sizes bigger than INT_MAX are currently not supported */ 37789928cc5SHong Zhang return NULL; 37889928cc5SHong Zhang } 37989928cc5SHong Zhang 38089928cc5SHong Zhang needed += p->offset + 1; 38189928cc5SHong Zhang if (needed <= p->length) { return p->buffer + p->offset; } 38289928cc5SHong Zhang 38389928cc5SHong Zhang if (p->noalloc) { return NULL; } 38489928cc5SHong Zhang 38589928cc5SHong Zhang /* calculate new buffer size */ 38689928cc5SHong Zhang if (needed > (INT_MAX / 2)) { 38789928cc5SHong Zhang /* overflow of int, use INT_MAX if possible */ 38889928cc5SHong Zhang if (needed <= INT_MAX) { 38989928cc5SHong Zhang newsize = INT_MAX; 39089928cc5SHong Zhang } else { 39189928cc5SHong Zhang return NULL; 39289928cc5SHong Zhang } 39389928cc5SHong Zhang } else { 39489928cc5SHong Zhang newsize = needed * 2; 39589928cc5SHong Zhang } 39689928cc5SHong Zhang 39789928cc5SHong Zhang if (p->hooks.reallocate != NULL) { 39889928cc5SHong Zhang /* reallocate with realloc if available */ 39989928cc5SHong Zhang newbuffer = (unsigned char *)p->hooks.reallocate(p->buffer, newsize); 40089928cc5SHong Zhang if (newbuffer == NULL) { 40189928cc5SHong Zhang p->hooks.deallocate(p->buffer); 40289928cc5SHong Zhang p->length = 0; 40389928cc5SHong Zhang p->buffer = NULL; 40489928cc5SHong Zhang 40589928cc5SHong Zhang return NULL; 40689928cc5SHong Zhang } 40789928cc5SHong Zhang } else { 40889928cc5SHong Zhang /* otherwise reallocate manually */ 40989928cc5SHong Zhang newbuffer = (unsigned char *)p->hooks.allocate(newsize); 41089928cc5SHong Zhang if (!newbuffer) { 41189928cc5SHong Zhang p->hooks.deallocate(p->buffer); 41289928cc5SHong Zhang p->length = 0; 41389928cc5SHong Zhang p->buffer = NULL; 41489928cc5SHong Zhang 41589928cc5SHong Zhang return NULL; 41689928cc5SHong Zhang } 41789928cc5SHong Zhang 41889928cc5SHong Zhang memcpy(newbuffer, p->buffer, p->offset + 1); 41989928cc5SHong Zhang p->hooks.deallocate(p->buffer); 42089928cc5SHong Zhang } 42189928cc5SHong Zhang p->length = newsize; 42289928cc5SHong Zhang p->buffer = newbuffer; 42389928cc5SHong Zhang 42489928cc5SHong Zhang return newbuffer + p->offset; 42589928cc5SHong Zhang } 42689928cc5SHong Zhang 42789928cc5SHong Zhang /* calculate the new length of the string in a printbuffer and update the offset */ 42889928cc5SHong Zhang static void update_offset(printbuffer *const buffer) 42989928cc5SHong Zhang { 43089928cc5SHong Zhang const unsigned char *buffer_pointer = NULL; 43189928cc5SHong Zhang if ((buffer == NULL) || (buffer->buffer == NULL)) { return; } 43289928cc5SHong Zhang buffer_pointer = buffer->buffer + buffer->offset; 43389928cc5SHong Zhang 43489928cc5SHong Zhang buffer->offset += strlen((const char *)buffer_pointer); 43589928cc5SHong Zhang } 43689928cc5SHong Zhang 43789928cc5SHong Zhang /* securely comparison of floating-point variables */ 43889928cc5SHong Zhang static cJSON_bool compare_double(double a, double b) 43989928cc5SHong Zhang { 44089928cc5SHong Zhang double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); 44189928cc5SHong Zhang return (fabs(a - b) <= maxVal * DBL_EPSILON); 44289928cc5SHong Zhang } 44389928cc5SHong Zhang 44489928cc5SHong Zhang /* Render the number nicely from the given item into a string. */ 44589928cc5SHong Zhang static cJSON_bool print_number(const cJSON *const item, printbuffer *const output_buffer) 44689928cc5SHong Zhang { 44789928cc5SHong Zhang unsigned char *output_pointer = NULL; 44889928cc5SHong Zhang double d = item->valuedouble; 44989928cc5SHong Zhang int length = 0; 45089928cc5SHong Zhang size_t i = 0; 45189928cc5SHong Zhang unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ 45289928cc5SHong Zhang unsigned char decimal_point = get_decimal_point(); 45389928cc5SHong Zhang double test = 0.0; 45489928cc5SHong Zhang 45589928cc5SHong Zhang if (output_buffer == NULL) { return false; } 45689928cc5SHong Zhang 45789928cc5SHong Zhang /* This checks for NaN and Infinity */ 45889928cc5SHong Zhang if (isnan(d) || isinf(d)) { 459ae1eecf8SHong Zhang length = snprintf((char *)number_buffer, sizeof(number_buffer) / sizeof(number_buffer[0]), "null"); 46089928cc5SHong Zhang } else if (d == (double)item->valueint) { 461ae1eecf8SHong Zhang length = snprintf((char *)number_buffer, sizeof(number_buffer) / sizeof(number_buffer[0]), "%d", item->valueint); 46289928cc5SHong Zhang } else { 463145b44c9SPierre Jolivet /* Try 15 decimal places of precision to avoid insignificant nonzero digits */ 464ae1eecf8SHong Zhang length = snprintf((char *)number_buffer, sizeof(number_buffer) / sizeof(number_buffer[0]), "%1.15g", d); 46589928cc5SHong Zhang 46689928cc5SHong Zhang /* Check whether the original double can be recovered */ 46789928cc5SHong Zhang if ((sscanf((char *)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { 46889928cc5SHong Zhang /* If not, print with 17 decimal places of precision */ 469ae1eecf8SHong Zhang length = snprintf((char *)number_buffer, sizeof(number_buffer) / sizeof(number_buffer[0]), "%1.17g", d); 47089928cc5SHong Zhang } 47189928cc5SHong Zhang } 47289928cc5SHong Zhang 473ae1eecf8SHong Zhang /* snprintf failed or buffer overrun occurred */ 47489928cc5SHong Zhang if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; } 47589928cc5SHong Zhang 47689928cc5SHong Zhang /* reserve appropriate space in the output */ 47789928cc5SHong Zhang output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); 47889928cc5SHong Zhang if (output_pointer == NULL) { return false; } 47989928cc5SHong Zhang 48089928cc5SHong Zhang /* copy the printed number to the output and replace locale 48189928cc5SHong Zhang * dependent decimal point with '.' */ 48289928cc5SHong Zhang for (i = 0; i < ((size_t)length); i++) { 48389928cc5SHong Zhang if (number_buffer[i] == decimal_point) { 48489928cc5SHong Zhang output_pointer[i] = '.'; 48589928cc5SHong Zhang continue; 48689928cc5SHong Zhang } 48789928cc5SHong Zhang 48889928cc5SHong Zhang output_pointer[i] = number_buffer[i]; 48989928cc5SHong Zhang } 49089928cc5SHong Zhang output_pointer[i] = '\0'; 49189928cc5SHong Zhang 49289928cc5SHong Zhang output_buffer->offset += (size_t)length; 49389928cc5SHong Zhang 49489928cc5SHong Zhang return true; 49589928cc5SHong Zhang } 49689928cc5SHong Zhang 49789928cc5SHong Zhang /* parse 4 digit hexadecimal number */ 49889928cc5SHong Zhang static unsigned parse_hex4(const unsigned char *const input) 49989928cc5SHong Zhang { 50089928cc5SHong Zhang unsigned int h = 0; 50189928cc5SHong Zhang size_t i = 0; 50289928cc5SHong Zhang 50389928cc5SHong Zhang for (i = 0; i < 4; i++) { 50489928cc5SHong Zhang /* parse digit */ 50589928cc5SHong Zhang if ((input[i] >= '0') && (input[i] <= '9')) { 50689928cc5SHong Zhang h += (unsigned int)input[i] - '0'; 50789928cc5SHong Zhang } else if ((input[i] >= 'A') && (input[i] <= 'F')) { 50889928cc5SHong Zhang h += (unsigned int)10 + input[i] - 'A'; 50989928cc5SHong Zhang } else if ((input[i] >= 'a') && (input[i] <= 'f')) { 51089928cc5SHong Zhang h += (unsigned int)10 + input[i] - 'a'; 51189928cc5SHong Zhang } else /* invalid */ 51289928cc5SHong Zhang { 51389928cc5SHong Zhang return 0; 51489928cc5SHong Zhang } 51589928cc5SHong Zhang 51689928cc5SHong Zhang if (i < 3) { 51789928cc5SHong Zhang /* shift left to make place for the next nibble */ 51889928cc5SHong Zhang h = h << 4; 51989928cc5SHong Zhang } 52089928cc5SHong Zhang } 52189928cc5SHong Zhang 52289928cc5SHong Zhang return h; 52389928cc5SHong Zhang } 52489928cc5SHong Zhang 52589928cc5SHong Zhang /* converts a UTF-16 literal to UTF-8 52689928cc5SHong Zhang * A literal can be one or two sequences of the form \uXXXX */ 52789928cc5SHong Zhang static unsigned char utf16_literal_to_utf8(const unsigned char *const input_pointer, const unsigned char *const input_end, unsigned char **output_pointer) 52889928cc5SHong Zhang { 52989928cc5SHong Zhang long unsigned int codepoint = 0; 53089928cc5SHong Zhang unsigned int first_code = 0; 53189928cc5SHong Zhang const unsigned char *first_sequence = input_pointer; 53289928cc5SHong Zhang unsigned char utf8_length = 0; 53389928cc5SHong Zhang unsigned char utf8_position = 0; 53489928cc5SHong Zhang unsigned char sequence_length = 0; 53589928cc5SHong Zhang unsigned char first_byte_mark = 0; 53689928cc5SHong Zhang 53789928cc5SHong Zhang if ((input_end - first_sequence) < 6) { 53889928cc5SHong Zhang /* input ends unexpectedly */ 53989928cc5SHong Zhang goto fail; 54089928cc5SHong Zhang } 54189928cc5SHong Zhang 54289928cc5SHong Zhang /* get the first utf16 sequence */ 54389928cc5SHong Zhang first_code = parse_hex4(first_sequence + 2); 54489928cc5SHong Zhang 54589928cc5SHong Zhang /* check that the code is valid */ 546*f4f49eeaSPierre Jolivet if ((first_code >= 0xDC00) && (first_code <= 0xDFFF)) { goto fail; } 54789928cc5SHong Zhang 54889928cc5SHong Zhang /* UTF16 surrogate pair */ 54989928cc5SHong Zhang if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { 55089928cc5SHong Zhang const unsigned char *second_sequence = first_sequence + 6; 55189928cc5SHong Zhang unsigned int second_code = 0; 55289928cc5SHong Zhang sequence_length = 12; /* \uXXXX\uXXXX */ 55389928cc5SHong Zhang 55489928cc5SHong Zhang if ((input_end - second_sequence) < 6) { 55589928cc5SHong Zhang /* input ends unexpectedly */ 55689928cc5SHong Zhang goto fail; 55789928cc5SHong Zhang } 55889928cc5SHong Zhang 55989928cc5SHong Zhang if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { 56089928cc5SHong Zhang /* missing second half of the surrogate pair */ 56189928cc5SHong Zhang goto fail; 56289928cc5SHong Zhang } 56389928cc5SHong Zhang 56489928cc5SHong Zhang /* get the second utf16 sequence */ 56589928cc5SHong Zhang second_code = parse_hex4(second_sequence + 2); 56689928cc5SHong Zhang /* check that the code is valid */ 56789928cc5SHong Zhang if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { 56889928cc5SHong Zhang /* invalid second half of the surrogate pair */ 56989928cc5SHong Zhang goto fail; 57089928cc5SHong Zhang } 57189928cc5SHong Zhang 57289928cc5SHong Zhang /* calculate the unicode codepoint from the surrogate pair */ 57389928cc5SHong Zhang codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); 57489928cc5SHong Zhang } else { 57589928cc5SHong Zhang sequence_length = 6; /* \uXXXX */ 57689928cc5SHong Zhang codepoint = first_code; 57789928cc5SHong Zhang } 57889928cc5SHong Zhang 57989928cc5SHong Zhang /* encode as UTF-8 58089928cc5SHong Zhang * takes at maximum 4 bytes to encode: 58189928cc5SHong Zhang * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 58289928cc5SHong Zhang if (codepoint < 0x80) { 58389928cc5SHong Zhang /* normal ascii, encoding 0xxxxxxx */ 58489928cc5SHong Zhang utf8_length = 1; 58589928cc5SHong Zhang } else if (codepoint < 0x800) { 58689928cc5SHong Zhang /* two bytes, encoding 110xxxxx 10xxxxxx */ 58789928cc5SHong Zhang utf8_length = 2; 58889928cc5SHong Zhang first_byte_mark = 0xC0; /* 11000000 */ 58989928cc5SHong Zhang } else if (codepoint < 0x10000) { 59089928cc5SHong Zhang /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ 59189928cc5SHong Zhang utf8_length = 3; 59289928cc5SHong Zhang first_byte_mark = 0xE0; /* 11100000 */ 59389928cc5SHong Zhang } else if (codepoint <= 0x10FFFF) { 59489928cc5SHong Zhang /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 59589928cc5SHong Zhang utf8_length = 4; 59689928cc5SHong Zhang first_byte_mark = 0xF0; /* 11110000 */ 59789928cc5SHong Zhang } else { 59889928cc5SHong Zhang /* invalid unicode codepoint */ 59989928cc5SHong Zhang goto fail; 60089928cc5SHong Zhang } 60189928cc5SHong Zhang 60289928cc5SHong Zhang /* encode as utf8 */ 60389928cc5SHong Zhang for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) { 60489928cc5SHong Zhang /* 10xxxxxx */ 60589928cc5SHong Zhang (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); 60689928cc5SHong Zhang codepoint >>= 6; 60789928cc5SHong Zhang } 60889928cc5SHong Zhang /* encode first byte */ 60989928cc5SHong Zhang if (utf8_length > 1) { 61089928cc5SHong Zhang (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); 61189928cc5SHong Zhang } else { 61289928cc5SHong Zhang (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); 61389928cc5SHong Zhang } 61489928cc5SHong Zhang 61589928cc5SHong Zhang *output_pointer += utf8_length; 61689928cc5SHong Zhang 61789928cc5SHong Zhang return sequence_length; 61889928cc5SHong Zhang 61989928cc5SHong Zhang fail: 62089928cc5SHong Zhang return 0; 62189928cc5SHong Zhang } 62289928cc5SHong Zhang 62389928cc5SHong Zhang /* Parse the input text into an unescaped cinput, and populate item. */ 62489928cc5SHong Zhang static cJSON_bool parse_string(cJSON *const item, parse_buffer *const input_buffer) 62589928cc5SHong Zhang { 62689928cc5SHong Zhang const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; 62789928cc5SHong Zhang const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; 62889928cc5SHong Zhang unsigned char *output_pointer = NULL; 62989928cc5SHong Zhang unsigned char *output = NULL; 63089928cc5SHong Zhang 63189928cc5SHong Zhang /* not a string */ 63289928cc5SHong Zhang if (buffer_at_offset(input_buffer)[0] != '\"') { goto fail; } 63389928cc5SHong Zhang 63489928cc5SHong Zhang { 63589928cc5SHong Zhang /* calculate approximate size of the output (overestimate) */ 63689928cc5SHong Zhang size_t allocation_length = 0; 63789928cc5SHong Zhang size_t skipped_bytes = 0; 63889928cc5SHong Zhang while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) { 63989928cc5SHong Zhang /* is escape sequence */ 64089928cc5SHong Zhang if (input_end[0] == '\\') { 64189928cc5SHong Zhang if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) { 64289928cc5SHong Zhang /* prevent buffer overflow when last input character is a backslash */ 64389928cc5SHong Zhang goto fail; 64489928cc5SHong Zhang } 64589928cc5SHong Zhang skipped_bytes++; 64689928cc5SHong Zhang input_end++; 64789928cc5SHong Zhang } 64889928cc5SHong Zhang input_end++; 64989928cc5SHong Zhang } 65089928cc5SHong Zhang if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) { goto fail; /* string ended unexpectedly */ } 65189928cc5SHong Zhang 65289928cc5SHong Zhang /* This is at most how much we need for the output */ 65389928cc5SHong Zhang allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes; 65489928cc5SHong Zhang output = (unsigned char *)input_buffer->hooks.allocate(allocation_length + sizeof("")); 65589928cc5SHong Zhang if (output == NULL) { goto fail; /* allocation failure */ } 65689928cc5SHong Zhang } 65789928cc5SHong Zhang 65889928cc5SHong Zhang output_pointer = output; 65989928cc5SHong Zhang /* loop through the string literal */ 66089928cc5SHong Zhang while (input_pointer < input_end) { 66189928cc5SHong Zhang if (*input_pointer != '\\') { 66289928cc5SHong Zhang *output_pointer++ = *input_pointer++; 66389928cc5SHong Zhang } 66489928cc5SHong Zhang /* escape sequence */ 66589928cc5SHong Zhang else { 66689928cc5SHong Zhang unsigned char sequence_length = 2; 66789928cc5SHong Zhang if ((input_end - input_pointer) < 1) { goto fail; } 66889928cc5SHong Zhang 66989928cc5SHong Zhang switch (input_pointer[1]) { 67089928cc5SHong Zhang case 'b': 67189928cc5SHong Zhang *output_pointer++ = '\b'; 67289928cc5SHong Zhang break; 67389928cc5SHong Zhang case 'f': 67489928cc5SHong Zhang *output_pointer++ = '\f'; 67589928cc5SHong Zhang break; 67689928cc5SHong Zhang case 'n': 67789928cc5SHong Zhang *output_pointer++ = '\n'; 67889928cc5SHong Zhang break; 67989928cc5SHong Zhang case 'r': 68089928cc5SHong Zhang *output_pointer++ = '\r'; 68189928cc5SHong Zhang break; 68289928cc5SHong Zhang case 't': 68389928cc5SHong Zhang *output_pointer++ = '\t'; 68489928cc5SHong Zhang break; 68589928cc5SHong Zhang case '\"': 68689928cc5SHong Zhang case '\\': 68789928cc5SHong Zhang case '/': 68889928cc5SHong Zhang *output_pointer++ = input_pointer[1]; 68989928cc5SHong Zhang break; 69089928cc5SHong Zhang 69189928cc5SHong Zhang /* UTF-16 literal */ 69289928cc5SHong Zhang case 'u': 69389928cc5SHong Zhang sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); 69489928cc5SHong Zhang if (sequence_length == 0) { 69589928cc5SHong Zhang /* failed to convert UTF16-literal to UTF-8 */ 69689928cc5SHong Zhang goto fail; 69789928cc5SHong Zhang } 69889928cc5SHong Zhang break; 69989928cc5SHong Zhang 70089928cc5SHong Zhang default: 70189928cc5SHong Zhang goto fail; 70289928cc5SHong Zhang } 70389928cc5SHong Zhang input_pointer += sequence_length; 70489928cc5SHong Zhang } 70589928cc5SHong Zhang } 70689928cc5SHong Zhang 70789928cc5SHong Zhang /* zero terminate the output */ 70889928cc5SHong Zhang *output_pointer = '\0'; 70989928cc5SHong Zhang 71089928cc5SHong Zhang item->type = cJSON_String; 71189928cc5SHong Zhang item->valuestring = (char *)output; 71289928cc5SHong Zhang 71389928cc5SHong Zhang input_buffer->offset = (size_t)(input_end - input_buffer->content); 71489928cc5SHong Zhang input_buffer->offset++; 71589928cc5SHong Zhang 71689928cc5SHong Zhang return true; 71789928cc5SHong Zhang 71889928cc5SHong Zhang fail: 71989928cc5SHong Zhang if (output != NULL) { input_buffer->hooks.deallocate(output); } 72089928cc5SHong Zhang 72189928cc5SHong Zhang if (input_pointer != NULL) { input_buffer->offset = (size_t)(input_pointer - input_buffer->content); } 72289928cc5SHong Zhang 72389928cc5SHong Zhang return false; 72489928cc5SHong Zhang } 72589928cc5SHong Zhang 72689928cc5SHong Zhang /* Render the cstring provided to an escaped version that can be printed. */ 72789928cc5SHong Zhang static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer) 72889928cc5SHong Zhang { 72989928cc5SHong Zhang const unsigned char *input_pointer = NULL; 73089928cc5SHong Zhang unsigned char *output = NULL; 73189928cc5SHong Zhang unsigned char *output_pointer = NULL; 73289928cc5SHong Zhang size_t output_length = 0; 73389928cc5SHong Zhang /* numbers of additional characters needed for escaping */ 73489928cc5SHong Zhang size_t escape_characters = 0; 73589928cc5SHong Zhang 73689928cc5SHong Zhang if (output_buffer == NULL) { return false; } 73789928cc5SHong Zhang 73889928cc5SHong Zhang /* empty string */ 73989928cc5SHong Zhang if (input == NULL) { 74089928cc5SHong Zhang output = ensure(output_buffer, sizeof("\"\"")); 74189928cc5SHong Zhang if (output == NULL) { return false; } 74289928cc5SHong Zhang strcpy((char *)output, "\"\""); 74389928cc5SHong Zhang 74489928cc5SHong Zhang return true; 74589928cc5SHong Zhang } 74689928cc5SHong Zhang 74789928cc5SHong Zhang /* set "flag" to 1 if something needs to be escaped */ 74889928cc5SHong Zhang for (input_pointer = input; *input_pointer; input_pointer++) { 74989928cc5SHong Zhang switch (*input_pointer) { 75089928cc5SHong Zhang case '\"': 75189928cc5SHong Zhang case '\\': 75289928cc5SHong Zhang case '\b': 75389928cc5SHong Zhang case '\f': 75489928cc5SHong Zhang case '\n': 75589928cc5SHong Zhang case '\r': 75689928cc5SHong Zhang case '\t': 75789928cc5SHong Zhang /* one character escape sequence */ 75889928cc5SHong Zhang escape_characters++; 75989928cc5SHong Zhang break; 76089928cc5SHong Zhang default: 76189928cc5SHong Zhang if (*input_pointer < 32) { 76289928cc5SHong Zhang /* UTF-16 escape sequence uXXXX */ 76389928cc5SHong Zhang escape_characters += 5; 76489928cc5SHong Zhang } 76589928cc5SHong Zhang break; 76689928cc5SHong Zhang } 76789928cc5SHong Zhang } 76889928cc5SHong Zhang output_length = (size_t)(input_pointer - input) + escape_characters; 76989928cc5SHong Zhang 77089928cc5SHong Zhang output = ensure(output_buffer, output_length + sizeof("\"\"")); 77189928cc5SHong Zhang if (output == NULL) { return false; } 77289928cc5SHong Zhang 77389928cc5SHong Zhang /* no characters have to be escaped */ 77489928cc5SHong Zhang if (escape_characters == 0) { 77589928cc5SHong Zhang output[0] = '\"'; 77689928cc5SHong Zhang memcpy(output + 1, input, output_length); 77789928cc5SHong Zhang output[output_length + 1] = '\"'; 77889928cc5SHong Zhang output[output_length + 2] = '\0'; 77989928cc5SHong Zhang 78089928cc5SHong Zhang return true; 78189928cc5SHong Zhang } 78289928cc5SHong Zhang 78389928cc5SHong Zhang output[0] = '\"'; 78489928cc5SHong Zhang output_pointer = output + 1; 78589928cc5SHong Zhang /* copy the string */ 78689928cc5SHong Zhang for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) { 78789928cc5SHong Zhang if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) { 78889928cc5SHong Zhang /* normal character, copy */ 78989928cc5SHong Zhang *output_pointer = *input_pointer; 79089928cc5SHong Zhang } else { 79189928cc5SHong Zhang /* character needs to be escaped */ 79289928cc5SHong Zhang *output_pointer++ = '\\'; 79389928cc5SHong Zhang switch (*input_pointer) { 79489928cc5SHong Zhang case '\\': 79589928cc5SHong Zhang *output_pointer = '\\'; 79689928cc5SHong Zhang break; 79789928cc5SHong Zhang case '\"': 79889928cc5SHong Zhang *output_pointer = '\"'; 79989928cc5SHong Zhang break; 80089928cc5SHong Zhang case '\b': 80189928cc5SHong Zhang *output_pointer = 'b'; 80289928cc5SHong Zhang break; 80389928cc5SHong Zhang case '\f': 80489928cc5SHong Zhang *output_pointer = 'f'; 80589928cc5SHong Zhang break; 80689928cc5SHong Zhang case '\n': 80789928cc5SHong Zhang *output_pointer = 'n'; 80889928cc5SHong Zhang break; 80989928cc5SHong Zhang case '\r': 81089928cc5SHong Zhang *output_pointer = 'r'; 81189928cc5SHong Zhang break; 81289928cc5SHong Zhang case '\t': 81389928cc5SHong Zhang *output_pointer = 't'; 81489928cc5SHong Zhang break; 81589928cc5SHong Zhang default: 81689928cc5SHong Zhang /* escape and print as unicode codepoint */ 817ae1eecf8SHong Zhang snprintf((char *)output_pointer, 6, "u%04x", *input_pointer); 818ae1eecf8SHong Zhang output_pointer += 5; 81989928cc5SHong Zhang break; 82089928cc5SHong Zhang } 82189928cc5SHong Zhang } 82289928cc5SHong Zhang } 82389928cc5SHong Zhang output[output_length + 1] = '\"'; 82489928cc5SHong Zhang output[output_length + 2] = '\0'; 82589928cc5SHong Zhang 82689928cc5SHong Zhang return true; 82789928cc5SHong Zhang } 82889928cc5SHong Zhang 82989928cc5SHong Zhang /* Invoke print_string_ptr (which is useful) on an item. */ 83089928cc5SHong Zhang static cJSON_bool print_string(const cJSON *const item, printbuffer *const p) 83189928cc5SHong Zhang { 83289928cc5SHong Zhang return print_string_ptr((unsigned char *)item->valuestring, p); 83389928cc5SHong Zhang } 83489928cc5SHong Zhang 83589928cc5SHong Zhang /* Predeclare these prototypes. */ 83689928cc5SHong Zhang static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer); 83789928cc5SHong Zhang static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer); 83889928cc5SHong Zhang static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer); 83989928cc5SHong Zhang static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer); 84089928cc5SHong Zhang static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer); 84189928cc5SHong Zhang static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer); 84289928cc5SHong Zhang 84389928cc5SHong Zhang /* Utility to jump whitespace and cr/lf */ 84489928cc5SHong Zhang static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer) 84589928cc5SHong Zhang { 84689928cc5SHong Zhang if ((buffer == NULL) || (buffer->content == NULL)) { return NULL; } 84789928cc5SHong Zhang 84889928cc5SHong Zhang if (cannot_access_at_index(buffer, 0)) { return buffer; } 84989928cc5SHong Zhang 85089928cc5SHong Zhang while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { buffer->offset++; } 85189928cc5SHong Zhang 85289928cc5SHong Zhang if (buffer->offset == buffer->length) { buffer->offset--; } 85389928cc5SHong Zhang 85489928cc5SHong Zhang return buffer; 85589928cc5SHong Zhang } 85689928cc5SHong Zhang 85789928cc5SHong Zhang /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ 85889928cc5SHong Zhang static parse_buffer *skip_utf8_bom(parse_buffer *const buffer) 85989928cc5SHong Zhang { 86089928cc5SHong Zhang if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) { return NULL; } 86189928cc5SHong Zhang 86289928cc5SHong Zhang if (can_access_at_index(buffer, 4) && (strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) { buffer->offset += 3; } 86389928cc5SHong Zhang 86489928cc5SHong Zhang return buffer; 86589928cc5SHong Zhang } 86689928cc5SHong Zhang 86789928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) 86889928cc5SHong Zhang { 86989928cc5SHong Zhang size_t buffer_length; 87089928cc5SHong Zhang 87189928cc5SHong Zhang if (NULL == value) { return NULL; } 87289928cc5SHong Zhang 87389928cc5SHong Zhang /* Adding null character size due to require_null_terminated. */ 87489928cc5SHong Zhang buffer_length = strlen(value) + sizeof(""); 87589928cc5SHong Zhang 87689928cc5SHong Zhang return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); 87789928cc5SHong Zhang } 87889928cc5SHong Zhang 87989928cc5SHong Zhang /* Parse an object - create a new root, and populate. */ 88089928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) 88189928cc5SHong Zhang { 88289928cc5SHong Zhang parse_buffer buffer = { 88389928cc5SHong Zhang 0, 0, 0, 0, {0, 0, 0} 88489928cc5SHong Zhang }; 88589928cc5SHong Zhang cJSON *item = NULL; 88689928cc5SHong Zhang 88789928cc5SHong Zhang /* reset error position */ 88889928cc5SHong Zhang global_error.json = NULL; 88989928cc5SHong Zhang global_error.position = 0; 89089928cc5SHong Zhang 89189928cc5SHong Zhang if (value == NULL || 0 == buffer_length) { goto fail; } 89289928cc5SHong Zhang 89389928cc5SHong Zhang buffer.content = (const unsigned char *)value; 89489928cc5SHong Zhang buffer.length = buffer_length; 89589928cc5SHong Zhang buffer.offset = 0; 89689928cc5SHong Zhang buffer.hooks = global_hooks; 89789928cc5SHong Zhang 89889928cc5SHong Zhang item = cJSON_New_Item(&global_hooks); 89989928cc5SHong Zhang if (item == NULL) /* memory fail */ 90089928cc5SHong Zhang { 90189928cc5SHong Zhang goto fail; 90289928cc5SHong Zhang } 90389928cc5SHong Zhang 90489928cc5SHong Zhang if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { 90589928cc5SHong Zhang /* parse failure. ep is set. */ 90689928cc5SHong Zhang goto fail; 90789928cc5SHong Zhang } 90889928cc5SHong Zhang 90989928cc5SHong Zhang /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ 91089928cc5SHong Zhang if (require_null_terminated) { 91189928cc5SHong Zhang buffer_skip_whitespace(&buffer); 91289928cc5SHong Zhang if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') { goto fail; } 91389928cc5SHong Zhang } 91489928cc5SHong Zhang if (return_parse_end) { *return_parse_end = (const char *)buffer_at_offset(&buffer); } 91589928cc5SHong Zhang 91689928cc5SHong Zhang return item; 91789928cc5SHong Zhang 91889928cc5SHong Zhang fail: 91989928cc5SHong Zhang if (item != NULL) { cJSON_Delete(item); } 92089928cc5SHong Zhang 92189928cc5SHong Zhang if (value != NULL) { 92289928cc5SHong Zhang error local_error; 92389928cc5SHong Zhang local_error.json = (const unsigned char *)value; 92489928cc5SHong Zhang local_error.position = 0; 92589928cc5SHong Zhang 92689928cc5SHong Zhang if (buffer.offset < buffer.length) { 92789928cc5SHong Zhang local_error.position = buffer.offset; 92889928cc5SHong Zhang } else if (buffer.length > 0) { 92989928cc5SHong Zhang local_error.position = buffer.length - 1; 93089928cc5SHong Zhang } 93189928cc5SHong Zhang 93289928cc5SHong Zhang if (return_parse_end != NULL) { *return_parse_end = (const char *)local_error.json + local_error.position; } 93389928cc5SHong Zhang 93489928cc5SHong Zhang global_error = local_error; 93589928cc5SHong Zhang } 93689928cc5SHong Zhang 93789928cc5SHong Zhang return NULL; 93889928cc5SHong Zhang } 93989928cc5SHong Zhang 94089928cc5SHong Zhang /* Default options for cJSON_Parse */ 94189928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) 94289928cc5SHong Zhang { 94389928cc5SHong Zhang return cJSON_ParseWithOpts(value, 0, 0); 94489928cc5SHong Zhang } 94589928cc5SHong Zhang 94689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) 94789928cc5SHong Zhang { 94889928cc5SHong Zhang return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); 94989928cc5SHong Zhang } 95089928cc5SHong Zhang 95189928cc5SHong Zhang #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) 95289928cc5SHong Zhang 95389928cc5SHong Zhang static unsigned char *print(const cJSON *const item, cJSON_bool format, const internal_hooks *const hooks) 95489928cc5SHong Zhang { 95589928cc5SHong Zhang static const size_t default_buffer_size = 256; 95689928cc5SHong Zhang printbuffer buffer[1]; 95789928cc5SHong Zhang unsigned char *printed = NULL; 95889928cc5SHong Zhang 95989928cc5SHong Zhang memset(buffer, 0, sizeof(buffer)); 96089928cc5SHong Zhang 96189928cc5SHong Zhang /* create buffer */ 96289928cc5SHong Zhang buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size); 96389928cc5SHong Zhang buffer->length = default_buffer_size; 96489928cc5SHong Zhang buffer->format = format; 96589928cc5SHong Zhang buffer->hooks = *hooks; 96689928cc5SHong Zhang if (buffer->buffer == NULL) { goto fail; } 96789928cc5SHong Zhang 96889928cc5SHong Zhang /* print the value */ 96989928cc5SHong Zhang if (!print_value(item, buffer)) { goto fail; } 97089928cc5SHong Zhang update_offset(buffer); 97189928cc5SHong Zhang 97289928cc5SHong Zhang /* check if reallocate is available */ 97389928cc5SHong Zhang if (hooks->reallocate != NULL) { 97489928cc5SHong Zhang printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1); 97589928cc5SHong Zhang if (printed == NULL) { goto fail; } 97689928cc5SHong Zhang buffer->buffer = NULL; 97789928cc5SHong Zhang } else /* otherwise copy the JSON over to a new buffer */ 97889928cc5SHong Zhang { 97989928cc5SHong Zhang printed = (unsigned char *)hooks->allocate(buffer->offset + 1); 98089928cc5SHong Zhang if (printed == NULL) { goto fail; } 98189928cc5SHong Zhang memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); 98289928cc5SHong Zhang printed[buffer->offset] = '\0'; /* just to be sure */ 98389928cc5SHong Zhang 98489928cc5SHong Zhang /* free the buffer */ 98589928cc5SHong Zhang hooks->deallocate(buffer->buffer); 98689928cc5SHong Zhang } 98789928cc5SHong Zhang 98889928cc5SHong Zhang return printed; 98989928cc5SHong Zhang 99089928cc5SHong Zhang fail: 99189928cc5SHong Zhang if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); } 99289928cc5SHong Zhang 99389928cc5SHong Zhang if (printed != NULL) { hooks->deallocate(printed); } 99489928cc5SHong Zhang 99589928cc5SHong Zhang return NULL; 99689928cc5SHong Zhang } 99789928cc5SHong Zhang 99889928cc5SHong Zhang /* Render a cJSON item/entity/structure to text. */ 99989928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) 100089928cc5SHong Zhang { 100189928cc5SHong Zhang return (char *)print(item, true, &global_hooks); 100289928cc5SHong Zhang } 100389928cc5SHong Zhang 100489928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) 100589928cc5SHong Zhang { 100689928cc5SHong Zhang return (char *)print(item, false, &global_hooks); 100789928cc5SHong Zhang } 100889928cc5SHong Zhang 100989928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) 101089928cc5SHong Zhang { 101189928cc5SHong Zhang printbuffer p = { 101289928cc5SHong Zhang 0, 0, 0, 0, 0, 0, {0, 0, 0} 101389928cc5SHong Zhang }; 101489928cc5SHong Zhang 101589928cc5SHong Zhang if (prebuffer < 0) { return NULL; } 101689928cc5SHong Zhang 101789928cc5SHong Zhang p.buffer = (unsigned char *)global_hooks.allocate((size_t)prebuffer); 101889928cc5SHong Zhang if (!p.buffer) { return NULL; } 101989928cc5SHong Zhang 102089928cc5SHong Zhang p.length = (size_t)prebuffer; 102189928cc5SHong Zhang p.offset = 0; 102289928cc5SHong Zhang p.noalloc = false; 102389928cc5SHong Zhang p.format = fmt; 102489928cc5SHong Zhang p.hooks = global_hooks; 102589928cc5SHong Zhang 102689928cc5SHong Zhang if (!print_value(item, &p)) { 102789928cc5SHong Zhang global_hooks.deallocate(p.buffer); 102889928cc5SHong Zhang return NULL; 102989928cc5SHong Zhang } 103089928cc5SHong Zhang 103189928cc5SHong Zhang return (char *)p.buffer; 103289928cc5SHong Zhang } 103389928cc5SHong Zhang 103489928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) 103589928cc5SHong Zhang { 103689928cc5SHong Zhang printbuffer p = { 103789928cc5SHong Zhang 0, 0, 0, 0, 0, 0, {0, 0, 0} 103889928cc5SHong Zhang }; 103989928cc5SHong Zhang 104089928cc5SHong Zhang if ((length < 0) || (buffer == NULL)) { return false; } 104189928cc5SHong Zhang 104289928cc5SHong Zhang p.buffer = (unsigned char *)buffer; 104389928cc5SHong Zhang p.length = (size_t)length; 104489928cc5SHong Zhang p.offset = 0; 104589928cc5SHong Zhang p.noalloc = true; 104689928cc5SHong Zhang p.format = format; 104789928cc5SHong Zhang p.hooks = global_hooks; 104889928cc5SHong Zhang 104989928cc5SHong Zhang return print_value(item, &p); 105089928cc5SHong Zhang } 105189928cc5SHong Zhang 105289928cc5SHong Zhang /* Parser core - when encountering text, process appropriately. */ 105389928cc5SHong Zhang static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer) 105489928cc5SHong Zhang { 105589928cc5SHong Zhang if ((input_buffer == NULL) || (input_buffer->content == NULL)) { return false; /* no input */ } 105689928cc5SHong Zhang 105789928cc5SHong Zhang /* parse the different types of values */ 105889928cc5SHong Zhang /* null */ 105989928cc5SHong Zhang if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)) { 106089928cc5SHong Zhang item->type = cJSON_NULL; 106189928cc5SHong Zhang input_buffer->offset += 4; 106289928cc5SHong Zhang return true; 106389928cc5SHong Zhang } 106489928cc5SHong Zhang /* false */ 106589928cc5SHong Zhang if (can_read(input_buffer, 5) && (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) == 0)) { 106689928cc5SHong Zhang item->type = cJSON_False; 106789928cc5SHong Zhang input_buffer->offset += 5; 106889928cc5SHong Zhang return true; 106989928cc5SHong Zhang } 107089928cc5SHong Zhang /* true */ 107189928cc5SHong Zhang if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)) { 107289928cc5SHong Zhang item->type = cJSON_True; 107389928cc5SHong Zhang item->valueint = 1; 107489928cc5SHong Zhang input_buffer->offset += 4; 107589928cc5SHong Zhang return true; 107689928cc5SHong Zhang } 107789928cc5SHong Zhang /* string */ 107889928cc5SHong Zhang if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) { return parse_string(item, input_buffer); } 107989928cc5SHong Zhang /* number */ 108089928cc5SHong Zhang 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, input_buffer); } 108189928cc5SHong Zhang /* array */ 108289928cc5SHong Zhang if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) { return parse_array(item, input_buffer); } 108389928cc5SHong Zhang /* object */ 108489928cc5SHong Zhang if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) { return parse_object(item, input_buffer); } 108589928cc5SHong Zhang 108689928cc5SHong Zhang return false; 108789928cc5SHong Zhang } 108889928cc5SHong Zhang 108989928cc5SHong Zhang /* Render a value to text. */ 109089928cc5SHong Zhang static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer) 109189928cc5SHong Zhang { 109289928cc5SHong Zhang unsigned char *output = NULL; 109389928cc5SHong Zhang 109489928cc5SHong Zhang if ((item == NULL) || (output_buffer == NULL)) { return false; } 109589928cc5SHong Zhang 1096*f4f49eeaSPierre Jolivet switch (item->type & 0xFF) { 109789928cc5SHong Zhang case cJSON_NULL: 109889928cc5SHong Zhang output = ensure(output_buffer, 5); 109989928cc5SHong Zhang if (output == NULL) { return false; } 110089928cc5SHong Zhang strcpy((char *)output, "null"); 110189928cc5SHong Zhang return true; 110289928cc5SHong Zhang 110389928cc5SHong Zhang case cJSON_False: 110489928cc5SHong Zhang output = ensure(output_buffer, 6); 110589928cc5SHong Zhang if (output == NULL) { return false; } 110689928cc5SHong Zhang strcpy((char *)output, "false"); 110789928cc5SHong Zhang return true; 110889928cc5SHong Zhang 110989928cc5SHong Zhang case cJSON_True: 111089928cc5SHong Zhang output = ensure(output_buffer, 5); 111189928cc5SHong Zhang if (output == NULL) { return false; } 111289928cc5SHong Zhang strcpy((char *)output, "true"); 111389928cc5SHong Zhang return true; 111489928cc5SHong Zhang 111589928cc5SHong Zhang case cJSON_Number: 111689928cc5SHong Zhang return print_number(item, output_buffer); 111789928cc5SHong Zhang 111889928cc5SHong Zhang case cJSON_Raw: { 111989928cc5SHong Zhang size_t raw_length = 0; 112089928cc5SHong Zhang if (item->valuestring == NULL) { return false; } 112189928cc5SHong Zhang 112289928cc5SHong Zhang raw_length = strlen(item->valuestring) + sizeof(""); 112389928cc5SHong Zhang output = ensure(output_buffer, raw_length); 112489928cc5SHong Zhang if (output == NULL) { return false; } 112589928cc5SHong Zhang memcpy(output, item->valuestring, raw_length); 112689928cc5SHong Zhang return true; 112789928cc5SHong Zhang } 112889928cc5SHong Zhang 112989928cc5SHong Zhang case cJSON_String: 113089928cc5SHong Zhang return print_string(item, output_buffer); 113189928cc5SHong Zhang 113289928cc5SHong Zhang case cJSON_Array: 113389928cc5SHong Zhang return print_array(item, output_buffer); 113489928cc5SHong Zhang 113589928cc5SHong Zhang case cJSON_Object: 113689928cc5SHong Zhang return print_object(item, output_buffer); 113789928cc5SHong Zhang 113889928cc5SHong Zhang default: 113989928cc5SHong Zhang return false; 114089928cc5SHong Zhang } 114189928cc5SHong Zhang } 114289928cc5SHong Zhang 114389928cc5SHong Zhang /* Build an array from input text. */ 114489928cc5SHong Zhang static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer) 114589928cc5SHong Zhang { 114689928cc5SHong Zhang cJSON *head = NULL; /* head of the linked list */ 114789928cc5SHong Zhang cJSON *current_item = NULL; 114889928cc5SHong Zhang 114989928cc5SHong Zhang if (input_buffer->depth >= CJSON_NESTING_LIMIT) { return false; /* to deeply nested */ } 115089928cc5SHong Zhang input_buffer->depth++; 115189928cc5SHong Zhang 115289928cc5SHong Zhang if (buffer_at_offset(input_buffer)[0] != '[') { 115389928cc5SHong Zhang /* not an array */ 115489928cc5SHong Zhang goto fail; 115589928cc5SHong Zhang } 115689928cc5SHong Zhang 115789928cc5SHong Zhang input_buffer->offset++; 115889928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 115989928cc5SHong Zhang if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) { 116089928cc5SHong Zhang /* empty array */ 116189928cc5SHong Zhang goto success; 116289928cc5SHong Zhang } 116389928cc5SHong Zhang 116489928cc5SHong Zhang /* check if we skipped to the end of the buffer */ 116589928cc5SHong Zhang if (cannot_access_at_index(input_buffer, 0)) { 116689928cc5SHong Zhang input_buffer->offset--; 116789928cc5SHong Zhang goto fail; 116889928cc5SHong Zhang } 116989928cc5SHong Zhang 117089928cc5SHong Zhang /* step back to character in front of the first element */ 117189928cc5SHong Zhang input_buffer->offset--; 117289928cc5SHong Zhang /* loop through the comma separated array elements */ 117389928cc5SHong Zhang do { 117489928cc5SHong Zhang /* allocate next item */ 1175*f4f49eeaSPierre Jolivet cJSON *new_item = cJSON_New_Item(&input_buffer->hooks); 117689928cc5SHong Zhang if (new_item == NULL) { goto fail; /* allocation failure */ } 117789928cc5SHong Zhang 117889928cc5SHong Zhang /* attach next item to list */ 117989928cc5SHong Zhang if (head == NULL) { 118089928cc5SHong Zhang /* start the linked list */ 118189928cc5SHong Zhang current_item = head = new_item; 118289928cc5SHong Zhang } else { 118389928cc5SHong Zhang /* add to the end and advance */ 118489928cc5SHong Zhang current_item->next = new_item; 118589928cc5SHong Zhang new_item->prev = current_item; 118689928cc5SHong Zhang current_item = new_item; 118789928cc5SHong Zhang } 118889928cc5SHong Zhang 118989928cc5SHong Zhang /* parse next value */ 119089928cc5SHong Zhang input_buffer->offset++; 119189928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 119289928cc5SHong Zhang if (!parse_value(current_item, input_buffer)) { goto fail; /* failed to parse value */ } 119389928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 119489928cc5SHong Zhang } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 119589928cc5SHong Zhang 119689928cc5SHong Zhang if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') { goto fail; /* expected end of array */ } 119789928cc5SHong Zhang 119889928cc5SHong Zhang success: 119989928cc5SHong Zhang input_buffer->depth--; 120089928cc5SHong Zhang 120189928cc5SHong Zhang if (head != NULL) { head->prev = current_item; } 120289928cc5SHong Zhang 120389928cc5SHong Zhang item->type = cJSON_Array; 120489928cc5SHong Zhang item->child = head; 120589928cc5SHong Zhang 120689928cc5SHong Zhang input_buffer->offset++; 120789928cc5SHong Zhang 120889928cc5SHong Zhang return true; 120989928cc5SHong Zhang 121089928cc5SHong Zhang fail: 121189928cc5SHong Zhang if (head != NULL) { cJSON_Delete(head); } 121289928cc5SHong Zhang 121389928cc5SHong Zhang return false; 121489928cc5SHong Zhang } 121589928cc5SHong Zhang 121689928cc5SHong Zhang /* Render an array to text */ 121789928cc5SHong Zhang static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer) 121889928cc5SHong Zhang { 121989928cc5SHong Zhang unsigned char *output_pointer = NULL; 122089928cc5SHong Zhang size_t length = 0; 122189928cc5SHong Zhang cJSON *current_element = item->child; 122289928cc5SHong Zhang 122389928cc5SHong Zhang if (output_buffer == NULL) { return false; } 122489928cc5SHong Zhang 122589928cc5SHong Zhang /* Compose the output array. */ 122689928cc5SHong Zhang /* opening square bracket */ 122789928cc5SHong Zhang output_pointer = ensure(output_buffer, 1); 122889928cc5SHong Zhang if (output_pointer == NULL) { return false; } 122989928cc5SHong Zhang 123089928cc5SHong Zhang *output_pointer = '['; 123189928cc5SHong Zhang output_buffer->offset++; 123289928cc5SHong Zhang output_buffer->depth++; 123389928cc5SHong Zhang 123489928cc5SHong Zhang while (current_element != NULL) { 123589928cc5SHong Zhang if (!print_value(current_element, output_buffer)) { return false; } 123689928cc5SHong Zhang update_offset(output_buffer); 123789928cc5SHong Zhang if (current_element->next) { 123889928cc5SHong Zhang length = (size_t)(output_buffer->format ? 2 : 1); 123989928cc5SHong Zhang output_pointer = ensure(output_buffer, length + 1); 124089928cc5SHong Zhang if (output_pointer == NULL) { return false; } 124189928cc5SHong Zhang *output_pointer++ = ','; 124289928cc5SHong Zhang if (output_buffer->format) { *output_pointer++ = ' '; } 124389928cc5SHong Zhang *output_pointer = '\0'; 124489928cc5SHong Zhang output_buffer->offset += length; 124589928cc5SHong Zhang } 124689928cc5SHong Zhang current_element = current_element->next; 124789928cc5SHong Zhang } 124889928cc5SHong Zhang 124989928cc5SHong Zhang output_pointer = ensure(output_buffer, 2); 125089928cc5SHong Zhang if (output_pointer == NULL) { return false; } 125189928cc5SHong Zhang *output_pointer++ = ']'; 125289928cc5SHong Zhang *output_pointer = '\0'; 125389928cc5SHong Zhang output_buffer->depth--; 125489928cc5SHong Zhang 125589928cc5SHong Zhang return true; 125689928cc5SHong Zhang } 125789928cc5SHong Zhang 125889928cc5SHong Zhang /* Build an object from the text. */ 125989928cc5SHong Zhang static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer) 126089928cc5SHong Zhang { 126189928cc5SHong Zhang cJSON *head = NULL; /* linked list head */ 126289928cc5SHong Zhang cJSON *current_item = NULL; 126389928cc5SHong Zhang 126489928cc5SHong Zhang if (input_buffer->depth >= CJSON_NESTING_LIMIT) { return false; /* to deeply nested */ } 126589928cc5SHong Zhang input_buffer->depth++; 126689928cc5SHong Zhang 126789928cc5SHong Zhang if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) { goto fail; /* not an object */ } 126889928cc5SHong Zhang 126989928cc5SHong Zhang input_buffer->offset++; 127089928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 127189928cc5SHong Zhang if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) { goto success; /* empty object */ } 127289928cc5SHong Zhang 127389928cc5SHong Zhang /* check if we skipped to the end of the buffer */ 127489928cc5SHong Zhang if (cannot_access_at_index(input_buffer, 0)) { 127589928cc5SHong Zhang input_buffer->offset--; 127689928cc5SHong Zhang goto fail; 127789928cc5SHong Zhang } 127889928cc5SHong Zhang 127989928cc5SHong Zhang /* step back to character in front of the first element */ 128089928cc5SHong Zhang input_buffer->offset--; 128189928cc5SHong Zhang /* loop through the comma separated array elements */ 128289928cc5SHong Zhang do { 128389928cc5SHong Zhang /* allocate next item */ 1284*f4f49eeaSPierre Jolivet cJSON *new_item = cJSON_New_Item(&input_buffer->hooks); 128589928cc5SHong Zhang if (new_item == NULL) { goto fail; /* allocation failure */ } 128689928cc5SHong Zhang 128789928cc5SHong Zhang /* attach next item to list */ 128889928cc5SHong Zhang if (head == NULL) { 128989928cc5SHong Zhang /* start the linked list */ 129089928cc5SHong Zhang current_item = head = new_item; 129189928cc5SHong Zhang } else { 129289928cc5SHong Zhang /* add to the end and advance */ 129389928cc5SHong Zhang current_item->next = new_item; 129489928cc5SHong Zhang new_item->prev = current_item; 129589928cc5SHong Zhang current_item = new_item; 129689928cc5SHong Zhang } 129789928cc5SHong Zhang 129889928cc5SHong Zhang /* parse the name of the child */ 129989928cc5SHong Zhang input_buffer->offset++; 130089928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 130189928cc5SHong Zhang if (!parse_string(current_item, input_buffer)) { goto fail; /* failed to parse name */ } 130289928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 130389928cc5SHong Zhang 130489928cc5SHong Zhang /* swap valuestring and string, because we parsed the name */ 130589928cc5SHong Zhang current_item->string = current_item->valuestring; 130689928cc5SHong Zhang current_item->valuestring = NULL; 130789928cc5SHong Zhang 130889928cc5SHong Zhang if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) { goto fail; /* invalid object */ } 130989928cc5SHong Zhang 131089928cc5SHong Zhang /* parse the value */ 131189928cc5SHong Zhang input_buffer->offset++; 131289928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 131389928cc5SHong Zhang if (!parse_value(current_item, input_buffer)) { goto fail; /* failed to parse value */ } 131489928cc5SHong Zhang buffer_skip_whitespace(input_buffer); 131589928cc5SHong Zhang } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); 131689928cc5SHong Zhang 131789928cc5SHong Zhang if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) { goto fail; /* expected end of object */ } 131889928cc5SHong Zhang 131989928cc5SHong Zhang success: 132089928cc5SHong Zhang input_buffer->depth--; 132189928cc5SHong Zhang 132289928cc5SHong Zhang if (head != NULL) { head->prev = current_item; } 132389928cc5SHong Zhang 132489928cc5SHong Zhang item->type = cJSON_Object; 132589928cc5SHong Zhang item->child = head; 132689928cc5SHong Zhang 132789928cc5SHong Zhang input_buffer->offset++; 132889928cc5SHong Zhang return true; 132989928cc5SHong Zhang 133089928cc5SHong Zhang fail: 133189928cc5SHong Zhang if (head != NULL) { cJSON_Delete(head); } 133289928cc5SHong Zhang 133389928cc5SHong Zhang return false; 133489928cc5SHong Zhang } 133589928cc5SHong Zhang 133689928cc5SHong Zhang /* Render an object to text. */ 133789928cc5SHong Zhang static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer) 133889928cc5SHong Zhang { 133989928cc5SHong Zhang unsigned char *output_pointer = NULL; 134089928cc5SHong Zhang size_t length = 0; 134189928cc5SHong Zhang cJSON *current_item = item->child; 134289928cc5SHong Zhang 134389928cc5SHong Zhang if (output_buffer == NULL) { return false; } 134489928cc5SHong Zhang 134589928cc5SHong Zhang /* Compose the output: */ 134689928cc5SHong Zhang length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ 134789928cc5SHong Zhang output_pointer = ensure(output_buffer, length + 1); 134889928cc5SHong Zhang if (output_pointer == NULL) { return false; } 134989928cc5SHong Zhang 135089928cc5SHong Zhang *output_pointer++ = '{'; 135189928cc5SHong Zhang output_buffer->depth++; 135289928cc5SHong Zhang if (output_buffer->format) { *output_pointer++ = '\n'; } 135389928cc5SHong Zhang output_buffer->offset += length; 135489928cc5SHong Zhang 135589928cc5SHong Zhang while (current_item) { 135689928cc5SHong Zhang if (output_buffer->format) { 135789928cc5SHong Zhang size_t i; 135889928cc5SHong Zhang output_pointer = ensure(output_buffer, output_buffer->depth); 135989928cc5SHong Zhang if (output_pointer == NULL) { return false; } 136089928cc5SHong Zhang for (i = 0; i < output_buffer->depth; i++) { *output_pointer++ = '\t'; } 136189928cc5SHong Zhang output_buffer->offset += output_buffer->depth; 136289928cc5SHong Zhang } 136389928cc5SHong Zhang 136489928cc5SHong Zhang /* print key */ 136589928cc5SHong Zhang if (!print_string_ptr((unsigned char *)current_item->string, output_buffer)) { return false; } 136689928cc5SHong Zhang update_offset(output_buffer); 136789928cc5SHong Zhang 136889928cc5SHong Zhang length = (size_t)(output_buffer->format ? 2 : 1); 136989928cc5SHong Zhang output_pointer = ensure(output_buffer, length); 137089928cc5SHong Zhang if (output_pointer == NULL) { return false; } 137189928cc5SHong Zhang *output_pointer++ = ':'; 137289928cc5SHong Zhang if (output_buffer->format) { *output_pointer++ = '\t'; } 137389928cc5SHong Zhang output_buffer->offset += length; 137489928cc5SHong Zhang 137589928cc5SHong Zhang /* print value */ 137689928cc5SHong Zhang if (!print_value(current_item, output_buffer)) { return false; } 137789928cc5SHong Zhang update_offset(output_buffer); 137889928cc5SHong Zhang 137989928cc5SHong Zhang /* print comma if not last */ 138089928cc5SHong Zhang length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); 138189928cc5SHong Zhang output_pointer = ensure(output_buffer, length + 1); 138289928cc5SHong Zhang if (output_pointer == NULL) { return false; } 138389928cc5SHong Zhang if (current_item->next) { *output_pointer++ = ','; } 138489928cc5SHong Zhang 138589928cc5SHong Zhang if (output_buffer->format) { *output_pointer++ = '\n'; } 138689928cc5SHong Zhang *output_pointer = '\0'; 138789928cc5SHong Zhang output_buffer->offset += length; 138889928cc5SHong Zhang 138989928cc5SHong Zhang current_item = current_item->next; 139089928cc5SHong Zhang } 139189928cc5SHong Zhang 139289928cc5SHong Zhang output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); 139389928cc5SHong Zhang if (output_pointer == NULL) { return false; } 139489928cc5SHong Zhang if (output_buffer->format) { 139589928cc5SHong Zhang size_t i; 139689928cc5SHong Zhang for (i = 0; i < (output_buffer->depth - 1); i++) { *output_pointer++ = '\t'; } 139789928cc5SHong Zhang } 139889928cc5SHong Zhang *output_pointer++ = '}'; 139989928cc5SHong Zhang *output_pointer = '\0'; 140089928cc5SHong Zhang output_buffer->depth--; 140189928cc5SHong Zhang 140289928cc5SHong Zhang return true; 140389928cc5SHong Zhang } 140489928cc5SHong Zhang 140589928cc5SHong Zhang /* Get Array size/item / object item. */ 140689928cc5SHong Zhang CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) 140789928cc5SHong Zhang { 140889928cc5SHong Zhang cJSON *child = NULL; 140989928cc5SHong Zhang size_t size = 0; 141089928cc5SHong Zhang 141189928cc5SHong Zhang if (array == NULL) { return 0; } 141289928cc5SHong Zhang 141389928cc5SHong Zhang child = array->child; 141489928cc5SHong Zhang 141589928cc5SHong Zhang while (child != NULL) { 141689928cc5SHong Zhang size++; 141789928cc5SHong Zhang child = child->next; 141889928cc5SHong Zhang } 141989928cc5SHong Zhang 142089928cc5SHong Zhang /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ 142189928cc5SHong Zhang 142289928cc5SHong Zhang return (int)size; 142389928cc5SHong Zhang } 142489928cc5SHong Zhang 142589928cc5SHong Zhang static cJSON *get_array_item(const cJSON *array, size_t index) 142689928cc5SHong Zhang { 142789928cc5SHong Zhang cJSON *current_child = NULL; 142889928cc5SHong Zhang 142989928cc5SHong Zhang if (array == NULL) { return NULL; } 143089928cc5SHong Zhang 143189928cc5SHong Zhang current_child = array->child; 143289928cc5SHong Zhang while ((current_child != NULL) && (index > 0)) { 143389928cc5SHong Zhang index--; 143489928cc5SHong Zhang current_child = current_child->next; 143589928cc5SHong Zhang } 143689928cc5SHong Zhang 143789928cc5SHong Zhang return current_child; 143889928cc5SHong Zhang } 143989928cc5SHong Zhang 144089928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) 144189928cc5SHong Zhang { 144289928cc5SHong Zhang if (index < 0) { return NULL; } 144389928cc5SHong Zhang 144489928cc5SHong Zhang return get_array_item(array, (size_t)index); 144589928cc5SHong Zhang } 144689928cc5SHong Zhang 144789928cc5SHong Zhang static cJSON *get_object_item(const cJSON *const object, const char *const name, const cJSON_bool case_sensitive) 144889928cc5SHong Zhang { 144989928cc5SHong Zhang cJSON *current_element = NULL; 145089928cc5SHong Zhang 145189928cc5SHong Zhang if ((object == NULL) || (name == NULL)) { return NULL; } 145289928cc5SHong Zhang 145389928cc5SHong Zhang current_element = object->child; 145489928cc5SHong Zhang if (case_sensitive) { 145589928cc5SHong Zhang while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { current_element = current_element->next; } 145689928cc5SHong Zhang } else { 1457*f4f49eeaSPierre Jolivet while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char *)name, (const unsigned char *)current_element->string) != 0)) { current_element = current_element->next; } 145889928cc5SHong Zhang } 145989928cc5SHong Zhang 146089928cc5SHong Zhang if ((current_element == NULL) || (current_element->string == NULL)) { return NULL; } 146189928cc5SHong Zhang 146289928cc5SHong Zhang return current_element; 146389928cc5SHong Zhang } 146489928cc5SHong Zhang 146589928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *const object, const char *const string) 146689928cc5SHong Zhang { 146789928cc5SHong Zhang return get_object_item(object, string, false); 146889928cc5SHong Zhang } 146989928cc5SHong Zhang 147089928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string) 147189928cc5SHong Zhang { 147289928cc5SHong Zhang return get_object_item(object, string, true); 147389928cc5SHong Zhang } 147489928cc5SHong Zhang 147589928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) 147689928cc5SHong Zhang { 147789928cc5SHong Zhang return cJSON_GetObjectItem(object, string) ? 1 : 0; 147889928cc5SHong Zhang } 147989928cc5SHong Zhang 148089928cc5SHong Zhang /* Utility for array list handling. */ 148189928cc5SHong Zhang static void suffix_object(cJSON *prev, cJSON *item) 148289928cc5SHong Zhang { 148389928cc5SHong Zhang prev->next = item; 148489928cc5SHong Zhang item->prev = prev; 148589928cc5SHong Zhang } 148689928cc5SHong Zhang 148789928cc5SHong Zhang /* Utility for handling references. */ 148889928cc5SHong Zhang static cJSON *create_reference(const cJSON *item, const internal_hooks *const hooks) 148989928cc5SHong Zhang { 149089928cc5SHong Zhang cJSON *reference = NULL; 149189928cc5SHong Zhang if (item == NULL) { return NULL; } 149289928cc5SHong Zhang 149389928cc5SHong Zhang reference = cJSON_New_Item(hooks); 149489928cc5SHong Zhang if (reference == NULL) { return NULL; } 149589928cc5SHong Zhang 149689928cc5SHong Zhang memcpy(reference, item, sizeof(cJSON)); 149789928cc5SHong Zhang reference->string = NULL; 149889928cc5SHong Zhang reference->type |= cJSON_IsReference; 149989928cc5SHong Zhang reference->next = reference->prev = NULL; 150089928cc5SHong Zhang return reference; 150189928cc5SHong Zhang } 150289928cc5SHong Zhang 150389928cc5SHong Zhang static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) 150489928cc5SHong Zhang { 150589928cc5SHong Zhang cJSON *child = NULL; 150689928cc5SHong Zhang 150789928cc5SHong Zhang if ((item == NULL) || (array == NULL) || (array == item)) { return false; } 150889928cc5SHong Zhang 150989928cc5SHong Zhang child = array->child; 151089928cc5SHong Zhang /* 151189928cc5SHong Zhang * To find the last item in array quickly, we use prev in array 151289928cc5SHong Zhang */ 151389928cc5SHong Zhang if (child == NULL) { 151489928cc5SHong Zhang /* list is empty, start new one */ 151589928cc5SHong Zhang array->child = item; 151689928cc5SHong Zhang item->prev = item; 151789928cc5SHong Zhang item->next = NULL; 151889928cc5SHong Zhang } else { 151989928cc5SHong Zhang /* append to the end */ 152089928cc5SHong Zhang if (child->prev) { 152189928cc5SHong Zhang suffix_object(child->prev, item); 152289928cc5SHong Zhang array->child->prev = item; 152389928cc5SHong Zhang } 152489928cc5SHong Zhang } 152589928cc5SHong Zhang 152689928cc5SHong Zhang return true; 152789928cc5SHong Zhang } 152889928cc5SHong Zhang 152989928cc5SHong Zhang /* Add item to array/object. */ 153089928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) 153189928cc5SHong Zhang { 153289928cc5SHong Zhang return add_item_to_array(array, item); 153389928cc5SHong Zhang } 153489928cc5SHong Zhang 153589928cc5SHong Zhang #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 153689928cc5SHong Zhang #pragma GCC diagnostic push 153789928cc5SHong Zhang #endif 153889928cc5SHong Zhang #ifdef __GNUC__ 153989928cc5SHong Zhang #pragma GCC diagnostic ignored "-Wcast-qual" 154089928cc5SHong Zhang #endif 154189928cc5SHong Zhang /* helper function to cast away const */ 154289928cc5SHong Zhang static void *cast_away_const(const void *string) 154389928cc5SHong Zhang { 154489928cc5SHong Zhang return (void *)string; 154589928cc5SHong Zhang } 154689928cc5SHong Zhang #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) 154789928cc5SHong Zhang #pragma GCC diagnostic pop 154889928cc5SHong Zhang #endif 154989928cc5SHong Zhang 155089928cc5SHong Zhang static cJSON_bool add_item_to_object(cJSON *const object, const char *const string, cJSON *const item, const internal_hooks *const hooks, const cJSON_bool constant_key) 155189928cc5SHong Zhang { 155289928cc5SHong Zhang char *new_key = NULL; 155389928cc5SHong Zhang int new_type = cJSON_Invalid; 155489928cc5SHong Zhang 155589928cc5SHong Zhang if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) { return false; } 155689928cc5SHong Zhang 155789928cc5SHong Zhang if (constant_key) { 155889928cc5SHong Zhang new_key = (char *)cast_away_const(string); 155989928cc5SHong Zhang new_type = item->type | cJSON_StringIsConst; 156089928cc5SHong Zhang } else { 156189928cc5SHong Zhang new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks); 156289928cc5SHong Zhang if (new_key == NULL) { return false; } 156389928cc5SHong Zhang 156489928cc5SHong Zhang new_type = item->type & ~cJSON_StringIsConst; 156589928cc5SHong Zhang } 156689928cc5SHong Zhang 156789928cc5SHong Zhang if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { hooks->deallocate(item->string); } 156889928cc5SHong Zhang 156989928cc5SHong Zhang item->string = new_key; 157089928cc5SHong Zhang item->type = new_type; 157189928cc5SHong Zhang 157289928cc5SHong Zhang return add_item_to_array(object, item); 157389928cc5SHong Zhang } 157489928cc5SHong Zhang 157589928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) 157689928cc5SHong Zhang { 157789928cc5SHong Zhang return add_item_to_object(object, string, item, &global_hooks, false); 157889928cc5SHong Zhang } 157989928cc5SHong Zhang 158089928cc5SHong Zhang /* Add an item to an object with constant string as key */ 158189928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) 158289928cc5SHong Zhang { 158389928cc5SHong Zhang return add_item_to_object(object, string, item, &global_hooks, true); 158489928cc5SHong Zhang } 158589928cc5SHong Zhang 158689928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) 158789928cc5SHong Zhang { 158889928cc5SHong Zhang if (array == NULL) { return false; } 158989928cc5SHong Zhang 159089928cc5SHong Zhang return add_item_to_array(array, create_reference(item, &global_hooks)); 159189928cc5SHong Zhang } 159289928cc5SHong Zhang 159389928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) 159489928cc5SHong Zhang { 159589928cc5SHong Zhang if ((object == NULL) || (string == NULL)) { return false; } 159689928cc5SHong Zhang 159789928cc5SHong Zhang return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); 159889928cc5SHong Zhang } 159989928cc5SHong Zhang 160089928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddNullToObject(cJSON *const object, const char *const name) 160189928cc5SHong Zhang { 160289928cc5SHong Zhang cJSON *null = cJSON_CreateNull(); 160389928cc5SHong Zhang if (add_item_to_object(object, name, null, &global_hooks, false)) { return null; } 160489928cc5SHong Zhang 160589928cc5SHong Zhang cJSON_Delete(null); 160689928cc5SHong Zhang return NULL; 160789928cc5SHong Zhang } 160889928cc5SHong Zhang 160989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddTrueToObject(cJSON *const object, const char *const name) 161089928cc5SHong Zhang { 161189928cc5SHong Zhang cJSON *true_item = cJSON_CreateTrue(); 161289928cc5SHong Zhang if (add_item_to_object(object, name, true_item, &global_hooks, false)) { return true_item; } 161389928cc5SHong Zhang 161489928cc5SHong Zhang cJSON_Delete(true_item); 161589928cc5SHong Zhang return NULL; 161689928cc5SHong Zhang } 161789928cc5SHong Zhang 161889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddFalseToObject(cJSON *const object, const char *const name) 161989928cc5SHong Zhang { 162089928cc5SHong Zhang cJSON *false_item = cJSON_CreateFalse(); 162189928cc5SHong Zhang if (add_item_to_object(object, name, false_item, &global_hooks, false)) { return false_item; } 162289928cc5SHong Zhang 162389928cc5SHong Zhang cJSON_Delete(false_item); 162489928cc5SHong Zhang return NULL; 162589928cc5SHong Zhang } 162689928cc5SHong Zhang 162789928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean) 162889928cc5SHong Zhang { 162989928cc5SHong Zhang cJSON *bool_item = cJSON_CreateBool(boolean); 163089928cc5SHong Zhang if (add_item_to_object(object, name, bool_item, &global_hooks, false)) { return bool_item; } 163189928cc5SHong Zhang 163289928cc5SHong Zhang cJSON_Delete(bool_item); 163389928cc5SHong Zhang return NULL; 163489928cc5SHong Zhang } 163589928cc5SHong Zhang 163689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number) 163789928cc5SHong Zhang { 163889928cc5SHong Zhang cJSON *number_item = cJSON_CreateNumber(number); 163989928cc5SHong Zhang if (add_item_to_object(object, name, number_item, &global_hooks, false)) { return number_item; } 164089928cc5SHong Zhang 164189928cc5SHong Zhang cJSON_Delete(number_item); 164289928cc5SHong Zhang return NULL; 164389928cc5SHong Zhang } 164489928cc5SHong Zhang 164589928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string) 164689928cc5SHong Zhang { 164789928cc5SHong Zhang cJSON *string_item = cJSON_CreateString(string); 164889928cc5SHong Zhang if (add_item_to_object(object, name, string_item, &global_hooks, false)) { return string_item; } 164989928cc5SHong Zhang 165089928cc5SHong Zhang cJSON_Delete(string_item); 165189928cc5SHong Zhang return NULL; 165289928cc5SHong Zhang } 165389928cc5SHong Zhang 165489928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw) 165589928cc5SHong Zhang { 165689928cc5SHong Zhang cJSON *raw_item = cJSON_CreateRaw(raw); 165789928cc5SHong Zhang if (add_item_to_object(object, name, raw_item, &global_hooks, false)) { return raw_item; } 165889928cc5SHong Zhang 165989928cc5SHong Zhang cJSON_Delete(raw_item); 166089928cc5SHong Zhang return NULL; 166189928cc5SHong Zhang } 166289928cc5SHong Zhang 166389928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddObjectToObject(cJSON *const object, const char *const name) 166489928cc5SHong Zhang { 166589928cc5SHong Zhang cJSON *object_item = cJSON_CreateObject(); 166689928cc5SHong Zhang if (add_item_to_object(object, name, object_item, &global_hooks, false)) { return object_item; } 166789928cc5SHong Zhang 166889928cc5SHong Zhang cJSON_Delete(object_item); 166989928cc5SHong Zhang return NULL; 167089928cc5SHong Zhang } 167189928cc5SHong Zhang 167289928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddArrayToObject(cJSON *const object, const char *const name) 167389928cc5SHong Zhang { 167489928cc5SHong Zhang cJSON *array = cJSON_CreateArray(); 167589928cc5SHong Zhang if (add_item_to_object(object, name, array, &global_hooks, false)) { return array; } 167689928cc5SHong Zhang 167789928cc5SHong Zhang cJSON_Delete(array); 167889928cc5SHong Zhang return NULL; 167989928cc5SHong Zhang } 168089928cc5SHong Zhang 168189928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item) 168289928cc5SHong Zhang { 168389928cc5SHong Zhang if ((parent == NULL) || (item == NULL)) { return NULL; } 168489928cc5SHong Zhang 168589928cc5SHong Zhang if (item != parent->child) { 168689928cc5SHong Zhang /* not the first element */ 168789928cc5SHong Zhang item->prev->next = item->next; 168889928cc5SHong Zhang } 168989928cc5SHong Zhang if (item->next != NULL) { 169089928cc5SHong Zhang /* not the last element */ 169189928cc5SHong Zhang item->next->prev = item->prev; 169289928cc5SHong Zhang } 169389928cc5SHong Zhang 169489928cc5SHong Zhang if (item == parent->child) { 169589928cc5SHong Zhang /* first element */ 169689928cc5SHong Zhang parent->child = item->next; 169789928cc5SHong Zhang } else if (item->next == NULL) { 169889928cc5SHong Zhang /* last element */ 169989928cc5SHong Zhang parent->child->prev = item->prev; 170089928cc5SHong Zhang } 170189928cc5SHong Zhang 170289928cc5SHong Zhang /* make sure the detached item doesn't point anywhere anymore */ 170389928cc5SHong Zhang item->prev = NULL; 170489928cc5SHong Zhang item->next = NULL; 170589928cc5SHong Zhang 170689928cc5SHong Zhang return item; 170789928cc5SHong Zhang } 170889928cc5SHong Zhang 170989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) 171089928cc5SHong Zhang { 171189928cc5SHong Zhang if (which < 0) { return NULL; } 171289928cc5SHong Zhang 171389928cc5SHong Zhang return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); 171489928cc5SHong Zhang } 171589928cc5SHong Zhang 171689928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) 171789928cc5SHong Zhang { 171889928cc5SHong Zhang cJSON_Delete(cJSON_DetachItemFromArray(array, which)); 171989928cc5SHong Zhang } 172089928cc5SHong Zhang 172189928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) 172289928cc5SHong Zhang { 172389928cc5SHong Zhang cJSON *to_detach = cJSON_GetObjectItem(object, string); 172489928cc5SHong Zhang 172589928cc5SHong Zhang return cJSON_DetachItemViaPointer(object, to_detach); 172689928cc5SHong Zhang } 172789928cc5SHong Zhang 172889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) 172989928cc5SHong Zhang { 173089928cc5SHong Zhang cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); 173189928cc5SHong Zhang 173289928cc5SHong Zhang return cJSON_DetachItemViaPointer(object, to_detach); 173389928cc5SHong Zhang } 173489928cc5SHong Zhang 173589928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) 173689928cc5SHong Zhang { 173789928cc5SHong Zhang cJSON_Delete(cJSON_DetachItemFromObject(object, string)); 173889928cc5SHong Zhang } 173989928cc5SHong Zhang 174089928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) 174189928cc5SHong Zhang { 174289928cc5SHong Zhang cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); 174389928cc5SHong Zhang } 174489928cc5SHong Zhang 174589928cc5SHong Zhang /* Replace array/object items with new ones. */ 174689928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) 174789928cc5SHong Zhang { 174889928cc5SHong Zhang cJSON *after_inserted = NULL; 174989928cc5SHong Zhang 175089928cc5SHong Zhang if (which < 0) { return false; } 175189928cc5SHong Zhang 175289928cc5SHong Zhang after_inserted = get_array_item(array, (size_t)which); 175389928cc5SHong Zhang if (after_inserted == NULL) { return add_item_to_array(array, newitem); } 175489928cc5SHong Zhang 175589928cc5SHong Zhang newitem->next = after_inserted; 175689928cc5SHong Zhang newitem->prev = after_inserted->prev; 175789928cc5SHong Zhang after_inserted->prev = newitem; 175889928cc5SHong Zhang if (after_inserted == array->child) { 175989928cc5SHong Zhang array->child = newitem; 176089928cc5SHong Zhang } else { 176189928cc5SHong Zhang newitem->prev->next = newitem; 176289928cc5SHong Zhang } 176389928cc5SHong Zhang return true; 176489928cc5SHong Zhang } 176589928cc5SHong Zhang 176689928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement) 176789928cc5SHong Zhang { 176889928cc5SHong Zhang if ((parent == NULL) || (replacement == NULL) || (item == NULL)) { return false; } 176989928cc5SHong Zhang 177089928cc5SHong Zhang if (replacement == item) { return true; } 177189928cc5SHong Zhang 177289928cc5SHong Zhang replacement->next = item->next; 177389928cc5SHong Zhang replacement->prev = item->prev; 177489928cc5SHong Zhang 177589928cc5SHong Zhang if (replacement->next != NULL) { replacement->next->prev = replacement; } 177689928cc5SHong Zhang if (parent->child == item) { 177789928cc5SHong Zhang if (parent->child->prev == parent->child) { replacement->prev = replacement; } 177889928cc5SHong Zhang parent->child = replacement; 177989928cc5SHong Zhang } else { /* 178089928cc5SHong Zhang * To find the last item in array quickly, we use prev in array. 178189928cc5SHong Zhang * We can't modify the last item's next pointer where this item was the parent's child 178289928cc5SHong Zhang */ 178389928cc5SHong Zhang if (replacement->prev != NULL) { replacement->prev->next = replacement; } 178489928cc5SHong Zhang if (replacement->next == NULL) { parent->child->prev = replacement; } 178589928cc5SHong Zhang } 178689928cc5SHong Zhang 178789928cc5SHong Zhang item->next = NULL; 178889928cc5SHong Zhang item->prev = NULL; 178989928cc5SHong Zhang cJSON_Delete(item); 179089928cc5SHong Zhang 179189928cc5SHong Zhang return true; 179289928cc5SHong Zhang } 179389928cc5SHong Zhang 179489928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) 179589928cc5SHong Zhang { 179689928cc5SHong Zhang if (which < 0) { return false; } 179789928cc5SHong Zhang 179889928cc5SHong Zhang return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); 179989928cc5SHong Zhang } 180089928cc5SHong Zhang 180189928cc5SHong Zhang static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) 180289928cc5SHong Zhang { 180389928cc5SHong Zhang if ((replacement == NULL) || (string == NULL)) { return false; } 180489928cc5SHong Zhang 180589928cc5SHong Zhang /* replace the name in the replacement */ 180689928cc5SHong Zhang if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) { cJSON_free(replacement->string); } 180789928cc5SHong Zhang replacement->string = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); 180889928cc5SHong Zhang if (replacement->string == NULL) { return false; } 180989928cc5SHong Zhang 181089928cc5SHong Zhang replacement->type &= ~cJSON_StringIsConst; 181189928cc5SHong Zhang 181289928cc5SHong Zhang return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); 181389928cc5SHong Zhang } 181489928cc5SHong Zhang 181589928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) 181689928cc5SHong Zhang { 181789928cc5SHong Zhang return replace_item_in_object(object, string, newitem, false); 181889928cc5SHong Zhang } 181989928cc5SHong Zhang 182089928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) 182189928cc5SHong Zhang { 182289928cc5SHong Zhang return replace_item_in_object(object, string, newitem, true); 182389928cc5SHong Zhang } 182489928cc5SHong Zhang 182589928cc5SHong Zhang /* Create basic types: */ 182689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) 182789928cc5SHong Zhang { 182889928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 182989928cc5SHong Zhang if (item) { item->type = cJSON_NULL; } 183089928cc5SHong Zhang 183189928cc5SHong Zhang return item; 183289928cc5SHong Zhang } 183389928cc5SHong Zhang 183489928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) 183589928cc5SHong Zhang { 183689928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 183789928cc5SHong Zhang if (item) { item->type = cJSON_True; } 183889928cc5SHong Zhang 183989928cc5SHong Zhang return item; 184089928cc5SHong Zhang } 184189928cc5SHong Zhang 184289928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) 184389928cc5SHong Zhang { 184489928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 184589928cc5SHong Zhang if (item) { item->type = cJSON_False; } 184689928cc5SHong Zhang 184789928cc5SHong Zhang return item; 184889928cc5SHong Zhang } 184989928cc5SHong Zhang 185089928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) 185189928cc5SHong Zhang { 185289928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 185389928cc5SHong Zhang if (item) { item->type = boolean ? cJSON_True : cJSON_False; } 185489928cc5SHong Zhang 185589928cc5SHong Zhang return item; 185689928cc5SHong Zhang } 185789928cc5SHong Zhang 185889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) 185989928cc5SHong Zhang { 186089928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 186189928cc5SHong Zhang if (item) { 186289928cc5SHong Zhang item->type = cJSON_Number; 186389928cc5SHong Zhang item->valuedouble = num; 186489928cc5SHong Zhang 186589928cc5SHong Zhang /* use saturation in case of overflow */ 186689928cc5SHong Zhang if (num >= INT_MAX) { 186789928cc5SHong Zhang item->valueint = INT_MAX; 186889928cc5SHong Zhang } else if (num <= (double)INT_MIN) { 186989928cc5SHong Zhang item->valueint = INT_MIN; 187089928cc5SHong Zhang } else { 187189928cc5SHong Zhang item->valueint = (int)num; 187289928cc5SHong Zhang } 187389928cc5SHong Zhang } 187489928cc5SHong Zhang 187589928cc5SHong Zhang return item; 187689928cc5SHong Zhang } 187789928cc5SHong Zhang 187889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) 187989928cc5SHong Zhang { 188089928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 188189928cc5SHong Zhang if (item) { 188289928cc5SHong Zhang item->type = cJSON_String; 188389928cc5SHong Zhang item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks); 188489928cc5SHong Zhang if (!item->valuestring) { 188589928cc5SHong Zhang cJSON_Delete(item); 188689928cc5SHong Zhang return NULL; 188789928cc5SHong Zhang } 188889928cc5SHong Zhang } 188989928cc5SHong Zhang 189089928cc5SHong Zhang return item; 189189928cc5SHong Zhang } 189289928cc5SHong Zhang 189389928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) 189489928cc5SHong Zhang { 189589928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 189689928cc5SHong Zhang if (item != NULL) { 189789928cc5SHong Zhang item->type = cJSON_String | cJSON_IsReference; 189889928cc5SHong Zhang item->valuestring = (char *)cast_away_const(string); 189989928cc5SHong Zhang } 190089928cc5SHong Zhang 190189928cc5SHong Zhang return item; 190289928cc5SHong Zhang } 190389928cc5SHong Zhang 190489928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) 190589928cc5SHong Zhang { 190689928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 190789928cc5SHong Zhang if (item != NULL) { 190889928cc5SHong Zhang item->type = cJSON_Object | cJSON_IsReference; 190989928cc5SHong Zhang item->child = (cJSON *)cast_away_const(child); 191089928cc5SHong Zhang } 191189928cc5SHong Zhang 191289928cc5SHong Zhang return item; 191389928cc5SHong Zhang } 191489928cc5SHong Zhang 191589928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) 191689928cc5SHong Zhang { 191789928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 191889928cc5SHong Zhang if (item != NULL) { 191989928cc5SHong Zhang item->type = cJSON_Array | cJSON_IsReference; 192089928cc5SHong Zhang item->child = (cJSON *)cast_away_const(child); 192189928cc5SHong Zhang } 192289928cc5SHong Zhang 192389928cc5SHong Zhang return item; 192489928cc5SHong Zhang } 192589928cc5SHong Zhang 192689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) 192789928cc5SHong Zhang { 192889928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 192989928cc5SHong Zhang if (item) { 193089928cc5SHong Zhang item->type = cJSON_Raw; 193189928cc5SHong Zhang item->valuestring = (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks); 193289928cc5SHong Zhang if (!item->valuestring) { 193389928cc5SHong Zhang cJSON_Delete(item); 193489928cc5SHong Zhang return NULL; 193589928cc5SHong Zhang } 193689928cc5SHong Zhang } 193789928cc5SHong Zhang 193889928cc5SHong Zhang return item; 193989928cc5SHong Zhang } 194089928cc5SHong Zhang 194189928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) 194289928cc5SHong Zhang { 194389928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 194489928cc5SHong Zhang if (item) { item->type = cJSON_Array; } 194589928cc5SHong Zhang 194689928cc5SHong Zhang return item; 194789928cc5SHong Zhang } 194889928cc5SHong Zhang 194989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) 195089928cc5SHong Zhang { 195189928cc5SHong Zhang cJSON *item = cJSON_New_Item(&global_hooks); 195289928cc5SHong Zhang if (item) { item->type = cJSON_Object; } 195389928cc5SHong Zhang 195489928cc5SHong Zhang return item; 195589928cc5SHong Zhang } 195689928cc5SHong Zhang 195789928cc5SHong Zhang /* Create Arrays: */ 195889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) 195989928cc5SHong Zhang { 196089928cc5SHong Zhang size_t i = 0; 196189928cc5SHong Zhang cJSON *n = NULL; 196289928cc5SHong Zhang cJSON *p = NULL; 196389928cc5SHong Zhang cJSON *a = NULL; 196489928cc5SHong Zhang 196589928cc5SHong Zhang if ((count < 0) || (numbers == NULL)) { return NULL; } 196689928cc5SHong Zhang 196789928cc5SHong Zhang a = cJSON_CreateArray(); 196889928cc5SHong Zhang 196989928cc5SHong Zhang for (i = 0; a && (i < (size_t)count); i++) { 197089928cc5SHong Zhang n = cJSON_CreateNumber(numbers[i]); 197189928cc5SHong Zhang if (!n) { 197289928cc5SHong Zhang cJSON_Delete(a); 197389928cc5SHong Zhang return NULL; 197489928cc5SHong Zhang } 197589928cc5SHong Zhang if (!i) { 197689928cc5SHong Zhang a->child = n; 197789928cc5SHong Zhang } else { 197889928cc5SHong Zhang suffix_object(p, n); 197989928cc5SHong Zhang } 198089928cc5SHong Zhang p = n; 198189928cc5SHong Zhang } 198289928cc5SHong Zhang 198389928cc5SHong Zhang if (a && a->child) { a->child->prev = n; } 198489928cc5SHong Zhang 198589928cc5SHong Zhang return a; 198689928cc5SHong Zhang } 198789928cc5SHong Zhang 198889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) 198989928cc5SHong Zhang { 199089928cc5SHong Zhang size_t i = 0; 199189928cc5SHong Zhang cJSON *n = NULL; 199289928cc5SHong Zhang cJSON *p = NULL; 199389928cc5SHong Zhang cJSON *a = NULL; 199489928cc5SHong Zhang 199589928cc5SHong Zhang if ((count < 0) || (numbers == NULL)) { return NULL; } 199689928cc5SHong Zhang 199789928cc5SHong Zhang a = cJSON_CreateArray(); 199889928cc5SHong Zhang 199989928cc5SHong Zhang for (i = 0; a && (i < (size_t)count); i++) { 200089928cc5SHong Zhang n = cJSON_CreateNumber((double)numbers[i]); 200189928cc5SHong Zhang if (!n) { 200289928cc5SHong Zhang cJSON_Delete(a); 200389928cc5SHong Zhang return NULL; 200489928cc5SHong Zhang } 200589928cc5SHong Zhang if (!i) { 200689928cc5SHong Zhang a->child = n; 200789928cc5SHong Zhang } else { 200889928cc5SHong Zhang suffix_object(p, n); 200989928cc5SHong Zhang } 201089928cc5SHong Zhang p = n; 201189928cc5SHong Zhang } 201289928cc5SHong Zhang 201389928cc5SHong Zhang if (a && a->child) { a->child->prev = n; } 201489928cc5SHong Zhang 201589928cc5SHong Zhang return a; 201689928cc5SHong Zhang } 201789928cc5SHong Zhang 201889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) 201989928cc5SHong Zhang { 202089928cc5SHong Zhang size_t i = 0; 202189928cc5SHong Zhang cJSON *n = NULL; 202289928cc5SHong Zhang cJSON *p = NULL; 202389928cc5SHong Zhang cJSON *a = NULL; 202489928cc5SHong Zhang 202589928cc5SHong Zhang if ((count < 0) || (numbers == NULL)) { return NULL; } 202689928cc5SHong Zhang 202789928cc5SHong Zhang a = cJSON_CreateArray(); 202889928cc5SHong Zhang 202989928cc5SHong Zhang for (i = 0; a && (i < (size_t)count); i++) { 203089928cc5SHong Zhang n = cJSON_CreateNumber(numbers[i]); 203189928cc5SHong Zhang if (!n) { 203289928cc5SHong Zhang cJSON_Delete(a); 203389928cc5SHong Zhang return NULL; 203489928cc5SHong Zhang } 203589928cc5SHong Zhang if (!i) { 203689928cc5SHong Zhang a->child = n; 203789928cc5SHong Zhang } else { 203889928cc5SHong Zhang suffix_object(p, n); 203989928cc5SHong Zhang } 204089928cc5SHong Zhang p = n; 204189928cc5SHong Zhang } 204289928cc5SHong Zhang 204389928cc5SHong Zhang if (a && a->child) { a->child->prev = n; } 204489928cc5SHong Zhang 204589928cc5SHong Zhang return a; 204689928cc5SHong Zhang } 204789928cc5SHong Zhang 204889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) 204989928cc5SHong Zhang { 205089928cc5SHong Zhang size_t i = 0; 205189928cc5SHong Zhang cJSON *n = NULL; 205289928cc5SHong Zhang cJSON *p = NULL; 205389928cc5SHong Zhang cJSON *a = NULL; 205489928cc5SHong Zhang 205589928cc5SHong Zhang if ((count < 0) || (strings == NULL)) { return NULL; } 205689928cc5SHong Zhang 205789928cc5SHong Zhang a = cJSON_CreateArray(); 205889928cc5SHong Zhang 205989928cc5SHong Zhang for (i = 0; a && (i < (size_t)count); i++) { 206089928cc5SHong Zhang n = cJSON_CreateString(strings[i]); 206189928cc5SHong Zhang if (!n) { 206289928cc5SHong Zhang cJSON_Delete(a); 206389928cc5SHong Zhang return NULL; 206489928cc5SHong Zhang } 206589928cc5SHong Zhang if (!i) { 206689928cc5SHong Zhang a->child = n; 206789928cc5SHong Zhang } else { 206889928cc5SHong Zhang suffix_object(p, n); 206989928cc5SHong Zhang } 207089928cc5SHong Zhang p = n; 207189928cc5SHong Zhang } 207289928cc5SHong Zhang 207389928cc5SHong Zhang if (a && a->child) { a->child->prev = n; } 207489928cc5SHong Zhang 207589928cc5SHong Zhang return a; 207689928cc5SHong Zhang } 207789928cc5SHong Zhang 207889928cc5SHong Zhang /* Duplication */ 207989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) 208089928cc5SHong Zhang { 208189928cc5SHong Zhang cJSON *newitem = NULL; 208289928cc5SHong Zhang cJSON *child = NULL; 208389928cc5SHong Zhang cJSON *next = NULL; 208489928cc5SHong Zhang cJSON *newchild = NULL; 208589928cc5SHong Zhang 208689928cc5SHong Zhang /* Bail on bad ptr */ 208789928cc5SHong Zhang if (!item) { goto fail; } 208889928cc5SHong Zhang /* Create new item */ 208989928cc5SHong Zhang newitem = cJSON_New_Item(&global_hooks); 209089928cc5SHong Zhang if (!newitem) { goto fail; } 209189928cc5SHong Zhang /* Copy over all vars */ 209289928cc5SHong Zhang newitem->type = item->type & (~cJSON_IsReference); 209389928cc5SHong Zhang newitem->valueint = item->valueint; 209489928cc5SHong Zhang newitem->valuedouble = item->valuedouble; 209589928cc5SHong Zhang if (item->valuestring) { 209689928cc5SHong Zhang newitem->valuestring = (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks); 209789928cc5SHong Zhang if (!newitem->valuestring) { goto fail; } 209889928cc5SHong Zhang } 209989928cc5SHong Zhang if (item->string) { 210089928cc5SHong Zhang newitem->string = (item->type & cJSON_StringIsConst) ? item->string : (char *)cJSON_strdup((unsigned char *)item->string, &global_hooks); 210189928cc5SHong Zhang if (!newitem->string) { goto fail; } 210289928cc5SHong Zhang } 210389928cc5SHong Zhang /* If non-recursive, then we're done! */ 210489928cc5SHong Zhang if (!recurse) { return newitem; } 210589928cc5SHong Zhang /* Walk the ->next chain for the child. */ 210689928cc5SHong Zhang child = item->child; 210789928cc5SHong Zhang while (child != NULL) { 210889928cc5SHong Zhang newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ 210989928cc5SHong Zhang if (!newchild) { goto fail; } 211089928cc5SHong Zhang if (next != NULL) { 211189928cc5SHong Zhang /* If newitem->child already set, then crosswire ->prev and ->next and move on */ 211289928cc5SHong Zhang next->next = newchild; 211389928cc5SHong Zhang newchild->prev = next; 211489928cc5SHong Zhang next = newchild; 211589928cc5SHong Zhang } else { 211689928cc5SHong Zhang /* Set newitem->child and move to it */ 211789928cc5SHong Zhang newitem->child = newchild; 211889928cc5SHong Zhang next = newchild; 211989928cc5SHong Zhang } 212089928cc5SHong Zhang child = child->next; 212189928cc5SHong Zhang } 212289928cc5SHong Zhang if (newitem && newitem->child) { newitem->child->prev = newchild; } 212389928cc5SHong Zhang 212489928cc5SHong Zhang return newitem; 212589928cc5SHong Zhang 212689928cc5SHong Zhang fail: 212789928cc5SHong Zhang if (newitem != NULL) { cJSON_Delete(newitem); } 212889928cc5SHong Zhang 212989928cc5SHong Zhang return NULL; 213089928cc5SHong Zhang } 213189928cc5SHong Zhang 213289928cc5SHong Zhang static void skip_oneline_comment(char **input) 213389928cc5SHong Zhang { 213489928cc5SHong Zhang *input += static_strlen("//"); 213589928cc5SHong Zhang 213689928cc5SHong Zhang for (; (*input)[0] != '\0'; ++(*input)) { 213789928cc5SHong Zhang if ((*input)[0] == '\n') { 213889928cc5SHong Zhang *input += static_strlen("\n"); 213989928cc5SHong Zhang return; 214089928cc5SHong Zhang } 214189928cc5SHong Zhang } 214289928cc5SHong Zhang } 214389928cc5SHong Zhang 214489928cc5SHong Zhang static void skip_multiline_comment(char **input) 214589928cc5SHong Zhang { 214689928cc5SHong Zhang *input += static_strlen("/*"); 214789928cc5SHong Zhang 214889928cc5SHong Zhang for (; (*input)[0] != '\0'; ++(*input)) { 214989928cc5SHong Zhang if (((*input)[0] == '*') && ((*input)[1] == '/')) { 215089928cc5SHong Zhang *input += static_strlen("*/"); 215189928cc5SHong Zhang return; 215289928cc5SHong Zhang } 215389928cc5SHong Zhang } 215489928cc5SHong Zhang } 215589928cc5SHong Zhang 215689928cc5SHong Zhang static void minify_string(char **input, char **output) 215789928cc5SHong Zhang { 215889928cc5SHong Zhang (*output)[0] = (*input)[0]; 215989928cc5SHong Zhang *input += static_strlen("\""); 216089928cc5SHong Zhang *output += static_strlen("\""); 216189928cc5SHong Zhang 216289928cc5SHong Zhang for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { 216389928cc5SHong Zhang (*output)[0] = (*input)[0]; 216489928cc5SHong Zhang 216589928cc5SHong Zhang if ((*input)[0] == '\"') { 216689928cc5SHong Zhang (*output)[0] = '\"'; 216789928cc5SHong Zhang *input += static_strlen("\""); 216889928cc5SHong Zhang *output += static_strlen("\""); 216989928cc5SHong Zhang return; 217089928cc5SHong Zhang } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { 217189928cc5SHong Zhang (*output)[1] = (*input)[1]; 217289928cc5SHong Zhang *input += static_strlen("\""); 217389928cc5SHong Zhang *output += static_strlen("\""); 217489928cc5SHong Zhang } 217589928cc5SHong Zhang } 217689928cc5SHong Zhang } 217789928cc5SHong Zhang 217889928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_Minify(char *json) 217989928cc5SHong Zhang { 218089928cc5SHong Zhang char *into = json; 218189928cc5SHong Zhang 218289928cc5SHong Zhang if (json == NULL) { return; } 218389928cc5SHong Zhang 218489928cc5SHong Zhang while (json[0] != '\0') { 218589928cc5SHong Zhang switch (json[0]) { 218689928cc5SHong Zhang case ' ': 218789928cc5SHong Zhang case '\t': 218889928cc5SHong Zhang case '\r': 218989928cc5SHong Zhang case '\n': 219089928cc5SHong Zhang json++; 219189928cc5SHong Zhang break; 219289928cc5SHong Zhang 219389928cc5SHong Zhang case '/': 219489928cc5SHong Zhang if (json[1] == '/') { 219589928cc5SHong Zhang skip_oneline_comment(&json); 219689928cc5SHong Zhang } else if (json[1] == '*') { 219789928cc5SHong Zhang skip_multiline_comment(&json); 219889928cc5SHong Zhang } else { 219989928cc5SHong Zhang json++; 220089928cc5SHong Zhang } 220189928cc5SHong Zhang break; 220289928cc5SHong Zhang 220389928cc5SHong Zhang case '\"': 220489928cc5SHong Zhang minify_string(&json, (char **)&into); 220589928cc5SHong Zhang break; 220689928cc5SHong Zhang 220789928cc5SHong Zhang default: 220889928cc5SHong Zhang into[0] = json[0]; 220989928cc5SHong Zhang json++; 221089928cc5SHong Zhang into++; 221189928cc5SHong Zhang } 221289928cc5SHong Zhang } 221389928cc5SHong Zhang 221489928cc5SHong Zhang /* and null-terminate. */ 221589928cc5SHong Zhang *into = '\0'; 221689928cc5SHong Zhang } 221789928cc5SHong Zhang 221889928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item) 221989928cc5SHong Zhang { 222089928cc5SHong Zhang if (item == NULL) { return false; } 222189928cc5SHong Zhang 222289928cc5SHong Zhang return (item->type & 0xFF) == cJSON_Invalid; 222389928cc5SHong Zhang } 222489928cc5SHong Zhang 222589928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item) 222689928cc5SHong Zhang { 222789928cc5SHong Zhang if (item == NULL) { return false; } 222889928cc5SHong Zhang 222989928cc5SHong Zhang return (item->type & 0xFF) == cJSON_False; 223089928cc5SHong Zhang } 223189928cc5SHong Zhang 223289928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item) 223389928cc5SHong Zhang { 223489928cc5SHong Zhang if (item == NULL) { return false; } 223589928cc5SHong Zhang 223689928cc5SHong Zhang return (item->type & 0xff) == cJSON_True; 223789928cc5SHong Zhang } 223889928cc5SHong Zhang 223989928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item) 224089928cc5SHong Zhang { 224189928cc5SHong Zhang if (item == NULL) { return false; } 224289928cc5SHong Zhang 224389928cc5SHong Zhang return (item->type & (cJSON_True | cJSON_False)) != 0; 224489928cc5SHong Zhang } 224589928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item) 224689928cc5SHong Zhang { 224789928cc5SHong Zhang if (item == NULL) { return false; } 224889928cc5SHong Zhang 224989928cc5SHong Zhang return (item->type & 0xFF) == cJSON_NULL; 225089928cc5SHong Zhang } 225189928cc5SHong Zhang 225289928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item) 225389928cc5SHong Zhang { 225489928cc5SHong Zhang if (item == NULL) { return false; } 225589928cc5SHong Zhang 225689928cc5SHong Zhang return (item->type & 0xFF) == cJSON_Number; 225789928cc5SHong Zhang } 225889928cc5SHong Zhang 225989928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item) 226089928cc5SHong Zhang { 226189928cc5SHong Zhang if (item == NULL) { return false; } 226289928cc5SHong Zhang 226389928cc5SHong Zhang return (item->type & 0xFF) == cJSON_String; 226489928cc5SHong Zhang } 226589928cc5SHong Zhang 226689928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item) 226789928cc5SHong Zhang { 226889928cc5SHong Zhang if (item == NULL) { return false; } 226989928cc5SHong Zhang 227089928cc5SHong Zhang return (item->type & 0xFF) == cJSON_Array; 227189928cc5SHong Zhang } 227289928cc5SHong Zhang 227389928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item) 227489928cc5SHong Zhang { 227589928cc5SHong Zhang if (item == NULL) { return false; } 227689928cc5SHong Zhang 227789928cc5SHong Zhang return (item->type & 0xFF) == cJSON_Object; 227889928cc5SHong Zhang } 227989928cc5SHong Zhang 228089928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item) 228189928cc5SHong Zhang { 228289928cc5SHong Zhang if (item == NULL) { return false; } 228389928cc5SHong Zhang 228489928cc5SHong Zhang return (item->type & 0xFF) == cJSON_Raw; 228589928cc5SHong Zhang } 228689928cc5SHong Zhang 228789928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive) 228889928cc5SHong Zhang { 228989928cc5SHong Zhang if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { return false; } 229089928cc5SHong Zhang 229189928cc5SHong Zhang /* check if type is valid */ 229289928cc5SHong Zhang switch (a->type & 0xFF) { 229389928cc5SHong Zhang case cJSON_False: 229489928cc5SHong Zhang case cJSON_True: 229589928cc5SHong Zhang case cJSON_NULL: 229689928cc5SHong Zhang case cJSON_Number: 229789928cc5SHong Zhang case cJSON_String: 229889928cc5SHong Zhang case cJSON_Raw: 229989928cc5SHong Zhang case cJSON_Array: 230089928cc5SHong Zhang case cJSON_Object: 230189928cc5SHong Zhang break; 230289928cc5SHong Zhang 230389928cc5SHong Zhang default: 230489928cc5SHong Zhang return false; 230589928cc5SHong Zhang } 230689928cc5SHong Zhang 230789928cc5SHong Zhang /* identical objects are equal */ 230889928cc5SHong Zhang if (a == b) { return true; } 230989928cc5SHong Zhang 231089928cc5SHong Zhang switch (a->type & 0xFF) { 231189928cc5SHong Zhang /* in these cases and equal type is enough */ 231289928cc5SHong Zhang case cJSON_False: 231389928cc5SHong Zhang case cJSON_True: 231489928cc5SHong Zhang case cJSON_NULL: 231589928cc5SHong Zhang return true; 231689928cc5SHong Zhang 231789928cc5SHong Zhang case cJSON_Number: 231889928cc5SHong Zhang if (compare_double(a->valuedouble, b->valuedouble)) { return true; } 231989928cc5SHong Zhang return false; 232089928cc5SHong Zhang 232189928cc5SHong Zhang case cJSON_String: 232289928cc5SHong Zhang case cJSON_Raw: 232389928cc5SHong Zhang if ((a->valuestring == NULL) || (b->valuestring == NULL)) { return false; } 232489928cc5SHong Zhang if (strcmp(a->valuestring, b->valuestring) == 0) { return true; } 232589928cc5SHong Zhang 232689928cc5SHong Zhang return false; 232789928cc5SHong Zhang 232889928cc5SHong Zhang case cJSON_Array: { 232989928cc5SHong Zhang cJSON *a_element = a->child; 233089928cc5SHong Zhang cJSON *b_element = b->child; 233189928cc5SHong Zhang 233289928cc5SHong Zhang for (; (a_element != NULL) && (b_element != NULL);) { 233389928cc5SHong Zhang if (!cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } 233489928cc5SHong Zhang 233589928cc5SHong Zhang a_element = a_element->next; 233689928cc5SHong Zhang b_element = b_element->next; 233789928cc5SHong Zhang } 233889928cc5SHong Zhang 233989928cc5SHong Zhang /* one of the arrays is longer than the other */ 234089928cc5SHong Zhang if (a_element != b_element) { return false; } 234189928cc5SHong Zhang 234289928cc5SHong Zhang return true; 234389928cc5SHong Zhang } 234489928cc5SHong Zhang 234589928cc5SHong Zhang case cJSON_Object: { 234689928cc5SHong Zhang cJSON *a_element = NULL; 234789928cc5SHong Zhang cJSON *b_element = NULL; 234889928cc5SHong Zhang cJSON_ArrayForEach(a_element, a) 234989928cc5SHong Zhang { 235089928cc5SHong Zhang /* TODO This has O(n^2) runtime, which is horrible! */ 235189928cc5SHong Zhang b_element = get_object_item(b, a_element->string, case_sensitive); 235289928cc5SHong Zhang if (b_element == NULL) { return false; } 235389928cc5SHong Zhang 235489928cc5SHong Zhang if (!cJSON_Compare(a_element, b_element, case_sensitive)) { return false; } 235589928cc5SHong Zhang } 235689928cc5SHong Zhang 235789928cc5SHong Zhang /* doing this twice, once on a and b to prevent true comparison if a subset of b 235889928cc5SHong Zhang * TODO: Do this the proper way, this is just a fix for now */ 235989928cc5SHong Zhang cJSON_ArrayForEach(b_element, b) 236089928cc5SHong Zhang { 236189928cc5SHong Zhang a_element = get_object_item(a, b_element->string, case_sensitive); 236289928cc5SHong Zhang if (a_element == NULL) { return false; } 236389928cc5SHong Zhang 236489928cc5SHong Zhang if (!cJSON_Compare(b_element, a_element, case_sensitive)) { return false; } 236589928cc5SHong Zhang } 236689928cc5SHong Zhang 236789928cc5SHong Zhang return true; 236889928cc5SHong Zhang } 236989928cc5SHong Zhang 237089928cc5SHong Zhang default: 237189928cc5SHong Zhang return false; 237289928cc5SHong Zhang } 237389928cc5SHong Zhang } 237489928cc5SHong Zhang 237589928cc5SHong Zhang CJSON_PUBLIC(void *) cJSON_malloc(size_t size) 237689928cc5SHong Zhang { 237789928cc5SHong Zhang return global_hooks.allocate(size); 237889928cc5SHong Zhang } 237989928cc5SHong Zhang 238089928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_free(void *object) 238189928cc5SHong Zhang { 238289928cc5SHong Zhang global_hooks.deallocate(object); 238389928cc5SHong Zhang } 2384