Переглянути джерело

Added buffered printing methods for faster printing.

git-svn-id: svn://svn.code.sf.net/p/cjson/code@61 e3330c51-1366-4df0-8b21-3ccf24e3d50e
Dave Gamble 10 роки тому
батько
коміт
d9fc81e6c8
2 змінених файлів з 246 додано та 114 видалено
  1. 242 114
      cJSON.c
  2. 4 0
      cJSON.h

+ 242 - 114
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,28 +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)
 	{
-		str=(char*)cJSON_malloc(21);	/* 2^64+1 can be represented in 21 chars. */
-		if (str)
-		{
-			int i,j,t;
- 
-			if (d<0) t=-d; else t=d;
-			for (i=0;t>0;t/=10) str[i++]='0'+(t%10);
-			if (d<0) str[i++]='-';
-			str[i--]=0;
-			for (j=0;j<i;i--,j++) {t=str[j];str[j]=str[i];str[i]=t;}	// reverse the string.
-		}
+		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)
+	{
+		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);
@@ -231,15 +259,16 @@ static int escapable[256]={	1,1,1,1,	1,1,1,1,	1,1,1,1,	1,1,1,1,	1,1,1,1,	1,1,1,1
 							0,0,0,0,	0,0,0,0,	0,0,0,0,	0,0,0,0,	0,0,0,0,	0,0,0,0,	0,0,0,0,	0,0,0,0};
 
 /* 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,flag=0;unsigned char token;
-
+	
 	ptr=str;while (*ptr) flag|=escapable[*ptr++];
 	if (!flag)
 	{
 		len=ptr-str;
-		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;*ptr2++='\"';
 		strcpy(ptr2,str);
@@ -248,10 +277,18 @@ static char *print_string_ptr(const char *str)
 		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;
@@ -279,15 +316,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;}
@@ -312,8 +349,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)
@@ -331,19 +379,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)
 	{
-		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=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
+	{
+		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;
 }
@@ -377,7 +441,7 @@ 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;
@@ -390,48 +454,69 @@ static char *print_array(cJSON *item,int depth,int fmt)
 	/* 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
 	{
+		/* 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]);
+			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;	
 }
 
@@ -472,7 +557,7 @@ 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;
@@ -484,59 +569,101 @@ static char *print_object(cJSON *item,int depth,int fmt)
 	/* 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)
-	{
-		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;
-	}
-	
-	/* Try to allocate the output string */
-	if (!fail) out=(char*)cJSON_malloc(len);
-	if (!out) fail=1;
-
-	/* Handle failure */
-	if (fail)
+	if (p)
 	{
-		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: */
+		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;
 	}
-	
-	/* Compose the output: */
-	*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
-	for (i=0;i<numentries;i++)
+	else
 	{
-		if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
+		/* 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)
+		{
+			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]);
+			*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);
+		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;	
 }
 
@@ -553,6 +680,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));}
 
@@ -627,4 +755,4 @@ void cJSON_Minify(char *json)
 		else *into++=*json++;			// All other characters.
 	}
 	*into=0;	// and null-terminate.
-}
+}

+ 4 - 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);