Skip to content

Commit 98a73ae

Browse files
committed
Merge pull request hmng#9 from metratec/master
reentrant cJSON parsing and various fixes: allow reliable cJSON stream p...
2 parents 36f7029 + 554a5a4 commit 98a73ae

File tree

3 files changed

+149
-75
lines changed

3 files changed

+149
-75
lines changed

include/cJSON.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
8181
/* Get item "string" from object. Case insensitive. */
8282
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
8383

84-
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
85-
extern const char *cJSON_GetErrorPtr();
86-
8784
/* These calls create a cJSON item of the appropriate type. */
8885
extern cJSON *cJSON_CreateNull();
8986
extern cJSON *cJSON_CreateTrue();

src/cJSON.c

Lines changed: 147 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@
3232
#include <ctype.h>
3333
#include "cJSON.h"
3434

35-
static const char *ep;
36-
//static const char *end_ptr;
37-
38-
const char *cJSON_GetErrorPtr() {return ep;}
39-
4035
static int cJSON_strcasecmp(const char *s1,const char *s2)
4136
{
4237
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
@@ -94,18 +89,41 @@ void cJSON_Delete(cJSON *c)
9489
}
9590

9691
/* Parse the input text to generate a number, and populate the result into item. */
97-
static const char *parse_number(cJSON *item,const char *num)
92+
static char **parse_number(cJSON *item, char **num)
9893
{
9994
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
10095

101-
/* Could use sscanf for this? */
102-
if (*num=='-') sign=-1,num++; /* Has sign? */
103-
if (*num=='0') num++; /* is zero */
104-
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
105-
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
106-
if (*num=='e' || *num=='E') /* Exponent? */
107-
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
108-
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
96+
if (**num=='-') { /* Has sign? */
97+
sign = -1;
98+
(*num)++;
99+
}
100+
if (**num=='0') /* is zero */
101+
(*num)++;
102+
if (**num>='1' && **num<='9') { /* Number? */
103+
do {
104+
n=(n*10.0)+(**num -'0');
105+
(*num)++;
106+
} while (**num>='0' && **num<='9');
107+
}
108+
if (**num=='.' && (*num)[1]>='0' && (*num)[1]<='9') { /* Fractional part? */
109+
(*num)++;
110+
do {
111+
n=(n*10.0)+(**num -'0');
112+
scale--;
113+
(*num)++;
114+
} while (**num>='0' && **num<='9');
115+
}
116+
if (**num=='e' || **num=='E') /* Exponent? */
117+
{ (*num)++;
118+
/* signed? */
119+
if (**num=='+')
120+
(*num)++;
121+
else if (**num=='-')
122+
signsubscale=-1,(*num)++;
123+
while (**num>='0' && **num<='9') { /* Number? */
124+
subscale=(subscale*10)+(**num - '0');
125+
(*num)++;
126+
}
109127
}
110128

111129
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
@@ -141,17 +159,17 @@ static char *print_number(cJSON *item)
141159

142160
/* Parse the input text into an unescaped cstring, and populate item. */
143161
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
144-
static const char *parse_string(cJSON *item,const char *str)
162+
static char **parse_string(cJSON *item, char **str)
145163
{
146-
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
147-
if (*str!='\"') {ep=str;return 0;} /* not a string! */
164+
char *ptr=*str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
165+
if (**str!='\"') return NULL; /* not a string! */
148166

149167
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
150168

151169
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
152170
if (!out) return 0;
153171

154-
ptr=str+1;ptr2=out;
172+
ptr=*str+1;ptr2=out;
155173
while (*ptr!='\"' && *ptr)
156174
{
157175
if (*ptr!='\\') *ptr2++=*ptr++;
@@ -197,7 +215,8 @@ static const char *parse_string(cJSON *item,const char *str)
197215
if (*ptr=='\"') ptr++;
198216
item->valuestring=out;
199217
item->type=cJSON_String;
200-
return ptr;
218+
*str = ptr;
219+
return str;
201220
}
202221

203222
/* Render the cstring provided to an escaped version that can be printed. */
@@ -239,24 +258,31 @@ static char *print_string_ptr(const char *str)
239258
static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);}
240259

