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