EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  <20202021  2022  2023  2024  2025  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  <20202021  2022  2023  2024  2025 
<== Date ==> <== Thread ==>

Subject: [Merge] ~bfrk/epics-base:write-filters into epics-base:7.0
From: Ben Franksen via Core-talk <core-talk at aps.anl.gov>
To: mp+381149 at code.launchpad.net
Date: Wed, 25 Mar 2020 06:51:24 -0000
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/381149

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..a6c31f2 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -34,7 +34,7 @@
 #include "errlog.h"
 #include "errMdef.h"
 
-#include "epicsExport.h" /* #define epicsExportSharedSymbols */
+#include "epicsExport.h"                /* #define epicsExportSharedSymbols */
 #include "caeventmask.h"
 #include "callback.h"
 #include "dbAccessDefs.h"
@@ -63,7 +63,7 @@
 #include "special.h"
 
 epicsShareDef struct dbBase *pdbbase = 0;
-epicsShareDef volatile int interruptAccept=FALSE;
+epicsShareDef volatile int interruptAccept = FALSE;
 
 epicsShareDef int dbAccessDebugPUTF = 0;
 epicsExportAddress(int, dbAccessDebugPUTF);
@@ -73,24 +73,24 @@ epicsExportAddress(int, dbAccessDebugPUTF);
 epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
 
 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