241260
/* Predeclare these prototypes. */
242-
static const char *parse_value(cJSON *item,const char *value);
261+
static char **parse_value(cJSON *item, char **value);
243262
static char *print_value(cJSON *item,int depth,int fmt);
244-
static const char *parse_array(cJSON *item,const char *value);
263+
static char **parse_array(cJSON *item, char **value);
245264
static char *print_array(cJSON *item,int depth,int fmt);
246-
static const char *parse_object(cJSON *item,const char *value);
265+
static char **parse_object(cJSON *item, char **value);
247266
static char *print_object(cJSON *item,int depth,int fmt);
248267

249268
/* Utility to jump whitespace and cr/lf */
250-
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
269+
static inline char **skip(char **in)
270+
{
271+
if (in && *in)
272+
while (isspace(**in))
273+
(*in)++;
274+
return in;
275+
}
251276

252277
/* Parse an object - create a new root, and populate. */
253278
cJSON *cJSON_Parse(const char *value)
254279
{
255280
cJSON *c=cJSON_New_Item();
256-
ep=0;
257281
if (!c) return 0; /* memory fail */
258282

259-
if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
283+
char **end_ptr = (char **)&value;
284+
285+
if (!parse_value(c,skip(end_ptr))) {cJSON_Delete(c);return 0;}
260286
return c;
261287
}
262288

@@ -267,30 +293,53 @@ cJSON *cJSON_Parse_Stream(const char *value, char **end_ptr)
267293
if(!end_ptr)
268294
return NULL;
269295
cJSON *c=cJSON_New_Item();
270-
ep=0;
271296
if (!c) return 0; /* memory fail */
272297

273-
if (!(*end_ptr=parse_value(c,skip(value)))) {cJSON_Delete(c);return 0;}
298+
*end_ptr = (char *)value;
299+
300+
if (!parse_value(c,skip(end_ptr))) {cJSON_Delete(c);return 0;}
274301
return c;
275302
}
276303

277304
/* Render a cJSON item/entity/structure to text. */
278305
char *cJSON_Print(cJSON *item) {return print_value(item,0,1);}
279306
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);}
280307

308+
static int stream_cmp(char **stream, const char *str)
309+
{
310+
while (**stream == *str) {
311+
(*stream)++;
312+
str++;
313+
}
314+
if (*str == '\0')
315+
return 0;
316+
return **stream - *str;
317+
}
318+
281319
/* Parser core - when encountering text, process appropriately. */
282-
static const char *parse_value(cJSON *item,const char *value)
320+
static char **parse_value(cJSON *item, char **value)
283321
{
284-
if (!value) return 0; /* Fail on null. */
285-
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
286-
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
287-
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
288-
if (*value=='\"') { return parse_string(item,value); }
289-
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
290-
if (*value=='[') { return parse_array(item,value); }
291-
if (*value=='{') { return parse_object(item,value); }
292-
293-
ep=value;return 0; /* failure. */
322+
if (!stream_cmp(value,"null")) {
323+
item->type=cJSON_NULL;
324+
return value;
325+
} else if (!stream_cmp(value,"false")) {
326+
item->type=cJSON_False;
327+
return value;
328+
} else if (!stream_cmp(value,"true")) {
329+
item->type=cJSON_True;
330+
item->valueint = 1;
331+
return value;
332+
}
333+
334+
switch (**value) {
335+
case '"': return parse_string(item,value);
336+
case '-':
337+
case '0'...'9': return parse_number(item,value);
338+
case '[': return parse_array(item,value);
339+
case '{': return parse_object(item,value);
340+
}
341+
342+
return NULL; /* failure */
294343
}
295344

296345
/* Render a value to text. */
@@ -312,33 +361,46 @@ static char *print_value(cJSON *item,int depth,int fmt)
312361
}
313362

314363
/* Build an array from input text. */
315-
static const char *parse_array(cJSON *item,const char *value)
364+
static char **parse_array(cJSON *item, char **value)
316365
{
317366
cJSON *child;
318-
if (*value!='[') {ep=value;return 0;} /* not an array! */
367+
if (**value!='[') /* not an array! */
368+
return NULL;
319369

320370
item->type=cJSON_Array;
321-
value=skip(value+1);
322-
if (*value==']') return value+1; /* empty array. */
371+
(*value)++;
372+
skip(value);
373+
if (**value==']') {
374+
(*value)++;
375+
return value; /* empty array. */
376+
}
323377

324378
item->child=child=cJSON_New_Item();
325379
if (!item->child) return 0; /* memory fail */
326-
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
327-
if (!value) return 0;
380+
if (!skip(parse_value(child,value))) /* skip any spacing, get the value. */
381+
return NULL;
328382

329-
while (*value==',')
383+
while (**value==',')
330384
{
331-
const char *nextch = skip(value+1);
332-
if (nextch!=0 && *nextch==']') {value=nextch;break;}
385+
(*value)++;
386+
skip(value);
387+
if (**value==']') {
388+
(*value)++;
389+
break;
390+
}
391+
333392
cJSON *new_item;
334393
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
335394
child->next=new_item;new_item->prev=child;child=new_item;
336-
value=skip(parse_value(child,nextch));
337-
if (!value) return 0; /* memory fail */
395+
if(!skip(parse_value(child,value)))
396+
return 0; /* memory fail */
338397
}
339398

340-
if (*value==']') return value+1; /* end of array */
341-
ep=value;return 0; /* malformed. */
399+
if (**value != ']')
400+
return NULL;
401+
(*value)++;
402+
403+
return value;
342404
}
343405

