Ver Fonte

Merge latest upstream code (r73) into github fork.

Signed-off-by: Anton Sergeev <Anton.Sergeev@elecard.ru>

Conflicts:
	cJSON.c
	test.c
Anton Sergeev há 10 anos atrás
pai
commit
74793934ad
6 ficheiros alterados com 755 adições e 112 exclusões
  1. 258 104
      cJSON.c
  2. 6 0
      cJSON.h
  3. 342 0
      cJSON_Utils.c
  4. 26 0
      cJSON_Utils.h
  5. 14 8
      test.c
  6. 109 0
      test_utils.c

+ 258 - 104
cJSON.c

@@ -86,7 +86,7 @@ void cJSON_Delete(cJSON *c)
 		next=c->next;
 		if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
 		if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
-		if (c->string) cJSON_free(c->string);
+		if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
 		cJSON_free(c);
 		c=next;
 	}
@@ -114,19 +114,56 @@ static const char *parse_number(cJSON *item,const char *num)
 	return num;
 }
 
-/* Render the number nicely from the given item into a string. */
-static char *print_number(cJSON *item)
+static int pow2gt (int x)	{	--x;	x|=x>>1;	x|=x>>2;	x|=x>>4;	x|=x>>8;	x|=x>>16;	return x+1;	}
+
+typedef struct {char *buffer; int length; int offset; } printbuffer;
+
+static char* ensure(printbuffer *p,int needed)
+{
+	char *newbuffer;int newsize;
+	if (!p || !p->buffer) return 0;
+	needed+=p->offset;
+	if (needed<=p->length) return p->buffer+p->offset;
+
+	newsize=pow2gt(needed);
+	newbuffer=(char*)cJSON_malloc(newsize);
+	if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
+	if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
+	cJSON_free(p->buffer);
+	p->length=newsize;
+	p->buffer=newbuffer;
+	return newbuffer+p->offset;
+}
+
+static int update(printbuffer *p)
 {
 	char *str;
+	if (!p || !p->buffer) return 0;
+	str=p->buffer+p->offset;
+	return p->offset+strlen(str);
+}
+
+/* Render the number nicely from the given item into a string. */
+static char *print_number(cJSON *item,printbuffer *p)
+{
+	char *str=0;
 	double d=item->valuedouble;
-	if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
+	if (d==0)
+	{
+		if (p)	str=ensure(p,2);
+		else	str=(char*)cJSON_malloc(2);	/* special case for 0. */
+		if (str) strcpy(str,"0");
+	}
+	else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
 	{
-		str=(char*)cJSON_malloc(21);	/* 2^64+1 can be represented in 21 chars. */
-		if (str) sprintf(str,"%d",item->valueint);
+		if (p)	str=ensure(p,21);
+		else	str=(char*)cJSON_malloc(21);	/* 2^64+1 can be represented in 21 chars. */
+		if (str)	sprintf(str,"%d",item->valueint);
 	}
 	else
 	{
-		str=(char*)cJSON_malloc(64);	/* This is a nice tradeoff. */
+		if (p)	str=ensure(p,64);
+		else	str=(char*)cJSON_malloc(64);	/* This is a nice tradeoff. */
 		if (str)
 		{
 			if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
@@ -212,14 +249,36 @@ static const char *parse_string(cJSON *item,const char *str)
 }
 
 /* Render the cstring provided to an escaped version that can be printed. */
-static char *print_string_ptr(const char *str)
+static char *print_string_ptr(const char *str,printbuffer *p)
 {
-	const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
+	const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;
+	
+	for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
+	if (!flag)
+	{
+		len=ptr-str;
+		if (p) out=ensure(p,len+3);
+		else		out=(char*)cJSON_malloc(len+3);
+		if (!out) return 0;
+		ptr2=out;*ptr2++='\"';
+		strcpy(ptr2,str);
+		ptr2[len]='\"';
+		ptr2[len+1]=0;
+		return out;
+	}
 	
-	if (!str) return cJSON_strdup("");
+	if (!str)
+	{
+		if (p)	out=ensure(p,3);
+		else	out=(char*)cJSON_malloc(3);
+		if (!out) return 0;
+		strcpy(out,"\"\"");
+		return out;
+	}
 	ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
 	
-	out=(char*)cJSON_malloc(len+3);
+	if (p)	out=ensure(p,len+3);
+	else	out=(char*)cJSON_malloc(len+3);
 	if (!out) return 0;
 
 	ptr2=out;ptr=str;
@@ -247,15 +306,15 @@ static char *print_string_ptr(const char *str)
 	return out;
 }
 /* Invote print_string_ptr (which is useful) on an item. */
-static char *print_string(cJSON *item)	{return print_string_ptr(item->valuestring);}
+static char *print_string(cJSON *item,printbuffer *p)	{return print_string_ptr(item->valuestring,p);}
 
 /* Predeclare these prototypes. */
 static const char *parse_value(cJSON *item,const char *value);
-static char *print_value(cJSON *item,int depth,int fmt);
+static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
 static const char *parse_array(cJSON *item,const char *value);
-static char *print_array(cJSON *item,int depth,int fmt);
+static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
 static const char *parse_object(cJSON *item,const char *value);
-static char *print_object(cJSON *item,int depth,int fmt);
+static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
 
 /* Utility to jump whitespace and cr/lf */
 static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
@@ -280,8 +339,19 @@ cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int r
 cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
 
 /* Render a cJSON item/entity/structure to text. */
-char *cJSON_Print(cJSON *item)				{return print_value(item,0,1);}
-char *cJSON_PrintUnformatted(cJSON *item)	{return print_value(item,0,0);}
+char *cJSON_Print(cJSON *item)				{return print_value(item,0,1,0);}
+char *cJSON_PrintUnformatted(cJSON *item)	{return print_value(item,0,0,0);}
+
+char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
+{
+	printbuffer p;
+	p.buffer=(char*)cJSON_malloc(prebuffer);
+	p.length=prebuffer;
+	p.offset=0;
+	return print_value(item,0,fmt,&p);
+	return p.buffer;
+}
+
 
 /* Parser core - when encountering text, process appropriately. */
 static const char *parse_value(cJSON *item,const char *value)
@@ -299,19 +369,35 @@ static const char *parse_value(cJSON *item,const char *value)
 }
 
 /* Render a value to text. */
-static char *print_value(cJSON *item,int depth,int fmt)
+static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
 {
 	char *out=0;
 	if (!item) return 0;
-	switch ((item->type)&255)
+	if (p)
+	{
+		switch ((item->type)&255)
+		{
+			case cJSON_NULL:	{out=ensure(p,5);	if (out) strcpy(out,"null");	break;}
+			case cJSON_False:	{out=ensure(p,6);	if (out) strcpy(out,"false");	break;}
+			case cJSON_True:	{out=ensure(p,5);	if (out) strcpy(out,"true");	break;}
+			case cJSON_Number:	out=print_number(item,p);break;
+			case cJSON_String:	out=print_string(item,p);break;
+			case cJSON_Array:	out=print_array(item,depth,fmt,p);break;
+			case cJSON_Object:	out=print_object(item,depth,fmt,p);break;
+		}
+	}
+	else
 	{
-		case cJSON_NULL:	out=cJSON_strdup("null");	break;
-		case cJSON_False:	out=cJSON_strdup("false");break;
-		case cJSON_True:	out=cJSON_strdup("true"); break;
-		case cJSON_Number:	out=print_number(item);break;
-		case cJSON_String:	out=print_string(item);break;
-		case cJSON_Array:	out=print_array(item,depth,fmt);break;
-		case cJSON_Object:	out=print_object(item,depth,fmt);break;
+		switch ((item->type)&255)
+		{
+			case cJSON_NULL:	out=cJSON_strdup("null");	break;
+			case cJSON_False:	out=cJSON_strdup("false");break;
+			case cJSON_True:	out=cJSON_strdup("true"); break;
+			case cJSON_Number:	out=print_number(item,0);break;
+			case cJSON_String:	out=print_string(item,0);break;
+			case cJSON_Array:	out=print_array(item,depth,fmt,0);break;
+			case cJSON_Object:	out=print_object(item,depth,fmt,0);break;
+		}
 	}
 	return out;
 }
@@ -345,60 +431,82 @@ static const char *parse_array(cJSON *item,const char *value)
 }
 
 /* Render an array to text */
