From 368cf5e2adaaf24b82d4ac28638f06a1aa2ccfae Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Fri, 31 Jul 2020 10:51:24 -0400 Subject: [PATCH] Address sonar grumblings The jwrapper parsing code was refactored to address several grumblings that sonar had about the code and to fix some bad practices. Issue-ID: RIC-629 Signed-off-by: E. Scott Daniels Change-Id: Ia42d676613d7eddbaa66a46f325dccd2ed783ad3 --- CHANGES | 3 + CMakeLists.txt | 2 +- docs/rel-notes.rst | 5 + src/json/jwrapper.c | 505 ++++++++++++++++++++++++++++------------------------ test/Makefile | 2 +- test/jhash_test.cpp | 15 +- 6 files changed, 299 insertions(+), 233 deletions(-) diff --git a/CHANGES b/CHANGES index 2e7cbff..5a9ef6c 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ # squished to one. release = Cherry +2020 July 31; version 2.2.1 + Correct "bugs" according to sonar (RIC-629) + 2020 July 30; version 2.2.0 Add support for configuration file reading and change notification. diff --git a/CMakeLists.txt b/CMakeLists.txt index cd2e3a1..fe6467d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ cmake_minimum_required( VERSION 3.5 ) set( major_version "2" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this set( minor_version "2" ) -set( patch_level "0" ) +set( patch_level "1" ) set( install_root "${CMAKE_INSTALL_PREFIX}" ) set( install_inc "include/ricxfcpp" ) diff --git a/docs/rel-notes.rst b/docs/rel-notes.rst index 13e3542..7f368e9 100644 --- a/docs/rel-notes.rst +++ b/docs/rel-notes.rst @@ -18,6 +18,11 @@ xAPP Framework. Cherry Release ============== +2020 July 31; version 2.2.1 +--------------------------- +Correct "bugs" according to sonar (RIC-629) + + 2020 July 30; version 2.2.0 --------------------------- Add support for configuration file reading and change diff --git a/src/json/jwrapper.c b/src/json/jwrapper.c index 85a2c24..a7c14e1 100644 --- a/src/json/jwrapper.c +++ b/src/json/jwrapper.c @@ -25,7 +25,10 @@ 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 @@ -43,10 +46,11 @@ #define JSMN_STATIC 1 // jsmn no longer builds into a library; this pulls as static functions #include -//#include <../../ext/jsmn/jsmn.h> #include +extern void jw_nuke( void* st ); + #define JSON_SYM_NAME "_jw_json_string" #define MAX_THINGS 1024 * 4 // max objects/elements @@ -59,7 +63,6 @@ #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 ); // --------------------------------------------------------------------------------------- @@ -90,6 +93,18 @@ static char* extract( char* buf, jsmntok_t *jtoken ) { 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. @@ -97,22 +112,20 @@ static char* extract( char* buf, jsmntok_t *jtoken ) { 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, " jwrapper adding: %s type=%d\n", name, jsmn_type ); - } + if( DEBUG > 1 ) fprintf( stderr, " 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; @@ -127,11 +140,10 @@ static jthing_t *mk_thing( void *st, char *name, int jsmn_type ) { 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; @@ -145,13 +157,12 @@ static jthing_t* suss_element( void* st, const char* name, int idx ) { 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( (jtp = suss_array( st, name )) != NULL // have pointer + && idx >= 0 // and in range + && idx < jtp->nele + && (jarray = jtp->v.pv) != NULL ) { // and exists - if( (jarray = jtp->v.pv) != NULL ) { rv = &jarray[idx]; - } } return rv; @@ -161,57 +172,81 @@ static jthing_t* suss_element( void* st, const char* name, int idx ) { /* 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. */ -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 - } - } + st = st; // silly things to keep sonar from complaining + name = name; + se = se; + data = data; - 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; + if( (j = (jthing_t *) ele) == NULL ) { + return; + } - case JSMN_OBJECT: // delete the sub symtab - jw_nuke( j->v.pv ); - j->jsmn_type = JSMN_UNDEFINED; // prevent a double free - free( j ); - break; + 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 + } + } - case JSMN_STRING: - case JSMN_PRIMITIVE: + 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; - } + } + 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. */ static void nix_mgt( void* st, void* se, const char* name, void* ele, void *data ) { + st = st; // silly things to keep sonar from complaining (let's hope the compiler is better than sonar + name = name; // and optimises these out). + se = se; + data = data; + 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. */ 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; + + st = st; // silly things to keep sonar from complaining (let's hope the compiler is better than sonar + name = name; // and optimises these out). + se = se; + data = data; j = (jthing_t *) ele; if( j ) { @@ -224,6 +259,12 @@ static void dump_things( void* st, void* se, const char* name, void* ele, void /* 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 @@ -240,17 +281,35 @@ void* parse_jobject( void* st, char *json, char* prefix ) { 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, "> ================= 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, " parsing(%s)\n", json ); njtokens = jsmn_parse( &jp, json, strlen( json ), jtokens, MAX_THINGS ); + if( DEBUG ) fprintf( stderr, "> tokens=%d\n", njtokens ); +#if DEBUG > 1 + for( di = 0; di < njtokens; di++ ) { + wbuf[4096]; + pull( wbuf, json, &jtokens[di] ); + fprintf( stderr, " [%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 ); @@ -258,20 +317,19 @@ void* parse_jobject( void* st, char *json, char* prefix ) { return NULL; } - if( DEBUG ) { - for( i = 1; i < njtokens-1; i++ ) { - fprintf( stderr, " %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 [%d] parsing %s t=%d\n", i, name, jtokens[i].type ); if( *prefix != 0 ) { snprintf( pname, sizeof( pname ), "%s.%s", prefix, name ); name = pname; @@ -279,163 +337,177 @@ void* parse_jobject( void* st, char *json, char* prefix ) { 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 ); - - parse_jobject( st, dstr, name ); // recurse to add the object as objectname.xxxx elements + if( DEBUG ) fprintf( stderr, " [%d] %s (object) has %d things\n", data_idx, name, jtokens[data_idx].size ); - 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 in current symtab + (jtp->v.pv = (void *) rmr_sym_alloc( 255 ) ) != NULL ) { // object is just a blob - dstr = strdup( extract( json, &jtokens[i] ) ); + 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, " 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, " %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, " %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 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 ); + if( DEBUG ) fprintf( stderr, " %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, " 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, " %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, " %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, " %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, " %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, " %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, " %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, " %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; @@ -443,8 +515,12 @@ void* parse_jobject( void* st, char *json, char* prefix ) { fprintf( stderr, "[WARN] jwrapper: element [%d] is undefined or of unknown type\n", i ); break; } + + if( DEBUG ) fprintf( stderr, " stepping i = %d by %d max=%d\n", i, step, njtokens ); + i += step; // step to next name token } + if( DEBUG ) fprintf( stderr, " ================= recursion ends =======================\n\n" ); free( jtokens ); return st; } @@ -455,8 +531,6 @@ void* parse_jobject( void* st, char *json, char* prefix ) { 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 @@ -489,13 +563,15 @@ extern void* jw_new( const char* json ) { 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 + } } } @@ -516,7 +592,7 @@ extern int jw_missing( void* st, const char* name ) { } /* - 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; @@ -532,14 +608,13 @@ extern int jw_exists( void* st, const char* name ) { 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; @@ -548,14 +623,13 @@ extern int jw_is_value( void* st, const char* name ) { 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; @@ -565,14 +639,13 @@ extern int jw_is_string( void* st, const char* name ) { 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; @@ -582,14 +655,13 @@ extern int jw_is_bool( void* st, const char* name ) { 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; @@ -599,14 +671,11 @@ extern int jw_is_null( void* st, const char* name ) { 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; } } @@ -621,11 +690,9 @@ extern double jw_value( void* st, const char* name ) { 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; } } @@ -637,14 +704,11 @@ extern double jw_value( void* st, const char* name ) { 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; } } @@ -657,14 +721,13 @@ extern void* jw_blob( void* st, const char* name ) { 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; @@ -677,14 +740,11 @@ extern int jw_bool_ele( void* st, const char* name, int idx ) { 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; } } @@ -700,14 +760,11 @@ extern char* jw_string_ele( void* st, const char* name, int idx ) { 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; } } @@ -722,11 +779,10 @@ extern int jw_is_string_ele( void* st, const char* name, int idx ) { 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; @@ -737,14 +793,13 @@ extern int jw_is_string_ele( void* st, const char* name, int idx ) { 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; @@ -755,14 +810,13 @@ extern int jw_is_value_ele( void* st, const char* name, int idx ) { 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; @@ -773,14 +827,13 @@ extern int jw_is_bool_ele( void* st, const char* name, int idx ) { 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; @@ -799,14 +852,11 @@ extern int jw_is_null_ele( void* st, const char* name, int idx ) { 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; } } @@ -819,14 +869,13 @@ extern void* jw_obj_ele( void* st, const char* name, int idx ) { 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; diff --git a/test/Makefile b/test/Makefile index cc47990..2a97c69 100644 --- a/test/Makefile +++ b/test/Makefile @@ -19,7 +19,7 @@ unit_test:: unit_test.cpp rmr_em.o # build a special jwrapper object with coverage settings jwrapper_test.o:: ../src/json/jwrapper.c ../src/json/jwrapper.h - cc $(coverage_opts) -g -I ../src/json -I ../ext/jsmn ../src/json/jwrapper.c -c -o jwrapper_test.o + cc $(coverage_opts) -DDEBUG=0 -g -I ../src/json -I ../ext/jsmn ../src/json/jwrapper.c -c -o jwrapper_test.o jhash_test:: jwrapper_test.o jhash_test.cpp # do NOT link the xapp lib; we include all modules in the test programme diff --git a/test/jhash_test.cpp b/test/jhash_test.cpp index 028fd11..7bb3614 100644 --- a/test/jhash_test.cpp +++ b/test/jhash_test.cpp @@ -204,9 +204,9 @@ int main( int argc, char** argv ) { errors += fail_if( state, "string element check on non-stirng element returned true" ); state = jh->Is_value_ele( (char *) "dues_assistance", 1 ); - errors += fail_if( !state, "string element check on sponser failed" ); + errors += fail_if( !state, "value element type check on value element reported false" ); state = jh->Is_value_ele( (char *) "current_on_dues", 1 ); - errors += fail_if( state, "string element check on non-stirng element returned true" ); + errors += fail_if( state, "value element type check on non-value element returned true" ); state = jh->Is_bool_ele( (char *) "current_on_dues", 1 ); errors += fail_if( !state, "string element check on sponser failed" ); @@ -224,7 +224,14 @@ int main( int argc, char** argv ) { // ---- test sussing of elements from arrays ------------------------------------------------- sval = jh->String_ele( (char *) "sponser", 1 ); - errors += fail_if( sval.compare( "" ) == 0, "get string element failed for sponser" ); + errors += fail_if( sval.compare( "" ) == 0, "get string element failed for sponser (empty string)" ); + errors += fail_if( sval.compare( "slate" ) != 0, "get string element failed for sponser (wrong value for[1])" ); + + sval = jh->String_ele( (char *) "sponser", 0 ); + errors += fail_if( sval.compare( "slate" ) != 0, "get string element failed for sponser (wrong value for [0])" ); + + sval = jh->String_ele( (char *) "sponser", 3 ); + errors += fail_if( sval.compare( "brick" ) != 0, "get string element failed for sponser (wrong value for [3])" ); val = jh->Value_ele( (char *) "dues_assistance", 1 ); errors += fail_if( val == 0.0, "get value element for dues_assistance was zero" ); @@ -238,6 +245,8 @@ int main( int argc, char** argv ) { val = jh->Value( (char *) "timestamp" ); fprintf( stderr, " timestamp: %.10f\n", val ); + jh->Dump(); // for coverage of debug things + // ----- jhashes can be moved, drive that logic for coverage xapp::Jhash j2( "{}" ); -- 2.16.6