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