-static char *print_array(cJSON *item,int depth,int fmt)
+static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
 {
 	char **entries;
 	char *out=0,*ptr,*ret;int len=5;
 	cJSON *child=item->child;
 	int numentries=0,i=0,fail=0;
+	size_t tmplen=0;
 	
 	/* How many entries in the array? */
 	while (child) numentries++,child=child->next;
 	/* Explicitly handle numentries==0 */
 	if (!numentries)
 	{
-		out=(char*)cJSON_malloc(3);
+		if (p)	out=ensure(p,3);
+		else	out=(char*)cJSON_malloc(3);
 		if (out) strcpy(out,"[]");
 		return out;
 	}
-	/* Allocate an array to hold the values for each */
-	entries=(char**)cJSON_malloc(numentries*sizeof(char*));
-	if (!entries) return 0;
-	memset(entries,0,numentries*sizeof(char*));
-	/* Retrieve all the results: */
-	child=item->child;
-	while (child && !fail)
-	{
-		ret=print_value(child,depth+1,fmt);
-		entries[i++]=ret;
-		if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
-		child=child->next;
-	}
-	
-	/* If we didn't fail, try to malloc the output string */
-	if (!fail) out=(char*)cJSON_malloc(len);
-	/* If that fails, we fail. */
-	if (!out) fail=1;
 
-	/* Handle failure. */
-	if (fail)
+	if (p)
 	{
-		for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
-		cJSON_free(entries);
-		return 0;
+		/* Compose the output array. */
+		i=p->offset;
+		ptr=ensure(p,1);if (!ptr) return 0;	*ptr='[';	p->offset++;
+		child=item->child;
+		while (child && !fail)
+		{
+			print_value(child,depth+1,fmt,p);
+			p->offset=update(p);
+			if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
+			child=child->next;
+		}
+		ptr=ensure(p,2);if (!ptr) return 0;	*ptr++=']';*ptr=0;
+		out=(p->buffer)+i;
 	}
-	
-	/* Compose the output array. */
-	*out='[';
-	ptr=out+1;*ptr=0;
-	for (i=0;i<numentries;i++)
+	else
 	{
-		strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
-		if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
-		cJSON_free(entries[i]);
+		/* Allocate an array to hold the values for each */
+		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+		if (!entries) return 0;
+		memset(entries,0,numentries*sizeof(char*));
+		/* Retrieve all the results: */
+		child=item->child;
+		while (child && !fail)
+		{
+			ret=print_value(child,depth+1,fmt,0);
+			entries[i++]=ret;
+			if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
+			child=child->next;
+		}
+		
+		/* If we didn't fail, try to malloc the output string */
+		if (!fail)	out=(char*)cJSON_malloc(len);
+		/* If that fails, we fail. */
+		if (!out) fail=1;
+
+		/* Handle failure. */
+		if (fail)
+		{
+			for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
+			cJSON_free(entries);
+			return 0;
+		}
+		
+		/* Compose the output array. */
+		*out='[';
+		ptr=out+1;*ptr=0;
+		for (i=0;i<numentries;i++)
+		{
+			tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
+			if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
+			cJSON_free(entries[i]);
+		}
+		cJSON_free(entries);
+		*ptr++=']';*ptr++=0;
 	}
-	cJSON_free(entries);
-	*ptr++=']';*ptr++=0;
 	return out;	
 }
 
