This code is based on the AT&T VFd open source library available
on github.com/att/vfd. The changes are mostly to port to the
- RMR version of symtab from VFd's version.
+ RMR version of symtab from VFd's version, however the parsing loop
+ was considerably refactored to eliminate the bad practices in the
+ original code, and to squelch the sonar complaint about a potential,
+ though not valid, NPE.
Author: E. Scott Daniels
Date: 26 June 2020
#define JSMN_STATIC 1 // jsmn no longer builds into a library; this pulls as static functions
#include <jsmn.h>
-//#include <../../ext/jsmn/jsmn.h>
#include <rmr/rmr_symtab.h>
+extern void jw_nuke( void* st );
+
#define JSON_SYM_NAME "_jw_json_string"
#define MAX_THINGS 1024 * 4 // max objects/elements
#define OBJ_SPACE 1 // space in the symbol table where json bits are stashed
#define MGT_SPACE 2 // non-json objects in the hash (management things)
-extern void jw_nuke( void* st );
// ---------------------------------------------------------------------------------------
we don't create a bunch of small buffers that must be found and freed; we
can just release the json string and we'll be done (read won't leak).
*/
-static char* extract( char* buf, jsmntok_t *jtoken ) {
+static char* extract( char* buf, const jsmntok_t *jtoken ) {
buf[jtoken->end] = 0;
return &buf[jtoken->start];
}
+#if DEBUG > 0
+/*
+ For debugging we should NOT extract as that disrupts the "flow" by
+ adding a nil before the parser gets a chance to actually parse an
+ object.
+*/
+static void pull( const char* dest, char* src, jsmntok_t* jtoken ) {
+ memcpy( dest, &src[jtoken->start], jtoken->end - jtoken->start );
+ dest[jtoken->end - jtoken->start] = 0;
+}
+#endif
+
/*
create a new jthing and add a reference to it in the symbol table st.
sets the number of elements to 1 by default.
static jthing_t *mk_thing( void *st, char *name, int jsmn_type ) {
jthing_t *jtp = NULL;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) malloc( sizeof( *jtp ) )) != NULL ) {
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) malloc( sizeof( *jtp ) )) != NULL ) {
- if( DEBUG ) {
- fprintf( stderr, "<DBUG> jwrapper adding: %s type=%d\n", name, jsmn_type );
- }
+ if( DEBUG > 1 ) fprintf( stderr, "<DBUG> jwrapper adding: %s type=%d\n", name, jsmn_type );
- jtp->jsmn_type = jsmn_type;
- jtp->prim_type = PT_UNKNOWN; // caller must set this
- jtp->nele = 1;
- jtp->v.fv = 0;
+ jtp->jsmn_type = jsmn_type;
+ jtp->prim_type = PT_UNKNOWN; // caller must set this
+ jtp->nele = 1;
+ jtp->v.fv = 0;
- rmr_sym_put( st, name, OBJ_SPACE, jtp );
- } else {
- fprintf( stderr, "[WARN] jwrapper: unable to create '%s' type=%d\n", name, jsmn_type );
+ rmr_sym_put( st, name, OBJ_SPACE, jtp );
+ } else {
+ fprintf( stderr, "[WARN] jwrapper: unable to create '%s' type=%d\n", name, jsmn_type );
+ }
}
return jtp;
static jthing_t* suss_array( void* st, const char* name ) {
jthing_t* jtp = NULL; // thing that is referenced by the symtab
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
-
- jtp = jtp->jsmn_type == JSMN_ARRAY ? jtp : NULL;
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
+ jtp = jtp->jsmn_type == JSMN_ARRAY ? jtp : NULL;
+ }
}
return jtp;
jthing_t* jarray;
jthing_t* rv = NULL;
- if( (jtp = suss_array( st, name )) != NULL && // have pointer
- idx >= 0 && // and in range
- idx < jtp->nele ) {
-
- if( (jarray = jtp->v.pv) != NULL ) {
+ if( (jtp = suss_array( st, name )) != NULL // have pointer
+ && idx >= 0 // and in range
+ && idx < jtp->nele
+ && jtp->v.pv != NULL ) { // and exists
+ jarray = jtp->v.pv;
rv = &jarray[idx];
- }
}
return rv;
/*
Invoked for each thing in the symtab; we free the things that actually point to
allocated data (e.g. arrays) and recurse to handle objects.
+
+ Only the element passed is used, but this is a prototype which is required by
+ the RMR symtab implementaion, so we play games in the code to keep sonar quiet.
+
+
+ Sonar will grumble aobut the parms needing to be marked const. Until RMR changes
+ the signature we can't and sonar will just have to unbunch its knickers.
*/
-static void nix_things( void* st, void* se, const char* name, void* ele, void *data ) {
+static void nix_things( void* st, void* se, const char* name, void* ele, void *data ) {
jthing_t* j;
jthing_t* jarray;
int i;
- j = (jthing_t *) ele;
- if( j ) {
- switch( j->jsmn_type ) {
- case JSMN_ARRAY:
- if( (jarray = (jthing_t *) j->v.pv) != NULL ) {
- for( i = 0; i < j->nele; i++ ) { // must look for embedded objects
- if( jarray[i].jsmn_type == JSMN_OBJECT ) {
- jw_nuke( jarray[i].v.pv );
- jarray[i].jsmn_type = JSMN_UNDEFINED; // prevent accidents
- }
- }
+ if( (j = (jthing_t *) ele) == NULL ) {
+ if( st == NULL && name == NULL && se == NULL && data == NULL ) { // these are ignored, but this keeps sonar from screaming bug
+ fprintf( stderr, "jwrapper: nix_thigs: all params were nil\n" );
+ }
+ return;
+ }
- free( j->v.pv ); // must free the array (arrays aren't nested, so all things in the array don't reference allocated mem)
- free( j );
+ switch( j->jsmn_type ) {
+ case JSMN_ARRAY:
+ if( (jarray = (jthing_t *) j->v.pv) != NULL ) {
+ for( i = 0; i < j->nele; i++ ) { // must look for embedded objects
+ if( jarray[i].jsmn_type == JSMN_OBJECT ) {
+ jw_nuke( jarray[i].v.pv );
+ jarray[i].jsmn_type = JSMN_UNDEFINED; // prevent accidents
+ }
}
- break;
- case JSMN_OBJECT: // delete the sub symtab
- jw_nuke( j->v.pv );
- j->jsmn_type = JSMN_UNDEFINED; // prevent a double free
+ free( j->v.pv ); // must free the array (arrays aren't nested, so all things in the array don't reference allocated mem)
free( j );
- break;
-
- case JSMN_STRING:
- case JSMN_PRIMITIVE:
- free( j );
- break;
- }
+ }
+ break;
+
+ case JSMN_OBJECT: // delete the sub symtab
+ jw_nuke( j->v.pv );
+ j->jsmn_type = JSMN_UNDEFINED; // prevent a double free
+ free( j );
+ break;
+
+ case JSMN_STRING:
+ case JSMN_PRIMITIVE:
+ free( j );
+ break;
+
+ default:
+ break; // more unneeded games to keep sonar complaints away
}
}
/*
Nix non-json things that are also in the hash.
+
+ Silly games played to keep sonar from complaining. This is driven by RMR
+ symtab code which defines the set of params and we use what we need.
+
+ Sonar will grumble aobut the parms needing to be marked const. Until RMR changes
+ the signature we can't and sonar will just have to unbunch its knickers.
*/
static void nix_mgt( void* st, void* se, const char* name, void* ele, void *data ) {
+
+ if( ele == NULL ) {
+ if( st == NULL && name == NULL && se == NULL && data == NULL ) { // these are ignored, but this keeps sonar from screaming bug
+ fprintf( stderr, "jwrapper: dump_things: all params were nil\n" );
+ }
+
+ return;
+ }
+
free( ele );
}
/*
Invoked for each thing and prints what we can to stderr.
+ Most parms ignored, but symtab code in RMR defines the prototype so they are required.
+
+ Sonar will grumble aobut the parms needing to be marked const. Until RMR changes
+ the signature we can't and sonar will just have to unbunch its knickers.
*/
static void dump_things( void* st, void* se, const char* name, void* ele, void *data ) {
- jthing_t* j;
- jthing_t* jarray;
- int i;
+ const jthing_t* j;
j = (jthing_t *) ele;
if( j ) {
fprintf( stderr, "<DBUG> jwrapper: element '%s' has ptype %d, jsmn type %d\n", name, j->prim_type, j->jsmn_type );
} else {
+ if( st == NULL && name == NULL && se == NULL && data == NULL ) { // these are ignored, but this keeps sonar from screaming bug
+ fprintf( stderr, "jwrapper: dump_things: all params were nil\n" );
+ }
fprintf( stderr, "<DBUG> jwrapper: element has no data: '%s'\n", name );
}
}
/*
Real work for parsing an object ({...}) from the json. Called by jw_new() and
recurses to deal with sub-objects.
+
+ The jsmn parser returns an array of tokens which are expected to be name/data
+ pair. Name must be jsmn string type or we will fail the parse. Data can be
+ any primative type (int,bool) or a complex type (array, object). An array
+ will allow an object as an element, but NOT a nested array; that will cause us
+ to report a failure.
*/
void* parse_jobject( void* st, char *json, char* prefix ) {
jthing_t *jtp; // json thing that we just created
char *data; // data string from the json
jthing_t* jarray; // array of jthings we'll coonstruct
int size;
- int osize;
int njtokens; // tokens actually sussed out
jsmn_parser jp; // 'parser' object
jsmntok_t *jtokens; // pointer to tokens returned by the parser
char pname[1024]; // name with prefix
- char wbuf[256]; // temp buf to build a working name in
char* dstr; // dup'd string
+ int step = 0; // parsing step value to skip tokens picked up
+ int data_idx; // index into tokens for the next bit of data
+ int di; // data skip index for object hopping
+ int stop; // loop index termination point
jsmn_init( &jp ); // does this have a failure mode?
+ if( DEBUG ) fprintf( stderr, "<DBUG>> ================= recursion begins =======================\n" );
jtokens = (jsmntok_t *) malloc( sizeof( *jtokens ) * MAX_THINGS );
if( jtokens == NULL ) {
fprintf( stderr, "[CRI] jwrapper: cannot allocate tokens array\n" );
return NULL;
}
+
+ if( DEBUG > 1 ) fprintf( stderr, "<DBUG> parsing(%s)\n", json );
njtokens = jsmn_parse( &jp, json, strlen( json ), jtokens, MAX_THINGS );
+ if( DEBUG ) fprintf( stderr, "<DBUG>> tokens=%d\n", njtokens );
+#if DEBUG > 1
+ for( di = 0; di < njtokens; di++ ) {
+ wbuf[4096];
+ pull( wbuf, json, &jtokens[di] );
+ fprintf( stderr, "<DBUG> [%d] t=%d start=%d end=%d (%s)\n",
+ di, jtokens[di].type, jtokens[di].start, jtokens[di].end, wbuf );
+ }
+ }
+#endif
+
if( jtokens[0].type != JSMN_OBJECT ) { // if it's not an object then we can't parse it.
fprintf( stderr, "[WARN] jwrapper: badly formed json; initial opening bracket ({) not detected\n" );
rmr_sym_free( st );
return NULL;
}
- if( DEBUG ) {
- for( i = 1; i < njtokens-1; i++ ) {
- fprintf( stderr, "<DBUG> %4d: size=%d start=%d end=%d %s\n", i, jtokens[i].size, jtokens[i].start, jtokens[i].end, extract( json, &jtokens[i] ) );
- }
- }
+ i = 1;
+ while( i < njtokens ) { // a final name without data will end up being silently skipped
+ step = 2; // will always need to step over name and data; object and array will add to this
- for( i = 1; i < njtokens-1; i++ ) { // we'll silently skip the last token if it's "name" without a value
if( jtokens[i].type != JSMN_STRING ) {
- fprintf( stderr, "[WARN] jwrapper: badly formed json [%d]; expected name (string) found type=%d %s\n", i, jtokens[i].type, extract( json, &jtokens[i] ) );
+ fprintf( stderr, "[WARN] jwrapper: badly formed json [%d]; expected name (string) found type=%d %s\n",
+ i, jtokens[i].type, extract( json, &jtokens[i] ) );
rmr_sym_free( st );
free( jtokens );
return NULL;
}
name = extract( json, &jtokens[i] );
+ if( DEBUG ) fprintf( stderr, "\n<DBUG> [%d] parsing %s t=%d\n", i, name, jtokens[i].type );
if( *prefix != 0 ) {
snprintf( pname, sizeof( pname ), "%s.%s", prefix, name );
name = pname;
size = jtokens[i].size;
- i++; // at the data token now
- switch( jtokens[i].type ) {
+ data_idx = i + 1; // at data token
+ switch( jtokens[data_idx].type ) {
case JSMN_OBJECT: // save object in two ways: as an object 'blob' and in the current symtab using name as a base (original)
- dstr = strdup( extract( json, &jtokens[i] ) );
- snprintf( wbuf, sizeof( wbuf ), "%s_json", name ); // must stash the json string in the symtab for clean up during nuke
- rmr_sym_put( st, wbuf, MGT_SPACE, dstr );
+ if( DEBUG ) fprintf( stderr, "<DBUG> [%d] %s (object) has %d things\n", data_idx, name, jtokens[data_idx].size );
- parse_jobject( st, dstr, name ); // recurse to add the object as objectname.xxxx elements
-
- if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL && // create thing and reference it in current symtab
- (jtp->v.pv = (void *) rmr_sym_alloc( 255 ) ) != NULL ) { // object is just a blob
+ if( (jtp = mk_thing( st, name, jtokens[data_idx].type )) != NULL ) { // create thing and reference it
+ jtp->v.pv = rmr_sym_alloc( 255 ); // object is just a blob; make it
+ }
- dstr = strdup( extract( json, &jtokens[i] ) );
+ if( jtp != NULL && jtp->v.pv != NULL ) { // double check allows for better coverage and keeps sonar happy
+ dstr = strdup( extract( json, &jtokens[data_idx] ) );
rmr_sym_put( jtp->v.pv, JSON_SYM_NAME, MGT_SPACE, dstr ); // must stash json so it is freed during nuke()
- parse_jobject( jtp->v.pv, dstr, "" ); // recurse acorss the string and build a new symtab
+ parse_jobject( jtp->v.pv, dstr, "" ); // recurse across the object and build a new symtab
- size = jtokens[i].end; // done with them, we need to skip them
- i++;
- while( i < njtokens-1 && jtokens[i].end < size ) {
- if( DEBUG ){
+ stop = jtokens[data_idx].end; // calc step; must loop as it's NOT just simple size*2 b/c nested objects are var length
+ if( DEBUG ) fprintf( stderr, "<DBUG> computing step over object elements\n" );
+ for( di = data_idx+1; di < njtokens-1 && jtokens[di].end < stop ; di++ ) {
+ step++;
+ if( DEBUG ) {
fprintf( stderr, "\tskip: [%d] object element start=%d end=%d (%s)\n",
- i, jtokens[i].start, jtokens[i].end, extract( json, &jtokens[i]) );
+ di, jtokens[di].start, jtokens[di].end, extract( json, &jtokens[di]) );
}
- i++;
}
-
- i--; // must allow loop to bump past the last
}
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s object finished step= %d\n", name, step );
break;
case JSMN_ARRAY:
- size = jtokens[i].size; // size is burried here, and not with the name
- jtp = mk_thing( st, name, jtokens[i].type );
+ size = jtokens[data_idx].size; // size is burried here, NOT with the name
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s is array size=%d\n", name, size );
+ jtp = mk_thing( st, name, jtokens[data_idx].type );
- i++; // skip first ele; it is the whole array string which I don't grock the need for, but it's their code...
- if( jtp == NULL ) {
- fprintf( stderr, "[WARN] jwrapper: memory alloc error processing element [%d] in json\n", i );
+ if( jtp == NULL || data_idx + size > njtokens ) {
+ fprintf( stderr, "[WARN] jwrapper: alloc, or size, error processing element [%d] in json; size=%d ntok=%d\n",
+ i, size, njtokens );
rmr_sym_free( st );
free( jtokens );
return NULL;
}
+
+ data_idx++; // skip first ele; it is the whole array string which I don't grock the need for, but it's their code...
jarray = jtp->v.pv = (jsmntok_t *) malloc( sizeof( *jarray ) * size ); // allocate the array
memset( jarray, 0, sizeof( *jarray ) * size );
jtp->nele = size;
for( n = 0; n < size; n++ ) { // for each array element
- jarray[n].prim_type = PT_UNKNOWN; // assume not primative type
- switch( jtokens[i+n].type ) {
+ step = 1; // array elements aren't named, so just one initial step
+ if( DEBUG ) fprintf( stderr, "\n<DBUG> parsing [%d] %s element %d of %d\n", data_idx, name, n, size );
+ jarray[n].prim_type = PT_UNKNOWN; // initially mark as unknown
+
+ switch( jtokens[data_idx].type ) {
case JSMN_OBJECT:
- jarray[n].v.pv = (void *) rmr_sym_alloc( 255 );
+ jarray[n].v.pv = rmr_sym_alloc( 255 );
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] is object size=%d\n", name, n, jtokens[data_idx].size );
if( jarray[n].v.pv != NULL ) {
jarray[n].jsmn_type = JSMN_OBJECT;
- parse_jobject( jarray[n].v.pv, extract( json, &jtokens[i+n] ), "" ); // recurse acorss the string and build a new symtab
- osize = jtokens[i+n].end; // done with them, we need to skip them
- i++;
- while( i+n < njtokens-1 && jtokens[n+i].end < osize ) {
- i++;
+ parse_jobject( jarray[n].v.pv, extract( json, &jtokens[data_idx] ), "" ); // recurse across the object and build a new symtab
+ stop = jtokens[data_idx].end; // same as before, must manually calc step
+ if( DEBUG ) fprintf( stderr, "<DBUG> computing step over object elements start=%d stop=%d\n", data_idx+1, stop );
+ for( di = data_idx+1; di < njtokens-1 && jtokens[di].end < stop ; di++ ) {
+ step++;
+ if( DEBUG > 1 ) {
+ fprintf( stderr, "\tskip: [%d] object element start=%d end=%d (%s)\n",
+ di, jtokens[di].start, jtokens[di].end, extract( json, &jtokens[di]) );
+ }
}
- i--; // allow incr at loop end
}
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] object element finished step= %d\n", name, n, step );
break;
case JSMN_ARRAY:
- fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (array) is not string or primative\n", i, n );
- n += jtokens[i+n].size; // this should skip the nested array
+ fprintf( stderr, "[ERR] jwrapper: %s [%d] array element is not a valid type: nested arrays not supported.\n", name, n );
free( jtp );
free( jarray );
jarray = NULL;
- break;
+ free( jtokens );
+ return NULL;
case JSMN_STRING:
- data = extract( json, &jtokens[i+n] );
+ data = extract( json, &jtokens[data_idx] );
jarray[n].v.pv = (void *) data;
jarray[n].prim_type = PT_STRING;
jarray[n].jsmn_type = JSMN_STRING;
break;
case JSMN_PRIMITIVE:
- data = extract( json, &jtokens[i+n] );
+ data = extract( json, &jtokens[data_idx] );
switch( *data ) {
case 'T':
case 't':
jarray[n].v.fv = 1;
jarray[n].prim_type = PT_BOOL;
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] bool = true\n", name, n );
break;
case 'F':
case 'f':
jarray[n].prim_type = PT_BOOL;
jarray[n].v.fv = 0;
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] bool = false\n", name, n );
break;
case 'N': // assume null, nil, or some variant
case 'n':
jarray[n].prim_type = PT_NULL;
jarray[n].v.fv = 0;
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] null primative\n", name, n );
break;
default:
jarray[n].prim_type = PT_VALUE;
jarray[n].v.fv = strtod( data, NULL ); // store all numerics as double
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] = %.03f\n", name, n, jarray[n].v.fv );
break;
}
jarray[n].jsmn_type = JSMN_PRIMITIVE;
break;
- case JSMN_UNDEFINED:
- // fallthrough
default:
- fprintf( stderr, "[WARN] jwrapper: [%d] array element %d is not valid type (unknown=%d) is not string or primative\n", i, n, jtokens[i].type );
+ if( DEBUG ) fprintf( stderr, "[ERR] jwrapper: %s [%d] array ele is unknown type: %d\n",
+ name, n, jtokens[data_idx].type );
rmr_sym_free( st );
free( jtokens );
return NULL;
- break;
}
+
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s[%d] finished, step = %d\n", name, n, step );
+ data_idx += step;
}
- i += size - 1; // must allow loop to push to next
+ step = data_idx - i; // data_index has been moved along, so it's a simple subtraction at this point
+ if( DEBUG ) fprintf( stderr, "<DBUG> %s array finished, total step = %d\n", name, step );
break;
case JSMN_STRING:
- data = extract( json, &jtokens[i] );
- if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL ) {
+ data = extract( json, &jtokens[data_idx] );
+ if( (jtp = mk_thing( st, name, jtokens[data_idx].type )) != NULL ) {
jtp->prim_type = PT_STRING;
jtp->v.pv = (void *) data; // just point into the large json string
}
break;
case JSMN_PRIMITIVE:
- data = extract( json, &jtokens[i] );
- if( (jtp = mk_thing( st, name, jtokens[i].type )) != NULL ) {
- switch( *data ) { // assume T|t is true and F|f is false
- case 'T':
- case 't':
- jtp->prim_type = PT_BOOL;
- jtp->v.fv = 1;
- break;
-
- case 'F':
- case 'f':
- jtp->prim_type = PT_BOOL;
- jtp->v.fv = 0;
- break;
-
- case 'N': // Null or some form of that
- case 'n':
- jtp->prim_type = PT_NULL;
- jtp->v.fv = 0;
- break;
+ data = extract( json, &jtokens[data_idx] );
- default:
- jtp->prim_type = PT_VALUE;
- jtp->v.fv = strtod( data, NULL ); // store all numerics as double
- break;
- }
+ if( (jtp = mk_thing( st, name, jtokens[data_idx].type )) == NULL ) {
+ break;
+ }
+ switch( *data ) { // assume T|t is true and F|f is false
+ case 'T':
+ case 't':
+ jtp->prim_type = PT_BOOL;
+ jtp->v.fv = 1;
+ break;
+
+ case 'F':
+ case 'f':
+ jtp->prim_type = PT_BOOL;
+ jtp->v.fv = 0;
+ break;
+
+ case 'N': // Null or some form of that
+ case 'n':
+ jtp->prim_type = PT_NULL;
+ jtp->v.fv = 0;
+ break;
+
+ default:
+ jtp->prim_type = PT_VALUE;
+ jtp->v.fv = strtod( data, NULL ); // store all numerics as double
+ break;
}
break;
fprintf( stderr, "[WARN] jwrapper: element [%d] is undefined or of unknown type\n", i );
break;
}
+
+ if( DEBUG ) fprintf( stderr, "<DBUG> stepping i = %d by %d max=%d\n", i, step, njtokens );
+ i += step; // step to next name token
}
+ if( DEBUG ) fprintf( stderr, "<DBUG> ================= recursion ends =======================\n\n" );
free( jtokens );
return st;
}
Destroy all operating structures assocaited with the symtab pointer passed in.
*/
extern void jw_nuke( void* st ) {
- char* buf; // pointer to the original json to free
-
if( st != NULL ) {
rmr_sym_foreach_class( st, OBJ_SPACE, nix_things, NULL ); // free any json thing that the symtab references
rmr_sym_foreach_class( st, MGT_SPACE, nix_mgt, NULL ); // free management things
char* djson; // dup so we can save it
void* rp = NULL; // return value
- if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS/4 )) != NULL ) {
- djson = strdup( json ); // allows user to free/overlay their buffer as needed
- rp = parse_jobject( st, djson, "" ); // empty prefix for the root object; parse_jo will clean up and free st
- if( rp == NULL ) {
- free( djson );
- } else {
- rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, MGT_SPACE, djson ); // must have a reference to the string until symtab is trashed
+ if( json != NULL ) {
+ if( (st = rmr_sym_alloc( MAX_THINGS/4 )) != NULL ) {
+ djson = strdup( json ); // allows user to free/overlay their buffer as needed
+ rp = parse_jobject( st, djson, "" ); // empty prefix for the root object; parse_jo will clean up and free st
+ if( rp == NULL ) {
+ free( djson );
+ } else {
+ rmr_sym_put( st, (unsigned char *) JSON_SYM_NAME, MGT_SPACE, djson ); // must have a reference to the string until symtab is trashed
+ }
}
}
}
/*
- Returns true (1) if the named field is in the blob;
+ Returns true (1) if the named field is in the blob.
*/
extern int jw_exists( void* st, const char* name ) {
int rv = 0;
Returns true (1) if the primative type is value (double).
*/
extern int jw_is_value( void* st, const char* name ) {
- jthing_t* jtp; // thing that is referenced by the symtab
+ const jthing_t* jtp; // thing that is referenced by the symtab
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
-
- rv = jtp->prim_type == PT_VALUE;
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
+ rv = jtp->prim_type == PT_VALUE;
+ }
}
return rv;
Returns true (1) if the primative type is string.
*/
extern int jw_is_string( void* st, const char* name ) {
- jthing_t* jtp; // thing that is referenced by the symtab
+ const jthing_t* jtp; // thing that is referenced by the symtab
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
-
- rv = jtp->prim_type == PT_STRING;
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
+ rv = jtp->prim_type == PT_STRING;
+ }
}
return rv;
Returns true (1) if the primative type is boolean.
*/
extern int jw_is_bool( void* st, const char* name ) {
- jthing_t* jtp; // thing that is referenced by the symtab
+ const jthing_t* jtp; // thing that is referenced by the symtab
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
-
- rv = jtp->prim_type == PT_BOOL;
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
+ rv = jtp->prim_type == PT_BOOL;
+ }
}
return rv;
Returns true (1) if the primative type was a 'null' type.
*/
extern int jw_is_null( void* st, const char* name ) {
- jthing_t* jtp; // thing that is referenced by the symtab
+ const jthing_t* jtp; // thing that is referenced by the symtab
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
-
- rv = jtp->prim_type == PT_NULL;
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
+ rv = jtp->prim_type == PT_NULL;
+ }
}
return rv;
Look up the name in the symtab and return the string (data).
*/
extern char* jw_string( void* st, const char* name ) {
- jthing_t* jtp; // thing that is referenced by the symtab
+ const jthing_t* jtp; // thing that is referenced by the symtab
char* rv = NULL;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
-
- if( jtp->jsmn_type == JSMN_STRING ) {
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL && jtp->jsmn_type == JSMN_STRING ) {
rv = (char *) jtp->v.pv;
}
}
jthing_t* jtp; // thing that is referenced by the symtab
double rv = 0.0;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL && jtp->jsmn_type == JSMN_PRIMITIVE ) {
- if( jtp->jsmn_type == JSMN_PRIMITIVE ) {
rv = jtp->v.fv;
}
}
Look up name and return the blob (symtab).
*/
extern void* jw_blob( void* st, const char* name ) {
- jthing_t* jtp; // thing that is referenced by the symtab
+ const jthing_t* jtp; // thing that is referenced by the symtab
void* rv = NULL;
- if( st != NULL &&
- name != NULL &&
- (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL ) {
-
- if( jtp->jsmn_type == JSMN_OBJECT ) {
+ if( st != NULL && name != NULL ) {
+ if( (jtp = (jthing_t *) rmr_sym_get( st, name, OBJ_SPACE )) != NULL && jtp->jsmn_type == JSMN_OBJECT ) {
rv = (void *) jtp->v.pv;
}
}
returns true/false based on the value.
*/
extern int jw_bool_ele( void* st, const char* name, int idx ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL ) {
rv = !! ((int) jtp->v.fv);
+ }
}
return rv;
element is not a string
*/
extern char* jw_string_ele( void* st, const char* name, int idx ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
char* rv = NULL;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
- if( jtp->jsmn_type == JSMN_STRING ) {
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL && jtp->jsmn_type == JSMN_STRING ) {
rv = (char *) jtp->v.pv;
}
}
element is not a value
*/
extern double jw_value_ele( void* st, const char* name, int idx ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
double rv = 0.0;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
- if( jtp->prim_type == PT_VALUE ) {
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL && jtp->prim_type == PT_VALUE ) {
rv = jtp->v.fv;
}
}
jthing_t* jtp; // thing that is referenced by the symtab entry
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL ) {
rv = jtp->prim_type == PT_STRING;
+ }
}
return rv;
Return true (1) if it is.
*/
extern int jw_is_value_ele( void* st, const char* name, int idx ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL ) {
rv = jtp->prim_type == PT_VALUE;
+ }
}
return rv;
Return true (1) if it is.
*/
extern int jw_is_bool_ele( void* st, const char* name, int idx ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL ) {
rv = jtp->prim_type == PT_BOOL;
+ }
}
return rv;
Return true (1) if it is.
*/
extern int jw_is_null_ele( void* st, const char* name, int idx ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
int rv = 0;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL ) {
rv = jtp->prim_type == PT_NULL;
+ }
}
return rv;
namespace.
*/
extern void* jw_obj_ele( void* st, const char* name, int idx ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
void* rv = NULL;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_element( st, name, idx )) != NULL ) {
-
- if( jtp->jsmn_type == JSMN_OBJECT ) {
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_element( st, name, idx )) != NULL && jtp->jsmn_type == JSMN_OBJECT ) {
rv = (void *) jtp->v.pv;
}
}
and returns the number of elements otherwise.
*/
extern int jw_array_len( void* st, const char* name ) {
- jthing_t* jtp; // thing that is referenced by the symtab entry
+ const jthing_t* jtp; // thing that is referenced by the symtab entry
int rv = -1;
- if( st != NULL &&
- name != NULL &&
- (jtp = suss_array( st, name )) != NULL ) {
-
- rv = jtp->nele;
+ if( st != NULL && name != NULL ) {
+ if( (jtp = suss_array( st, name )) != NULL ) {
+ rv = jtp->nele;
+ }
}
return rv;