+    /* 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
 };
 
 /*
@@ -109,217 +109,219 @@ void dbSpcAsRegisterCallback(SPC_ASCALLBACK func)
     spcAsCallback = func;
 }
 
-long dbPutSpecial(DBADDR *paddr,int pass)
+long dbPutSpecial(DBADDR *paddr, int pass)
 {
-    long int	(*pspecial)()=NULL;
-    rset        *prset;
-    dbCommon 	*precord = paddr->precord;
-    long	status=0;
-    long	special=paddr->special;
+    long int (*pspecial) () = NULL;
+    rset *prset;
+    dbCommon *precord = paddr->precord;
+    long status = 0;
+    long special = paddr->special;
 
     prset = dbGetRset(paddr);
-    if(special<100) { /*global processing*/
-	if((special==SPC_NOMOD) && (pass==0)) {
-	    status = S_db_noMod;
-	    recGblDbaddrError(status,paddr,"dbPut");
-	    return(status);
-	}else if(special==SPC_SCAN){
-	    if(pass==0)
-		scanDelete(precord);
-	    else
-		scanAdd(precord);
-	}else if((special==SPC_AS) && (pass==1)) {
-            if(spcAsCallback) (*spcAsCallback)(precord);
-	}
-    }else {
-	if( prset && (pspecial = (prset->special))) {
-	    status=(*pspecial)(paddr,pass);
-	    if(status) return(status);
-	} else if(pass==0){
-	    recGblRecSupError(S_db_noSupport,paddr,"dbPut", "special");
-	    return(S_db_noSupport);
-	}
+    if (special < 100) {                /*global processing */
+        if ((special == SPC_NOMOD) && (pass == 0)) {
+            status = S_db_noMod;
+            recGblDbaddrError(status, paddr, "dbPut");
+            return (status);
+        } else if (special == SPC_SCAN) {
+            if (pass == 0)
+                scanDelete(precord);
+            else
+                scanAdd(precord);
+        } else if ((special == SPC_AS) && (pass == 1)) {
+            if (spcAsCallback)
+                (*spcAsCallback) (precord);
+        }
+    } else {
+        if (prset && (pspecial = (prset->special))) {
+            status = (*pspecial) (paddr, pass);
+            if (status)
+                return (status);
+        } else if (pass == 0) {
+            recGblRecSupError(S_db_noSupport, paddr, "dbPut", "special");
+            return (S_db_noSupport);
+        }
     }
-    return(0);
+    return (0);
 }
 
 static void get_enum_strs(DBADDR *paddr, char **ppbuffer,
-    rset *prset,long	*options)
+    rset * prset, long *options)
 {
-	short		field_type=paddr->field_type;
-	dbFldDes	*pdbFldDes = paddr->pfldDes;
-	dbMenu		*pdbMenu;
-	dbDeviceMenu	*pdbDeviceMenu;
-	char		**papChoice;
-	unsigned long	no_str;
-	char		*ptemp;
-	struct dbr_enumStrs *pdbr_enumStrs=(struct dbr_enumStrs*)(*ppbuffer);
+    short field_type = paddr->field_type;
+    dbFldDes *pdbFldDes = paddr->pfldDes;
+    dbMenu *pdbMenu;
+    dbDeviceMenu *pdbDeviceMenu;
+    char **papChoice;
+    unsigned long no_str;
+    char *ptemp;
+    struct dbr_enumStrs *pdbr_enumStrs = (struct dbr_enumStrs *)(*ppbuffer);
     unsigned int i;
 
-	memset(pdbr_enumStrs,'\0',dbr_enumStrs_size);
-	switch(field_type) {
-		case DBF_ENUM:
-		    if( prset && prset->get_enum_strs ) {
-			(*prset->get_enum_strs)(paddr,pdbr_enumStrs);
-		    } else {
-			*options = (*options)^DBR_ENUM_STRS;/*Turn off option*/
-		    }
-		    break;
-		case DBF_MENU:
-		    pdbMenu = (dbMenu *)pdbFldDes->ftPvt;
-		    no_str = pdbMenu->nChoice;
-		    papChoice= pdbMenu->papChoiceValue;
-		    goto choice_common;
-		case DBF_DEVICE:
-		    pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt;
-                    if(!pdbDeviceMenu) {
-                        *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/
-                        break;
-                    }
-		    no_str = pdbDeviceMenu->nChoice;
-		    papChoice = pdbDeviceMenu->papChoice;
-		    goto choice_common;
-choice_common:
-		    i = sizeof(pdbr_enumStrs->strs)/
-			sizeof(pdbr_enumStrs->strs[0]);
-		    if(i<no_str) no_str = i;
-		    pdbr_enumStrs->no_str = no_str;
-		    ptemp = &(pdbr_enumStrs->strs[0][0]);
-		    for (i=0; i<no_str; i++) {
-			if(papChoice[i]==NULL) *ptemp=0;
-			else {
-			    strncpy(ptemp,papChoice[i],
-				sizeof(pdbr_enumStrs->strs[0]));
-			    *(ptemp+sizeof(pdbr_enumStrs->strs[0])-1) = 0;
-			}
-			ptemp += sizeof(pdbr_enumStrs->strs[0]);
-		    }
-		    break;
-		default:
-		    *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/
-		    break;
-	}
-	*ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size;
-	return;
+    memset(pdbr_enumStrs, '\0', dbr_enumStrs_size);
+    switch (field_type) {
+    case DBF_ENUM:
+        if (prset && prset->get_enum_strs) {
+            (*prset->get_enum_strs) (paddr, pdbr_enumStrs);
+        } else {
+            *options = (*options) ^ DBR_ENUM_STRS; /*Turn off option */
+        }
+        break;
+    case DBF_MENU:
+        pdbMenu = (dbMenu *) pdbFldDes->ftPvt;
+        no_str = pdbMenu->nChoice;
+        papChoice = pdbMenu->papChoiceValue;
+        goto choice_common;
+    case DBF_DEVICE:
+        pdbDeviceMenu = (dbDeviceMenu *) pdbFldDes->ftPvt;
+        if (!pdbDeviceMenu) {
+            *options = (*options) ^ DBR_ENUM_STRS; /*Turn off option */
+            break;
+        }
+        no_str = pdbDeviceMenu->nChoice;
+        papChoice = pdbDeviceMenu->papChoice;
+        goto choice_common;
+      choice_common:
+        i = sizeof(pdbr_enumStrs->strs) / sizeof(pdbr_enumStrs->strs[0]);
+        if (i < no_str)
+            no_str = i;
+        pdbr_enumStrs->no_str = no_str;
+        ptemp = &(pdbr_enumStrs->strs[0][0]);
+        for (i = 0; i < no_str; i++) {
+            if (papChoice[i] == NULL)
+                *ptemp = 0;
+            else {
+                strncpy(ptemp, papChoice[i], sizeof(pdbr_enumStrs->strs[0]));
+                *(ptemp + sizeof(pdbr_enumStrs->strs[0]) - 1) = 0;
+            }
+            ptemp += sizeof(pdbr_enumStrs->strs[0]);
+        }
+        break;
+    default:
+        *options = (*options) ^ DBR_ENUM_STRS; /*Turn off option */
+        break;
+    }
+    *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size;
+    return;
 }
 
 static void get_graphics(DBADDR *paddr, char **ppbuffer,
-    rset *prset,long	*options)
+    rset * prset, long *options)
 {
-	struct			dbr_grDouble grd;
-	int			got_data=FALSE;
-
-        grd.upper_disp_limit = grd.lower_disp_limit = 0.0;
-	if( prset && prset->get_graphic_double ) {
-		(*prset->get_graphic_double)(paddr,&grd);
-		got_data=TRUE;
-	}
-	if( (*options) & (DBR_GR_LONG) ) {
-		char	*pbuffer=*ppbuffer;
-
-		if(got_data) {
-		    struct dbr_grLong *pgr=(struct dbr_grLong*)pbuffer;
-		    pgr->upper_disp_limit = (epicsInt32)grd.upper_disp_limit;
-		    pgr->lower_disp_limit = (epicsInt32)grd.lower_disp_limit;
-		} else {
-		    memset(pbuffer,'\0',dbr_grLong_size);
-		    *options = (*options) ^ DBR_GR_LONG; /*Turn off option*/
-		}
-		*ppbuffer = ((char *)*ppbuffer) + dbr_grLong_size;
-	}
-	if( (*options) & (DBR_GR_DOUBLE) ) {
-		char	*pbuffer=*ppbuffer;
-
-		if(got_data) {
-		    struct dbr_grDouble *pgr=(struct dbr_grDouble*)pbuffer;
-		    pgr->upper_disp_limit = grd.upper_disp_limit;
-		    pgr->lower_disp_limit = grd.lower_disp_limit;
-		} else {
-		    memset(pbuffer,'\0',dbr_grDouble_size);
-		    *options = (*options) ^ DBR_GR_DOUBLE; /*Turn off option*/
-		}
-		*ppbuffer = ((char *)*ppbuffer) + dbr_grDouble_size;
-	}
-	return;
+    struct dbr_grDouble grd;
+    int got_data = FALSE;
+
+    grd.upper_disp_limit = grd.lower_disp_limit = 0.0;
+    if (prset && prset->get_graphic_double) {
+        (*prset->get_graphic_double) (paddr, &grd);
+        got_data = TRUE;
+    }
+    if ((*options) & (DBR_GR_LONG)) {
+        char *pbuffer = *ppbuffer;
+
+        if (got_data) {
+            struct dbr_grLong *pgr = (struct dbr_grLong *)pbuffer;
+            pgr->upper_disp_limit = (epicsInt32) grd.upper_disp_limit;
+            pgr->lower_disp_limit = (epicsInt32) grd.lower_disp_limit;
+        } else {
+            memset(pbuffer, '\0', dbr_grLong_size);
+            *options = (*options) ^ DBR_GR_LONG; /*Turn off option */
+        }
+        *ppbuffer = ((char *)*ppbuffer) + dbr_grLong_size;
+    }
+    if ((*options) & (DBR_GR_DOUBLE)) {
+        char *pbuffer = *ppbuffer;
+
+        if (got_data) {
+            struct dbr_grDouble *pgr = (struct dbr_grDouble *)pbuffer;
+            pgr->upper_disp_limit = grd.upper_disp_limit;
+            pgr->lower_disp_limit = grd.lower_disp_limit;
+        } else {
+            memset(pbuffer, '\0', dbr_grDouble_size);
+            *options = (*options) ^ DBR_GR_DOUBLE; /*Turn off option */
+        }
+        *ppbuffer = ((char *)*ppbuffer) + dbr_grDouble_size;
+    }
+    return;
 }
 
 static void get_control(DBADDR *paddr, char **ppbuffer,
-    rset *prset,long	*options)
+    rset * prset, long *options)
 {
-	struct dbr_ctrlDouble	ctrld;
-	int			got_data=FALSE;
-
-        ctrld.upper_ctrl_limit = ctrld.lower_ctrl_limit = 0.0;
-	if( prset && prset->get_control_double ) {
-		(*prset->get_control_double)(paddr,&ctrld);
-		got_data=TRUE;
-	}
-	if( (*options) & (DBR_CTRL_LONG) ) {
-		char	*pbuffer=*ppbuffer;
-
-		if(got_data) {
-		    struct dbr_ctrlLong *pctrl=(struct dbr_ctrlLong*)pbuffer;
-		    pctrl->upper_ctrl_limit = (epicsInt32)ctrld.upper_ctrl_limit;
-		    pctrl->lower_ctrl_limit = (epicsInt32)ctrld.lower_ctrl_limit;
-		} else {
-		    memset(pbuffer,'\0',dbr_ctrlLong_size);
-		    *options = (*options) ^ DBR_CTRL_LONG; /*Turn off option*/
-		}
-		*ppbuffer = ((char *)*ppbuffer) + dbr_ctrlLong_size;
-	}
-	if( (*options) & (DBR_CTRL_DOUBLE) ) {
-		char	*pbuffer=*ppbuffer;
-
-		if(got_data) {
-		   struct dbr_ctrlDouble *pctrl=(struct dbr_ctrlDouble*)pbuffer;
-		   pctrl->upper_ctrl_limit = ctrld.upper_ctrl_limit;
-		   pctrl->lower_ctrl_limit = ctrld.lower_ctrl_limit;
-		} else {
-		    memset(pbuffer,'\0',dbr_ctrlDouble_size);
-		    *options = (*options) ^ DBR_CTRL_DOUBLE; /*Turn off option*/
-		}
-		*ppbuffer = ((char *)*ppbuffer) + dbr_ctrlDouble_size;
-	}
-	return;
+    struct dbr_ctrlDouble ctrld;
+    int got_data = FALSE;
+
+    ctrld.upper_ctrl_limit = ctrld.lower_ctrl_limit = 0.0;
+    if (prset && prset->get_control_double) {
+        (*prset->get_control_double) (paddr, &ctrld);
+        got_data = TRUE;
+    }
+    if ((*options) & (DBR_CTRL_LONG)) {
+        char *pbuffer = *ppbuffer;
+
+        if (got_data) {
+            struct dbr_ctrlLong *pctrl = (struct dbr_ctrlLong *)pbuffer;
+            pctrl->upper_ctrl_limit = (epicsInt32) ctrld.upper_ctrl_limit;
+            pctrl->lower_ctrl_limit = (epicsInt32) ctrld.lower_ctrl_limit;
+        } else {
+            memset(pbuffer, '\0', dbr_ctrlLong_size);
+            *options = (*options) ^ DBR_CTRL_LONG; /*Turn off option */
+        }
+        *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlLong_size;
+    }
+    if ((*options) & (DBR_CTRL_DOUBLE)) {
+        char *pbuffer = *ppbuffer;
+
+        if (got_data) {
+            struct dbr_ctrlDouble *pctrl = (struct dbr_ctrlDouble *)pbuffer;
+            pctrl->upper_ctrl_limit = ctrld.upper_ctrl_limit;
+            pctrl->lower_ctrl_limit = ctrld.lower_ctrl_limit;
+        } else {
+            memset(pbuffer, '\0', dbr_ctrlDouble_size);
+            *options = (*options) ^ DBR_CTRL_DOUBLE; /*Turn off option */
+        }
+        *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlDouble_size;
+    }
+    return;
 }
 
 static void get_alarm(DBADDR *paddr, char **ppbuffer,
-    rset *prset, long *options)
+    rset * prset, long *options)
 {
     char *pbuffer = *ppbuffer;
-    struct dbr_alDouble ald = {epicsNAN, epicsNAN, epicsNAN, epicsNAN};
+    struct dbr_alDouble ald = { epicsNAN, epicsNAN, epicsNAN, epicsNAN };
     long no_data = TRUE;
 
     if (prset && prset->get_alarm_double)
         no_data = prset->get_alarm_double(paddr, &ald);
 
     if (*options & DBR_AL_LONG) {
-        struct dbr_alLong *pal = (struct dbr_alLong*) pbuffer;
+        struct dbr_alLong *pal = (struct dbr_alLong *)pbuffer;
 
-        pal->upper_alarm_limit   = finite(ald.upper_alarm_limit) ?
+        pal->upper_alarm_limit = finite(ald.upper_alarm_limit) ?
             (epicsInt32) ald.upper_alarm_limit : 0;
         pal->upper_warning_limit = finite(ald.upper_warning_limit) ?
             (epicsInt32) ald.upper_warning_limit : 0;
         pal->lower_warning_limit = finite(ald.lower_warning_limit) ?
             (epicsInt32) ald.lower_warning_limit : 0;
-        pal->lower_alarm_limit   = finite(ald.lower_alarm_limit) ?
+        pal->lower_alarm_limit = finite(ald.lower_alarm_limit) ?
             (epicsInt32) ald.lower_alarm_limit : 0;
 
         if (no_data)
-            *options ^= DBR_AL_LONG; /*Turn off option*/
+            *options ^= DBR_AL_LONG;    /*Turn off option */
 
         *ppbuffer += dbr_alLong_size;
     }
     if (*options & DBR_AL_DOUBLE) {
-        struct dbr_alDouble *pal = (struct dbr_alDouble*) pbuffer;
+        struct dbr_alDouble *pal = (struct dbr_alDouble *)pbuffer;
 
-        pal->upper_alarm_limit   = ald.upper_alarm_limit;
+        pal->upper_alarm_limit = ald.upper_alarm_limit;
         pal->upper_warning_limit = ald.upper_warning_limit;
         pal->lower_warning_limit = ald.lower_warning_limit;
-        pal->lower_alarm_limit   = ald.lower_alarm_limit;
+        pal->lower_alarm_limit = ald.lower_alarm_limit;
 
         if (no_data)
-            *options ^= DBR_AL_DOUBLE; /*Turn off option*/
+            *options ^= DBR_AL_DOUBLE;  /*Turn off option */
 
         *ppbuffer += dbr_alDouble_size;
     }
@@ -330,88 +332,89 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer,
  * blocks only changing the buffer pointer in a way that does not break alignment.
  */
 static void getOptions(DBADDR *paddr, char **poriginal, long *options,
-        void *pflin)
+    void *pflin)
 {
-	db_field_log	*pfl= (db_field_log *)pflin;
-    rset	*prset;
-        short		field_type;
-	dbCommon	*pcommon;
-	char		*pbuffer = *poriginal;
-
-        if (!pfl || pfl->type == dbfl_type_rec)
-            field_type = paddr->field_type;
-        else
-            field_type = pfl->field_type;
-	prset=dbGetRset(paddr);
-	/* Process options */
-	pcommon = paddr->precord;
-	if( (*options) & DBR_STATUS ) {
-	    unsigned short *pushort = (unsigned short *)pbuffer;
-
-            if (!pfl || pfl->type == dbfl_type_rec) {
-                *pushort++ = pcommon->stat;
-                *pushort++ = pcommon->sevr;
-            } else {
-                *pushort++ = pfl->stat;
-                *pushort++ = pfl->sevr;
-            }
-	    *pushort++ = pcommon->acks;
-	    *pushort++ = pcommon->ackt;
-	    pbuffer = (char *)pushort;
-	}
-	if( (*options) & DBR_UNITS ) {
-	    memset(pbuffer,'\0',dbr_units_size);
-	    if( prset && prset->get_units ){
-		(*prset->get_units)(paddr, pbuffer);
-		pbuffer[DB_UNITS_SIZE-1] = '\0';
-	    } else {
-		*options ^= DBR_UNITS; /*Turn off DBR_UNITS*/
-	    }
-	    pbuffer += dbr_units_size;
-	}
-	if( (*options) & DBR_PRECISION ) {
-	    memset(pbuffer, '\0', dbr_precision_size);
-	    if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE)
-	    &&  prset && prset->get_precision ){
-                (*prset->get_precision)(paddr,(long *)pbuffer);
-	    } else {
-		*options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/
-	    }
-	    pbuffer += dbr_precision_size;
-	}
-	if( (*options) & DBR_TIME ) {
-	    epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
-
-            if (!pfl || pfl->type == dbfl_type_rec) {
-                *ptime++ = pcommon->time.secPastEpoch;
-                *ptime++ = pcommon->time.nsec;
-            } else {
-                *ptime++ = pfl->time.secPastEpoch;
-                *ptime++ = pfl->time.nsec;
-	    }
-	    pbuffer = (char *)ptime;
-	}
-	if( (*options) & DBR_ENUM_STRS )
-	    get_enum_strs(paddr, &pbuffer, prset, options);
-	if( (*options) & (DBR_GR_LONG|DBR_GR_DOUBLE ))
-	    get_graphics(paddr, &pbuffer, prset, options);
-	if((*options) & (DBR_CTRL_LONG | DBR_CTRL_DOUBLE ))
-	    get_control(paddr, &pbuffer, prset, options);
-	if((*options) & (DBR_AL_LONG | DBR_AL_DOUBLE ))
-	    get_alarm(paddr, &pbuffer, prset, options);
-	*poriginal = pbuffer;
+    db_field_log *pfl = (db_field_log *) pflin;
+    rset *prset;
+    short field_type;
+    dbCommon *pcommon;
+    char *pbuffer = *poriginal;
+
+    if (!pfl)
+        field_type = paddr->field_type;
+    else
+        field_type = pfl->field_type;
+    prset = dbGetRset(paddr);
+    /* Process options */
+    pcommon = paddr->precord;
+    if ((*options) & DBR_STATUS) {
+        unsigned short *pushort = (unsigned short *)pbuffer;
+
+        if (!pfl) {
+            *pushort++ = pcommon->stat;
+            *pushort++ = pcommon->sevr;
+        } else {
+            *pushort++ = pfl->stat;
+            *pushort++ = pfl->sevr;
+        }
+        *pushort++ = pcommon->acks;
+        *pushort++ = pcommon->ackt;
+        pbuffer = (char *)pushort;
+    }
+    if ((*options) & DBR_UNITS) {
+        memset(pbuffer, '\0', dbr_units_size);
+        if (prset && prset->get_units) {
+            (*prset->get_units) (paddr, pbuffer);
+            pbuffer[DB_UNITS_SIZE - 1] = '\0';
+        } else {
+            *options ^= DBR_UNITS;      /*Turn off DBR_UNITS */
+        }
+        pbuffer += dbr_units_size;
+    }
+    if ((*options) & DBR_PRECISION) {
+        memset(pbuffer, '\0', dbr_precision_size);
+        if ((field_type == DBF_FLOAT || field_type == DBF_DOUBLE)
+            && prset && prset->get_precision) {
+            (*prset->get_precision) (paddr, (long *)pbuffer);
+        } else {
+            *options ^= DBR_PRECISION;  /*Turn off DBR_PRECISION */
+        }
+        pbuffer += dbr_precision_size;
+    }
+    if ((*options) & DBR_TIME) {
+        epicsUInt32 *ptime = (epicsUInt32 *) pbuffer;
+
+        if (!pfl) {
+            *ptime++ = pcommon->time.secPastEpoch;
+            *ptime++ = pcommon->time.nsec;
+        } else {
+            *ptime++ = pfl->time.secPastEpoch;
+            *ptime++ = pfl->time.nsec;
+        }
+        pbuffer = (char *)ptime;
+    }
+    if ((*options) & DBR_ENUM_STRS)
+        get_enum_strs(paddr, &pbuffer, prset, options);
+    if ((*options) & (DBR_GR_LONG | DBR_GR_DOUBLE))
+        get_graphics(paddr, &pbuffer, prset, options);
+    if ((*options) & (DBR_CTRL_LONG | DBR_CTRL_DOUBLE))
+        get_control(paddr, &pbuffer, prset, options);
+    if ((*options) & (DBR_AL_LONG | DBR_AL_DOUBLE))
+        get_alarm(paddr, &pbuffer, prset, options);
+    *poriginal = pbuffer;
 }
 