@@ -439,70 +547,113 @@ static const char *parse_object(cJSON *item,const char *value)
 }
 
 /* Render an object to text. */
-static char *print_object(cJSON *item,int depth,int fmt)
+static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
 {
 	char **entries=0,**names=0;
 	char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
 	cJSON *child=item->child;
 	int numentries=0,fail=0;
+	size_t tmplen=0;
 	/* Count the number of entries. */
 	while (child) numentries++,child=child->next;
 	/* Explicitly handle empty object case */
 	if (!numentries)
 	{
-		out=(char*)cJSON_malloc(fmt?depth+4:3);
+		if (p) out=ensure(p,fmt?depth+4:3);
+		else	out=(char*)cJSON_malloc(fmt?depth+4:3);
 		if (!out)	return 0;
 		ptr=out;*ptr++='{';
 		if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
 		*ptr++='}';*ptr++=0;
 		return out;
 	}
-	/* Allocate space for the names and the objects */
-	entries=(char**)cJSON_malloc(numentries*sizeof(char*));
-	if (!entries) return 0;
-	names=(char**)cJSON_malloc(numentries*sizeof(char*));
-	if (!names) {cJSON_free(entries);return 0;}
-	memset(entries,0,sizeof(char*)*numentries);
-	memset(names,0,sizeof(char*)*numentries);
-
-	/* Collect all the results into our arrays: */
-	child=item->child;depth++;if (fmt) len+=depth;
-	while (child&&!fail)
+	if (p)
 	{
-		names[i]=str=print_string_ptr(child->string);
-		entries[i++]=ret=print_value(child,depth,fmt);
-		if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
-		child=child->next;
+		/* Compose the output: */
+		i=p->offset;
+		len=fmt?2:1;	ptr=ensure(p,len+1);	if (!ptr) return 0;
+		*ptr++='{';	if (fmt) *ptr++='\n';	*ptr=0;	p->offset+=len;
+		child=item->child;depth++;
+		while (child)
+		{
+			if (fmt)
+			{
+				ptr=ensure(p,depth);	if (!ptr) return 0;
+				for (j=0;j<depth;j++) *ptr++='\t';
+				p->offset+=depth;
+			}
+			print_string_ptr(child->string,p);
+			p->offset=update(p);
+			
+			len=fmt?2:1;
+			ptr=ensure(p,len);	if (!ptr) return 0;
+			*ptr++=':';if (fmt) *ptr++='\t';
+			p->offset+=len;
+			
+			print_value(child,depth,fmt,p);
+			p->offset=update(p);
+
+			len=(fmt?1:0)+(child->next?1:0);
+			ptr=ensure(p,len+1); if (!ptr) return 0;
+			if (child->next) *ptr++=',';
+			if (fmt) *ptr++='\n';*ptr=0;
+			p->offset+=len;
+			child=child->next;
+		}
+		ptr=ensure(p,fmt?(depth+1):2);	 if (!ptr) return 0;
+		if (fmt)	for (i=0;i<depth-1;i++) *ptr++='\t';
+		*ptr++='}';*ptr=0;
+		out=(p->buffer)+i;
 	}
-	
-	/* Try to allocate the output string */
-	if (!fail) out=(char*)cJSON_malloc(len);
-	if (!out) fail=1;
-
-	/* Handle failure */
-	if (fail)
+	else
 	{
-		for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
+		/* Allocate space for the names and the objects */
+		entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+		if (!entries) return 0;
+		names=(char**)cJSON_malloc(numentries*sizeof(char*));
+		if (!names) {cJSON_free(entries);return 0;}
+		memset(entries,0,sizeof(char*)*numentries);
+		memset(names,0,sizeof(char*)*numentries);
+
+		/* Collect all the results into our arrays: */
+		child=item->child;depth++;if (fmt) len+=depth;
+		while (child && !fail)
+		{
+			names[i]=str=print_string_ptr(child->string,0);
+			entries[i++]=ret=print_value(child,depth,fmt,0);
+			if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
+			child=child->next;
+		}
+		
+		/* Try to allocate the output string */
+		if (!fail)	out=(char*)cJSON_malloc(len);
+		if (!out) fail=1;
+
+		/* Handle failure */
+		if (fail)
+		{
+			for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
+			cJSON_free(names);cJSON_free(entries);
+			return 0;
+		}
+		
+		/* Compose the output: */
+		*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
+		for (i=0;i<numentries;i++)
+		{
+			if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
+			tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
+			*ptr++=':';if (fmt) *ptr++='\t';
+			strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+			if (i!=numentries-1) *ptr++=',';
+			if (fmt) *ptr++='\n';*ptr=0;
+			cJSON_free(names[i]);cJSON_free(entries[i]);
+		}
+		
 		cJSON_free(names);cJSON_free(entries);
-		return 0;
-	}
-	
-	/* Compose the output: */
-	*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
-	for (i=0;i<numentries;i++)
-	{
-		if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
-		strcpy(ptr,names[i]);ptr+=strlen(names[i]);
-		*ptr++=':';if (fmt) *ptr++='\t';
-		strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
-		if (i!=numentries-1) *ptr++=',';
-		if (fmt) *ptr++='\n';*ptr=0;
-		cJSON_free(names[i]);cJSON_free(entries[i]);
+		if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
+		*ptr++='}';*ptr++=0;
 	}
-	
-	cJSON_free(names);cJSON_free(entries);
-	if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
-	*ptr++='}';*ptr++=0;
 	return out;	
 }
 
