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