-rset * dbGetRset(const struct dbAddr *paddr)
+rset *dbGetRset(const struct dbAddr *paddr)
 {
-	struct dbFldDes *pfldDes = paddr->pfldDes;
+    struct dbFldDes *pfldDes = paddr->pfldDes;
 
-	if(!pfldDes) return(0);
-	return(pfldDes->pdbRecordType->prset);
+    if (!pfldDes)
+        return (0);
+    return (pfldDes->pdbRecordType->prset);
 }
 
-long dbPutAttribute(
-    const char *recordTypename, const char *name, const char *value)
+long dbPutAttribute(const char *recordTypename, const char *name,
+    const char *value)
 {
     DBENTRY dbEntry;
     DBENTRY *pdbEntry = &dbEntry;
@@ -430,7 +433,7 @@ long dbPutAttribute(
     if (!status)
         status = dbPutRecordAttribute(pdbEntry, name, value);
     dbFinishEntry(pdbEntry);
-done:
+  done:
     if (status)
         errMessage(status, "dbPutAttribute failure");
     return status;
@@ -458,7 +461,7 @@ int dbGetFieldIndex(const struct dbAddr *paddr)
  *     5.  Run the process routine specific to the record type.
  *     6.  Check to see if record contents should be automatically printed.
  */
-long dbProcess(dbCommon *precord)
+long dbProcess(dbCommon * precord)
 {
     rset *prset = precord->rset;
     dbRecordType *pdbRecordType = precord->rdes;
@@ -466,7 +469,7 @@ long dbProcess(dbCommon *precord)
     char context[40] = "";
     long status = 0;
     int *ptrace;
-    int	set_trace = FALSE;
+    int set_trace = FALSE;
     dbFldDes *pdbFldDes;
     int callNotifyCompletion = FALSE;
 
@@ -492,7 +495,7 @@ long dbProcess(dbCommon *precord)
             goto all_done;
     }
 
-    /* check for trace processing*/
+    /* check for trace processing */
     if (tpro) {
         if (!*ptrace) {
             *ptrace = 1;
@@ -519,19 +522,17 @@ long dbProcess(dbCommon *precord)
 
         /* raise scan alarm after MAX_LOCK times */
         if ((precord->stat == SCAN_ALARM) ||
-            (precord->lcnt++ < MAX_LOCK) ||
-            (precord->sevr >= INVALID_ALARM)) goto all_done;
+            (precord->lcnt++ < MAX_LOCK) || (precord->sevr >= INVALID_ALARM))
+            goto all_done;
 
         recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM);
         monitor_mask = recGblResetAlarms(precord);
-        monitor_mask |= DBE_VALUE|DBE_LOG;
+        monitor_mask |= DBE_VALUE | DBE_LOG;
         pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
         db_post_events(precord,
-                (void *)(((char *)precord) + pdbFldDes->offset),
-                monitor_mask);
+            (void *)(((char *)precord) + pdbFldDes->offset), monitor_mask);
         goto all_done;
-    }
-    else
+    } else
         precord->lcnt = 0;
 
     /*
@@ -547,7 +548,7 @@ long dbProcess(dbCommon *precord)
             printf("%s: dbProcess of Disabled '%s'\n",
                 context, precord->name);
 
-        /*take care of caching and notifyCompletion*/
+        /*take care of caching and notifyCompletion */
         precord->rpro = FALSE;
         precord->putf = FALSE;
         callNotifyCompletion = TRUE;
