From 77eba532227842e00d6463afd9f6fd1ea20c487b Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:25:14 -0400 Subject: [PATCH] Change arr plugin behaviour from exclusive to inclusive range end specifier --- src/ioc/db/dbChannel.c | 113 ++++++++++++++++------------ src/ioc/db/filters/arr.c | 9 ++- src/ioc/db/filters/test/arrTest.cpp | 90 ++++++++++++---------- src/ioc/db/test/arrShorthandTest.c | 17 ++--- 4 files changed, 126 insertions(+), 103 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index ba7ba259f..6385c8801 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -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; diff --git a/src/ioc/db/filters/arr.c b/src/ioc/db/filters/arr.c index b37f393ff..7fa392a08 100644 --- a/src/ioc/db/filters/arr.c +++ b/src/ioc/db/filters/arr.c @@ -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; } diff --git a/src/ioc/db/filters/test/arrTest.cpp b/src/ioc/db/filters/test/arrTest.cpp index 4eb6daed8..76918ac30 100644 --- a/src/ioc/db/filters/test/arrTest.cpp +++ b/src/ioc/db/filters/test/arrTest.cpp @@ -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) diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c index 95504a858..307b73766 100644 --- a/src/ioc/db/test/arrShorthandTest.c +++ b/src/ioc/db/test/arrShorthandTest.c @@ -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);