]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/libopensrf/osrf_json_object.c
LP#1666706: add --with-websockets-port configure option
[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 /**
17         @file osrf_json_object.c
18         @brief Implementation of the basic operations involving jsonObjects.
19
20         As a performance tweak: we maintain a free list of jsonObjects that have already
21         been allocated but are not currently in use.  When we need to create a jsonObject,
22         we can take one from the free list, if one is available, instead of calling
23         malloc().  Likewise when we free a jsonObject, we can stick it on the free list
24         for potential reuse instead of calling free().
25 */
26
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <opensrf/log.h>
32 #include <opensrf/osrf_json.h>
33 #include <opensrf/osrf_utf8.h>
34
35 /* cleans up an object if it is morphing another object, also
36  * verifies that the appropriate storage container exists where appropriate */
37 /**
38         @brief Coerce a jsonObj into a specified type, if it isn't already of that type.
39         @param _obj_ Pointer to the jsonObject to be coerced.
40         @param newtype The desired type.
41
42         If the old type and the new type don't match, discard and free the old contents.
43
44         If the old type is JSON_STRING or JSON_NUMBER, free the internal string buffer even
45         if the type is not changing.
46
47         If the new type is JSON_ARRAY or JSON_HASH, make sure there is an osrfList or osrfHash
48         in the jsonObject, respectively.
49 */
50 #define JSON_INIT_CLEAR(_obj_, newtype)         \
51         if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
52                 osrfHashFree(_obj_->value.h);                   \
53                 _obj_->value.h = NULL;                                  \
54         } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) {       \
55                 osrfListFree(_obj_->value.l);                   \
56                 _obj_->value.l = NULL;                                  \
57         } else if( _obj_->type == JSON_STRING || _obj_->type == JSON_NUMBER ) { \
58                 free(_obj_->value.s);                                   \
59                 _obj_->value.s = NULL;                                  \
60         } else if( _obj_->type == JSON_BOOL && newtype != JSON_BOOL ) { \
61                 _obj_->value.l = NULL;                                  \
62         } \
63         _obj_->type = newtype; \
64         if( newtype == JSON_HASH && _obj_->value.h == NULL ) {  \
65                 _obj_->value.h = osrfNewHash();         \
66                 osrfHashSetCallback( _obj_->value.h, _jsonFreeHashItem ); \
67         } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) {  \
68                 _obj_->value.l = osrfNewList();         \
69                 _obj_->value.l->freeItem = _jsonFreeListItem;\
70         }
71
72 /** Count of the times we put a freed jsonObject on the free list instead of calling free() */
73 static int unusedObjCapture = 0;
74 /** Count of the times we reused a jsonObject from the free list instead of calling malloc() */
75 static int unusedObjRelease = 0;
76 /** Count of the times we allocated a jsonObject with malloc() */
77 static int mallocObjCreate = 0;
78 /** Number of unused jsonObjects currently on the free list */
79 static int currentListLen = 0;
80
81 /**
82         Union overlaying a jsonObject with a pointer.  When the jsonObject is not in use as a
83         jsonObject, we use the overlaid pointer to maintain a linked list of unused jsonObjects.
84 */
85 union unusedObjUnion{
86
87         union unusedObjUnion* next;
88         jsonObject obj;
89 };
90 typedef union unusedObjUnion unusedObj;
91
92 /** Pointer to the head of the free list */
93 static unusedObj* freeObjList = NULL;
94
95 static void add_json_to_buffer( const jsonObject* obj,
96         growing_buffer * buf, int do_classname, int second_pass );
97
98 /**
99         @brief Return all jsonObjects in the free list to the heap.
100
101         Reclaims memory occupied by unused jsonObjects in the free list.  It is never really
102         necessary to call this function, assuming that we don't run out of memory.  However
103         it might be worth calling if we have built and destroyed a lot of jsonObjects that
104         we don't expect to need again, in order to reduce our memory footprint.
105 */
106 void jsonObjectFreeUnused( void ) {
107
108         unusedObj* temp;
109         while( freeObjList ) {
110                 temp = freeObjList->next;
111                 free( freeObjList );
112                 freeObjList = temp;
113         }
114 }
115
116 /**
117         @brief Create a new jsonObject, optionally containing a string.
118         @param data Pointer to a string to be stored in the jsonObject; may be NULL.
119         @return Pointer to a newly allocate jsonObject.
120
121         If @a data is NULL, create a jsonObject of type JSON_NULL.  Otherwise create
122         a jsonObject of type JSON_STRING, containing the specified string.
123
124         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
125 */
126 jsonObject* jsonNewObject(const char* data) {
127
128         jsonObject* o;
129
130         // Allocate a jsonObject; from the free list if possible,
131         // or from the heap if necessary.
132         if( freeObjList ) {
133                 o = (jsonObject*) freeObjList;
134                 freeObjList = freeObjList->next;
135         unusedObjRelease++;
136         currentListLen--;
137         } else {
138                 OSRF_MALLOC( o, sizeof(jsonObject) );
139         mallocObjCreate++;
140     }
141
142         o->size = 0;
143         o->classname = NULL;
144         o->parent = NULL;
145
146         if(data) {
147                 o->type = JSON_STRING;
148                 o->value.s = strdup(data);
149         } else {
150                 o->type = JSON_NULL;
151                 o->value.s = NULL;
152         }
153
154         return o;
155 }
156
157 /**
158         @brief Create a jsonObject, optionally containing a formatted string.
159         @param data Pointer to a printf-style format string; may be NULL.  Subsequent parameters,
160         if any, will be formatted and inserted into the resulting string.
161         @return Pointer to a newly created jsonObject.
162
163         If @a data is NULL, create a jsonObject of type JSON_NULL, and ignore any extra parameters.
164         Otherwise create a jsonObject of type JSON_STRING, containing the formatted string.
165
166         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
167  */
168 jsonObject* jsonNewObjectFmt(const char* data, ...) {
169
170         jsonObject* o;
171
172         if( freeObjList ) {
173                 o = (jsonObject*) freeObjList;
174                 freeObjList = freeObjList->next;
175         unusedObjRelease++;
176         currentListLen--;
177         } else {
178                 OSRF_MALLOC( o, sizeof(jsonObject) );
179         mallocObjCreate++;
180     }
181
182         o->size = 0;
183         o->classname = NULL;
184         o->parent = NULL;
185
186         if(data) {
187                 VA_LIST_TO_STRING(data);
188                 o->type = JSON_STRING;
189                 o->value.s = strdup(VA_BUF);
190         }
191         else {
192                 o->type = JSON_NULL;
193                 o->value.s = NULL;
194         }
195         
196         return o;
197 }
198
199 /**
200         @brief Create a new jsonObject of type JSON_NUMBER.
201         @param num The number to store in the jsonObject.
202         @return Pointer to the newly created jsonObject.
203
204         The number is stored internally as a character string, as formatted by
205         doubleToString().
206
207         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
208 */
209 jsonObject* jsonNewNumberObject( double num ) {
210         jsonObject* o = jsonNewObject(NULL);
211         o->type = JSON_NUMBER;
212         o->value.s = doubleToString( num );
213         return o;
214 }
215
216 /**
217         @brief Create a new jsonObject of type JSON_NUMBER from a numeric string.
218         @param numstr Pointer to a numeric character string.
219         @return Pointer to a newly created jsonObject containing the numeric value, or NULL
220                 if the string is not numeric.
221
222         The jsonIsNumeric() function determines whether the input string is numeric.
223
224         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
225 */
226 jsonObject* jsonNewNumberStringObject( const char* numstr ) {
227         if( !numstr )
228                 numstr = "0";
229         else if( !jsonIsNumeric( numstr ) )
230                 return NULL;
231
232         jsonObject* o = jsonNewObject(NULL);
233         o->type = JSON_NUMBER;
234         o->value.s = strdup( numstr );
235         return o;
236 }
237
238 /**
239         @brief Create a new jsonObject of type JSON_BOOL, with a specified boolean value.
240         @param val An int used as a boolean value.
241         @return Pointer to the new jsonObject.
242
243         Zero represents false, and non-zero represents true, according to the usual convention.
244         In practice the value of @a val is stored unchanged, but the calling code should not try to
245         take advantage of that fact, because future versions may behave differently.
246
247         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
248 */
249 jsonObject* jsonNewBoolObject(int val) {
250     jsonObject* o = jsonNewObject(NULL);
251     o->type = JSON_BOOL;
252     jsonSetBool(o, val);
253     return o;
254 }
255
256 /**
257         @brief Create a new jsonObject of a specified type, with a default value.
258         @param type One of the 6 JSON types, as specified by the JSON_* macros.
259         @return Pointer to the new jsonObject.
260
261         An invalid type parameter will go unnoticed, and may lead to unpleasantness.
262
263         The default value is equivalent to an empty string (for a JSON_STRING), zero (for a
264         JSON_NUMBER), an empty hash (for a JSON_HASH), an empty array (for a JSON_ARRAY), or
265         false (for a JSON_BOOL).  A JSON_NULL, of course, has no value, but can still be
266         useful.
267
268         The calling code is responsible for freeing the jsonObject by calling jsonObjectFree().
269 */
270 jsonObject* jsonNewObjectType(int type) {
271         jsonObject* o = jsonNewObject(NULL);
272         o->type = type;
273         if( JSON_BOOL == type )
274                 o->value.b = 0;
275         return o;
276 }
277
278 /**
279         @brief Free a jsonObject and everything in it.
280         @param o Pointer to the jsonObject to be freed.
281
282         Any jsonObjects stored inside the jsonObject (in hashes or arrays) will be freed as
283         well, and so one, recursively.
284 */
285 void jsonObjectFree( jsonObject* o ) {
286
287         if(!o || o->parent) return;
288         free(o->classname);
289
290         switch(o->type) {
291                 case JSON_HASH          : osrfHashFree(o->value.h); break;
292                 case JSON_ARRAY : osrfListFree(o->value.l); break;
293                 case JSON_STRING        : free(o->value.s); break;
294                 case JSON_NUMBER        : free(o->value.s); break;
295         }
296
297         // Stick the old jsonObject onto a free list
298         // for potential reuse
299         
300         unusedObj* unused = (unusedObj*) o;
301         unused->next = freeObjList;
302         freeObjList = unused;
303
304     unusedObjCapture++;
305     currentListLen++;
306     if (unusedObjCapture > 1 && !(unusedObjCapture % 1000))
307         osrfLogDebug( OSRF_LOG_MARK, "Objects malloc()'d: %d, "
308                         "Reusable objects captured: %d, Objects reused: %d, "
309                         "Current List Length: %d",
310                         mallocObjCreate, unusedObjCapture, unusedObjRelease, currentListLen );
311 }
312
313 /**
314         @brief Free a jsonObject through a void pointer.
315         @param key Not used.
316         @param item Pointer to the jsonObject to be freed, cast to a void pointer.
317
318         This function is a callback for freeing jsonObjects stored in an osrfHash.
319 */
320 static void _jsonFreeHashItem(char* key, void* item){
321         if(!item) return;
322         jsonObject* o = (jsonObject*) item;
323         o->parent = NULL; /* detach the item */
324         jsonObjectFree(o);
325 }
326
327 /**
328         @brief Free a jsonObject through a void pointer.
329         @param item Pointer to the jsonObject to be freed, cast to a void pointer.
330
331         This function is a callback for freeing jsonObjects stored in an osrfList.
332  */
333 static void _jsonFreeListItem(void* item){
334         if(!item) return;
335         jsonObject* o = (jsonObject*) item;
336         o->parent = NULL; /* detach the item */
337         jsonObjectFree(o);
338 }
339
340 /**
341         @brief Assign a boolean value to a jsonObject of type JSON_BOOL.
342         @param bl Pointer to the jsonObject.
343         @param val The boolean value to be applied, encoded as an int.
344
345         If the jsonObject is not already of type JSON_BOOL, it is converted to one, and
346         any previous contents are freed.
347
348         Zero represents false, and non-zero represents true, according to the usual convention.
349         In practice the value of @a val is stored unchanged, but the calling code should not try to
350         take advantage of that fact, because future versions may behave differently.
351 */
352 void jsonSetBool(jsonObject* bl, int val) {
353     if(!bl) return;
354     JSON_INIT_CLEAR(bl, JSON_BOOL);
355     bl->value.b = val;
356 }
357
358 /**
359         @brief Insert one jsonObject into a jsonObect of type JSON_ARRAY, at the end of the array.
360         @param o Pointer to the outer jsonObject that will receive additional payload.
361         @param newo Pointer to the inner jsonObject, or NULL.
362         @return The number of jsonObjects directly subordinate to the outer jsonObject,
363                 or -1 for error.
364
365         If the pointer to the outer jsonObject is NULL, jsonObjectPush returns -1.
366
367         If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
368         any previous contents are freed.
369
370         If the pointer to the inner jsonObject is NULL, jsonObjectPush creates a new jsonObject
371         of type JSON_NULL, and appends it to the array.
372 */
373 unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
374     if(!o) return -1;
375     if(!newo) newo = jsonNewObject(NULL);
376         JSON_INIT_CLEAR(o, JSON_ARRAY);
377         newo->parent = o;
378         osrfListPush( o->value.l, newo );
379         o->size = o->value.l->size;
380         return o->size;
381 }
382
383 /**
384         @brief Insert a jsonObject at a specified position in a jsonObject of type JSON_ARRAY.
385         @param dest Pointer to the outer jsonObject that will receive the new payload.
386         @param index A zero-based subscript specifying the position of the new jsonObject.
387         @param newObj Pointer to the new jsonObject to be inserted, or NULL.
388         @return The size of the internal osrfList where the array is stored, or -1 on error.
389
390         If @a dest is NULL, jsonObjectSetIndex returns -1.
391
392         If the outer jsonObject is not already of type JSON_ARRAY, it is converted to one, and
393         any previous contents are freed.
394
395         If @a newObj is NULL, jsonObject creates a new jsonObject of type JSON_NULL and
396         inserts it into the array.
397
398         If there is already a jsonObject at the specified location, it is freed and replaced.
399
400         Depending on the placement of the inner jsonObject, it may leave unoccupied holes in the
401         array that are included in the reported size.  As a result of this and other peculiarities
402         of the underlying osrfList within the jsonObject, the reported size may not reflect the
403         number of jsonObjects in the array.  See osrf_list.c for further details.
404 */
405 unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
406         if(!dest) return -1;
407         if(!newObj) newObj = jsonNewObject(NULL);
408         JSON_INIT_CLEAR(dest, JSON_ARRAY);
409         newObj->parent = dest;
410         osrfListSet( dest->value.l, newObj, index );
411         dest->size = dest->value.l->size;
412         return dest->value.l->size;
413 }
414
415 /**
416         @brief Insert a jsonObject into a jsonObject of type JSON_HASH, for a specified key.
417         @param o Pointer to the outer jsonObject that will receive the new payload.
418         @param key Pointer to a string that will serve as the key.
419         @param newo Pointer to the new jsonObject that will be inserted, or NULL.
420         @return The number of items stored in the first level of the hash.
421
422         If the @a o parameter is NULL, jsonObjectSetKey returns -1.
423
424         If the outer jsonObject is not already of type JSON_HASH, it will be converted to one,
425         and any previous contents will be freed.
426
427         If @a key is NULL, jsonObjectSetKey returns the size of the hash without doing anything
428         (apart from possibly converting the outer jsonObject as described above).
429
430         If @a newo is NULL, jsonObjectSetKey creates a new jsonObject of type JSON_NULL and inserts
431         it with the specified key.
432
433         If a previous jsonObject is already stored with the same key, it is freed and replaced.
434 */
435 unsigned long jsonObjectSetKey( jsonObject* o, const char* key, jsonObject* newo) {
436     if(!o) return -1;
437     if(!newo) newo = jsonNewObject(NULL);
438         JSON_INIT_CLEAR(o, JSON_HASH);
439         newo->parent = o;
440         osrfHashSet( o->value.h, newo, key );
441         o->size = osrfHashGetCount(o->value.h);
442         return o->size;
443 }
444
445 /**
446         @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
447         @param obj Pointer to the outer jsonObject.
448         @param key The key for the associated item to be found.
449         @return A pointer to the inner jsonObject, if found, or NULL if not.
450
451         Returns NULL if either parameter is NULL, or if @a obj points to a jsonObject not of type
452         JSON_HASH, or if the specified key is not found in the outer jsonObject.
453
454         The returned pointer (if not NULL) points to the interior of the outer jsonObject.  The
455         calling code should @em not try to free it, but it may change its contents.
456 */
457 jsonObject* jsonObjectGetKey( jsonObject* obj, const char* key ) {
458         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
459         return osrfHashGet( obj->value.h, key);
460 }
461
462 /**
463         @brief From a jsonObject of type JSON_HASH, find the inner jsonObject for a specified key.
464         @param obj Pointer to the outer jsonObject.
465         @param key The key for the associated item to be found.
466         @return A pointer to the inner jsonObject, if found, or NULL if not.
467
468         This function is identical to jsonObjectGetKey(), except that the outer jsonObject may be
469         const, and the pointer returned points to const.  As a result, the calling code is put on
470         notice that it should not try to modify the contents of the inner jsonObject.
471  */
472 const jsonObject* jsonObjectGetKeyConst( const jsonObject* obj, const char* key ) {
473         if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
474         return osrfHashGet( obj->value.h, key);
475 }
476
477 /**
478         @brief Recursively traverse a jsonObject, translating it into a JSON string.
479         @param obj Pointer to the jsonObject to be translated.
480         @param buf Pointer to a growing_buffer that will receive the JSON string.
481         @param do_classname Boolean; if true, expand (i.e. encode) class names.
482         @param second_pass Boolean; should always be false except for some recursive calls.
483  
484         If @a do_classname is true, expand any class names, as described in the discussion of
485         jsonObjectToJSON().
486
487         @a second_pass should always be false except for some recursive calls.  It is used
488         when expanding classnames, to distinguish between the first and second passes
489         through a given node.
490 */
491 static void add_json_to_buffer( const jsonObject* obj,
492         growing_buffer * buf, int do_classname, int second_pass ) {
493
494     if(NULL == obj) {
495         OSRF_BUFFER_ADD(buf, "null");
496         return;
497     }
498
499         if( obj->classname && do_classname )
500         {
501                 if( second_pass )
502                         second_pass = 0;
503                 else
504                 {
505                         // Pretend we see an extra layer of JSON_HASH
506                         
507                         OSRF_BUFFER_ADD( buf, "{\"" );
508                         OSRF_BUFFER_ADD( buf, JSON_CLASS_KEY );
509                         OSRF_BUFFER_ADD( buf, "\":\"" );
510                         OSRF_BUFFER_ADD( buf, obj->classname );
511                         OSRF_BUFFER_ADD( buf, "\",\"" );
512                         OSRF_BUFFER_ADD( buf, JSON_DATA_KEY );
513                         OSRF_BUFFER_ADD( buf, "\":" );
514                         add_json_to_buffer( obj, buf, 1, 1 );
515                         buffer_add_char( buf, '}' );
516                         return;
517                 }
518         }
519
520         switch(obj->type) {
521
522                 case JSON_BOOL :
523                         if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); 
524                         else OSRF_BUFFER_ADD(buf, "false"); 
525                         break;
526
527         case JSON_NUMBER: {
528             if(obj->value.s) OSRF_BUFFER_ADD( buf, obj->value.s );
529             else OSRF_BUFFER_ADD_CHAR( buf, '0' );
530             break;
531         }
532
533                 case JSON_NULL:
534                         OSRF_BUFFER_ADD(buf, "null");
535                         break;
536
537                 case JSON_STRING:
538                         OSRF_BUFFER_ADD_CHAR(buf, '"');
539                         buffer_append_utf8(buf, obj->value.s);
540                         OSRF_BUFFER_ADD_CHAR(buf, '"');
541                         break;
542                         
543                 case JSON_ARRAY: {
544                         OSRF_BUFFER_ADD_CHAR(buf, '[');
545                         if( obj->value.l ) {
546                                 int i;
547                                 for( i = 0; i != obj->value.l->size; i++ ) {
548                                         if(i > 0) OSRF_BUFFER_ADD(buf, ",");
549                                         add_json_to_buffer(
550                                                 OSRF_LIST_GET_INDEX(obj->value.l, i), buf, do_classname, second_pass );
551                                 }
552                         }
553                         OSRF_BUFFER_ADD_CHAR(buf, ']');
554                         break;
555                 }
556
557                 case JSON_HASH: {
558         
559                         OSRF_BUFFER_ADD_CHAR(buf, '{');
560                         osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
561                         jsonObject* item;
562                         int i = 0;
563
564                         while( (item = osrfHashIteratorNext(itr)) ) {
565                                 if(i++ > 0) OSRF_BUFFER_ADD_CHAR(buf, ',');
566                                 OSRF_BUFFER_ADD_CHAR(buf, '"');
567                                 buffer_append_utf8(buf, osrfHashIteratorKey(itr));
568                                 OSRF_BUFFER_ADD(buf, "\":");
569                                 add_json_to_buffer( item, buf, do_classname, second_pass );
570                         }
571
572                         osrfHashIteratorFree(itr);
573                         OSRF_BUFFER_ADD_CHAR(buf, '}');
574                         break;
575                 }
576         }
577 }
578
579 /**
580         @brief Translate a jsonObject into a JSON string, without expanding class names.
581         @param obj Pointer to the jsonObject to be translated.
582         @return A pointer to a newly allocated string containing the JSON.
583
584         The calling code is responsible for freeing the resulting string.
585 */
586 char* jsonObjectToJSONRaw( const jsonObject* obj ) {
587         if(!obj) return NULL;
588         growing_buffer* buf = buffer_init(32);
589         add_json_to_buffer( obj, buf, 0, 0 );
590         return buffer_release( buf );
591 }
592
593 /**
594         @brief Translate a jsonObject into a JSON string, with expansion of class names.
595         @param obj Pointer to the jsonObject to be translated.
596         @return A pointer to a newly allocated string containing the JSON.
597
598         At every level, any jsonObject containing a class name will be translated as if it were
599         under an extra layer of JSON_HASH, with JSON_CLASS_KEY as the key for the class name and
600         JSON_DATA_KEY as the key for the jsonObject.
601
602         The calling code is responsible for freeing the resulting string.
603  */
604 char* jsonObjectToJSON( const jsonObject* obj ) {
605         if(!obj) return NULL;
606         growing_buffer* buf = buffer_init(32);
607         add_json_to_buffer( obj, buf, 1, 0 );
608         return buffer_release( buf );
609 }
610
611 /**
612         @brief Create a new jsonIterator for traversing a specified jsonObject.
613         @param obj Pointer to the jsonObject to be traversed.
614         @return A pointer to the newly allocated jsonIterator, or NULL upon error.
615
616         jsonNewIterator returns NULL if @a obj is NULL.
617
618         The new jsonIterator does not point to any particular position within the jsonObject to
619         be traversed.  The next call to jsonIteratorNext() will position it at the beginning.
620
621         The calling code is responsible for freeing the jsonIterator by calling jsonIteratorFree().
622 */
623 jsonIterator* jsonNewIterator(const jsonObject* obj) {
624         if(!obj) return NULL;
625         jsonIterator* itr;
626         OSRF_MALLOC(itr, sizeof(jsonIterator));
627
628         itr->obj    = (jsonObject*) obj;
629         itr->index  = 0;
630         itr->key    = NULL;
631
632         if( obj->type == JSON_HASH )
633                 itr->hashItr = osrfNewHashIterator(obj->value.h);
634         else
635                 itr->hashItr = NULL;
636         
637         return itr;
638 }
639
640 /**
641         @brief Free a jsonIterator and everything in it.
642         @param itr Pointer to the jsonIterator to be freed.
643 */
644 void jsonIteratorFree(jsonIterator* itr) {
645         if(!itr) return;
646         osrfHashIteratorFree(itr->hashItr);
647         free(itr);
648 }
649
650 /**
651         @brief Advance a jsonIterator to the next position within a jsonObject.
652         @param itr Pointer to the jsonIterator to be advanced.
653         @return A Pointer to the next jsonObject within the jsonObject being traversed; or NULL.
654
655         If the jsonObject being traversed is of type JSON_HASH, jsonIteratorNext returns a pointer
656         to the next jsonObject within the internal osrfHash.  The associated key string is available
657         via the pointer member itr->key.
658
659         If the jsonObject being traversed is of type JSON_ARRAY, jsonIteratorNext returns a pointer
660         to the next jsonObject within the internal osrfList.
661
662         In either case, the jsonIterator remains at the same level within the jsonObject that it is
663         traversing.  It does @em not descend to traverse deeper levels recursively.
664
665         If there is no next jsonObject within the jsonObject being traversed, jsonIteratorNext
666         returns NULL.  It also returns NULL if @a itr is NULL, or if the jsonIterator is
667         detectably corrupted, or if the jsonObject to be traversed is of a type other than
668         JSON_HASH or JSON_ARRAY.
669
670         Once jsonIteratorNext has reached the end of the jsonObject that it is traversing,
671         subsequent calls using the same iterator will continue to return NULL.  There is no available
672         function to start over at the beginning.
673
674         The pointer returned, if not NULL, points to an internal element of the jsonObject being
675         traversed.  The calling code should @em not try to free it, but it may modify its contents.
676 */
677 jsonObject* jsonIteratorNext(jsonIterator* itr) {
678         if(!(itr && itr->obj)) return NULL;
679         if( itr->obj->type == JSON_HASH ) {
680                 if(!itr->hashItr)
681                         return NULL;
682
683                 jsonObject* item = osrfHashIteratorNext(itr->hashItr);
684                 if( item )
685                         itr->key = osrfHashIteratorKey(itr->hashItr);
686                 else
687                         itr->key = NULL;
688                 return item;
689         } else {
690                 return jsonObjectGetIndex( itr->obj, itr->index++ );
691         }
692 }
693
694 /**
695         @brief Determine whether a jsonIterator is positioned at the last element of a jsonObject.
696         @param itr Pointer to the jsonIterator whose position is to be tested.
697         @return An int, as boolean: 0 if the iterator is positioned at the end, or 1 if it isn't.
698
699         If the jsonIterator is positioned where a call to jsonIteratorNext() would return NULL,
700         then jsonIteratorHasNext returns 0.  Otherwise it returns 1.
701 */
702 int jsonIteratorHasNext(const jsonIterator* itr) {
703         if(!(itr && itr->obj)) return 0;
704         if( itr->obj->type == JSON_HASH )
705                 return osrfHashIteratorHasNext( itr->hashItr );
706         return (itr->index < itr->obj->size) ? 1 : 0;
707 }
708
709 /**
710         @brief Fetch a pointer to a specified element within a jsonObject of type JSON_ARRAY.
711         @param obj Pointer to the outer jsonObject.
712         @param index A zero-based index identifying the element to be fetched.
713         @return A pointer to the element at the specified location, if any, or NULL.
714
715         The return value is NULL if @a obj is null, or if the outer jsonObject is not of type
716         JSON_ARRAY, or if there is no element at the specified location.
717
718         If not NULL, the pointer returned points to an element within the outer jsonObject.  The
719         calling code should @em not try to free it, but it may modify its contents.
720 */
721 jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
722         if(!obj) return NULL;
723         return (obj->type == JSON_ARRAY) ? 
724         (OSRF_LIST_GET_INDEX(obj->value.l, index)) : NULL;
725 }
726
727 /**
728         @brief Remove a specified element from a jsonObject of type JSON_ARRAY.
729         @param dest Pointer to the jsonObject from which the element is to be removed.
730         @param index A zero-based index identifying the element to be removed.
731         @return The number of elements remaining at the top level, or -1 upon error.
732
733         The return value is -1 if @a dest is NULL, or if it points to a jsonObject not of type
734         JSON_ARRAY.  Otherwise it reflects the number of elements remaining in the top level of
735         the outer jsonObject, not counting any at lower levels.
736 */
737 unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
738         if( dest && dest->type == JSON_ARRAY ) {
739                 osrfListRemove(dest->value.l, index);
740                 return dest->value.l->size;
741         }
742         return -1;
743 }
744
745 /**
746         @brief Extract a specified element from a jsonObject of type JSON_ARRAY.
747         @param dest Pointer to the jsonObject from which the element is to be extracted.
748         @param index A zero-based index identifying the element to be extracted.
749         @return A pointer to the extracted element, if successful; otherwise NULL.
750
751         THe return value is NULL if @a dest is NULL, or if it points to a jsonObject not of type
752         JSON_ARRAY, or if there is no element at the specified location.
753
754         Otherwise, the calling code assumes ownership of the jsonObject to which the return value
755         points, and is responsible for freeing it by calling jsonObjectFree().  The original outer
756         jsonObject remains unchanged except for the removal of the specified element.
757
758         This function is sijmilar to jsonObjectRemoveIndex(), except that it returns a pointer to
759         the removed sub-object instead of destroying it.
760 */
761 jsonObject* jsonObjectExtractIndex(jsonObject* dest, unsigned long index) {
762         if( dest && dest->type == JSON_ARRAY ) {
763                 jsonObject* obj = osrfListExtract(dest->value.l, index);
764                 if( obj )
765                         obj->parent = NULL;
766                 return obj;
767         } else
768                 return NULL;
769 }
770
771 /**
772         @brief Remove an element, specified by key, from a jsonObject of type JSON_HASH.
773         @param dest Pointer to the outer jsonObject from which an element is to be removed.
774         @param key The key for the associated element to be removed.
775         @return 1 if successful, or -1 if not.
776
777         The operation is considered successful if @a dest and @a key are both non-NULL, and
778         @a dest points to a jsonObject of type JSON_HASH, even if the specified key is not found.
779 */
780 unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
781         if( dest && key && dest->type == JSON_HASH ) {
782                 osrfHashRemove(dest->value.h, key);
783                 return 1;
784         }
785         return -1;
786 }
787
788 /**
789         @brief Format a double into a character string.
790         @param num The double to be formatted.
791         @return A newly allocated character string containing the formatted number.
792
793         The number is formatted according to the printf-style format specification "%.30g". and
794         will therefore contain no more than 30 significant digits.
795
796         The calling code is responsible for freeing the resulting string.
797 */
798 char* doubleToString( double num ) {
799
800         char buf[ 64 ];
801         size_t len = snprintf(buf, sizeof( buf ), "%.30g", num) + 1;
802         if( len < sizeof( buf ) )
803                 return strdup( buf );
804         else
805         {
806                 // Need a bigger buffer (should never be necessary)
807                 
808                 char* bigger_buff = safe_malloc( len + 1 );
809                 (void) snprintf(bigger_buff, len + 1, "%.30g", num);
810                 return bigger_buff;
811         }
812 }
813
814 /**
815         @brief Fetch a pointer to the string stored in a jsonObject, if any.
816         @param obj Pointer to the jsonObject.
817         @return Pointer to the string stored internally, or NULL.
818
819         If @a obj points to a jsonObject of type JSON_STRING or JSON_NUMBER, the returned value
820         points to the string stored internally (a numeric string in the case of a JSON_NUMBER).
821         Otherwise the returned value is NULL.
822 */
823 const char* jsonObjectGetString(const jsonObject* obj) {
824         if(obj)
825         {
826                 if( obj->type == JSON_STRING )
827                         return obj->value.s;
828                 else if( obj->type == JSON_NUMBER )
829                         return obj->value.s ? obj->value.s : "0";
830                 else
831                         return NULL;
832         }
833         else
834                 return NULL;
835 }
836
837 /**
838         @brief Translate a jsonObject to a double.
839         @param @obj Pointer to the jsonObject.
840         @return The numeric value stored in the jsonObject.
841
842         If @a obj is NULL, or if it points to a jsonObject not of type JSON_NUMBER, the value
843         returned is zero.
844 */
845 double jsonObjectGetNumber( const jsonObject* obj ) {
846         return (obj && obj->type == JSON_NUMBER && obj->value.s)
847                         ? strtod( obj->value.s, NULL ) : 0;
848 }
849
850 /**
851         @brief Store a copy of a specified character string in a jsonObject of type JSON_STRING.
852         @param dest Pointer to the jsonObject in which the string will be stored.
853         @param string Pointer to the string to be stored.
854
855         Both @a dest and @a string must be non-NULL.
856
857         If the jsonObject is not already of type JSON_STRING, it is converted to a JSON_STRING,
858         with any previous contents freed.
859 */
860 void jsonObjectSetString(jsonObject* dest, const char* string) {
861         if(!(dest && string)) return;
862         JSON_INIT_CLEAR(dest, JSON_STRING);
863         dest->value.s = strdup(string);
864 }
865
866 /**
867  Turn a jsonObject into a JSON_NUMBER (if it isn't already one) and store
868  a specified numeric string in it.  If the string is not numeric,
869  store the equivalent of zero, and return an error status.
870 **/
871 /**
872         @brief Store a copy of a numeric character string in a jsonObject of type JSON_NUMBER.
873         @param dest Pointer to the jsonObject in which the number will be stored.
874         @param string Pointer to the numeric string to be stored.
875
876         Both @a dest and @a string must be non-NULL.
877
878         If the jsonObject is not already of type JSON_NUMBER, it is converted to a JSON_STRING,
879         with any previous contents freed.
880
881         If the input string is not numeric as determined by jsonIsNumeric(), the number stored
882         is zero.
883  */
884 int jsonObjectSetNumberString(jsonObject* dest, const char* string) {
885         if(!(dest && string)) return -1;
886         JSON_INIT_CLEAR(dest, JSON_NUMBER);
887
888         if( jsonIsNumeric( string ) ) {
889                 dest->value.s = strdup(string);
890                 return 0;
891         }
892         else {
893                 dest->value.s = NULL;  // equivalent to zero
894                 return -1;
895         }
896 }
897
898 /**
899         @brief Store a number in a jsonObject of type JSON_NUMBER.
900         @param dest Pointer to the jsonObject in which the number will be stored.
901         @param num The number to be stored.
902
903         If the jsonObject is not already of type JSON_NUMBER, it is converted to one, with any
904         previous contents freed.
905 */
906 void jsonObjectSetNumber(jsonObject* dest, double num) {
907         if(!dest) return;
908         JSON_INIT_CLEAR(dest, JSON_NUMBER);
909         dest->value.s = doubleToString( num );
910 }
911
912 /**
913         @brief Assign a class name to a jsonObject.
914         @param dest Pointer to the jsonObject.
915         @param classname Pointer to a string containing the class name.
916
917         Both dest and classname must be non-NULL.
918 */
919 void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
920         if(!(dest && classname)) return;
921         free(dest->classname);
922         dest->classname = strdup(classname);
923 }
924
925 /**
926         @brief Fetch a pointer to the class name of a jsonObject, if any.
927         @param dest Pointer to the jsonObject.
928         @return Pointer to a string containing the class name, if there is one; or NULL.
929
930         If not NULL, the pointer returned points to a string stored internally within the
931         jsonObject.  The calling code should @em not try to free it.
932 */
933 const char* jsonObjectGetClass(const jsonObject* dest) {
934     if(!dest) return NULL;
935     return dest->classname;
936 }
937
938 /**
939         @brief Create a copy of an existing jsonObject, including all internal sub-objects.
940         @param o Pointer to the jsonObject to be copied.
941         @return A pointer to the newly created copy.
942
943         The calling code is responsible for freeing the copy of the original.
944 */
945 jsonObject* jsonObjectClone( const jsonObject* o ) {
946     if(!o) return jsonNewObject(NULL);
947
948     int i;
949     jsonObject* arr; 
950     jsonObject* hash; 
951     jsonIterator* itr;
952     jsonObject* tmp;
953     jsonObject* result = NULL;
954
955     switch(o->type) {
956         case JSON_NULL:
957             result = jsonNewObject(NULL);
958             break;
959         case JSON_STRING:
960             result = jsonNewObject(jsonObjectGetString(o));
961             break;
962         case JSON_NUMBER:
963                         result = jsonNewObject( o->value.s );
964                         result->type = JSON_NUMBER;
965             break;
966         case JSON_BOOL:
967             result = jsonNewBoolObject(jsonBoolIsTrue((jsonObject*) o));
968             break;
969         case JSON_ARRAY:
970             arr = jsonNewObject(NULL);
971             arr->type = JSON_ARRAY;
972             for(i=0; i < o->size; i++) 
973                 jsonObjectPush(arr, jsonObjectClone(jsonObjectGetIndex(o, i)));
974             result = arr;
975             break;
976         case JSON_HASH:
977             hash = jsonNewObject(NULL);
978             hash->type = JSON_HASH;
979             itr = jsonNewIterator(o);
980             while( (tmp = jsonIteratorNext(itr)) )
981                 jsonObjectSetKey(hash, itr->key, jsonObjectClone(tmp));
982             jsonIteratorFree(itr);
983             result = hash;
984             break;
985     }
986
987     jsonObjectSetClass(result, jsonObjectGetClass(o));
988     return result;
989 }
990
991 /**
992         @brief Return the truth or falsity of a jsonObject of type JSON_BOOL.
993         @param boolObj Pointer to the jsonObject.
994         @return 1 or 0, depending on whether the stored boolean is true or false.
995
996         If @a boolObj is NULL, or if it points to a jsonObject not of type JSON_BOOL, the
997         returned value is zero.
998 */
999 int jsonBoolIsTrue( const jsonObject* boolObj ) {
1000     if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
1001         return 1;
1002     return 0;
1003 }
1004
1005
1006 /**
1007         @brief Create a copy of the string stored in a jsonObject.
1008         @param o Pointer to the jsonObject whose string is to be copied.
1009         @return Pointer to a newly allocated string copied from the jsonObject.
1010
1011         If @a o is NULL, or if it points to a jsonObject not of type JSON_STRING or JSON_NUMBER,
1012         the returned value is NULL.  In the case of a JSON_NUMBER, the string created is numeric.
1013
1014         The calling code is responsible for freeing the newly created string.
1015 */
1016 char* jsonObjectToSimpleString( const jsonObject* o ) {
1017         if(!o) return NULL;
1018
1019         char* value = NULL;
1020
1021         switch( o->type ) {
1022
1023                 case JSON_NUMBER:
1024                         value = strdup( o->value.s ? o->value.s : "0" );
1025                         break;
1026
1027                 case JSON_STRING:
1028                         value = strdup(o->value.s);
1029         }
1030
1031         return value;
1032 }
1033
1034 /**
1035  Return 1 if the string is numeric, otherwise return 0.
1036  This validation follows the rules defined by the grammar at:
1037  http://www.json.org/
1038  **/
1039 /**
1040         @brief Determine whether a specified character string is a valid JSON number.
1041         @param s Pointer to the string to be examined.
1042         @return 1 if the string is numeric, or 0 if not.
1043
1044         This function defines numericity according to JSON rules; see http://json.org/.  This
1045         determination is based purely on the lexical properties of the string.  In particular
1046         there is no guarantee that the number in a numeric string is representable in C as a
1047         long long, a long double, or any other built-in type.
1048
1049         A numeric string consists of:
1050
1051         - An optional leading minus sign (but not a plus sign)
1052         - One or more decimal digits.  The first digit may be a zero only if it is the only
1053           digit to the left of the decimal.
1054         - Optionally, a decimal point followed by one or more decimal digits.
1055         - An optional exponent, consisting of:
1056                 - The letter E, in upper or lower case
1057                 - An optional plus or minus sign
1058                 - One or more decimal digits
1059
1060         See also jsonScrubNumber().
1061 */
1062 int jsonIsNumeric( const char* s ) {
1063
1064         if( !s || !*s ) return 0;
1065
1066         const char* p = s;
1067
1068         // skip leading minus sign, if present (leading plus sign not allowed)
1069
1070         if( '-' == *p )
1071                 ++p;
1072
1073         // There must be at least one digit to the left of the decimal
1074
1075         if( isdigit( (unsigned char) *p ) ) {
1076                 if( '0' == *p++ ) {
1077
1078                         // If the first digit is zero, it must be the
1079                         // only digit to the left of the decimal
1080
1081                         if( isdigit( (unsigned char) *p ) )
1082                                 return 0;
1083                 }
1084                 else {
1085
1086                         // Skip over the following digits
1087
1088                         while( isdigit( (unsigned char) *p ) ) ++p;
1089                 }
1090         }
1091         else
1092                 return 0;
1093
1094         if( !*p )
1095                 return 1;             // integer
1096
1097         if( '.' == *p ) {
1098
1099                 ++p;
1100
1101                 // If there is a decimal point, there must be
1102                 // at least one digit to the right of it
1103
1104                 if( isdigit( (unsigned char) *p ) )
1105                         ++p;
1106                 else
1107                         return 0;
1108
1109                 // skip over contiguous digits
1110
1111                 while( isdigit( (unsigned char) *p ) ) ++p;
1112         }
1113
1114         if( ! *p )
1115                 return 1;  // decimal fraction, no exponent
1116         else if( *p != 'e' && *p != 'E' )
1117                 return 0;  // extra junk, no exponent
1118         else
1119                 ++p;
1120
1121         // If we get this far, we have the beginnings of an exponent.
1122         // Skip over optional sign of exponent.
1123
1124         if( '-' == *p || '+' == *p )
1125                 ++p;
1126
1127         // There must be at least one digit in the exponent
1128         
1129         if( isdigit( (unsigned char) *p ) )
1130                 ++p;
1131         else
1132                 return 0;
1133
1134         // skip over contiguous digits
1135
1136         while( isdigit( (unsigned char) *p ) ) ++p;
1137
1138         if( *p )
1139                 return 0;  // extra junk
1140         else
1141                 return 1;  // number with exponent
1142 }
1143
1144 /**
1145         @brief Edit a string into a valid JSON number, if possible.
1146         @param s Pointer to the string to be edited.
1147         @return A pointer to a newly created numeric string, if possible; otherwise NULL.
1148
1149         JSON has rather exacting requirements about what constitutes a valid numeric string (see
1150         jsonIsNumeric()).  Real-world input may be a bit sloppy.  jsonScrubNumber accepts numeric
1151         strings in a less formal format and reformats them, where possible, according to JSON
1152         rules.  It removes leading white space, a leading plus sign, and extraneous leading zeros.
1153         It adds a leading zero as needed when the absolute value is less than 1.  It also accepts
1154         scientific notation in the form of a bare exponent (e.g. "E-3"), supplying a leading factor
1155         of "1".
1156
1157         If the input string is non-numeric even according to these relaxed rules, the return value
1158         is NULL.
1159
1160         The calling code is responsible for freeing the newly created numeric string.
1161 */
1162 char* jsonScrubNumber( const char* s ) {
1163         if( !s || !*s ) return NULL;
1164
1165         growing_buffer* buf = buffer_init( 64 );
1166
1167         // Skip leading white space, if present
1168
1169         while( isspace( (unsigned char) *s ) ) ++s;
1170
1171         // Skip leading plus sign, if present, but keep a minus
1172
1173         if( '-' == *s )
1174         {
1175                 buffer_add_char( buf, '-' );
1176                 ++s;
1177         }
1178         else if( '+' == *s )
1179                 ++s;
1180
1181         if( '\0' == *s ) {
1182                 // No digits found
1183
1184                 buffer_free( buf );
1185                 return NULL;
1186         }
1187         // Skip any leading zeros
1188
1189         while( '0' == *s ) ++s;
1190
1191         // Capture digits to the left of the decimal,
1192         // and note whether there are any.
1193
1194         int left_digit = 0;  // boolean
1195
1196         if( isdigit( (unsigned char) *s ) ) {
1197                 buffer_add_char( buf, *s++ );
1198                 left_digit = 1;
1199         }
1200         
1201         while( isdigit( (unsigned char) *s  ) )
1202                 buffer_add_char( buf, *s++ );
1203
1204         // Now we expect to see a decimal point,
1205         // an exponent, or end-of-string.
1206
1207         switch( *s )
1208         {
1209                 case '\0' :
1210                         break;
1211                 case '.' :
1212                 {
1213                         // Add a single leading zero, if we need to
1214
1215                         if( ! left_digit )
1216                                 buffer_add_char( buf, '0' );
1217                         buffer_add_char( buf, '.' );
1218                         ++s;
1219
1220                         if( ! left_digit && ! isdigit( (unsigned char) *s ) )
1221                         {
1222                                 // No digits on either side of decimal
1223
1224                                 buffer_free( buf );
1225                                 return NULL;
1226                         }
1227
1228                         // Collect digits to right of decimal
1229
1230                         while( isdigit( (unsigned char) *s ) )
1231                                 buffer_add_char( buf, *s++ );
1232
1233                         break;
1234                 }
1235                 case 'e' :
1236                 case 'E' :
1237
1238                         // Exponent; we'll deal with it later, but
1239                         // meanwhile make sure we have something
1240                         // to its left
1241
1242                         if( ! left_digit )
1243                                 buffer_add_char( buf, '1' );
1244                         break;
1245                 default :
1246
1247                         // Unexpected character; bail out
1248
1249                         buffer_free( buf );
1250                         return NULL;
1251         }
1252
1253         if( '\0' == *s )    // Are we done yet?
1254                 return buffer_release( buf );
1255
1256         if( 'e' != *s && 'E' != *s ) {
1257
1258                 // Unexpected character: bail out
1259
1260                 buffer_free( buf );
1261                 return NULL;
1262         }
1263
1264         // We have an exponent.  Load the e or E,
1265         // and the sign if there is one.
1266
1267         buffer_add_char( buf, *s++ );
1268
1269         if( '+' == *s || '-' == *s )
1270                 buffer_add_char( buf, *s++ );
1271
1272         // Collect digits of the exponent
1273
1274         while( isdigit( (unsigned char) *s ) )
1275                 buffer_add_char( buf, *s++ );
1276
1277         // There better not be anything left
1278
1279         if( *s ) {
1280                 buffer_free( buf );
1281                 return NULL;
1282         }
1283
1284         return buffer_release( buf );
1285 }