@@ -519,6 +670,7 @@ static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!re
 /* Add item to array/object. */
 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);}}
 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);}
+void   cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item)	{if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
 void	cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)						{cJSON_AddItemToArray(array,create_reference(item));}
 void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)	{cJSON_AddItemToObject(object,string,create_reference(item));}
 
@@ -529,6 +681,8 @@ cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJS
 void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
 
 /* Replace array/object items with new ones. */
+void   cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
+	newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
 void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)		{cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
 	newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
 	if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}

+ 6 - 0
cJSON.h

@@ -38,6 +38,7 @@ extern "C"
 #define cJSON_Object 6
 	
 #define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
 
 /* The cJSON structure: */
 typedef struct cJSON {
@@ -68,6 +69,8 @@ extern cJSON *cJSON_Parse(const char *value);
 extern char  *cJSON_Print(cJSON *item);
 /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
 extern char  *cJSON_PrintUnformatted(cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
 /* Delete a cJSON entity and all subentities. */
 extern void   cJSON_Delete(cJSON *c);
 
@@ -100,6 +103,7 @@ extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
 /* Append item to the specified array/object. */
 extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
 extern void	cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
+extern void	cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);	/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
 /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
 extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
 extern void	cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
@@ -111,6 +115,7 @@ extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
 extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
 	
 /* Update array items. */
+extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);	/* Shifts pre-existing items to the right. */
 extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
 extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
 
@@ -135,6 +140,7 @@ extern void cJSON_Minify(char *json);
 
 /* When assigning an integer value, it needs to be propagated to valuedouble too. */
 #define cJSON_SetIntValue(object,val)			((object)?(object)->valueint=(object)->valuedouble=(val):(val))
+#define cJSON_SetNumberValue(object,val)		((object)?(object)->valueint=(object)->valuedouble=(val):(val))
 
 #ifdef __cplusplus
 }

+ 342 - 0
cJSON_Utils.c

