Add metrics to the framework
[ric-plt/xapp-frame-cpp.git] / src / json / jwrapper.c
1
2 // vi: ts=4 sw=4 noet:
3 /*
4 ==================================================================================
5         Copyright (c) 2020 Nokia
6         Copyright (c) 2020 AT&T Intellectual Property.
7
8    Licensed under the Apache License, Version 2.0 (the "License");
9    you may not use this file except in compliance with the License.
10    You may obtain a copy of the License at
11
12        http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19 ==================================================================================
20 */
21
22 /*
23         Mnemonic:       jwrapper.c
24         Abstract:       A wrapper interface to the jsmn library which makes it a bit easier
25                                 to use.  Parses a json string capturing the contents in a symtab.
26
27                                 This code is based on the AT&T VFd open source library available
28                                 on github.com/att/vfd.  The changes are mostly to port to the
29                                 RMR version of symtab from VFd's version.
30
31         Author:         E. Scott Daniels
32         Date:           26 June 2020
33
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdint.h>
40
41 #ifndef DEBUG
42         #define DEBUG 0
43 #endif
44
45 #define JSMN_STATIC 1           // jsmn no longer builds into a library; this pulls as static functions
46 #include <jsmn.h>
47 //#include <../../ext/jsmn/jsmn.h>
48
49 #include <rmr/rmr_symtab.h>
50
51 #define JSON_SYM_NAME   "_jw_json_string"
52 #define MAX_THINGS              1024 * 4        // max objects/elements
53
54 #define PT_UNKNOWN              0                       // primative types; unknown for non prim
55 #define PT_VALUE                1
56 #define PT_BOOL                 2
57 #define PT_NULL                 3
58 #define PT_STRING               4
59
60 #define OBJ_SPACE               1                       // space in the symbol table where json bits are stashed
61 #define MGT_SPACE               2                       // non-json objects in the hash (management things)
62
63 extern void jw_nuke( void* st );
64
65 // ---------------------------------------------------------------------------------------
66
67 /*
68         This is what we will manage in the symtab. Right now we store all values (primatives)
69         as double, but we could be smarter about it and look for a decimal. Unsigned and
70         differences between long, long long etc are tough.
71 */
72 typedef struct jthing {
73         int jsmn_type;                          // propigated type from jsmn (jsmn constants)
74         int prim_type;                          // finer grained primative type (bool, null, value)
75         int     nele;                                   // number of elements if applies
76         union {
77                 double fv;
78                 void *pv;
79         } v;
80 } jthing_t;
81
82
83 /*
84         Given the json token, 'extract' the element by marking the end with a
85         nil character, and returning a pointer to the start.  We do this so that
86         we don't create a bunch of small buffers that must be found and freed; we
87         can just release the json string and we'll be done (read won't leak).
88 */
89 static char* extract( char* buf, jsmntok_t *jtoken ) {
90         buf[jtoken->end] = 0;
91         return &buf[jtoken->start];
92 }
93
94 /*
95         create a new jthing and add a reference to it in the symbol table st.
96         sets the number of elements to 1 by default.
97 */
98 static jthing_t *mk_thing( void *st, char *name, int jsmn_type ) {
99         jthing_t        *jtp = NULL;
100
101         if( st != NULL &&
102                 name != NULL &&
103                 (jtp = (jthing_t *) malloc( sizeof( *jtp ) )) != NULL ) {
104
105                 if( DEBUG ) {
106                         fprintf( stderr, "<DBUG> jwrapper adding: %s type=%d\n", name, jsmn_type );
107                 }
108
109                 jtp->jsmn_type = jsmn_type;
110                 jtp->prim_type = PT_UNKNOWN;                    // caller must set this
111                 jtp->nele = 1;
112                 jtp->v.fv = 0;
113
114                 rmr_sym_put( st, name, OBJ_SPACE, jtp );
115         } else {
116                 fprintf( stderr, "[WARN] jwrapper: unable to create '%s' type=%d\n", name, jsmn_type );
117         }
118
119         return jtp;
120 }
121
122
123 /*
124         Find the named array. Returns a pointer to the jthing that represents
125         the array (type, size and pointer to actual array of jthings).
126         Returns nil pointer if the named thing isn't there or isn't an array.
127 */
128 static jthing_t* suss_array( void* st, const char* name ) {
129         jthing_t* jtp = NULL;                                                   // thing that is referenced by the symtab
130
131         if( st != NULL &&
132                 name != NULL &&
133                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
134
135                 jtp =  jtp->jsmn_type == JSMN_ARRAY  ? jtp : NULL;
136         }
137
138         return jtp;
139 }
140
141 /*
142         Suss an array from the hash and return the ith element.
143 */
144 static jthing_t* suss_element( void* st, const char* name, int idx ) {
145         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
146         jthing_t* jarray;
147         jthing_t* rv = NULL;
148
149         if( (jtp = suss_array( st, name )) != NULL &&           // have pointer
150                 idx >= 0 &&                                                                             // and in range
151                 idx < jtp->nele ) {
152
153                 if( (jarray = jtp->v.pv)  != NULL ) {
154                         rv = &jarray[idx];
155                 }
156         }
157
158         return rv;
159 }
160
161
162 /*
163         Invoked for each thing in the symtab; we free the things that actually point to
164         allocated data (e.g. arrays) and recurse to handle objects.
165 */
166 static void nix_things( void* st, void* se, const char* name,  void* ele, void *data ) {
167         jthing_t*       j;
168         jthing_t*       jarray;
169         int i;
170
171         j = (jthing_t *) ele;
172         if( j ) {
173                 switch( j->jsmn_type ) {
174                         case JSMN_ARRAY:
175                                 if( (jarray = (jthing_t *) j->v.pv)  != NULL ) {
176                                         for( i = 0; i < j->nele; i++ ) {                                        // must look for embedded objects
177                                                 if( jarray[i].jsmn_type == JSMN_OBJECT ) {
178                                                         jw_nuke( jarray[i].v.pv );
179                                                         jarray[i].jsmn_type = JSMN_UNDEFINED;                   // prevent accidents
180                                                 }
181                                         }
182
183                                         free( j->v.pv );                        // must free the array (arrays aren't nested, so all things in the array don't reference allocated mem)
184                                         free( j );
185                                 }
186                                 break;
187
188                         case JSMN_OBJECT:                                                       // delete the sub symtab
189                                 jw_nuke( j->v.pv );
190                                 j->jsmn_type = JSMN_UNDEFINED;                  // prevent a double free
191                                 free( j );
192                                 break;
193
194                         case JSMN_STRING:
195                         case JSMN_PRIMITIVE:
196                                 free( j );
197                                 break;
198                 }
199         }
200 }
201
202 /*
203         Nix non-json things that are also in the hash.
204 */
205 static void nix_mgt( void* st, void* se, const char* name,  void* ele, void *data ) {
206         free( ele );
207 }
208
209 /*
210         Invoked for each thing and prints what we can to stderr.
211 */
212 static void dump_things( void* st, void* se, const char* name,  void* ele, void *data ) {
213         jthing_t*       j;
214         jthing_t*       jarray;
215         int i;
216
217         j = (jthing_t *) ele;
218         if( j ) {
219                 fprintf( stderr, "<DBUG> jwrapper: element '%s' has ptype %d, jsmn type %d\n", name, j->prim_type, j->jsmn_type );
220         } else {
221                 fprintf( stderr, "<DBUG> jwrapper: element has no data: '%s'\n", name );
222         }
223 }
224
225 /*
226         Real work for parsing an object ({...}) from the json.   Called by jw_new() and
227         recurses to deal with sub-objects.
228 */
229 void* parse_jobject( void* st, char *json, char* prefix ) {
230         jthing_t        *jtp;                   // json thing that we just created
231         int             i;
232         int             n;
233         char    *name;                          // name in the json
234         char    *data;                          // data string from the json
235         jthing_t*       jarray;                 // array of jthings we'll coonstruct
236         int             size;
237         int             osize;
238         int             njtokens;                       // tokens actually sussed out
239         jsmn_parser jp;                         // 'parser' object
240         jsmntok_t *jtokens;                     // pointer to tokens returned by the parser
241         char    pname[1024];            // name with prefix
242         char    wbuf[256];                      // temp buf to build a working name in
243         char*   dstr;                           // dup'd string
244
245         jsmn_init( &jp );                       // does this have a failure mode?
246
247         jtokens = (jsmntok_t *) malloc( sizeof( *jtokens ) * MAX_THINGS );
248         if( jtokens == NULL ) {
249                 fprintf( stderr, "[CRI] jwrapper: cannot allocate tokens array\n" );
250                 return NULL;
251         }
252
253         njtokens = jsmn_parse( &jp, json, strlen( json ), jtokens, MAX_THINGS );
254
255         if( jtokens[0].type != JSMN_OBJECT ) {                          // if it's not an object then we can't parse it.
256                 fprintf( stderr, "[WARN] jwrapper: badly formed json; initial opening bracket ({) not detected\n" );
257                 rmr_sym_free( st );
258                 free( jtokens );
259                 return NULL;
260         }
261
262         if( DEBUG ) {
263                 for( i = 1; i < njtokens-1; i++ ) {
264                         fprintf( stderr, "<DBUG> %4d: size=%d start=%d end=%d %s\n", i, jtokens[i].size, jtokens[i].start, jtokens[i].end, extract( json, &jtokens[i] ) );
265                 }
266         }
267
268         for( i = 1; i < njtokens-1; i++ ) {                                     // we'll silently skip the last token if it's "name" without a value
269                 if( jtokens[i].type != JSMN_STRING ) {
270                         fprintf( stderr, "[WARN] jwrapper: badly formed json [%d]; expected name (string) found type=%d %s\n", i, jtokens[i].type, extract( json, &jtokens[i] ) );
271                         rmr_sym_free( st );
272                         free( jtokens );
273                         return NULL;
274                 }
275                 name = extract( json, &jtokens[i] );
276                 if( *prefix != 0 ) {
277                         snprintf( pname, sizeof( pname ), "%s.%s", prefix, name );
278                         name = pname;
279                 }
280
281                 size = jtokens[i].size;
282
283                 i++;                                                                            // at the data token now
284                 switch( jtokens[i].type ) {
285                         case JSMN_OBJECT:                               // save object in two ways: as an object 'blob' and in the current symtab using name as a base (original)
286                                 dstr = strdup( extract( json, &jtokens[i] ) );
287                                 snprintf( wbuf, sizeof( wbuf ), "%s_json", name );      // must stash the json string in the symtab for clean up during nuke
288                                 rmr_sym_put( st, wbuf, MGT_SPACE, dstr );
289
290                                 parse_jobject( st, dstr, name );                                        // recurse to add the object as objectname.xxxx elements
291
292                                 if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL &&            // create thing and reference it in current symtab
293                                         (jtp->v.pv = (void *) rmr_sym_alloc( 255 ) ) != NULL ) {                // object is just a blob
294
295                                         dstr = strdup( extract( json, &jtokens[i] ) );
296                                         rmr_sym_put( jtp->v.pv, JSON_SYM_NAME, MGT_SPACE, dstr );               // must stash json so it is freed during nuke()
297                                         parse_jobject( jtp->v.pv,  dstr, "" );                                                  // recurse acorss the string and build a new symtab
298
299                                         size = jtokens[i].end;                                                                                  // done with them, we need to skip them
300                                         i++;
301                                         while( i < njtokens-1  &&  jtokens[i].end < size ) {
302                                                 if( DEBUG ){
303                                                         fprintf( stderr, "\tskip: [%d] object element start=%d end=%d (%s)\n",
304                                                                 i, jtokens[i].start, jtokens[i].end, extract( json, &jtokens[i])  );
305                                                 }
306                                                 i++;
307                                         }
308
309                                         i--;                                            // must allow loop to bump past the last
310                                 }
311                                 break;
312
313                         case JSMN_ARRAY:
314                                 size = jtokens[i].size;         // size is burried here, and not with the name
315                                 jtp = mk_thing( st, name, jtokens[i].type );
316
317                                 i++;                    // skip first ele; it is the whole array string which I don't grock the need for, but it's their code...
318                                 if( jtp == NULL ) {
319                                         fprintf( stderr, "[WARN] jwrapper: memory alloc error processing element [%d] in json\n", i );
320                                         rmr_sym_free( st );
321                                         free( jtokens );
322                                         return NULL;
323                                 }
324                                 jarray = jtp->v.pv = (jsmntok_t *) malloc( sizeof( *jarray ) * size );          // allocate the array
325                                 memset( jarray, 0, sizeof( *jarray ) * size );
326                                 jtp->nele = size;
327
328                                 for( n = 0; n < size; n++ ) {                                                           // for each array element
329                                         jarray[n].prim_type      = PT_UNKNOWN;                                          // assume not primative type
330                                         switch( jtokens[i+n].type ) {
331
332                                                 case JSMN_OBJECT:
333                                                         jarray[n].v.pv = (void *) rmr_sym_alloc( 255 );
334                                                         if( jarray[n].v.pv != NULL ) {
335                                                                 jarray[n].jsmn_type = JSMN_OBJECT;
336                                                                 parse_jobject( jarray[n].v.pv,  extract( json, &jtokens[i+n]  ), "" );          // recurse acorss the string and build a new symtab
337                                                                 osize = jtokens[i+n].end;                                                                       // done with them, we need to skip them
338                                                                 i++;
339                                                                 while( i+n < njtokens-1  &&  jtokens[n+i].end < osize ) {
340                                                                         i++;
341                                                                 }
342                                                                 i--;                                    // allow incr at loop end
343                                                         }
344                                                         break;
345
346                                                 case JSMN_ARRAY:
347                                                         fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (array) is not string or primative\n", i, n );
348                                                         n += jtokens[i+n].size;                 // this should skip the nested array
349                                                         free( jtp );
350                                                         free( jarray );
351                                                         jarray = NULL;
352                                                         break;
353
354                                                 case JSMN_STRING:
355                                                         data = extract( json, &jtokens[i+n] );
356                                                         jarray[n].v.pv = (void *) data;
357                                                         jarray[n].prim_type = PT_STRING;
358                                                         jarray[n].jsmn_type = JSMN_STRING;
359                                                         break;
360
361                                                 case JSMN_PRIMITIVE:
362                                                         data = extract( json, &jtokens[i+n] );
363                                                         switch( *data ) {
364                                                                 case 'T':
365                                                                 case 't':
366                                                                         jarray[n].v.fv = 1;
367                                                                         jarray[n].prim_type      = PT_BOOL;
368                                                                         break;
369
370                                                                 case 'F':
371                                                                 case 'f':
372                                                                         jarray[n].prim_type      = PT_BOOL;
373                                                                         jarray[n].v.fv = 0;
374                                                                         break;
375
376                                                                 case 'N':                                                                               // assume null, nil, or some variant
377                                                                 case 'n':
378                                                                         jarray[n].prim_type      = PT_NULL;
379                                                                         jarray[n].v.fv = 0;
380                                                                         break;
381
382                                                                 default:
383                                                                         jarray[n].prim_type      = PT_VALUE;
384                                                                         jarray[n].v.fv = strtod( data, NULL );          // store all numerics as double
385                                                                         break;
386                                                         }
387
388                                                         jarray[n].jsmn_type = JSMN_PRIMITIVE;
389                                                         break;
390
391                                                 case JSMN_UNDEFINED:
392                                                         // fallthrough
393                                                 default:
394                                                         fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (unknown=%d) is not string or primative\n", i, n, jtokens[i].type  );
395                                                         rmr_sym_free( st );
396                                                         free( jtokens );
397                                                         return NULL;
398                                                         break;
399                                         }
400                                 }
401
402                                 i += size - 1;          // must allow loop to push to next
403                                 break;
404
405                         case JSMN_STRING:
406                                 data = extract( json, &jtokens[i] );
407                                 if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL ) {
408                                         jtp->prim_type = PT_STRING;
409                                         jtp->v.pv =  (void *) data;                                             // just point into the large json string
410                                 }
411                                 break;
412
413                         case JSMN_PRIMITIVE:
414                                 data = extract( json, &jtokens[i] );
415                                 if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL ) {
416                                         switch( *data ) {                                                               // assume T|t is true and F|f is false
417                                                 case 'T':
418                                                 case 't':
419                                                         jtp->prim_type = PT_BOOL;
420                                                         jtp->v.fv = 1;
421                                                         break;
422
423                                                 case 'F':
424                                                 case 'f':
425                                                         jtp->prim_type = PT_BOOL;
426                                                         jtp->v.fv = 0;
427                                                         break;
428
429                                                 case 'N':                                                                       // Null or some form of that
430                                                 case 'n':
431                                                         jtp->prim_type = PT_NULL;
432                                                         jtp->v.fv = 0;
433                                                         break;
434
435                                                 default:
436                                                         jtp->prim_type = PT_VALUE;
437                                                         jtp->v.fv = strtod( data, NULL );               // store all numerics as double
438                                                         break;
439                                         }
440                                 }
441                                 break;
442
443                         default:
444                                 fprintf( stderr, "[WARN] jwrapper: element [%d] is undefined or of unknown type\n", i );
445                                 break;
446                 }
447         }
448
449         free( jtokens );
450         return st;
451 }
452
453 // --------------- public functions -----------------------------------------------------------------
454
455 /*
456         Destroy all operating structures assocaited with the symtab pointer passed in.
457 */
458 extern void jw_nuke( void* st ) {
459         char*   buf;                                    // pointer to the original json to free
460
461         if( st != NULL ) {
462                 rmr_sym_foreach_class( st, OBJ_SPACE, nix_things, NULL );       // free any json thing that the symtab references
463                 rmr_sym_foreach_class( st, MGT_SPACE, nix_mgt, NULL );          // free management things
464                 rmr_sym_free( st );                                                                                     // free the symtab itself
465         }
466 }
467
468 /*
469         Scan the given st and write some useful (?) info to stderr.
470 */
471 extern void jw_dump( void* st ) {
472         if( st != NULL ) {
473                 rmr_sym_foreach_class( st, OBJ_SPACE, dump_things, NULL );
474         } else {
475                 fprintf( stderr, "<DBUG> jwrapper: dump: no table\n" );
476         }
477 }
478
479 /*
480         Given a json string, parse it, and put the things into a symtab.
481         return the symtab pointer to the caller. They pass the symtab
482         pointer back to the various get functions.
483
484         This is the entry point. It sets up the symbol table and invokes the parse object
485         funtion to start at the first level. Parse object will recurse for nested objects
486         if present.
487 */
488 extern void* jw_new( const char* json ) {
489         void    *st = NULL;                     // symbol table
490         char*   djson;                          // dup so we can save it
491         void*   rp = NULL;                      // return value
492
493         if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS/4 )) != NULL ) {
494                 djson = strdup( json );                                                                                                 // allows user to free/overlay their buffer as needed
495                 rp =  parse_jobject( st,  djson, "" );                  // empty prefix for the root object; parse_jo will clean up and free st
496                 if( rp == NULL ) {
497                         free( djson );
498                 } else {
499                         rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, MGT_SPACE, djson );   // must have a reference to the string until symtab is trashed
500                 }
501         }
502
503         return rp;
504 }
505
506 /*
507         Returns true (1) if the named field is missing.
508 */
509 extern int jw_missing( void* st, const char* name ) {
510         int rv = 0;
511
512         if( st != NULL && name != NULL ) {
513                 rv = rmr_sym_get( st, name, OBJ_SPACE ) == NULL;
514         }
515
516         return rv;
517 }
518
519 /*
520         Returns true (1) if the named field is in the blob;
521 */
522 extern int jw_exists( void* st, const char* name ) {
523         int rv = 0;
524
525         if( st != NULL && name != NULL ) {
526                 rv =  rmr_sym_get( st, name, OBJ_SPACE ) != NULL;
527         }
528
529         return rv;
530 }
531
532 /*
533         Returns true (1) if the primative type is value (double).
534 */
535 extern int jw_is_value( void* st, const char* name ) {
536         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
537         int rv = 0;
538
539         if( st != NULL &&
540                 name != NULL &&
541                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
542
543                 rv = jtp->prim_type == PT_VALUE;
544         }
545
546         return rv;
547 }
548 /*
549         Returns true (1) if the primative type is string.
550 */
551 extern int jw_is_string( void* st, const char* name ) {
552         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
553         int rv = 0;
554
555         if( st != NULL &&
556                 name != NULL &&
557                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
558
559                 rv = jtp->prim_type == PT_STRING;
560         }
561
562         return rv;
563 }
564
565 /*
566         Returns true (1) if the primative type is boolean.
567 */
568 extern int jw_is_bool( void* st, const char* name ) {
569         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
570         int             rv = 0;
571
572         if( st != NULL &&
573                 name != NULL &&
574                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
575
576                 rv =  jtp->prim_type == PT_BOOL;
577         }
578
579         return rv;
580 }
581
582 /*
583         Returns true (1) if the primative type was a 'null' type.
584 */
585 extern int jw_is_null( void* st, const char* name ) {
586         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
587         int             rv = 0;
588
589         if( st != NULL &&
590                 name != NULL &&
591                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
592
593                 rv = jtp->prim_type == PT_NULL;
594         }
595
596         return rv;
597 }
598
599 /*
600         Look up the name in the symtab and return the string (data).
601 */
602 extern char* jw_string( void* st, const char* name ) {
603         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
604         char*           rv = NULL;
605
606         if( st != NULL &&
607                 name != NULL &&
608                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
609
610                 if( jtp->jsmn_type == JSMN_STRING ) {
611                         rv = (char *) jtp->v.pv;
612                 }
613         }
614
615         return rv;
616 }
617
618 /*
619         Look up name and return the value.
620 */
621 extern double jw_value( void* st, const char* name ) {
622         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
623         double  rv = 0.0;
624
625         if( st != NULL &&
626                 name != NULL &&
627                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
628
629                 if( jtp->jsmn_type == JSMN_PRIMITIVE ) {
630                         rv = jtp->v.fv;
631                 }
632         }
633
634         return rv;
635 }
636
637 /*
638         Look up name and return the blob (symtab).
639 */
640 extern void* jw_blob( void* st, const char* name ) {
641         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
642         void*   rv = NULL;
643
644         if( st != NULL &&
645                 name != NULL &&
646                 (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
647
648                 if( jtp->jsmn_type == JSMN_OBJECT ) {
649                         rv = (void *) jtp->v.pv;
650                 }
651         }
652
653         return rv;
654 }
655
656 /*
657         Look up the element and return boolean state; This takes the C approach and
658         returns true/false based on the value.
659 */
660 extern int jw_bool_ele( void* st, const char* name, int idx ) {
661         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
662         int             rv = 0;
663
664         if( st != NULL &&
665                 name != NULL &&
666                 (jtp = suss_element( st, name, idx )) != NULL ) {
667
668                         rv = !! ((int) jtp->v.fv);
669         }
670
671         return rv;
672 }
673 /*
674         Look up array element as a string. Returns NULL if:
675                 name is not an array
676                 name is not in the hash
677                 index is out of range
678                 element is not a string
679 */
680 extern char* jw_string_ele( void* st, const char* name, int idx ) {
681         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
682         char*           rv = NULL;
683
684         if( st != NULL &&
685                 name != NULL &&
686                 (jtp = suss_element( st, name, idx )) != NULL ) {
687
688                 if( jtp->jsmn_type == JSMN_STRING ) {
689                         rv = (char *) jtp->v.pv;
690                 }
691         }
692
693         return rv;
694 }
695
696 /*
697         Look up array element as a value. Returns 0 if:
698                 name is not an array
699                 name is not in the hash
700                 index is out of range
701                 element is not a value
702 */
703 extern double jw_value_ele( void* st, const char* name, int idx ) {
704         jthing_t*       jtp;                                                    // thing that is referenced by the symtab entry
705         double          rv = 0.0;
706
707         if( st != NULL &&
708                 name != NULL &&
709                 (jtp = suss_element( st, name, idx )) != NULL ) {
710
711                 if( jtp->prim_type == PT_VALUE ) {
712                         rv = jtp->v.fv;
713                 }
714         }
715
716         return rv;
717 }
718 /*
719         Look up the element and check to see if it is a string.
720         Return true (1) if it is.
721 */
722 extern int jw_is_string_ele( void* st, const char* name, int idx ) {
723         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
724         int             rv = 0;
725
726         if( st != NULL &&
727                 name != NULL &&
728                 (jtp = suss_element( st, name, idx )) != NULL ) {
729
730                         rv = jtp->prim_type == PT_STRING;
731         }
732
733         return rv;
734 }
735
736 /*
737         Look up the element and check to see if it is a value primative.
738         Return true (1) if it is.
739 */
740 extern int jw_is_value_ele( void* st, const char* name, int idx ) {
741         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
742         int             rv = 0;
743
744         if( st != NULL &&
745                 name != NULL &&
746                 (jtp = suss_element( st, name, idx )) != NULL ) {
747
748                         rv = jtp->prim_type == PT_VALUE;
749         }
750
751         return rv;
752 }
753
754 /*
755         Look up the element and check to see if it is a boolean primative.
756         Return true (1) if it is.
757 */
758 extern int jw_is_bool_ele( void* st, const char* name, int idx ) {
759         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
760         int             rv = 0;
761
762         if( st != NULL &&
763                 name != NULL &&
764                 (jtp = suss_element( st, name, idx )) != NULL ) {
765
766                         rv = jtp->prim_type == PT_BOOL;
767         }
768
769         return rv;
770 }
771
772 /*
773         Look up the element and check to see if it is a null primative.
774         Return true (1) if it is.
775 */
776 extern int jw_is_null_ele( void* st, const char* name, int idx ) {
777         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
778         int             rv = 0;
779
780         if( st != NULL &&
781                 name != NULL &&
782                 (jtp = suss_element( st, name, idx )) != NULL ) {
783
784                         rv =  jtp->prim_type == PT_NULL;
785         }
786
787         return rv;
788 }
789
790 /*
791         Look up array element as an object. Returns NULL if:
792                 name is not an array
793                 name is not in the hash
794                 index is out of range
795                 element is not an object
796
797         An object in an array is a standalone symbol table. Thus the object
798         is treated differently than a nested object whose members are a
799         part of the parent namespace.  An object in an array has its own
800         namespace.
801 */
802 extern void* jw_obj_ele( void* st, const char* name, int idx ) {
803         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
804         void*           rv = NULL;
805
806         if( st != NULL &&
807                 name != NULL &&
808                 (jtp = suss_element( st, name, idx )) != NULL ) {
809
810                 if( jtp->jsmn_type == JSMN_OBJECT ) {
811                         rv = (void *) jtp->v.pv;
812                 }
813         }
814
815         return rv;
816 }
817
818 /*
819         Return the size of the array named. Returns -1 if the thing isn't an array,
820         and returns the number of elements otherwise.
821 */
822 extern int jw_array_len( void* st, const char* name ) {
823         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
824         int             rv = -1;
825
826         if( st != NULL &&
827                 name != NULL &&
828                 (jtp = suss_array( st, name )) != NULL ) {
829
830                 rv = jtp->nele;
831         }
832
833         return rv;
834 }