@@ -564,8 +565,8 @@ long dbProcess(dbCommon *precord)
         db_post_events(precord, &precord->sevr, DBE_VALUE);
         pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
         db_post_events(precord,
-                (void *)(((char *)precord) + pdbFldDes->offset),
-                DBE_VALUE|DBE_ALARM);
+            (void *)(((char *)precord) + pdbFldDes->offset),
+            DBE_VALUE | DBE_ALARM);
         goto all_done;
     }
 
@@ -573,7 +574,7 @@ long dbProcess(dbCommon *precord)
     /* FIXME: put this in iocInit() !!! */
     if (!prset || !prset->process) {
         callNotifyCompletion = TRUE;
-        precord->pact = 1;/*set pact so error is issued only once*/
+        precord->pact = 1;              /*set pact so error is issued only once */
         recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess");
         status = S_db_noRSET;
         if (*ptrace)
@@ -592,7 +593,7 @@ long dbProcess(dbCommon *precord)
         dbPrint(precord);
     }
 
-all_done:
+  all_done:
     if (set_trace)
         *ptrace = 0;
     if (callNotifyCompletion && precord->ppn)
@@ -601,7 +602,7 @@ all_done:
     return status;
 }
 
-long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
+long dbEntryToAddr(const DBENTRY * pdbentry, DBADDR *paddr)
 {
     dbFldDes *pflddes = pdbentry->pflddes;
     short dbfType = pflddes->field_type;
@@ -610,9 +611,9 @@ long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
     paddr->pfield = pdbentry->pfield;
     paddr->pfldDes = pflddes;
     paddr->no_elements = 1;
-    paddr->field_type  = dbfType;
-    paddr->field_size  = pflddes->size;
-    paddr->special     = pflddes->special;
+    paddr->field_type = dbfType;
+    paddr->field_size = pflddes->size;
+    paddr->special = pflddes->special;
     paddr->dbr_field_type = mapDBFToDBR[dbfType];
 
     if (paddr->special == SPC_DBADDR) {
@@ -643,16 +644,20 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
 
     dbInitEntry(pdbbase, &dbEntry);
     status = dbFindRecordPart(&dbEntry, &pname);
-    if (status) goto finish;
+    if (status)
+        goto finish;
 
-    if (*pname == '.') ++pname;
+    if (*pname == '.')
+        ++pname;
     status = dbFindFieldPart(&dbEntry, &pname);
     if (status == S_dbLib_fieldNotFound)
         status = dbGetAttributePart(&dbEntry, &pname);
-    if (status) goto finish;
+    if (status)
+        goto finish;
 
     status = dbEntryToAddr(&dbEntry, paddr);
-    if (status) goto finish;
+    if (status)
+        goto finish;
 
     /* Handle field modifiers */
     if (*pname++ == '$') {
@@ -664,29 +669,27 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
             paddr->field_type = DBF_CHAR;
             paddr->field_size = 1;
             paddr->dbr_field_type = DBR_CHAR;
-        }
-        else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
+        } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
             /* Clients see a char array, but keep original dbfType */
             paddr->no_elements = PVLINK_STRINGSZ;
             paddr->field_size = 1;
             paddr->dbr_field_type = DBR_CHAR;
-        }
-        else {
+        } else {
             status = S_dbLib_fieldNotFound;
         }
     }
 
