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