Change arr plugin behaviour from exclusive to inclusive range end specifier

This commit is contained in:
Ralph Lange
2012-04-27 13:25:14 -04:00
committed by Michael Davidsaver
parent 0d9d992717
commit 77eba53222
4 changed files with 126 additions and 103 deletions

View File

@@ -330,15 +330,64 @@ if (Func) { \
if (result != parse_continue) goto failure; \
}
static void insertArrPlugin(dbChannel* chan, epicsInt32 start, epicsInt32 incr, epicsInt32 end) {
static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppnext) {
epicsInt32 start = 0;
epicsInt32 end = -1;
epicsInt32 incr = 1;
epicsInt32 l;
char *pnext;
short exist;
chFilter *filter;
const chFilterPlugin *plug;
parse_result result;
long status = 0;
/* If no number is present, strtol() returns 0 and sets pnext=pname,
else pnext points to the first char after the number */
pname++;
l = strtol(pname, &pnext, 0);
exist = pnext - pname;
if (exist) start = l;
pname = pnext;
if (*pname == ']' && exist) {
end = start;
goto insertplug;
}
if (*pname != ':') {
status = S_dbLib_fieldNotFound;
goto finish;
}
pname++;
l = strtol(pname, &pnext, 0);
exist = pnext - pname;
pname = pnext;
if (*pname == ']') {
if (exist) end = l;
goto insertplug;
}
if (exist) incr = l;
if (*pname != ':') {
status = S_dbLib_fieldNotFound;
goto finish;
}
pname++;
l = strtol(pname, &pnext, 0);
exist = pnext - pname;
if (exist) end = l;
pname = pnext;
if (*pname != ']') {
status = S_dbLib_fieldNotFound;
goto finish;
}
insertplug:
pname++;
*ppnext = pname;
plug = dbFindFilter("arr", 3);
if (!plug) {
printf("dbChannelCreate: Required channel filter 'arr' not found\n");
return;
status = S_dbLib_fieldNotFound;
goto finish;
}
/* FIXME: Use a free-list */
@@ -349,15 +398,15 @@ static void insertArrPlugin(dbChannel* chan, epicsInt32 start, epicsInt32 incr,
TRY(filter->plug->fif->parse_start, (filter));
TRY(filter->plug->fif->parse_start_map, (filter));
if (start) {
if (start != 0) {
TRY(filter->plug->fif->parse_map_key, (filter, "s", 1));
TRY(filter->plug->fif->parse_integer, (filter, start));
}
if (incr) {
if (incr != 1) {
TRY(filter->plug->fif->parse_map_key, (filter, "i", 1));
TRY(filter->plug->fif->parse_integer, (filter, incr));
}
if (end) {
if (end != -1) {
TRY(filter->plug->fif->parse_map_key, (filter, "e", 1));
TRY(filter->plug->fif->parse_integer, (filter, end));
}
@@ -365,10 +414,14 @@ static void insertArrPlugin(dbChannel* chan, epicsInt32 start, epicsInt32 incr,
TRY(filter->plug->fif->parse_end, (filter));
ellAdd(&chan->filters, &filter->list_node);
return;
return 0;
failure:
free(filter); // FIXME: Use free-list
status = S_dbLib_fieldNotFound;
finish:
return status;
}
/* Stolen from dbAccess.c: */
@@ -450,51 +503,11 @@ dbChannel * dbChannelCreate(const char *name)
}
if (*pname == '[') {
/* Array subset modifier that calls the arr plugin */
epicsInt32 start = 0;
epicsInt32 end = 0;
epicsInt32 incr = 0;
char * pnext;
short exist;
pname++;
start = strtol(pname, &pnext, 0);
exist = pnext - pname;
pname = pnext;
if (*pname == ']' && exist) {
end = start + 1;
goto insertplug;
}
if (*pname != ':') {
status = S_dbLib_fieldNotFound;
goto finish;
}
pname++;
incr = strtol(pname, &pnext, 0);
pname = pnext;
if (*pname == ']') {
end = incr;
incr = 0;
goto insertplug;
}
if (*pname != ':') {
status = S_dbLib_fieldNotFound;
goto finish;
}
pname++;
end = strtol(pname, &pnext, 0);
pname = pnext;
if (*pname != ']') {
status = S_dbLib_fieldNotFound;
goto finish;
}
insertplug:
insertArrPlugin(chan, start, incr, end);
pname++;
status = parseArrayRange(chan, pname, &pname);
if (status) goto finish;
}
/* JSON may follow a $ */
/* JSON may follow */
if (*pname == '{') {
status = chf_parse(chan, &pname);
if (status) goto finish;

View File

@@ -43,6 +43,7 @@ static void * allocPvt(void)
{
myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
my->incr = 1;
my->end = -1;
return (void *) my;
}
@@ -75,11 +76,11 @@ static long wrapArrayIndices(long *start, const long increment, long *end, const
if (*start < 0) *start = 0;
if (*start > no_elements) *start = no_elements;
if (*end <= 0) *end = no_elements + *end;
if (*end <= 0) *end = 0;
if (*end > no_elements) *end = no_elements;
if (*end < 0) *end = no_elements + *end;
if (*end < 0) *end = 0;
if (*end >= no_elements) *end = no_elements - 1;
if (*end - *start >= 1) len = 1 + (*end - *start - 1 ) / increment;
if (*end - *start >= 0) len = 1 + (*end - *start) / increment;
return len;
}

View File

@@ -186,37 +186,47 @@ static void check(short dbr_type) {
testHead("Ten %s elements from rec, increment 1, full size (default)", typname);
createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1);
testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type);
testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements);
testOk(pch->final_type == valaddr.field_type,
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
testOk(pch->final_no_elements == valaddr.no_elements,
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
TEST1(10, 0, 1, "no offset");
TEST1(10, 4, 1, "wrapped");
dbChannelDelete(pch);
testHead("Ten %s elements from rec, increment 1, out-of-bound start parameter", typname);
createAndOpen(valname, "{\"arr\":{\"s\":-500}}", "out-of-bound start", &pch, 1);
testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type);
testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements);
testOk(pch->final_type == valaddr.field_type,
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
testOk(pch->final_no_elements == valaddr.no_elements,
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
TEST1(10, 4, 1, "wrapped");
dbChannelDelete(pch);
testHead("Ten %s elements from rec, increment 1, out-of-bound end parameter", typname);
createAndOpen(valname, "{\"arr\":{\"e\":500}}", "out-of-bound end", &pch, 1);
testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type);
testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements);
testOk(pch->final_type == valaddr.field_type,
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
testOk(pch->final_no_elements == valaddr.no_elements,
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
TEST1(10, 4, 1, "wrapped");
dbChannelDelete(pch);
testHead("Ten %s elements from rec, increment 1, zero increment parameter", typname);
createAndOpen(valname, "{\"arr\":{\"i\":0}}", "zero increment", &pch, 1);
testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type);
testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements);
testOk(pch->final_type == valaddr.field_type,
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
testOk(pch->final_no_elements == valaddr.no_elements,
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
TEST1(10, 4, 1, "wrapped");
dbChannelDelete(pch);
testHead("Ten %s elements from rec, increment 1, invalid increment parameter", typname);
createAndOpen(valname, "{\"arr\":{\"i\":-30}}", "invalid increment", &pch, 1);
testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type);
testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements);
testOk(pch->final_type == valaddr.field_type,
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type);
testOk(pch->final_no_elements == valaddr.no_elements,
"final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements);
TEST1(10, 4, 1, "wrapped");
dbChannelDelete(pch);
@@ -224,8 +234,10 @@ static void check(short dbr_type) {
testHead("Five %s elements from rec, increment " #Incr ", " Type " addressing", typname); \
createAndOpen(valname, "{\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \
"(" #Left ":" #Incr ":" #Right ")", &pch, 1); \
testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); \
testOk(pch->final_no_elements == 4 / Incr + 1, "final no_elements correct (%d)", 4 / Incr + 1); \
testOk(pch->final_type == valaddr.field_type, \
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \
testOk(pch->final_no_elements == 4 / Incr + 1, \
"final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \
TEST1(5, 0, Incr, "no offset"); \
TEST1(5, 3, Incr, "from upper block"); \
TEST1(5, 5, Incr, "wrapped"); \
@@ -234,24 +246,24 @@ static void check(short dbr_type) {
/* Contiguous block of 5 */
TEST5(1, 2, 7, "regular");
TEST5(1, -8, 7, "left side from-end");
TEST5(1, 2, -3, "right side from-end");
TEST5(1, -8, -3, "both sides from-end");
TEST5(1, 2, 6, "regular");
TEST5(1, -8, 6, "left side from-end");
TEST5(1, 2, -4, "right side from-end");
TEST5(1, -8, -4, "both sides from-end");
/* 5 elements with increment 2 */
TEST5(2, 2, 7, "regular");
TEST5(2, -8, 7, "left side from-end");
TEST5(2, 2, -3, "right side from-end");
TEST5(2, -8, -3, "both sides from-end");
TEST5(2, 2, 6, "regular");
TEST5(2, -8, 6, "left side from-end");
TEST5(2, 2, -4, "right side from-end");
TEST5(2, -8, -4, "both sides from-end");
/* 5 elements with increment 3 */
TEST5(3, 2, 7, "regular");
TEST5(3, -8, 7, "left side from-end");
TEST5(3, 2, -3, "right side from-end");
TEST5(3, -8, -3, "both sides from-end");
TEST5(3, 2, 6, "regular");
TEST5(3, -8, 6, "left side from-end");
TEST5(3, 2, -4, "right side from-end");
TEST5(3, -8, -4, "both sides from-end");
/* From buffer (plugin chain) */
@@ -259,31 +271,33 @@ static void check(short dbr_type) {
testHead("Five %s elements from buffer, increment " #Incr ", " Type " addressing", typname); \
createAndOpen(valname, "{\"arr\":{},\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \
"(" #Left ":" #Incr ":" #Right ")", &pch, 2); \
testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); \
testOk(pch->final_no_elements == 4 / Incr + 1, "final no_elements correct (%d)", 4 / Incr + 1); \
testOk(pch->final_type == valaddr.field_type, \
"final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \
testOk(pch->final_no_elements == 4 / Incr + 1, \
"final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \
TEST1(5, 0, Incr, "no offset"); \
dbChannelDelete(pch);
/* Contiguous block of 5 */
TEST5B(1, 2, 7, "regular");
TEST5B(1, -8, 7, "left side from-end");
TEST5B(1, 2, -3, "right side from-end");
TEST5B(1, -8, -3, "both sides from-end");
TEST5B(1, 2, 6, "regular");
TEST5B(1, -8, 6, "left side from-end");
TEST5B(1, 2, -4, "right side from-end");
TEST5B(1, -8, -4, "both sides from-end");
/* 5 elements with increment 2 */
TEST5B(2, 2, 7, "regular");
TEST5B(2, -8, 7, "left side from-end");
TEST5B(2, 2, -3, "right side from-end");
TEST5B(2, -8, -3, "both sides from-end");
TEST5B(2, 2, 6, "regular");
TEST5B(2, -8, 6, "left side from-end");
TEST5B(2, 2, -4, "right side from-end");
TEST5B(2, -8, -4, "both sides from-end");
/* 5 elements with increment 3 */
TEST5B(3, 2, 7, "regular");
TEST5B(3, -8, 7, "left side from-end");
TEST5B(3, 2, -3, "right side from-end");
TEST5B(3, -8, -3, "both sides from-end");
TEST5B(3, 2, 6, "regular");
TEST5B(3, -8, 6, "left side from-end");
TEST5B(3, 2, -4, "right side from-end");
TEST5B(3, -8, -4, "both sides from-end");
}
MAIN(arrTest)

View File

@@ -44,21 +44,16 @@ static void * allocPvt(void)
{
my.start = 0;
my.incr = 1;
my.end = 0;
my.end = -1;
return &my;
}
static int parse_ok(void *user)
{
return parse_continue;
}
static chfPluginIf myPif = {
allocPvt,
NULL, /* freePvt, */
NULL, /* parse_error, */
parse_ok,
NULL, /* parse_ok, */
NULL, /* channel_open, */
NULL, /* channelRegisterPre, */
@@ -112,10 +107,10 @@ MAIN(chfPluginTest)
TESTBAD("invalid char after 2nd arg [2:3x", "[2:3x");
TESTBAD("invalid char after 3rd arg [2:3:4x", "[2:3:4x");
TESTGOOD("one element [index]", "[2]", 2, 1, 3);
TESTGOOD("to end [s:]", "[2:]", 2, 1, 0);
TESTGOOD("to end [s::]", "[2::]", 2, 1, 0);
TESTGOOD("to end with incr [s:i:]", "[2:3:]", 2, 3, 0);
TESTGOOD("one element [index]", "[2]", 2, 1, 2);
TESTGOOD("to end [s:]", "[2:]", 2, 1, -1);
TESTGOOD("to end [s::]", "[2::]", 2, 1, -1);
TESTGOOD("to end with incr [s:i:]", "[2:3:]", 2, 3, -1);
TESTGOOD("from beginning [:e]", "[:2]", 0, 1, 2);
TESTGOOD("from beginning [::e]", "[::2]", 0, 1, 2);
TESTGOOD("from begin with incr [:i:e]", "[:3:2]", 0, 3, 2);