Mercurial > wgsdk
comparison src/json.c @ 12:dd427b7cc459 default tip
json: replace with nxjson library
more lightweight, reduces the binary size by about 40 kb
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Fri, 15 Mar 2024 20:46:18 -0400 |
parents | e6a594f16403 |
children |
comparison
equal
deleted
inserted
replaced
11:e6a594f16403 | 12:dd427b7cc459 |
---|---|
1 /* | 1 /* |
2 Copyright (c) 2009-2017 Dave Gamble and cJSON contributors | 2 * Copyright (c) 2013 Yaroslav Stavnichiy <yarosla@gmail.com> |
3 | 3 * |
4 Permission is hereby granted, free of charge, to any person obtaining a copy | 4 * This file is part of NXJSON. |
5 of this software and associated documentation files (the "Software"), to deal | 5 * |
6 in the Software without restriction, including without limitation the rights | 6 * NXJSON is free software: you can redistribute it and/or modify |
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 7 * it under the terms of the GNU Lesser General Public License |
8 copies of the Software, and to permit persons to whom the Software is | 8 * as published by the Free Software Foundation, either version 3 |
9 furnished to do so, subject to the following conditions: | 9 * of the License, or (at your option) any later version. |
10 | 10 * |
11 The above copyright notice and this permission notice shall be included in | 11 * NXJSON is distributed in the hope that it will be useful, |
12 all copies or substantial portions of the Software. | 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 14 * GNU Lesser General Public License for more details. |
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 15 * |
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 16 * You should have received a copy of the GNU Lesser General Public |
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 17 * License along with NXJSON. If not, see <http://www.gnu.org/licenses/>. |
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 18 */ |
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 19 |
20 THE SOFTWARE. | 20 // this file can be #included in your code |
21 */ | 21 #ifndef NXJSON_C |
22 | 22 #define NXJSON_C |
23 /* cJSON */ | 23 |
24 /* JSON parser in C. */ | 24 #ifdef __cplusplus |
25 | 25 extern "C" { |
26 /* disable warnings about old C89 functions in MSVC */ | |
27 #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) | |
28 #define _CRT_SECURE_NO_DEPRECATE | |
29 #endif | 26 #endif |
30 | 27 |
31 #ifdef __GNUC__ | 28 |
32 #pragma GCC visibility push(default) | 29 #include <stdlib.h> |
30 #include <stdio.h> | |
31 #include <string.h> | |
32 #include <assert.h> | |
33 #include <errno.h> | |
34 | |
35 #include "json.h" | |
36 | |
37 // redefine NX_JSON_CALLOC & NX_JSON_FREE to use custom allocator | |
38 #ifndef NX_JSON_CALLOC | |
39 #define NX_JSON_CALLOC() calloc(1, sizeof(nx_json)) | |
40 #define NX_JSON_FREE(json) free((void*)(json)) | |
33 #endif | 41 #endif |
34 #if defined(_MSC_VER) | 42 |
35 #pragma warning (push) | 43 // redefine NX_JSON_REPORT_ERROR to use custom error reporting |
36 /* disable warning about single line comments in system headers */ | 44 #ifndef NX_JSON_REPORT_ERROR |
37 #pragma warning (disable : 4001) | 45 #define NX_JSON_REPORT_ERROR(msg, p) fprintf(stderr, "NXJSON PARSE ERROR (%d): " msg " at %s\n", __LINE__, p) |
38 #endif | 46 #endif |
39 | 47 |
40 #include <string.h> | 48 #define IS_WHITESPACE(c) ((unsigned char)(c)<=(unsigned char)' ') |
41 #include <stdio.h> | 49 |
42 #include <math.h> | 50 static nx_json *create_json(nx_json_type type, const char *key, nx_json *parent) { |
43 #include <stdlib.h> | 51 nx_json *js = NX_JSON_CALLOC(); |
44 #include <limits.h> | 52 assert(js); |
45 #include <ctype.h> | 53 js->type = type; |
46 #include <float.h> | 54 js->key = key; |
47 | 55 if (!parent->children.last) { |
48 #ifdef ENABLE_LOCALES | 56 parent->children.first = parent->children.last = js; |
49 #include <locale.h> | 57 } else { |
58 parent->children.last->next = js; | |
59 parent->children.last = js; | |
60 } | |
61 parent->children.length++; | |
62 return js; | |
63 } | |
64 | |
65 void nx_json_free(const nx_json *js) { | |
66 if (!js) { | |
67 return; | |
68 } | |
69 if (js->type == NX_JSON_OBJECT || js->type == NX_JSON_ARRAY) { | |
70 nx_json *p = js->children.first; | |
71 nx_json *p1; | |
72 while (p) { | |
73 p1 = p->next; | |
74 nx_json_free (p); | |
75 p = p1; | |
76 } | |
77 } | |
78 NX_JSON_FREE(js); | |
79 } | |
80 | |
81 static int unicode_to_utf8(unsigned int codepoint, char *p, char **endp) { | |
82 // code from http://stackoverflow.com/a/4609989/697313 | |
83 if (codepoint < 0x80) *p++ = codepoint; | |
84 else if (codepoint < 0x800) *p++ = 192 + codepoint / 64, *p++ = 128 + codepoint % 64; | |
85 else if (codepoint - 0xd800u < 0x800) return 0; // surrogate must have been treated earlier | |
86 else if (codepoint < 0x10000) | |
87 *p++ = 224 + codepoint / 4096, *p++ = 128 + codepoint / 64 % 64, *p++ = 128 + codepoint % 64; | |
88 else if (codepoint < 0x110000) | |
89 *p++ = 240 + codepoint / 262144, *p++ = 128 + codepoint / 4096 % 64, *p++ = 128 + codepoint / 64 % 64, *p++ = | |
90 128 + codepoint % 64; | |
91 else return 0; // error | |
92 *endp = p; | |
93 return 1; | |
94 } | |
95 | |
96 nx_json_unicode_encoder nx_json_unicode_to_utf8 = unicode_to_utf8; | |
97 | |
98 static inline int hex_val(char c) { | |
99 if (c >= '0' && c <= '9') return c - '0'; | |
100 if (c >= 'a' && c <= 'f') return c - 'a' + 10; | |
101 if (c >= 'A' && c <= 'F') return c - 'A' + 10; | |
102 return -1; | |
103 } | |
104 | |
105 static char *unescape_string(char *s, char **end, nx_json_unicode_encoder encoder) { | |
106 char *p = s; | |
107 char *d = s; | |
108 char c; | |
109 while ((c = *p++)) { | |
110 if (c == '"') { | |
111 *d = '\0'; | |
112 *end = p; | |
113 return s; | |
114 } else if (c == '\\') { | |
115 switch (*p) { | |
116 case '\\': | |
117 case '/': | |
118 case '"': | |
119 *d++ = *p++; | |
120 break; | |
121 case 'b': | |
122 *d++ = '\b'; | |
123 p++; | |
124 break; | |
125 case 'f': | |
126 *d++ = '\f'; | |
127 p++; | |
128 break; | |
129 case 'n': | |
130 *d++ = '\n'; | |
131 p++; | |
132 break; | |
133 case 'r': | |
134 *d++ = '\r'; | |
135 p++; | |
136 break; | |
137 case 't': | |
138 *d++ = '\t'; | |
139 p++; | |
140 break; | |
141 case 'u': // unicode | |
142 if (!encoder) { | |
143 // leave untouched | |
144 *d++ = c; | |
145 break; | |
146 } | |
147 char *ps = p - 1; | |
148 int h1, h2, h3, h4; | |
149 if ((h1 = hex_val (p[1])) < 0 || (h2 = hex_val (p[2])) < 0 || (h3 = hex_val (p[3])) < 0 || | |
150 (h4 = hex_val (p[4])) < 0) { | |
151 NX_JSON_REPORT_ERROR("invalid unicode escape", p - 1); | |
152 return 0; | |
153 } | |
154 unsigned int codepoint = h1 << 12 | h2 << 8 | h3 << 4 | h4; | |
155 if ((codepoint & 0xfc00) == 0xd800) { // high surrogate; need one more unicode to succeed | |
156 p += 6; | |
157 if (p[-1] != '\\' || *p != 'u' || (h1 = hex_val (p[1])) < 0 || (h2 = hex_val (p[2])) < 0 || | |
158 (h3 = hex_val (p[3])) < 0 || (h4 = hex_val (p[4])) < 0) { | |
159 NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps); | |
160 return 0; | |
161 } | |
162 unsigned int codepoint2 = h1 << 12 | h2 << 8 | h3 << 4 | h4; | |
163 if ((codepoint2 & 0xfc00) != 0xdc00) { | |
164 NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps); | |
165 return 0; | |
166 } | |
167 codepoint = 0x10000 + ((codepoint - 0xd800) << 10) + (codepoint2 - 0xdc00); | |
168 } | |
169 if (!encoder (codepoint, d, &d)) { | |
170 NX_JSON_REPORT_ERROR("invalid codepoint", ps); | |
171 return 0; | |
172 } | |
173 p += 5; | |
174 break; | |
175 default: | |
176 // leave untouched | |
177 *d++ = c; | |
178 break; | |
179 } | |
180 } else { | |
181 *d++ = c; | |
182 } | |
183 } | |
184 NX_JSON_REPORT_ERROR("no closing quote for string", s); | |
185 return 0; | |
186 } | |
187 | |
188 static char *skip_block_comment(char *p) { | |
189 // assume p[-2]=='/' && p[-1]=='*' | |
190 char *ps = p - 2; | |
191 if (!*p) { | |
192 NX_JSON_REPORT_ERROR("endless comment", ps); | |
193 return 0; | |
194 } | |
195 REPEAT: | |
196 p = strchr (p + 1, '/'); | |
197 if (!p) { | |
198 NX_JSON_REPORT_ERROR("endless comment", ps); | |
199 return 0; | |
200 } | |
201 if (p[-1] != '*') goto REPEAT; | |
202 return p + 1; | |
203 } | |
204 | |
205 static char *parse_key(const char **key, char *p, nx_json_unicode_encoder encoder) { | |
206 // on '}' return with *p=='}' | |
207 char c; | |
208 while ((c = *p++)) { | |
209 if (c == '"') { | |
210 *key = unescape_string (p, &p, encoder); | |
211 if (!*key) return 0; // propagate error | |
212 while (*p && IS_WHITESPACE(*p)) p++; | |
213 if (*p == ':') return p + 1; | |
214 NX_JSON_REPORT_ERROR("unexpected chars", p); | |
215 return 0; | |
216 } else if (IS_WHITESPACE(c) || c == ',') { | |
217 // continue | |
218 } else if (c == '}') { | |
219 return p - 1; | |
220 } else if (c == '/') { | |
221 if (*p == '/') { // line comment | |
222 char *ps = p - 1; | |
223 p = strchr (p + 1, '\n'); | |
224 if (!p) { | |
225 NX_JSON_REPORT_ERROR("endless comment", ps); | |
226 return 0; // error | |
227 } | |
228 p++; | |
229 } else if (*p == '*') { // block comment | |
230 p = skip_block_comment (p + 1); | |
231 if (!p) return 0; | |
232 } else { | |
233 NX_JSON_REPORT_ERROR("unexpected chars", p - 1); | |
234 return 0; // error | |
235 } | |
236 } else { | |
237 NX_JSON_REPORT_ERROR("unexpected chars", p - 1); | |
238 return 0; // error | |
239 } | |
240 } | |
241 NX_JSON_REPORT_ERROR("unexpected chars", p - 1); | |
242 return 0; // error | |
243 } | |
244 | |
245 static char *parse_value(nx_json *parent, const char *key, char *p, nx_json_unicode_encoder encoder) { | |
246 nx_json *js; | |
247 while (1) { | |
248 switch (*p) { | |
249 case '\0': | |
250 NX_JSON_REPORT_ERROR("unexpected end of text", p); | |
251 return 0; // error | |
252 case ' ': | |
253 case '\t': | |
254 case '\n': | |
255 case '\r': | |
256 case ',': | |
257 // skip | |
258 p++; | |
259 break; | |
260 case '{': | |
261 js = create_json (NX_JSON_OBJECT, key, parent); | |
262 p++; | |
263 while (1) { | |
264 const char *new_key; | |
265 p = parse_key (&new_key, p, encoder); | |
266 if (!p) return 0; // error | |
267 if (*p == '}') return p + 1; // end of object | |
268 p = parse_value (js, new_key, p, encoder); | |
269 if (!p) return 0; // error | |
270 } | |
271 case '[': | |
272 js = create_json (NX_JSON_ARRAY, key, parent); | |
273 p++; | |
274 while (1) { | |
275 p = parse_value (js, 0, p, encoder); | |
276 if (!p) return 0; // error | |
277 if (*p == ']') return p + 1; // end of array | |
278 } | |
279 case ']': | |
280 return p; | |
281 case '"': | |
282 p++; | |
283 js = create_json (NX_JSON_STRING, key, parent); | |
284 js->text_value = unescape_string (p, &p, encoder); | |
285 if (!js->text_value) return 0; // propagate error | |
286 return p; | |
287 case '-': | |
288 case '0': | |
289 case '1': | |
290 case '2': | |
291 case '3': | |
292 case '4': | |
293 case '5': | |
294 case '6': | |
295 case '7': | |
296 case '8': | |
297 case '9': { | |
298 js = create_json (NX_JSON_INTEGER, key, parent); | |
299 char *pe; | |
300 if (*p == '-') { | |
301 js->num.s_value = (nxjson_s64) strtoll (p, &pe, 0); | |
302 } else { | |
303 js->num.u_value = (nxjson_u64) strtoull (p, &pe, 0); | |
304 } | |
305 if (pe == p || errno == ERANGE) { | |
306 NX_JSON_REPORT_ERROR("invalid number", p); | |
307 return 0; // error | |
308 } | |
309 if (*pe == '.' || *pe == 'e' || *pe == 'E') { // double value | |
310 js->type = NX_JSON_DOUBLE; | |
311 js->num.dbl_value = strtod (p, &pe); | |
312 if (pe == p || errno == ERANGE) { | |
313 NX_JSON_REPORT_ERROR("invalid number", p); | |
314 return 0; // error | |
315 } | |
316 } else { | |
317 if (*p == '-') { | |
318 js->num.dbl_value = js->num.s_value; | |
319 } else { | |
320 js->num.dbl_value = js->num.u_value; | |
321 } | |
322 } | |
323 return pe; | |
324 } | |
325 case 't': | |
326 if (!strncmp (p, "true", 4)) { | |
327 js = create_json (NX_JSON_BOOL, key, parent); | |
328 js->num.u_value = 1; | |
329 return p + 4; | |
330 } | |
331 NX_JSON_REPORT_ERROR("unexpected chars", p); | |
332 return 0; // error | |
333 case 'f': | |
334 if (!strncmp (p, "false", 5)) { | |
335 js = create_json (NX_JSON_BOOL, key, parent); | |
336 js->num.u_value = 0; | |
337 return p + 5; | |
338 } | |
339 NX_JSON_REPORT_ERROR("unexpected chars", p); | |
340 return 0; // error | |
341 case 'n': | |
342 if (!strncmp (p, "null", 4)) { | |
343 create_json (NX_JSON_NULL, key, parent); | |
344 return p + 4; | |
345 } | |
346 NX_JSON_REPORT_ERROR("unexpected chars", p); | |
347 return 0; // error | |
348 case '/': // comment | |
349 if (p[1] == '/') { // line comment | |
350 char *ps = p; | |
351 p = strchr (p + 2, '\n'); | |
352 if (!p) { | |
353 NX_JSON_REPORT_ERROR("endless comment", ps); | |
354 return 0; // error | |
355 } | |
356 p++; | |
357 } else if (p[1] == '*') { // block comment | |
358 p = skip_block_comment (p + 2); | |
359 if (!p) return 0; | |
360 } else { | |
361 NX_JSON_REPORT_ERROR("unexpected chars", p); | |
362 return 0; // error | |
363 } | |
364 break; | |
365 default: | |
366 NX_JSON_REPORT_ERROR("unexpected chars", p); | |
367 return 0; // error | |
368 } | |
369 } | |
370 } | |
371 | |
372 const nx_json *nx_json_parse_utf8(char *text) { | |
373 return nx_json_parse (text, unicode_to_utf8); | |
374 } | |
375 | |
376 const nx_json *nx_json_parse(char *text, nx_json_unicode_encoder encoder) { | |
377 nx_json js = {0}; | |
378 if (!parse_value (&js, 0, text, encoder)) { | |
379 if (js.children.first) nx_json_free (js.children.first); | |
380 return 0; | |
381 } | |
382 return js.children.first; | |
383 } | |
384 | |
385 const nx_json *nx_json_get(const nx_json *json, const char *key) { | |
386 nx_json *js; | |
387 for (js = json->children.first; js; js = js->next) { | |
388 if (js->key && !strcmp (js->key, key)) return js; | |
389 } | |
390 return NULL; | |
391 } | |
392 | |
393 const nx_json *nx_json_item(const nx_json *json, int idx) { | |
394 nx_json *js; | |
395 for (js = json->children.first; js; js = js->next) { | |
396 if (!idx--) return js; | |
397 } | |
398 return NULL; | |
399 } | |
400 | |
401 | |
402 #ifdef __cplusplus | |
403 } | |
50 #endif | 404 #endif |
51 | 405 |
52 #if defined(_MSC_VER) | 406 #endif /* NXJSON_C */ |
53 #pragma warning (pop) | |
54 #endif | |
55 #ifdef __GNUC__ | |
56 #pragma GCC visibility pop | |
57 #endif | |
58 | |
59 #include "json.h" | |
60 | |
61 /* define our own boolean type */ | |
62 #ifdef true | |
63 #undef true | |
64 #endif | |
65 #define true ((cJSON_bool)1) | |
66 | |
67 #ifdef false | |
68 #undef false | |
69 #endif | |
70 #define false ((cJSON_bool)0) | |
71 | |
72 /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ | |
73 #ifndef isinf | |
74 #define isinf(d) (isnan((d - d)) && !isnan(d)) | |
75 #endif | |
76 #ifndef isnan | |
77 #define isnan(d) (d != d) | |
78 #endif | |
79 | |
80 #ifndef NAN | |
81 #ifdef _WIN32 | |
82 #define NAN sqrt(-1.0) | |
83 #else | |
84 #define NAN 0.0/0.0 | |
85 #endif | |
86 #endif | |
87 | |
88 typedef struct { | |
89 const unsigned char *json; | |
90 size_t position; | |
91 } error; | |
92 static error global_error = { NULL, 0 }; | |
93 | |
94 CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) | |
95 { | |
96 return (const char*) (global_error.json + global_error.position); | |
97 } | |
98 | |
99 CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) | |
100 { | |
101 if (!cJSON_IsString(item)) | |
102 { | |
103 return NULL; | |
104 } | |
105 | |
106 return item->valuestring; | |
107 } | |
108 | |
109 CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) | |
110 { | |
111 if (!cJSON_IsNumber(item)) | |
112 { | |
113 return (double) NAN; | |
114 } | |
115 | |
116 return item->valuedouble; | |
117 } | |
118 | |
119 /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ | |
120 #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 17) | |
121 #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. | |
122 #endif | |
123 | |
124 CJSON_PUBLIC(const char*) cJSON_Version(void) | |
125 { | |
126 static char version[15]; | |
127 sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); | |
128 | |
129 return version; | |
130 } | |
131 | |
132 /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ | |
133 static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) | |
134 { | |
135 if ((string1 == NULL) || (string2 == NULL)) | |
136 { | |
137 return 1; | |
138 } | |
139 | |
140 if (string1 == string2) | |
141 { | |
142 return 0; | |
143 } | |
144 | |
145 for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) | |
146 { | |
147 if (*string1 == '\0') | |
148 { | |
149 return 0; | |
150 } | |
151 } | |
152 | |
153 return tolower(*string1) - tolower(*string2); | |
154 } | |
155 | |
156 typedef struct internal_hooks | |
157 { | |
158 void *(CJSON_CDECL *allocate)(size_t size); | |
159 void (CJSON_CDECL *deallocate)(void *pointer); | |
160 void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); | |
161 } internal_hooks; | |
162 | |
163 #if defined(_MSC_VER) | |
164 /* work around MSVC error C2322: '...' address of dllimport '...' is not static */ | |
165 static void * CJSON_CDECL internal_malloc(size_t size) | |
166 { | |
167 return malloc(size); | |
168 } | |
169 static void CJSON_CDECL internal_free(void *pointer) | |
170 { | |
171 free(pointer); | |
172 } | |
173 static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) | |
174 { | |
175 return realloc(pointer, size); | |
176 } | |
177 #else | |
178 #define internal_malloc malloc | |
179 #define internal_free free | |
180 #define internal_realloc realloc | |
181 #endif | |
182 | |
183 /* strlen of character literals resolved at compile time */ | |
184 #define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) | |
185 | |
186 static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; | |
187 | |
188 static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) | |
189 { | |
190 size_t length = 0; | |
191 unsigned char *copy = NULL; | |
192 | |
193 if (string == NULL) | |
194 { | |
195 return NULL; | |
196 } | |
197 | |
198 length = strlen((const char*)string) + sizeof(""); | |
199 copy = (unsigned char*)hooks->allocate(length); | |
200 if (copy == NULL) | |
201 { | |
202 return NULL; | |
203 } | |
204 memcpy(copy, string, length); | |
205 | |
206 return copy; | |
207 } | |
208 | |
209 CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) | |
210 { | |
211 if (hooks == NULL) | |
212 { | |
213 /* Reset hooks */ | |
214 global_hooks.allocate = malloc; | |
215 global_hooks.deallocate = free; | |
216 global_hooks.reallocate = realloc; | |
217 return; | |
218 } | |
219 | |
220 global_hooks.allocate = malloc; | |
221 if (hooks->malloc_fn != NULL) | |
222 { | |
223 global_hooks.allocate = hooks->malloc_fn; | |
224 } | |
225 | |
226 global_hooks.deallocate = free; | |
227 if (hooks->free_fn != NULL) | |
228 { | |
229 global_hooks.deallocate = hooks->free_fn; | |
230 } | |
231 | |
232 /* use realloc only if both free and malloc are used */ | |
233 global_hooks.reallocate = NULL; | |
234 if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) | |
235 { | |
236 global_hooks.reallocate = realloc; | |
237 } | |
238 } | |
239 | |
240 /* Internal constructor. */ | |
241 static cJSON *cJSON_New_Item(const internal_hooks * const hooks) | |
242 { | |
243 cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); | |
244 if (node) | |
245 { | |
246 memset(node, '\0', sizeof(cJSON)); | |
247 } | |
248 | |
249 return node; | |
250 } | |
251 | |
252 /* Delete a cJSON structure. */ | |
253 CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) | |
254 { | |
255 cJSON *next = NULL; | |
256 while (item != NULL) | |
257 { | |
258 next = item->next; | |
259 if (!(item->type & cJSON_IsReference) && (item->child != NULL)) | |
260 { | |
261 cJSON_Delete(item->child); | |
262 } | |
263 if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) | |
264 { | |
265 global_hooks.deallocate(item->valuestring); | |
266 } | |
267 if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) | |
268 { | |
269 global_hooks.deallocate(item->string); | |
270 } | |
271 global_hooks.deallocate(item); | |
272 item = next; | |
273 } | |
274 } | |
275 | |
276 /* get the decimal point character of the current locale */ | |
277 static unsigned char get_decimal_point(void) | |
278 { | |
279 #ifdef ENABLE_LOCALES | |
280 struct lconv *lconv = localeconv(); | |
281 return (unsigned char) lconv->decimal_point[0]; | |
282 #else | |
283 return '.'; | |
284 #endif | |
285 } | |
286 | |
287 typedef struct | |
288 { | |
289 const unsigned char *content; | |
290 size_t length; | |
291 size_t offset; | |
292 size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ | |
293 internal_hooks hooks; | |
294 } parse_buffer; | |
295 | |
296 /* check if the given size is left to read in a given parse buffer (starting with 1) */ | |
297 #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) | |
298 /* check if the buffer can be accessed at the given index (starting with 0) */ | |
299 #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) | |
300 #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) | |
301 /* get a pointer to the buffer at the position */ | |
302 #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) | |
303 | |
304 /* Parse the input text to generate a number, and populate the result into item. */ | |
305 static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) | |
306 { | |
307 double number = 0; | |
308 unsigned char *after_end = NULL; | |
309 unsigned char number_c_string[64]; | |
310 unsigned char decimal_point = get_decimal_point(); | |
311 size_t i = 0; | |
312 | |
313 if ((input_buffer == NULL) || (input_buffer->content == NULL)) | |
314 { | |
315 return false; | |
316 } | |
317 | |
318 /* copy the number into a temporary buffer and replace '.' with the decimal point | |
319 * of the current locale (for strtod) | |
320 * This also takes care of '\0' not necessarily being available for marking the end of the input */ | |
321 for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) | |
322 { | |
323 switch (buffer_at_offset(input_buffer)[i]) | |
324 { | |
325 case '0': | |
326 case '1': | |
327 case '2': | |
328 case '3': | |
329 case '4': | |
330 case '5': | |
331 case '6': | |
332 case '7': | |
333 case '8': | |
334 case '9': | |
335 case '+': | |
336 case '-': | |
337 case 'e': | |
338 case 'E': | |
339 number_c_string[i] = buffer_at_offset(input_buffer)[i]; | |
340 break; | |
341 | |
342 case '.': | |
343 number_c_string[i] = decimal_point; | |
344 break; | |
345 | |
346 default: | |
347 goto loop_end; | |
348 } | |
349 } | |
350 loop_end: | |
351 number_c_string[i] = '\0'; | |
352 | |
353 number = strtod((const char*)number_c_string, (char**)&after_end); | |
354 if (number_c_string == after_end) | |
355 { | |
356 return false; /* parse_error */ | |
357 } | |
358 | |
359 item->valuedouble = number; | |
360 | |
361 /* use saturation in case of overflow */ | |
362 if (number >= INT_MAX) | |
363 { | |
364 item->valueint = INT_MAX; | |
365 } | |
366 else if (number <= (double)INT_MIN) | |
367 { | |
368 item->valueint = INT_MIN; | |
369 } | |
370 else | |
371 { | |
372 item->valueint = (int)number; | |
373 } | |
374 | |
375 item->type = cJSON_Number; | |
376 | |
377 input_buffer->offset += (size_t)(after_end - number_c_string); | |
378 return true; | |
379 } | |
380 | |
381 /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ | |
382 CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) | |
383 { | |
384 if (number >= INT_MAX) | |
385 { | |
386 object->valueint = INT_MAX; | |
387 } | |
388 else if (number <= (double)INT_MIN) | |
389 { | |
390 object->valueint = INT_MIN; | |
391 } | |
392 else | |
393 { | |
394 object->valueint = (int)number; | |
395 } | |
396 | |
397 return object->valuedouble = number; | |
398 } | |
399 | |
400 CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) | |
401 { | |
402 char *copy = NULL; | |
403 /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ | |
404 if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) | |
405 { | |
406 return NULL; | |
407 } | |
408 /* return NULL if the object is corrupted */ | |
409 if (object->valuestring == NULL) | |
410 { | |
411 return NULL; | |
412 } | |
413 if (strlen(valuestring) <= strlen(object->valuestring)) | |
414 { | |
415 strcpy(object->valuestring, valuestring); | |
416 return object->valuestring; | |
417 } | |
418 copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); | |
419 if (copy == NULL) | |
420 { | |
421 return NULL; | |
422 } | |
423 if (object->valuestring != NULL) | |
424 { | |
425 cJSON_free(object->valuestring); | |
426 } | |
427 object->valuestring = copy; | |
428 | |
429 return copy; | |
430 } | |
431 | |
432 typedef struct | |
433 { | |
434 unsigned char *buffer; | |
435 size_t length; | |
436 size_t offset; | |
437 size_t depth; /* current nesting depth (for formatted printing) */ | |
438 cJSON_bool noalloc; | |
439 cJSON_bool format; /* is this print a formatted print */ | |
440 internal_hooks hooks; | |
441 } printbuffer; | |
442 | |
443 /* realloc printbuffer if necessary to have at least "needed" bytes more */ | |
444 static unsigned char* ensure(printbuffer * const p, size_t needed) | |
445 { | |
446 unsigned char *newbuffer = NULL; | |
447 size_t newsize = 0; | |
448 | |
449 if ((p == NULL) || (p->buffer == NULL)) | |
450 { | |
451 return NULL; | |
452 } | |
453 | |
454 if ((p->length > 0) && (p->offset >= p->length)) | |
455 { | |
456 /* make sure that offset is valid */ | |
457 return NULL; | |
458 } | |
459 | |
460 if (needed > INT_MAX) | |
461 { | |
462 /* sizes bigger than INT_MAX are currently not supported */ | |
463 return NULL; | |
464 } | |
465 | |
466 needed += p->offset + 1; | |
467 if (needed <= p->length) | |
468 { | |
469 return p->buffer + p->offset; | |
470 } | |
471 | |
472 if (p->noalloc) { | |
473 return NULL; | |
474 } | |
475 | |
476 /* calculate new buffer size */ | |
477 if (needed > (INT_MAX / 2)) | |
478 { | |
479 /* overflow of int, use INT_MAX if possible */ | |
480 if (needed <= INT_MAX) | |
481 { | |
482 newsize = INT_MAX; | |
483 } | |
484 else | |
485 { | |
486 return NULL; | |
487 } | |
488 } | |
489 else | |
490 { | |
491 newsize = needed * 2; | |
492 } | |
493 | |
494 if (p->hooks.reallocate != NULL) | |
495 { | |
496 /* reallocate with realloc if available */ | |
497 newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); | |
498 if (newbuffer == NULL) | |
499 { | |
500 p->hooks.deallocate(p->buffer); | |
501 p->length = 0; | |
502 p->buffer = NULL; | |
503 | |
504 return NULL; | |
505 } | |
506 } | |
507 else | |
508 { | |
509 /* otherwise reallocate manually */ | |
510 newbuffer = (unsigned char*)p->hooks.allocate(newsize); | |
511 if (!newbuffer) | |
512 { | |
513 p->hooks.deallocate(p->buffer); | |
514 p->length = 0; | |
515 p->buffer = NULL; | |
516 | |
517 return NULL; | |
518 } | |
519 | |
520 memcpy(newbuffer, p->buffer, p->offset + 1); | |
521 p->hooks.deallocate(p->buffer); | |
522 } | |
523 p->length = newsize; | |
524 p->buffer = newbuffer; | |
525 | |
526 return newbuffer + p->offset; | |
527 } | |
528 | |
529 /* calculate the new length of the string in a printbuffer and update the offset */ | |
530 static void update_offset(printbuffer * const buffer) | |
531 { | |
532 const unsigned char *buffer_pointer = NULL; | |
533 if ((buffer == NULL) || (buffer->buffer == NULL)) | |
534 { | |
535 return; | |
536 } | |
537 buffer_pointer = buffer->buffer + buffer->offset; | |
538 | |
539 buffer->offset += strlen((const char*)buffer_pointer); | |
540 } | |
541 | |
542 /* securely comparison of floating-point variables */ | |
543 static cJSON_bool compare_double(double a, double b) | |
544 { | |
545 double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); | |
546 return (fabs(a - b) <= maxVal * DBL_EPSILON); | |
547 } | |
548 | |
549 /* Render the number nicely from the given item into a string. */ | |
550 static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) | |
551 { | |
552 unsigned char *output_pointer = NULL; | |
553 double d = item->valuedouble; | |
554 int length = 0; | |
555 size_t i = 0; | |
556 unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ | |
557 unsigned char decimal_point = get_decimal_point(); | |
558 double test = 0.0; | |
559 | |
560 if (output_buffer == NULL) | |
561 { | |
562 return false; | |
563 } | |
564 | |
565 /* This checks for NaN and Infinity */ | |
566 if (isnan(d) || isinf(d)) | |
567 { | |
568 length = sprintf((char*)number_buffer, "null"); | |
569 } | |
570 else if(d == (double)item->valueint) | |
571 { | |
572 length = sprintf((char*)number_buffer, "%d", item->valueint); | |
573 } | |
574 else | |
575 { | |
576 /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ | |
577 length = sprintf((char*)number_buffer, "%1.15g", d); | |
578 | |
579 /* Check whether the original double can be recovered */ | |
580 if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) | |
581 { | |
582 /* If not, print with 17 decimal places of precision */ | |
583 length = sprintf((char*)number_buffer, "%1.17g", d); | |
584 } | |
585 } | |
586 | |
587 /* sprintf failed or buffer overrun occurred */ | |
588 if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) | |
589 { | |
590 return false; | |
591 } | |
592 | |
593 /* reserve appropriate space in the output */ | |
594 output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); | |
595 if (output_pointer == NULL) | |
596 { | |
597 return false; | |
598 } | |
599 | |
600 /* copy the printed number to the output and replace locale | |
601 * dependent decimal point with '.' */ | |
602 for (i = 0; i < ((size_t)length); i++) | |
603 { | |
604 if (number_buffer[i] == decimal_point) | |
605 { | |
606 output_pointer[i] = '.'; | |
607 continue; | |
608 } | |
609 | |
610 output_pointer[i] = number_buffer[i]; | |
611 } | |
612 output_pointer[i] = '\0'; | |
613 | |
614 output_buffer->offset += (size_t)length; | |
615 | |
616 return true; | |
617 } | |
618 | |
619 /* parse 4 digit hexadecimal number */ | |
620 static unsigned parse_hex4(const unsigned char * const input) | |
621 { | |
622 unsigned int h = 0; | |
623 size_t i = 0; | |
624 | |
625 for (i = 0; i < 4; i++) | |
626 { | |
627 /* parse digit */ | |
628 if ((input[i] >= '0') && (input[i] <= '9')) | |
629 { | |
630 h += (unsigned int) input[i] - '0'; | |
631 } | |
632 else if ((input[i] >= 'A') && (input[i] <= 'F')) | |
633 { | |
634 h += (unsigned int) 10 + input[i] - 'A'; | |
635 } | |
636 else if ((input[i] >= 'a') && (input[i] <= 'f')) | |
637 { | |
638 h += (unsigned int) 10 + input[i] - 'a'; | |
639 } | |
640 else /* invalid */ | |
641 { | |
642 return 0; | |
643 } | |
644 | |
645 if (i < 3) | |
646 { | |
647 /* shift left to make place for the next nibble */ | |
648 h = h << 4; | |
649 } | |
650 } | |
651 | |
652 return h; | |
653 } | |
654 | |
655 /* converts a UTF-16 literal to UTF-8 | |
656 * A literal can be one or two sequences of the form \uXXXX */ | |
657 static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) | |
658 { | |
659 long unsigned int codepoint = 0; | |
660 unsigned int first_code = 0; | |
661 const unsigned char *first_sequence = input_pointer; | |
662 unsigned char utf8_length = 0; | |
663 unsigned char utf8_position = 0; | |
664 unsigned char sequence_length = 0; | |
665 unsigned char first_byte_mark = 0; | |
666 | |
667 if ((input_end - first_sequence) < 6) | |
668 { | |
669 /* input ends unexpectedly */ | |
670 goto fail; | |
671 } | |
672 | |
673 /* get the first utf16 sequence */ | |
674 first_code = parse_hex4(first_sequence + 2); | |
675 | |
676 /* check that the code is valid */ | |
677 if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) | |
678 { | |
679 goto fail; | |
680 } | |
681 | |
682 /* UTF16 surrogate pair */ | |
683 if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) | |
684 { | |
685 const unsigned char *second_sequence = first_sequence + 6; | |
686 unsigned int second_code = 0; | |
687 sequence_length = 12; /* \uXXXX\uXXXX */ | |
688 | |
689 if ((input_end - second_sequence) < 6) | |
690 { | |
691 /* input ends unexpectedly */ | |
692 goto fail; | |
693 } | |
694 | |
695 if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) | |
696 { | |
697 /* missing second half of the surrogate pair */ | |
698 goto fail; | |
699 } | |
700 | |
701 /* get the second utf16 sequence */ | |
702 second_code = parse_hex4(second_sequence + 2); | |
703 /* check that the code is valid */ | |
704 if ((second_code < 0xDC00) || (second_code > 0xDFFF)) | |
705 { | |
706 /* invalid second half of the surrogate pair */ | |
707 goto fail; | |
708 } | |
709 | |
710 | |
711 /* calculate the unicode codepoint from the surrogate pair */ | |
712 codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); | |
713 } | |
714 else | |
715 { | |
716 sequence_length = 6; /* \uXXXX */ | |
717 codepoint = first_code; | |
718 } | |
719 | |
720 /* encode as UTF-8 | |
721 * takes at maximum 4 bytes to encode: | |
722 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ | |
723 if (codepoint < 0x80) | |
724 { | |
725 /* normal ascii, encoding 0xxxxxxx */ | |
726 utf8_length = 1; | |
727 } | |
728 else if (codepoint < 0x800) | |
729 { | |
730 /* two bytes, encoding 110xxxxx 10xxxxxx */ | |
731 utf8_length = 2; | |
732 first_byte_mark = 0xC0; /* 11000000 */ | |
733 } | |
734 else if (codepoint < 0x10000) | |
735 { | |
736 /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ | |
737 utf8_length = 3; | |
738 first_byte_mark = 0xE0; /* 11100000 */ | |
739 } | |
740 else if (codepoint <= 0x10FFFF) | |
741 { | |
742 /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ | |
743 utf8_length = 4; | |
744 first_byte_mark = 0xF0; /* 11110000 */ | |
745 } | |
746 else | |
747 { | |
748 /* invalid unicode codepoint */ | |
749 goto fail; | |
750 } | |
751 | |
752 /* encode as utf8 */ | |
753 for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) | |
754 { | |
755 /* 10xxxxxx */ | |
756 (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); | |
757 codepoint >>= 6; | |
758 } | |
759 /* encode first byte */ | |
760 if (utf8_length > 1) | |
761 { | |
762 (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); | |
763 } | |
764 else | |
765 { | |
766 (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); | |
767 } | |
768 | |
769 *output_pointer += utf8_length; | |
770 | |
771 return sequence_length; | |
772 | |
773 fail: | |
774 return 0; | |
775 } | |
776 | |
777 /* Parse the input text into an unescaped cinput, and populate item. */ | |
778 static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) | |
779 { | |
780 const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; | |
781 const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; | |
782 unsigned char *output_pointer = NULL; | |
783 unsigned char *output = NULL; | |
784 | |
785 /* not a string */ | |
786 if (buffer_at_offset(input_buffer)[0] != '\"') | |
787 { | |
788 goto fail; | |
789 } | |
790 | |
791 { | |
792 /* calculate approximate size of the output (overestimate) */ | |
793 size_t allocation_length = 0; | |
794 size_t skipped_bytes = 0; | |
795 while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) | |
796 { | |
797 /* is escape sequence */ | |
798 if (input_end[0] == '\\') | |
799 { | |
800 if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) | |
801 { | |
802 /* prevent buffer overflow when last input character is a backslash */ | |
803 goto fail; | |
804 } | |
805 skipped_bytes++; | |
806 input_end++; | |
807 } | |
808 input_end++; | |
809 } | |
810 if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) | |
811 { | |
812 goto fail; /* string ended unexpectedly */ | |
813 } | |
814 | |
815 /* This is at most how much we need for the output */ | |
816 allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; | |
817 output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); | |
818 if (output == NULL) | |
819 { | |
820 goto fail; /* allocation failure */ | |
821 } | |
822 } | |
823 | |
824 output_pointer = output; | |
825 /* loop through the string literal */ | |
826 while (input_pointer < input_end) | |
827 { | |
828 if (*input_pointer != '\\') | |
829 { | |
830 *output_pointer++ = *input_pointer++; | |
831 } | |
832 /* escape sequence */ | |
833 else | |
834 { | |
835 unsigned char sequence_length = 2; | |
836 if ((input_end - input_pointer) < 1) | |
837 { | |
838 goto fail; | |
839 } | |
840 | |
841 switch (input_pointer[1]) | |
842 { | |
843 case 'b': | |
844 *output_pointer++ = '\b'; | |
845 break; | |
846 case 'f': | |
847 *output_pointer++ = '\f'; | |
848 break; | |
849 case 'n': | |
850 *output_pointer++ = '\n'; | |
851 break; | |
852 case 'r': | |
853 *output_pointer++ = '\r'; | |
854 break; | |
855 case 't': | |
856 *output_pointer++ = '\t'; | |
857 break; | |
858 case '\"': | |
859 case '\\': | |
860 case '/': | |
861 *output_pointer++ = input_pointer[1]; | |
862 break; | |
863 | |
864 /* UTF-16 literal */ | |
865 case 'u': | |
866 sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); | |
867 if (sequence_length == 0) | |
868 { | |
869 /* failed to convert UTF16-literal to UTF-8 */ | |
870 goto fail; | |
871 } | |
872 break; | |
873 | |
874 default: | |
875 goto fail; | |
876 } | |
877 input_pointer += sequence_length; | |
878 } | |
879 } | |
880 | |
881 /* zero terminate the output */ | |
882 *output_pointer = '\0'; | |
883 | |
884 item->type = cJSON_String; | |
885 item->valuestring = (char*)output; | |
886 | |
887 input_buffer->offset = (size_t) (input_end - input_buffer->content); | |
888 input_buffer->offset++; | |
889 | |
890 return true; | |
891 | |
892 fail: | |
893 if (output != NULL) | |
894 { | |
895 input_buffer->hooks.deallocate(output); | |
896 } | |
897 | |
898 if (input_pointer != NULL) | |
899 { | |
900 input_buffer->offset = (size_t)(input_pointer - input_buffer->content); | |
901 } | |
902 | |
903 return false; | |
904 } | |
905 | |
906 /* Render the cstring provided to an escaped version that can be printed. */ | |
907 static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) | |
908 { | |
909 const unsigned char *input_pointer = NULL; | |
910 unsigned char *output = NULL; | |
911 unsigned char *output_pointer = NULL; | |
912 size_t output_length = 0; | |
913 /* numbers of additional characters needed for escaping */ | |
914 size_t escape_characters = 0; | |
915 | |
916 if (output_buffer == NULL) | |
917 { | |
918 return false; | |
919 } | |
920 | |
921 /* empty string */ | |
922 if (input == NULL) | |
923 { | |
924 output = ensure(output_buffer, sizeof("\"\"")); | |
925 if (output == NULL) | |
926 { | |
927 return false; | |
928 } | |
929 strcpy((char*)output, "\"\""); | |
930 | |
931 return true; | |
932 } | |
933 | |
934 /* set "flag" to 1 if something needs to be escaped */ | |
935 for (input_pointer = input; *input_pointer; input_pointer++) | |
936 { | |
937 switch (*input_pointer) | |
938 { | |
939 case '\"': | |
940 case '\\': | |
941 case '\b': | |
942 case '\f': | |
943 case '\n': | |
944 case '\r': | |
945 case '\t': | |
946 /* one character escape sequence */ | |
947 escape_characters++; | |
948 break; | |
949 default: | |
950 if (*input_pointer < 32) | |
951 { | |
952 /* UTF-16 escape sequence uXXXX */ | |
953 escape_characters += 5; | |
954 } | |
955 break; | |
956 } | |
957 } | |
958 output_length = (size_t)(input_pointer - input) + escape_characters; | |
959 | |
960 output = ensure(output_buffer, output_length + sizeof("\"\"")); | |
961 if (output == NULL) | |
962 { | |
963 return false; | |
964 } | |
965 | |
966 /* no characters have to be escaped */ | |
967 if (escape_characters == 0) | |
968 { | |
969 output[0] = '\"'; | |
970 memcpy(output + 1, input, output_length); | |
971 output[output_length + 1] = '\"'; | |
972 output[output_length + 2] = '\0'; | |
973 | |
974 return true; | |
975 } | |
976 | |
977 output[0] = '\"'; | |
978 output_pointer = output + 1; | |
979 /* copy the string */ | |
980 for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) | |
981 { | |
982 if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) | |
983 { | |
984 /* normal character, copy */ | |
985 *output_pointer = *input_pointer; | |
986 } | |
987 else | |
988 { | |
989 /* character needs to be escaped */ | |
990 *output_pointer++ = '\\'; | |
991 switch (*input_pointer) | |
992 { | |
993 case '\\': | |
994 *output_pointer = '\\'; | |
995 break; | |
996 case '\"': | |
997 *output_pointer = '\"'; | |
998 break; | |
999 case '\b': | |
1000 *output_pointer = 'b'; | |
1001 break; | |
1002 case '\f': | |
1003 *output_pointer = 'f'; | |
1004 break; | |
1005 case '\n': | |
1006 *output_pointer = 'n'; | |
1007 break; | |
1008 case '\r': | |
1009 *output_pointer = 'r'; | |
1010 break; | |
1011 case '\t': | |
1012 *output_pointer = 't'; | |
1013 break; | |
1014 default: | |
1015 /* escape and print as unicode codepoint */ | |
1016 sprintf((char*)output_pointer, "u%04x", *input_pointer); | |
1017 output_pointer += 4; | |
1018 break; | |
1019 } | |
1020 } | |
1021 } | |
1022 output[output_length + 1] = '\"'; | |
1023 output[output_length + 2] = '\0'; | |
1024 | |
1025 return true; | |
1026 } | |
1027 | |
1028 /* Invoke print_string_ptr (which is useful) on an item. */ | |
1029 static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) | |
1030 { | |
1031 return print_string_ptr((unsigned char*)item->valuestring, p); | |
1032 } | |
1033 | |
1034 /* Predeclare these prototypes. */ | |
1035 static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); | |
1036 static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); | |
1037 static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); | |
1038 static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); | |
1039 static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); | |
1040 static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); | |
1041 | |
1042 /* Utility to jump whitespace and cr/lf */ | |
1043 static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) | |
1044 { | |
1045 if ((buffer == NULL) || (buffer->content == NULL)) | |
1046 { | |
1047 return NULL; | |
1048 } | |
1049 | |
1050 if (cannot_access_at_index(buffer, 0)) | |
1051 { | |
1052 return buffer; | |
1053 } | |
1054 | |
1055 while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) | |
1056 { | |
1057 buffer->offset++; | |
1058 } | |
1059 | |
1060 if (buffer->offset == buffer->length) | |
1061 { | |
1062 buffer->offset--; | |
1063 } | |
1064 | |
1065 return buffer; | |
1066 } | |
1067 | |
1068 /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ | |
1069 static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) | |
1070 { | |
1071 if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) | |
1072 { | |
1073 return NULL; | |
1074 } | |
1075 | |
1076 if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) | |
1077 { | |
1078 buffer->offset += 3; | |
1079 } | |
1080 | |
1081 return buffer; | |
1082 } | |
1083 | |
1084 CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) | |
1085 { | |
1086 size_t buffer_length; | |
1087 | |
1088 if (NULL == value) | |
1089 { | |
1090 return NULL; | |
1091 } | |
1092 | |
1093 /* Adding null character size due to require_null_terminated. */ | |
1094 buffer_length = strlen(value) + sizeof(""); | |
1095 | |
1096 return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); | |
1097 } | |
1098 | |
1099 /* Parse an object - create a new root, and populate. */ | |
1100 CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) | |
1101 { | |
1102 parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; | |
1103 cJSON *item = NULL; | |
1104 | |
1105 /* reset error position */ | |
1106 global_error.json = NULL; | |
1107 global_error.position = 0; | |
1108 | |
1109 if (value == NULL || 0 == buffer_length) | |
1110 { | |
1111 goto fail; | |
1112 } | |
1113 | |
1114 buffer.content = (const unsigned char*)value; | |
1115 buffer.length = buffer_length; | |
1116 buffer.offset = 0; | |
1117 buffer.hooks = global_hooks; | |
1118 | |
1119 item = cJSON_New_Item(&global_hooks); | |
1120 if (item == NULL) /* memory fail */ | |
1121 { | |
1122 goto fail; | |
1123 } | |
1124 | |
1125 if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) | |
1126 { | |
1127 /* parse failure. ep is set. */ | |
1128 goto fail; | |
1129 } | |
1130 | |
1131 /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ | |
1132 if (require_null_terminated) | |
1133 { | |
1134 buffer_skip_whitespace(&buffer); | |
1135 if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') | |
1136 { | |
1137 goto fail; | |
1138 } | |
1139 } | |
1140 if (return_parse_end) | |
1141 { | |
1142 *return_parse_end = (const char*)buffer_at_offset(&buffer); | |
1143 } | |
1144 | |
1145 return item; | |
1146 | |
1147 fail: | |
1148 if (item != NULL) | |
1149 { | |
1150 cJSON_Delete(item); | |
1151 } | |
1152 | |
1153 if (value != NULL) | |
1154 { | |
1155 error local_error; | |
1156 local_error.json = (const unsigned char*)value; | |
1157 local_error.position = 0; | |
1158 | |
1159 if (buffer.offset < buffer.length) | |
1160 { | |
1161 local_error.position = buffer.offset; | |
1162 } | |
1163 else if (buffer.length > 0) | |
1164 { | |
1165 local_error.position = buffer.length - 1; | |
1166 } | |
1167 | |
1168 if (return_parse_end != NULL) | |
1169 { | |
1170 *return_parse_end = (const char*)local_error.json + local_error.position; | |
1171 } | |
1172 | |
1173 global_error = local_error; | |
1174 } | |
1175 | |
1176 return NULL; | |
1177 } | |
1178 | |
1179 /* Default options for cJSON_Parse */ | |
1180 CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) | |
1181 { | |
1182 return cJSON_ParseWithOpts(value, 0, 0); | |
1183 } | |
1184 | |
1185 CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) | |
1186 { | |
1187 return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); | |
1188 } | |
1189 | |
1190 #define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) | |
1191 | |
1192 static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) | |
1193 { | |
1194 static const size_t default_buffer_size = 256; | |
1195 printbuffer buffer[1]; | |
1196 unsigned char *printed = NULL; | |
1197 | |
1198 memset(buffer, 0, sizeof(buffer)); | |
1199 | |
1200 /* create buffer */ | |
1201 buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); | |
1202 buffer->length = default_buffer_size; | |
1203 buffer->format = format; | |
1204 buffer->hooks = *hooks; | |
1205 if (buffer->buffer == NULL) | |
1206 { | |
1207 goto fail; | |
1208 } | |
1209 | |
1210 /* print the value */ | |
1211 if (!print_value(item, buffer)) | |
1212 { | |
1213 goto fail; | |
1214 } | |
1215 update_offset(buffer); | |
1216 | |
1217 /* check if reallocate is available */ | |
1218 if (hooks->reallocate != NULL) | |
1219 { | |
1220 printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); | |
1221 if (printed == NULL) { | |
1222 goto fail; | |
1223 } | |
1224 buffer->buffer = NULL; | |
1225 } | |
1226 else /* otherwise copy the JSON over to a new buffer */ | |
1227 { | |
1228 printed = (unsigned char*) hooks->allocate(buffer->offset + 1); | |
1229 if (printed == NULL) | |
1230 { | |
1231 goto fail; | |
1232 } | |
1233 memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); | |
1234 printed[buffer->offset] = '\0'; /* just to be sure */ | |
1235 | |
1236 /* free the buffer */ | |
1237 hooks->deallocate(buffer->buffer); | |
1238 } | |
1239 | |
1240 return printed; | |
1241 | |
1242 fail: | |
1243 if (buffer->buffer != NULL) | |
1244 { | |
1245 hooks->deallocate(buffer->buffer); | |
1246 } | |
1247 | |
1248 if (printed != NULL) | |
1249 { | |
1250 hooks->deallocate(printed); | |
1251 } | |
1252 | |
1253 return NULL; | |
1254 } | |
1255 | |
1256 /* Render a cJSON item/entity/structure to text. */ | |
1257 CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) | |
1258 { | |
1259 return (char*)print(item, true, &global_hooks); | |
1260 } | |
1261 | |
1262 CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) | |
1263 { | |
1264 return (char*)print(item, false, &global_hooks); | |
1265 } | |
1266 | |
1267 CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) | |
1268 { | |
1269 printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; | |
1270 | |
1271 if (prebuffer < 0) | |
1272 { | |
1273 return NULL; | |
1274 } | |
1275 | |
1276 p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); | |
1277 if (!p.buffer) | |
1278 { | |
1279 return NULL; | |
1280 } | |
1281 | |
1282 p.length = (size_t)prebuffer; | |
1283 p.offset = 0; | |
1284 p.noalloc = false; | |
1285 p.format = fmt; | |
1286 p.hooks = global_hooks; | |
1287 | |
1288 if (!print_value(item, &p)) | |
1289 { | |
1290 global_hooks.deallocate(p.buffer); | |
1291 return NULL; | |
1292 } | |
1293 | |
1294 return (char*)p.buffer; | |
1295 } | |
1296 | |
1297 CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) | |
1298 { | |
1299 printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; | |
1300 | |
1301 if ((length < 0) || (buffer == NULL)) | |
1302 { | |
1303 return false; | |
1304 } | |
1305 | |
1306 p.buffer = (unsigned char*)buffer; | |
1307 p.length = (size_t)length; | |
1308 p.offset = 0; | |
1309 p.noalloc = true; | |
1310 p.format = format; | |
1311 p.hooks = global_hooks; | |
1312 | |
1313 return print_value(item, &p); | |
1314 } | |
1315 | |
1316 /* Parser core - when encountering text, process appropriately. */ | |
1317 static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) | |
1318 { | |
1319 if ((input_buffer == NULL) || (input_buffer->content == NULL)) | |
1320 { | |
1321 return false; /* no input */ | |
1322 } | |
1323 | |
1324 /* parse the different types of values */ | |
1325 /* null */ | |
1326 if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) | |
1327 { | |
1328 item->type = cJSON_NULL; | |
1329 input_buffer->offset += 4; | |
1330 return true; | |
1331 } | |
1332 /* false */ | |
1333 if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) | |
1334 { | |
1335 item->type = cJSON_False; | |
1336 input_buffer->offset += 5; | |
1337 return true; | |
1338 } | |
1339 /* true */ | |
1340 if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) | |
1341 { | |
1342 item->type = cJSON_True; | |
1343 item->valueint = 1; | |
1344 input_buffer->offset += 4; | |
1345 return true; | |
1346 } | |
1347 /* string */ | |
1348 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) | |
1349 { | |
1350 return parse_string(item, input_buffer); | |
1351 } | |
1352 /* number */ | |
1353 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')))) | |
1354 { | |
1355 return parse_number(item, input_buffer); | |
1356 } | |
1357 /* array */ | |
1358 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) | |
1359 { | |
1360 return parse_array(item, input_buffer); | |
1361 } | |
1362 /* object */ | |
1363 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) | |
1364 { | |
1365 return parse_object(item, input_buffer); | |
1366 } | |
1367 | |
1368 return false; | |
1369 } | |
1370 | |
1371 /* Render a value to text. */ | |
1372 static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) | |
1373 { | |
1374 unsigned char *output = NULL; | |
1375 | |
1376 if ((item == NULL) || (output_buffer == NULL)) | |
1377 { | |
1378 return false; | |
1379 } | |
1380 | |
1381 switch ((item->type) & 0xFF) | |
1382 { | |
1383 case cJSON_NULL: | |
1384 output = ensure(output_buffer, 5); | |
1385 if (output == NULL) | |
1386 { | |
1387 return false; | |
1388 } | |
1389 strcpy((char*)output, "null"); | |
1390 return true; | |
1391 | |
1392 case cJSON_False: | |
1393 output = ensure(output_buffer, 6); | |
1394 if (output == NULL) | |
1395 { | |
1396 return false; | |
1397 } | |
1398 strcpy((char*)output, "false"); | |
1399 return true; | |
1400 | |
1401 case cJSON_True: | |
1402 output = ensure(output_buffer, 5); | |
1403 if (output == NULL) | |
1404 { | |
1405 return false; | |
1406 } | |
1407 strcpy((char*)output, "true"); | |
1408 return true; | |
1409 | |
1410 case cJSON_Number: | |
1411 return print_number(item, output_buffer); | |
1412 | |
1413 case cJSON_Raw: | |
1414 { | |
1415 size_t raw_length = 0; | |
1416 if (item->valuestring == NULL) | |
1417 { | |
1418 return false; | |
1419 } | |
1420 | |
1421 raw_length = strlen(item->valuestring) + sizeof(""); | |
1422 output = ensure(output_buffer, raw_length); | |
1423 if (output == NULL) | |
1424 { | |
1425 return false; | |
1426 } | |
1427 memcpy(output, item->valuestring, raw_length); | |
1428 return true; | |
1429 } | |
1430 | |
1431 case cJSON_String: | |
1432 return print_string(item, output_buffer); | |
1433 | |
1434 case cJSON_Array: | |
1435 return print_array(item, output_buffer); | |
1436 | |
1437 case cJSON_Object: | |
1438 return print_object(item, output_buffer); | |
1439 | |
1440 default: | |
1441 return false; | |
1442 } | |
1443 } | |
1444 | |
1445 /* Build an array from input text. */ | |
1446 static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) | |
1447 { | |
1448 cJSON *head = NULL; /* head of the linked list */ | |
1449 cJSON *current_item = NULL; | |
1450 | |
1451 if (input_buffer->depth >= CJSON_NESTING_LIMIT) | |
1452 { | |
1453 return false; /* to deeply nested */ | |
1454 } | |
1455 input_buffer->depth++; | |
1456 | |
1457 if (buffer_at_offset(input_buffer)[0] != '[') | |
1458 { | |
1459 /* not an array */ | |
1460 goto fail; | |
1461 } | |
1462 | |
1463 input_buffer->offset++; | |
1464 buffer_skip_whitespace(input_buffer); | |
1465 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) | |
1466 { | |
1467 /* empty array */ | |
1468 goto success; | |
1469 } | |
1470 | |
1471 /* check if we skipped to the end of the buffer */ | |
1472 if (cannot_access_at_index(input_buffer, 0)) | |
1473 { | |
1474 input_buffer->offset--; | |
1475 goto fail; | |
1476 } | |
1477 | |
1478 /* step back to character in front of the first element */ | |
1479 input_buffer->offset--; | |
1480 /* loop through the comma separated array elements */ | |
1481 do | |
1482 { | |
1483 /* allocate next item */ | |
1484 cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); | |
1485 if (new_item == NULL) | |
1486 { | |
1487 goto fail; /* allocation failure */ | |
1488 } | |
1489 | |
1490 /* attach next item to list */ | |
1491 if (head == NULL) | |
1492 { | |
1493 /* start the linked list */ | |
1494 current_item = head = new_item; | |
1495 } | |
1496 else | |
1497 { | |
1498 /* add to the end and advance */ | |
1499 current_item->next = new_item; | |
1500 new_item->prev = current_item; | |
1501 current_item = new_item; | |
1502 } | |
1503 | |
1504 /* parse next value */ | |
1505 input_buffer->offset++; | |
1506 buffer_skip_whitespace(input_buffer); | |
1507 if (!parse_value(current_item, input_buffer)) | |
1508 { | |
1509 goto fail; /* failed to parse value */ | |
1510 } | |
1511 buffer_skip_whitespace(input_buffer); | |
1512 } | |
1513 while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); | |
1514 | |
1515 if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') | |
1516 { | |
1517 goto fail; /* expected end of array */ | |
1518 } | |
1519 | |
1520 success: | |
1521 input_buffer->depth--; | |
1522 | |
1523 if (head != NULL) { | |
1524 head->prev = current_item; | |
1525 } | |
1526 | |
1527 item->type = cJSON_Array; | |
1528 item->child = head; | |
1529 | |
1530 input_buffer->offset++; | |
1531 | |
1532 return true; | |
1533 | |
1534 fail: | |
1535 if (head != NULL) | |
1536 { | |
1537 cJSON_Delete(head); | |
1538 } | |
1539 | |
1540 return false; | |
1541 } | |
1542 | |
1543 /* Render an array to text */ | |
1544 static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) | |
1545 { | |
1546 unsigned char *output_pointer = NULL; | |
1547 size_t length = 0; | |
1548 cJSON *current_element = item->child; | |
1549 | |
1550 if (output_buffer == NULL) | |
1551 { | |
1552 return false; | |
1553 } | |
1554 | |
1555 /* Compose the output array. */ | |
1556 /* opening square bracket */ | |
1557 output_pointer = ensure(output_buffer, 1); | |
1558 if (output_pointer == NULL) | |
1559 { | |
1560 return false; | |
1561 } | |
1562 | |
1563 *output_pointer = '['; | |
1564 output_buffer->offset++; | |
1565 output_buffer->depth++; | |
1566 | |
1567 while (current_element != NULL) | |
1568 { | |
1569 if (!print_value(current_element, output_buffer)) | |
1570 { | |
1571 return false; | |
1572 } | |
1573 update_offset(output_buffer); | |
1574 if (current_element->next) | |
1575 { | |
1576 length = (size_t) (output_buffer->format ? 2 : 1); | |
1577 output_pointer = ensure(output_buffer, length + 1); | |
1578 if (output_pointer == NULL) | |
1579 { | |
1580 return false; | |
1581 } | |
1582 *output_pointer++ = ','; | |
1583 if(output_buffer->format) | |
1584 { | |
1585 *output_pointer++ = ' '; | |
1586 } | |
1587 *output_pointer = '\0'; | |
1588 output_buffer->offset += length; | |
1589 } | |
1590 current_element = current_element->next; | |
1591 } | |
1592 | |
1593 output_pointer = ensure(output_buffer, 2); | |
1594 if (output_pointer == NULL) | |
1595 { | |
1596 return false; | |
1597 } | |
1598 *output_pointer++ = ']'; | |
1599 *output_pointer = '\0'; | |
1600 output_buffer->depth--; | |
1601 | |
1602 return true; | |
1603 } | |
1604 | |
1605 /* Build an object from the text. */ | |
1606 static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) | |
1607 { | |
1608 cJSON *head = NULL; /* linked list head */ | |
1609 cJSON *current_item = NULL; | |
1610 | |
1611 if (input_buffer->depth >= CJSON_NESTING_LIMIT) | |
1612 { | |
1613 return false; /* to deeply nested */ | |
1614 } | |
1615 input_buffer->depth++; | |
1616 | |
1617 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) | |
1618 { | |
1619 goto fail; /* not an object */ | |
1620 } | |
1621 | |
1622 input_buffer->offset++; | |
1623 buffer_skip_whitespace(input_buffer); | |
1624 if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) | |
1625 { | |
1626 goto success; /* empty object */ | |
1627 } | |
1628 | |
1629 /* check if we skipped to the end of the buffer */ | |
1630 if (cannot_access_at_index(input_buffer, 0)) | |
1631 { | |
1632 input_buffer->offset--; | |
1633 goto fail; | |
1634 } | |
1635 | |
1636 /* step back to character in front of the first element */ | |
1637 input_buffer->offset--; | |
1638 /* loop through the comma separated array elements */ | |
1639 do | |
1640 { | |
1641 /* allocate next item */ | |
1642 cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); | |
1643 if (new_item == NULL) | |
1644 { | |
1645 goto fail; /* allocation failure */ | |
1646 } | |
1647 | |
1648 /* attach next item to list */ | |
1649 if (head == NULL) | |
1650 { | |
1651 /* start the linked list */ | |
1652 current_item = head = new_item; | |
1653 } | |
1654 else | |
1655 { | |
1656 /* add to the end and advance */ | |
1657 current_item->next = new_item; | |
1658 new_item->prev = current_item; | |
1659 current_item = new_item; | |
1660 } | |
1661 | |
1662 /* parse the name of the child */ | |
1663 input_buffer->offset++; | |
1664 buffer_skip_whitespace(input_buffer); | |
1665 if (!parse_string(current_item, input_buffer)) | |
1666 { | |
1667 goto fail; /* failed to parse name */ | |
1668 } | |
1669 buffer_skip_whitespace(input_buffer); | |
1670 | |
1671 /* swap valuestring and string, because we parsed the name */ | |
1672 current_item->string = current_item->valuestring; | |
1673 current_item->valuestring = NULL; | |
1674 | |
1675 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) | |
1676 { | |
1677 goto fail; /* invalid object */ | |
1678 } | |
1679 | |
1680 /* parse the value */ | |
1681 input_buffer->offset++; | |
1682 buffer_skip_whitespace(input_buffer); | |
1683 if (!parse_value(current_item, input_buffer)) | |
1684 { | |
1685 goto fail; /* failed to parse value */ | |
1686 } | |
1687 buffer_skip_whitespace(input_buffer); | |
1688 } | |
1689 while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); | |
1690 | |
1691 if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) | |
1692 { | |
1693 goto fail; /* expected end of object */ | |
1694 } | |
1695 | |
1696 success: | |
1697 input_buffer->depth--; | |
1698 | |
1699 if (head != NULL) { | |
1700 head->prev = current_item; | |
1701 } | |
1702 | |
1703 item->type = cJSON_Object; | |
1704 item->child = head; | |
1705 | |
1706 input_buffer->offset++; | |
1707 return true; | |
1708 | |
1709 fail: | |
1710 if (head != NULL) | |
1711 { | |
1712 cJSON_Delete(head); | |
1713 } | |
1714 | |
1715 return false; | |
1716 } | |
1717 | |
1718 /* Render an object to text. */ | |
1719 static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) | |
1720 { | |
1721 unsigned char *output_pointer = NULL; | |
1722 size_t length = 0; | |
1723 cJSON *current_item = item->child; | |
1724 | |
1725 if (output_buffer == NULL) | |
1726 { | |
1727 return false; | |
1728 } | |
1729 | |
1730 /* Compose the output: */ | |
1731 length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ | |
1732 output_pointer = ensure(output_buffer, length + 1); | |
1733 if (output_pointer == NULL) | |
1734 { | |
1735 return false; | |
1736 } | |
1737 | |
1738 *output_pointer++ = '{'; | |
1739 output_buffer->depth++; | |
1740 if (output_buffer->format) | |
1741 { | |
1742 *output_pointer++ = '\n'; | |
1743 } | |
1744 output_buffer->offset += length; | |
1745 | |
1746 while (current_item) | |
1747 { | |
1748 if (output_buffer->format) | |
1749 { | |
1750 size_t i; | |
1751 output_pointer = ensure(output_buffer, output_buffer->depth); | |
1752 if (output_pointer == NULL) | |
1753 { | |
1754 return false; | |
1755 } | |
1756 for (i = 0; i < output_buffer->depth; i++) | |
1757 { | |
1758 *output_pointer++ = '\t'; | |
1759 } | |
1760 output_buffer->offset += output_buffer->depth; | |
1761 } | |
1762 | |
1763 /* print key */ | |
1764 if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) | |
1765 { | |
1766 return false; | |
1767 } | |
1768 update_offset(output_buffer); | |
1769 | |
1770 length = (size_t) (output_buffer->format ? 2 : 1); | |
1771 output_pointer = ensure(output_buffer, length); | |
1772 if (output_pointer == NULL) | |
1773 { | |
1774 return false; | |
1775 } | |
1776 *output_pointer++ = ':'; | |
1777 if (output_buffer->format) | |
1778 { | |
1779 *output_pointer++ = '\t'; | |
1780 } | |
1781 output_buffer->offset += length; | |
1782 | |
1783 /* print value */ | |
1784 if (!print_value(current_item, output_buffer)) | |
1785 { | |
1786 return false; | |
1787 } | |
1788 update_offset(output_buffer); | |
1789 | |
1790 /* print comma if not last */ | |
1791 length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); | |
1792 output_pointer = ensure(output_buffer, length + 1); | |
1793 if (output_pointer == NULL) | |
1794 { | |
1795 return false; | |
1796 } | |
1797 if (current_item->next) | |
1798 { | |
1799 *output_pointer++ = ','; | |
1800 } | |
1801 | |
1802 if (output_buffer->format) | |
1803 { | |
1804 *output_pointer++ = '\n'; | |
1805 } | |
1806 *output_pointer = '\0'; | |
1807 output_buffer->offset += length; | |
1808 | |
1809 current_item = current_item->next; | |
1810 } | |
1811 | |
1812 output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); | |
1813 if (output_pointer == NULL) | |
1814 { | |
1815 return false; | |
1816 } | |
1817 if (output_buffer->format) | |
1818 { | |
1819 size_t i; | |
1820 for (i = 0; i < (output_buffer->depth - 1); i++) | |
1821 { | |
1822 *output_pointer++ = '\t'; | |
1823 } | |
1824 } | |
1825 *output_pointer++ = '}'; | |
1826 *output_pointer = '\0'; | |
1827 output_buffer->depth--; | |
1828 | |
1829 return true; | |
1830 } | |
1831 | |
1832 /* Get Array size/item / object item. */ | |
1833 CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) | |
1834 { | |
1835 cJSON *child = NULL; | |
1836 size_t size = 0; | |
1837 | |
1838 if (array == NULL) | |
1839 { | |
1840 return 0; | |
1841 } | |
1842 | |
1843 child = array->child; | |
1844 | |
1845 while(child != NULL) | |
1846 { | |
1847 size++; | |
1848 child = child->next; | |
1849 } | |
1850 | |
1851 /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ | |
1852 | |
1853 return (int)size; | |
1854 } | |
1855 | |
1856 static cJSON* get_array_item(const cJSON *array, size_t index) | |
1857 { | |
1858 cJSON *current_child = NULL; | |
1859 | |
1860 if (array == NULL) | |
1861 { | |
1862 return NULL; | |
1863 } | |
1864 | |
1865 current_child = array->child; | |
1866 while ((current_child != NULL) && (index > 0)) | |
1867 { | |
1868 index--; | |
1869 current_child = current_child->next; | |
1870 } | |
1871 | |
1872 return current_child; | |
1873 } | |
1874 | |
1875 CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) | |
1876 { | |
1877 if (index < 0) | |
1878 { | |
1879 return NULL; | |
1880 } | |
1881 | |
1882 return get_array_item(array, (size_t)index); | |
1883 } | |
1884 | |
1885 static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) | |
1886 { | |
1887 cJSON *current_element = NULL; | |
1888 | |
1889 if ((object == NULL) || (name == NULL)) | |
1890 { | |
1891 return NULL; | |
1892 } | |
1893 | |
1894 current_element = object->child; | |
1895 if (case_sensitive) | |
1896 { | |
1897 while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) | |
1898 { | |
1899 current_element = current_element->next; | |
1900 } | |
1901 } | |
1902 else | |
1903 { | |
1904 while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) | |
1905 { | |
1906 current_element = current_element->next; | |
1907 } | |
1908 } | |
1909 | |
1910 if ((current_element == NULL) || (current_element->string == NULL)) { | |
1911 return NULL; | |
1912 } | |
1913 | |
1914 return current_element; | |
1915 } | |
1916 | |
1917 CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) | |
1918 { | |
1919 return get_object_item(object, string, false); | |
1920 } | |
1921 | |
1922 CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) | |
1923 { | |
1924 return get_object_item(object, string, true); | |
1925 } | |
1926 | |
1927 CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) | |
1928 { | |
1929 return cJSON_GetObjectItem(object, string) ? 1 : 0; | |
1930 } | |
1931 | |
1932 /* Utility for array list handling. */ | |
1933 static void suffix_object(cJSON *prev, cJSON *item) | |
1934 { | |
1935 prev->next = item; | |
1936 item->prev = prev; | |
1937 } | |
1938 | |
1939 /* Utility for handling references. */ | |
1940 static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) | |
1941 { | |
1942 cJSON *reference = NULL; | |
1943 if (item == NULL) | |
1944 { | |
1945 return NULL; | |
1946 } | |
1947 | |
1948 reference = cJSON_New_Item(hooks); | |
1949 if (reference == NULL) | |
1950 { | |
1951 return NULL; | |
1952 } | |
1953 | |
1954 memcpy(reference, item, sizeof(cJSON)); | |
1955 reference->string = NULL; | |
1956 reference->type |= cJSON_IsReference; | |
1957 reference->next = reference->prev = NULL; | |
1958 return reference; | |
1959 } | |
1960 | |
1961 static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) | |
1962 { | |
1963 cJSON *child = NULL; | |
1964 | |
1965 if ((item == NULL) || (array == NULL) || (array == item)) | |
1966 { | |
1967 return false; | |
1968 } | |
1969 | |
1970 child = array->child; | |
1971 /* | |
1972 * To find the last item in array quickly, we use prev in array | |
1973 */ | |
1974 if (child == NULL) | |
1975 { | |
1976 /* list is empty, start new one */ | |
1977 array->child = item; | |
1978 item->prev = item; | |
1979 item->next = NULL; | |
1980 } | |
1981 else | |
1982 { | |
1983 /* append to the end */ | |
1984 if (child->prev) | |
1985 { | |
1986 suffix_object(child->prev, item); | |
1987 array->child->prev = item; | |
1988 } | |
1989 } | |
1990 | |
1991 return true; | |
1992 } | |
1993 | |
1994 /* Add item to array/object. */ | |
1995 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) | |
1996 { | |
1997 return add_item_to_array(array, item); | |
1998 } | |
1999 | |
2000 #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) | |
2001 #pragma GCC diagnostic push | |
2002 #endif | |
2003 #ifdef __GNUC__ | |
2004 #pragma GCC diagnostic ignored "-Wcast-qual" | |
2005 #endif | |
2006 /* helper function to cast away const */ | |
2007 static void* cast_away_const(const void* string) | |
2008 { | |
2009 return (void*)string; | |
2010 } | |
2011 #if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) | |
2012 #pragma GCC diagnostic pop | |
2013 #endif | |
2014 | |
2015 | |
2016 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) | |
2017 { | |
2018 char *new_key = NULL; | |
2019 int new_type = cJSON_Invalid; | |
2020 | |
2021 if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) | |
2022 { | |
2023 return false; | |
2024 } | |
2025 | |
2026 if (constant_key) | |
2027 { | |
2028 new_key = (char*)cast_away_const(string); | |
2029 new_type = item->type | cJSON_StringIsConst; | |
2030 } | |
2031 else | |
2032 { | |
2033 new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); | |
2034 if (new_key == NULL) | |
2035 { | |
2036 return false; | |
2037 } | |
2038 | |
2039 new_type = item->type & ~cJSON_StringIsConst; | |
2040 } | |
2041 | |
2042 if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) | |
2043 { | |
2044 hooks->deallocate(item->string); | |
2045 } | |
2046 | |
2047 item->string = new_key; | |
2048 item->type = new_type; | |
2049 | |
2050 return add_item_to_array(object, item); | |
2051 } | |
2052 | |
2053 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) | |
2054 { | |
2055 return add_item_to_object(object, string, item, &global_hooks, false); | |
2056 } | |
2057 | |
2058 /* Add an item to an object with constant string as key */ | |
2059 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) | |
2060 { | |
2061 return add_item_to_object(object, string, item, &global_hooks, true); | |
2062 } | |
2063 | |
2064 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) | |
2065 { | |
2066 if (array == NULL) | |
2067 { | |
2068 return false; | |
2069 } | |
2070 | |
2071 return add_item_to_array(array, create_reference(item, &global_hooks)); | |
2072 } | |
2073 | |
2074 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) | |
2075 { | |
2076 if ((object == NULL) || (string == NULL)) | |
2077 { | |
2078 return false; | |
2079 } | |
2080 | |
2081 return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); | |
2082 } | |
2083 | |
2084 CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) | |
2085 { | |
2086 cJSON *null = cJSON_CreateNull(); | |
2087 if (add_item_to_object(object, name, null, &global_hooks, false)) | |
2088 { | |
2089 return null; | |
2090 } | |
2091 | |
2092 cJSON_Delete(null); | |
2093 return NULL; | |
2094 } | |
2095 | |
2096 CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) | |
2097 { | |
2098 cJSON *true_item = cJSON_CreateTrue(); | |
2099 if (add_item_to_object(object, name, true_item, &global_hooks, false)) | |
2100 { | |
2101 return true_item; | |
2102 } | |
2103 | |
2104 cJSON_Delete(true_item); | |
2105 return NULL; | |
2106 } | |
2107 | |
2108 CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) | |
2109 { | |
2110 cJSON *false_item = cJSON_CreateFalse(); | |
2111 if (add_item_to_object(object, name, false_item, &global_hooks, false)) | |
2112 { | |
2113 return false_item; | |
2114 } | |
2115 | |
2116 cJSON_Delete(false_item); | |
2117 return NULL; | |
2118 } | |
2119 | |
2120 CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) | |
2121 { | |
2122 cJSON *bool_item = cJSON_CreateBool(boolean); | |
2123 if (add_item_to_object(object, name, bool_item, &global_hooks, false)) | |
2124 { | |
2125 return bool_item; | |
2126 } | |
2127 | |
2128 cJSON_Delete(bool_item); | |
2129 return NULL; | |
2130 } | |
2131 | |
2132 CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) | |
2133 { | |
2134 cJSON *number_item = cJSON_CreateNumber(number); | |
2135 if (add_item_to_object(object, name, number_item, &global_hooks, false)) | |
2136 { | |
2137 return number_item; | |
2138 } | |
2139 | |
2140 cJSON_Delete(number_item); | |
2141 return NULL; | |
2142 } | |
2143 | |
2144 CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) | |
2145 { | |
2146 cJSON *string_item = cJSON_CreateString(string); | |
2147 if (add_item_to_object(object, name, string_item, &global_hooks, false)) | |
2148 { | |
2149 return string_item; | |
2150 } | |
2151 | |
2152 cJSON_Delete(string_item); | |
2153 return NULL; | |
2154 } | |
2155 | |
2156 CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) | |
2157 { | |
2158 cJSON *raw_item = cJSON_CreateRaw(raw); | |
2159 if (add_item_to_object(object, name, raw_item, &global_hooks, false)) | |
2160 { | |
2161 return raw_item; | |
2162 } | |
2163 | |
2164 cJSON_Delete(raw_item); | |
2165 return NULL; | |
2166 } | |
2167 | |
2168 CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) | |
2169 { | |
2170 cJSON *object_item = cJSON_CreateObject(); | |
2171 if (add_item_to_object(object, name, object_item, &global_hooks, false)) | |
2172 { | |
2173 return object_item; | |
2174 } | |
2175 | |
2176 cJSON_Delete(object_item); | |
2177 return NULL; | |
2178 } | |
2179 | |
2180 CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) | |
2181 { | |
2182 cJSON *array = cJSON_CreateArray(); | |
2183 if (add_item_to_object(object, name, array, &global_hooks, false)) | |
2184 { | |
2185 return array; | |
2186 } | |
2187 | |
2188 cJSON_Delete(array); | |
2189 return NULL; | |
2190 } | |
2191 | |
2192 CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) | |
2193 { | |
2194 if ((parent == NULL) || (item == NULL)) | |
2195 { | |
2196 return NULL; | |
2197 } | |
2198 | |
2199 if (item != parent->child) | |
2200 { | |
2201 /* not the first element */ | |
2202 item->prev->next = item->next; | |
2203 } | |
2204 if (item->next != NULL) | |
2205 { | |
2206 /* not the last element */ | |
2207 item->next->prev = item->prev; | |
2208 } | |
2209 | |
2210 if (item == parent->child) | |
2211 { | |
2212 /* first element */ | |
2213 parent->child = item->next; | |
2214 } | |
2215 else if (item->next == NULL) | |
2216 { | |
2217 /* last element */ | |
2218 parent->child->prev = item->prev; | |
2219 } | |
2220 | |
2221 /* make sure the detached item doesn't point anywhere anymore */ | |
2222 item->prev = NULL; | |
2223 item->next = NULL; | |
2224 | |
2225 return item; | |
2226 } | |
2227 | |
2228 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) | |
2229 { | |
2230 if (which < 0) | |
2231 { | |
2232 return NULL; | |
2233 } | |
2234 | |
2235 return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); | |
2236 } | |
2237 | |
2238 CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) | |
2239 { | |
2240 cJSON_Delete(cJSON_DetachItemFromArray(array, which)); | |
2241 } | |
2242 | |
2243 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) | |
2244 { | |
2245 cJSON *to_detach = cJSON_GetObjectItem(object, string); | |
2246 | |
2247 return cJSON_DetachItemViaPointer(object, to_detach); | |
2248 } | |
2249 | |
2250 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) | |
2251 { | |
2252 cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); | |
2253 | |
2254 return cJSON_DetachItemViaPointer(object, to_detach); | |
2255 } | |
2256 | |
2257 CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) | |
2258 { | |
2259 cJSON_Delete(cJSON_DetachItemFromObject(object, string)); | |
2260 } | |
2261 | |
2262 CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) | |
2263 { | |
2264 cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); | |
2265 } | |
2266 | |
2267 /* Replace array/object items with new ones. */ | |
2268 CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) | |
2269 { | |
2270 cJSON *after_inserted = NULL; | |
2271 | |
2272 if (which < 0 || newitem == NULL) | |
2273 { | |
2274 return false; | |
2275 } | |
2276 | |
2277 after_inserted = get_array_item(array, (size_t)which); | |
2278 if (after_inserted == NULL) | |
2279 { | |
2280 return add_item_to_array(array, newitem); | |
2281 } | |
2282 | |
2283 if (after_inserted != array->child && after_inserted->prev == NULL) { | |
2284 /* return false if after_inserted is a corrupted array item */ | |
2285 return false; | |
2286 } | |
2287 | |
2288 newitem->next = after_inserted; | |
2289 newitem->prev = after_inserted->prev; | |
2290 after_inserted->prev = newitem; | |
2291 if (after_inserted == array->child) | |
2292 { | |
2293 array->child = newitem; | |
2294 } | |
2295 else | |
2296 { | |
2297 newitem->prev->next = newitem; | |
2298 } | |
2299 return true; | |
2300 } | |
2301 | |
2302 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) | |
2303 { | |
2304 if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) | |
2305 { | |
2306 return false; | |
2307 } | |
2308 | |
2309 if (replacement == item) | |
2310 { | |
2311 return true; | |
2312 } | |
2313 | |
2314 replacement->next = item->next; | |
2315 replacement->prev = item->prev; | |
2316 | |
2317 if (replacement->next != NULL) | |
2318 { | |
2319 replacement->next->prev = replacement; | |
2320 } | |
2321 if (parent->child == item) | |
2322 { | |
2323 if (parent->child->prev == parent->child) | |
2324 { | |
2325 replacement->prev = replacement; | |
2326 } | |
2327 parent->child = replacement; | |
2328 } | |
2329 else | |
2330 { /* | |
2331 * To find the last item in array quickly, we use prev in array. | |
2332 * We can't modify the last item's next pointer where this item was the parent's child | |
2333 */ | |
2334 if (replacement->prev != NULL) | |
2335 { | |
2336 replacement->prev->next = replacement; | |
2337 } | |
2338 if (replacement->next == NULL) | |
2339 { | |
2340 parent->child->prev = replacement; | |
2341 } | |
2342 } | |
2343 | |
2344 item->next = NULL; | |
2345 item->prev = NULL; | |
2346 cJSON_Delete(item); | |
2347 | |
2348 return true; | |
2349 } | |
2350 | |
2351 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) | |
2352 { | |
2353 if (which < 0) | |
2354 { | |
2355 return false; | |
2356 } | |
2357 | |
2358 return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); | |
2359 } | |
2360 | |
2361 static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) | |
2362 { | |
2363 if ((replacement == NULL) || (string == NULL)) | |
2364 { | |
2365 return false; | |
2366 } | |
2367 | |
2368 /* replace the name in the replacement */ | |
2369 if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) | |
2370 { | |
2371 cJSON_free(replacement->string); | |
2372 } | |
2373 replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); | |
2374 if (replacement->string == NULL) | |
2375 { | |
2376 return false; | |
2377 } | |
2378 | |
2379 replacement->type &= ~cJSON_StringIsConst; | |
2380 | |
2381 return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); | |
2382 } | |
2383 | |
2384 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) | |
2385 { | |
2386 return replace_item_in_object(object, string, newitem, false); | |
2387 } | |
2388 | |
2389 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) | |
2390 { | |
2391 return replace_item_in_object(object, string, newitem, true); | |
2392 } | |
2393 | |
2394 /* Create basic types: */ | |
2395 CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) | |
2396 { | |
2397 cJSON *item = cJSON_New_Item(&global_hooks); | |
2398 if(item) | |
2399 { | |
2400 item->type = cJSON_NULL; | |
2401 } | |
2402 | |
2403 return item; | |
2404 } | |
2405 | |
2406 CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) | |
2407 { | |
2408 cJSON *item = cJSON_New_Item(&global_hooks); | |
2409 if(item) | |
2410 { | |
2411 item->type = cJSON_True; | |
2412 } | |
2413 | |
2414 return item; | |
2415 } | |
2416 | |
2417 CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) | |
2418 { | |
2419 cJSON *item = cJSON_New_Item(&global_hooks); | |
2420 if(item) | |
2421 { | |
2422 item->type = cJSON_False; | |
2423 } | |
2424 | |
2425 return item; | |
2426 } | |
2427 | |
2428 CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) | |
2429 { | |
2430 cJSON *item = cJSON_New_Item(&global_hooks); | |
2431 if(item) | |
2432 { | |
2433 item->type = boolean ? cJSON_True : cJSON_False; | |
2434 } | |
2435 | |
2436 return item; | |
2437 } | |
2438 | |
2439 CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) | |
2440 { | |
2441 cJSON *item = cJSON_New_Item(&global_hooks); | |
2442 if(item) | |
2443 { | |
2444 item->type = cJSON_Number; | |
2445 item->valuedouble = num; | |
2446 | |
2447 /* use saturation in case of overflow */ | |
2448 if (num >= INT_MAX) | |
2449 { | |
2450 item->valueint = INT_MAX; | |
2451 } | |
2452 else if (num <= (double)INT_MIN) | |
2453 { | |
2454 item->valueint = INT_MIN; | |
2455 } | |
2456 else | |
2457 { | |
2458 item->valueint = (int)num; | |
2459 } | |
2460 } | |
2461 | |
2462 return item; | |
2463 } | |
2464 | |
2465 CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) | |
2466 { | |
2467 cJSON *item = cJSON_New_Item(&global_hooks); | |
2468 if(item) | |
2469 { | |
2470 item->type = cJSON_String; | |
2471 item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); | |
2472 if(!item->valuestring) | |
2473 { | |
2474 cJSON_Delete(item); | |
2475 return NULL; | |
2476 } | |
2477 } | |
2478 | |
2479 return item; | |
2480 } | |
2481 | |
2482 CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) | |
2483 { | |
2484 cJSON *item = cJSON_New_Item(&global_hooks); | |
2485 if (item != NULL) | |
2486 { | |
2487 item->type = cJSON_String | cJSON_IsReference; | |
2488 item->valuestring = (char*)cast_away_const(string); | |
2489 } | |
2490 | |
2491 return item; | |
2492 } | |
2493 | |
2494 CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) | |
2495 { | |
2496 cJSON *item = cJSON_New_Item(&global_hooks); | |
2497 if (item != NULL) { | |
2498 item->type = cJSON_Object | cJSON_IsReference; | |
2499 item->child = (cJSON*)cast_away_const(child); | |
2500 } | |
2501 | |
2502 return item; | |
2503 } | |
2504 | |
2505 CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { | |
2506 cJSON *item = cJSON_New_Item(&global_hooks); | |
2507 if (item != NULL) { | |
2508 item->type = cJSON_Array | cJSON_IsReference; | |
2509 item->child = (cJSON*)cast_away_const(child); | |
2510 } | |
2511 | |
2512 return item; | |
2513 } | |
2514 | |
2515 CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) | |
2516 { | |
2517 cJSON *item = cJSON_New_Item(&global_hooks); | |
2518 if(item) | |
2519 { | |
2520 item->type = cJSON_Raw; | |
2521 item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); | |
2522 if(!item->valuestring) | |
2523 { | |
2524 cJSON_Delete(item); | |
2525 return NULL; | |
2526 } | |
2527 } | |
2528 | |
2529 return item; | |
2530 } | |
2531 | |
2532 CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) | |
2533 { | |
2534 cJSON *item = cJSON_New_Item(&global_hooks); | |
2535 if(item) | |
2536 { | |
2537 item->type=cJSON_Array; | |
2538 } | |
2539 | |
2540 return item; | |
2541 } | |
2542 | |
2543 CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) | |
2544 { | |
2545 cJSON *item = cJSON_New_Item(&global_hooks); | |
2546 if (item) | |
2547 { | |
2548 item->type = cJSON_Object; | |
2549 } | |
2550 | |
2551 return item; | |
2552 } | |
2553 | |
2554 /* Create Arrays: */ | |
2555 CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) | |
2556 { | |
2557 size_t i = 0; | |
2558 cJSON *n = NULL; | |
2559 cJSON *p = NULL; | |
2560 cJSON *a = NULL; | |
2561 | |
2562 if ((count < 0) || (numbers == NULL)) | |
2563 { | |
2564 return NULL; | |
2565 } | |
2566 | |
2567 a = cJSON_CreateArray(); | |
2568 | |
2569 for(i = 0; a && (i < (size_t)count); i++) | |
2570 { | |
2571 n = cJSON_CreateNumber(numbers[i]); | |
2572 if (!n) | |
2573 { | |
2574 cJSON_Delete(a); | |
2575 return NULL; | |
2576 } | |
2577 if(!i) | |
2578 { | |
2579 a->child = n; | |
2580 } | |
2581 else | |
2582 { | |
2583 suffix_object(p, n); | |
2584 } | |
2585 p = n; | |
2586 } | |
2587 | |
2588 if (a && a->child) { | |
2589 a->child->prev = n; | |
2590 } | |
2591 | |
2592 return a; | |
2593 } | |
2594 | |
2595 CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) | |
2596 { | |
2597 size_t i = 0; | |
2598 cJSON *n = NULL; | |
2599 cJSON *p = NULL; | |
2600 cJSON *a = NULL; | |
2601 | |
2602 if ((count < 0) || (numbers == NULL)) | |
2603 { | |
2604 return NULL; | |
2605 } | |
2606 | |
2607 a = cJSON_CreateArray(); | |
2608 | |
2609 for(i = 0; a && (i < (size_t)count); i++) | |
2610 { | |
2611 n = cJSON_CreateNumber((double)numbers[i]); | |
2612 if(!n) | |
2613 { | |
2614 cJSON_Delete(a); | |
2615 return NULL; | |
2616 } | |
2617 if(!i) | |
2618 { | |
2619 a->child = n; | |
2620 } | |
2621 else | |
2622 { | |
2623 suffix_object(p, n); | |
2624 } | |
2625 p = n; | |
2626 } | |
2627 | |
2628 if (a && a->child) { | |
2629 a->child->prev = n; | |
2630 } | |
2631 | |
2632 return a; | |
2633 } | |
2634 | |
2635 CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) | |
2636 { | |
2637 size_t i = 0; | |
2638 cJSON *n = NULL; | |
2639 cJSON *p = NULL; | |
2640 cJSON *a = NULL; | |
2641 | |
2642 if ((count < 0) || (numbers == NULL)) | |
2643 { | |
2644 return NULL; | |
2645 } | |
2646 | |
2647 a = cJSON_CreateArray(); | |
2648 | |
2649 for(i = 0; a && (i < (size_t)count); i++) | |
2650 { | |
2651 n = cJSON_CreateNumber(numbers[i]); | |
2652 if(!n) | |
2653 { | |
2654 cJSON_Delete(a); | |
2655 return NULL; | |
2656 } | |
2657 if(!i) | |
2658 { | |
2659 a->child = n; | |
2660 } | |
2661 else | |
2662 { | |
2663 suffix_object(p, n); | |
2664 } | |
2665 p = n; | |
2666 } | |
2667 | |
2668 if (a && a->child) { | |
2669 a->child->prev = n; | |
2670 } | |
2671 | |
2672 return a; | |
2673 } | |
2674 | |
2675 CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) | |
2676 { | |
2677 size_t i = 0; | |
2678 cJSON *n = NULL; | |
2679 cJSON *p = NULL; | |
2680 cJSON *a = NULL; | |
2681 | |
2682 if ((count < 0) || (strings == NULL)) | |
2683 { | |
2684 return NULL; | |
2685 } | |
2686 | |
2687 a = cJSON_CreateArray(); | |
2688 | |
2689 for (i = 0; a && (i < (size_t)count); i++) | |
2690 { | |
2691 n = cJSON_CreateString(strings[i]); | |
2692 if(!n) | |
2693 { | |
2694 cJSON_Delete(a); | |
2695 return NULL; | |
2696 } | |
2697 if(!i) | |
2698 { | |
2699 a->child = n; | |
2700 } | |
2701 else | |
2702 { | |
2703 suffix_object(p,n); | |
2704 } | |
2705 p = n; | |
2706 } | |
2707 | |
2708 if (a && a->child) { | |
2709 a->child->prev = n; | |
2710 } | |
2711 | |
2712 return a; | |
2713 } | |
2714 | |
2715 /* Duplication */ | |
2716 CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) | |
2717 { | |
2718 cJSON *newitem = NULL; | |
2719 cJSON *child = NULL; | |
2720 cJSON *next = NULL; | |
2721 cJSON *newchild = NULL; | |
2722 | |
2723 /* Bail on bad ptr */ | |
2724 if (!item) | |
2725 { | |
2726 goto fail; | |
2727 } | |
2728 /* Create new item */ | |
2729 newitem = cJSON_New_Item(&global_hooks); | |
2730 if (!newitem) | |
2731 { | |
2732 goto fail; | |
2733 } | |
2734 /* Copy over all vars */ | |
2735 newitem->type = item->type & (~cJSON_IsReference); | |
2736 newitem->valueint = item->valueint; | |
2737 newitem->valuedouble = item->valuedouble; | |
2738 if (item->valuestring) | |
2739 { | |
2740 newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); | |
2741 if (!newitem->valuestring) | |
2742 { | |
2743 goto fail; | |
2744 } | |
2745 } | |
2746 if (item->string) | |
2747 { | |
2748 newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); | |
2749 if (!newitem->string) | |
2750 { | |
2751 goto fail; | |
2752 } | |
2753 } | |
2754 /* If non-recursive, then we're done! */ | |
2755 if (!recurse) | |
2756 { | |
2757 return newitem; | |
2758 } | |
2759 /* Walk the ->next chain for the child. */ | |
2760 child = item->child; | |
2761 while (child != NULL) | |
2762 { | |
2763 newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ | |
2764 if (!newchild) | |
2765 { | |
2766 goto fail; | |
2767 } | |
2768 if (next != NULL) | |
2769 { | |
2770 /* If newitem->child already set, then crosswire ->prev and ->next and move on */ | |
2771 next->next = newchild; | |
2772 newchild->prev = next; | |
2773 next = newchild; | |
2774 } | |
2775 else | |
2776 { | |
2777 /* Set newitem->child and move to it */ | |
2778 newitem->child = newchild; | |
2779 next = newchild; | |
2780 } | |
2781 child = child->next; | |
2782 } | |
2783 if (newitem && newitem->child) | |
2784 { | |
2785 newitem->child->prev = newchild; | |
2786 } | |
2787 | |
2788 return newitem; | |
2789 | |
2790 fail: | |
2791 if (newitem != NULL) | |
2792 { | |
2793 cJSON_Delete(newitem); | |
2794 } | |
2795 | |
2796 return NULL; | |
2797 } | |
2798 | |
2799 static void skip_oneline_comment(char **input) | |
2800 { | |
2801 *input += static_strlen("//"); | |
2802 | |
2803 for (; (*input)[0] != '\0'; ++(*input)) | |
2804 { | |
2805 if ((*input)[0] == '\n') { | |
2806 *input += static_strlen("\n"); | |
2807 return; | |
2808 } | |
2809 } | |
2810 } | |
2811 | |
2812 static void skip_multiline_comment(char **input) | |
2813 { | |
2814 *input += static_strlen("/*"); | |
2815 | |
2816 for (; (*input)[0] != '\0'; ++(*input)) | |
2817 { | |
2818 if (((*input)[0] == '*') && ((*input)[1] == '/')) | |
2819 { | |
2820 *input += static_strlen("*/"); | |
2821 return; | |
2822 } | |
2823 } | |
2824 } | |
2825 | |
2826 static void minify_string(char **input, char **output) { | |
2827 (*output)[0] = (*input)[0]; | |
2828 *input += static_strlen("\""); | |
2829 *output += static_strlen("\""); | |
2830 | |
2831 | |
2832 for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { | |
2833 (*output)[0] = (*input)[0]; | |
2834 | |
2835 if ((*input)[0] == '\"') { | |
2836 (*output)[0] = '\"'; | |
2837 *input += static_strlen("\""); | |
2838 *output += static_strlen("\""); | |
2839 return; | |
2840 } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { | |
2841 (*output)[1] = (*input)[1]; | |
2842 *input += static_strlen("\""); | |
2843 *output += static_strlen("\""); | |
2844 } | |
2845 } | |
2846 } | |
2847 | |
2848 CJSON_PUBLIC(void) cJSON_Minify(char *json) | |
2849 { | |
2850 char *into = json; | |
2851 | |
2852 if (json == NULL) | |
2853 { | |
2854 return; | |
2855 } | |
2856 | |
2857 while (json[0] != '\0') | |
2858 { | |
2859 switch (json[0]) | |
2860 { | |
2861 case ' ': | |
2862 case '\t': | |
2863 case '\r': | |
2864 case '\n': | |
2865 json++; | |
2866 break; | |
2867 | |
2868 case '/': | |
2869 if (json[1] == '/') | |
2870 { | |
2871 skip_oneline_comment(&json); | |
2872 } | |
2873 else if (json[1] == '*') | |
2874 { | |
2875 skip_multiline_comment(&json); | |
2876 } else { | |
2877 json++; | |
2878 } | |
2879 break; | |
2880 | |
2881 case '\"': | |
2882 minify_string(&json, (char**)&into); | |
2883 break; | |
2884 | |
2885 default: | |
2886 into[0] = json[0]; | |
2887 json++; | |
2888 into++; | |
2889 } | |
2890 } | |
2891 | |
2892 /* and null-terminate. */ | |
2893 *into = '\0'; | |
2894 } | |
2895 | |
2896 CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) | |
2897 { | |
2898 if (item == NULL) | |
2899 { | |
2900 return false; | |
2901 } | |
2902 | |
2903 return (item->type & 0xFF) == cJSON_Invalid; | |
2904 } | |
2905 | |
2906 CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) | |
2907 { | |
2908 if (item == NULL) | |
2909 { | |
2910 return false; | |
2911 } | |
2912 | |
2913 return (item->type & 0xFF) == cJSON_False; | |
2914 } | |
2915 | |
2916 CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) | |
2917 { | |
2918 if (item == NULL) | |
2919 { | |
2920 return false; | |
2921 } | |
2922 | |
2923 return (item->type & 0xff) == cJSON_True; | |
2924 } | |
2925 | |
2926 | |
2927 CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) | |
2928 { | |
2929 if (item == NULL) | |
2930 { | |
2931 return false; | |
2932 } | |
2933 | |
2934 return (item->type & (cJSON_True | cJSON_False)) != 0; | |
2935 } | |
2936 CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) | |
2937 { | |
2938 if (item == NULL) | |
2939 { | |
2940 return false; | |
2941 } | |
2942 | |
2943 return (item->type & 0xFF) == cJSON_NULL; | |
2944 } | |
2945 | |
2946 CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) | |
2947 { | |
2948 if (item == NULL) | |
2949 { | |
2950 return false; | |
2951 } | |
2952 | |
2953 return (item->type & 0xFF) == cJSON_Number; | |
2954 } | |
2955 | |
2956 CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) | |
2957 { | |
2958 if (item == NULL) | |
2959 { | |
2960 return false; | |
2961 } | |
2962 | |
2963 return (item->type & 0xFF) == cJSON_String; | |
2964 } | |
2965 | |
2966 CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) | |
2967 { | |
2968 if (item == NULL) | |
2969 { | |
2970 return false; | |
2971 } | |
2972 | |
2973 return (item->type & 0xFF) == cJSON_Array; | |
2974 } | |
2975 | |
2976 CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) | |
2977 { | |
2978 if (item == NULL) | |
2979 { | |
2980 return false; | |
2981 } | |
2982 | |
2983 return (item->type & 0xFF) == cJSON_Object; | |
2984 } | |
2985 | |
2986 CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) | |
2987 { | |
2988 if (item == NULL) | |
2989 { | |
2990 return false; | |
2991 } | |
2992 | |
2993 return (item->type & 0xFF) == cJSON_Raw; | |
2994 } | |
2995 | |
2996 CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) | |
2997 { | |
2998 if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) | |
2999 { | |
3000 return false; | |
3001 } | |
3002 | |
3003 /* check if type is valid */ | |
3004 switch (a->type & 0xFF) | |
3005 { | |
3006 case cJSON_False: | |
3007 case cJSON_True: | |
3008 case cJSON_NULL: | |
3009 case cJSON_Number: | |
3010 case cJSON_String: | |
3011 case cJSON_Raw: | |
3012 case cJSON_Array: | |
3013 case cJSON_Object: | |
3014 break; | |
3015 | |
3016 default: | |
3017 return false; | |
3018 } | |
3019 | |
3020 /* identical objects are equal */ | |
3021 if (a == b) | |
3022 { | |
3023 return true; | |
3024 } | |
3025 | |
3026 switch (a->type & 0xFF) | |
3027 { | |
3028 /* in these cases and equal type is enough */ | |
3029 case cJSON_False: | |
3030 case cJSON_True: | |
3031 case cJSON_NULL: | |
3032 return true; | |
3033 | |
3034 case cJSON_Number: | |
3035 if (compare_double(a->valuedouble, b->valuedouble)) | |
3036 { | |
3037 return true; | |
3038 } | |
3039 return false; | |
3040 | |
3041 case cJSON_String: | |
3042 case cJSON_Raw: | |
3043 if ((a->valuestring == NULL) || (b->valuestring == NULL)) | |
3044 { | |
3045 return false; | |
3046 } | |
3047 if (strcmp(a->valuestring, b->valuestring) == 0) | |
3048 { | |
3049 return true; | |
3050 } | |
3051 | |
3052 return false; | |
3053 | |
3054 case cJSON_Array: | |
3055 { | |
3056 cJSON *a_element = a->child; | |
3057 cJSON *b_element = b->child; | |
3058 | |
3059 for (; (a_element != NULL) && (b_element != NULL);) | |
3060 { | |
3061 if (!cJSON_Compare(a_element, b_element, case_sensitive)) | |
3062 { | |
3063 return false; | |
3064 } | |
3065 | |
3066 a_element = a_element->next; | |
3067 b_element = b_element->next; | |
3068 } | |
3069 | |
3070 /* one of the arrays is longer than the other */ | |
3071 if (a_element != b_element) { | |
3072 return false; | |
3073 } | |
3074 | |
3075 return true; | |
3076 } | |
3077 | |
3078 case cJSON_Object: | |
3079 { | |
3080 cJSON *a_element = NULL; | |
3081 cJSON *b_element = NULL; | |
3082 cJSON_ArrayForEach(a_element, a) | |
3083 { | |
3084 /* TODO This has O(n^2) runtime, which is horrible! */ | |
3085 b_element = get_object_item(b, a_element->string, case_sensitive); | |
3086 if (b_element == NULL) | |
3087 { | |
3088 return false; | |
3089 } | |
3090 | |
3091 if (!cJSON_Compare(a_element, b_element, case_sensitive)) | |
3092 { | |
3093 return false; | |
3094 } | |
3095 } | |
3096 | |
3097 /* doing this twice, once on a and b to prevent true comparison if a subset of b | |
3098 * TODO: Do this the proper way, this is just a fix for now */ | |
3099 cJSON_ArrayForEach(b_element, b) | |
3100 { | |
3101 a_element = get_object_item(a, b_element->string, case_sensitive); | |
3102 if (a_element == NULL) | |
3103 { | |
3104 return false; | |
3105 } | |
3106 | |
3107 if (!cJSON_Compare(b_element, a_element, case_sensitive)) | |
3108 { | |
3109 return false; | |
3110 } | |
3111 } | |
3112 | |
3113 return true; | |
3114 } | |
3115 | |
3116 default: | |
3117 return false; | |
3118 } | |
3119 } | |
3120 | |
3121 CJSON_PUBLIC(void *) cJSON_malloc(size_t size) | |
3122 { | |
3123 return global_hooks.allocate(size); | |
3124 } | |
3125 | |
3126 CJSON_PUBLIC(void) cJSON_free(void *object) | |
3127 { | |
3128 global_hooks.deallocate(object); | |
3129 } |