This patch is a rewrite of the jsonObjectToJSON and jsonObjectToJSONRaw
functions. It is dependent on my previous patch to utils.c and utils.h,
adding the new buffer_append_uescape function.
One purpose is to replace a call to the uescape function with a call to
the faster buffer_append_uescape function. The other purpose is to
introduce a faster way to translate a jsonObject into a string.
(Also in one spot I broke up a very long string literal into several
smaller pieces so that it wouldn't wrap around in the editor.)
In the existing jsonObjectToJSON function, we receive a pointer to a
jsonObject and return a string of JSON. However we don't translate the
original jsonObject directly. Instead, we create a modified clone of the
original, inserting an additional JSON_HASH node wherever we find a
classname. Then we translate the clone, and finally destroy it.
It always struck me as an egregious waste to create and destroy a whole
parallel object just so that we could turn it into a string. So I looked
for a way to eliminate the cloning.
The result is a modification of add_json_to_buffer(), a local function
that recursively traverses and translates the jsonObject. When it sees a
classname (and it has been asked to expand classnames), the new version
inserts additional gibberish into the output text and then continues the
traversal, without modifying or copying the original jsonObject at all.
In my benchmark, this new approach was faster than the original by a
factor of about 5. When I combined this change with the use of the new
buffer_append_uencode function, it was faster by a factor of about 7.2.
The benchmark used a moderately complex jsonObject about 5 or 6 levels
deep, with both hashes and arrays, with classnames at several levels.
The performance gain will no doubt depend on the contents of the
jsonObject,but I haven't tried to isolate the variables.
The new version is a bit trickier and harder to read than the old. In my
opinion the speedup is worth the obscurity, because a lot of places in
Evergreen will benefit.
----------------
With this change, the jsonObjectEncodeClass function, which had been
called only from jsonObjectToJSON, is no longer called from anywhere.
It's the function that created the modified copy of the original
jsonObject. I see no reason to keep it around. After a decent interval,
if no one objects I shall submit a patch to eliminate it.
Likewise the jsonObjectToJSONRaw function is no longer called from
anywhere. It translates a jsonObject to JSON without expanding classnames.
In this rewritten version it is identical to jsonObjectToJSON except that
it calls add_json_to_buffer() with a different parameter value.
If we eliminate jsonObjectToJSONRaw, I can simplify add_json_to_buffer()
a bit. However we might someday want to be able to translate a jsonObject
without expanding classnames. If nobody minds, I'll leave this function
where it is.
Scott McKellar
http://home.swbell.net/mck9/ct/
Developer's Certificate of Origin 1.1 By making a contribution to
this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license indicated
in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source license
and I have the right under that license to submit that work with
modifications, whether created in whole or in part by me, under the
same open source license (unless I am permitted to submit under a
different license), as indicated in the file; or
(c) The contribution was provided directly to me by some other person
who certified (a), (b) or (c) and I have not modified it; and
(d) In the case of each of (a), (b), or (c), I understand and agree
that this project and the contribution are public and that a record
of the contribution (including all personal information I submit
with it, including my sign-off) is maintained indefinitely and may
be redistributed consistent with this project or the open source
license indicated in the file.
*** ./trunk/src/libopensrf/osrf_json_object.c 2008-11-17 16:12:14.000000000 -0600
--- ./trunk-mod/src/libopensrf/osrf_json_object.c 2008-11-17 22:25:01.000000000 -0600
***************
*** 61,67 ****
static unusedObj* freeObjList = NULL;
! static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf );
/**
* Return all unused jsonObjects to the heap
--- 61,68 ----
static unusedObj* freeObjList = NULL;
! static void add_json_to_buffer( const jsonObject* obj,
! growing_buffer * buf, int do_classname, int second_pass );
/**
* Return all unused jsonObjects to the heap
***************
*** 194,200 ****
unusedObjCapture++;
currentListLen++;
if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
! osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, Reusable objects captured: %d, Objects reused: %d, Current List Length: %d", mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
}
static void _jsonFreeHashItem(char* key, void* item){
--- 195,204 ----
unusedObjCapture++;
currentListLen++;
if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
! osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, "
! "Reusable objects captured: %d, Objects reused: %d, "
! "Current List Length: %d",
! mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
}
static void _jsonFreeHashItem(char* key, void* item){
***************
*** 256,276 ****
return osrfHashGet( obj->value.h, key);
}
! char* jsonObjectToJSON( const jsonObject* obj ) {
! jsonObject* obj2 = jsonObjectEncodeClass( obj );
! char* json = jsonObjectToJSONRaw(obj2);
! jsonObjectFree(obj2);
! return json;
! }
!
! char* jsonObjectToJSONRaw( const jsonObject* obj ) {
! if(!obj) return NULL;
! growing_buffer* buf = buffer_init(32);
! add_json_to_buffer( obj, buf );
! return buffer_release( buf );
! }
! static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf ) {
switch(obj->type) {
--- 260,303 ----
return osrfHashGet( obj->value.h, key);
}
! /**
! * Recursively traverse a jsonObject, formatting it into a JSON string.
! *
! * The last two parameters are booleans.
! *
! * If do_classname is true, examine each node for a classname, and if you
! * find one, pretend that the node is under an extra layer of JSON_HASH, with
! * JSON_CLASS_KEY and JSON_DATA_KEY as keys.
! *
! * second_pass should always be false except for some recursive calls. It
! * is used when expanding classnames, to distinguish between the first and
! * second passes through a given node.
! *
! * @return Nothing
! */
! static void add_json_to_buffer( const jsonObject* obj,
! growing_buffer * buf, int do_classname, int second_pass ) {
! if( obj->classname && do_classname )
! {
! if( second_pass )
! second_pass = 0;
! else
! {
! // Pretend we see an extra layer of JSON_HASH
!
! OSRF_BUFFER_ADD( buf, "{\"" );
! OSRF_BUFFER_ADD( buf, JSON_CLASS_KEY );
! OSRF_BUFFER_ADD( buf, "\":\"" );
! OSRF_BUFFER_ADD( buf, obj->classname );
! OSRF_BUFFER_ADD( buf, "\",\"" );
! OSRF_BUFFER_ADD( buf, JSON_DATA_KEY );
! OSRF_BUFFER_ADD( buf, "\":" );
! add_json_to_buffer( obj, buf, 1, 1 );
! buffer_add_char( buf, '}' );
! return;
! }
! }
switch(obj->type) {
***************
*** 279,289 ****
else OSRF_BUFFER_ADD(buf, "false");
break;
! case JSON_NUMBER: {
! if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
! else OSRF_BUFFER_ADD_CHAR( buf, '0' );
! break;
! }
case JSON_NULL:
OSRF_BUFFER_ADD(buf, "null");
--- 306,316 ----
else OSRF_BUFFER_ADD(buf, "false");
break;
! case JSON_NUMBER: {
! if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
! else OSRF_BUFFER_ADD_CHAR( buf, '0' );
! break;
! }
case JSON_NULL:
OSRF_BUFFER_ADD(buf, "null");
***************
*** 291,302 ****
case JSON_STRING:
OSRF_BUFFER_ADD_CHAR(buf, '"');
! char* data = obj->value.s;
! int len = strlen(data);
!
! char* output = uescape(data, len, 1);
! OSRF_BUFFER_ADD(buf, output);
! free(output);
OSRF_BUFFER_ADD_CHAR(buf, '"');
break;
--- 318,324 ----
case JSON_STRING:
OSRF_BUFFER_ADD_CHAR(buf, '"');
! buffer_append_uescape(buf, obj->value.s);
OSRF_BUFFER_ADD_CHAR(buf, '"');
break;
***************
*** 306,312 ****
int i;
for( i = 0; i != obj->value.l->size; i++ ) {
if(i > 0) OSRF_BUFFER_ADD(buf, ",");
! add_json_to_buffer( OSRF_LIST_GET_INDEX(obj->value.l, i), buf );
}
}
OSRF_BUFFER_ADD_CHAR(buf, ']');
--- 328,335 ----
int i;
for( i = 0; i != obj->value.l->size; i++ ) {
if(i > 0) OSRF_BUFFER_ADD(buf, ",");
! add_json_to_buffer(
! OSRF_LIST_GET_INDEX(obj->value.l, i), buf, do_classname, second_pass );
}
}
OSRF_BUFFER_ADD_CHAR(buf, ']');
***************
*** 314,329 ****
}
case JSON_HASH: {
!
OSRF_BUFFER_ADD_CHAR(buf, '{');
osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
jsonObject* item;
int i = 0;
while( (item = osrfHashIteratorNext(itr)) ) {
! if(i++ > 0) OSRF_BUFFER_ADD(buf, ",");
! buffer_fadd(buf, "\"%s\":", osrfHashIteratorKey(itr));
! add_json_to_buffer( item, buf );
}
osrfHashIteratorFree(itr);
--- 337,354 ----
}
case JSON_HASH: {
!
OSRF_BUFFER_ADD_CHAR(buf, '{');
osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
jsonObject* item;
int i = 0;
while( (item = osrfHashIteratorNext(itr)) ) {
! if(i++ > 0) OSRF_BUFFER_ADD_CHAR(buf, ',');
! OSRF_BUFFER_ADD_CHAR(buf, '"');
! OSRF_BUFFER_ADD(buf, osrfHashIteratorKey(itr));
! OSRF_BUFFER_ADD(buf, "\":");
! add_json_to_buffer( item, buf, do_classname, second_pass );
}
osrfHashIteratorFree(itr);
***************
*** 333,338 ****
--- 358,376 ----
}
}
+ char* jsonObjectToJSONRaw( const jsonObject* obj ) {
+ if(!obj) return NULL;
+ growing_buffer* buf = buffer_init(32);
+ add_json_to_buffer( obj, buf, 0, 0 );
+ return buffer_release( buf );
+ }
+
+ char* jsonObjectToJSON( const jsonObject* obj ) {
+ if(!obj) return NULL;
+ growing_buffer* buf = buffer_init(32);
+ add_json_to_buffer( obj, buf, 1, 0 );
+ return buffer_release( buf );
+ }
jsonIterator* jsonNewIterator(const jsonObject* obj) {
if(!obj) return NULL;