xref: /petsc/src/mat/tests/cJSON.c (revision 9d47de495d3c23378050c1b4a410c12a375cb6c6)
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 */
73*beceaeb6SBarry Smith #if !defined(isinf)
74f4f49eeaSPierre Jolivet   #define isinf(d) (isnan(d - d) && !isnan(d))
7589928cc5SHong Zhang #endif
76*beceaeb6SBarry Smith #if !defined(isnan)
7789928cc5SHong Zhang   #define isnan(d) (d != d)
7889928cc5SHong Zhang #endif
7989928cc5SHong Zhang 
80*beceaeb6SBarry Smith #if !defined(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 
cJSON_GetErrorPtr(void)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 
cJSON_GetStringValue(const cJSON * const item)9989928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON *const item)
10089928cc5SHong Zhang {
101ac530a7eSPierre Jolivet   if (!cJSON_IsString(item)) return NULL;
10289928cc5SHong Zhang 
10389928cc5SHong Zhang   return item->valuestring;
10489928cc5SHong Zhang }
10589928cc5SHong Zhang 
cJSON_GetNumberValue(const cJSON * const item)10689928cc5SHong Zhang CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON *const item)
10789928cc5SHong Zhang {
108ac530a7eSPierre Jolivet   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 
cJSON_Version(void)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 */
case_insensitive_strcmp(const unsigned char * string1,const unsigned char * string2)12789928cc5SHong Zhang static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
12889928cc5SHong Zhang {
129ac530a7eSPierre Jolivet   if ((string1 == NULL) || (string2 == NULL)) return 1;
13089928cc5SHong Zhang 
131ac530a7eSPierre Jolivet   if (string1 == string2) return 0;
13289928cc5SHong Zhang 
13389928cc5SHong Zhang   for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++) {
134ac530a7eSPierre Jolivet     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 */
internal_malloc(size_t size)14889928cc5SHong Zhang static void *CJSON_CDECL internal_malloc(size_t size)
14989928cc5SHong Zhang {
15089928cc5SHong Zhang   return malloc(size);
15189928cc5SHong Zhang }
internal_free(void * pointer)15289928cc5SHong Zhang static void CJSON_CDECL internal_free(void *pointer)
15389928cc5SHong Zhang {
15489928cc5SHong Zhang   free(pointer);
15589928cc5SHong Zhang }
internal_realloc(void * pointer,size_t size)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 
cJSON_strdup(const unsigned char * string,const internal_hooks * const hooks)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 
176ac530a7eSPierre Jolivet   if (string == NULL) return NULL;
17789928cc5SHong Zhang 
17889928cc5SHong Zhang   length = strlen((const char *)string) + sizeof("");
17989928cc5SHong Zhang   copy   = (unsigned char *)hooks->allocate(length);
180ac530a7eSPierre Jolivet   if (copy == NULL) return NULL;
18189928cc5SHong Zhang   memcpy(copy, string, length);
18289928cc5SHong Zhang 
18389928cc5SHong Zhang   return copy;
18489928cc5SHong Zhang }
18589928cc5SHong Zhang 
cJSON_InitHooks(cJSON_Hooks * hooks)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;
197ac530a7eSPierre Jolivet   if (hooks->malloc_fn != NULL) global_hooks.allocate = hooks->malloc_fn;
19889928cc5SHong Zhang 
19989928cc5SHong Zhang   global_hooks.deallocate = free;
200ac530a7eSPierre Jolivet   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;
204ac530a7eSPierre Jolivet   if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) global_hooks.reallocate = realloc;
20589928cc5SHong Zhang }
20689928cc5SHong Zhang 
20789928cc5SHong Zhang /* Internal constructor. */
cJSON_New_Item(const internal_hooks * const hooks)20889928cc5SHong Zhang static cJSON *cJSON_New_Item(const internal_hooks *const hooks)
20989928cc5SHong Zhang {
21089928cc5SHong Zhang   cJSON *node = (cJSON *)hooks->allocate(sizeof(cJSON));
211ac530a7eSPierre Jolivet   if (node) memset(node, '\0', sizeof(cJSON));
21289928cc5SHong Zhang 
21389928cc5SHong Zhang   return node;
21489928cc5SHong Zhang }
21589928cc5SHong Zhang 
21689928cc5SHong Zhang /* Delete a cJSON structure. */
cJSON_Delete(cJSON * item)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;
222ac530a7eSPierre Jolivet     if (!(item->type & cJSON_IsReference) && (item->child != NULL)) cJSON_Delete(item->child);
223ac530a7eSPierre Jolivet     if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) global_hooks.deallocate(item->valuestring);
224ac530a7eSPierre Jolivet     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 */
get_decimal_point(void)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. */
parse_number(cJSON * const item,parse_buffer * const input_buffer)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 
266ac530a7eSPierre Jolivet   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);
302ac530a7eSPierre Jolivet   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 */
cJSON_SetNumberHelper(cJSON * object,double number)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 
cJSON_SetValuestring(cJSON * object,const char * valuestring)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 */
339ac530a7eSPierre Jolivet   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);
345ac530a7eSPierre Jolivet   if (copy == NULL) return NULL;
346ac530a7eSPierre Jolivet   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 */
ensure(printbuffer * const p,size_t needed)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 
368ac530a7eSPierre Jolivet   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;
381ac530a7eSPierre Jolivet   if (needed <= p->length) return p->buffer + p->offset;
38289928cc5SHong Zhang 
383ac530a7eSPierre Jolivet   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 */
update_offset(printbuffer * const buffer)42889928cc5SHong Zhang static void update_offset(printbuffer *const buffer)
42989928cc5SHong Zhang {
43089928cc5SHong Zhang   const unsigned char *buffer_pointer = NULL;
431ac530a7eSPierre Jolivet   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 */
compare_double(double a,double b)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);
4414ad8454bSPierre Jolivet   return fabs(a - b) <= maxVal * DBL_EPSILON;
44289928cc5SHong Zhang }
44389928cc5SHong Zhang 
44489928cc5SHong Zhang /* Render the number nicely from the given item into a string. */
print_number(const cJSON * const item,printbuffer * const output_buffer)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 
455ac530a7eSPierre Jolivet   if (output_buffer == NULL) return false;
45689928cc5SHong Zhang 
45776c63389SBarry Smith   /* 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 */
474ac530a7eSPierre Jolivet   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(""));
478ac530a7eSPierre Jolivet   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 */
parse_hex4(const unsigned char * const input)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 */
utf16_literal_to_utf8(const unsigned char * const input_pointer,const unsigned char * const input_end,unsigned char ** output_pointer)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 */
546ac530a7eSPierre 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. */
parse_string(cJSON * const item,parse_buffer * const input_buffer)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 */
632ac530a7eSPierre Jolivet   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     }
650ac530a7eSPierre Jolivet     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(""));
655ac530a7eSPierre Jolivet     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) {
6613a7d0413SPierre Jolivet     if (*input_pointer != '\\') *output_pointer++ = *input_pointer++;
66289928cc5SHong Zhang     /* escape sequence */
66389928cc5SHong Zhang     else {
66489928cc5SHong Zhang       unsigned char sequence_length = 2;
665ac530a7eSPierre Jolivet       if ((input_end - input_pointer) < 1) goto fail;
66689928cc5SHong Zhang 
66789928cc5SHong Zhang       switch (input_pointer[1]) {
66889928cc5SHong Zhang       case 'b':
66989928cc5SHong Zhang         *output_pointer++ = '\b';
67089928cc5SHong Zhang         break;
67189928cc5SHong Zhang       case 'f':
67289928cc5SHong Zhang         *output_pointer++ = '\f';
67389928cc5SHong Zhang         break;
67489928cc5SHong Zhang       case 'n':
67589928cc5SHong Zhang         *output_pointer++ = '\n';
67689928cc5SHong Zhang         break;
67789928cc5SHong Zhang       case 'r':
67889928cc5SHong Zhang         *output_pointer++ = '\r';
67989928cc5SHong Zhang         break;
68089928cc5SHong Zhang       case 't':
68189928cc5SHong Zhang         *output_pointer++ = '\t';
68289928cc5SHong Zhang         break;
68389928cc5SHong Zhang       case '\"':
68489928cc5SHong Zhang       case '\\':
68589928cc5SHong Zhang       case '/':
68689928cc5SHong Zhang         *output_pointer++ = input_pointer[1];
68789928cc5SHong Zhang         break;
68889928cc5SHong Zhang 
68989928cc5SHong Zhang       /* UTF-16 literal */
69089928cc5SHong Zhang       case 'u':
69189928cc5SHong Zhang         sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
69289928cc5SHong Zhang         if (sequence_length == 0) {
69389928cc5SHong Zhang           /* failed to convert UTF16-literal to UTF-8 */
69489928cc5SHong Zhang           goto fail;
69589928cc5SHong Zhang         }
69689928cc5SHong Zhang         break;
69789928cc5SHong Zhang 
69889928cc5SHong Zhang       default:
69989928cc5SHong Zhang         goto fail;
70089928cc5SHong Zhang       }
70189928cc5SHong Zhang       input_pointer += sequence_length;
70289928cc5SHong Zhang     }
70389928cc5SHong Zhang   }
70489928cc5SHong Zhang 
70589928cc5SHong Zhang   /* zero terminate the output */
70689928cc5SHong Zhang   *output_pointer = '\0';
70789928cc5SHong Zhang 
70889928cc5SHong Zhang   item->type        = cJSON_String;
70989928cc5SHong Zhang   item->valuestring = (char *)output;
71089928cc5SHong Zhang 
71189928cc5SHong Zhang   input_buffer->offset = (size_t)(input_end - input_buffer->content);
71289928cc5SHong Zhang   input_buffer->offset++;
71389928cc5SHong Zhang 
71489928cc5SHong Zhang   return true;
71589928cc5SHong Zhang 
71689928cc5SHong Zhang fail:
717ac530a7eSPierre Jolivet   if (output != NULL) input_buffer->hooks.deallocate(output);
71889928cc5SHong Zhang 
719ac530a7eSPierre Jolivet   if (input_pointer != NULL) input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
72089928cc5SHong Zhang 
72189928cc5SHong Zhang   return false;
72289928cc5SHong Zhang }
72389928cc5SHong Zhang 
72489928cc5SHong Zhang /* Render the cstring provided to an escaped version that can be printed. */
print_string_ptr(const unsigned char * const input,printbuffer * const output_buffer)72589928cc5SHong Zhang static cJSON_bool print_string_ptr(const unsigned char *const input, printbuffer *const output_buffer)
72689928cc5SHong Zhang {
72789928cc5SHong Zhang   const unsigned char *input_pointer  = NULL;
72889928cc5SHong Zhang   unsigned char       *output         = NULL;
72989928cc5SHong Zhang   unsigned char       *output_pointer = NULL;
73089928cc5SHong Zhang   size_t               output_length  = 0;
73189928cc5SHong Zhang   /* numbers of additional characters needed for escaping */
73289928cc5SHong Zhang   size_t escape_characters = 0;
73389928cc5SHong Zhang 
734ac530a7eSPierre Jolivet   if (output_buffer == NULL) return false;
73589928cc5SHong Zhang 
73689928cc5SHong Zhang   /* empty string */
73789928cc5SHong Zhang   if (input == NULL) {
73889928cc5SHong Zhang     output = ensure(output_buffer, sizeof("\"\""));
739ac530a7eSPierre Jolivet     if (output == NULL) return false;
74089928cc5SHong Zhang     strcpy((char *)output, "\"\"");
74189928cc5SHong Zhang 
74289928cc5SHong Zhang     return true;
74389928cc5SHong Zhang   }
74489928cc5SHong Zhang 
74589928cc5SHong Zhang   /* set "flag" to 1 if something needs to be escaped */
74689928cc5SHong Zhang   for (input_pointer = input; *input_pointer; input_pointer++) {
74789928cc5SHong Zhang     switch (*input_pointer) {
74889928cc5SHong Zhang     case '\"':
74989928cc5SHong Zhang     case '\\':
75089928cc5SHong Zhang     case '\b':
75189928cc5SHong Zhang     case '\f':
75289928cc5SHong Zhang     case '\n':
75389928cc5SHong Zhang     case '\r':
75489928cc5SHong Zhang     case '\t':
75589928cc5SHong Zhang       /* one character escape sequence */
75689928cc5SHong Zhang       escape_characters++;
75789928cc5SHong Zhang       break;
75889928cc5SHong Zhang     default:
75989928cc5SHong Zhang       if (*input_pointer < 32) {
76089928cc5SHong Zhang         /* UTF-16 escape sequence uXXXX */
76189928cc5SHong Zhang         escape_characters += 5;
76289928cc5SHong Zhang       }
76389928cc5SHong Zhang       break;
76489928cc5SHong Zhang     }
76589928cc5SHong Zhang   }
76689928cc5SHong Zhang   output_length = (size_t)(input_pointer - input) + escape_characters;
76789928cc5SHong Zhang 
76889928cc5SHong Zhang   output = ensure(output_buffer, output_length + sizeof("\"\""));
769ac530a7eSPierre Jolivet   if (output == NULL) return false;
77089928cc5SHong Zhang 
77189928cc5SHong Zhang   /* no characters have to be escaped */
77289928cc5SHong Zhang   if (escape_characters == 0) {
77389928cc5SHong Zhang     output[0] = '\"';
77489928cc5SHong Zhang     memcpy(output + 1, input, output_length);
77589928cc5SHong Zhang     output[output_length + 1] = '\"';
77689928cc5SHong Zhang     output[output_length + 2] = '\0';
77789928cc5SHong Zhang 
77889928cc5SHong Zhang     return true;
77989928cc5SHong Zhang   }
78089928cc5SHong Zhang 
78189928cc5SHong Zhang   output[0]      = '\"';
78289928cc5SHong Zhang   output_pointer = output + 1;
78389928cc5SHong Zhang   /* copy the string */
78489928cc5SHong Zhang   for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) {
78589928cc5SHong Zhang     if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) {
78689928cc5SHong Zhang       /* normal character, copy */
78789928cc5SHong Zhang       *output_pointer = *input_pointer;
78889928cc5SHong Zhang     } else {
78989928cc5SHong Zhang       /* character needs to be escaped */
79089928cc5SHong Zhang       *output_pointer++ = '\\';
79189928cc5SHong Zhang       switch (*input_pointer) {
79289928cc5SHong Zhang       case '\\':
79389928cc5SHong Zhang         *output_pointer = '\\';
79489928cc5SHong Zhang         break;
79589928cc5SHong Zhang       case '\"':
79689928cc5SHong Zhang         *output_pointer = '\"';
79789928cc5SHong Zhang         break;
79889928cc5SHong Zhang       case '\b':
79989928cc5SHong Zhang         *output_pointer = 'b';
80089928cc5SHong Zhang         break;
80189928cc5SHong Zhang       case '\f':
80289928cc5SHong Zhang         *output_pointer = 'f';
80389928cc5SHong Zhang         break;
80489928cc5SHong Zhang       case '\n':
80589928cc5SHong Zhang         *output_pointer = 'n';
80689928cc5SHong Zhang         break;
80789928cc5SHong Zhang       case '\r':
80889928cc5SHong Zhang         *output_pointer = 'r';
80989928cc5SHong Zhang         break;
81089928cc5SHong Zhang       case '\t':
81189928cc5SHong Zhang         *output_pointer = 't';
81289928cc5SHong Zhang         break;
81389928cc5SHong Zhang       default:
81489928cc5SHong Zhang         /* escape and print as unicode codepoint */
815ae1eecf8SHong Zhang         snprintf((char *)output_pointer, 6, "u%04x", *input_pointer);
816ae1eecf8SHong Zhang         output_pointer += 5;
81789928cc5SHong Zhang         break;
81889928cc5SHong Zhang       }
81989928cc5SHong Zhang     }
82089928cc5SHong Zhang   }
82189928cc5SHong Zhang   output[output_length + 1] = '\"';
82289928cc5SHong Zhang   output[output_length + 2] = '\0';
82389928cc5SHong Zhang 
82489928cc5SHong Zhang   return true;
82589928cc5SHong Zhang }
82689928cc5SHong Zhang 
82789928cc5SHong Zhang /* Invoke print_string_ptr (which is useful) on an item. */
print_string(const cJSON * const item,printbuffer * const p)82889928cc5SHong Zhang static cJSON_bool print_string(const cJSON *const item, printbuffer *const p)
82989928cc5SHong Zhang {
83089928cc5SHong Zhang   return print_string_ptr((unsigned char *)item->valuestring, p);
83189928cc5SHong Zhang }
83289928cc5SHong Zhang 
83389928cc5SHong Zhang /* Predeclare these prototypes. */
83489928cc5SHong Zhang static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer);
83589928cc5SHong Zhang static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer);
83689928cc5SHong Zhang static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer);
83789928cc5SHong Zhang static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer);
83889928cc5SHong Zhang static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer);
83989928cc5SHong Zhang static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer);
84089928cc5SHong Zhang 
84189928cc5SHong Zhang /* Utility to jump whitespace and cr/lf */
buffer_skip_whitespace(parse_buffer * const buffer)84289928cc5SHong Zhang static parse_buffer *buffer_skip_whitespace(parse_buffer *const buffer)
84389928cc5SHong Zhang {
844ac530a7eSPierre Jolivet   if ((buffer == NULL) || (buffer->content == NULL)) return NULL;
84589928cc5SHong Zhang 
846ac530a7eSPierre Jolivet   if (cannot_access_at_index(buffer, 0)) return buffer;
84789928cc5SHong Zhang 
848ac530a7eSPierre Jolivet   while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) buffer->offset++;
84989928cc5SHong Zhang 
850ac530a7eSPierre Jolivet   if (buffer->offset == buffer->length) buffer->offset--;
85189928cc5SHong Zhang 
85289928cc5SHong Zhang   return buffer;
85389928cc5SHong Zhang }
85489928cc5SHong Zhang 
85589928cc5SHong Zhang /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
skip_utf8_bom(parse_buffer * const buffer)85689928cc5SHong Zhang static parse_buffer *skip_utf8_bom(parse_buffer *const buffer)
85789928cc5SHong Zhang {
858ac530a7eSPierre Jolivet   if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) return NULL;
85989928cc5SHong Zhang 
860ac530a7eSPierre Jolivet   if (can_access_at_index(buffer, 4) && (strncmp((const char *)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) buffer->offset += 3;
86189928cc5SHong Zhang 
86289928cc5SHong Zhang   return buffer;
86389928cc5SHong Zhang }
86489928cc5SHong Zhang 
cJSON_ParseWithOpts(const char * value,const char ** return_parse_end,cJSON_bool require_null_terminated)86589928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
86689928cc5SHong Zhang {
86789928cc5SHong Zhang   size_t buffer_length;
86889928cc5SHong Zhang 
869ac530a7eSPierre Jolivet   if (NULL == value) return NULL;
87089928cc5SHong Zhang 
87189928cc5SHong Zhang   /* Adding null character size due to require_null_terminated. */
87289928cc5SHong Zhang   buffer_length = strlen(value) + sizeof("");
87389928cc5SHong Zhang 
87489928cc5SHong Zhang   return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
87589928cc5SHong Zhang }
87689928cc5SHong Zhang 
87789928cc5SHong Zhang /* Parse an object - create a new root, and populate. */
cJSON_ParseWithLengthOpts(const char * value,size_t buffer_length,const char ** return_parse_end,cJSON_bool require_null_terminated)87889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
87989928cc5SHong Zhang {
88089928cc5SHong Zhang   parse_buffer buffer = {
88189928cc5SHong Zhang     0, 0, 0, 0, {0, 0, 0}
88289928cc5SHong Zhang   };
88389928cc5SHong Zhang   cJSON *item = NULL;
88489928cc5SHong Zhang 
88589928cc5SHong Zhang   /* reset error position */
88689928cc5SHong Zhang   global_error.json     = NULL;
88789928cc5SHong Zhang   global_error.position = 0;
88889928cc5SHong Zhang 
889ac530a7eSPierre Jolivet   if (value == NULL || 0 == buffer_length) goto fail;
89089928cc5SHong Zhang 
89189928cc5SHong Zhang   buffer.content = (const unsigned char *)value;
89289928cc5SHong Zhang   buffer.length  = buffer_length;
89389928cc5SHong Zhang   buffer.offset  = 0;
89489928cc5SHong Zhang   buffer.hooks   = global_hooks;
89589928cc5SHong Zhang 
89689928cc5SHong Zhang   item = cJSON_New_Item(&global_hooks);
89789928cc5SHong Zhang   if (item == NULL) /* memory fail */
89889928cc5SHong Zhang   {
89989928cc5SHong Zhang     goto fail;
90089928cc5SHong Zhang   }
90189928cc5SHong Zhang 
90289928cc5SHong Zhang   if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) {
90389928cc5SHong Zhang     /* parse failure. ep is set. */
90489928cc5SHong Zhang     goto fail;
90589928cc5SHong Zhang   }
90689928cc5SHong Zhang 
90789928cc5SHong Zhang   /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
90889928cc5SHong Zhang   if (require_null_terminated) {
90989928cc5SHong Zhang     buffer_skip_whitespace(&buffer);
910ac530a7eSPierre Jolivet     if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') goto fail;
91189928cc5SHong Zhang   }
912ac530a7eSPierre Jolivet   if (return_parse_end) *return_parse_end = (const char *)buffer_at_offset(&buffer);
91389928cc5SHong Zhang 
91489928cc5SHong Zhang   return item;
91589928cc5SHong Zhang 
91689928cc5SHong Zhang fail:
917ac530a7eSPierre Jolivet   if (item != NULL) cJSON_Delete(item);
91889928cc5SHong Zhang 
91989928cc5SHong Zhang   if (value != NULL) {
92089928cc5SHong Zhang     error local_error;
92189928cc5SHong Zhang     local_error.json     = (const unsigned char *)value;
92289928cc5SHong Zhang     local_error.position = 0;
92389928cc5SHong Zhang 
92489928cc5SHong Zhang     if (buffer.offset < buffer.length) {
92589928cc5SHong Zhang       local_error.position = buffer.offset;
92689928cc5SHong Zhang     } else if (buffer.length > 0) {
92789928cc5SHong Zhang       local_error.position = buffer.length - 1;
92889928cc5SHong Zhang     }
92989928cc5SHong Zhang 
930ac530a7eSPierre Jolivet     if (return_parse_end != NULL) *return_parse_end = (const char *)local_error.json + local_error.position;
93189928cc5SHong Zhang 
93289928cc5SHong Zhang     global_error = local_error;
93389928cc5SHong Zhang   }
93489928cc5SHong Zhang 
93589928cc5SHong Zhang   return NULL;
93689928cc5SHong Zhang }
93789928cc5SHong Zhang 
93889928cc5SHong Zhang /* Default options for cJSON_Parse */
cJSON_Parse(const char * value)93989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
94089928cc5SHong Zhang {
94189928cc5SHong Zhang   return cJSON_ParseWithOpts(value, 0, 0);
94289928cc5SHong Zhang }
94389928cc5SHong Zhang 
cJSON_ParseWithLength(const char * value,size_t buffer_length)94489928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
94589928cc5SHong Zhang {
94689928cc5SHong Zhang   return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
94789928cc5SHong Zhang }
94889928cc5SHong Zhang 
94989928cc5SHong Zhang #define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
95089928cc5SHong Zhang 
print(const cJSON * const item,cJSON_bool format,const internal_hooks * const hooks)95189928cc5SHong Zhang static unsigned char *print(const cJSON *const item, cJSON_bool format, const internal_hooks *const hooks)
95289928cc5SHong Zhang {
95389928cc5SHong Zhang   static const size_t default_buffer_size = 256;
95489928cc5SHong Zhang   printbuffer         buffer[1];
95589928cc5SHong Zhang   unsigned char      *printed = NULL;
95689928cc5SHong Zhang 
95789928cc5SHong Zhang   memset(buffer, 0, sizeof(buffer));
95889928cc5SHong Zhang 
95989928cc5SHong Zhang   /* create buffer */
96089928cc5SHong Zhang   buffer->buffer = (unsigned char *)hooks->allocate(default_buffer_size);
96189928cc5SHong Zhang   buffer->length = default_buffer_size;
96289928cc5SHong Zhang   buffer->format = format;
96389928cc5SHong Zhang   buffer->hooks  = *hooks;
964ac530a7eSPierre Jolivet   if (buffer->buffer == NULL) goto fail;
96589928cc5SHong Zhang 
96689928cc5SHong Zhang   /* print the value */
967ac530a7eSPierre Jolivet   if (!print_value(item, buffer)) goto fail;
96889928cc5SHong Zhang   update_offset(buffer);
96989928cc5SHong Zhang 
97089928cc5SHong Zhang   /* check if reallocate is available */
97189928cc5SHong Zhang   if (hooks->reallocate != NULL) {
97289928cc5SHong Zhang     printed = (unsigned char *)hooks->reallocate(buffer->buffer, buffer->offset + 1);
973ac530a7eSPierre Jolivet     if (printed == NULL) goto fail;
97489928cc5SHong Zhang     buffer->buffer = NULL;
97589928cc5SHong Zhang   } else /* otherwise copy the JSON over to a new buffer */
97689928cc5SHong Zhang   {
97789928cc5SHong Zhang     printed = (unsigned char *)hooks->allocate(buffer->offset + 1);
978ac530a7eSPierre Jolivet     if (printed == NULL) goto fail;
97989928cc5SHong Zhang     memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
98089928cc5SHong Zhang     printed[buffer->offset] = '\0'; /* just to be sure */
98189928cc5SHong Zhang 
98289928cc5SHong Zhang     /* free the buffer */
98389928cc5SHong Zhang     hooks->deallocate(buffer->buffer);
98489928cc5SHong Zhang   }
98589928cc5SHong Zhang 
98689928cc5SHong Zhang   return printed;
98789928cc5SHong Zhang 
98889928cc5SHong Zhang fail:
989ac530a7eSPierre Jolivet   if (buffer->buffer != NULL) hooks->deallocate(buffer->buffer);
99089928cc5SHong Zhang 
991ac530a7eSPierre Jolivet   if (printed != NULL) hooks->deallocate(printed);
99289928cc5SHong Zhang 
99389928cc5SHong Zhang   return NULL;
99489928cc5SHong Zhang }
99589928cc5SHong Zhang 
99689928cc5SHong Zhang /* Render a cJSON item/entity/structure to text. */
cJSON_Print(const cJSON * item)99789928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
99889928cc5SHong Zhang {
99989928cc5SHong Zhang   return (char *)print(item, true, &global_hooks);
100089928cc5SHong Zhang }
100189928cc5SHong Zhang 
cJSON_PrintUnformatted(const cJSON * item)100289928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
100389928cc5SHong Zhang {
100489928cc5SHong Zhang   return (char *)print(item, false, &global_hooks);
100589928cc5SHong Zhang }
100689928cc5SHong Zhang 
cJSON_PrintBuffered(const cJSON * item,int prebuffer,cJSON_bool fmt)100789928cc5SHong Zhang CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
100889928cc5SHong Zhang {
100989928cc5SHong Zhang   printbuffer p = {
101089928cc5SHong Zhang     0, 0, 0, 0, 0, 0, {0, 0, 0}
101189928cc5SHong Zhang   };
101289928cc5SHong Zhang 
1013ac530a7eSPierre Jolivet   if (prebuffer < 0) return NULL;
101489928cc5SHong Zhang 
101589928cc5SHong Zhang   p.buffer = (unsigned char *)global_hooks.allocate((size_t)prebuffer);
1016ac530a7eSPierre Jolivet   if (!p.buffer) return NULL;
101789928cc5SHong Zhang 
101889928cc5SHong Zhang   p.length  = (size_t)prebuffer;
101989928cc5SHong Zhang   p.offset  = 0;
102089928cc5SHong Zhang   p.noalloc = false;
102189928cc5SHong Zhang   p.format  = fmt;
102289928cc5SHong Zhang   p.hooks   = global_hooks;
102389928cc5SHong Zhang 
102489928cc5SHong Zhang   if (!print_value(item, &p)) {
102589928cc5SHong Zhang     global_hooks.deallocate(p.buffer);
102689928cc5SHong Zhang     return NULL;
102789928cc5SHong Zhang   }
102889928cc5SHong Zhang 
102989928cc5SHong Zhang   return (char *)p.buffer;
103089928cc5SHong Zhang }
103189928cc5SHong Zhang 
cJSON_PrintPreallocated(cJSON * item,char * buffer,const int length,const cJSON_bool format)103289928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
103389928cc5SHong Zhang {
103489928cc5SHong Zhang   printbuffer p = {
103589928cc5SHong Zhang     0, 0, 0, 0, 0, 0, {0, 0, 0}
103689928cc5SHong Zhang   };
103789928cc5SHong Zhang 
1038ac530a7eSPierre Jolivet   if ((length < 0) || (buffer == NULL)) return false;
103989928cc5SHong Zhang 
104089928cc5SHong Zhang   p.buffer  = (unsigned char *)buffer;
104189928cc5SHong Zhang   p.length  = (size_t)length;
104289928cc5SHong Zhang   p.offset  = 0;
104389928cc5SHong Zhang   p.noalloc = true;
104489928cc5SHong Zhang   p.format  = format;
104589928cc5SHong Zhang   p.hooks   = global_hooks;
104689928cc5SHong Zhang 
104789928cc5SHong Zhang   return print_value(item, &p);
104889928cc5SHong Zhang }
104989928cc5SHong Zhang 
105089928cc5SHong Zhang /* Parser core - when encountering text, process appropriately. */
parse_value(cJSON * const item,parse_buffer * const input_buffer)105189928cc5SHong Zhang static cJSON_bool parse_value(cJSON *const item, parse_buffer *const input_buffer)
105289928cc5SHong Zhang {
1053ac530a7eSPierre Jolivet   if ((input_buffer == NULL) || (input_buffer->content == NULL)) return false; /* no input */
105489928cc5SHong Zhang 
105589928cc5SHong Zhang   /* parse the different types of values */
105689928cc5SHong Zhang   /* null */
105789928cc5SHong Zhang   if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "null", 4) == 0)) {
105889928cc5SHong Zhang     item->type = cJSON_NULL;
105989928cc5SHong Zhang     input_buffer->offset += 4;
106089928cc5SHong Zhang     return true;
106189928cc5SHong Zhang   }
106289928cc5SHong Zhang   /* false */
106389928cc5SHong Zhang   if (can_read(input_buffer, 5) && (strncmp((const char *)buffer_at_offset(input_buffer), "false", 5) == 0)) {
106489928cc5SHong Zhang     item->type = cJSON_False;
106589928cc5SHong Zhang     input_buffer->offset += 5;
106689928cc5SHong Zhang     return true;
106789928cc5SHong Zhang   }
106889928cc5SHong Zhang   /* true */
106989928cc5SHong Zhang   if (can_read(input_buffer, 4) && (strncmp((const char *)buffer_at_offset(input_buffer), "true", 4) == 0)) {
107089928cc5SHong Zhang     item->type     = cJSON_True;
107189928cc5SHong Zhang     item->valueint = 1;
107289928cc5SHong Zhang     input_buffer->offset += 4;
107389928cc5SHong Zhang     return true;
107489928cc5SHong Zhang   }
107589928cc5SHong Zhang   /* string */
1076ac530a7eSPierre Jolivet   if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) return parse_string(item, input_buffer);
107789928cc5SHong Zhang   /* number */
1078ac530a7eSPierre Jolivet   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);
107989928cc5SHong Zhang   /* array */
1080ac530a7eSPierre Jolivet   if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) return parse_array(item, input_buffer);
108189928cc5SHong Zhang   /* object */
1082ac530a7eSPierre Jolivet   if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) return parse_object(item, input_buffer);
108389928cc5SHong Zhang 
108489928cc5SHong Zhang   return false;
108589928cc5SHong Zhang }
108689928cc5SHong Zhang 
108789928cc5SHong Zhang /* Render a value to text. */
print_value(const cJSON * const item,printbuffer * const output_buffer)108889928cc5SHong Zhang static cJSON_bool print_value(const cJSON *const item, printbuffer *const output_buffer)
108989928cc5SHong Zhang {
109089928cc5SHong Zhang   unsigned char *output = NULL;
109189928cc5SHong Zhang 
1092ac530a7eSPierre Jolivet   if ((item == NULL) || (output_buffer == NULL)) return false;
109389928cc5SHong Zhang 
1094f4f49eeaSPierre Jolivet   switch (item->type & 0xFF) {
109589928cc5SHong Zhang   case cJSON_NULL:
109689928cc5SHong Zhang     output = ensure(output_buffer, 5);
1097ac530a7eSPierre Jolivet     if (output == NULL) return false;
109889928cc5SHong Zhang     strcpy((char *)output, "null");
109989928cc5SHong Zhang     return true;
110089928cc5SHong Zhang 
110189928cc5SHong Zhang   case cJSON_False:
110289928cc5SHong Zhang     output = ensure(output_buffer, 6);
1103ac530a7eSPierre Jolivet     if (output == NULL) return false;
110489928cc5SHong Zhang     strcpy((char *)output, "false");
110589928cc5SHong Zhang     return true;
110689928cc5SHong Zhang 
110789928cc5SHong Zhang   case cJSON_True:
110889928cc5SHong Zhang     output = ensure(output_buffer, 5);
1109ac530a7eSPierre Jolivet     if (output == NULL) return false;
111089928cc5SHong Zhang     strcpy((char *)output, "true");
111189928cc5SHong Zhang     return true;
111289928cc5SHong Zhang 
111389928cc5SHong Zhang   case cJSON_Number:
111489928cc5SHong Zhang     return print_number(item, output_buffer);
111589928cc5SHong Zhang 
111689928cc5SHong Zhang   case cJSON_Raw: {
111789928cc5SHong Zhang     size_t raw_length = 0;
1118ac530a7eSPierre Jolivet     if (item->valuestring == NULL) return false;
111989928cc5SHong Zhang 
112089928cc5SHong Zhang     raw_length = strlen(item->valuestring) + sizeof("");
112189928cc5SHong Zhang     output     = ensure(output_buffer, raw_length);
1122ac530a7eSPierre Jolivet     if (output == NULL) return false;
112389928cc5SHong Zhang     memcpy(output, item->valuestring, raw_length);
112489928cc5SHong Zhang     return true;
112589928cc5SHong Zhang   }
112689928cc5SHong Zhang 
112789928cc5SHong Zhang   case cJSON_String:
112889928cc5SHong Zhang     return print_string(item, output_buffer);
112989928cc5SHong Zhang 
113089928cc5SHong Zhang   case cJSON_Array:
113189928cc5SHong Zhang     return print_array(item, output_buffer);
113289928cc5SHong Zhang 
113389928cc5SHong Zhang   case cJSON_Object:
113489928cc5SHong Zhang     return print_object(item, output_buffer);
113589928cc5SHong Zhang 
113689928cc5SHong Zhang   default:
113789928cc5SHong Zhang     return false;
113889928cc5SHong Zhang   }
113989928cc5SHong Zhang }
114089928cc5SHong Zhang 
114189928cc5SHong Zhang /* Build an array from input text. */
parse_array(cJSON * const item,parse_buffer * const input_buffer)114289928cc5SHong Zhang static cJSON_bool parse_array(cJSON *const item, parse_buffer *const input_buffer)
114389928cc5SHong Zhang {
114489928cc5SHong Zhang   cJSON *head         = NULL; /* head of the linked list */
114589928cc5SHong Zhang   cJSON *current_item = NULL;
114689928cc5SHong Zhang 
1147ac530a7eSPierre Jolivet   if (input_buffer->depth >= CJSON_NESTING_LIMIT) return false; /* to deeply nested */
114889928cc5SHong Zhang   input_buffer->depth++;
114989928cc5SHong Zhang 
115089928cc5SHong Zhang   if (buffer_at_offset(input_buffer)[0] != '[') {
115189928cc5SHong Zhang     /* not an array */
115289928cc5SHong Zhang     goto fail;
115389928cc5SHong Zhang   }
115489928cc5SHong Zhang 
115589928cc5SHong Zhang   input_buffer->offset++;
115689928cc5SHong Zhang   buffer_skip_whitespace(input_buffer);
115789928cc5SHong Zhang   if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) {
115889928cc5SHong Zhang     /* empty array */
115989928cc5SHong Zhang     goto success;
116089928cc5SHong Zhang   }
116189928cc5SHong Zhang 
116289928cc5SHong Zhang   /* check if we skipped to the end of the buffer */
116389928cc5SHong Zhang   if (cannot_access_at_index(input_buffer, 0)) {
116489928cc5SHong Zhang     input_buffer->offset--;
116589928cc5SHong Zhang     goto fail;
116689928cc5SHong Zhang   }
116789928cc5SHong Zhang 
116889928cc5SHong Zhang   /* step back to character in front of the first element */
116989928cc5SHong Zhang   input_buffer->offset--;
117089928cc5SHong Zhang   /* loop through the comma separated array elements */
117189928cc5SHong Zhang   do {
117289928cc5SHong Zhang     /* allocate next item */
1173f4f49eeaSPierre Jolivet     cJSON *new_item = cJSON_New_Item(&input_buffer->hooks);
1174ac530a7eSPierre Jolivet     if (new_item == NULL) goto fail; /* allocation failure */
117589928cc5SHong Zhang 
117689928cc5SHong Zhang     /* attach next item to list */
117789928cc5SHong Zhang     if (head == NULL) {
117889928cc5SHong Zhang       /* start the linked list */
117989928cc5SHong Zhang       current_item = head = new_item;
118089928cc5SHong Zhang     } else {
118189928cc5SHong Zhang       /* add to the end and advance */
118289928cc5SHong Zhang       current_item->next = new_item;
118389928cc5SHong Zhang       new_item->prev     = current_item;
118489928cc5SHong Zhang       current_item       = new_item;
118589928cc5SHong Zhang     }
118689928cc5SHong Zhang 
118789928cc5SHong Zhang     /* parse next value */
118889928cc5SHong Zhang     input_buffer->offset++;
118989928cc5SHong Zhang     buffer_skip_whitespace(input_buffer);
1190ac530a7eSPierre Jolivet     if (!parse_value(current_item, input_buffer)) goto fail; /* failed to parse value */
119189928cc5SHong Zhang     buffer_skip_whitespace(input_buffer);
119289928cc5SHong Zhang   } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
119389928cc5SHong Zhang 
1194ac530a7eSPierre Jolivet   if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') goto fail; /* expected end of array */
119589928cc5SHong Zhang 
119689928cc5SHong Zhang success:
119789928cc5SHong Zhang   input_buffer->depth--;
119889928cc5SHong Zhang 
1199ac530a7eSPierre Jolivet   if (head != NULL) head->prev = current_item;
120089928cc5SHong Zhang 
120189928cc5SHong Zhang   item->type  = cJSON_Array;
120289928cc5SHong Zhang   item->child = head;
120389928cc5SHong Zhang 
120489928cc5SHong Zhang   input_buffer->offset++;
120589928cc5SHong Zhang 
120689928cc5SHong Zhang   return true;
120789928cc5SHong Zhang 
120889928cc5SHong Zhang fail:
1209ac530a7eSPierre Jolivet   if (head != NULL) cJSON_Delete(head);
121089928cc5SHong Zhang 
121189928cc5SHong Zhang   return false;
121289928cc5SHong Zhang }
121389928cc5SHong Zhang 
121489928cc5SHong Zhang /* Render an array to text */
print_array(const cJSON * const item,printbuffer * const output_buffer)121589928cc5SHong Zhang static cJSON_bool print_array(const cJSON *const item, printbuffer *const output_buffer)
121689928cc5SHong Zhang {
121789928cc5SHong Zhang   unsigned char *output_pointer  = NULL;
121889928cc5SHong Zhang   size_t         length          = 0;
121989928cc5SHong Zhang   cJSON         *current_element = item->child;
122089928cc5SHong Zhang 
1221ac530a7eSPierre Jolivet   if (output_buffer == NULL) return false;
122289928cc5SHong Zhang 
122389928cc5SHong Zhang   /* Compose the output array. */
122489928cc5SHong Zhang   /* opening square bracket */
122589928cc5SHong Zhang   output_pointer = ensure(output_buffer, 1);
1226ac530a7eSPierre Jolivet   if (output_pointer == NULL) return false;
122789928cc5SHong Zhang 
122889928cc5SHong Zhang   *output_pointer = '[';
122989928cc5SHong Zhang   output_buffer->offset++;
123089928cc5SHong Zhang   output_buffer->depth++;
123189928cc5SHong Zhang 
123289928cc5SHong Zhang   while (current_element != NULL) {
1233ac530a7eSPierre Jolivet     if (!print_value(current_element, output_buffer)) return false;
123489928cc5SHong Zhang     update_offset(output_buffer);
123589928cc5SHong Zhang     if (current_element->next) {
123689928cc5SHong Zhang       length         = (size_t)(output_buffer->format ? 2 : 1);
123789928cc5SHong Zhang       output_pointer = ensure(output_buffer, length + 1);
1238ac530a7eSPierre Jolivet       if (output_pointer == NULL) return false;
123989928cc5SHong Zhang       *output_pointer++ = ',';
1240ac530a7eSPierre Jolivet       if (output_buffer->format) *output_pointer++ = ' ';
124189928cc5SHong Zhang       *output_pointer = '\0';
124289928cc5SHong Zhang       output_buffer->offset += length;
124389928cc5SHong Zhang     }
124489928cc5SHong Zhang     current_element = current_element->next;
124589928cc5SHong Zhang   }
124689928cc5SHong Zhang 
124789928cc5SHong Zhang   output_pointer = ensure(output_buffer, 2);
1248ac530a7eSPierre Jolivet   if (output_pointer == NULL) return false;
124989928cc5SHong Zhang   *output_pointer++ = ']';
125089928cc5SHong Zhang   *output_pointer   = '\0';
125189928cc5SHong Zhang   output_buffer->depth--;
125289928cc5SHong Zhang 
125389928cc5SHong Zhang   return true;
125489928cc5SHong Zhang }
125589928cc5SHong Zhang 
125689928cc5SHong Zhang /* Build an object from the text. */
parse_object(cJSON * const item,parse_buffer * const input_buffer)125789928cc5SHong Zhang static cJSON_bool parse_object(cJSON *const item, parse_buffer *const input_buffer)
125889928cc5SHong Zhang {
125989928cc5SHong Zhang   cJSON *head         = NULL; /* linked list head */
126089928cc5SHong Zhang   cJSON *current_item = NULL;
126189928cc5SHong Zhang 
1262ac530a7eSPierre Jolivet   if (input_buffer->depth >= CJSON_NESTING_LIMIT) return false; /* to deeply nested */
126389928cc5SHong Zhang   input_buffer->depth++;
126489928cc5SHong Zhang 
1265ac530a7eSPierre Jolivet   if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) goto fail; /* not an object */
126689928cc5SHong Zhang 
126789928cc5SHong Zhang   input_buffer->offset++;
126889928cc5SHong Zhang   buffer_skip_whitespace(input_buffer);
1269ac530a7eSPierre Jolivet   if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) goto success; /* empty object */
127089928cc5SHong Zhang 
127189928cc5SHong Zhang   /* check if we skipped to the end of the buffer */
127289928cc5SHong Zhang   if (cannot_access_at_index(input_buffer, 0)) {
127389928cc5SHong Zhang     input_buffer->offset--;
127489928cc5SHong Zhang     goto fail;
127589928cc5SHong Zhang   }
127689928cc5SHong Zhang 
127789928cc5SHong Zhang   /* step back to character in front of the first element */
127889928cc5SHong Zhang   input_buffer->offset--;
127989928cc5SHong Zhang   /* loop through the comma separated array elements */
128089928cc5SHong Zhang   do {
128189928cc5SHong Zhang     /* allocate next item */
1282f4f49eeaSPierre Jolivet     cJSON *new_item = cJSON_New_Item(&input_buffer->hooks);
1283ac530a7eSPierre Jolivet     if (new_item == NULL) goto fail; /* allocation failure */
128489928cc5SHong Zhang 
128589928cc5SHong Zhang     /* attach next item to list */
128689928cc5SHong Zhang     if (head == NULL) {
128789928cc5SHong Zhang       /* start the linked list */
128889928cc5SHong Zhang       current_item = head = new_item;
128989928cc5SHong Zhang     } else {
129089928cc5SHong Zhang       /* add to the end and advance */
129189928cc5SHong Zhang       current_item->next = new_item;
129289928cc5SHong Zhang       new_item->prev     = current_item;
129389928cc5SHong Zhang       current_item       = new_item;
129489928cc5SHong Zhang     }
129589928cc5SHong Zhang 
129689928cc5SHong Zhang     /* parse the name of the child */
129789928cc5SHong Zhang     input_buffer->offset++;
129889928cc5SHong Zhang     buffer_skip_whitespace(input_buffer);
1299ac530a7eSPierre Jolivet     if (!parse_string(current_item, input_buffer)) goto fail; /* failed to parse name */
130089928cc5SHong Zhang     buffer_skip_whitespace(input_buffer);
130189928cc5SHong Zhang 
130289928cc5SHong Zhang     /* swap valuestring and string, because we parsed the name */
130389928cc5SHong Zhang     current_item->string      = current_item->valuestring;
130489928cc5SHong Zhang     current_item->valuestring = NULL;
130589928cc5SHong Zhang 
1306ac530a7eSPierre Jolivet     if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) goto fail; /* invalid object */
130789928cc5SHong Zhang 
130889928cc5SHong Zhang     /* parse the value */
130989928cc5SHong Zhang     input_buffer->offset++;
131089928cc5SHong Zhang     buffer_skip_whitespace(input_buffer);
1311ac530a7eSPierre Jolivet     if (!parse_value(current_item, input_buffer)) goto fail; /* failed to parse value */
131289928cc5SHong Zhang     buffer_skip_whitespace(input_buffer);
131389928cc5SHong Zhang   } while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
131489928cc5SHong Zhang 
1315ac530a7eSPierre Jolivet   if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) goto fail; /* expected end of object */
131689928cc5SHong Zhang 
131789928cc5SHong Zhang success:
131889928cc5SHong Zhang   input_buffer->depth--;
131989928cc5SHong Zhang 
1320ac530a7eSPierre Jolivet   if (head != NULL) head->prev = current_item;
132189928cc5SHong Zhang 
132289928cc5SHong Zhang   item->type  = cJSON_Object;
132389928cc5SHong Zhang   item->child = head;
132489928cc5SHong Zhang 
132589928cc5SHong Zhang   input_buffer->offset++;
132689928cc5SHong Zhang   return true;
132789928cc5SHong Zhang 
132889928cc5SHong Zhang fail:
1329ac530a7eSPierre Jolivet   if (head != NULL) cJSON_Delete(head);
133089928cc5SHong Zhang 
133189928cc5SHong Zhang   return false;
133289928cc5SHong Zhang }
133389928cc5SHong Zhang 
133489928cc5SHong Zhang /* Render an object to text. */
print_object(const cJSON * const item,printbuffer * const output_buffer)133589928cc5SHong Zhang static cJSON_bool print_object(const cJSON *const item, printbuffer *const output_buffer)
133689928cc5SHong Zhang {
133789928cc5SHong Zhang   unsigned char *output_pointer = NULL;
133889928cc5SHong Zhang   size_t         length         = 0;
133989928cc5SHong Zhang   cJSON         *current_item   = item->child;
134089928cc5SHong Zhang 
1341ac530a7eSPierre Jolivet   if (output_buffer == NULL) return false;
134289928cc5SHong Zhang 
134389928cc5SHong Zhang   /* Compose the output: */
134489928cc5SHong Zhang   length         = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */
134589928cc5SHong Zhang   output_pointer = ensure(output_buffer, length + 1);
1346ac530a7eSPierre Jolivet   if (output_pointer == NULL) return false;
134789928cc5SHong Zhang 
134889928cc5SHong Zhang   *output_pointer++ = '{';
134989928cc5SHong Zhang   output_buffer->depth++;
1350ac530a7eSPierre Jolivet   if (output_buffer->format) *output_pointer++ = '\n';
135189928cc5SHong Zhang   output_buffer->offset += length;
135289928cc5SHong Zhang 
135389928cc5SHong Zhang   while (current_item) {
135489928cc5SHong Zhang     if (output_buffer->format) {
135589928cc5SHong Zhang       size_t i;
135689928cc5SHong Zhang       output_pointer = ensure(output_buffer, output_buffer->depth);
1357ac530a7eSPierre Jolivet       if (output_pointer == NULL) return false;
1358ac530a7eSPierre Jolivet       for (i = 0; i < output_buffer->depth; i++) *output_pointer++ = '\t';
135989928cc5SHong Zhang       output_buffer->offset += output_buffer->depth;
136089928cc5SHong Zhang     }
136189928cc5SHong Zhang 
136289928cc5SHong Zhang     /* print key */
1363ac530a7eSPierre Jolivet     if (!print_string_ptr((unsigned char *)current_item->string, output_buffer)) return false;
136489928cc5SHong Zhang     update_offset(output_buffer);
136589928cc5SHong Zhang 
136689928cc5SHong Zhang     length         = (size_t)(output_buffer->format ? 2 : 1);
136789928cc5SHong Zhang     output_pointer = ensure(output_buffer, length);
1368ac530a7eSPierre Jolivet     if (output_pointer == NULL) return false;
136989928cc5SHong Zhang     *output_pointer++ = ':';
1370ac530a7eSPierre Jolivet     if (output_buffer->format) *output_pointer++ = '\t';
137189928cc5SHong Zhang     output_buffer->offset += length;
137289928cc5SHong Zhang 
137389928cc5SHong Zhang     /* print value */
1374ac530a7eSPierre Jolivet     if (!print_value(current_item, output_buffer)) return false;
137589928cc5SHong Zhang     update_offset(output_buffer);
137689928cc5SHong Zhang 
137789928cc5SHong Zhang     /* print comma if not last */
137889928cc5SHong Zhang     length         = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
137989928cc5SHong Zhang     output_pointer = ensure(output_buffer, length + 1);
1380ac530a7eSPierre Jolivet     if (output_pointer == NULL) return false;
1381ac530a7eSPierre Jolivet     if (current_item->next) *output_pointer++ = ',';
138289928cc5SHong Zhang 
1383ac530a7eSPierre Jolivet     if (output_buffer->format) *output_pointer++ = '\n';
138489928cc5SHong Zhang     *output_pointer = '\0';
138589928cc5SHong Zhang     output_buffer->offset += length;
138689928cc5SHong Zhang 
138789928cc5SHong Zhang     current_item = current_item->next;
138889928cc5SHong Zhang   }
138989928cc5SHong Zhang 
139089928cc5SHong Zhang   output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
1391ac530a7eSPierre Jolivet   if (output_pointer == NULL) return false;
139289928cc5SHong Zhang   if (output_buffer->format) {
139389928cc5SHong Zhang     size_t i;
1394ac530a7eSPierre Jolivet     for (i = 0; i < (output_buffer->depth - 1); i++) *output_pointer++ = '\t';
139589928cc5SHong Zhang   }
139689928cc5SHong Zhang   *output_pointer++ = '}';
139789928cc5SHong Zhang   *output_pointer   = '\0';
139889928cc5SHong Zhang   output_buffer->depth--;
139989928cc5SHong Zhang 
140089928cc5SHong Zhang   return true;
140189928cc5SHong Zhang }
140289928cc5SHong Zhang 
140389928cc5SHong Zhang /* Get Array size/item / object item. */
cJSON_GetArraySize(const cJSON * array)140489928cc5SHong Zhang CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
140589928cc5SHong Zhang {
140689928cc5SHong Zhang   cJSON *child = NULL;
140789928cc5SHong Zhang   size_t size  = 0;
140889928cc5SHong Zhang 
1409ac530a7eSPierre Jolivet   if (array == NULL) return 0;
141089928cc5SHong Zhang 
141189928cc5SHong Zhang   child = array->child;
141289928cc5SHong Zhang 
141389928cc5SHong Zhang   while (child != NULL) {
141489928cc5SHong Zhang     size++;
141589928cc5SHong Zhang     child = child->next;
141689928cc5SHong Zhang   }
141789928cc5SHong Zhang 
141889928cc5SHong Zhang   /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
141989928cc5SHong Zhang 
142089928cc5SHong Zhang   return (int)size;
142189928cc5SHong Zhang }
142289928cc5SHong Zhang 
get_array_item(const cJSON * array,size_t index)142389928cc5SHong Zhang static cJSON *get_array_item(const cJSON *array, size_t index)
142489928cc5SHong Zhang {
142589928cc5SHong Zhang   cJSON *current_child = NULL;
142689928cc5SHong Zhang 
1427ac530a7eSPierre Jolivet   if (array == NULL) return NULL;
142889928cc5SHong Zhang 
142989928cc5SHong Zhang   current_child = array->child;
143089928cc5SHong Zhang   while ((current_child != NULL) && (index > 0)) {
143189928cc5SHong Zhang     index--;
143289928cc5SHong Zhang     current_child = current_child->next;
143389928cc5SHong Zhang   }
143489928cc5SHong Zhang 
143589928cc5SHong Zhang   return current_child;
143689928cc5SHong Zhang }
143789928cc5SHong Zhang 
cJSON_GetArrayItem(const cJSON * array,int index)143889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
143989928cc5SHong Zhang {
1440ac530a7eSPierre Jolivet   if (index < 0) return NULL;
144189928cc5SHong Zhang 
144289928cc5SHong Zhang   return get_array_item(array, (size_t)index);
144389928cc5SHong Zhang }
144489928cc5SHong Zhang 
get_object_item(const cJSON * const object,const char * const name,const cJSON_bool case_sensitive)144589928cc5SHong Zhang static cJSON *get_object_item(const cJSON *const object, const char *const name, const cJSON_bool case_sensitive)
144689928cc5SHong Zhang {
144789928cc5SHong Zhang   cJSON *current_element = NULL;
144889928cc5SHong Zhang 
1449ac530a7eSPierre Jolivet   if ((object == NULL) || (name == NULL)) return NULL;
145089928cc5SHong Zhang 
145189928cc5SHong Zhang   current_element = object->child;
145289928cc5SHong Zhang   if (case_sensitive) {
1453ac530a7eSPierre Jolivet     while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) current_element = current_element->next;
145489928cc5SHong Zhang   } else {
1455ac530a7eSPierre Jolivet     while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char *)name, (const unsigned char *)current_element->string) != 0)) current_element = current_element->next;
145689928cc5SHong Zhang   }
145789928cc5SHong Zhang 
1458ac530a7eSPierre Jolivet   if ((current_element == NULL) || (current_element->string == NULL)) return NULL;
145989928cc5SHong Zhang 
146089928cc5SHong Zhang   return current_element;
146189928cc5SHong Zhang }
146289928cc5SHong Zhang 
cJSON_GetObjectItem(const cJSON * const object,const char * const string)146389928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *const object, const char *const string)
146489928cc5SHong Zhang {
146589928cc5SHong Zhang   return get_object_item(object, string, false);
146689928cc5SHong Zhang }
146789928cc5SHong Zhang 
cJSON_GetObjectItemCaseSensitive(const cJSON * const object,const char * const string)146889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *const object, const char *const string)
146989928cc5SHong Zhang {
147089928cc5SHong Zhang   return get_object_item(object, string, true);
147189928cc5SHong Zhang }
147289928cc5SHong Zhang 
cJSON_HasObjectItem(const cJSON * object,const char * string)147389928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
147489928cc5SHong Zhang {
147589928cc5SHong Zhang   return cJSON_GetObjectItem(object, string) ? 1 : 0;
147689928cc5SHong Zhang }
147789928cc5SHong Zhang 
147889928cc5SHong Zhang /* Utility for array list handling. */
suffix_object(cJSON * prev,cJSON * item)147989928cc5SHong Zhang static void suffix_object(cJSON *prev, cJSON *item)
148089928cc5SHong Zhang {
148189928cc5SHong Zhang   prev->next = item;
148289928cc5SHong Zhang   item->prev = prev;
148389928cc5SHong Zhang }
148489928cc5SHong Zhang 
148589928cc5SHong Zhang /* Utility for handling references. */
create_reference(const cJSON * item,const internal_hooks * const hooks)148689928cc5SHong Zhang static cJSON *create_reference(const cJSON *item, const internal_hooks *const hooks)
148789928cc5SHong Zhang {
148889928cc5SHong Zhang   cJSON *reference = NULL;
1489ac530a7eSPierre Jolivet   if (item == NULL) return NULL;
149089928cc5SHong Zhang 
149189928cc5SHong Zhang   reference = cJSON_New_Item(hooks);
1492ac530a7eSPierre Jolivet   if (reference == NULL) return NULL;
149389928cc5SHong Zhang 
149489928cc5SHong Zhang   memcpy(reference, item, sizeof(cJSON));
149589928cc5SHong Zhang   reference->string = NULL;
149689928cc5SHong Zhang   reference->type |= cJSON_IsReference;
149789928cc5SHong Zhang   reference->next = reference->prev = NULL;
149889928cc5SHong Zhang   return reference;
149989928cc5SHong Zhang }
150089928cc5SHong Zhang 
add_item_to_array(cJSON * array,cJSON * item)150189928cc5SHong Zhang static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
150289928cc5SHong Zhang {
150389928cc5SHong Zhang   cJSON *child = NULL;
150489928cc5SHong Zhang 
1505ac530a7eSPierre Jolivet   if ((item == NULL) || (array == NULL) || (array == item)) return false;
150689928cc5SHong Zhang 
150789928cc5SHong Zhang   child = array->child;
150889928cc5SHong Zhang   /*
150989928cc5SHong Zhang      * To find the last item in array quickly, we use prev in array
151089928cc5SHong Zhang      */
151189928cc5SHong Zhang   if (child == NULL) {
151289928cc5SHong Zhang     /* list is empty, start new one */
151389928cc5SHong Zhang     array->child = item;
151489928cc5SHong Zhang     item->prev   = item;
151589928cc5SHong Zhang     item->next   = NULL;
151689928cc5SHong Zhang   } else {
151789928cc5SHong Zhang     /* append to the end */
151889928cc5SHong Zhang     if (child->prev) {
151989928cc5SHong Zhang       suffix_object(child->prev, item);
152089928cc5SHong Zhang       array->child->prev = item;
152189928cc5SHong Zhang     }
152289928cc5SHong Zhang   }
152389928cc5SHong Zhang 
152489928cc5SHong Zhang   return true;
152589928cc5SHong Zhang }
152689928cc5SHong Zhang 
152789928cc5SHong Zhang /* Add item to array/object. */
cJSON_AddItemToArray(cJSON * array,cJSON * item)152889928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
152989928cc5SHong Zhang {
153089928cc5SHong Zhang   return add_item_to_array(array, item);
153189928cc5SHong Zhang }
153289928cc5SHong Zhang 
153389928cc5SHong Zhang #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
153489928cc5SHong Zhang   #pragma GCC diagnostic push
153589928cc5SHong Zhang #endif
153689928cc5SHong Zhang #ifdef __GNUC__
153789928cc5SHong Zhang   #pragma GCC diagnostic ignored "-Wcast-qual"
153889928cc5SHong Zhang #endif
153989928cc5SHong Zhang /* helper function to cast away const */
cast_away_const(const void * string)154089928cc5SHong Zhang static void *cast_away_const(const void *string)
154189928cc5SHong Zhang {
154289928cc5SHong Zhang   return (void *)string;
154389928cc5SHong Zhang }
154489928cc5SHong Zhang #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
154589928cc5SHong Zhang   #pragma GCC diagnostic pop
154689928cc5SHong Zhang #endif
154789928cc5SHong Zhang 
add_item_to_object(cJSON * const object,const char * const string,cJSON * const item,const internal_hooks * const hooks,const cJSON_bool constant_key)154889928cc5SHong 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)
154989928cc5SHong Zhang {
155089928cc5SHong Zhang   char *new_key  = NULL;
155189928cc5SHong Zhang   int   new_type = cJSON_Invalid;
155289928cc5SHong Zhang 
1553ac530a7eSPierre Jolivet   if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) return false;
155489928cc5SHong Zhang 
155589928cc5SHong Zhang   if (constant_key) {
155689928cc5SHong Zhang     new_key  = (char *)cast_away_const(string);
155789928cc5SHong Zhang     new_type = item->type | cJSON_StringIsConst;
155889928cc5SHong Zhang   } else {
155989928cc5SHong Zhang     new_key = (char *)cJSON_strdup((const unsigned char *)string, hooks);
1560ac530a7eSPierre Jolivet     if (new_key == NULL) return false;
156189928cc5SHong Zhang 
156289928cc5SHong Zhang     new_type = item->type & ~cJSON_StringIsConst;
156389928cc5SHong Zhang   }
156489928cc5SHong Zhang 
1565ac530a7eSPierre Jolivet   if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) hooks->deallocate(item->string);
156689928cc5SHong Zhang 
156789928cc5SHong Zhang   item->string = new_key;
156889928cc5SHong Zhang   item->type   = new_type;
156989928cc5SHong Zhang 
157089928cc5SHong Zhang   return add_item_to_array(object, item);
157189928cc5SHong Zhang }
157289928cc5SHong Zhang 
cJSON_AddItemToObject(cJSON * object,const char * string,cJSON * item)157389928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
157489928cc5SHong Zhang {
157589928cc5SHong Zhang   return add_item_to_object(object, string, item, &global_hooks, false);
157689928cc5SHong Zhang }
157789928cc5SHong Zhang 
157889928cc5SHong Zhang /* Add an item to an object with constant string as key */
cJSON_AddItemToObjectCS(cJSON * object,const char * string,cJSON * item)157989928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
158089928cc5SHong Zhang {
158189928cc5SHong Zhang   return add_item_to_object(object, string, item, &global_hooks, true);
158289928cc5SHong Zhang }
158389928cc5SHong Zhang 
cJSON_AddItemReferenceToArray(cJSON * array,cJSON * item)158489928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
158589928cc5SHong Zhang {
1586ac530a7eSPierre Jolivet   if (array == NULL) return false;
158789928cc5SHong Zhang 
158889928cc5SHong Zhang   return add_item_to_array(array, create_reference(item, &global_hooks));
158989928cc5SHong Zhang }
159089928cc5SHong Zhang 
cJSON_AddItemReferenceToObject(cJSON * object,const char * string,cJSON * item)159189928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
159289928cc5SHong Zhang {
1593ac530a7eSPierre Jolivet   if ((object == NULL) || (string == NULL)) return false;
159489928cc5SHong Zhang 
159589928cc5SHong Zhang   return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
159689928cc5SHong Zhang }
159789928cc5SHong Zhang 
cJSON_AddNullToObject(cJSON * const object,const char * const name)159889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddNullToObject(cJSON *const object, const char *const name)
159989928cc5SHong Zhang {
160089928cc5SHong Zhang   cJSON *null = cJSON_CreateNull();
1601ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, null, &global_hooks, false)) return null;
160289928cc5SHong Zhang 
160389928cc5SHong Zhang   cJSON_Delete(null);
160489928cc5SHong Zhang   return NULL;
160589928cc5SHong Zhang }
160689928cc5SHong Zhang 
cJSON_AddTrueToObject(cJSON * const object,const char * const name)160789928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddTrueToObject(cJSON *const object, const char *const name)
160889928cc5SHong Zhang {
160989928cc5SHong Zhang   cJSON *true_item = cJSON_CreateTrue();
1610ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, true_item, &global_hooks, false)) return true_item;
161189928cc5SHong Zhang 
161289928cc5SHong Zhang   cJSON_Delete(true_item);
161389928cc5SHong Zhang   return NULL;
161489928cc5SHong Zhang }
161589928cc5SHong Zhang 
cJSON_AddFalseToObject(cJSON * const object,const char * const name)161689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddFalseToObject(cJSON *const object, const char *const name)
161789928cc5SHong Zhang {
161889928cc5SHong Zhang   cJSON *false_item = cJSON_CreateFalse();
1619ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, false_item, &global_hooks, false)) return false_item;
162089928cc5SHong Zhang 
162189928cc5SHong Zhang   cJSON_Delete(false_item);
162289928cc5SHong Zhang   return NULL;
162389928cc5SHong Zhang }
162489928cc5SHong Zhang 
cJSON_AddBoolToObject(cJSON * const object,const char * const name,const cJSON_bool boolean)162589928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddBoolToObject(cJSON *const object, const char *const name, const cJSON_bool boolean)
162689928cc5SHong Zhang {
162789928cc5SHong Zhang   cJSON *bool_item = cJSON_CreateBool(boolean);
1628ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, bool_item, &global_hooks, false)) return bool_item;
162989928cc5SHong Zhang 
163089928cc5SHong Zhang   cJSON_Delete(bool_item);
163189928cc5SHong Zhang   return NULL;
163289928cc5SHong Zhang }
163389928cc5SHong Zhang 
cJSON_AddNumberToObject(cJSON * const object,const char * const name,const double number)163489928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddNumberToObject(cJSON *const object, const char *const name, const double number)
163589928cc5SHong Zhang {
163689928cc5SHong Zhang   cJSON *number_item = cJSON_CreateNumber(number);
1637ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, number_item, &global_hooks, false)) return number_item;
163889928cc5SHong Zhang 
163989928cc5SHong Zhang   cJSON_Delete(number_item);
164089928cc5SHong Zhang   return NULL;
164189928cc5SHong Zhang }
164289928cc5SHong Zhang 
cJSON_AddStringToObject(cJSON * const object,const char * const name,const char * const string)164389928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddStringToObject(cJSON *const object, const char *const name, const char *const string)
164489928cc5SHong Zhang {
164589928cc5SHong Zhang   cJSON *string_item = cJSON_CreateString(string);
1646ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, string_item, &global_hooks, false)) return string_item;
164789928cc5SHong Zhang 
164889928cc5SHong Zhang   cJSON_Delete(string_item);
164989928cc5SHong Zhang   return NULL;
165089928cc5SHong Zhang }
165189928cc5SHong Zhang 
cJSON_AddRawToObject(cJSON * const object,const char * const name,const char * const raw)165289928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddRawToObject(cJSON *const object, const char *const name, const char *const raw)
165389928cc5SHong Zhang {
165489928cc5SHong Zhang   cJSON *raw_item = cJSON_CreateRaw(raw);
1655ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, raw_item, &global_hooks, false)) return raw_item;
165689928cc5SHong Zhang 
165789928cc5SHong Zhang   cJSON_Delete(raw_item);
165889928cc5SHong Zhang   return NULL;
165989928cc5SHong Zhang }
166089928cc5SHong Zhang 
cJSON_AddObjectToObject(cJSON * const object,const char * const name)166189928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddObjectToObject(cJSON *const object, const char *const name)
166289928cc5SHong Zhang {
166389928cc5SHong Zhang   cJSON *object_item = cJSON_CreateObject();
1664ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, object_item, &global_hooks, false)) return object_item;
166589928cc5SHong Zhang 
166689928cc5SHong Zhang   cJSON_Delete(object_item);
166789928cc5SHong Zhang   return NULL;
166889928cc5SHong Zhang }
166989928cc5SHong Zhang 
cJSON_AddArrayToObject(cJSON * const object,const char * const name)167089928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_AddArrayToObject(cJSON *const object, const char *const name)
167189928cc5SHong Zhang {
167289928cc5SHong Zhang   cJSON *array = cJSON_CreateArray();
1673ac530a7eSPierre Jolivet   if (add_item_to_object(object, name, array, &global_hooks, false)) return array;
167489928cc5SHong Zhang 
167589928cc5SHong Zhang   cJSON_Delete(array);
167689928cc5SHong Zhang   return NULL;
167789928cc5SHong Zhang }
167889928cc5SHong Zhang 
cJSON_DetachItemViaPointer(cJSON * parent,cJSON * const item)167989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON *const item)
168089928cc5SHong Zhang {
1681ac530a7eSPierre Jolivet   if ((parent == NULL) || (item == NULL)) return NULL;
168289928cc5SHong Zhang 
168389928cc5SHong Zhang   if (item != parent->child) {
168489928cc5SHong Zhang     /* not the first element */
168589928cc5SHong Zhang     item->prev->next = item->next;
168689928cc5SHong Zhang   }
168789928cc5SHong Zhang   if (item->next != NULL) {
168889928cc5SHong Zhang     /* not the last element */
168989928cc5SHong Zhang     item->next->prev = item->prev;
169089928cc5SHong Zhang   }
169189928cc5SHong Zhang 
169289928cc5SHong Zhang   if (item == parent->child) {
169389928cc5SHong Zhang     /* first element */
169489928cc5SHong Zhang     parent->child = item->next;
169589928cc5SHong Zhang   } else if (item->next == NULL) {
169689928cc5SHong Zhang     /* last element */
169789928cc5SHong Zhang     parent->child->prev = item->prev;
169889928cc5SHong Zhang   }
169989928cc5SHong Zhang 
170089928cc5SHong Zhang   /* make sure the detached item doesn't point anywhere anymore */
170189928cc5SHong Zhang   item->prev = NULL;
170289928cc5SHong Zhang   item->next = NULL;
170389928cc5SHong Zhang 
170489928cc5SHong Zhang   return item;
170589928cc5SHong Zhang }
170689928cc5SHong Zhang 
cJSON_DetachItemFromArray(cJSON * array,int which)170789928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
170889928cc5SHong Zhang {
1709ac530a7eSPierre Jolivet   if (which < 0) return NULL;
171089928cc5SHong Zhang 
171189928cc5SHong Zhang   return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
171289928cc5SHong Zhang }
171389928cc5SHong Zhang 
cJSON_DeleteItemFromArray(cJSON * array,int which)171489928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
171589928cc5SHong Zhang {
171689928cc5SHong Zhang   cJSON_Delete(cJSON_DetachItemFromArray(array, which));
171789928cc5SHong Zhang }
171889928cc5SHong Zhang 
cJSON_DetachItemFromObject(cJSON * object,const char * string)171989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
172089928cc5SHong Zhang {
172189928cc5SHong Zhang   cJSON *to_detach = cJSON_GetObjectItem(object, string);
172289928cc5SHong Zhang 
172389928cc5SHong Zhang   return cJSON_DetachItemViaPointer(object, to_detach);
172489928cc5SHong Zhang }
172589928cc5SHong Zhang 
cJSON_DetachItemFromObjectCaseSensitive(cJSON * object,const char * string)172689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
172789928cc5SHong Zhang {
172889928cc5SHong Zhang   cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
172989928cc5SHong Zhang 
173089928cc5SHong Zhang   return cJSON_DetachItemViaPointer(object, to_detach);
173189928cc5SHong Zhang }
173289928cc5SHong Zhang 
cJSON_DeleteItemFromObject(cJSON * object,const char * string)173389928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
173489928cc5SHong Zhang {
173589928cc5SHong Zhang   cJSON_Delete(cJSON_DetachItemFromObject(object, string));
173689928cc5SHong Zhang }
173789928cc5SHong Zhang 
cJSON_DeleteItemFromObjectCaseSensitive(cJSON * object,const char * string)173889928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
173989928cc5SHong Zhang {
174089928cc5SHong Zhang   cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
174189928cc5SHong Zhang }
174289928cc5SHong Zhang 
174389928cc5SHong Zhang /* Replace array/object items with new ones. */
cJSON_InsertItemInArray(cJSON * array,int which,cJSON * newitem)174489928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
174589928cc5SHong Zhang {
174689928cc5SHong Zhang   cJSON *after_inserted = NULL;
174789928cc5SHong Zhang 
1748ac530a7eSPierre Jolivet   if (which < 0) return false;
174989928cc5SHong Zhang 
175089928cc5SHong Zhang   after_inserted = get_array_item(array, (size_t)which);
1751ac530a7eSPierre Jolivet   if (after_inserted == NULL) return add_item_to_array(array, newitem);
175289928cc5SHong Zhang 
175389928cc5SHong Zhang   newitem->next        = after_inserted;
175489928cc5SHong Zhang   newitem->prev        = after_inserted->prev;
175589928cc5SHong Zhang   after_inserted->prev = newitem;
175689928cc5SHong Zhang   if (after_inserted == array->child) {
175789928cc5SHong Zhang     array->child = newitem;
175889928cc5SHong Zhang   } else {
175989928cc5SHong Zhang     newitem->prev->next = newitem;
176089928cc5SHong Zhang   }
176189928cc5SHong Zhang   return true;
176289928cc5SHong Zhang }
176389928cc5SHong Zhang 
cJSON_ReplaceItemViaPointer(cJSON * const parent,cJSON * const item,cJSON * replacement)176489928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON *const parent, cJSON *const item, cJSON *replacement)
176589928cc5SHong Zhang {
1766ac530a7eSPierre Jolivet   if ((parent == NULL) || (replacement == NULL) || (item == NULL)) return false;
176789928cc5SHong Zhang 
1768ac530a7eSPierre Jolivet   if (replacement == item) return true;
176989928cc5SHong Zhang 
177089928cc5SHong Zhang   replacement->next = item->next;
177189928cc5SHong Zhang   replacement->prev = item->prev;
177289928cc5SHong Zhang 
1773ac530a7eSPierre Jolivet   if (replacement->next != NULL) replacement->next->prev = replacement;
177489928cc5SHong Zhang   if (parent->child == item) {
1775ac530a7eSPierre Jolivet     if (parent->child->prev == parent->child) replacement->prev = replacement;
177689928cc5SHong Zhang     parent->child = replacement;
177789928cc5SHong Zhang   } else { /*
177889928cc5SHong Zhang          * To find the last item in array quickly, we use prev in array.
177989928cc5SHong Zhang          * We can't modify the last item's next pointer where this item was the parent's child
178089928cc5SHong Zhang          */
1781ac530a7eSPierre Jolivet     if (replacement->prev != NULL) replacement->prev->next = replacement;
1782ac530a7eSPierre Jolivet     if (replacement->next == NULL) parent->child->prev = replacement;
178389928cc5SHong Zhang   }
178489928cc5SHong Zhang 
178589928cc5SHong Zhang   item->next = NULL;
178689928cc5SHong Zhang   item->prev = NULL;
178789928cc5SHong Zhang   cJSON_Delete(item);
178889928cc5SHong Zhang 
178989928cc5SHong Zhang   return true;
179089928cc5SHong Zhang }
179189928cc5SHong Zhang 
cJSON_ReplaceItemInArray(cJSON * array,int which,cJSON * newitem)179289928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
179389928cc5SHong Zhang {
1794ac530a7eSPierre Jolivet   if (which < 0) return false;
179589928cc5SHong Zhang 
179689928cc5SHong Zhang   return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
179789928cc5SHong Zhang }
179889928cc5SHong Zhang 
replace_item_in_object(cJSON * object,const char * string,cJSON * replacement,cJSON_bool case_sensitive)179989928cc5SHong Zhang static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
180089928cc5SHong Zhang {
1801ac530a7eSPierre Jolivet   if ((replacement == NULL) || (string == NULL)) return false;
180289928cc5SHong Zhang 
180389928cc5SHong Zhang   /* replace the name in the replacement */
1804ac530a7eSPierre Jolivet   if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) cJSON_free(replacement->string);
180589928cc5SHong Zhang   replacement->string = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks);
1806ac530a7eSPierre Jolivet   if (replacement->string == NULL) return false;
180789928cc5SHong Zhang 
180889928cc5SHong Zhang   replacement->type &= ~cJSON_StringIsConst;
180989928cc5SHong Zhang 
181089928cc5SHong Zhang   return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
181189928cc5SHong Zhang }
181289928cc5SHong Zhang 
cJSON_ReplaceItemInObject(cJSON * object,const char * string,cJSON * newitem)181389928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
181489928cc5SHong Zhang {
181589928cc5SHong Zhang   return replace_item_in_object(object, string, newitem, false);
181689928cc5SHong Zhang }
181789928cc5SHong Zhang 
cJSON_ReplaceItemInObjectCaseSensitive(cJSON * object,const char * string,cJSON * newitem)181889928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
181989928cc5SHong Zhang {
182089928cc5SHong Zhang   return replace_item_in_object(object, string, newitem, true);
182189928cc5SHong Zhang }
182289928cc5SHong Zhang 
182389928cc5SHong Zhang /* Create basic types: */
cJSON_CreateNull(void)182489928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
182589928cc5SHong Zhang {
182689928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
1827ac530a7eSPierre Jolivet   if (item) item->type = cJSON_NULL;
182889928cc5SHong Zhang 
182989928cc5SHong Zhang   return item;
183089928cc5SHong Zhang }
183189928cc5SHong Zhang 
cJSON_CreateTrue(void)183289928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
183389928cc5SHong Zhang {
183489928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
1835ac530a7eSPierre Jolivet   if (item) item->type = cJSON_True;
183689928cc5SHong Zhang 
183789928cc5SHong Zhang   return item;
183889928cc5SHong Zhang }
183989928cc5SHong Zhang 
cJSON_CreateFalse(void)184089928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
184189928cc5SHong Zhang {
184289928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
1843ac530a7eSPierre Jolivet   if (item) item->type = cJSON_False;
184489928cc5SHong Zhang 
184589928cc5SHong Zhang   return item;
184689928cc5SHong Zhang }
184789928cc5SHong Zhang 
cJSON_CreateBool(cJSON_bool boolean)184889928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
184989928cc5SHong Zhang {
185089928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
1851ac530a7eSPierre Jolivet   if (item) item->type = boolean ? cJSON_True : cJSON_False;
185289928cc5SHong Zhang 
185389928cc5SHong Zhang   return item;
185489928cc5SHong Zhang }
185589928cc5SHong Zhang 
cJSON_CreateNumber(double num)185689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
185789928cc5SHong Zhang {
185889928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
185989928cc5SHong Zhang   if (item) {
186089928cc5SHong Zhang     item->type        = cJSON_Number;
186189928cc5SHong Zhang     item->valuedouble = num;
186289928cc5SHong Zhang 
186389928cc5SHong Zhang     /* use saturation in case of overflow */
186489928cc5SHong Zhang     if (num >= INT_MAX) {
186589928cc5SHong Zhang       item->valueint = INT_MAX;
186689928cc5SHong Zhang     } else if (num <= (double)INT_MIN) {
186789928cc5SHong Zhang       item->valueint = INT_MIN;
186889928cc5SHong Zhang     } else {
186989928cc5SHong Zhang       item->valueint = (int)num;
187089928cc5SHong Zhang     }
187189928cc5SHong Zhang   }
187289928cc5SHong Zhang 
187389928cc5SHong Zhang   return item;
187489928cc5SHong Zhang }
187589928cc5SHong Zhang 
cJSON_CreateString(const char * string)187689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
187789928cc5SHong Zhang {
187889928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
187989928cc5SHong Zhang   if (item) {
188089928cc5SHong Zhang     item->type        = cJSON_String;
188189928cc5SHong Zhang     item->valuestring = (char *)cJSON_strdup((const unsigned char *)string, &global_hooks);
188289928cc5SHong Zhang     if (!item->valuestring) {
188389928cc5SHong Zhang       cJSON_Delete(item);
188489928cc5SHong Zhang       return NULL;
188589928cc5SHong Zhang     }
188689928cc5SHong Zhang   }
188789928cc5SHong Zhang 
188889928cc5SHong Zhang   return item;
188989928cc5SHong Zhang }
189089928cc5SHong Zhang 
cJSON_CreateStringReference(const char * string)189189928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
189289928cc5SHong Zhang {
189389928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
189489928cc5SHong Zhang   if (item != NULL) {
189589928cc5SHong Zhang     item->type        = cJSON_String | cJSON_IsReference;
189689928cc5SHong Zhang     item->valuestring = (char *)cast_away_const(string);
189789928cc5SHong Zhang   }
189889928cc5SHong Zhang 
189989928cc5SHong Zhang   return item;
190089928cc5SHong Zhang }
190189928cc5SHong Zhang 
cJSON_CreateObjectReference(const cJSON * child)190289928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
190389928cc5SHong Zhang {
190489928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
190589928cc5SHong Zhang   if (item != NULL) {
190689928cc5SHong Zhang     item->type  = cJSON_Object | cJSON_IsReference;
190789928cc5SHong Zhang     item->child = (cJSON *)cast_away_const(child);
190889928cc5SHong Zhang   }
190989928cc5SHong Zhang 
191089928cc5SHong Zhang   return item;
191189928cc5SHong Zhang }
191289928cc5SHong Zhang 
cJSON_CreateArrayReference(const cJSON * child)191389928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child)
191489928cc5SHong Zhang {
191589928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
191689928cc5SHong Zhang   if (item != NULL) {
191789928cc5SHong Zhang     item->type  = cJSON_Array | cJSON_IsReference;
191889928cc5SHong Zhang     item->child = (cJSON *)cast_away_const(child);
191989928cc5SHong Zhang   }
192089928cc5SHong Zhang 
192189928cc5SHong Zhang   return item;
192289928cc5SHong Zhang }
192389928cc5SHong Zhang 
cJSON_CreateRaw(const char * raw)192489928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
192589928cc5SHong Zhang {
192689928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
192789928cc5SHong Zhang   if (item) {
192889928cc5SHong Zhang     item->type        = cJSON_Raw;
192989928cc5SHong Zhang     item->valuestring = (char *)cJSON_strdup((const unsigned char *)raw, &global_hooks);
193089928cc5SHong Zhang     if (!item->valuestring) {
193189928cc5SHong Zhang       cJSON_Delete(item);
193289928cc5SHong Zhang       return NULL;
193389928cc5SHong Zhang     }
193489928cc5SHong Zhang   }
193589928cc5SHong Zhang 
193689928cc5SHong Zhang   return item;
193789928cc5SHong Zhang }
193889928cc5SHong Zhang 
cJSON_CreateArray(void)193989928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
194089928cc5SHong Zhang {
194189928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
1942ac530a7eSPierre Jolivet   if (item) item->type = cJSON_Array;
194389928cc5SHong Zhang 
194489928cc5SHong Zhang   return item;
194589928cc5SHong Zhang }
194689928cc5SHong Zhang 
cJSON_CreateObject(void)194789928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
194889928cc5SHong Zhang {
194989928cc5SHong Zhang   cJSON *item = cJSON_New_Item(&global_hooks);
1950ac530a7eSPierre Jolivet   if (item) item->type = cJSON_Object;
195189928cc5SHong Zhang 
195289928cc5SHong Zhang   return item;
195389928cc5SHong Zhang }
195489928cc5SHong Zhang 
195589928cc5SHong Zhang /* Create Arrays: */
cJSON_CreateIntArray(const int * numbers,int count)195689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
195789928cc5SHong Zhang {
195889928cc5SHong Zhang   size_t i = 0;
195989928cc5SHong Zhang   cJSON *n = NULL;
196089928cc5SHong Zhang   cJSON *p = NULL;
196189928cc5SHong Zhang   cJSON *a = NULL;
196289928cc5SHong Zhang 
1963ac530a7eSPierre Jolivet   if ((count < 0) || (numbers == NULL)) return NULL;
196489928cc5SHong Zhang 
196589928cc5SHong Zhang   a = cJSON_CreateArray();
196689928cc5SHong Zhang 
196789928cc5SHong Zhang   for (i = 0; a && (i < (size_t)count); i++) {
196889928cc5SHong Zhang     n = cJSON_CreateNumber(numbers[i]);
196989928cc5SHong Zhang     if (!n) {
197089928cc5SHong Zhang       cJSON_Delete(a);
197189928cc5SHong Zhang       return NULL;
197289928cc5SHong Zhang     }
197389928cc5SHong Zhang     if (!i) {
197489928cc5SHong Zhang       a->child = n;
197589928cc5SHong Zhang     } else {
197689928cc5SHong Zhang       suffix_object(p, n);
197789928cc5SHong Zhang     }
197889928cc5SHong Zhang     p = n;
197989928cc5SHong Zhang   }
198089928cc5SHong Zhang 
1981ac530a7eSPierre Jolivet   if (a && a->child) a->child->prev = n;
198289928cc5SHong Zhang 
198389928cc5SHong Zhang   return a;
198489928cc5SHong Zhang }
198589928cc5SHong Zhang 
cJSON_CreateFloatArray(const float * numbers,int count)198689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
198789928cc5SHong Zhang {
198889928cc5SHong Zhang   size_t i = 0;
198989928cc5SHong Zhang   cJSON *n = NULL;
199089928cc5SHong Zhang   cJSON *p = NULL;
199189928cc5SHong Zhang   cJSON *a = NULL;
199289928cc5SHong Zhang 
1993ac530a7eSPierre Jolivet   if ((count < 0) || (numbers == NULL)) return NULL;
199489928cc5SHong Zhang 
199589928cc5SHong Zhang   a = cJSON_CreateArray();
199689928cc5SHong Zhang 
199789928cc5SHong Zhang   for (i = 0; a && (i < (size_t)count); i++) {
199889928cc5SHong Zhang     n = cJSON_CreateNumber((double)numbers[i]);
199989928cc5SHong Zhang     if (!n) {
200089928cc5SHong Zhang       cJSON_Delete(a);
200189928cc5SHong Zhang       return NULL;
200289928cc5SHong Zhang     }
200389928cc5SHong Zhang     if (!i) {
200489928cc5SHong Zhang       a->child = n;
200589928cc5SHong Zhang     } else {
200689928cc5SHong Zhang       suffix_object(p, n);
200789928cc5SHong Zhang     }
200889928cc5SHong Zhang     p = n;
200989928cc5SHong Zhang   }
201089928cc5SHong Zhang 
2011ac530a7eSPierre Jolivet   if (a && a->child) a->child->prev = n;
201289928cc5SHong Zhang 
201389928cc5SHong Zhang   return a;
201489928cc5SHong Zhang }
201589928cc5SHong Zhang 
cJSON_CreateDoubleArray(const double * numbers,int count)201689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
201789928cc5SHong Zhang {
201889928cc5SHong Zhang   size_t i = 0;
201989928cc5SHong Zhang   cJSON *n = NULL;
202089928cc5SHong Zhang   cJSON *p = NULL;
202189928cc5SHong Zhang   cJSON *a = NULL;
202289928cc5SHong Zhang 
2023ac530a7eSPierre Jolivet   if ((count < 0) || (numbers == NULL)) return NULL;
202489928cc5SHong Zhang 
202589928cc5SHong Zhang   a = cJSON_CreateArray();
202689928cc5SHong Zhang 
202789928cc5SHong Zhang   for (i = 0; a && (i < (size_t)count); i++) {
202889928cc5SHong Zhang     n = cJSON_CreateNumber(numbers[i]);
202989928cc5SHong Zhang     if (!n) {
203089928cc5SHong Zhang       cJSON_Delete(a);
203189928cc5SHong Zhang       return NULL;
203289928cc5SHong Zhang     }
203389928cc5SHong Zhang     if (!i) {
203489928cc5SHong Zhang       a->child = n;
203589928cc5SHong Zhang     } else {
203689928cc5SHong Zhang       suffix_object(p, n);
203789928cc5SHong Zhang     }
203889928cc5SHong Zhang     p = n;
203989928cc5SHong Zhang   }
204089928cc5SHong Zhang 
2041ac530a7eSPierre Jolivet   if (a && a->child) a->child->prev = n;
204289928cc5SHong Zhang 
204389928cc5SHong Zhang   return a;
204489928cc5SHong Zhang }
204589928cc5SHong Zhang 
cJSON_CreateStringArray(const char * const * strings,int count)204689928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
204789928cc5SHong Zhang {
204889928cc5SHong Zhang   size_t i = 0;
204989928cc5SHong Zhang   cJSON *n = NULL;
205089928cc5SHong Zhang   cJSON *p = NULL;
205189928cc5SHong Zhang   cJSON *a = NULL;
205289928cc5SHong Zhang 
2053ac530a7eSPierre Jolivet   if ((count < 0) || (strings == NULL)) return NULL;
205489928cc5SHong Zhang 
205589928cc5SHong Zhang   a = cJSON_CreateArray();
205689928cc5SHong Zhang 
205789928cc5SHong Zhang   for (i = 0; a && (i < (size_t)count); i++) {
205889928cc5SHong Zhang     n = cJSON_CreateString(strings[i]);
205989928cc5SHong Zhang     if (!n) {
206089928cc5SHong Zhang       cJSON_Delete(a);
206189928cc5SHong Zhang       return NULL;
206289928cc5SHong Zhang     }
206389928cc5SHong Zhang     if (!i) {
206489928cc5SHong Zhang       a->child = n;
206589928cc5SHong Zhang     } else {
206689928cc5SHong Zhang       suffix_object(p, n);
206789928cc5SHong Zhang     }
206889928cc5SHong Zhang     p = n;
206989928cc5SHong Zhang   }
207089928cc5SHong Zhang 
2071ac530a7eSPierre Jolivet   if (a && a->child) a->child->prev = n;
207289928cc5SHong Zhang 
207389928cc5SHong Zhang   return a;
207489928cc5SHong Zhang }
207589928cc5SHong Zhang 
207689928cc5SHong Zhang /* Duplication */
cJSON_Duplicate(const cJSON * item,cJSON_bool recurse)207789928cc5SHong Zhang CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
207889928cc5SHong Zhang {
207989928cc5SHong Zhang   cJSON *newitem  = NULL;
208089928cc5SHong Zhang   cJSON *child    = NULL;
208189928cc5SHong Zhang   cJSON *next     = NULL;
208289928cc5SHong Zhang   cJSON *newchild = NULL;
208389928cc5SHong Zhang 
208489928cc5SHong Zhang   /* Bail on bad ptr */
2085ac530a7eSPierre Jolivet   if (!item) goto fail;
208689928cc5SHong Zhang   /* Create new item */
208789928cc5SHong Zhang   newitem = cJSON_New_Item(&global_hooks);
2088ac530a7eSPierre Jolivet   if (!newitem) goto fail;
208989928cc5SHong Zhang   /* Copy over all vars */
209089928cc5SHong Zhang   newitem->type        = item->type & (~cJSON_IsReference);
209189928cc5SHong Zhang   newitem->valueint    = item->valueint;
209289928cc5SHong Zhang   newitem->valuedouble = item->valuedouble;
209389928cc5SHong Zhang   if (item->valuestring) {
209489928cc5SHong Zhang     newitem->valuestring = (char *)cJSON_strdup((unsigned char *)item->valuestring, &global_hooks);
2095ac530a7eSPierre Jolivet     if (!newitem->valuestring) goto fail;
209689928cc5SHong Zhang   }
209789928cc5SHong Zhang   if (item->string) {
209889928cc5SHong Zhang     newitem->string = (item->type & cJSON_StringIsConst) ? item->string : (char *)cJSON_strdup((unsigned char *)item->string, &global_hooks);
2099ac530a7eSPierre Jolivet     if (!newitem->string) goto fail;
210089928cc5SHong Zhang   }
210189928cc5SHong Zhang   /* If non-recursive, then we're done! */
2102ac530a7eSPierre Jolivet   if (!recurse) return newitem;
210389928cc5SHong Zhang   /* Walk the ->next chain for the child. */
210489928cc5SHong Zhang   child = item->child;
210589928cc5SHong Zhang   while (child != NULL) {
210689928cc5SHong Zhang     newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
2107ac530a7eSPierre Jolivet     if (!newchild) goto fail;
210889928cc5SHong Zhang     if (next != NULL) {
210989928cc5SHong Zhang       /* If newitem->child already set, then crosswire ->prev and ->next and move on */
211089928cc5SHong Zhang       next->next     = newchild;
211189928cc5SHong Zhang       newchild->prev = next;
211289928cc5SHong Zhang       next           = newchild;
211389928cc5SHong Zhang     } else {
211489928cc5SHong Zhang       /* Set newitem->child and move to it */
211589928cc5SHong Zhang       newitem->child = newchild;
211689928cc5SHong Zhang       next           = newchild;
211789928cc5SHong Zhang     }
211889928cc5SHong Zhang     child = child->next;
211989928cc5SHong Zhang   }
2120ac530a7eSPierre Jolivet   if (newitem && newitem->child) newitem->child->prev = newchild;
212189928cc5SHong Zhang 
212289928cc5SHong Zhang   return newitem;
212389928cc5SHong Zhang 
212489928cc5SHong Zhang fail:
2125ac530a7eSPierre Jolivet   if (newitem != NULL) cJSON_Delete(newitem);
212689928cc5SHong Zhang 
212789928cc5SHong Zhang   return NULL;
212889928cc5SHong Zhang }
212989928cc5SHong Zhang 
skip_oneline_comment(char ** input)213089928cc5SHong Zhang static void skip_oneline_comment(char **input)
213189928cc5SHong Zhang {
213289928cc5SHong Zhang   *input += static_strlen("//");
213389928cc5SHong Zhang 
213489928cc5SHong Zhang   for (; (*input)[0] != '\0'; ++(*input)) {
213589928cc5SHong Zhang     if ((*input)[0] == '\n') {
213689928cc5SHong Zhang       *input += static_strlen("\n");
213789928cc5SHong Zhang       return;
213889928cc5SHong Zhang     }
213989928cc5SHong Zhang   }
214089928cc5SHong Zhang }
214189928cc5SHong Zhang 
skip_multiline_comment(char ** input)214289928cc5SHong Zhang static void skip_multiline_comment(char **input)
214389928cc5SHong Zhang {
214489928cc5SHong Zhang   *input += static_strlen("/*");
214589928cc5SHong Zhang 
214689928cc5SHong Zhang   for (; (*input)[0] != '\0'; ++(*input)) {
214789928cc5SHong Zhang     if (((*input)[0] == '*') && ((*input)[1] == '/')) {
214889928cc5SHong Zhang       *input += static_strlen("*/");
214989928cc5SHong Zhang       return;
215089928cc5SHong Zhang     }
215189928cc5SHong Zhang   }
215289928cc5SHong Zhang }
215389928cc5SHong Zhang 
minify_string(char ** input,char ** output)215489928cc5SHong Zhang static void minify_string(char **input, char **output)
215589928cc5SHong Zhang {
215689928cc5SHong Zhang   (*output)[0] = (*input)[0];
215789928cc5SHong Zhang   *input += static_strlen("\"");
215889928cc5SHong Zhang   *output += static_strlen("\"");
215989928cc5SHong Zhang 
216089928cc5SHong Zhang   for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
216189928cc5SHong Zhang     (*output)[0] = (*input)[0];
216289928cc5SHong Zhang 
216389928cc5SHong Zhang     if ((*input)[0] == '\"') {
216489928cc5SHong Zhang       (*output)[0] = '\"';
216589928cc5SHong Zhang       *input += static_strlen("\"");
216689928cc5SHong Zhang       *output += static_strlen("\"");
216789928cc5SHong Zhang       return;
216889928cc5SHong Zhang     } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
216989928cc5SHong Zhang       (*output)[1] = (*input)[1];
217089928cc5SHong Zhang       *input += static_strlen("\"");
217189928cc5SHong Zhang       *output += static_strlen("\"");
217289928cc5SHong Zhang     }
217389928cc5SHong Zhang   }
217489928cc5SHong Zhang }
217589928cc5SHong Zhang 
cJSON_Minify(char * json)217689928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_Minify(char *json)
217789928cc5SHong Zhang {
217889928cc5SHong Zhang   char *into = json;
217989928cc5SHong Zhang 
2180ac530a7eSPierre Jolivet   if (json == NULL) return;
218189928cc5SHong Zhang 
218289928cc5SHong Zhang   while (json[0] != '\0') {
218389928cc5SHong Zhang     switch (json[0]) {
218489928cc5SHong Zhang     case ' ':
218589928cc5SHong Zhang     case '\t':
218689928cc5SHong Zhang     case '\r':
218789928cc5SHong Zhang     case '\n':
218889928cc5SHong Zhang       json++;
218989928cc5SHong Zhang       break;
219089928cc5SHong Zhang 
219189928cc5SHong Zhang     case '/':
219289928cc5SHong Zhang       if (json[1] == '/') {
219389928cc5SHong Zhang         skip_oneline_comment(&json);
219489928cc5SHong Zhang       } else if (json[1] == '*') {
219589928cc5SHong Zhang         skip_multiline_comment(&json);
219689928cc5SHong Zhang       } else {
219789928cc5SHong Zhang         json++;
219889928cc5SHong Zhang       }
219989928cc5SHong Zhang       break;
220089928cc5SHong Zhang 
220189928cc5SHong Zhang     case '\"':
220289928cc5SHong Zhang       minify_string(&json, (char **)&into);
220389928cc5SHong Zhang       break;
220489928cc5SHong Zhang 
220589928cc5SHong Zhang     default:
220689928cc5SHong Zhang       into[0] = json[0];
220789928cc5SHong Zhang       json++;
220889928cc5SHong Zhang       into++;
220989928cc5SHong Zhang     }
221089928cc5SHong Zhang   }
221189928cc5SHong Zhang 
221289928cc5SHong Zhang   /* and null-terminate. */
221389928cc5SHong Zhang   *into = '\0';
221489928cc5SHong Zhang }
221589928cc5SHong Zhang 
cJSON_IsInvalid(const cJSON * const item)221689928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON *const item)
221789928cc5SHong Zhang {
2218ac530a7eSPierre Jolivet   if (item == NULL) return false;
221989928cc5SHong Zhang 
222089928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_Invalid;
222189928cc5SHong Zhang }
222289928cc5SHong Zhang 
cJSON_IsFalse(const cJSON * const item)222389928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON *const item)
222489928cc5SHong Zhang {
2225ac530a7eSPierre Jolivet   if (item == NULL) return false;
222689928cc5SHong Zhang 
222789928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_False;
222889928cc5SHong Zhang }
222989928cc5SHong Zhang 
cJSON_IsTrue(const cJSON * const item)223089928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON *const item)
223189928cc5SHong Zhang {
2232ac530a7eSPierre Jolivet   if (item == NULL) return false;
223389928cc5SHong Zhang 
223489928cc5SHong Zhang   return (item->type & 0xff) == cJSON_True;
223589928cc5SHong Zhang }
223689928cc5SHong Zhang 
cJSON_IsBool(const cJSON * const item)223789928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON *const item)
223889928cc5SHong Zhang {
2239ac530a7eSPierre Jolivet   if (item == NULL) return false;
224089928cc5SHong Zhang 
224189928cc5SHong Zhang   return (item->type & (cJSON_True | cJSON_False)) != 0;
224289928cc5SHong Zhang }
cJSON_IsNull(const cJSON * const item)224389928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON *const item)
224489928cc5SHong Zhang {
2245ac530a7eSPierre Jolivet   if (item == NULL) return false;
224689928cc5SHong Zhang 
224789928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_NULL;
224889928cc5SHong Zhang }
224989928cc5SHong Zhang 
cJSON_IsNumber(const cJSON * const item)225089928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON *const item)
225189928cc5SHong Zhang {
2252ac530a7eSPierre Jolivet   if (item == NULL) return false;
225389928cc5SHong Zhang 
225489928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_Number;
225589928cc5SHong Zhang }
225689928cc5SHong Zhang 
cJSON_IsString(const cJSON * const item)225789928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON *const item)
225889928cc5SHong Zhang {
2259ac530a7eSPierre Jolivet   if (item == NULL) return false;
226089928cc5SHong Zhang 
226189928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_String;
226289928cc5SHong Zhang }
226389928cc5SHong Zhang 
cJSON_IsArray(const cJSON * const item)226489928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON *const item)
226589928cc5SHong Zhang {
2266ac530a7eSPierre Jolivet   if (item == NULL) return false;
226789928cc5SHong Zhang 
226889928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_Array;
226989928cc5SHong Zhang }
227089928cc5SHong Zhang 
cJSON_IsObject(const cJSON * const item)227189928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON *const item)
227289928cc5SHong Zhang {
2273ac530a7eSPierre Jolivet   if (item == NULL) return false;
227489928cc5SHong Zhang 
227589928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_Object;
227689928cc5SHong Zhang }
227789928cc5SHong Zhang 
cJSON_IsRaw(const cJSON * const item)227889928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON *const item)
227989928cc5SHong Zhang {
2280ac530a7eSPierre Jolivet   if (item == NULL) return false;
228189928cc5SHong Zhang 
228289928cc5SHong Zhang   return (item->type & 0xFF) == cJSON_Raw;
228389928cc5SHong Zhang }
228489928cc5SHong Zhang 
cJSON_Compare(const cJSON * const a,const cJSON * const b,const cJSON_bool case_sensitive)228589928cc5SHong Zhang CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON *const a, const cJSON *const b, const cJSON_bool case_sensitive)
228689928cc5SHong Zhang {
2287ac530a7eSPierre Jolivet   if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) return false;
228889928cc5SHong Zhang 
228989928cc5SHong Zhang   /* check if type is valid */
229089928cc5SHong Zhang   switch (a->type & 0xFF) {
229189928cc5SHong Zhang   case cJSON_False:
229289928cc5SHong Zhang   case cJSON_True:
229389928cc5SHong Zhang   case cJSON_NULL:
229489928cc5SHong Zhang   case cJSON_Number:
229589928cc5SHong Zhang   case cJSON_String:
229689928cc5SHong Zhang   case cJSON_Raw:
229789928cc5SHong Zhang   case cJSON_Array:
229889928cc5SHong Zhang   case cJSON_Object:
229989928cc5SHong Zhang     break;
230089928cc5SHong Zhang 
230189928cc5SHong Zhang   default:
230289928cc5SHong Zhang     return false;
230389928cc5SHong Zhang   }
230489928cc5SHong Zhang 
230589928cc5SHong Zhang   /* identical objects are equal */
2306ac530a7eSPierre Jolivet   if (a == b) return true;
230789928cc5SHong Zhang 
230889928cc5SHong Zhang   switch (a->type & 0xFF) {
230989928cc5SHong Zhang   /* in these cases and equal type is enough */
231089928cc5SHong Zhang   case cJSON_False:
231189928cc5SHong Zhang   case cJSON_True:
231289928cc5SHong Zhang   case cJSON_NULL:
231389928cc5SHong Zhang     return true;
231489928cc5SHong Zhang 
231589928cc5SHong Zhang   case cJSON_Number:
2316ac530a7eSPierre Jolivet     if (compare_double(a->valuedouble, b->valuedouble)) return true;
231789928cc5SHong Zhang     return false;
231889928cc5SHong Zhang 
231989928cc5SHong Zhang   case cJSON_String:
232089928cc5SHong Zhang   case cJSON_Raw:
2321ac530a7eSPierre Jolivet     if ((a->valuestring == NULL) || (b->valuestring == NULL)) return false;
2322ac530a7eSPierre Jolivet     if (strcmp(a->valuestring, b->valuestring) == 0) return true;
232389928cc5SHong Zhang 
232489928cc5SHong Zhang     return false;
232589928cc5SHong Zhang 
232689928cc5SHong Zhang   case cJSON_Array: {
232789928cc5SHong Zhang     cJSON *a_element = a->child;
232889928cc5SHong Zhang     cJSON *b_element = b->child;
232989928cc5SHong Zhang 
233089928cc5SHong Zhang     for (; (a_element != NULL) && (b_element != NULL);) {
2331ac530a7eSPierre Jolivet       if (!cJSON_Compare(a_element, b_element, case_sensitive)) return false;
233289928cc5SHong Zhang 
233389928cc5SHong Zhang       a_element = a_element->next;
233489928cc5SHong Zhang       b_element = b_element->next;
233589928cc5SHong Zhang     }
233689928cc5SHong Zhang 
233789928cc5SHong Zhang     /* one of the arrays is longer than the other */
2338ac530a7eSPierre Jolivet     if (a_element != b_element) return false;
233989928cc5SHong Zhang 
234089928cc5SHong Zhang     return true;
234189928cc5SHong Zhang   }
234289928cc5SHong Zhang 
234389928cc5SHong Zhang   case cJSON_Object: {
234489928cc5SHong Zhang     cJSON *a_element = NULL;
234589928cc5SHong Zhang     cJSON *b_element = NULL;
234689928cc5SHong Zhang     cJSON_ArrayForEach(a_element, a)
234789928cc5SHong Zhang     {
234889928cc5SHong Zhang       /* TODO This has O(n^2) runtime, which is horrible! */
234989928cc5SHong Zhang       b_element = get_object_item(b, a_element->string, case_sensitive);
2350ac530a7eSPierre Jolivet       if (b_element == NULL) return false;
235189928cc5SHong Zhang 
2352ac530a7eSPierre Jolivet       if (!cJSON_Compare(a_element, b_element, case_sensitive)) return false;
235389928cc5SHong Zhang     }
235489928cc5SHong Zhang 
235589928cc5SHong Zhang     /* doing this twice, once on a and b to prevent true comparison if a subset of b
235689928cc5SHong Zhang              * TODO: Do this the proper way, this is just a fix for now */
235789928cc5SHong Zhang     cJSON_ArrayForEach(b_element, b)
235889928cc5SHong Zhang     {
235989928cc5SHong Zhang       a_element = get_object_item(a, b_element->string, case_sensitive);
2360ac530a7eSPierre Jolivet       if (a_element == NULL) return false;
236189928cc5SHong Zhang 
2362ac530a7eSPierre Jolivet       if (!cJSON_Compare(b_element, a_element, case_sensitive)) return false;
236389928cc5SHong Zhang     }
236489928cc5SHong Zhang 
236589928cc5SHong Zhang     return true;
236689928cc5SHong Zhang   }
236789928cc5SHong Zhang 
236889928cc5SHong Zhang   default:
236989928cc5SHong Zhang     return false;
237089928cc5SHong Zhang   }
237189928cc5SHong Zhang }
237289928cc5SHong Zhang 
cJSON_malloc(size_t size)237389928cc5SHong Zhang CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
237489928cc5SHong Zhang {
237589928cc5SHong Zhang   return global_hooks.allocate(size);
237689928cc5SHong Zhang }
237789928cc5SHong Zhang 
cJSON_free(void * object)237889928cc5SHong Zhang CJSON_PUBLIC(void) cJSON_free(void *object)
237989928cc5SHong Zhang {
238089928cc5SHong Zhang   global_hooks.deallocate(object);
238189928cc5SHong Zhang }
2382