Ben Franksen has proposed merging ~bfrk/epics-base:write-filters into epics-base:7.0 with ~dirk.zimoch/epics-base:dbChannelForDBLinks as a prerequisite.
Requested reviews:
EPICS Core Developers (epics-core)
For more details, see:
https://code.launchpad.net/~bfrk/epics-base/+git/epics-base/+merge/381272
This is just the initial refactor, no new features yet.
--
Your team EPICS Core Developers is requested to review the proposed merge of ~bfrk/epics-base:write-filters into epics-base:7.0.
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index 9b149dd..1446ed5 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -338,7 +338,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
dbCommon *pcommon;
char *pbuffer = *poriginal;
- if (!pfl || pfl->type == dbfl_type_rec)
+ if (!pfl)
field_type = paddr->field_type;
else
field_type = pfl->field_type;
@@ -348,7 +348,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
if( (*options) & DBR_STATUS ) {
unsigned short *pushort = (unsigned short *)pbuffer;
- if (!pfl || pfl->type == dbfl_type_rec) {
+ if (!pfl) {
*pushort++ = pcommon->stat;
*pushort++ = pcommon->sevr;
} else {
@@ -382,7 +382,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
if( (*options) & DBR_TIME ) {
epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
- if (!pfl || pfl->type == dbfl_type_rec) {
+ if (!pfl) {
*ptime++ = pcommon->time.secPastEpoch;
*ptime++ = pcommon->time.nsec;
} else {
@@ -867,14 +867,14 @@ static long getAttrValue(DBADDR *paddr, short dbrType,
return 0;
}
-long dbGetField(DBADDR *paddr,short dbrType,
- void *pbuffer, long *options, long *nRequest, void *pflin)
+long dbGetField(DBADDR *paddr, short dbrType,
+ void *pbuffer, long *options, long *nRequest)
{
dbCommon *precord = paddr->precord;
long status = 0;
dbScanLock(precord);
- status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin);
+ status = dbGet(paddr, dbrType, pbuffer, options, nRequest, NULL);
dbScanUnlock(precord);
return status;
}
@@ -895,7 +895,7 @@ long dbGet(DBADDR *paddr, short dbrType,
if (nRequest && *nRequest == 0)
return 0;
- if (!pfl || pfl->type == dbfl_type_rec) {
+ if (!pfl) {
field_type = paddr->field_type;
no_elements = capacity = paddr->no_elements;
@@ -911,7 +911,7 @@ long dbGet(DBADDR *paddr, short dbrType,
} else {
field_type = pfl->field_type;
no_elements = capacity = pfl->no_elements;
- offset = 0;
+ offset = pfl->offset;
}
if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) {
@@ -937,7 +937,7 @@ long dbGet(DBADDR *paddr, short dbrType,
if (offset == 0 && (!nRequest || no_elements == 1)) {
if (nRequest)
*nRequest = 1;
- if (!pfl || pfl->type == dbfl_type_rec) {
+ if (!pfl) {
status = dbFastGetConvertRoutine[field_type][dbrType]
(paddr->pfield, pbuf, paddr);
} else {
@@ -950,6 +950,7 @@ long dbGet(DBADDR *paddr, short dbrType,
localAddr.field_type = pfl->field_type;
localAddr.field_size = pfl->field_size;
+ /* not used by dbFastConvert: */
localAddr.no_elements = pfl->no_elements;
if (pfl->type == dbfl_type_val)
localAddr.pfield = (char *) &pfl->u.v.field;
@@ -981,14 +982,15 @@ long dbGet(DBADDR *paddr, short dbrType,
}
/* convert data into the caller's buffer */
if (n <= 0) {
- ;/*do nothing*/
- } else if (!pfl || pfl->type == dbfl_type_rec) {
+ ; /*do nothing */
+ } else if (!pfl) {
status = convert(paddr, pbuf, n, capacity, offset);
} else {
DBADDR localAddr = *paddr; /* Structure copy */
localAddr.field_type = pfl->field_type;
localAddr.field_size = pfl->field_size;
+ /* not used by dbConvert, it uses the passed capacity instead: */
localAddr.no_elements = pfl->no_elements;
if (pfl->type == dbfl_type_val)
localAddr.pfield = (char *) &pfl->u.v.field;
diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
index 805dfd4..4a95989 100644
--- a/modules/database/src/ioc/db/dbAccessDefs.h
+++ b/modules/database/src/ioc/db/dbAccessDefs.h
@@ -243,7 +243,7 @@ epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset);
epicsShareFunc long dbGetField(
struct dbAddr *,short dbrType,void *pbuffer,long *options,
- long *nRequest,void *pfl);
+ long *nRequest);
epicsShareFunc long dbGet(
struct dbAddr *,short dbrType,void *pbuffer,long *options,
long *nRequest,void *pfl);
diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
index a8bfbf1..e361f3b 100644
--- a/modules/database/src/ioc/db/dbChannel.c
+++ b/modules/database/src/ioc/db/dbChannel.c
@@ -49,14 +49,12 @@ typedef struct parseContext {
static void *dbChannelFreeList;
static void *chFilterFreeList;
-static void *dbchStringFreeList;
void dbChannelExit(void)
{
freeListCleanup(dbChannelFreeList);
freeListCleanup(chFilterFreeList);
- freeListCleanup(dbchStringFreeList);
- dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
+ dbChannelFreeList = chFilterFreeList = NULL;
}
void dbChannelInit (void)
@@ -66,7 +64,6 @@ void dbChannelInit (void)
freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
- freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
db_init_event_freelists();
}
@@ -446,28 +443,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
return status;
}
-/* 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_INT64 => */DBR_INT64,
- /* DBF_UINT64 => */DBR_UINT64,
- /* 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 };
-
dbChannel * dbChannelCreate(const char *name)
{
const char *pname = name;
@@ -736,39 +711,6 @@ void dbChannelDelete(dbChannel *chan)
freeListFree(dbChannelFreeList, chan);
}
-static void freeArray(db_field_log *pfl) {
- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
- freeListFree(dbchStringFreeList, pfl->u.r.field);
- } else {
- free(pfl->u.r.field);
- }
-}
-
-void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
-{
- void *p;
- struct dbCommon *prec = dbChannelRecord(chan);
-
- if (pfl->type != dbfl_type_rec) return;
-
- pfl->type = dbfl_type_ref;
- pfl->stat = prec->stat;
- pfl->sevr = prec->sevr;
- pfl->time = prec->time;
- pfl->field_type = chan->addr.field_type;
- pfl->no_elements = chan->addr.no_elements;
- pfl->field_size = chan->addr.field_size;
- pfl->u.r.dtor = freeArray;
- pfl->u.r.pvt = pvt;
- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
- p = freeListCalloc(dbchStringFreeList);
- } else {
- p = calloc(pfl->no_elements, pfl->field_size);
- }
- if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
- pfl->u.r.field = p;
-}
-
/* FIXME: Do these belong in a different file? */
void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser)
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index fab9c66..6e98dee 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -64,8 +64,9 @@ typedef struct dbChannel {
/* Prototype for the channel event function that is called in filter stacks
*
* When invoked the scan lock for the record associated with 'chan' _may_ be locked.
- * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying
- * data out of the associated record.
+ * If pLog->type==dbfl_type_ref and pLog->u.r.dtor is NULL, then dbScanLock() must
+ * be called before accessing the data, as this indicates the data is owned by the
+ * record.
*
* This function has ownership of the field log pLog, if it wishes to discard
* this update it should free the field log with db_delete_field_log() and
@@ -224,7 +225,6 @@ epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, voi
epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
-epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
#ifdef __cplusplus
}
diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
index 466552d..8677add 100644
--- a/modules/database/src/ioc/db/dbDbLink.c
+++ b/modules/database/src/ioc/db/dbDbLink.c
@@ -181,7 +181,10 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
if (ellCount(&chan->filters)) {
db_field_log *pfl;
- /* For the moment, empty arrays are not supported by EPICS */
+ /*
+ * For the moment, empty arrays are not supported by EPICS.
+ * See the remark in src/std/filters/arr.c for details.
+ */
if (dbChannelFinalElements(chan) <= 0) /* empty array request */
return S_db_badField;
pfl = db_create_read_log(chan);
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index ed73529..532c2ba 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -667,27 +667,22 @@ int db_post_extra_labor (dbEventCtx ctx)
return DB_EVENT_OK;
}
-/*
- * DB_CREATE_EVENT_LOG()
- *
- * NOTE: This assumes that the db scan lock is already applied
- * (as it copies data from the record)
- */
-db_field_log* db_create_event_log (struct evSubscrip *pevent)
+static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
{
db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
if (pLog) {
- struct dbChannel *chan = pevent->chan;
struct dbCommon *prec = dbChannelRecord(chan);
- pLog->ctx = dbfl_context_event;
- if (pevent->useValque) {
+ pLog->stat = prec->stat;
+ pLog->sevr = prec->sevr;
+ pLog->time = prec->time;
+ pLog->field_type = dbChannelFieldType(chan);
+ pLog->field_size = dbChannelFieldSize(chan);
+ pLog->no_elements = dbChannelElements(chan);
+ pLog->offset = 0;
+
+ if (use_val) {
pLog->type = dbfl_type_val;
- pLog->stat = prec->stat;
- pLog->sevr = prec->sevr;
- pLog->time = prec->time;
- pLog->field_type = dbChannelFieldType(chan);
- pLog->no_elements = dbChannelElements(chan);
/*
* use memcpy to avoid a bus error on
* union copy of char in the db at an odd
@@ -697,23 +692,62 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent)
dbChannelField(chan),
dbChannelFieldSize(chan));
} else {
- pLog->type = dbfl_type_rec;
+ rset *prset;
+
+ pLog->type = dbfl_type_ref;
+
+ if (dbChannelSpecial(chan) == SPC_DBADDR &&
+ (prset = dbGetRset(&chan->addr)) &&
+ prset->get_array_info)
+ /* This condition implies !use_val, see db_add_event */
+ {
+ void *pfieldsave = dbChannelField(chan);
+ prec = dbChannelRecord(chan);
+ prset->get_array_info(&chan->addr, &pLog->no_elements, &pLog->offset);
+ /* don't make a copy yet, just reference the field value */
+ pLog->u.r.field = dbChannelField(chan);
+ dbChannelField(chan) = pfieldsave;
+ }
+ else {
+ /* don't make a copy yet, just reference the field value */
+ pLog->u.r.field = dbChannelField(chan);
+ }
+ /* indicate field value still owned by record */
+ pLog->u.r.dtor = NULL;
+ /* no private data yet, may be set by a filter */
+ pLog->u.r.pvt = NULL;
}
}
return pLog;
}
/*
+ * DB_CREATE_EVENT_LOG()
+ *
+ * NOTE: This assumes that the db scan lock is already applied
+ * (as it calls rset->get_array_info)
+ */
+db_field_log* db_create_event_log (struct evSubscrip *pevent)
+{
+ db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque);
+ if (pLog) {
+ pLog->ctx = dbfl_context_event;
+ }
+ return pLog;
+}
+
+/*
* DB_CREATE_READ_LOG()
*
*/
db_field_log* db_create_read_log (struct dbChannel *chan)
{
- db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
-
+ db_field_log *pLog = db_create_field_log(chan,
+ dbChannelElements(chan) == 1 &&
+ dbChannelSpecial(chan) != SPC_DBADDR &&
+ dbChannelFieldSize(chan) <= sizeof(union native_value));
if (pLog) {
pLog->ctx = dbfl_context_read;
- pLog->type = dbfl_type_rec;
}
return pLog;
}
@@ -737,20 +771,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
LOCKEVQUE (ev_que);
/*
- * if we have an event on the queue and both the last
- * event on the queue and the current event are emtpy
- * (i.e. of type dbfl_type_rec), simply ignore duplicate
- * events (saving empty events serves no purpose)
- */
- if (pevent->npend > 0u &&
- (*pevent->pLastLog)->type == dbfl_type_rec &&
- pLog->type == dbfl_type_rec) {
- db_delete_field_log(pLog);
- UNLOCKEVQUE (ev_que);
- return;
- }
-
- /*
* add to task local event que
*/
diff --git a/modules/database/src/ioc/db/dbExtractArray.c b/modules/database/src/ioc/db/dbExtractArray.c
index e16ab4c..f0ab281 100644
--- a/modules/database/src/ioc/db/dbExtractArray.c
+++ b/modules/database/src/ioc/db/dbExtractArray.c
@@ -13,11 +13,12 @@
/*
* Author: Ralph Lange <Ralph.Lange at bessy.de>
*
- * based on dbConvert.c
+ * based on dbConvert.c, see copyNoConvert
* written by: Bob Dalesio, Marty Kraimer
*/
#include <string.h>
+#include <assert.h>
#include "epicsTypes.h"
@@ -25,61 +26,31 @@
#include "dbAddr.h"
#include "dbExtractArray.h"
-void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
- long nRequest, long no_elements, long offset, long increment)
+void dbExtractArray(const void *pfrom, void *pto,
+ short field_size, short field_type,
+ long nRequest, long no_elements, long offset, long increment)
{
char *pdst = (char *) pto;
- char *psrc = (char *) paddr->pfield;
- long nUpperPart;
- int i;
- short srcSize = paddr->field_size;
- short dstSize = srcSize;
- char isString = (paddr->field_type == DBF_STRING);
+ const char *psrc = (char *) pfrom;
- if (nRequest > no_elements) nRequest = no_elements;
- if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
+ /* assert preconditions */
+ assert(nRequest >= 0);
+ assert(no_elements >= 0);
+ assert(increment > 0);
+ assert(0 <= offset);
+ assert(offset < no_elements);
- if (increment == 1 && dstSize == srcSize) {
- nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
- memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
+ if (increment == 1) {
+ long nUpperPart =
+ nRequest < no_elements - offset ? nRequest : no_elements - offset;
+ memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart);
if (nRequest > nUpperPart)
- memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
- if (isString)
- for (i = 1; i <= nRequest; i++)
- pdst[dstSize*i-1] = '\0';
+ memcpy(pdst + (field_size * nUpperPart), psrc,
+ field_size * (nRequest - nUpperPart));
} else {
- for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
+ for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) {
offset %= no_elements;
- memcpy(pdst, &psrc[offset*srcSize], dstSize);
- if (isString) pdst[dstSize-1] = '\0';
- }
- }
-}
-
-void dbExtractArrayFromBuf(const void *pfrom, void *pto,
- short field_size, short field_type,
- long nRequest, long no_elements, long offset, long increment)
-{
- char *pdst = (char *) pto;
- char *psrc = (char *) pfrom;
- int i;
- short srcSize = field_size;
- short dstSize = srcSize;
- char isString = (field_type == DBF_STRING);
-
- if (nRequest > no_elements) nRequest = no_elements;
- if (offset > no_elements - 1) offset = no_elements - 1;
- if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
-
- if (increment == 1) {
- memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
- if (isString)
- for (i = 1; i <= nRequest; i++)
- pdst[dstSize*i] = '\0';
- } else {
- for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
- memcpy(pdst, &psrc[offset*srcSize], dstSize);
- if (isString) pdst[dstSize] = '\0';
+ memcpy(pdst, psrc + (offset * field_size), field_size);
}
}
}
diff --git a/modules/database/src/ioc/db/dbExtractArray.h b/modules/database/src/ioc/db/dbExtractArray.h
index 7ed3584..9680660 100644
--- a/modules/database/src/ioc/db/dbExtractArray.h
+++ b/modules/database/src/ioc/db/dbExtractArray.h
@@ -21,11 +21,22 @@
extern "C" {
#endif
-epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
- long nRequest, long no_elements, long offset, long increment);
-epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
- short field_size, short field_type,
- long nRequest, long no_elements, long offset, long increment);
+/*
+ * This function does not do any conversion. This means we don't have to
+ * truncate the field_size to MAX_STRING_SIZE or add extra null terminators
+ * for string values. All of this is done by the dbConvert routines which
+ * will be called whether or not a filter is active.
+ *
+ * Checked preconditions:
+ * - nRequest >= 0, no_elements >= 0, increment > 0
+ * - 0 <= offset < no_elements
+ * Unchecked preconditions:
+ * - pto points to a buffer with at least field_size*nRequest bytes
+ * - pfrom points to a buffer with at least field_size*no_elements bytes
+ */
+epicsShareFunc void dbExtractArray(const void *pfrom, void *pto,
+ short field_size, short field_type,
+ long nRequest, long no_elements, long offset, long increment);
#ifdef __cplusplus
}
diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
index 1193954..206d6c9 100644
--- a/modules/database/src/ioc/db/dbTest.c
+++ b/modules/database/src/ioc/db/dbTest.c
@@ -341,14 +341,14 @@ long dbgf(const char *pname)
no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
if (addr.dbr_field_type == DBR_ENUM) {
long status = dbGetField(&addr, DBR_STRING, pbuffer,
- &options, &no_elements, NULL);
+ &options, &no_elements);
printBuffer(status, DBR_STRING, pbuffer, 0L, 0L,
no_elements, &msg_Buff, 10);
}
else {
long status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
- &options, &no_elements, NULL);
+ &options, &no_elements);
printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
no_elements, &msg_Buff, 10);
@@ -487,7 +487,7 @@ long dbtgf(const char *pname)
ret_options = req_options;
no_elements = 0;
status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
- &ret_options, &no_elements, NULL);
+ &ret_options, &no_elements);
printBuffer(status, addr.dbr_field_type, pbuffer,
req_options, ret_options, no_elements, pMsgBuff, tab_size);
@@ -496,62 +496,62 @@ long dbtgf(const char *pname)
dbr_type = DBR_STRING;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_CHAR;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_UCHAR;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_SHORT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_USHORT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_LONG;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_ULONG;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_INT64;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_UINT64;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_FLOAT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_DOUBLE;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_ENUM;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
pmsg[0] = '\0';
@@ -651,7 +651,7 @@ long dbtpf(const char *pname, const char *pvalue)
printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
- &options, &no_elements, NULL);
+ &options, &no_elements);
printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
no_elements, pMsgBuff, tab_size);
}
diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c
index 6846ef5..458a28b 100644
--- a/modules/database/src/ioc/db/dbUnitTest.c
+++ b/modules/database/src/ioc/db/dbUnitTest.c
@@ -197,7 +197,7 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
return;
}
- status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
+ status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq);
if (status) {
testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
return;
@@ -270,7 +270,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
return;
}
- status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);
+ status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest);
if (status) {
testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
index 1534517..4b3b82d 100644
--- a/modules/database/src/ioc/db/db_field_log.h
+++ b/modules/database/src/ioc/db/db_field_log.h
@@ -56,20 +56,31 @@ union native_value {
struct db_field_log;
typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
-/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */
+/*
+ * A db_field_log has one of two types:
+ *
+ * dbfl_type_ref - Reference to value
+ * Used for variable size (array) data types. Meta-data
+ * is stored in the field log, but value data is stored externally.
+ * Only the dbfl_ref side of the data union is valid.
+ *
+ * dbfl_type_val - Internal value
+ * Used to store small scalar data. Meta-data and value are
+ * present in this structure and no external references are used.
+ * Only the dbfl_val side of the data union is valid.
+ */
typedef enum dbfl_type {
- dbfl_type_rec = 0,
dbfl_type_val,
dbfl_type_ref
} dbfl_type;
/* Context of db_field_log: event = subscription update, read = read reply */
typedef enum dbfl_context {
- dbfl_context_read = 0,
+ dbfl_context_read,
dbfl_context_event
} dbfl_context;
-#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")
+#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref")
struct dbfl_val {
union native_value field; /* Field value */
@@ -81,6 +92,8 @@ struct dbfl_val {
* db_delete_field_log(). Any code which changes a dbfl_type_ref
* field log to another type, or to reference different data,
* must explicitly call the dtor function.
+ * If the dtor is NULL, then this means the array data is still owned
+ * by a record.
*/
struct dbfl_ref {
dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
@@ -88,8 +101,17 @@ struct dbfl_ref {
void *field; /* Field value */
};
+/*
+ * Note: The offset member is understood to apply an implicit index mapping
+ *
+ * i' = (i + offset) % no_elements
+ *
+ * of request index i. The resulting i' is used to to index into u.r.field.
+ *
+ * Also note that field_size may be larger than MAX_STRING_SIZE.
+ */
typedef struct db_field_log {
- unsigned int type:2; /* type (union) selector */
+ unsigned int type:1; /* type (union) selector */
/* ctx is used for all types */
unsigned int ctx:1; /* context (operation type) */
/* the following are used for value and reference types */
@@ -97,37 +119,15 @@ typedef struct db_field_log {
unsigned short stat; /* Alarm Status */
unsigned short sevr; /* Alarm Severity */
short field_type; /* DBF type of data */
- short field_size; /* Data size */
- long no_elements; /* No of array elements */
+ short field_size; /* Size of a single element */
+ long no_elements; /* No of valid array elements */
+ long offset; /* See above */
union {
struct dbfl_val v;
struct dbfl_ref r;
} u;
} db_field_log;
-/*
- * A db_field_log will in one of three types:
- *
- * dbfl_type_rec - Reference to record
- * The field log stores no data itself. Data must instead be taken
- * via the dbChannel* which must always be provided when along
- * with the field log.
- * For this type only the 'type' and 'ctx' members are used.
- *
- * dbfl_type_ref - Reference to outside value
- * Used for variable size (array) data types. Meta-data
- * is stored in the field log, but value data is stored externally
- * (see struct dbfl_ref).
- * For this type all meta-data members are used. The dbfl_ref side of the
- * data union is used.
- *
- * dbfl_type_val - Internal value
- * Used to store small scalar data. Meta-data and value are
- * present in this structure and no external references are used.
- * For this type all meta-data members are used. The dbfl_val side of the
- * data union is used.
- */
-
#ifdef __cplusplus
}
#endif
diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
index f91708a..b90335e 100644
--- a/modules/database/src/std/filters/arr.c
+++ b/modules/database/src/std/filters/arr.c
@@ -12,16 +12,13 @@
#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 "dbExtractArray.h"
+#include "db_field_log.h"
+#include "dbLock.h"
+#include "epicsExit.h"
+#include "freeList.h"
+#include "epicsExport.h"
typedef struct myStruct {
epicsInt32 start;
@@ -45,6 +42,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;
@@ -93,78 +92,61 @@ 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 offset = 0;
- long nSource = dbChannelElements(chan);
- long capacity = nSource;
- void *pdst;
+ long nTarget;
+ void *pTarget;
+ /* initial values for the source array */
+ long offset = pfl->offset;
+ long nSource = pfl->no_elements;
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;
+ must_lock = !pfl->u.r.dtor;
+ if (must_lock)
+ dbScanLock(dbChannelRecord(chan));
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);
- }
- if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
- if (nTarget) {
+ /*
+ * Side note: it would be nice if we could avoid the copying in
+ * the case of my->incr==1. This is currently not possible due to
+ * the way the offset member is interpreted (namely as shifting the
+ * array in a ring-buffer style).
+ */
+ if (nTarget > 0) {
+ /* copy the data */
+ void *pSource = pfl->u.r.field;
+ pTarget = freeListCalloc(my->arrayFreeList);
+ if (!pTarget) break;
+ offset = (offset + start) % nSource;
+ dbExtractArray(pSource, pTarget, pfl->field_size,
+ pfl->field_type, nTarget, nSource, 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 offset and no_elements to refer to the new pTarget */
+ pfl->offset = 0;
+ /*
+ * 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;
diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c
index 5925b0b..56c9f5b 100644
--- a/modules/database/src/std/filters/ts.c
+++ b/modules/database/src/std/filters/ts.c
@@ -11,21 +11,39 @@
*/
#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 "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) {
+ dbScanLock(dbChannelRecord(chan));
+ memcpy(pTarget, pSource, pfl->field_size * pfl->no_elements);
+ pfl->u.r.field = pTarget;
+ pfl->u.r.dtor = freeArray;
+ pfl->u.r.pvt = pvt;
+ dbScanUnlock(dbChannelRecord(chan));
+ }
}
pfl->time = now;
diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp
index 8255fdc..ff74e01 100644
--- a/modules/database/test/ioc/db/dbChArrTest.cpp
+++ b/modules/database/test/ioc/db/dbChArrTest.cpp
@@ -130,7 +130,7 @@ static void check(short dbr_type) {
memset(buf, 0, sizeof(buf)); \
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
pfl = db_create_read_log(pch); \
- testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
+ testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
@@ -178,6 +178,7 @@ static void check(short dbr_type) {
pfl->field_type = DBF_CHAR; \
pfl->field_size = 1; \
pfl->no_elements = 26; \
+ pfl->offset = 0; \
pfl->u.r.dtor = freeArray; \
pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
diff --git a/modules/database/test/std/filters/arrTest.cpp b/modules/database/test/std/filters/arrTest.cpp
index 1ec16b3..08ba07b 100644
--- a/modules/database/test/std/filters/arrTest.cpp
+++ b/modules/database/test/std/filters/arrTest.cpp
@@ -56,25 +56,26 @@ const char *server_port = CA_SERVER_PORT;
static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
for (int i = 0; i < pfl1->no_elements; i++) {
+ int j = (i + pfl1->offset) % pfl1->no_elements;
switch (type) {
case DBR_DOUBLE:
- if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
+ if (((epicsFloat64*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
testDiag("at index=%d: field log has %g, should be %d",
- i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
+ i, ((epicsFloat64*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
return 0;
}
break;
case DBR_LONG:
- if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
+ if (((epicsInt32*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
testDiag("at index=%d: field log has %d, should be %d",
- i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
+ i, ((epicsInt32*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
return 0;
}
break;
case DBR_STRING:
- if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
+ if (strtol(&((const char*)pfl1->u.r.field)[j*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) {
testDiag("at index=%d: field log has '%s', should be '%d'",
- i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
+ i, &((const char*)pfl1->u.r.field)[j*pfl1->field_size], ((epicsInt32*)p2)[i]);
return 0;
}
break;
@@ -119,7 +120,7 @@ static void testHead (const char *title, const char *typ = "") {
off = Offset; \
(void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
pfl = db_create_read_log(pch); \
- testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
+ testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \
pfl2 = dbChannelRunPostChain(pch, pfl); \
testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
diff --git a/modules/database/test/std/filters/dbndTest.c b/modules/database/test/std/filters/dbndTest.c
index 4d70f83..fd4a472 100644
--- a/modules/database/test/std/filters/dbndTest.c
+++ b/modules/database/test/std/filters/dbndTest.c
@@ -129,7 +129,7 @@ MAIN(dbndTest)
dbEventCtx evtctx;
int logsFree, logsFinal;
- testPlan(77);
+ testPlan(72);
testdbPrepare();
@@ -170,12 +170,9 @@ MAIN(dbndTest)
"dbnd has one filter with argument in pre chain");
testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
- /* Field logs of type ref and rec: pass any update */
-
- testHead("Field logs of type ref and rec");
- fl1.type = dbfl_type_rec;
- mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
+ /* Field logs of type ref: pass any update */
+ testHead("Field logs of type ref");
fl1.type = dbfl_type_ref;
mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
- Replies:
- Re: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
- [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
- Navigate by Date:
- Prev:
[Merge] ~bfrk/epics-base:write-filters-rebased into epics-base:7.0 Ben Franksen via Core-talk
- Next:
Re: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
<2020>
2021
2022
2023
2024
- Navigate by Thread:
- Prev:
[Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
- Next:
Re: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0 Ben Franksen via Core-talk
- Index:
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
<2020>
2021
2022
2023
2024
|