Various improvements:

* A filter's parse routines now return a parse_result (enum)
 * Return value from test filter parse routines is configurable
 * Either call parse_end() or parse_abort(), but never both.
This commit is contained in:
Andrew Johnson
2012-04-27 13:21:40 -04:00
committed by Michael Davidsaver
parent eee5a28983
commit 4324ffd274
3 changed files with 195 additions and 145 deletions

View File

@@ -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;
}

View File

@@ -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 ... */

View File

@@ -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));