-finish:
+  finish:
     dbFinishEntry(&dbEntry);
     return status;
 }
 
-void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
+void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY * pdbentry)
 {
     struct dbCommon *prec = paddr->precord;
     dbCommonPvt *ppvt = dbRec2Pvt(prec);
 
-    memset((char *)pdbentry,'\0',sizeof(DBENTRY));
+    memset((char *)pdbentry, '\0', sizeof(DBENTRY));
 
     pdbentry->pdbbase = pdbbase;
     pdbentry->precordType = prec->rdes;
@@ -696,24 +699,24 @@ void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
     pdbentry->indfield = paddr->pfldDes->indRecordType;
 }
 
-void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
+void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY * pdbentry)
 {
     dbCommonPvt *ppvt = dbRec2Pvt(prec);
 
-    memset((char *)pdbentry,'\0',sizeof(DBENTRY));
+    memset((char *)pdbentry, '\0', sizeof(DBENTRY));
 
     pdbentry->pdbbase = pdbbase;
     pdbentry->precordType = prec->rdes;
     pdbentry->precnode = ppvt->recnode;
 }
 
-struct link* dbGetDevLink(struct dbCommon* prec)
+struct link *dbGetDevLink(struct dbCommon *prec)
 {
     DBLINK *plink = 0;
     DBENTRY entry;
     dbInitEntryFromRecord(prec, &entry);
-    if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) {
-        plink = (DBLINK*)entry.pfield;
+    if (dbFindField(&entry, "INP") == 0 || dbFindField(&entry, "OUT") == 0) {
+        plink = (DBLINK *) entry.pfield;
     }
     dbFinishEntry(&entry);
     return plink;
@@ -723,41 +726,53 @@ long dbValueSize(short dbr_type)
 {
     /* sizes for value associated with each DBR request type */
     static long size[] = {
-        MAX_STRING_SIZE,             /* STRING       */
-        sizeof(epicsInt8),           /* CHAR         */
-        sizeof(epicsUInt8),          /* UCHAR        */
-        sizeof(epicsInt16),          /* SHORT        */
-        sizeof(epicsUInt16),         /* USHORT       */
-        sizeof(epicsInt32),          /* LONG         */
-        sizeof(epicsUInt32),         /* ULONG        */
-        sizeof(epicsInt64),          /* INT64        */
-        sizeof(epicsUInt64),         /* UINT64       */
-        sizeof(epicsFloat32),        /* FLOAT        */
-        sizeof(epicsFloat64),        /* DOUBLE       */
-        sizeof(epicsEnum16)};        /* ENUM         */
-
-    return(size[dbr_type]);
+        MAX_STRING_SIZE,                /* STRING       */
+        sizeof(epicsInt8),              /* CHAR         */
+        sizeof(epicsUInt8),             /* UCHAR        */
+        sizeof(epicsInt16),             /* SHORT        */
+        sizeof(epicsUInt16),            /* USHORT       */
+        sizeof(epicsInt32),             /* LONG         */
+        sizeof(epicsUInt32),            /* ULONG        */
+        sizeof(epicsInt64),             /* INT64        */
+        sizeof(epicsUInt64),            /* UINT64       */
+        sizeof(epicsFloat32),           /* FLOAT        */
+        sizeof(epicsFloat64),           /* DOUBLE       */
+        sizeof(epicsEnum16)
+    };                                  /* ENUM         */
+
+    return (size[dbr_type]);
 }
 
