Patch from Scott McKellar which adds a free-object-pool for jsonObjects, reducing...
[OpenSRF.git] / src / libopensrf / osrf_json_object.c
1 /*
2 Copyright (C) 2006  Georgia Public Library Service 
3 Bill Erickson <billserickson@gmail.com>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 */
15
16 #include <limits.h>
17 #include <opensrf/log.h>
18 #include <opensrf/osrf_json.h>
19 #include <opensrf/osrf_json_utils.h>
20
21 /* cleans up an object if it is morphing another object, also
22  * verifies that the appropriate storage container exists where appropriate */
23 #define JSON_INIT_CLEAR(_obj_, newtype)         \
24         if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
25                 osrfHashFree(_obj_->value.h);                   \
26                 _obj_->value.h = NULL;                                  \
27 } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) {       \
28                 osrfListFree(_obj_->value.l);                   \
29                 _obj_->value.l = NULL;                                  \
30 } else if( _obj_->type == JSON_STRING && newtype != JSON_STRING ) { \
31                 free(_obj_->value.s);                                           \
32                 _obj_->value.s = NULL;                                  \
33 } \
34         _obj_->type = newtype;\
35         if( newtype == JSON_HASH && _obj_->value.h == NULL ) {  \
36                 _obj_->value.h = osrfNewHash();         \
37                 _obj_->value.h->freeItem = _jsonFreeHashItem; \
38 } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) {  \
39                 _obj_->value.l = osrfNewList();         \
40                 _obj_->value.l->freeItem = _jsonFreeListItem;\
41 }
42
43 static int unusedObjCapture = 0;
44 static int unusedObjRelease = 0;
45
46 union unusedObjUnion{
47
48         union unusedObjUnion* next;
49         jsonObject obj;
50 };
51 typedef union unusedObjUnion unusedObj;
52
53 // We maintain a free list of jsonObjects that are available
54 // for use, in order to reduce the churning through
55 // malloc() and free().
56
57 static unusedObj* freeObjList = NULL;
58
59 static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf );
60
61 /**
62  * Return all unused jsonObjects to the heap
63  * @return Nothing
64  */
65 void jsonObjectFreeUnused( void ) {
66
67         unusedObj* temp;
68         while( freeObjList ) {
69                 temp = freeObjList->next;
70                 free( freeObjList );
71                 freeObjList = temp;
72         }
73 }
74
75 jsonObject* jsonNewObject(const char* data) {
76
77         jsonObject* o;
78
79         if( freeObjList ) {
80                 o = (jsonObject*) freeObjList;
81                 freeObjList = freeObjList->next;
82         unusedObjRelease++;
83         }
84         else
85                 OSRF_MALLOC( o, sizeof(jsonObject) );
86
87         o->size = 0;
88         o->classname = NULL;
89         o->parent = NULL;
90
91         if(data) {
92                 o->type = JSON_STRING;
93                 o->value.s = strdup(data);
94         } else {
95                 o->type = JSON_NULL;
96                 o->value.s = NULL;
97         }
98
99         return o;
100 }
101
102 jsonObject* jsonNewObjectFmt(const char* data, ...) {
103
104         jsonObject* o;
105
106         if( freeObjList ) {
107                 o = (jsonObject*) freeObjList;
108                 freeObjList = freeObjList->next;
109         }
110         else
111                 OSRF_MALLOC( o, sizeof(jsonObject) );
112
113         o->size = 0;
114         o->classname = NULL;
115         o->parent = NULL;
116
117         if(data) {
118                 VA_LIST_TO_STRING(data);
119                 o->type = JSON_STRING;
120                 o->value.s = strdup(VA_BUF);
121         }
122         else {
123                 o->type = JSON_NULL;
124                 o->value.s = NULL;
125         }
126         
127         return o;
128 }
129
130 jsonObject* jsonNewNumberObject( double num ) {
131         jsonObject* o = jsonNewObject(NULL);
132         o->type = JSON_NUMBER;
133         o->value.n = num;
134         return o;
135 }
136
137 jsonObject* jsonNewBoolObject(int val) {
138     jsonObject* o = jsonNewObject(NULL);
139     o->type = JSON_BOOL;
140     jsonSetBool(o, val);
141     return o;
142 }
143
144 jsonObject* jsonNewObjectType(int type) {
145         jsonObject* o = jsonNewObject(NULL);
146         o->type = type;
147         return o;
148 }
149
150 void jsonObjectFree( jsonObject* o ) {
151
152         if(!o || o->parent) return;
153         free(o->classname);
154
155         switch(o->type) {
156                 case JSON_HASH          : osrfHashFree(o->value.h); break;
157                 case JSON_ARRAY : osrfListFree(o->value.l); break;
158                 case JSON_STRING        : free(o->value.s); break;
159         }
160
161         // Stick the old jsonObject onto a free list
162         // for potential reuse
163         
164         unusedObj* unused = (unusedObj*) o;
165         unused->next = freeObjList;
166         freeObjList = unused;
167
168     unusedObjCapture++;
169     if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
170         osrfLogDebug( OSRF_LOG_MARK, "Reusable objects captured: %d, Objects reused: %d", unusedObjCapture, unusedObjRelease );
171 }
172
173 static void _jsonFreeHashItem(char* key, void* item){
174         if(!item) return;
175         jsonObject* o = (jsonObject*) item;
176         o->parent = NULL; /* detach the item */
177         jsonObjectFree(o);
178 }
179 static void _jsonFreeListItem(void* item){
180         if(!item) return;
181         jsonObject* o = (jsonObject*) item;
182         o->parent = NULL; /* detach the item */
183         jsonObjectFree(o);
184 }
185
186 void jsonSetBool(jsonObject* bl, int val) {
187     if(!bl) return;
188     JSON_INIT_CLEAR(bl, JSON_BOOL);
189     bl->value.b = val;
190 }
191
192 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
193     if(!o) return -1;
194     if(!newo) newo = jsonNewObject(NULL);
195         JSON_INIT_CLEAR(o, JSON_ARRAY);
196         newo->parent = o;
197         osrfListPush( o->value.l, newo );
198         o->size = o->value.l->size;
199         return o->size;
200 }
201
202 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
203     if(!dest) return -1;
204     if(!newObj) newObj = jsonNewObject(NULL);
205         JSON_INIT_CLEAR(dest, JSON_ARRAY);
206         newObj->parent = dest;
207         osrfListSet( dest->value.l, newObj, index );
208         dest->size = dest->value.l->size;
209         return dest->value.l->size;
210 }
211
212 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
213     if(!o) return -1;
214     if(!newo) newo = jsonNewObject(NULL);
215         JSON_INIT_CLEAR(o, JSON_HASH);
216         newo->parent = o;
217         osrfHashSet( o->value.h, newo, key );
218         o->size = o->value.h->size;
219         return o->size;
220 }
221
222 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
223         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
224         return osrfHashGet( obj->value.h, key);
225 }
226
227 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
228         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
229         return osrfHashGet( obj->value.h, key);
230 }
231
232 char* jsonObjectToJSON( const jsonObject* obj ) {
233         jsonObject* obj2 = jsonObjectEncodeClass( obj );
234         char* json = jsonObjectToJSONRaw(obj2);
235         jsonObjectFree(obj2);
236         return json;
237 }
238
239 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
240         if(!obj) return NULL;
241         growing_buffer* buf = buffer_init(32);
242         add_json_to_buffer( obj, buf );
243         return buffer_release( buf );
244 }
245
246 static void add_json_to_buffer( const jsonObject* obj, growing_buffer * buf ) {
247
248         switch(obj->type) {
249
250                 case JSON_BOOL :
251                         if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); 
252                         else OSRF_BUFFER_ADD(buf, "false"); 
253                         break;
254
255                 case JSON_NUMBER: {
256                         double x = obj->value.n;
257                         if( x <= INT_MAX && x >= INT_MIN && x == (int) x ) {
258                                 INT_TO_STRING((int)x);
259                                 OSRF_BUFFER_ADD(buf, INTSTR);
260
261                         } else {
262                                 DOUBLE_TO_STRING(x);
263                                 OSRF_BUFFER_ADD(buf, DOUBLESTR);
264                         }
265                         break;
266                 }
267
268                 case JSON_NULL:
269                         OSRF_BUFFER_ADD(buf, "null");
270                         break;
271
272                 case JSON_STRING:
273                         OSRF_BUFFER_ADD_CHAR(buf, '"');
274                         char* data = obj->value.s;
275                         int len = strlen(data);
276                         
277                         char* output = uescape(data, len, 1);
278                         OSRF_BUFFER_ADD(buf, output);
279                         free(output);
280                         OSRF_BUFFER_ADD_CHAR(buf, '"');
281                         break;
282                         
283                 case JSON_ARRAY: {
284                         OSRF_BUFFER_ADD_CHAR(buf, '[');
285                         if( obj->value.l ) {
286                                 int i;
287                                 for( i = 0; i != obj->value.l->size; i++ ) {
288                                         if(i > 0) OSRF_BUFFER_ADD(buf, ",");
289                                         add_json_to_buffer( OSRF_LIST_GET_INDEX(obj->value.l, i), buf );
290                                 }
291                         }
292                         OSRF_BUFFER_ADD_CHAR(buf, ']');
293                         break;
294                 }
295
296                 case JSON_HASH: {
297
298                         OSRF_BUFFER_ADD_CHAR(buf, '{');
299                         osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
300                         jsonObject* item;
301                         int i = 0;
302
303                         while( (item = osrfHashIteratorNext(itr)) ) {
304                                 if(i++ > 0) OSRF_BUFFER_ADD(buf, ",");
305                                 buffer_fadd(buf, "\"%s\":", itr->current);
306                                 add_json_to_buffer( item, buf );
307                         }
308
309                         osrfHashIteratorFree(itr);
310                         OSRF_BUFFER_ADD_CHAR(buf, '}');
311                         break;
312                 }
313         }
314 }
315
316
317 jsonIterator* jsonNewIterator(const jsonObject* obj) {
318         if(!obj) return NULL;
319         jsonIterator* itr;
320         OSRF_MALLOC(itr, sizeof(jsonIterator));
321
322         itr->obj                = (jsonObject*) obj;
323         itr->index      = 0;
324         itr->key                = NULL;
325
326         if( obj->type == JSON_HASH )
327                 itr->hashItr = osrfNewHashIterator(obj->value.h);
328         
329         return itr;
330 }
331
332 void jsonIteratorFree(jsonIterator* itr) {
333         if(!itr) return;
334         free(itr->key);
335         osrfHashIteratorFree(itr->hashItr);
336         free(itr);
337 }
338
339 jsonObject* jsonIteratorNext(jsonIterator* itr) {
340         if(!(itr && itr->obj)) return NULL;
341         if( itr->obj->type == JSON_HASH ) {
342                 if(!itr->hashItr) return NULL;
343                 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
344                 free(itr->key);
345                 itr->key = strdup(itr->hashItr->current);
346                 return item;
347         } else {
348                 return jsonObjectGetIndex( itr->obj, itr->index++ );
349         }
350 }
351
352 int jsonIteratorHasNext(const jsonIterator* itr) {
353         if(!(itr && itr->obj)) return 0;
354         if( itr->obj->type == JSON_HASH )
355                 return osrfHashIteratorHasNext( itr->hashItr );
356         return (itr->index < itr->obj->size) ? 1 : 0;
357 }
358
359 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
360         if(!obj) return NULL;
361         return (obj->type == JSON_ARRAY) ? 
362         (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
363 }
364
365
366
367 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
368         if( dest && dest->type == JSON_ARRAY ) {
369                 osrfListRemove(dest->value.l, index);
370                 return dest->value.l->size;
371         }
372         return -1;
373 }
374
375
376 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
377         if( dest && key && dest->type == JSON_HASH ) {
378                 osrfHashRemove(dest->value.h, key);
379                 return 1;
380         }
381         return -1;
382 }
383
384 char* jsonObjectGetString(const jsonObject* obj) {
385         return (obj && obj->type == JSON_STRING) ? obj->value.s : NULL;
386 }
387
388 double jsonObjectGetNumber( const jsonObject* obj ) {
389         return (obj && obj->type == JSON_NUMBER) ? obj->value.n : 0;
390 }
391
392 void jsonObjectSetString(jsonObject* dest, const char* string) {
393         if(!(dest && string)) return;
394         JSON_INIT_CLEAR(dest, JSON_STRING);
395         free(dest->value.s);
396         dest->value.s = strdup(string);
397 }
398
399 void jsonObjectSetNumber(jsonObject* dest, double num) {
400         if(!dest) return;
401         JSON_INIT_CLEAR(dest, JSON_NUMBER);
402         dest->value.n = num;
403 }
404
405 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
406         if(!(dest && classname)) return;
407         free(dest->classname);
408         dest->classname = strdup(classname);
409 }
410 const char* jsonObjectGetClass(const jsonObject* dest) {
411     if(!dest) return NULL;
412     return dest->classname;
413 }
414
415 jsonObject* jsonObjectClone( const jsonObject* o ) {
416     if(!o) return jsonNewObject(NULL);
417
418     int i;
419     jsonObject* arr; 
420     jsonObject* hash; 
421     jsonIterator* itr;
422     jsonObject* tmp;
423     jsonObject* result = NULL;
424
425     switch(o->type) {
426         case JSON_NULL:
427             result = jsonNewObject(NULL);
428             break;
429         case JSON_STRING:
430             result = jsonNewObject(jsonObjectGetString(o));
431             break;
432         case JSON_NUMBER:
433             result = jsonNewNumberObject(jsonObjectGetNumber(o));
434             break;
435         case JSON_BOOL:
436             result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
437             break;
438         case JSON_ARRAY:
439             arr = jsonNewObject(NULL);
440             arr->type = JSON_ARRAY;
441             for(i=0; i < o->size; i++) 
442                 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
443             result = arr;
444             break;
445         case JSON_HASH:
446             hash = jsonNewObject(NULL);
447             hash->type = JSON_HASH;
448             itr = jsonNewIterator(o);
449             while( (tmp = jsonIteratorNext(itr)) )
450                 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
451             jsonIteratorFree(itr);
452             result = hash;
453             break;
454     }
455
456     jsonObjectSetClass(result, jsonObjectGetClass(o));
457     return result;
458 }
459
460 int jsonBoolIsTrue( const jsonObject* boolObj ) {
461     if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
462         return 1;
463     return 0;
464 }
465
466
467 char* jsonObjectToSimpleString( const jsonObject* o ) {
468         if(!o) return NULL;
469
470         char* value = NULL;
471
472         switch( o->type ) {
473
474                 case JSON_NUMBER: {
475
476                         if( o->value.n == (int) o->value.n ) {
477                                 INT_TO_STRING((int) o->value.n);        
478                                 value = strdup(INTSTR);
479
480                         } else {
481                                 DOUBLE_TO_STRING(o->value.n);
482                                 value = strdup(DOUBLESTR);
483                         }
484
485                         break;
486                 }
487
488                 case JSON_STRING:
489                         value = strdup(o->value.s);
490         }
491
492         return value;
493 }
494
495