3 ==================================================================================
4 Copyright (c) 2020 Nokia
5 Copyright (c) 2020 AT&T Intellectual Property.
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
11 http://www.apache.org/licenses/LICENSE-2.0
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 ==================================================================================
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.
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.
33 Author: E. Scott Daniels
47 #define JSMN_STATIC 1 // jsmn no longer builds into a library; this pulls as static functions
50 #include <rmr/rmr_symtab.h>
52 extern void jw_nuke( void* st );
54 #define JSON_SYM_NAME "_jw_json_string"
55 #define MAX_THINGS 1024 * 4 // max objects/elements
57 #define PT_UNKNOWN 0 // primative types; unknown for non prim
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)
67 // ---------------------------------------------------------------------------------------
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.
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
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).
91 static char* extract( char* buf, const jsmntok_t *jtoken ) {
93 return &buf[jtoken->start];
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
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;
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.
112 static jthing_t *mk_thing( void *st, char *name, int jsmn_type ) {
113 jthing_t *jtp = NULL;
115 if( st != NULL && name != NULL ) {
116 if( (jtp = (jthing_t *) malloc( sizeof( *jtp ) )) != NULL ) {
118 if( DEBUG > 1 ) fprintf( stderr, "<DBUG> jwrapper adding: %s type=%d\n", name, jsmn_type );
120 jtp->jsmn_type = jsmn_type;
121 jtp->prim_type = PT_UNKNOWN; // caller must set this
125 rmr_sym_put( st, name, OBJ_SPACE, jtp );
127 fprintf( stderr, "[WARN] jwrapper: unable to create '%s' type=%d\n", name, jsmn_type );
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.
140 static jthing_t* suss_array( void* st, const char* name ) {
141 jthing_t* jtp = NULL; // thing that is referenced by the symtab
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;
153 Suss an array from the hash and return the ith element.
155 static jthing_t* suss_element( void* st, const char* name, int idx ) {
156 jthing_t* jtp; // thing that is referenced by the symtab
160 if( (jtp = suss_array( st, name )) != NULL // have pointer
161 && idx >= 0 // and in range
163 && jtp->v.pv != NULL ) { // and exists
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.
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.
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.
183 static void nix_things( void* st, void* se, const char* name, void* ele, void *data ) {
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" );
195 switch( j->jsmn_type ) {
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
205 free( j->v.pv ); // must free the array (arrays aren't nested, so all things in the array don't reference allocated mem)
210 case JSMN_OBJECT: // delete the sub symtab
212 j->jsmn_type = JSMN_UNDEFINED; // prevent a double free
222 break; // more unneeded games to keep sonar complaints away
227 Nix non-json things that are also in the hash.
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.
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.
235 static void nix_mgt( void* st, void* se, const char* name, void* ele, void *data ) {
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" );
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.
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.
255 static void dump_things( void* st, void* se, const char* name, void* ele, void *data ) {
258 j = (jthing_t *) ele;
260 fprintf( stderr, "<DBUG> jwrapper: element '%s' has ptype %d, jsmn type %d\n", name, j->prim_type, j->jsmn_type );
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" );
265 fprintf( stderr, "<DBUG> jwrapper: element has no data: '%s'\n", name );
270 Real work for parsing an object ({...}) from the json. Called by jw_new() and
271 recurses to deal with sub-objects.
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
279 void* parse_jobject( void* st, char *json, char* prefix ) {
280 jthing_t *jtp; // json thing that we just created
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
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
297 jsmn_init( &jp ); // does this have a failure mode?
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" );
307 if( DEBUG > 1 ) fprintf( stderr, "<DBUG> parsing(%s)\n", json );
308 njtokens = jsmn_parse( &jp, json, strlen( json ), jtokens, MAX_THINGS );
310 if( DEBUG ) fprintf( stderr, "<DBUG>> tokens=%d\n", njtokens );
312 for( di = 0; di < njtokens; di++ ) {
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 );
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" );
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
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] ) );
339 name = extract( json, &jtokens[i] );
340 if( DEBUG ) fprintf( stderr, "\n<DBUG> [%d] parsing %s t=%d\n", i, name, jtokens[i].type );
342 snprintf( pname, sizeof( pname ), "%s.%s", prefix, name );
346 size = jtokens[i].size;
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 );
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
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
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++ ) {
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]) );
372 if( DEBUG ) fprintf( stderr, "<DBUG> %s object finished step= %d\n", name, step );
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 );
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",
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 );
393 for( n = 0; n < size; n++ ) { // for each array element
394 step = 1; // array elements aren't named, so just one initial step
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
399 switch( jtokens[data_idx].type ) {
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++ ) {
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]) );
416 if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] object element finished step= %d\n", name, n, step );
420 fprintf( stderr, "[ERR] jwrapper: %s [%d] array element is not a valid type: nested arrays not supported.\n", name, n );
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;
435 data = extract( json, &jtokens[data_idx] );
440 jarray[n].prim_type = PT_BOOL;
441 if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] bool = true\n", name, n );
446 jarray[n].prim_type = PT_BOOL;
448 if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] bool = false\n", name, n );
451 case 'N': // assume null, nil, or some variant
453 jarray[n].prim_type = PT_NULL;
455 if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] null primative\n", name, n );
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 );
465 jarray[n].jsmn_type = JSMN_PRIMITIVE;
469 if( DEBUG ) fprintf( stderr, "[ERR] jwrapper: %s [%d] array ele is unknown type: %d\n",
470 name, n, jtokens[data_idx].type );
476 if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] finished, step = %d\n", name, n, step );
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 );
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
493 data = extract( json, &jtokens[data_idx] );
495 if( (jtp = mk_thing( st, name, jtokens[data_idx].type )) == NULL ) {
498 switch( *data ) { // assume T|t is true and F|f is false
501 jtp->prim_type = PT_BOOL;
507 jtp->prim_type = PT_BOOL;
511 case 'N': // Null or some form of that
513 jtp->prim_type = PT_NULL;
518 jtp->prim_type = PT_VALUE;
519 jtp->v.fv = strtod( data, NULL ); // store all numerics as double
525 fprintf( stderr, "[WARN] jwrapper: element [%d] is undefined or of unknown type\n", i );
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
533 if( DEBUG ) fprintf( stderr, "<DBUG> ================= recursion ends =======================\n\n" );
538 // --------------- public functions -----------------------------------------------------------------
541 Destroy all operating structures assocaited with the symtab pointer passed in.
543 extern void jw_nuke( void* st ) {
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
552 Scan the given st and write some useful (?) info to stderr.
554 extern void jw_dump( void* st ) {
556 rmr_sym_foreach_class( st, OBJ_SPACE, dump_things, NULL );
558 fprintf( stderr, "<DBUG> jwrapper: dump: no table\n" );
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.
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
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
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
583 rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, MGT_SPACE, djson ); // must have a reference to the string until symtab is trashed
592 Returns true (1) if the named field is missing.
594 extern int jw_missing( void* st, const char* name ) {
597 if( st != NULL && name != NULL ) {
598 rv = rmr_sym_get( st, name, OBJ_SPACE ) == NULL;
605 Returns true (1) if the named field is in the blob.
607 extern int jw_exists( void* st, const char* name ) {
610 if( st != NULL && name != NULL ) {
611 rv = rmr_sym_get( st, name, OBJ_SPACE ) != NULL;
618 Returns true (1) if the primative type is value (double).
620 extern int jw_is_value( void* st, const char* name ) {
621 const jthing_t* jtp; // thing that is referenced by the symtab
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;
633 Returns true (1) if the primative type is string.
635 extern int jw_is_string( void* st, const char* name ) {
636 const jthing_t* jtp; // thing that is referenced by the symtab
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;
649 Returns true (1) if the primative type is boolean.
651 extern int jw_is_bool( void* st, const char* name ) {
652 const jthing_t* jtp; // thing that is referenced by the symtab
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;
665 Returns true (1) if the primative type was a 'null' type.
667 extern int jw_is_null( void* st, const char* name ) {
668 const jthing_t* jtp; // thing that is referenced by the symtab
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;
681 Look up the name in the symtab and return the string (data).
683 extern char* jw_string( void* st, const char* name ) {
684 const jthing_t* jtp; // thing that is referenced by the symtab
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;
697 Look up name and return the value.
699 extern double jw_value( void* st, const char* name ) {
700 jthing_t* jtp; // thing that is referenced by the symtab
703 if( st != NULL && name != NULL ) {
704 if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL && jtp->jsmn_type == JSMN_PRIMITIVE ) {
714 Look up name and return the blob (symtab).
716 extern void* jw_blob( void* st, const char* name ) {
717 const jthing_t* jtp; // thing that is referenced by the symtab
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;
730 Look up the element and return boolean state; This takes the C approach and
731 returns true/false based on the value.
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
737 if( st != NULL && name != NULL ) {
738 if( (jtp = suss_element( st, name, idx )) != NULL ) {
739 rv = !! ((int) jtp->v.fv);
746 Look up array element as a string. Returns NULL if:
748 name is not in the hash
749 index is out of range
750 element is not a string
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
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;
766 Look up array element as a value. Returns 0 if:
768 name is not in the hash
769 index is out of range
770 element is not a value
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
776 if( st != NULL && name != NULL ) {
777 if( (jtp = suss_element( st, name, idx )) != NULL && jtp->prim_type == PT_VALUE ) {
785 Look up the element and check to see if it is a string.
786 Return true (1) if it is.
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
792 if( st != NULL && name != NULL ) {
793 if( (jtp = suss_element( st, name, idx )) != NULL ) {
794 rv = jtp->prim_type == PT_STRING;
802 Look up the element and check to see if it is a value primative.
803 Return true (1) if it is.
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
809 if( st != NULL && name != NULL ) {
810 if( (jtp = suss_element( st, name, idx )) != NULL ) {
811 rv = jtp->prim_type == PT_VALUE;
819 Look up the element and check to see if it is a boolean primative.
820 Return true (1) if it is.
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
826 if( st != NULL && name != NULL ) {
827 if( (jtp = suss_element( st, name, idx )) != NULL ) {
828 rv = jtp->prim_type == PT_BOOL;
836 Look up the element and check to see if it is a null primative.
837 Return true (1) if it is.
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
843 if( st != NULL && name != NULL ) {
844 if( (jtp = suss_element( st, name, idx )) != NULL ) {
845 rv = jtp->prim_type == PT_NULL;
853 Look up array element as an object. Returns NULL if:
855 name is not in the hash
856 index is out of range
857 element is not an object
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
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
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;
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.
881 extern int jw_array_len( void* st, const char* name ) {
882 const jthing_t* jtp; // thing that is referenced by the symtab entry
885 if( st != NULL && name != NULL ) {
886 if( (jtp = suss_array( st, name )) != NULL ) {