@@ -0,0 +1,342 @@
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "cJSON_Utils.h"
+
+static int cJSONUtils_strcasecmp(const char *s1,const char *s2)
+{
+	if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
+	for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)	if(*s1 == 0)	return 0;
+	return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
+}
+
+/* JSON Pointer implementation: */
+static int cJSONUtils_Pstrcasecmp(const char *a,const char *e)
+{
+	if (!a || !e) return (a==e)?0:1;
+	for (;*a && *e && *e!='/';a++,e++) {
+		if (*e=='~') {if (!(e[1]=='0' && *a=='~') && !(e[1]=='1' && *a=='/')) return 1;  else e++;}
+		else if (tolower(*a)!=tolower(*e)) return 1;
+	}
+	if ((*e!=0 && *e!='/') != (*a!=0)) return 1;
+	return 0;
+}
+
+static int cJSONUtils_PointerEncodedstrlen(const char *s)	{int l=0;for (;*s;s++,l++) if (*s=='~' || *s=='/') l++;return l;}
+
+static void cJSONUtils_PointerEncodedstrcpy(char *d,const char *s)
+{
+	for (;*s;s++)
+	{
+		if (*s=='/') {*d++='~';*d++='1';}
+		else if (*s=='~') {*d++='~';*d++='0';}
+		else *d++=*s;
+	}
+	*d=0;
+}
+
+char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target)
+{
+	int type=object->type,c=0;cJSON *obj=0;
+
+	if (object==target) return strdup("");
+
+	for (obj=object->child;obj;obj=obj->next,c++)
+	{
+		char *found=cJSONUtils_FindPointerFromObjectTo(obj,target);
+		if (found)
+		{
+			if (type==cJSON_Array)
+			{
+				char *ret=(char*)malloc(strlen(found)+23);
+				sprintf(ret,"/%d%s",c,found);
+				free(found);
+				return ret;
+			}
+			else if (type==cJSON_Object)
+			{
+				char *ret=(char*)malloc(strlen(found)+cJSONUtils_PointerEncodedstrlen(obj->string)+2);
+				*ret='/';cJSONUtils_PointerEncodedstrcpy(ret+1,obj->string);
+				strcat(ret,found);
+				free(found);
+				return ret;
+			}
+			free(found);
+			return 0;
+		}
+	}
+	return 0;
+}
+
+cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer)
+{
+	while (*pointer++=='/' && object)
+	{
+		if (object->type==cJSON_Array)
+		{
+			int which=0; while (*pointer>='0' && *pointer<='9') which=(10*which) + *pointer++ - '0';
+			if (*pointer && *pointer!='/') return 0;
+			object=cJSON_GetArrayItem(object,which);
+		}
+		else if (object->type==cJSON_Object)
+		{
+			object=object->child;	while (object && cJSONUtils_Pstrcasecmp(object->string,pointer)) object=object->next;	/* GetObjectItem. */
+			while (*pointer && *pointer!='/') pointer++;
+		}
+		else return 0;
+	}
+	return object;
+}
+
+/* JSON Patch implementation. */
+static void cJSONUtils_InplaceDecodePointerString(char *string)
+{
+	char *s2=string;
+	for (;*string;s2++,string++) *s2=(*string!='~')?(*string):((*(++string)=='0')?'~':'/');
+	*s2=0;
+}
+
+static cJSON *cJSONUtils_PatchDetach(cJSON *object,const char *path)
+{
+	char *parentptr=0,*childptr=0;cJSON *parent=0,*ret=0;
+
+	parentptr=strdup(path);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
+	parent=cJSONUtils_GetPointer(object,parentptr);
+	cJSONUtils_InplaceDecodePointerString(childptr);
+
+	if (!parent) ret=0;	/* Couldn't find object to remove child from. */
+	else if (parent->type==cJSON_Array)		ret=cJSON_DetachItemFromArray(parent,atoi(childptr));
+	else if (parent->type==cJSON_Object)	ret=cJSON_DetachItemFromObject(parent,childptr);
+	free(parentptr);
+	return ret;
+}
+
+static int cJSONUtils_Compare(cJSON *a,cJSON *b)
+{
+	if (a->type!=b->type)	return -1;	/* mismatched type. */
+	switch (a->type)
+	{
+	case cJSON_Number:	return (a->valueint!=b->valueint || a->valuedouble!=b->valuedouble)?-2:0;	/* numeric mismatch. */
+	case cJSON_String:	return (strcmp(a->valuestring,b->valuestring)!=0)?-3:0;						/* string mismatch. */
+	case cJSON_Array:	for (a=a->child,b=b->child;a && b;a=a->next,b=b->next)	{int err=cJSONUtils_Compare(a,b);if (err) return err;}
+						return (a || b)?-4:0;	/* array size mismatch. */
+	case cJSON_Object:
+						cJSONUtils_SortObject(a);
+						cJSONUtils_SortObject(b);
+						a=a->child,b=b->child;
+						while (a && b)
+						{
+							int err;
+							if (cJSONUtils_strcasecmp(a->string,b->string))	return -6;	/* missing member */
+							err=cJSONUtils_Compare(a,b);if (err) return err;
+							a=a->next,b=b->next;
+						}
+						return (a || b)?-5:0;	/* object length mismatch */
+
+	default:			break;
+	}
+	return 0;
+}
+
+static int cJSONUtils_ApplyPatch(cJSON *object,cJSON *patch)
+{
+	cJSON *op=0,*path=0,*value=0,*parent=0;int opcode=0;char *parentptr=0,*childptr=0;
+
+	op=cJSON_GetObjectItem(patch,"op");
+	path=cJSON_GetObjectItem(patch,"path");
+	if (!op || !path) return 2;	/* malformed patch. */
+
+	if		(!strcmp(op->valuestring,"add"))	opcode=0;
+	else if (!strcmp(op->valuestring,"remove")) opcode=1;
+	else if (!strcmp(op->valuestring,"replace"))opcode=2;
+	else if (!strcmp(op->valuestring,"move"))	opcode=3;
+	else if (!strcmp(op->valuestring,"copy"))	opcode=4;
+	else if (!strcmp(op->valuestring,"test"))	return cJSONUtils_Compare(cJSONUtils_GetPointer(object,path->valuestring),cJSON_GetObjectItem(patch,"value"));
+	else return 3; /* unknown opcode. */
+
+	if (opcode==1 || opcode==2)	/* Remove/Replace */
+	{
+		cJSON_Delete(cJSONUtils_PatchDetach(object,path->valuestring));	/* Get rid of old. */
+		if (opcode==1) return 0;	/* For Remove, this is job done. */
+	}
+
+	if (opcode==3 || opcode==4)	/* Copy/Move uses "from". */
+	{
+		cJSON *from=cJSON_GetObjectItem(patch,"from");	if (!from) return 4; /* missing "from" for copy/move. */
+
+		if (opcode==3) value=cJSONUtils_PatchDetach(object,from->valuestring);
+		if (opcode==4) value=cJSONUtils_GetPointer(object,from->valuestring);
+		if (!value) return 5; /* missing "from" for copy/move. */
+		if (opcode==4) value=cJSON_Duplicate(value,1);
+		if (!value) return 6; /* out of memory for copy/move. */
+	}
+	else	/* Add/Replace uses "value". */
+	{
+		value=cJSON_GetObjectItem(patch,"value");
+		if (!value) return 7; /* missing "value" for add/replace. */
+		value=cJSON_Duplicate(value,1);
+		if (!value) return 8; /* out of memory for add/replace. */
+	}
+		
+	/* Now, just add "value" to "path". */
+
+	parentptr=strdup(path->valuestring);	childptr=strrchr(parentptr,'/');	if (childptr) *childptr++=0;
+	parent=cJSONUtils_GetPointer(object,parentptr);
+	cJSONUtils_InplaceDecodePointerString(childptr);
+
+	/* add, remove, replace, move, copy, test. */
+	if (!parent) {free(parentptr); return 9;}	/* Couldn't find object to add to. */
+	else if (parent->type==cJSON_Array)
+	{
+		if (!strcmp(childptr,"-"))	cJSON_AddItemToArray(parent,value);
+		else						cJSON_InsertItemInArray(parent,atoi(childptr),value);
+	}
+	else if (parent->type==cJSON_Object)
+	{
+		cJSON_DeleteItemFromObject(parent,childptr);
+		cJSON_AddItemToObject(parent,childptr,value);
+	}
+	free(parentptr);
+	return 0;
+}
+
+
+int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches)
+{
+	int err;
+	if (!patches->type==cJSON_Array) return 1;	/* malformed patches. */
+	if (patches) patches=patches->child;
+	while (patches)
+	{
+		if ((err=cJSONUtils_ApplyPatch(object,patches))) return err;
+		patches=patches->next;
+	}
+	return 0;
+}
+
+static void cJSONUtils_GeneratePatch(cJSON *patches,const char *op,const char *path,const char *suffix,cJSON *val)
+{
+	cJSON *patch=cJSON_CreateObject();
+	cJSON_AddItemToObject(patch,"op",cJSON_CreateString(op));
+	if (suffix)
+	{
+		char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(suffix)+2);
+		cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),suffix);
+		cJSON_AddItemToObject(patch,"path",cJSON_CreateString(newpath));
+		free(newpath);
+	}
+	else	cJSON_AddItemToObject(patch,"path",cJSON_CreateString(path));
+	if (val) cJSON_AddItemToObject(patch,"value",cJSON_Duplicate(val,1));
+	cJSON_AddItemToArray(patches,patch);
+}
+
+void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val)	{cJSONUtils_GeneratePatch(array,op,path,0,val);}
+
+static void cJSONUtils_CompareToPatch(cJSON *patches,const char *path,cJSON *from,cJSON *to)
+{
+	if (from->type!=to->type)	{cJSONUtils_GeneratePatch(patches,"replace",path,0,to);	return;	}
+	
+	switch (from->type)
+	{
+	case cJSON_Number:	
+		if (from->valueint!=to->valueint || from->valuedouble!=to->valuedouble)
+			cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
+		return;
+						
+	case cJSON_String:	
+		if (strcmp(from->valuestring,to->valuestring)!=0)
+			cJSONUtils_GeneratePatch(patches,"replace",path,0,to);
+		return;
+
+	case cJSON_Array:
+	{
+		int c;char *newpath=(char*)malloc(strlen(path)+23);	/* Allow space for 64bit int. */
+		for (c=0,from=from->child,to=to->child;from && to;from=from->next,to=to->next,c++){
+										sprintf(newpath,"%s/%d",path,c);	cJSONUtils_CompareToPatch(patches,newpath,from,to);
+		}
+		for (;from;from=from->next,c++)	{sprintf(newpath,"%d",c);	cJSONUtils_GeneratePatch(patches,"remove",path,newpath,0);	}
+		for (;to;to=to->next,c++)		cJSONUtils_GeneratePatch(patches,"add",path,"-",to);
+		free(newpath);
+		return;
+	}
+
+	case cJSON_Object:
+	{
+		cJSON *a,*b;
+		cJSONUtils_SortObject(from);
+		cJSONUtils_SortObject(to);
+		
+		a=from->child,b=to->child;
+		while (a || b)
+		{
+			int diff=(!a)?1:(!b)?-1:cJSONUtils_strcasecmp(a->string,b->string);
+			if (!diff)
+			{
+				char *newpath=(char*)malloc(strlen(path)+cJSONUtils_PointerEncodedstrlen(a->string)+2);
+				cJSONUtils_PointerEncodedstrcpy(newpath+sprintf(newpath,"%s/",path),a->string);
+				cJSONUtils_CompareToPatch(patches,newpath,a,b);
+				free(newpath);
+				a=a->next;
+				b=b->next;
+			}
+			else if (diff<0)	{cJSONUtils_GeneratePatch(patches,"remove",path,a->string,0);	a=a->next;}
+			else				{cJSONUtils_GeneratePatch(patches,"add",path,b->string,b);		b=b->next;}
+		}
+		return;
+	}
+
+	default:			break;
+	}
+}
+
+
+cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to)
+{
+	cJSON *patches=cJSON_CreateArray();	
+	cJSONUtils_CompareToPatch(patches,"",from,to);
+	return patches;
+}
+
+
+static cJSON *cJSONUtils_SortList(cJSON *list)
+{
+	cJSON *first=list,*second=list,*ptr=list;
+
+	if (!list || !list->next) return list;	/* One entry is sorted already. */
+	
+	while (ptr && ptr->next && cJSONUtils_strcasecmp(ptr->string,ptr->next->string)<0) ptr=ptr->next;	/* Test for list sorted. */
+	if (!ptr || !ptr->next) return list;	/* Leave sorted lists unmodified. */
+	ptr=list;
+
+	while (ptr) {second=second->next;ptr=ptr->next;if (ptr) ptr=ptr->next;}	/* Walk two pointers to find the middle. */
+	if (second && second->prev) second->prev->next=0;	/* Split the lists */
+
+	first=cJSONUtils_SortList(first);	/* Recursively sort the sub-lists. */
+	second=cJSONUtils_SortList(second);
+	list=ptr=0;
+
+	while (first && second)	/* Merge the sub-lists */
+	{		
+		if (cJSONUtils_strcasecmp(first->string,second->string)<0)
+		{
+			if (!list) list=ptr=first;
+			else	{ptr->next=first;first->prev=ptr;ptr=first;}
+			first=first->next;
+		}
+		else 
+		{
+			if (!list) list=ptr=second;
+			else	{ptr->next=second;second->prev=ptr;ptr=second;}
+			second=second->next;
+		}
+	}
+	if (first)	{	if (!list) return first;	ptr->next=first;	first->prev=ptr;	}	/* Append any tails. */
+	if (second)	{	if (!list) return second;	ptr->next=second;	second->prev=ptr;	}
+
+	return list;
+}
+
+void cJSONUtils_SortObject(cJSON *object)	{object->child=cJSONUtils_SortList(object->child);}
+
+