-
 long dbBufferSize(short dbr_type, long options, long no_elements)
 {
-    long nbytes=0;
+    long nbytes = 0;
 
     nbytes += dbValueSize(dbr_type) * no_elements;
-    if (options & DBR_STATUS)      nbytes += dbr_status_size;
-    if (options & DBR_UNITS)       nbytes += dbr_units_size;
-    if (options & DBR_PRECISION)   nbytes += dbr_precision_size;
-    if (options & DBR_TIME)        nbytes += dbr_time_size;
-    if (options & DBR_ENUM_STRS)   nbytes += dbr_enumStrs_size;
-    if (options & DBR_GR_LONG)     nbytes += dbr_grLong_size;
-    if (options & DBR_GR_DOUBLE)   nbytes += dbr_grDouble_size;
-    if (options & DBR_CTRL_LONG)   nbytes += dbr_ctrlLong_size;
-    if (options & DBR_CTRL_DOUBLE) nbytes += dbr_ctrlDouble_size;
-    if (options & DBR_AL_LONG)     nbytes += dbr_alLong_size;
-    if (options & DBR_AL_DOUBLE)   nbytes += dbr_alDouble_size;
-    return(nbytes);
+    if (options & DBR_STATUS)
+        nbytes += dbr_status_size;
+    if (options & DBR_UNITS)
+        nbytes += dbr_units_size;
+    if (options & DBR_PRECISION)
+        nbytes += dbr_precision_size;
+    if (options & DBR_TIME)
+        nbytes += dbr_time_size;
+    if (options & DBR_ENUM_STRS)
+        nbytes += dbr_enumStrs_size;
+    if (options & DBR_GR_LONG)
+        nbytes += dbr_grLong_size;
+    if (options & DBR_GR_DOUBLE)
+        nbytes += dbr_grDouble_size;
+    if (options & DBR_CTRL_LONG)
+        nbytes += dbr_ctrlLong_size;
+    if (options & DBR_CTRL_DOUBLE)
+        nbytes += dbr_ctrlDouble_size;
+    if (options & DBR_AL_LONG)
+        nbytes += dbr_alLong_size;
+    if (options & DBR_AL_DOUBLE)
+        nbytes += dbr_alDouble_size;
+    return (nbytes);
 }
