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