2
Copyright (c) 2009 Dave Gamble
4
Permission is hereby granted, free of charge, to any person obtaining a copy
5
of this software and associated documentation files (the "Software"), to deal
6
in the Software without restriction, including without limitation the rights
7
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
copies of the Software, and to permit persons to whom the Software is
9
furnished to do so, subject to the following conditions:
11
The above copyright notice and this permission notice shall be included in
12
all copies or substantial portions of the Software.
14
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
/* JSON parser in C. */
35
static const char *ep;
37
const char *cJSON_GetErrorPtr() {return ep;}
39
static int cJSON_strcasecmp(const char *s1,const char *s2)
41
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
42
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
43
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
46
static void *(*cJSON_malloc)(size_t sz) = malloc;
47
static void (*cJSON_free)(void *ptr) = free;
49
static char* cJSON_strdup(const char* str)
54
len = strlen(str) + 1;
55
if (!(copy = (char*)cJSON_malloc(len))) return 0;
60
void cJSON_InitHooks(cJSON_Hooks* hooks)
62
if (!hooks) { /* Reset hooks */
63
cJSON_malloc = malloc;
68
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
69
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
72
/* Internal constructor. */
73
static cJSON *cJSON_New_Item()
75
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
76
if (node) memset(node,0,sizeof(cJSON));
80
/* Delete a cJSON structure. */
81
void cJSON_Delete(cJSON *c)
87
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
88
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
89
if (c->string) cJSON_free(c->string);
95
/* Parse the input text to generate a number, and populate the result into item. */
96
static const char *parse_number(cJSON *item,const char *num)
98
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
100
/* Could use sscanf for this? */
101
if (*num=='-') sign=-1,num++; /* Has sign? */
102
if (*num=='0') num++; /* is zero */
103
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
104
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
105
if (*num=='e' || *num=='E') /* Exponent? */
106
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
107
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
110
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
113
item->valueint=(int)n;
114
item->type=cJSON_Number;
118
/* Render the number nicely from the given item into a string. */
119
static char *print_number(cJSON *item)
122
double d=item->valuedouble;
123
if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
125
str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
126
if (str) sprintf(str,"%d",item->valueint);
130
str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
133
if (fabs(floor(d)-d)<=DBL_EPSILON) sprintf(str,"%.0f",d);
134
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
135
else sprintf(str,"%f",d);
141
/* Parse the input text into an unescaped cstring, and populate item. */
142
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
143
static const char *parse_string(cJSON *item,const char *str)
145
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
146
if (*str!='\"') {ep=str;return 0;} /* not a string! */
148
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
150
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
154
while (*ptr!='\"' && *ptr)
156
if (*ptr!='\\') *ptr2++=*ptr++;
162
case 'b': *ptr2++='\b'; break;
163
case 'f': *ptr2++='\f'; break;
164
case 'n': *ptr2++='\n'; break;
165
case 'r': *ptr2++='\r'; break;
166
case 't': *ptr2++='\t'; break;
167
case 'u': /* transcode utf16 to utf8. */
168
sscanf(ptr+1,"%4x",&uc);ptr+=4; /* get the unicode char. */
170
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // check for invalid.
172
if (uc>=0xD800 && uc<=0xDBFF) // UTF16 surrogate pairs.
174
if (ptr[1]!='\\' || ptr[2]!='u') break; // missing second-half of surrogate.
175
sscanf(ptr+3,"%4x",&uc2);ptr+=6;
176
if (uc2<0xDC00 || uc2>0xDFFF) break; // invalid second-half of surrogate.
177
uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);
180
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
183
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
184
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
185
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
186
case 1: *--ptr2 =(uc | firstByteMark[len]);
190
default: *ptr2++=*ptr; break;
196
if (*ptr=='\"') ptr++;
197
item->valuestring=out;
198
item->type=cJSON_String;
202
/* Render the cstring provided to an escaped version that can be printed. */
203
static char *print_string_ptr(const char *str)
205
const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
207
if (!str) return cJSON_strdup("");
208
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
210
out=(char*)cJSON_malloc(len+3);
217
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
221
switch (token=*ptr++)
223
case '\\': *ptr2++='\\'; break;
224
case '\"': *ptr2++='\"'; break;
225
case '\b': *ptr2++='b'; break;
226
case '\f': *ptr2++='f'; break;
227
case '\n': *ptr2++='n'; break;
228
case '\r': *ptr2++='r'; break;
229
case '\t': *ptr2++='t'; break;
230
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
234
*ptr2++='\"';*ptr2++=0;
237
/* Invote print_string_ptr (which is useful) on an item. */
238
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
240
/* Predeclare these prototypes. */
241
static const char *parse_value(cJSON *item,const char *value);
242
static char *print_value(cJSON *item,int depth,int fmt);
243
static const char *parse_array(cJSON *item,const char *value);
244
static char *print_array(cJSON *item,int depth,int fmt);
245
static const char *parse_object(cJSON *item,const char *value);
246
static char *print_object(cJSON *item,int depth,int fmt);
248
/* Utility to jump whitespace and cr/lf */
249
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
251
/* Parse an object - create a new root, and populate. */
252
cJSON *cJSON_Parse(const char *value)
254
cJSON *c=cJSON_New_Item();
256
if (!c) return 0; /* memory fail */
258
if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
262
/* Render a cJSON item/entity/structure to text. */
263
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
264
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
266
/* Parser core - when encountering text, process appropriately. */
267
static const char *parse_value(cJSON *item,const char *value)
269
if (!value) return 0; /* Fail on null. */
270
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
271
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
272
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
273
if (*value=='\"') { return parse_string(item,value); }
274
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
275
if (*value=='[') { return parse_array(item,value); }
276
if (*value=='{') { return parse_object(item,value); }
278
ep=value;return 0; /* failure. */
281
/* Render a value to text. */
282
static char *print_value(cJSON *item,int depth,int fmt)
286
switch ((item->type)&255)
288
case cJSON_NULL: out=cJSON_strdup("null"); break;
289
case cJSON_False: out=cJSON_strdup("false");break;
290
case cJSON_True: out=cJSON_strdup("true"); break;
291
case cJSON_Number: out=print_number(item);break;
292
case cJSON_String: out=print_string(item);break;
293
case cJSON_Array: out=print_array(item,depth,fmt);break;
294
case cJSON_Object: out=print_object(item,depth,fmt);break;
299
/* Build an array from input text. */
300
static const char *parse_array(cJSON *item,const char *value)
303
if (*value!='[') {ep=value;return 0;} /* not an array! */
305
item->type=cJSON_Array;
307
if (*value==']') return value+1; /* empty array. */
309
item->child=child=cJSON_New_Item();
310
if (!item->child) return 0; /* memory fail */
311
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
312
if (!value) return 0;
317
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
318
child->next=new_item;new_item->prev=child;child=new_item;
319
value=skip(parse_value(child,skip(value+1)));
320
if (!value) return 0; /* memory fail */
323
if (*value==']') return value+1; /* end of array */
324
ep=value;return 0; /* malformed. */
327
/* Render an array to text */
328
static char *print_array(cJSON *item,int depth,int fmt)
331
char *out=0,*ptr,*ret;int len=5;
332
cJSON *child=item->child;
333
int numentries=0,i=0,fail=0;
335
/* How many entries in the array? */
336
while (child) numentries++,child=child->next;
337
/* Allocate an array to hold the values for each */
338
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
339
if (!entries) return 0;
340
memset(entries,0,numentries*sizeof(char*));
341
/* Retrieve all the results: */
343
while (child && !fail)
345
ret=print_value(child,depth+1,fmt);
347
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
351
/* If we didn't fail, try to malloc the output string */
352
if (!fail) out=(char*)cJSON_malloc(len);
353
/* If that fails, we fail. */
356
/* Handle failure. */
359
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
364
/* Compose the output array. */
367
for (i=0;i<numentries;i++)
369
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
370
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
371
cJSON_free(entries[i]);
378
/* Build an object from the text. */
379
static const char *parse_object(cJSON *item,const char *value)
382
if (*value!='{') {ep=value;return 0;} /* not an object! */
384
item->type=cJSON_Object;
386
if (*value=='}') return value+1; /* empty array. */
388
item->child=child=cJSON_New_Item();
389
if (!item->child) return 0;
390
value=skip(parse_string(child,skip(value)));
391
if (!value) return 0;
392
child->string=child->valuestring;child->valuestring=0;
393
if (*value!=':') {ep=value;return 0;} /* fail! */
394
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
395
if (!value) return 0;
400
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
401
child->next=new_item;new_item->prev=child;child=new_item;
402
value=skip(parse_string(child,skip(value+1)));
403
if (!value) return 0;
404
child->string=child->valuestring;child->valuestring=0;
405
if (*value!=':') {ep=value;return 0;} /* fail! */
406
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
407
if (!value) return 0;
410
if (*value=='}') return value+1; /* end of array */
411
ep=value;return 0; /* malformed. */
414
/* Render an object to text. */
415
static char *print_object(cJSON *item,int depth,int fmt)
417
char **entries=0,**names=0;
418
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
419
cJSON *child=item->child;
420
int numentries=0,fail=0;
421
/* Count the number of entries. */
422
while (child) numentries++,child=child->next;
423
/* Allocate space for the names and the objects */
424
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
425
if (!entries) return 0;
426
names=(char**)cJSON_malloc(numentries*sizeof(char*));
427
if (!names) {cJSON_free(entries);return 0;}
428
memset(entries,0,sizeof(char*)*numentries);
429
memset(names,0,sizeof(char*)*numentries);
431
/* Collect all the results into our arrays: */
432
child=item->child;depth++;if (fmt) len+=depth;
435
names[i]=str=print_string_ptr(child->string);
436
entries[i++]=ret=print_value(child,depth,fmt);
437
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
441
/* Try to allocate the output string */
442
if (!fail) out=(char*)cJSON_malloc(len);
448
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
449
cJSON_free(names);cJSON_free(entries);
453
/* Compose the output: */
454
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
455
for (i=0;i<numentries;i++)
457
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
458
strcpy(ptr,names[i]);ptr+=strlen(names[i]);
459
*ptr++=':';if (fmt) *ptr++='\t';
460
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
461
if (i!=numentries-1) *ptr++=',';
462
if (fmt) *ptr++='\n';*ptr=0;
463
cJSON_free(names[i]);cJSON_free(entries[i]);
466
cJSON_free(names);cJSON_free(entries);
467
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
472
/* Get Array size/item / object item. */
473
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
474
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
475
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
477
/* Utility for array list handling. */
478
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
479
/* Utility for handling references. */
480
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
482
/* Add item to array/object. */
483
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
484
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
485
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
486
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
488
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
489
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
490
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
491
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
492
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
494
/* Replace array/object items with new ones. */
495
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
496
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
497
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
498
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
500
/* Create basic types: */
501
cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
502
cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
503
cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
504
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
505
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
506
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
507
cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
508
cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
511
cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
512
cJSON *cJSON_CreateFloatArray(float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
513
cJSON *cJSON_CreateDoubleArray(double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
514
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}