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