Change arr plugin behaviour from exclusive to inclusive range end specifier
This commit is contained in:
committed by
Michael Davidsaver
parent
0d9d992717
commit
77eba53222
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user