+ 26 - 0
cJSON_Utils.h

@@ -0,0 +1,26 @@
+#include "cJSON.h"
+
+/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec.	*/
+cJSON *cJSONUtils_GetPointer(cJSON *object,const char *pointer);
+
+/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec.		*/
+cJSON* cJSONUtils_GeneratePatches(cJSON *from,cJSON *to);
+void cJSONUtils_AddPatchToArray(cJSON *array,const char *op,const char *path,cJSON *val);	/* Utility for generating patch array entries.	*/
+int cJSONUtils_ApplyPatches(cJSON *object,cJSON *patches);	/* Returns 0 for success. */
+
+/*
+// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
+//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
+//{
+//	cJSON *modme=cJSON_Duplicate(*object,1);
+//	int error=cJSONUtils_ApplyPatches(modme,patches);
+//	if (!error)	{cJSON_Delete(*object);*object=modme;}
+//	else		cJSON_Delete(modme);
+//	return error;
+//}
+// Code not added to library since this strategy is a LOT slower.
+*/
+
+char *cJSONUtils_FindPointerFromObjectTo(cJSON *object,cJSON *target);	/* Given a root object and a target object, construct a pointer from one to the other.	*/
+
+void cJSONUtils_SortObject(cJSON *object);	/* Sorts the members of the object into alphabetical order.	*/