+
 int dbLoadDatabase(const char *file, const char *path, const char *subs)
 {
     if (!file) {
@@ -767,7 +782,7 @@ int dbLoadDatabase(const char *file, const char *path, const char *subs)
     return dbReadDatabase(&pdbbase, file, path, subs);
 }
 
-int dbLoadRecords(const char* file, const char* subs)
+int dbLoadRecords(const char *file, const char *subs)
 {
     int status;
 
@@ -781,7 +796,6 @@ int dbLoadRecords(const char* file, const char* subs)
     return status;
 }
 
-
 static long getLinkValue(DBADDR *paddr, short dbrType,
     char *pbuf, long *nRequest)
 {
@@ -797,7 +811,7 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
      * valid DBADDR, so no point to check again.
      * Request for zero elements always succeeds
      */
-    if(!nReq)
+    if (!nReq)
         return 0;
 
     switch (dbrType) {
@@ -806,8 +820,9 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
         nReq = 1;
         break;
 
-    case DBR_DOUBLE:    /* Needed for dbCa links */
-        if (nRequest) *nRequest = 1;
+    case DBR_DOUBLE:                   /* Needed for dbCa links */
+        if (nRequest)
+            *nRequest = 1;
         *(double *)pbuf = epicsNAN;
         return 0;
 
@@ -821,27 +836,30 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
 
     dbInitEntry(pdbbase, &dbEntry);
     status = dbFindRecord(&dbEntry, precord->name);
-    if (!status) status = dbFindField(&dbEntry, pfldDes->name);
+    if (!status)
+        status = dbFindField(&dbEntry, pfldDes->name);
     if (!status) {
         const char *rtnString = dbGetString(&dbEntry);
 
-        strncpy(pbuf, rtnString, maxlen-1);
-        pbuf[maxlen-1] = 0;
-        if(dbrType!=DBR_STRING)
-            nReq = strlen(pbuf)+1;
-        if(nRequest) *nRequest = nReq;
+        strncpy(pbuf, rtnString, maxlen - 1);
+        pbuf[maxlen - 1] = 0;
+        if (dbrType != DBR_STRING)
+            nReq = strlen(pbuf) + 1;
+        if (nRequest)
+            *nRequest = nReq;
     }
     dbFinishEntry(&dbEntry);
     return status;
 }
 
 static long getAttrValue(DBADDR *paddr, short dbrType,
-        char *pbuf, long *nRequest)
+    char *pbuf, long *nRequest)
 {
     int maxlen;
     long nReq = nRequest ? *nRequest : 1;
 
-    if (!paddr->pfield) return S_db_badField;
+    if (!paddr->pfield)
+        return S_db_badField;
 
     switch (dbrType) {
     case DBR_STRING:
@@ -859,22 +877,23 @@ static long getAttrValue(DBADDR *paddr, short dbrType,
         return S_db_badDbrtype;
     }
 
-    strncpy(pbuf, paddr->pfield, maxlen-1);
-    pbuf[maxlen-1] = 0;
-    if(dbrType!=DBR_STRING)
-        nReq = strlen(pbuf)+1;
-    if(nRequest) *nRequest = nReq;
+    strncpy(pbuf, paddr->pfield, maxlen - 1);
+    pbuf[maxlen - 1] = 0;
+    if (dbrType != DBR_STRING)
+        nReq = strlen(pbuf) + 1;
+    if (nRequest)
+        *nRequest = nReq;
     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;
 }
@@ -884,7 +903,7 @@ long dbGet(DBADDR *paddr, short dbrType,
 {
     char *pbuf = pbuffer;
     void *pfieldsave = paddr->pfield;
-    db_field_log *pfl = (db_field_log *)pflin;
+    db_field_log *pfl = (db_field_log *) pflin;
     short field_type;
     long capacity, no_elements, offset;
     rset *prset;
@@ -895,7 +914,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;
 
@@ -903,15 +922,14 @@ long dbGet(DBADDR *paddr, short dbrType,
          * may modify paddr->pfield
          */
         if (paddr->pfldDes->special == SPC_DBADDR &&
-            (prset = dbGetRset(paddr)) &&
-            prset->get_array_info) {
+            (prset = dbGetRset(paddr)) && prset->get_array_info) {
             status = prset->get_array_info(paddr, &no_elements, &offset);
         } else
             offset = 0;
     } 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,11 +955,11 @@ 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 {
-            DBADDR localAddr = *paddr; /* Structure copy */
+            DBADDR localAddr = *paddr;  /* Structure copy */
 
             if (pfl->no_elements < 1) {
                 status = S_db_badField;
@@ -950,11 +968,12 @@ 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;
+                localAddr.pfield = (char *)&pfl->u.v.field;
             else
-                localAddr.pfield = (char *)  pfl->u.r.field;
+                localAddr.pfield = (char *)pfl->u.r.field;
             status = dbFastGetConvertRoutine[field_type][dbrType]
                 (localAddr.pfield, pbuf, &localAddr);
         }
@@ -974,52 +993,55 @@ long dbGet(DBADDR *paddr, short dbrType,
             char message[80];
 
             sprintf(message, "dbGet: Missing conversion for [%d][%d]\n",
-                    field_type, dbrType);
+                field_type, dbrType);
             recGblDbaddrError(S_db_badDbrtype, paddr, message);
             status = S_db_badDbrtype;
             goto done;
         }
         /* 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 */
+            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;
+                localAddr.pfield = (char *)&pfl->u.v.field;
             else
-                localAddr.pfield = (char *)  pfl->u.r.field;
+                localAddr.pfield = (char *)pfl->u.r.field;
             status = convert(&localAddr, pbuf, n, capacity, offset);
         }
 
-        if(!status && dbrType==DBF_CHAR && nRequest &&
-                paddr->pfldDes && paddr->pfldDes->field_type==DBF_STRING)
-        {
+        if (!status && dbrType == DBF_CHAR && nRequest &&
+            paddr->pfldDes && paddr->pfldDes->field_type == DBF_STRING) {
             /* long string ensure nil and truncate to actual length */
             long nReq = *nRequest;
-            pbuf[nReq-1] = '\0';
-            *nRequest = strlen(pbuf)+1;
+            pbuf[nReq - 1] = '\0';
+            *nRequest = strlen(pbuf) + 1;
         }
     }
-done:
+  done:
     paddr->pfield = pfieldsave;
     return status;
 }
 
-devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp) {
-    return (devSup *)ellNth(&prdes->devList, dtyp+1);
+devSup *dbDTYPtoDevSup(dbRecordType * prdes, int dtyp)
+{
+    return (devSup *) ellNth(&prdes->devList, dtyp + 1);
 }
 
-devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset) {
-    devSup *pdevSup = (devSup *)ellFirst(&prdes->devList);
+devSup *dbDSETtoDevSup(dbRecordType * prdes, dset * pdset)
+{
+    devSup *pdevSup = (devSup *) ellFirst(&prdes->devList);
     while (pdevSup) {
-        if (pdset == pdevSup->pdset) return pdevSup;
-        pdevSup = (devSup *)ellNext(&pdevSup->node);
+        if (pdset == pdevSup->pdset)
+            return pdevSup;
+        pdevSup = (devSup *) ellNext(&pdevSup->node);
     }
     return NULL;
 }
@@ -1027,24 +1049,24 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset) {
 static long dbPutFieldLink(DBADDR *paddr,
     short dbrType, const void *pbuffer, long nRequest)
 {
-    dbLinkInfo  link_info;
-    dbChannel   *chan = NULL;
-    dbCommon    *precord = paddr->precord;
-    dbCommon    *lockrecs[2];
-    dbLocker    locker;
-    dbFldDes    *pfldDes = paddr->pfldDes;
-    long        special = paddr->special;
+    dbLinkInfo link_info;
+    dbChannel *chan = NULL;
+    dbCommon *precord = paddr->precord;
+    dbCommon *lockrecs[2];
+    dbLocker locker;
+    dbFldDes *pfldDes = paddr->pfldDes;
+    long special = paddr->special;
     struct link *plink = (struct link *)paddr->pfield;
-    const char  *pstring = (const char *)pbuffer;
+    const char *pstring = (const char *)pbuffer;
     struct dsxt *old_dsxt = NULL;
     dset *new_dset = NULL;
     struct dsxt *new_dsxt = NULL;
-    devSup      *new_devsup = NULL;
-    long        status;
-    int         isDevLink;
-    short       scan;
+    devSup *new_devsup = NULL;
+    long status;
+    int isDevLink;
+    short scan;
 
-    STATIC_ASSERT(DBLOCKER_NALLOC>=2);
+    STATIC_ASSERT(DBLOCKER_NALLOC >= 2);
 
     switch (dbrType) {
     case DBR_CHAR:
@@ -1068,14 +1090,14 @@ static long dbPutFieldLink(DBADDR *paddr,
         (link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) {
         chan = dbChannelCreate(link_info.target);
         if (chan && dbChannelOpen(chan) != 0) {
-            errlogPrintf("ERROR: dbPutFieldLink %s.%s=%s: dbChannelOpen() failed\n",
+            errlogPrintf
+                ("ERROR: dbPutFieldLink %s.%s=%s: dbChannelOpen() failed\n",
                 precord->name, pfldDes->name, link_info.target);
             goto cleanup;
         }
     }
 
-    isDevLink = ellCount(&precord->rdes->devList) > 0 &&
-                pfldDes->isDevLink;
+    isDevLink = ellCount(&precord->rdes->devList) > 0 && pfldDes->isDevLink;
 
     memset(&locker, 0, sizeof(locker));
     lockrecs[0] = precord;
@@ -1130,17 +1152,19 @@ static long dbPutFieldLink(DBADDR *paddr,
 
     if (dbLinkIsDefined(plink)) {
         dbRemoveLink(&locker, plink);   /* Clear out old link */
-    }
-    else if (!isDevLink) {
+    } else if (!isDevLink) {
         status = S_db_badHWaddr;
         goto restoreScan;
     }
 
-    if (special) status = dbPutSpecial(paddr, 0);
+    if (special)
+        status = dbPutSpecial(paddr, 0);
 
-    if (!status) status = dbSetLink(plink, &link_info, new_devsup);
+    if (!status)
+        status = dbSetLink(plink, &link_info, new_devsup);
 
-    if (!status && special) status = dbPutSpecial(paddr, 1);
+    if (!status && special)
+        status = dbPutSpecial(paddr, 1);
 
     if (status) {
         if (isDevLink) {
@@ -1163,20 +1187,20 @@ static long dbPutFieldLink(DBADDR *paddr,
         }
     }
 
-    switch (plink->type) { /* New link type */
+    switch (plink->type) {              /* New link type */
     case PV_LINK:
     case CONSTANT:
     case JSON_LINK:
         dbAddLink(&locker, plink, pfldDes->field_type, chan);
-        chan = NULL; /* don't clean it up */
+        chan = NULL;                    /* don't clean it up */
         break;
 
     case DB_LINK:
     case CA_LINK:
     case MACRO_LINK:
-        break;  /* should never get here */
+        break;                          /* should never get here */
 
-    default: /* Hardware address */
+    default:                           /* Hardware address */
         if (!isDevLink) {
             status = S_db_badHWaddr;
             goto postScanEvent;
@@ -1185,19 +1209,18 @@ static long dbPutFieldLink(DBADDR *paddr,
     }
     db_post_events(precord, plink, DBE_VALUE | DBE_LOG);
 
-restoreScan:
-    if (isDevLink &&
-        scan == menuScanI_O_Intr) { /* undo scanDelete() */
+  restoreScan:
+    if (isDevLink && scan == menuScanI_O_Intr) { /* undo scanDelete() */
         precord->scan = scan;
         scanAdd(precord);
     }
-postScanEvent:
+  postScanEvent:
     if (scan != precord->scan)
         db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
-unlock:
+  unlock:
     dbScanUnlockMany(&locker);
     dbLockerFinalize(&locker);
-cleanup:
+  cleanup:
     if (chan)
         dbChannelDelete(chan);
     free(link_info.target);
@@ -1207,16 +1230,16 @@ cleanup:
 long dbPutField(DBADDR *paddr, short dbrType,
     const void *pbuffer, long nRequest)
 {
-    long	status = 0;
-    long	special  = paddr->special;
-    dbFldDes	*pfldDes = paddr->pfldDes;
-    dbCommon	*precord = paddr->precord;
-    short	dbfType  = paddr->field_type;
+    long status = 0;
+    long special = paddr->special;
+    dbFldDes *pfldDes = paddr->pfldDes;
+    dbCommon *precord = paddr->precord;
+    short dbfType = paddr->field_type;
 
     if (special == SPC_ATTRIBUTE)
         return S_db_noMod;
 
-    /*check for putField disabled*/
+    /*check for putField disabled */
     if (precord->disp && paddr->pfield != &precord->disp)
         return S_db_putDisabled;
 
@@ -1228,8 +1251,7 @@ long dbPutField(DBADDR *paddr, short dbrType,
     if (status == 0) {
         if (paddr->pfield == &precord->proc ||
             (pfldDes->process_passive &&
-             precord->scan == 0 &&
-             dbrType < DBR_PUT_ACKT)) {
+                precord->scan == 0 && dbrType < DBR_PUT_ACKT)) {
             if (precord->pact) {
                 if (dbAccessDebugPUTF && precord->tpro)
                     printf("%s: dbPutField to Active '%s', setting RPRO=1\n",
@@ -1252,11 +1274,11 @@ static long putAckt(DBADDR *paddr, const void *pbuffer, long nRequest,
     dbCommon *precord = paddr->precord;
     const unsigned short *ptrans = pbuffer;
 
-    if (*ptrans == precord->ackt) return 0;
+    if (*ptrans == precord->ackt)
+        return 0;
     precord->ackt = *ptrans;
     db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM);
-    if (!precord->ackt &&
-        precord->acks > precord->sevr) {
+    if (!precord->ackt && precord->acks > precord->sevr) {
         precord->acks = precord->sevr;
         db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM);
     }
@@ -1278,14 +1300,13 @@ static long putAcks(DBADDR *paddr, const void *pbuffer, long nRequest,
     return 0;
 }
 
-long dbPut(DBADDR *paddr, short dbrType,
-    const void *pbuffer, long nRequest)
+long dbPut(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest)
 {
     dbCommon *precord = paddr->precord;
-    short field_type  = paddr->field_type;
-    long no_elements  = paddr->no_elements;
-    long special      = paddr->special;
-    void *pfieldsave  = paddr->pfield;
+    short field_type = paddr->field_type;
+    long no_elements = paddr->no_elements;
+    long special = paddr->special;
+    void *pfieldsave = paddr->pfield;
     rset *prset = dbGetRset(paddr);
     long status = 0;
     long offset;
@@ -1309,7 +1330,8 @@ long dbPut(DBADDR *paddr, short dbrType,
 
     if (special) {
         status = dbPutSpecial(paddr, 0);
-        if (status) return status;
+        if (status)
+            return status;
     }
 
     if (paddr->pfldDes->special == SPC_DBADDR &&
@@ -1318,18 +1340,19 @@ long dbPut(DBADDR *paddr, short dbrType,
 
         status = prset->get_array_info(paddr, &dummy, &offset);
         /* paddr->pfield may be modified */
-        if (status) goto done;
+        if (status)
+            goto done;
     } else
         offset = 0;
 
     if (no_elements <= 1) {
-        status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,
+        status = dbFastPutConvertRoutine[dbrType][field_type] (pbuffer,
             paddr->pfield, paddr);
         nRequest = 1;
     } else {
         if (no_elements < nRequest)
             nRequest = no_elements;
-        status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
+        status = dbPutConvertRoutine[dbrType][field_type] (paddr, pbuffer,
             nRequest, no_elements, offset);
     }
 
@@ -1343,17 +1366,19 @@ long dbPut(DBADDR *paddr, short dbrType,
     /* Always do special processing if needed */
     if (special) {
         long status2 = dbPutSpecial(paddr, 1);
-        if (status2) goto done;
+        if (status2)
+            goto done;
     }
-    if (status) goto done;
+    if (status)
+        goto done;
 
     /* Propagate monitor events for this field, */
     /* unless the field is VAL and PP is true. */
     pfldDes = paddr->pfldDes;
     isValueField = dbIsValueField(pfldDes);
-    if (isValueField) precord->udf = FALSE;
-    if (precord->mlis.count &&
-        !(isValueField && pfldDes->process_passive))
+    if (isValueField)
+        precord->udf = FALSE;
+    if (precord->mlis.count && !(isValueField && pfldDes->process_passive))
         db_post_events(precord, pfieldsave, DBE_VALUE | DBE_LOG);
     /* If this field is a property (metadata) field,
      * then post a property change event (even if the field
@@ -1361,7 +1386,7 @@ long dbPut(DBADDR *paddr, short dbrType,
      */
     if (precord->mlis.count && pfldDes->prop)
         db_post_events(precord, NULL, DBE_PROPERTY);
-done:
+  done:
     paddr->pfield = pfieldsave;
     return status;
 }
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 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  <20202021  2022  2023  2024  2025 
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  <20202021  2022  2023  2024  2025 
ANJ, 25 Mar 2020 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions ·
· Download · Search · IRMIS · Talk · Documents · Links · Licensing ·