diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 313ecea72..eabf71350 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -21,7 +21,6 @@ #include "special.h" #include "yajl_parse.h" - typedef struct parseContext { dbChannel *chan; chFilter *filter; @@ -34,31 +33,33 @@ typedef struct filterPlugin { const chFilterIf *fif; } filterPlugin; +#define CALLIF(rtn) !rtn ? parse_stop : rtn -#define CALLIF(rtn) rtn && rtn - -static void chf_value(parseContext *parser, int result) +static void chf_value(parseContext *parser, parse_result *presult) { chFilter *filter = parser->filter; - if (!result || parser->depth > 0) return; + if (*presult == parse_stop || parser->depth > 0) + return; parser->filter = NULL; - if (filter->fif->parse_end(filter)) { + if (filter->fif->parse_end(filter) == parse_continue) { ellAdd(&parser->chan->filters, &filter->node); + } else { + free(filter); // FIXME: Use free-list + *presult = parse_stop; } } - static int chf_null(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_null) (filter ); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_null)(filter ); + chf_value(parser, &result); return result; } @@ -66,11 +67,11 @@ static int chf_boolean(void * ctx, int boolVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_boolean) (filter , boolVal); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_boolean)(filter , boolVal); + chf_value(parser, &result); return result; } @@ -78,11 +79,11 @@ static int chf_integer(void * ctx, long integerVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_integer) (filter , integerVal); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_integer)(filter , integerVal); + chf_value(parser, &result); return result; } @@ -90,11 +91,11 @@ static int chf_double(void * ctx, double doubleVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_double) (filter , doubleVal); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_double)(filter , doubleVal); + chf_value(parser, &result); return result; } @@ -103,11 +104,11 @@ static int chf_string(void * ctx, const unsigned char * stringVal, { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_string) (filter , (const char *) stringVal, stringLen); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_string)(filter , (const char *) stringVal, stringLen); + chf_value(parser, &result); return result; } @@ -118,11 +119,11 @@ static int chf_start_map(void * ctx) if (!filter) { assert(parser->depth == 0); - return 1; /* Opening '{' */ + return parse_continue; /* Opening '{' */ } ++parser->depth; - return CALLIF(filter->fif->parse_start_map) (filter ); + return CALLIF(filter->fif->parse_start_map)(filter ); } static int chf_map_key(void * ctx, const unsigned char * key, @@ -131,10 +132,11 @@ static int chf_map_key(void * ctx, const unsigned char * key, parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; const chFilterIf *fif; - int result; + parse_result result; if (filter) { - return CALLIF(filter->fif->parse_map_key) (filter , (const char *) key, stringLen); + assert(parser->depth > 0); + return CALLIF(filter->fif->parse_map_key)(filter , (const char *) key, stringLen); } assert(parser->depth == 0); @@ -144,6 +146,7 @@ static int chf_map_key(void * ctx, const unsigned char * key, return parse_stop; } + /* FIXME: Use a free-list */ filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating dbChannel filter"); filter->chan = parser->chan; filter->fif = fif; @@ -153,7 +156,7 @@ static int chf_map_key(void * ctx, const unsigned char * key, if (result == parse_continue) { parser->filter = filter; } else { - free(filter); + free(filter); // FIXME: Use free-list } return result; } @@ -162,15 +165,19 @@ static int chf_end_map(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; + parse_result result; - if (filter) { - int result = CALLIF(filter->fif->parse_end_map) (filter ); - - if (--parser->depth == 0) chf_value(parser, result); - return result; + if (!filter) { + assert(parser->depth == 0); + return parse_continue; /* Final closing '}' */ } - assert(parser->depth == 0); - return parse_continue; /* Final closing '}' */ + + assert(parser->depth > 0); + result = CALLIF(filter->fif->parse_end_map)(filter ); + + --parser->depth; + chf_value(parser, &result); + return result; } static int chf_start_array(void * ctx) @@ -180,18 +187,19 @@ static int chf_start_array(void * ctx) assert(filter); ++parser->depth; - return CALLIF(filter->fif->parse_start_array) (filter ); + return CALLIF(filter->fif->parse_start_array)(filter ); } static int chf_end_array(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_end_array) (filter ); - if (--parser->depth == 0) chf_value(parser, result); + result = CALLIF(filter->fif->parse_end_array)(filter ); + --parser->depth; + chf_value(parser, &result); return result; } @@ -202,23 +210,23 @@ static const yajl_callbacks chf_callbacks = static const yajl_parser_config chf_config = { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */ - static long chf_parse(dbChannel *chan, const char *json) { parseContext parser = - { chan, NULL, 0}; + { chan, NULL, 0 }; yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_config, NULL, &parser); size_t jlen = strlen(json); yajl_status ys; long status; - if (!yh) return S_db_noMemory; + if (!yh) + return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); if (ys == yajl_status_insufficient_data) ys = yajl_parse_complete(yh); - switch(ys) { + switch (ys) { case yajl_status_ok: status = 0; break; @@ -244,24 +252,24 @@ static long chf_parse(dbChannel *chan, const char *json) } /* Stolen from dbAccess.c: */ -static short mapDBFToDBR[DBF_NTYPES] = { - /* DBF_STRING => */ DBR_STRING, - /* DBF_CHAR => */ DBR_CHAR, - /* DBF_UCHAR => */ DBR_UCHAR, - /* DBF_SHORT => */ DBR_SHORT, - /* DBF_USHORT => */ DBR_USHORT, - /* DBF_LONG => */ DBR_LONG, - /* DBF_ULONG => */ DBR_ULONG, - /* DBF_FLOAT => */ DBR_FLOAT, - /* DBF_DOUBLE => */ DBR_DOUBLE, - /* DBF_ENUM, => */ DBR_ENUM, - /* DBF_MENU, => */ DBR_ENUM, - /* DBF_DEVICE => */ DBR_ENUM, - /* DBF_INLINK => */ DBR_STRING, - /* DBF_OUTLINK => */ DBR_STRING, - /* DBF_FWDLINK => */ DBR_STRING, - /* DBF_NOACCESS => */ DBR_NOACCESS -}; +static short mapDBFToDBR[DBF_NTYPES] = + { + /* DBF_STRING => */DBR_STRING, + /* DBF_CHAR => */DBR_CHAR, + /* DBF_UCHAR => */DBR_UCHAR, + /* DBF_SHORT => */DBR_SHORT, + /* DBF_USHORT => */DBR_USHORT, + /* DBF_LONG => */DBR_LONG, + /* DBF_ULONG => */DBR_ULONG, + /* DBF_FLOAT => */DBR_FLOAT, + /* DBF_DOUBLE => */DBR_DOUBLE, + /* DBF_ENUM, => */DBR_ENUM, + /* DBF_MENU, => */DBR_ENUM, + /* DBF_DEVICE => */DBR_ENUM, + /* DBF_INLINK => */DBR_STRING, + /* DBF_OUTLINK => */DBR_STRING, + /* DBF_FWDLINK => */DBR_STRING, + /* DBF_NOACCESS => */DBR_NOACCESS }; long dbChannelFind(dbChannel *chan, const char *pname) { @@ -288,25 +296,28 @@ long dbChannelFind(dbChannel *chan, const char *pname) dbInitEntry(pdbbase, &dbEntry); status = dbFindRecordPart(&dbEntry, &pname); - if (status) goto finish; + if (status) + goto finish; - if (*pname == '.') ++pname; + if (*pname == '.') + ++pname; status = dbFindFieldPart(&dbEntry, &pname); if (status == S_dbLib_fieldNotFound) status = dbGetAttributePart(&dbEntry, &pname); - if (status) goto finish; + if (status) + goto finish; - paddr = &chan->addr; + paddr = &chan->addr; pflddes = dbEntry.pflddes; dbfType = pflddes->field_type; - paddr->precord = dbEntry.precnode->precord; - paddr->pfield = dbEntry.pfield; - paddr->pfldDes = pflddes; - paddr->no_elements = 1; - paddr->field_type = dbfType; - paddr->field_size = pflddes->size; - paddr->special = pflddes->special; + paddr->precord = dbEntry.precnode->precord; + paddr->pfield = dbEntry.pfield; + paddr->pfldDes = pflddes; + paddr->no_elements = 1; + paddr->field_type = dbfType; + paddr->field_size = pflddes->size; + paddr->special = pflddes->special; paddr->dbr_field_type = mapDBFToDBR[dbfType]; /* Handle field modifiers */ @@ -315,14 +326,14 @@ long dbChannelFind(dbChannel *chan, const char *pname) case '$': /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { - paddr->no_elements = pflddes->size; - paddr->field_type = DBF_CHAR; - paddr->field_size = 1; + paddr->no_elements = pflddes->size; + paddr->field_type = DBF_CHAR; + paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { /* Clients see a char array, but keep original dbfType */ - paddr->no_elements = PVNAME_STRINGSZ + 12; - paddr->field_size = 1; + paddr->no_elements = PVNAME_STRINGSZ + 12; + paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else { status = S_dbLib_fieldNotFound; @@ -339,8 +350,7 @@ long dbChannelFind(dbChannel *chan, const char *pname) pname++; } -finish: - dbFinishEntry(&dbEntry); + finish: dbFinishEntry(&dbEntry); return status; } @@ -350,7 +360,8 @@ long dbChannelOpen(dbChannel *chan) chFilter *filter; long status = 0; - if (chan->magic != DBCHANNEL_MAGIC) return S_db_notInit; + if (chan->magic != DBCHANNEL_MAGIC) + return S_db_notInit; paddr = &chan->addr; if (paddr->special == SPC_DBADDR) { @@ -359,14 +370,16 @@ long dbChannelOpen(dbChannel *chan) /* Let record type modify the dbAddr */ if (prset && prset->cvt_dbaddr) { status = prset->cvt_dbaddr(paddr); - if (status) return status; + if (status) + return status; } } filter = (chFilter *) ellFirst(&chan->filters); while (filter) { status = filter->fif->channel_open(filter); - if (status) break; + if (status) + break; filter = (chFilter *) ellNext(&filter->node); } return status; @@ -376,7 +389,8 @@ void dbChannelReport(dbChannel *chan, int level) { chFilter *filter; - if (chan->magic != DBCHANNEL_MAGIC) return; + if (chan->magic != DBCHANNEL_MAGIC) + return; filter = (chFilter *) ellFirst(&chan->filters); while (filter) { @@ -389,7 +403,8 @@ long dbChannelClose(dbChannel *chan) { chFilter *filter; - if (chan->magic != DBCHANNEL_MAGIC) return S_db_notInit; + if (chan->magic != DBCHANNEL_MAGIC) + return S_db_notInit; while ((filter = (chFilter *) ellGet(&chan->filters))) { filter->fif->channel_close(filter); @@ -412,16 +427,17 @@ void dbRegisterFilter(const char *name, const chFilterIf *fif) } pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList); - if (pgph) return; + if (pgph) + return; pfilt = dbCalloc(1, sizeof(filterPlugin)); pfilt->name = strdup(name); - pfilt->fif = fif; + pfilt->fif = fif; ellAdd(&pdbbase->filterList, &pfilt->node); pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList); if (!pgph) { - free((void *)pfilt->name); + free((void *) pfilt->name); free(pfilt); printf("dbRegisterFilter: gphAdd failed\n"); return; @@ -431,10 +447,12 @@ void dbRegisterFilter(const char *name, const chFilterIf *fif) const chFilterIf * dbFindFilter(const char *name, size_t len) { - GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, &pdbbase->filterList); + GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, + &pdbbase->filterList); filterPlugin *pfilt; - if (!pgph) return NULL; - pfilt = (filterPlugin *)pgph->userPvt; + if (!pgph) + return NULL; + pfilt = (filterPlugin *) pgph->userPvt; return pfilt->fif; } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index aa43325b0..902245fe6 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -23,7 +23,6 @@ #define S_db_notInit (M_dbAccess|21) /*dbChannel not initialized*/ - /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { epicsUInt32 magic; @@ -34,31 +33,44 @@ typedef struct dbChannel { typedef struct chFilter chFilter; /* Return values from chFilterIf->parse_* routines: */ -enum {parse_stop, parse_continue}; +typedef enum { + parse_stop, parse_continue +} parse_result; /* These routines must be implemented by each filter plug-in */ typedef struct chFilterIf { - /* Parsing event handlers */ - int (* parse_start)(chFilter *filter); + /* Parsing event handlers: */ + parse_result (* parse_start)(chFilter *filter); + /* If parse_start() returns parse_continue for a filter, one of + * parse_abort() or parse_end() will later be called for that same + * filter. + */ void (* parse_abort)(chFilter *filter); - int (* parse_end)(chFilter *filter); + /* If parse_abort() is called it should release any memory allocated + * for this filter; no further parse_...() calls will be made; + */ + parse_result (* parse_end)(chFilter *filter); + /* If parse_end() returns parse_stop it should have released any + * memory allocated for this filter; no further parse_...() calls will + * be made in this case. + */ - int (* parse_null)(chFilter *filter); - int (* parse_boolean)(chFilter *filter, int boolVal); - int (* parse_integer)(chFilter *filter, long integerVal); - int (* parse_double)(chFilter *filter, double doubleVal); - int (* parse_string)(chFilter *filter, const char *stringVal, + parse_result (* parse_null)(chFilter *filter); + parse_result (* parse_boolean)(chFilter *filter, int boolVal); + parse_result (* parse_integer)(chFilter *filter, long integerVal); + parse_result (* parse_double)(chFilter *filter, double doubleVal); + parse_result (* parse_string)(chFilter *filter, const char *stringVal, size_t stringLen); /* NB: stringVal is not zero-terminated: */ - int (* parse_start_map)(chFilter *filter); - int (* parse_map_key)(chFilter *filter, const char *key, - size_t stringLen); - int (* parse_end_map)(chFilter *filter); + parse_result (* parse_start_map)(chFilter *filter); + parse_result (* parse_map_key)(chFilter *filter, const char *key, + size_t stringLen); /* NB: key is not zero-terminated: */ + parse_result (* parse_end_map)(chFilter *filter); - int (* parse_start_array)(chFilter *filter); - int (* parse_end_array)(chFilter *filter); + parse_result (* parse_start_array)(chFilter *filter); + parse_result (* parse_end_array)(chFilter *filter); - /* Channel operations */ + /* Channel operations: */ long (* channel_open)(chFilter *filter); void (* channel_report)(chFilter *filter, int level); /* FIXME: More routines here ... */ diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 221a6b3d5..06b979ede 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -29,76 +29,84 @@ #define e_report 0x00004000 #define e_close 0x00008000 -unsigned int e; +#define r_any (e_start | e_abort | e_end | \ + e_null | e_boolean | e_integer | e_double | e_string | \ + e_start_map | e_map_key | e_end_map | e_start_array | e_end_array) +#define r_scalar (e_start | e_abort | e_end | \ + e_null | e_boolean | e_integer | e_double | e_string) -int p_start(chFilter *filter) +unsigned int e, r; +#define p_ret(x) return r & x ? parse_continue : parse_stop + +parse_result p_start(chFilter *filter) { testOk(e & e_start, "parse_start called"); - return parse_continue; + p_ret(e_start); } void p_abort(chFilter *filter) { testOk(e & e_abort, "parse_abort called"); } -int p_end(chFilter *filter) + +parse_result p_end(chFilter *filter) { testOk(e & e_end, "parse_end called"); - return parse_continue; + p_ret(e_end); } -int p_null(chFilter *filter) +parse_result p_null(chFilter *filter) { testOk(e & e_null, "parse_null called"); - return parse_continue; + p_ret(e_null); } -int p_boolean(chFilter *filter, int boolVal) +parse_result p_boolean(chFilter *filter, int boolVal) { testOk(e & e_boolean, "parse_boolean called, val = %d", boolVal); - return parse_continue; + p_ret(e_boolean); } -int p_integer(chFilter *filter, long integerVal) +parse_result p_integer(chFilter *filter, long integerVal) { testOk(e & e_integer, "parse_integer called, val = %ld", integerVal); - return parse_continue; + p_ret(e_integer); } -int p_double(chFilter *filter, double doubleVal) +parse_result p_double(chFilter *filter, double doubleVal) { testOk(e & e_double, "parse_double called, val = %g", doubleVal); - return parse_continue; + p_ret(e_double); } -int p_string(chFilter *filter, const char *stringVal, size_t stringLen) +parse_result p_string(chFilter *filter, const char *stringVal, size_t stringLen) { testOk(e & e_string, "parse_string called, val = '%.*s'", stringLen, stringVal); - return parse_continue; + p_ret(e_string); } -int p_start_map(chFilter *filter) +parse_result p_start_map(chFilter *filter) { testOk(e & e_start_map, "parse_start_map called"); - return parse_continue; + p_ret(e_start_map); } -int p_map_key(chFilter *filter, const char *key, size_t stringLen) +parse_result p_map_key(chFilter *filter, const char *key, size_t stringLen) { testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", stringLen, key); - return parse_continue; + p_ret(e_map_key); } -int p_end_map(chFilter *filter) +parse_result p_end_map(chFilter *filter) { testOk(e & e_end_map, "parse_end_map called"); - return parse_continue; + p_ret(e_end_map); } -int p_start_array(chFilter *filter) +parse_result p_start_array(chFilter *filter) { testOk(e & e_start_array, "parse_start_array called"); - return parse_continue; + p_ret(e_start_array); } -int p_end_array(chFilter *filter) +parse_result p_end_array(chFilter *filter) { testOk(e & e_end_array, "parse_end_array called"); - return parse_continue; + p_ret(e_end_array); } long c_open(chFilter *filter) @@ -115,39 +123,49 @@ void c_close(chFilter *filter) testOk(e & e_close, "channel_close called"); } -chFilterIf anyIf = +chFilterIf testIf = { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, c_open, c_report, c_close }; -chFilterIf scalarIf = - { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, - p_string, NULL, NULL, NULL, NULL, NULL, c_open, - c_report, c_close}; - MAIN(dbChannelTest) { dbChannel ch; - testPlan(45); + testPlan(59); testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); - e = 0; + r = e = 0; testOk1(!dbChannelFind(&ch, "x.NAME$")); - testOk1(ch.addr.no_elements > 1); + testOk1(ch.addr.no_elements> 1); testOk1(!dbChannelFind(&ch, "x.{}")); - dbRegisterFilter("any", &anyIf); + testOk1(dbChannelFind(&ch, "y")); + testOk1(dbChannelFind(&ch, "x.{\"none\":null}")); + dbRegisterFilter("any", &testIf); + + e = e_start; + testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + + r = e_start; + e = e_start | e_null | e_abort; + testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + + r = e_start | e_null; + e = e_start | e_null | e_end; + testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + + r = r_any; e = e_start | e_null | e_end; testOk1(!dbChannelFind(&ch, "x.{\"any\":null}")); e = e_close; testOk1(!dbChannelClose(&ch)); - dbRegisterFilter("scalar", &scalarIf); + dbRegisterFilter("scalar", &testIf); e = e_start | e_null | e_end; testOk1(!dbChannelFind(&ch, "x.{\"scalar\":null}")); @@ -158,7 +176,8 @@ MAIN(dbChannelTest) e = e_close; testOk1(!dbChannelClose(&ch)); - e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; + e = e_start | e_start_array | e_boolean | e_integer | e_end_array + | e_end; testOk1(!dbChannelFind(&ch, "x.{\"any\":[true,1]}")); e = e_close; testOk1(!dbChannelClose(&ch)); @@ -167,12 +186,13 @@ MAIN(dbChannelTest) | e_end; testOk1(!dbChannelFind(&ch, "x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}")); - e = e_close | e_start | e_abort; + r = r_scalar; + e = e_close | e_start | e_start_array | e_abort; testOk1(dbChannelFind(&ch, "x.{\"scalar\":[null]}")); e = 0; testOk1(!dbChannelClose(&ch)); - e = e_start | e_abort; + e = e_start | e_start_map | e_abort; testOk1(dbChannelFind(&ch, "x.{\"scalar\":{}}")); e = 0; testOk1(!dbChannelClose(&ch));