+ 14 - 8
test.c

@@ -43,8 +43,10 @@ void doit(char *text)
 /* Read a file, parse, render back, etc. */
 void dofile(char *filename)
 {
-	FILE *f=fopen(filename,"rb");fseek(f,0,SEEK_END);long len=ftell(f);fseek(f,0,SEEK_SET);
-	char *data=(char*)malloc(len+1);fread(data,1,len,f);data[len]='\0';fclose(f);
+	FILE *f;long len;char *data;
+	
+	f=fopen(filename,"rb");fseek(f,0,SEEK_END);len=ftell(f);fseek(f,0,SEEK_SET);
+	data=(char*)malloc(len+1);fread(data,1,len,f);data[len]='\0';fclose(f);
 	doit(data);
 	free(data);
 }
@@ -56,6 +58,16 @@ struct record {const char *precision;double lat,lon;const char *address,*city,*s
 void create_objects()
 {
 	cJSON *root,*fmt,*img,*thm,*fld;char *out;int i;	/* declare a few. */
+	/* Our "days of the week" array: */
+	const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
+	/* Our matrix: */
+	int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}};
+	/* Our "gallery" item: */
+	int ids[4]={116,943,234,38793};
+	/* Our array of "records": */
+	struct record fields[2]={
+		{"zip",37.7668,-1.223959e+2,"","SAN FRANCISCO","CA","94107","US"},
+		{"zip",37.371991,-1.22026e+2,"","SUNNYVALE","CA","94085","US"}};
 
 	/* Here we construct some JSON standards, from the JSON site. */
 	
@@ -72,13 +84,11 @@ void create_objects()
 	out=cJSON_Print(root);	cJSON_Delete(root);	printf("%s\n",out);	free(out);	/* Print to text, Delete the cJSON, print it, release the string. */
 
 	/* Our "days of the week" array: */
-	const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
 	root=cJSON_CreateStringArray(strings,7);
 
 	out=cJSON_Print(root);	cJSON_Delete(root);	printf("%s\n",out);	free(out);
 
 	/* Our matrix: */
-	int numbers[3][3]={{0,-1,0},{1,0,0},{0,0,1}};
 	root=cJSON_CreateArray();
 	for (i=0;i<3;i++) cJSON_AddItemToArray(root,cJSON_CreateIntArray(numbers[i],3));
 
@@ -88,7 +98,6 @@ void create_objects()
 
 
 	/* Our "gallery" item: */
-	int ids[4]={116,943,234,38793};
 	root=cJSON_CreateObject();
 	cJSON_AddItemToObject(root, "Image", img=cJSON_CreateObject());
 	cJSON_AddNumberToObject(img,"Width",800);
@@ -103,9 +112,6 @@ void create_objects()
 	out=cJSON_Print(root);	cJSON_Delete(root);	printf("%s\n",out);	free(out);
 
 	/* Our array of "records": */
-	struct record fields[2]={
-		{"zip",37.7668,-1.223959e+2,"","SAN FRANCISCO","CA","94107","US"},
-		{"zip",37.371991,-1.22026e+2,"","SUNNYVALE","CA","94085","US"}};
 
 	root=cJSON_CreateArray();
 	for (i=0;i<2;i++)

+ 109 - 0
test_utils.c