344406
/* Render an array to text */
@@ -393,41 +455,56 @@ static char *print_array(cJSON *item,int depth,int fmt)
393455
}
394456

395457
/* Build an object from the text. */
396-
static const char *parse_object(cJSON *item,const char *value)
458+
static char **parse_object(cJSON *item, char **value)
397459
{
398460
cJSON *child;
399-
if (*value!='{') {ep=value;return 0;} /* not an object! */
461+
if (**value!='{') return NULL; /* not an object! */
400462

401463
item->type=cJSON_Object;
402-
value=skip(value+1);
403-
if (*value=='}') return value+1; /* empty array. */
464+
(*value)++;
465+
skip(value);
466+
if (**value=='}') {
467+
(*value)++;
468+
return value; /* empty object. */
469+
}
404470

405471
item->child=child=cJSON_New_Item();
406472
if (!item->child) return 0;
407-
value=skip(parse_string(child,skip(value)));
408-
if (!value) return 0;
473+
if (!skip(parse_string(child,value)))
474+
return 0;
409475
child->string=child->valuestring;child->valuestring=0;
410-
if (*value!=':') {ep=value;return 0;} /* fail! */
411-
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
412-
if (!value) return 0;
476+
if (**value!=':') return NULL; /* fail! */
477+
(*value)++;
478+
if (!skip(parse_value(child,skip(value)))) /* skip any spacing, get the value. */
479+
return 0;
413480

414-
while (*value==',')
481+
while (**value==',')
415482
{
416-
const char *nextch = skip(value+1);
417-
if (nextch!=0 && *nextch=='}') {value=nextch;break;}
483+
(*value)++;
484+
skip(value);
485+
486+
if (**value == '}') {
487+
(*value)++;
488+
break;
489+
}
490+
418491
cJSON *new_item;
419492
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
420493
child->next=new_item;new_item->prev=child;child=new_item;
421-
value=skip(parse_string(child,nextch));
422-
if (!value) return 0;
494+
if (!skip(parse_string(child,value)))
495+
return 0;
423496
child->string=child->valuestring;child->valuestring=0;
424-
if (*value!=':') {ep=value;return 0;} /* fail! */
425-
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
426-
if (!value) return 0;
497+
if (**value!=':') return NULL; /* fail! */
498+
(*value)++;
499+
if (!skip(parse_value(child,skip(value)))) /* skip any spacing, get the value. */
500+
return 0;
427501
}
428502

429-
if (*value=='}') return value+1; /* end of array */
430-
ep=value;return 0; /* malformed. */
503+
if (**value != '}')
504+
return NULL;
505+
(*value)++;
506+
507+
return value;
431508
}
432509

433510
/* Render an object to text. */

src/jsonrpc-c.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
163163
return close_connection(loop, w);
164164
} else {
165165
cJSON *root;
166-
char *end_ptr;
166+
char *end_ptr = NULL;
167167
conn->pos += bytes_read;
168168

169169
if ((root = cJSON_Parse_Stream(conn->buffer, &end_ptr)) != NULL) {
@@ -187,7 +187,7 @@ static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
187187
} else {
188188
// did we parse the all buffer? If so, just wait for more.
189189
// else there was an error before the buffer's end
190-
if (cJSON_GetErrorPtr() != (conn->buffer + conn->pos)) {
190+
if (end_ptr != (conn->buffer + conn->pos)) {
191191
if (server->debug_level) {
192192
printf("INVALID JSON Received:\n---\n%s\n---\n",
193193
conn->buffer);

0 commit comments

Comments
 (0)