RIC:1060: Change in PTL
[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, const 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                 && jtp->v.pv != NULL ) {                                                // and exists
164                         jarray = jtp->v.pv;
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
180         Sonar will grumble aobut the parms needing to be marked const. Until RMR changes
181         the signature we can't and sonar will just have to unbunch its knickers.
182 */
183 static void nix_things( void* st, void* se, const char* name, void* ele, void *data ) {
184         jthing_t*       j;
185         jthing_t*       jarray;
186         int i;
187
188         if( (j = (jthing_t *) ele) == NULL )  {
189                 if( st == NULL && name == NULL && se == NULL && data == NULL ) {        // these are ignored, but this keeps sonar from screaming bug
190                         fprintf( stderr, "jwrapper: nix_thigs: all params were nil\n" );
191                 }
192                 return;
193         }
194
195         switch( j->jsmn_type ) {
196                 case JSMN_ARRAY:
197                         if( (jarray = (jthing_t *) j->v.pv)  != NULL ) {
198                                 for( i = 0; i < j->nele; i++ ) {                                        // must look for embedded objects
199                                         if( jarray[i].jsmn_type == JSMN_OBJECT ) {
200                                                 jw_nuke( jarray[i].v.pv );
201                                                 jarray[i].jsmn_type = JSMN_UNDEFINED;                   // prevent accidents
202                                         }
203                                 }
204
205                                 free( j->v.pv );                        // must free the array (arrays aren't nested, so all things in the array don't reference allocated mem)
206                                 free( j );
207                         }
208                         break;
209
210                 case JSMN_OBJECT:                                                       // delete the sub symtab
211                         jw_nuke( j->v.pv );
212                         j->jsmn_type = JSMN_UNDEFINED;                  // prevent a double free
213                         free( j );
214                         break;
215
216                 case JSMN_STRING:
217                 case JSMN_PRIMITIVE:
218                         free( j );
219                         break;
220
221                 default:
222                         break;          // more unneeded games to keep sonar complaints away
223         }
224 }
225
226 /*
227         Nix non-json things that are also in the hash.
228
229         Silly games played to keep sonar from complaining. This is driven by RMR
230         symtab code which defines the set of params and we use what we need.
231
232         Sonar will grumble aobut the parms needing to be marked const. Until RMR changes
233         the signature we can't and sonar will just have to unbunch its knickers.
234 */
235 static void nix_mgt( void* st, void* se, const char* name,  void* ele, void *data ) {
236
237         if( ele == NULL ) {
238                 if( st == NULL && name == NULL && se == NULL && data == NULL ) {        // these are ignored, but this keeps sonar from screaming bug
239                         fprintf( stderr, "jwrapper: dump_things: all params were nil\n" );
240                 }
241
242                 return;
243         }
244
245         free( ele );
246 }
247
248 /*
249         Invoked for each thing and prints what we can to stderr.
250         Most parms ignored, but symtab code in RMR defines the prototype so they are required.
251
252         Sonar will grumble aobut the parms needing to be marked const. Until RMR changes
253         the signature we can't and sonar will just have to unbunch its knickers.
254 */
255 static void dump_things( void* st, void* se, const char* name,  void* ele, void *data ) {
256         const jthing_t* j;
257
258         j = (jthing_t *) ele;
259         if( j ) {
260                 fprintf( stderr, "<DBUG> jwrapper: element '%s' has ptype %d, jsmn type %d\n", name, j->prim_type, j->jsmn_type );
261         } else {
262                 if( st == NULL && name == NULL && se == NULL && data == NULL ) {        // these are ignored, but this keeps sonar from screaming bug
263                         fprintf( stderr, "jwrapper: dump_things: all params were nil\n" );
264                 }
265                 fprintf( stderr, "<DBUG> jwrapper: element has no data: '%s'\n", name );
266         }
267 }
268
269 /*
270         Real work for parsing an object ({...}) from the json.   Called by jw_new() and
271         recurses to deal with sub-objects.
272
273         The jsmn parser returns an array of tokens which are expected to be name/data
274         pair. Name must be jsmn string type or we will fail the parse. Data can be
275         any primative type (int,bool) or a complex type (array, object). An array
276         will allow an object as an element, but NOT a nested array; that will cause us
277         to report a failure.
278 */
279 void* parse_jobject( void* st, char *json, char* prefix ) {
280         jthing_t        *jtp;                   // json thing that we just created
281         int             i;
282         int             n;
283         char    *name;                          // name in the json
284         char    *data;                          // data string from the json
285         jthing_t*       jarray;                 // array of jthings we'll coonstruct
286         int             size;
287         int             njtokens;                       // tokens actually sussed out
288         jsmn_parser jp;                         // 'parser' object
289         jsmntok_t *jtokens;                     // pointer to tokens returned by the parser
290         char    pname[1024];            // name with prefix
291         char*   dstr;                           // dup'd string
292         int             step = 0;                       // parsing step value to skip tokens picked up
293         int             data_idx;                       // index into tokens for the next bit of data
294         int             di;                                     // data skip index for object hopping
295         int             stop;                           // loop index termination point
296
297         jsmn_init( &jp );                       // does this have a failure mode?
298
299         if( DEBUG ) fprintf( stderr, "<DBUG>> ================= recursion begins  =======================\n" );
300         jtokens = (jsmntok_t *) malloc( sizeof( *jtokens ) * MAX_THINGS );
301         if( jtokens == NULL ) {
302                 fprintf( stderr, "[CRI] jwrapper: cannot allocate tokens array\n" );
303                 return NULL;
304         }
305
306
307         if( DEBUG > 1 ) fprintf( stderr, "<DBUG> parsing(%s)\n", json );
308         njtokens = jsmn_parse( &jp, json, strlen( json ), jtokens, MAX_THINGS );
309
310         if( DEBUG ) fprintf( stderr, "<DBUG>> tokens=%d\n", njtokens );
311 #if DEBUG > 1
312                 for( di = 0; di < njtokens; di++ ) {
313                         wbuf[4096];
314                         pull( wbuf, json, &jtokens[di] );
315                         fprintf( stderr, "<DBUG> [%d] t=%d start=%d end=%d (%s)\n",
316                                 di, jtokens[di].type, jtokens[di].start, jtokens[di].end, wbuf );
317                 }
318         }
319 #endif
320
321         if( jtokens[0].type != JSMN_OBJECT ) {                          // if it's not an object then we can't parse it.
322                 fprintf( stderr, "[WARN] jwrapper: badly formed json; initial opening bracket ({) not detected\n" );
323                 rmr_sym_free( st );
324                 free( jtokens );
325                 return NULL;
326         }
327
328         i = 1;
329         while( i < njtokens ) {                                         // a final name without data will end up being silently skipped
330                 step = 2;                                                               // will always need to step over name and data; object and array will add to this
331
332                 if( jtokens[i].type != JSMN_STRING ) {
333                         fprintf( stderr, "[WARN] jwrapper: badly formed json [%d]; expected name (string) found type=%d %s\n",
334                                         i, jtokens[i].type, extract( json, &jtokens[i] ) );
335                         rmr_sym_free( st );
336                         free( jtokens );
337                         return NULL;
338                 }
339                 name = extract( json, &jtokens[i] );
340                 if( DEBUG ) fprintf( stderr, "\n<DBUG> [%d] parsing %s t=%d\n", i, name, jtokens[i].type );
341                 if( *prefix != 0 ) {
342                         snprintf( pname, sizeof( pname ), "%s.%s", prefix, name );
343                         name = pname;
344                 }
345
346                 size = jtokens[i].size;
347
348                 data_idx = i + 1;                                                               // at data token
349                 switch( jtokens[data_idx].type ) {
350                         case JSMN_OBJECT:                               // save object in two ways: as an object 'blob' and in the current symtab using name as a base (original)
351                                 if( DEBUG ) fprintf( stderr, "<DBUG> [%d] %s (object) has %d things\n",  data_idx, name, jtokens[data_idx].size );
352
353                                 if( (jtp = mk_thing( st, name, jtokens[data_idx].type )) != NULL ) {    // create thing and reference it
354                                         jtp->v.pv =  rmr_sym_alloc( 255 );                                                                      // object is just a blob; make it
355                                 }
356
357                                 if( jtp != NULL && jtp->v.pv != NULL ) {                                // double check allows for better coverage and keeps sonar happy
358                                         dstr = strdup( extract( json, &jtokens[data_idx] ) );
359                                         rmr_sym_put( jtp->v.pv, JSON_SYM_NAME, MGT_SPACE, dstr );               // must stash json so it is freed during nuke()
360                                         parse_jobject( jtp->v.pv,  dstr, "" );                                                  // recurse across the object and build a new symtab
361
362                                         stop = jtokens[data_idx].end;           // calc step; must loop as it's NOT just simple size*2 b/c nested objects are var length
363                                         if( DEBUG ) fprintf( stderr, "<DBUG> computing step over object elements\n" );
364                                         for( di = data_idx+1; di < njtokens-1  && jtokens[di].end < stop ; di++ ) {
365                                                 step++;
366                                                 if( DEBUG ) {
367                                                         fprintf( stderr, "\tskip: [%d] object element start=%d end=%d (%s)\n",
368                                                                 di, jtokens[di].start, jtokens[di].end, extract( json, &jtokens[di])  );
369                                                 }
370                                         }
371                                 }
372                                 if( DEBUG ) fprintf( stderr, "<DBUG> %s object finished step= %d\n",  name, step );
373                                 break;
374
375                         case JSMN_ARRAY:
376                                 size = jtokens[data_idx].size;                                                  // size is burried here, NOT with the name
377                                 if( DEBUG ) fprintf( stderr, "<DBUG> %s is array size=%d\n", name, size );
378                                 jtp = mk_thing( st, name, jtokens[data_idx].type );
379
380                                 if( jtp == NULL || data_idx + size > njtokens ) {
381                                         fprintf( stderr, "[WARN] jwrapper: alloc, or size, error processing element [%d] in json; size=%d ntok=%d\n",
382                                                 i, size, njtokens );
383                                         rmr_sym_free( st );
384                                         free( jtokens );
385                                         return NULL;
386                                 }
387
388                                 data_idx++;                     // skip first ele; it is the whole array string which I don't grock the need for, but it's their code...
389                                 jarray = jtp->v.pv = (jsmntok_t *) malloc( sizeof( *jarray ) * size );          // allocate the array
390                                 memset( jarray, 0, sizeof( *jarray ) * size );
391                                 jtp->nele = size;
392
393                                 for( n = 0; n < size; n++ ) {                                                           // for each array element
394                                         step = 1;                       // array elements aren't named, so just one initial step
395
396                                         if( DEBUG ) fprintf( stderr, "\n<DBUG> parsing [%d] %s element %d of %d\n", data_idx, name, n, size );
397                                         jarray[n].prim_type      = PT_UNKNOWN;                                          // initially mark as unknown
398
399                                         switch( jtokens[data_idx].type ) {
400                                                 case JSMN_OBJECT:
401                                                         jarray[n].v.pv = rmr_sym_alloc( 255 );
402                                                         if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] is object size=%d\n", name, n, jtokens[data_idx].size );
403                                                         if( jarray[n].v.pv != NULL ) {
404                                                                 jarray[n].jsmn_type = JSMN_OBJECT;
405                                                                 parse_jobject( jarray[n].v.pv,  extract( json, &jtokens[data_idx]  ), "" );             // recurse across the object and build a new symtab
406                                                                 stop = jtokens[data_idx].end;                                                                   // same as before, must manually calc step
407                                                                 if( DEBUG ) fprintf( stderr, "<DBUG> computing step over object elements start=%d stop=%d\n", data_idx+1, stop );
408                                                                 for( di = data_idx+1; di < njtokens-1  && jtokens[di].end < stop ; di++ ) {
409                                                                         step++;
410                                                                         if( DEBUG > 1 ) {
411                                                                                 fprintf( stderr, "\tskip: [%d] object element start=%d end=%d (%s)\n",
412                                                                                         di, jtokens[di].start, jtokens[di].end, extract( json, &jtokens[di])  );
413                                                                         }
414                                                                 }
415                                                         }
416                                                         if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] object element finished step= %d\n",  name, n, step );
417                                                         break;
418
419                                                 case JSMN_ARRAY:
420                                                         fprintf( stderr, "[ERR] jwrapper: %s [%d] array element is not a valid type: nested arrays not supported.\n", name, n );
421                                                         free( jtp );
422                                                         free( jarray );
423                                                         jarray = NULL;
424                                                         free( jtokens );
425                                                         return NULL;
426
427                                                 case JSMN_STRING:
428                                                         data = extract( json, &jtokens[data_idx] );
429                                                         jarray[n].v.pv = (void *) data;
430                                                         jarray[n].prim_type = PT_STRING;
431                                                         jarray[n].jsmn_type = JSMN_STRING;
432                                                         break;
433
434                                                 case JSMN_PRIMITIVE:
435                                                         data = extract( json, &jtokens[data_idx] );
436                                                         switch( *data ) {
437                                                                 case 'T':
438                                                                 case 't':
439                                                                         jarray[n].v.fv = 1;
440                                                                         jarray[n].prim_type      = PT_BOOL;
441                                                                         if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] bool = true\n", name, n );
442                                                                         break;
443
444                                                                 case 'F':
445                                                                 case 'f':
446                                                                         jarray[n].prim_type      = PT_BOOL;
447                                                                         jarray[n].v.fv = 0;
448                                                                         if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] bool = false\n", name, n );
449                                                                         break;
450
451                                                                 case 'N':                                                                               // assume null, nil, or some variant
452                                                                 case 'n':
453                                                                         jarray[n].prim_type      = PT_NULL;
454                                                                         jarray[n].v.fv = 0;
455                                                                         if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] null primative\n", name, n );
456                                                                         break;
457
458                                                                 default:
459                                                                         jarray[n].prim_type      = PT_VALUE;
460                                                                         jarray[n].v.fv = strtod( data, NULL );          // store all numerics as double
461                                                                         if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] = %.03f\n", name, n, jarray[n].v.fv );
462                                                                         break;
463                                                         }
464
465                                                         jarray[n].jsmn_type = JSMN_PRIMITIVE;
466                                                         break;
467
468                                                 default:
469                                                         if( DEBUG ) fprintf( stderr, "[ERR] jwrapper: %s [%d] array ele is unknown type: %d\n",
470                                                                 name, n, jtokens[data_idx].type  );
471                                                         rmr_sym_free( st );
472                                                         free( jtokens );
473                                                         return NULL;
474                                         }
475
476                                         if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] finished, step = %d\n", name, n, step );
477                                         data_idx += step;
478                                 }
479
480                                 step = data_idx - i;            // data_index has been moved along, so it's a simple subtraction at this point
481                                 if( DEBUG ) fprintf( stderr, "<DBUG> %s array finished, total step = %d\n", name, step );
482                                 break;
483
484                         case JSMN_STRING:
485                                 data = extract( json, &jtokens[data_idx] );
486                                 if( (jtp = mk_thing( st, name, jtokens[data_idx].type )) != NULL ) {
487                                         jtp->prim_type = PT_STRING;
488                                         jtp->v.pv =  (void *) data;                                             // just point into the large json string
489                                 }
490                                 break;
491
492                         case JSMN_PRIMITIVE:
493                                 data = extract( json, &jtokens[data_idx] );
494
495                                 if( (jtp = mk_thing( st, name, jtokens[data_idx].type )) == NULL ) {
496                                         break;
497                                 }
498                                 switch( *data ) {                                                               // assume T|t is true and F|f is false
499                                         case 'T':
500                                         case 't':
501                                                 jtp->prim_type = PT_BOOL;
502                                                 jtp->v.fv = 1;
503                                                 break;
504
505                                         case 'F':
506                                         case 'f':
507                                                 jtp->prim_type = PT_BOOL;
508                                                 jtp->v.fv = 0;
509                                                 break;
510
511                                         case 'N':                                                                       // Null or some form of that
512                                         case 'n':
513                                                 jtp->prim_type = PT_NULL;
514                                                 jtp->v.fv = 0;
515                                                 break;
516
517                                         default:
518                                                 jtp->prim_type = PT_VALUE;
519                                                 jtp->v.fv = strtod( data, NULL );               // store all numerics as double
520                                                 break;
521                                 }
522                                 break;
523
524                         default:
525                                 fprintf( stderr, "[WARN] jwrapper: element [%d] is undefined or of unknown type\n", i );
526                                 break;
527                 }
528
529                 if( DEBUG ) fprintf( stderr, "<DBUG> stepping i = %d  by %d max=%d\n", i, step, njtokens );
530                 i += step;                              // step to next name token
531         }
532
533         if( DEBUG ) fprintf( stderr, "<DBUG> ================= recursion ends  =======================\n\n" );
534         free( jtokens );
535         return st;
536 }
537
538 // --------------- public functions -----------------------------------------------------------------
539
540 /*
541         Destroy all operating structures assocaited with the symtab pointer passed in.
542 */
543 extern void jw_nuke( void* st ) {
544         if( st != NULL ) {
545                 rmr_sym_foreach_class( st, OBJ_SPACE, nix_things, NULL );       // free any json thing that the symtab references
546                 rmr_sym_foreach_class( st, MGT_SPACE, nix_mgt, NULL );          // free management things
547                 rmr_sym_free( st );                                                                                     // free the symtab itself
548         }
549 }
550
551 /*
552         Scan the given st and write some useful (?) info to stderr.
553 */
554 extern void jw_dump( void* st ) {
555         if( st != NULL ) {
556                 rmr_sym_foreach_class( st, OBJ_SPACE, dump_things, NULL );
557         } else {
558                 fprintf( stderr, "<DBUG> jwrapper: dump: no table\n" );
559         }
560 }
561
562 /*
563         Given a json string, parse it, and put the things into a symtab.
564         return the symtab pointer to the caller. They pass the symtab
565         pointer back to the various get functions.
566
567         This is the entry point. It sets up the symbol table and invokes the parse object
568         funtion to start at the first level. Parse object will recurse for nested objects
569         if present.
570 */
571 extern void* jw_new( const char* json ) {
572         void    *st = NULL;                     // symbol table
573         char*   djson;                          // dup so we can save it
574         void*   rp = NULL;                      // return value
575
576         if( json != NULL ) {
577                 if( (st = rmr_sym_alloc( MAX_THINGS/4 )) != NULL ) {
578                         djson = strdup( json );                                                                                                 // allows user to free/overlay their buffer as needed
579                         rp =  parse_jobject( st,  djson, "" );                  // empty prefix for the root object; parse_jo will clean up and free st
580                         if( rp == NULL ) {
581                                 free( djson );
582                         } else {
583                                 rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, MGT_SPACE, djson );   // must have a reference to the string until symtab is trashed
584                         }
585                 }
586         }
587
588         return rp;
589 }
590
591 /*
592         Returns true (1) if the named field is missing.
593 */
594 extern int jw_missing( void* st, const char* name ) {
595         int rv = 0;
596
597         if( st != NULL && name != NULL ) {
598                 rv = rmr_sym_get( st, name, OBJ_SPACE ) == NULL;
599         }
600
601         return rv;
602 }
603
604 /*
605         Returns true (1) if the named field is in the blob.
606 */
607 extern int jw_exists( void* st, const char* name ) {
608         int rv = 0;
609
610         if( st != NULL && name != NULL ) {
611                 rv =  rmr_sym_get( st, name, OBJ_SPACE ) != NULL;
612         }
613
614         return rv;
615 }
616
617 /*
618         Returns true (1) if the primative type is value (double).
619 */
620 extern int jw_is_value( void* st, const char* name ) {
621         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab
622         int rv = 0;
623
624         if( st != NULL && name != NULL ) {
625                 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
626                         rv = jtp->prim_type == PT_VALUE;
627                 }
628         }
629
630         return rv;
631 }
632 /*
633         Returns true (1) if the primative type is string.
634 */
635 extern int jw_is_string( void* st, const char* name ) {
636         const jthing_t* jtp;                                                            // thing that is referenced by the symtab
637         int rv = 0;
638
639         if( st != NULL && name != NULL ) {
640                 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
641                         rv = jtp->prim_type == PT_STRING;
642                 }
643         }
644
645         return rv;
646 }
647
648 /*
649         Returns true (1) if the primative type is boolean.
650 */
651 extern int jw_is_bool( void* st, const char* name ) {
652         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab
653         int             rv = 0;
654
655         if( st != NULL && name != NULL ) {
656                 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
657                         rv =  jtp->prim_type == PT_BOOL;
658                 }
659         }
660
661         return rv;
662 }
663
664 /*
665         Returns true (1) if the primative type was a 'null' type.
666 */
667 extern int jw_is_null( void* st, const char* name ) {
668         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab
669         int             rv = 0;
670
671         if( st != NULL && name != NULL ) {
672                 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
673                         rv = jtp->prim_type == PT_NULL;
674                 }
675         }
676
677         return rv;
678 }
679
680 /*
681         Look up the name in the symtab and return the string (data).
682 */
683 extern char* jw_string( void* st, const char* name ) {
684         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab
685         char*           rv = NULL;
686
687         if( st != NULL && name != NULL ) {
688                 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL && jtp->jsmn_type == JSMN_STRING ) {
689                         rv = (char *) jtp->v.pv;
690                 }
691         }
692
693         return rv;
694 }
695
696 /*
697         Look up name and return the value.
698 */
699 extern double jw_value( void* st, const char* name ) {
700         jthing_t* jtp;                                                                  // thing that is referenced by the symtab
701         double  rv = 0.0;
702
703         if( st != NULL && name != NULL  ) {
704                 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL  && jtp->jsmn_type == JSMN_PRIMITIVE ) {
705
706                         rv = jtp->v.fv;
707                 }
708         }
709
710         return rv;
711 }
712
713 /*
714         Look up name and return the blob (symtab).
715 */
716 extern void* jw_blob( void* st, const char* name ) {
717         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab
718         void*   rv = NULL;
719
720         if( st != NULL && name != NULL ) {
721                 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL  && jtp->jsmn_type == JSMN_OBJECT ) {
722                         rv = (void *) jtp->v.pv;
723                 }
724         }
725
726         return rv;
727 }
728
729 /*
730         Look up the element and return boolean state; This takes the C approach and
731         returns true/false based on the value.
732 */
733 extern int jw_bool_ele( void* st, const char* name, int idx ) {
734         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab entry
735         int             rv = 0;
736
737         if( st != NULL && name != NULL ) {
738                 if( (jtp = suss_element( st, name, idx )) != NULL ) {
739                         rv = !! ((int) jtp->v.fv);
740                 }
741         }
742
743         return rv;
744 }
745 /*
746         Look up array element as a string. Returns NULL if:
747                 name is not an array
748                 name is not in the hash
749                 index is out of range
750                 element is not a string
751 */
752 extern char* jw_string_ele( void* st, const char* name, int idx ) {
753         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab entry
754         char*           rv = NULL;
755
756         if( st != NULL && name != NULL ) {
757                 if( (jtp = suss_element( st, name, idx )) != NULL && jtp->jsmn_type == JSMN_STRING ) {
758                         rv = (char *) jtp->v.pv;
759                 }
760         }
761
762         return rv;
763 }
764
765 /*
766         Look up array element as a value. Returns 0 if:
767                 name is not an array
768                 name is not in the hash
769                 index is out of range
770                 element is not a value
771 */
772 extern double jw_value_ele( void* st, const char* name, int idx ) {
773         const jthing_t* jtp;                                                    // thing that is referenced by the symtab entry
774         double          rv = 0.0;
775
776         if( st != NULL && name != NULL ) {
777                 if( (jtp = suss_element( st, name, idx )) != NULL  && jtp->prim_type == PT_VALUE ) {
778                         rv = jtp->v.fv;
779                 }
780         }
781
782         return rv;
783 }
784 /*
785         Look up the element and check to see if it is a string.
786         Return true (1) if it is.
787 */
788 extern int jw_is_string_ele( void* st, const char* name, int idx ) {
789         jthing_t* jtp;                                                                  // thing that is referenced by the symtab entry
790         int             rv = 0;
791
792         if( st != NULL && name != NULL  ) {
793                 if( (jtp = suss_element( st, name, idx )) != NULL ) {
794                         rv = jtp->prim_type == PT_STRING;
795                 }
796         }
797
798         return rv;
799 }
800
801 /*
802         Look up the element and check to see if it is a value primative.
803         Return true (1) if it is.
804 */
805 extern int jw_is_value_ele( void* st, const char* name, int idx ) {
806         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab entry
807         int             rv = 0;
808
809         if( st != NULL && name != NULL ) {
810                 if( (jtp = suss_element( st, name, idx )) != NULL ) {
811                         rv = jtp->prim_type == PT_VALUE;
812                 }
813         }
814
815         return rv;
816 }
817
818 /*
819         Look up the element and check to see if it is a boolean primative.
820         Return true (1) if it is.
821 */
822 extern int jw_is_bool_ele( void* st, const char* name, int idx ) {
823         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab entry
824         int             rv = 0;
825
826         if( st != NULL && name != NULL ) {
827                 if( (jtp = suss_element( st, name, idx )) != NULL ) {
828                         rv = jtp->prim_type == PT_BOOL;
829                 }
830         }
831
832         return rv;
833 }
834
835 /*
836         Look up the element and check to see if it is a null primative.
837         Return true (1) if it is.
838 */
839 extern int jw_is_null_ele( void* st, const char* name, int idx ) {
840         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab entry
841         int             rv = 0;
842
843         if( st != NULL && name != NULL ) {
844                 if( (jtp = suss_element( st, name, idx )) != NULL ) {
845                         rv =  jtp->prim_type == PT_NULL;
846                 }
847         }
848
849         return rv;
850 }
851
852 /*
853         Look up array element as an object. Returns NULL if:
854                 name is not an array
855                 name is not in the hash
856                 index is out of range
857                 element is not an object
858
859         An object in an array is a standalone symbol table. Thus the object
860         is treated differently than a nested object whose members are a
861         part of the parent namespace.  An object in an array has its own
862         namespace.
863 */
864 extern void* jw_obj_ele( void* st, const char* name, int idx ) {
865         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab entry
866         void*           rv = NULL;
867
868         if( st != NULL && name != NULL ) {
869                 if( (jtp = suss_element( st, name, idx )) != NULL && jtp->jsmn_type == JSMN_OBJECT ) {
870                         rv = (void *) jtp->v.pv;
871                 }
872         }
873
874         return rv;
875 }
876
877 /*
878         Return the size of the array named. Returns -1 if the thing isn't an array,
879         and returns the number of elements otherwise.
880 */
881 extern int jw_array_len( void* st, const char* name ) {
882         const jthing_t* jtp;                                                                    // thing that is referenced by the symtab entry
883         int             rv = -1;
884
885         if( st != NULL && name != NULL ) {
886                 if( (jtp = suss_array( st, name )) != NULL ) {
887                         rv = jtp->nele;
888                 }
889         }
890
891         return rv;
892 }