@@ -0,0 +1,109 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "cJSON_Utils.h"
+
+int main()
+{
+	int i;
+	/* JSON Pointer tests: */
+	cJSON *root;
+	const char *json="{"
+		"\"foo\": [\"bar\", \"baz\"],"
+		"\"\": 0,"
+		"\"a/b\": 1,"
+		"\"c%d\": 2,"
+		"\"e^f\": 3,"
+		"\"g|h\": 4,"
+		"\"i\\\\j\": 5,"
+		"\"k\\\"l\": 6,"
+		"\" \": 7,"
+		"\"m~n\": 8"
+	"}";
+   
+	const char *tests[12]={"","/foo","/foo/0","/","/a~1b","/c%d","/e^f","/g|h","/i\\j","/k\"l","/ ","/m~0n"};
+   
+	/* JSON Apply Patch tests: */
+	const char *patches[15][3]={
+	{"{ \"foo\": \"bar\"}", "[{ \"op\": \"add\", \"path\": \"/baz\", \"value\": \"qux\" }]","{\"baz\": \"qux\",\"foo\": \"bar\"}"},
+	{"{ \"foo\": [ \"bar\", \"baz\" ] }", "[{ \"op\": \"add\", \"path\": \"/foo/1\", \"value\": \"qux\" }]","{\"foo\": [ \"bar\", \"qux\", \"baz\" ] }"},
+	{"{\"baz\": \"qux\",\"foo\": \"bar\"}"," [{ \"op\": \"remove\", \"path\": \"/baz\" }]","{\"foo\": \"bar\" }"},
+	{"{ \"foo\": [ \"bar\", \"qux\", \"baz\" ] }","[{ \"op\": \"remove\", \"path\": \"/foo/1\" }]","{\"foo\": [ \"bar\", \"baz\" ] }"},
+	{"{ \"baz\": \"qux\",\"foo\": \"bar\"}","[{ \"op\": \"replace\", \"path\": \"/baz\", \"value\": \"boo\" }]","{\"baz\": \"boo\",\"foo\": \"bar\"}"},
+	{"{\"foo\": {\"bar\": \"baz\",\"waldo\": \"fred\"},\"qux\": {\"corge\": \"grault\"}}","[{ \"op\": \"move\", \"from\": \"/foo/waldo\", \"path\": \"/qux/thud\" }]","{\"foo\": {\"bar\": \"baz\"},\"qux\": {\"corge\": \"grault\",\"thud\": \"fred\"}}"},
+	{"{ \"foo\": [ \"all\", \"grass\", \"cows\", \"eat\" ] }","[ { \"op\": \"move\", \"from\": \"/foo/1\", \"path\": \"/foo/3\" }]","{ \"foo\": [ \"all\", \"cows\", \"eat\", \"grass\" ] }"},
+	{"{\"baz\": \"qux\",\"foo\": [ \"a\", 2, \"c\" ]}","[{ \"op\": \"test\", \"path\": \"/baz\", \"value\": \"qux\" },{ \"op\": \"test\", \"path\": \"/foo/1\", \"value\": 2 }]",""},
+	{"{ \"baz\": \"qux\" }","[ { \"op\": \"test\", \"path\": \"/baz\", \"value\": \"bar\" }]",""},
+	{"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/child\", \"value\": { \"grandchild\": { } } }]","{\"foo\": \"bar\",\"child\": {\"grandchild\": {}}}"},
+	{"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/baz\", \"value\": \"qux\", \"xyz\": 123 }]","{\"foo\": \"bar\",\"baz\": \"qux\"}"},
+	{"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/baz/bat\", \"value\": \"qux\" }]",""},
+	{"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": 10}]",""},
+	{"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": \"10\"}]",""},
+	{"{ \"foo\": [\"bar\"] }","[ { \"op\": \"add\", \"path\": \"/foo/-\", \"value\": [\"abc\", \"def\"] }]","{\"foo\": [\"bar\", [\"abc\", \"def\"]] }"}};
+
+	/* Misc tests */
+	int numbers[10]={0,1,2,3,4,5,6,7,8,9};
+	const char *random="QWERTYUIOPASDFGHJKLZXCVBNM";
+	char buf[2]={0,0},*before,*after;
+	cJSON *object,*nums,*num6,*sortme;
+
+
+
+	printf("JSON Pointer Tests\n");
+	root=cJSON_Parse(json);
+	for (i=0;i<12;i++)
+	{
+		char *output=cJSON_Print(cJSONUtils_GetPointer(root,tests[i]));
+		printf("Test %d:\n%s\n\n",i+1,output);
+		free(output);
+	}
+	cJSON_Delete(root);
+
+
+	printf("JSON Apply Patch Tests\n");
+	for (i=0;i<15;i++)
+	{
+		cJSON *object=cJSON_Parse(patches[i][0]);
+		cJSON *patch=cJSON_Parse(patches[i][1]);
+		int err=cJSONUtils_ApplyPatches(object,patch);
+		char *output=cJSON_Print(object);
+		printf("Test %d (err %d):\n%s\n\n",i+1,err,output);
+		free(output);cJSON_Delete(object);cJSON_Delete(patch);
+	}
+
+	/* JSON Generate Patch tests: */
+	printf("JSON Generate Patch Tests\n");
+	for (i=0;i<15;i++)
+	{
+		cJSON *from,*to,*patch;char *out;
+		if (!strlen(patches[i][2])) continue;
+		from=cJSON_Parse(patches[i][0]);
+		to=cJSON_Parse(patches[i][2]);
+		patch=cJSONUtils_GeneratePatches(from,to);
+		out=cJSON_Print(patch);
+		printf("Test %d: (patch: %s):\n%s\n\n",i+1,patches[i][1],out);
+		free(out);cJSON_Delete(from);cJSON_Delete(to);cJSON_Delete(patch);
+	}
+
+	/* Misc tests: */
+	printf("JSON Pointer construct\n");
+	object=cJSON_CreateObject();
+	nums=cJSON_CreateIntArray(numbers,10);
+	num6=cJSON_GetArrayItem(nums,6);
+	cJSON_AddItemToObject(object,"numbers",nums);
+	printf("Pointer: [%s]\n",cJSONUtils_FindPointerFromObjectTo(object,num6));
+	printf("Pointer: [%s]\n",cJSONUtils_FindPointerFromObjectTo(object,nums));
+	printf("Pointer: [%s]\n",cJSONUtils_FindPointerFromObjectTo(object,object));
+
+	/* JSON Sort test: */
+	sortme=cJSON_CreateObject();
+	for (i=0;i<26;i++)
+	{
+		buf[0]=random[i];cJSON_AddItemToObject(sortme,buf,cJSON_CreateNumber(1));
+	}
+	before=cJSON_PrintUnformatted(sortme);
+	cJSONUtils_SortObject(sortme);
+	after=cJSON_PrintUnformatted(sortme);
+	printf("Before: [%s]\nAfter: [%s]\n\n",before,after);
+	free(before);free(after);cJSON_Delete(sortme);		
+}