refactor db_field_log and filters to get rid of dbfl_type_rec

This refactor simplifies and streamlines the code associated with server
side filters. Apart from immediate benefits (clearer code, less duplication)
it is also hoped that this will make it easier to add write filters.

The data pointer dbfl_ref.field can now either point to a copy owned by a
filter, or it can point to the original data owned by a record. In the
latter case, the dbfl_ref.dtor is NULL.

The dbExtractArray* functions are unified to the single function
dbExtractArray and stripped of conversion functionality. This is redundant
because we always call dbGet after applying filters, which takes care of
conversion. Accordingly, dbChannelMakeArrayCopy is now obsolete and its
single use (in the ts filter) replaced with dbExtractArray. Instead, we add
the helper function dbChannelGetArrayInfo to wrap the common boilerplate
around calls to the get_array_info method, used in both arr.c and ts.c.
This commit is contained in:
Ben Franksen
2020-03-30 21:34:32 +02:00
parent 4ab9808180
commit 27fe3e4468
12 changed files with 239 additions and 281 deletions
+47 -69
View File
@@ -13,16 +13,14 @@
#include <stdio.h>
#include <freeList.h>
#include <dbAccess.h>
#include <dbExtractArray.h>
#include <db_field_log.h>
#include <dbLock.h>
#include <recSup.h>
#include <epicsExit.h>
#include <special.h>
#include <chfPlugin.h>
#include <epicsExport.h>
#include "chfPlugin.h"
#include "dbAccessDefs.h"
#include "dbExtractArray.h"
#include "db_field_log.h"
#include "dbLock.h"
#include "epicsExit.h"
#include "freeList.h"
#include "epicsExport.h"
typedef struct myStruct {
epicsInt32 start;
@@ -46,6 +44,8 @@ static void * allocPvt(void)
myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
if (!my) return NULL;
/* defaults */
my->start = 0;
my->incr = 1;
my->end = -1;
return (void *) my;
@@ -94,78 +94,56 @@ static long wrapArrayIndices(long *start, const long increment, long *end,
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
{
myStruct *my = (myStruct*) pvt;
struct dbCommon *prec;
rset *prset;
int must_lock;
long start = my->start;
long end = my->end;
long nTarget = 0;
long nTarget;
void *pTarget;
long offset = 0;
long nSource = dbChannelElements(chan);
long capacity = nSource;
void *pdst;
long nSource = pfl->no_elements;
void *pSource = pfl->u.r.field;
switch (pfl->type) {
case dbfl_type_val:
/* Only filter arrays */
/* TODO Treat scalars as arrays with 1 element */
break;
case dbfl_type_rec:
/* Extract from record */
if (dbChannelSpecial(chan) == SPC_DBADDR &&
nSource > 1 &&
(prset = dbGetRset(&chan->addr)) &&
prset->get_array_info)
{
void *pfieldsave = dbChannelField(chan);
prec = dbChannelRecord(chan);
dbScanLock(prec);
prset->get_array_info(&chan->addr, &nSource, &offset);
nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
pfl->type = dbfl_type_ref;
pfl->stat = prec->stat;
pfl->sevr = prec->sevr;
pfl->time = prec->time;
pfl->field_type = dbChannelFieldType(chan);
pfl->field_size = dbChannelFieldSize(chan);
pfl->no_elements = nTarget;
if (nTarget) {
pdst = freeListCalloc(my->arrayFreeList);
if (pdst) {
pfl->u.r.dtor = freeArray;
pfl->u.r.pvt = my->arrayFreeList;
offset = (offset + start) % dbChannelElements(chan);
dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity,
offset, my->incr);
pfl->u.r.field = pdst;
}
}
dbScanUnlock(prec);
dbChannelField(chan) = pfieldsave;
}
break;
/* Extract from buffer */
case dbfl_type_ref:
pdst = NULL;
nSource = pfl->no_elements;
nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
pfl->no_elements = nTarget;
if (nTarget) {
/* Copy the data out */
void *psrc = pfl->u.r.field;
pdst = freeListCalloc(my->arrayFreeList);
if (!pdst) break;
offset = start;
dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type,
nTarget, nSource, offset, my->incr);
must_lock = !pfl->u.r.dtor;
if (must_lock) {
dbScanLock(dbChannelRecord(chan));
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
}
if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
if (nTarget) {
nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
if (nTarget > 0) {
/* copy the data */
pTarget = freeListCalloc(my->arrayFreeList);
if (!pTarget) break;
/* must do the wrap-around with the original no_elements */
offset = (offset + start) % pfl->no_elements;
dbExtractArray(pSource, pTarget, pfl->field_size,
nTarget, pfl->no_elements, offset, my->incr);
if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
pfl->u.r.field = pTarget;
pfl->u.r.dtor = freeArray;
pfl->u.r.pvt = my->arrayFreeList;
pfl->u.r.field = pdst;
}
/* Adjust no_elements to refer to the new pTarget.
*
* Setting pfl->no_elements outside of the "if" clause above is
* done to make requests fail if nTarget is zero, that is, if all
* elements selected by the filter are outside the array bounds.
* TODO:
* It would be possible to lift this restriction by interpreting
* a request with *no* number of elements (NULL pointer) as scalar
* (meaning: fail if we get less than one element); in contrast,
* a request that explicitly specifies one element would be
* interpreted as an array request, for which zero elements would
* be a normal expected result.
*/
pfl->no_elements = nTarget;
if (must_lock)
dbScanUnlock(dbChannelRecord(chan));
break;
}
return pfl;
+32 -9
View File
@@ -12,21 +12,44 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chfPlugin.h>
#include <dbLock.h>
#include <db_field_log.h>
#include <epicsExport.h>
#include "chfPlugin.h"
#include "db_field_log.h"
#include "dbExtractArray.h"
#include "dbLock.h"
#include "epicsExport.h"
/*
* The size of the data is different for each channel, and can even
* change at runtime, so a freeList doesn't make much sense here.
*/
static void freeArray(db_field_log *pfl) {
free(pfl->u.r.field);
}
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
epicsTimeStamp now;
epicsTimeGetCurrent(&now);
/* If string or array, must make a copy (to ensure coherence between time and data) */
if (pfl->type == dbfl_type_rec) {
dbScanLock(dbChannelRecord(chan));
dbChannelMakeArrayCopy(pvt, pfl, chan);
dbScanUnlock(dbChannelRecord(chan));
/* If reference and not already copied,
must make a copy (to ensure coherence between time and data) */
if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
void *pTarget = calloc(pfl->no_elements, pfl->field_size);
void *pSource = pfl->u.r.field;
if (pTarget) {
long offset = 0;
long nSource = pfl->no_elements;
dbScanLock(dbChannelRecord(chan));
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
dbExtractArray(pSource, pTarget, pfl->field_size,
nSource, pfl->no_elements, offset, 1);
pfl->u.r.field = pTarget;
pfl->u.r.dtor = freeArray;
pfl->u.r.pvt = pvt;
dbScanUnlock(dbChannelRecord(chan));
}
}
pfl->time = now;