Merge ~bfrk/epics-base:write-filters into ~epics-core/epics-base/+git/epics-base:7.0

Proposed by Ben Franksen
Status: Superseded
Proposed branch: ~bfrk/epics-base:write-filters
Merge into: ~epics-core/epics-base/+git/epics-base:7.0
Prerequisite: ~dirk.zimoch/epics-base:dbChannelForDBLinks
Diff against target: 1056 lines (+274/-268) (has conflicts)
16 files modified
modules/database/src/ioc/db/dbAccess.c (+25/-20)
modules/database/src/ioc/db/dbAccessDefs.h (+1/-1)
modules/database/src/ioc/db/dbChannel.c (+17/-55)
modules/database/src/ioc/db/dbChannel.h (+5/-3)
modules/database/src/ioc/db/dbDbLink.c (+4/-1)
modules/database/src/ioc/db/dbEvent.c (+36/-33)
modules/database/src/ioc/db/dbExtractArray.c (+19/-49)
modules/database/src/ioc/db/dbExtractArray.h (+30/-5)
modules/database/src/ioc/db/dbTest.c (+16/-16)
modules/database/src/ioc/db/dbUnitTest.c (+2/-2)
modules/database/src/ioc/db/db_field_log.h (+23/-30)
modules/database/src/std/filters/arr.c (+57/-34)
modules/database/src/std/filters/ts.c (+32/-9)
modules/database/test/ioc/db/dbChArrTest.cpp (+1/-1)
modules/database/test/std/filters/arrTest.cpp (+3/-3)
modules/database/test/std/filters/dbndTest.c (+3/-6)
Conflict in modules/database/src/std/filters/arr.c
Reviewer Review Type Date Requested Status
EPICS Core Developers Pending
Review via email: mp+381272@code.launchpad.net

This proposal supersedes a proposal from 2020-03-25.

This proposal has been superseded by a proposal from 2020-03-31.

Description of the change

This is just the initial refactor, no new features yet.

To post a comment you must log in.
Revision history for this message
Ben Franksen (bfrk) wrote : Posted in a previous version of this proposal

I see now that the diff is dominated by my first commit that reformats dbAccess.c. The actual refactor makes only very few changes there. If this hurts I can rebase this patch out of the current branch. How do you people treat code reformatting patches? I did this one with my standard C indent config that very closely matches the standard EPICS base style.

Revision history for this message
Ben Franksen (bfrk) wrote : Posted in a previous version of this proposal

I rebased and resubmitted to get rid of the reformat commit. Will create a different merge request for that one.

Revision history for this message
Dirk Zimoch (dirk.zimoch) wrote : Posted in a previous version of this proposal

Apropos reformat commit: Should we do a general reformat of EPICS base? For example get rid of all the tabs.

Revision history for this message
Ben Franksen (bfrk) wrote : Posted in a previous version of this proposal

> Apropos reformat commit: Should we do a general reformat of EPICS base? For
> example get rid of all the tabs.

I suggest to move this discussion to https://code.launchpad.net/~bfrk/epics-base/+git/epics-base/+merge/381153 or core-talk.

Revision history for this message
Ben Franksen (bfrk) wrote :

I have renamed my branch back to write-filters and removed the latest commit.

~bfrk/epics-base:write-filters updated
1457519... by Dirk Zimoch

clean up code structure

6d211c7... by Ben Franksen

refactor db_field_log and filters to get rid of dbfl_type_rec

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

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

The dbExtractArray* functions are unified to the single function
dbExtractArray and stripped of conversion functionality. This is redundant
because we always call dbGet after applying filters, which takes care of
conversion. Accordingly, dbChannelMakeArrayCopy is now obsolete and its
single use (in the ts filter) replaced with dbExtractArray. Instead, we add
the helper function dbChannelGetArrayInfo to wrap the common boilerplate
around calls to the get_array_info method, used in both arr.c and ts.c.

7287a1f... by Ben Franksen

Remove parameter db_field_log* from dbPutField

It turned out that dbGetField was always called with a NULL pointer as its
db_field_log* argument. This makes sense because the extra db_field_log
parameter of dbGet is an internal implementation detail and not something
that should be visible in the external API. Nowadays, even dbGetField is no
longer quite appropriate for user code, which should rather call
dbChannelGetField in order to profit from filters and long strings.

3d977d9... by Ben Franksen

make it clearer what the result of wrapArrayIndices will be

Unmerged commits

6d211c7... by Ben Franksen

refactor db_field_log and filters to get rid of dbfl_type_rec

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

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

The dbExtractArray* functions are unified to the single function
dbExtractArray and stripped of conversion functionality. This is redundant
because we always call dbGet after applying filters, which takes care of
conversion. Accordingly, dbChannelMakeArrayCopy is now obsolete and its
single use (in the ts filter) replaced with dbExtractArray. Instead, we add
the helper function dbChannelGetArrayInfo to wrap the common boilerplate
around calls to the get_array_info method, used in both arr.c and ts.c.

3d977d9... by Ben Franksen

make it clearer what the result of wrapArrayIndices will be

7287a1f... by Ben Franksen

Remove parameter db_field_log* from dbPutField

It turned out that dbGetField was always called with a NULL pointer as its
db_field_log* argument. This makes sense because the extra db_field_log
parameter of dbGet is an internal implementation detail and not something
that should be visible in the external API. Nowadays, even dbGetField is no
longer quite appropriate for user code, which should rather call
dbChannelGetField in order to profit from filters and long strings.

1457519... by Dirk Zimoch

clean up code structure

e419914... by Dirk Zimoch

Release notes updated

ed0b4d4... by Dirk Zimoch

set number of planned link filter tests

fc61f20... by Dirk Zimoch

removed unnecessary recGblSetSevr call

8a8de85... by Dirk Zimoch

re-order link filter tests to alternate between success and failure

1324246... by Dirk Zimoch

unused variable removed

2986080... by Dirk Zimoch

Revert "fix crash in PINI: use local db_field_log"

This reverts commit a590151accb1d187562c515a48e013244dd98a45.

Conflicts:
 modules/database/src/ioc/db/dbDbLink.c

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index 9b149dd..3709049 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,
338 dbCommon *pcommon;338 dbCommon *pcommon;
339 char *pbuffer = *poriginal;339 char *pbuffer = *poriginal;
340340
341 if (!pfl || pfl->type == dbfl_type_rec)341 if (!pfl)
342 field_type = paddr->field_type;342 field_type = paddr->field_type;
343 else343 else
344 field_type = pfl->field_type;344 field_type = pfl->field_type;
@@ -348,7 +348,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
348 if( (*options) & DBR_STATUS ) {348 if( (*options) & DBR_STATUS ) {
349 unsigned short *pushort = (unsigned short *)pbuffer;349 unsigned short *pushort = (unsigned short *)pbuffer;
350350
351 if (!pfl || pfl->type == dbfl_type_rec) {351 if (!pfl) {
352 *pushort++ = pcommon->stat;352 *pushort++ = pcommon->stat;
353 *pushort++ = pcommon->sevr;353 *pushort++ = pcommon->sevr;
354 } else {354 } else {
@@ -382,7 +382,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
382 if( (*options) & DBR_TIME ) {382 if( (*options) & DBR_TIME ) {
383 epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;383 epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
384384
385 if (!pfl || pfl->type == dbfl_type_rec) {385 if (!pfl) {
386 *ptime++ = pcommon->time.secPastEpoch;386 *ptime++ = pcommon->time.secPastEpoch;
387 *ptime++ = pcommon->time.nsec;387 *ptime++ = pcommon->time.nsec;
388 } else {388 } else {
@@ -867,14 +867,14 @@ static long getAttrValue(DBADDR *paddr, short dbrType,
867 return 0;867 return 0;
868}868}
869869
870long dbGetField(DBADDR *paddr,short dbrType,870long dbGetField(DBADDR *paddr, short dbrType,
871 void *pbuffer, long *options, long *nRequest, void *pflin)871 void *pbuffer, long *options, long *nRequest)
872{872{
873 dbCommon *precord = paddr->precord;873 dbCommon *precord = paddr->precord;
874 long status = 0;874 long status = 0;
875875
876 dbScanLock(precord);876 dbScanLock(precord);
877 status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin);877 status = dbGet(paddr, dbrType, pbuffer, options, nRequest, NULL);
878 dbScanUnlock(precord);878 dbScanUnlock(precord);
879 return status;879 return status;
880}880}
@@ -895,22 +895,23 @@ long dbGet(DBADDR *paddr, short dbrType,
895 if (nRequest && *nRequest == 0)895 if (nRequest && *nRequest == 0)
896 return 0;896 return 0;
897897
898 if (!pfl || pfl->type == dbfl_type_rec) {898 if (!pfl) {
899 field_type = paddr->field_type;899 field_type = paddr->field_type;
900 no_elements = capacity = paddr->no_elements;900 no_elements = capacity = paddr->no_elements;
901
902 /* Update field info from record
903 * may modify paddr->pfield
904 */
905 if (paddr->pfldDes->special == SPC_DBADDR &&
906 (prset = dbGetRset(paddr)) &&
907 prset->get_array_info) {
908 status = prset->get_array_info(paddr, &no_elements, &offset);
909 } else
910 offset = 0;
911 } else {901 } else {
912 field_type = pfl->field_type;902 field_type = pfl->field_type;
913 no_elements = capacity = pfl->no_elements;903 no_elements = capacity = pfl->no_elements;
904 }
905
906 /* Update field info from record
907 * may modify paddr->pfield
908 */
909 if ((!pfl || (pfl->type==dbfl_type_ref && !pfl->u.r.dtor)) &&
910 paddr->pfldDes->special == SPC_DBADDR &&
911 (prset = dbGetRset(paddr)) &&
912 prset->get_array_info) {
913 status = prset->get_array_info(paddr, &no_elements, &offset);
914 } else {
914 offset = 0;915 offset = 0;
915 }916 }
916917
@@ -937,7 +938,7 @@ long dbGet(DBADDR *paddr, short dbrType,
937 if (offset == 0 && (!nRequest || no_elements == 1)) {938 if (offset == 0 && (!nRequest || no_elements == 1)) {
938 if (nRequest)939 if (nRequest)
939 *nRequest = 1;940 *nRequest = 1;
940 if (!pfl || pfl->type == dbfl_type_rec) {941 if (!pfl) {
941 status = dbFastGetConvertRoutine[field_type][dbrType]942 status = dbFastGetConvertRoutine[field_type][dbrType]
942 (paddr->pfield, pbuf, paddr);943 (paddr->pfield, pbuf, paddr);
943 } else {944 } else {
@@ -950,6 +951,7 @@ long dbGet(DBADDR *paddr, short dbrType,
950951
951 localAddr.field_type = pfl->field_type;952 localAddr.field_type = pfl->field_type;
952 localAddr.field_size = pfl->field_size;953 localAddr.field_size = pfl->field_size;
954 /* not used by dbFastConvert: */
953 localAddr.no_elements = pfl->no_elements;955 localAddr.no_elements = pfl->no_elements;
954 if (pfl->type == dbfl_type_val)956 if (pfl->type == dbfl_type_val)
955 localAddr.pfield = (char *) &pfl->u.v.field;957 localAddr.pfield = (char *) &pfl->u.v.field;
@@ -965,6 +967,8 @@ long dbGet(DBADDR *paddr, short dbrType,
965 if (nRequest) {967 if (nRequest) {
966 if (no_elements < *nRequest)968 if (no_elements < *nRequest)
967 *nRequest = no_elements;969 *nRequest = no_elements;
970 if (capacity < *nRequest)
971 *nRequest = capacity;
968 n = *nRequest;972 n = *nRequest;
969 } else {973 } else {
970 n = 1;974 n = 1;
@@ -981,14 +985,15 @@ long dbGet(DBADDR *paddr, short dbrType,
981 }985 }
982 /* convert data into the caller's buffer */986 /* convert data into the caller's buffer */
983 if (n <= 0) {987 if (n <= 0) {
984 ;/*do nothing*/988 ; /*do nothing */
985 } else if (!pfl || pfl->type == dbfl_type_rec) {989 } else if (!pfl) {
986 status = convert(paddr, pbuf, n, capacity, offset);990 status = convert(paddr, pbuf, n, capacity, offset);
987 } else {991 } else {
988 DBADDR localAddr = *paddr; /* Structure copy */992 DBADDR localAddr = *paddr; /* Structure copy */
989993
990 localAddr.field_type = pfl->field_type;994 localAddr.field_type = pfl->field_type;
991 localAddr.field_size = pfl->field_size;995 localAddr.field_size = pfl->field_size;
996 /* not used by dbConvert, it uses the passed capacity instead: */
992 localAddr.no_elements = pfl->no_elements;997 localAddr.no_elements = pfl->no_elements;
993 if (pfl->type == dbfl_type_val)998 if (pfl->type == dbfl_type_val)
994 localAddr.pfield = (char *) &pfl->u.v.field;999 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);
243epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset);243epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset);
244epicsShareFunc long dbGetField(244epicsShareFunc long dbGetField(
245 struct dbAddr *,short dbrType,void *pbuffer,long *options,245 struct dbAddr *,short dbrType,void *pbuffer,long *options,
246 long *nRequest,void *pfl);246 long *nRequest);
247epicsShareFunc long dbGet(247epicsShareFunc long dbGet(
248 struct dbAddr *,short dbrType,void *pbuffer,long *options,248 struct dbAddr *,short dbrType,void *pbuffer,long *options,
249 long *nRequest,void *pfl);249 long *nRequest,void *pfl);
diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
index a8bfbf1..4c1215c 100644
--- a/modules/database/src/ioc/db/dbChannel.c
+++ b/modules/database/src/ioc/db/dbChannel.c
@@ -49,14 +49,12 @@ typedef struct parseContext {
4949
50static void *dbChannelFreeList;50static void *dbChannelFreeList;
51static void *chFilterFreeList;51static void *chFilterFreeList;
52static void *dbchStringFreeList;
5352
54void dbChannelExit(void)53void dbChannelExit(void)
55{54{
56 freeListCleanup(dbChannelFreeList);55 freeListCleanup(dbChannelFreeList);
57 freeListCleanup(chFilterFreeList);56 freeListCleanup(chFilterFreeList);
58 freeListCleanup(dbchStringFreeList);57 dbChannelFreeList = chFilterFreeList = NULL;
59 dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
60}58}
6159
62void dbChannelInit (void)60void dbChannelInit (void)
@@ -66,7 +64,6 @@ void dbChannelInit (void)
6664
67 freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);65 freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
68 freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);66 freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
69 freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
70 db_init_event_freelists();67 db_init_event_freelists();
71}68}
7269
@@ -446,28 +443,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
446 return status;443 return status;
447}444}
448445
449/* Stolen from dbAccess.c: */
450static short mapDBFToDBR[DBF_NTYPES] =
451 {
452 /* DBF_STRING => */DBR_STRING,
453 /* DBF_CHAR => */DBR_CHAR,
454 /* DBF_UCHAR => */DBR_UCHAR,
455 /* DBF_SHORT => */DBR_SHORT,
456 /* DBF_USHORT => */DBR_USHORT,
457 /* DBF_LONG => */DBR_LONG,
458 /* DBF_ULONG => */DBR_ULONG,
459 /* DBF_INT64 => */DBR_INT64,
460 /* DBF_UINT64 => */DBR_UINT64,
461 /* DBF_FLOAT => */DBR_FLOAT,
462 /* DBF_DOUBLE => */DBR_DOUBLE,
463 /* DBF_ENUM, => */DBR_ENUM,
464 /* DBF_MENU, => */DBR_ENUM,
465 /* DBF_DEVICE => */DBR_ENUM,
466 /* DBF_INLINK => */DBR_STRING,
467 /* DBF_OUTLINK => */DBR_STRING,
468 /* DBF_FWDLINK => */DBR_STRING,
469 /* DBF_NOACCESS => */DBR_NOACCESS };
470
471dbChannel * dbChannelCreate(const char *name)446dbChannel * dbChannelCreate(const char *name)
472{447{
473 const char *pname = name;448 const char *pname = name;
@@ -736,37 +711,24 @@ void dbChannelDelete(dbChannel *chan)
736 freeListFree(dbChannelFreeList, chan);711 freeListFree(dbChannelFreeList, chan);
737}712}
738713
739static void freeArray(db_field_log *pfl) {714/*
740 if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {715 * Helper function to adjust no_elements, offset, and pfield
741 freeListFree(dbchStringFreeList, pfl->u.r.field);716 * when copying an array from a record.
742 } else {717 */
743 free(pfl->u.r.field);718void dbChannelGetArrayInfo(dbChannel *chan,
744 }719 void **pfield, long *no_elements, long *offset)
745}
746
747void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
748{720{
749 void *p;721 rset *prset;
750 struct dbCommon *prec = dbChannelRecord(chan);722 if (dbChannelSpecial(chan) == SPC_DBADDR &&
751723 (prset = dbGetRset(&chan->addr)) &&
752 if (pfl->type != dbfl_type_rec) return;724 prset->get_array_info)
753725 {
754 pfl->type = dbfl_type_ref;726 void *pfieldsave = dbChannelField(chan);
755 pfl->stat = prec->stat;727 /* it is expected that this call always succeeds */
756 pfl->sevr = prec->sevr;728 prset->get_array_info(&chan->addr, no_elements, offset);
757 pfl->time = prec->time;729 *pfield = dbChannelField(chan);
758 pfl->field_type = chan->addr.field_type;730 dbChannelField(chan) = pfieldsave;
759 pfl->no_elements = chan->addr.no_elements;
760 pfl->field_size = chan->addr.field_size;
761 pfl->u.r.dtor = freeArray;
762 pfl->u.r.pvt = pvt;
763 if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
764 p = freeListCalloc(dbchStringFreeList);
765 } else {
766 p = calloc(pfl->no_elements, pfl->field_size);
767 }731 }
768 if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
769 pfl->u.r.field = p;
770}732}
771733
772/* FIXME: Do these belong in a different file? */734/* FIXME: Do these belong in a different file? */
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index fab9c66..7bfc9b0 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -64,8 +64,9 @@ typedef struct dbChannel {
64/* Prototype for the channel event function that is called in filter stacks64/* Prototype for the channel event function that is called in filter stacks
65 *65 *
66 * When invoked the scan lock for the record associated with 'chan' _may_ be locked.66 * When invoked the scan lock for the record associated with 'chan' _may_ be locked.
67 * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying67 * If pLog->type==dbfl_type_ref and pLog->u.r.dtor is NULL, then dbScanLock() must
68 * data out of the associated record.68 * be called before accessing the data, as this indicates the data is owned by the
69 * record.
69 *70 *
70 * This function has ownership of the field log pLog, if it wishes to discard71 * This function has ownership of the field log pLog, if it wishes to discard
71 * this update it should free the field log with db_delete_field_log() and72 * this update it should free the field log with db_delete_field_log() and
@@ -224,7 +225,8 @@ epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, voi
224epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);225epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
225epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);226epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
226epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);227epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
227epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);228epicsShareFunc void dbChannelGetArrayInfo(dbChannel *chan,
229 void **pfield, long *no_elements, long *offset);
228230
229#ifdef __cplusplus231#ifdef __cplusplus
230}232}
diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
index 688cb7a..4e1dc69 100644
--- a/modules/database/src/ioc/db/dbDbLink.c
+++ b/modules/database/src/ioc/db/dbDbLink.c
@@ -203,7 +203,10 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
203 /* filter, array, or special */203 /* filter, array, or special */
204 ppv_link->getCvt = NULL;204 ppv_link->getCvt = NULL;
205205
206 /* For the moment, empty arrays are not supported by EPICS */206 /*
207 * For the moment, empty arrays are not supported by EPICS.
208 * See the remark in src/std/filters/arr.c for details.
209 */
207 if (dbChannelFinalElements(chan) <= 0) /* empty array request */210 if (dbChannelFinalElements(chan) <= 0) /* empty array request */
208 return S_db_badField;211 return S_db_badField;
209212
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index ed73529..f3a6907 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -667,27 +667,21 @@ int db_post_extra_labor (dbEventCtx ctx)
667 return DB_EVENT_OK;667 return DB_EVENT_OK;
668}668}
669669
670/*670static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
671 * DB_CREATE_EVENT_LOG()
672 *
673 * NOTE: This assumes that the db scan lock is already applied
674 * (as it copies data from the record)
675 */
676db_field_log* db_create_event_log (struct evSubscrip *pevent)
677{671{
678 db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);672 db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
679673
680 if (pLog) {674 if (pLog) {
681 struct dbChannel *chan = pevent->chan;
682 struct dbCommon *prec = dbChannelRecord(chan);675 struct dbCommon *prec = dbChannelRecord(chan);
683 pLog->ctx = dbfl_context_event;676 pLog->stat = prec->stat;
684 if (pevent->useValque) {677 pLog->sevr = prec->sevr;
678 pLog->time = prec->time;
679 pLog->field_type = dbChannelFieldType(chan);
680 pLog->field_size = dbChannelFieldSize(chan);
681 pLog->no_elements = dbChannelElements(chan);
682
683 if (use_val) {
685 pLog->type = dbfl_type_val;684 pLog->type = dbfl_type_val;
686 pLog->stat = prec->stat;
687 pLog->sevr = prec->sevr;
688 pLog->time = prec->time;
689 pLog->field_type = dbChannelFieldType(chan);
690 pLog->no_elements = dbChannelElements(chan);
691 /*685 /*
692 * use memcpy to avoid a bus error on686 * use memcpy to avoid a bus error on
693 * union copy of char in the db at an odd687 * union copy of char in the db at an odd
@@ -697,23 +691,46 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent)
697 dbChannelField(chan),691 dbChannelField(chan),
698 dbChannelFieldSize(chan));692 dbChannelFieldSize(chan));
699 } else {693 } else {
700 pLog->type = dbfl_type_rec;694 pLog->type = dbfl_type_ref;
695
696 /* don't make a copy yet, just reference the field value */
697 pLog->u.r.field = dbChannelField(chan);
698 /* indicate field value still owned by record */
699 pLog->u.r.dtor = NULL;
700 /* no private data yet, may be set by a filter */
701 pLog->u.r.pvt = NULL;
701 }702 }
702 }703 }
703 return pLog;704 return pLog;
704}705}
705706
706/*707/*
708 * DB_CREATE_EVENT_LOG()
709 *
710 * NOTE: This assumes that the db scan lock is already applied
711 * (as it calls rset->get_array_info)
712 */
713db_field_log* db_create_event_log (struct evSubscrip *pevent)
714{
715 db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque);
716 if (pLog) {
717 pLog->ctx = dbfl_context_event;
718 }
719 return pLog;
720}
721
722/*
707 * DB_CREATE_READ_LOG()723 * DB_CREATE_READ_LOG()
708 *724 *
709 */725 */
710db_field_log* db_create_read_log (struct dbChannel *chan)726db_field_log* db_create_read_log (struct dbChannel *chan)
711{727{
712 db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);728 db_field_log *pLog = db_create_field_log(chan,
713729 dbChannelElements(chan) == 1 &&
730 dbChannelSpecial(chan) != SPC_DBADDR &&
731 dbChannelFieldSize(chan) <= sizeof(union native_value));
714 if (pLog) {732 if (pLog) {
715 pLog->ctx = dbfl_context_read;733 pLog->ctx = dbfl_context_read;
716 pLog->type = dbfl_type_rec;
717 }734 }
718 return pLog;735 return pLog;
719}736}
@@ -737,20 +754,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
737 LOCKEVQUE (ev_que);754 LOCKEVQUE (ev_que);
738755
739 /*756 /*
740 * if we have an event on the queue and both the last
741 * event on the queue and the current event are emtpy
742 * (i.e. of type dbfl_type_rec), simply ignore duplicate
743 * events (saving empty events serves no purpose)
744 */
745 if (pevent->npend > 0u &&
746 (*pevent->pLastLog)->type == dbfl_type_rec &&
747 pLog->type == dbfl_type_rec) {
748 db_delete_field_log(pLog);
749 UNLOCKEVQUE (ev_que);
750 return;
751 }
752
753 /*
754 * add to task local event que757 * add to task local event que
755 */758 */
756759
diff --git a/modules/database/src/ioc/db/dbExtractArray.c b/modules/database/src/ioc/db/dbExtractArray.c
index e16ab4c..667de24 100644
--- a/modules/database/src/ioc/db/dbExtractArray.c
+++ b/modules/database/src/ioc/db/dbExtractArray.c
@@ -13,11 +13,12 @@
13/*13/*
14 * Author: Ralph Lange <Ralph.Lange@bessy.de>14 * Author: Ralph Lange <Ralph.Lange@bessy.de>
15 *15 *
16 * based on dbConvert.c16 * based on dbConvert.c, see copyNoConvert
17 * written by: Bob Dalesio, Marty Kraimer17 * written by: Bob Dalesio, Marty Kraimer
18 */18 */
1919
20#include <string.h>20#include <string.h>
21#include <assert.h>
2122
22#include "epicsTypes.h"23#include "epicsTypes.h"
2324
@@ -25,61 +26,30 @@
25#include "dbAddr.h"26#include "dbAddr.h"
26#include "dbExtractArray.h"27#include "dbExtractArray.h"
2728
28void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,29void dbExtractArray(const void *pfrom, void *pto, short field_size,
29 long nRequest, long no_elements, long offset, long increment)30 long nRequest, long no_elements, long offset, long increment)
30{31{
31 char *pdst = (char *) pto;32 char *pdst = (char *) pto;
32 char *psrc = (char *) paddr->pfield;33 const char *psrc = (char *) pfrom;
33 long nUpperPart;
34 int i;
35 short srcSize = paddr->field_size;
36 short dstSize = srcSize;
37 char isString = (paddr->field_type == DBF_STRING);
3834
39 if (nRequest > no_elements) nRequest = no_elements;35 /* assert preconditions */
40 if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;36 assert(nRequest >= 0);
37 assert(no_elements >= 0);
38 assert(increment > 0);
39 assert(0 <= offset);
40 assert(offset < no_elements);
4141
42 if (increment == 1 && dstSize == srcSize) {42 if (increment == 1) {
43 nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;43 long nUpperPart =
44 memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);44 nRequest < no_elements - offset ? nRequest : no_elements - offset;
45 memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart);
45 if (nRequest > nUpperPart)46 if (nRequest > nUpperPart)
46 memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));47 memcpy(pdst + (field_size * nUpperPart), psrc,
47 if (isString)48 field_size * (nRequest - nUpperPart));
48 for (i = 1; i <= nRequest; i++)
49 pdst[dstSize*i-1] = '\0';
50 } else {49 } else {
51 for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {50 for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) {
52 offset %= no_elements;51 offset %= no_elements;
53 memcpy(pdst, &psrc[offset*srcSize], dstSize);52 memcpy(pdst, psrc + (offset * field_size), field_size);
54 if (isString) pdst[dstSize-1] = '\0';
55 }
56 }
57}
58
59void dbExtractArrayFromBuf(const void *pfrom, void *pto,
60 short field_size, short field_type,
61 long nRequest, long no_elements, long offset, long increment)
62{
63 char *pdst = (char *) pto;
64 char *psrc = (char *) pfrom;
65 int i;
66 short srcSize = field_size;
67 short dstSize = srcSize;
68 char isString = (field_type == DBF_STRING);
69
70 if (nRequest > no_elements) nRequest = no_elements;
71 if (offset > no_elements - 1) offset = no_elements - 1;
72 if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
73
74 if (increment == 1) {
75 memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
76 if (isString)
77 for (i = 1; i <= nRequest; i++)
78 pdst[dstSize*i] = '\0';
79 } else {
80 for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
81 memcpy(pdst, &psrc[offset*srcSize], dstSize);
82 if (isString) pdst[dstSize] = '\0';
83 }53 }
84 }54 }
85}55}
diff --git a/modules/database/src/ioc/db/dbExtractArray.h b/modules/database/src/ioc/db/dbExtractArray.h
index 7ed3584..374d1d1 100644
--- a/modules/database/src/ioc/db/dbExtractArray.h
+++ b/modules/database/src/ioc/db/dbExtractArray.h
@@ -21,11 +21,36 @@
21extern "C" {21extern "C" {
22#endif22#endif
2323
24epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,24/** @brief Make a copy of parts of an array.
25 long nRequest, long no_elements, long offset, long increment);25 *
26epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,26 * The source array may or may not be a record field.
27 short field_size, short field_type,27 *
28 long nRequest, long no_elements, long offset, long increment);28 * The increment parameter is used to support array filters; it
29 * means: copy only every increment'th element, starting at offset.
30 *
31 * The offset and no_elements parameters are used to support the
32 * circular buffer feature of record fields: elements before offset
33 * are treated as if they came right after no_elements.
34 *
35 * This function does not do any conversion on the array elements.
36 *
37 * Preconditions:
38 * nRequest >= 0, no_elements >= 0, increment > 0
39 * 0 <= offset < no_elements
40 * pto points to a buffer with at least field_size*nRequest bytes
41 * pfrom points to a buffer with exactly field_size*no_elements bytes
42 *
43 * @param pfrom Pointer to source array.
44 * @param pto Pointer to target array.
45 * @param field_size Size of an array element.
46 * @param nRequest Number of elements to copy.
47 * @param no_elements Number of elements in source array.
48 * @param offset Wrap-around point in source array.
49 * @param increment Copy only every increment'th element.
50 */
51epicsShareFunc void dbExtractArray(const void *pfrom, void *pto,
52 short field_size, long nRequest, long no_elements, long offset,
53 long increment);
2954
30#ifdef __cplusplus55#ifdef __cplusplus
31}56}
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)
341 no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);341 no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
342 if (addr.dbr_field_type == DBR_ENUM) {342 if (addr.dbr_field_type == DBR_ENUM) {
343 long status = dbGetField(&addr, DBR_STRING, pbuffer,343 long status = dbGetField(&addr, DBR_STRING, pbuffer,
344 &options, &no_elements, NULL);344 &options, &no_elements);
345345
346 printBuffer(status, DBR_STRING, pbuffer, 0L, 0L,346 printBuffer(status, DBR_STRING, pbuffer, 0L, 0L,
347 no_elements, &msg_Buff, 10);347 no_elements, &msg_Buff, 10);
348 }348 }
349 else {349 else {
350 long status = dbGetField(&addr, addr.dbr_field_type, pbuffer,350 long status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
351 &options, &no_elements, NULL);351 &options, &no_elements);
352352
353 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,353 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
354 no_elements, &msg_Buff, 10);354 no_elements, &msg_Buff, 10);
@@ -487,7 +487,7 @@ long dbtgf(const char *pname)
487 ret_options = req_options;487 ret_options = req_options;
488 no_elements = 0;488 no_elements = 0;
489 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,489 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
490 &ret_options, &no_elements, NULL);490 &ret_options, &no_elements);
491 printBuffer(status, addr.dbr_field_type, pbuffer,491 printBuffer(status, addr.dbr_field_type, pbuffer,
492 req_options, ret_options, no_elements, pMsgBuff, tab_size);492 req_options, ret_options, no_elements, pMsgBuff, tab_size);
493493
@@ -496,62 +496,62 @@ long dbtgf(const char *pname)
496496
497 dbr_type = DBR_STRING;497 dbr_type = DBR_STRING;
498 no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));498 no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
499 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);499 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
500 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);500 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
501501
502 dbr_type = DBR_CHAR;502 dbr_type = DBR_CHAR;
503 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));503 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
504 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);504 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
505 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);505 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
506506
507 dbr_type = DBR_UCHAR;507 dbr_type = DBR_UCHAR;
508 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));508 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
509 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);509 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
510 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);510 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
511511
512 dbr_type = DBR_SHORT;512 dbr_type = DBR_SHORT;
513 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));513 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
514 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);514 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
515 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);515 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
516516
517 dbr_type = DBR_USHORT;517 dbr_type = DBR_USHORT;
518 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));518 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
519 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);519 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
520 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);520 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
521521
522 dbr_type = DBR_LONG;522 dbr_type = DBR_LONG;
523 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));523 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
524 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);524 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
525 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);525 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
526526
527 dbr_type = DBR_ULONG;527 dbr_type = DBR_ULONG;
528 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));528 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
529 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);529 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
530 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);530 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
531531
532 dbr_type = DBR_INT64;532 dbr_type = DBR_INT64;
533 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));533 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
534 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);534 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
535 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);535 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
536536
537 dbr_type = DBR_UINT64;537 dbr_type = DBR_UINT64;
538 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));538 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
539 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);539 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
540 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);540 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
541541
542 dbr_type = DBR_FLOAT;542 dbr_type = DBR_FLOAT;
543 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));543 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
544 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);544 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
545 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);545 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
546546
547 dbr_type = DBR_DOUBLE;547 dbr_type = DBR_DOUBLE;
548 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));548 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
549 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);549 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
550 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);550 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
551551
552 dbr_type = DBR_ENUM;552 dbr_type = DBR_ENUM;
553 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));553 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
554 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);554 status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
555 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);555 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
556556
557 pmsg[0] = '\0';557 pmsg[0] = '\0';
@@ -651,7 +651,7 @@ long dbtpf(const char *pname, const char *pvalue)
651651
652 printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);652 printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
653 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,653 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
654 &options, &no_elements, NULL);654 &options, &no_elements);
655 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,655 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
656 no_elements, pMsgBuff, tab_size);656 no_elements, pMsgBuff, tab_size);
657 }657 }
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)
197 return;197 return;
198 }198 }
199199
200 status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);200 status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq);
201 if (status) {201 if (status) {
202 testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));202 testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
203 return;203 return;
@@ -270,7 +270,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
270 return;270 return;
271 }271 }
272272
273 status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);273 status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest);
274 if (status) {274 if (status) {
275 testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);275 testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
276276
diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
index 1534517..a801142 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 {
56struct db_field_log;56struct db_field_log;
57typedef void (dbfl_freeFunc)(struct db_field_log *pfl);57typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
5858
59/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */59/*
60 * A db_field_log has one of two types:
61 *
62 * dbfl_type_ref - Reference to value
63 * Used for variable size (array) data types. Meta-data
64 * is stored in the field log, but value data is stored externally.
65 * Only the dbfl_ref side of the data union is valid.
66 *
67 * dbfl_type_val - Internal value
68 * Used to store small scalar data. Meta-data and value are
69 * present in this structure and no external references are used.
70 * Only the dbfl_val side of the data union is valid.
71 */
60typedef enum dbfl_type {72typedef enum dbfl_type {
61 dbfl_type_rec = 0,
62 dbfl_type_val,73 dbfl_type_val,
63 dbfl_type_ref74 dbfl_type_ref
64} dbfl_type;75} dbfl_type;
6576
66/* Context of db_field_log: event = subscription update, read = read reply */77/* Context of db_field_log: event = subscription update, read = read reply */
67typedef enum dbfl_context {78typedef enum dbfl_context {
68 dbfl_context_read = 0,79 dbfl_context_read,
69 dbfl_context_event80 dbfl_context_event
70} dbfl_context;81} dbfl_context;
7182
72#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")83#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref")
7384
74struct dbfl_val {85struct dbfl_val {
75 union native_value field; /* Field value */86 union native_value field; /* Field value */
@@ -81,6 +92,8 @@ struct dbfl_val {
81 * db_delete_field_log(). Any code which changes a dbfl_type_ref92 * db_delete_field_log(). Any code which changes a dbfl_type_ref
82 * field log to another type, or to reference different data,93 * field log to another type, or to reference different data,
83 * must explicitly call the dtor function.94 * must explicitly call the dtor function.
95 * If the dtor is NULL, then this means the array data is still owned
96 * by a record.
84 */97 */
85struct dbfl_ref {98struct dbfl_ref {
86 dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */99 dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
@@ -88,8 +101,11 @@ struct dbfl_ref {
88 void *field; /* Field value */101 void *field; /* Field value */
89};102};
90103
104/*
105 * Note that field_size may be larger than MAX_STRING_SIZE.
106 */
91typedef struct db_field_log {107typedef struct db_field_log {
92 unsigned int type:2; /* type (union) selector */108 unsigned int type:1; /* type (union) selector */
93 /* ctx is used for all types */109 /* ctx is used for all types */
94 unsigned int ctx:1; /* context (operation type) */110 unsigned int ctx:1; /* context (operation type) */
95 /* the following are used for value and reference types */111 /* the following are used for value and reference types */
@@ -97,37 +113,14 @@ typedef struct db_field_log {
97 unsigned short stat; /* Alarm Status */113 unsigned short stat; /* Alarm Status */
98 unsigned short sevr; /* Alarm Severity */114 unsigned short sevr; /* Alarm Severity */
99 short field_type; /* DBF type of data */115 short field_type; /* DBF type of data */
100 short field_size; /* Data size */116 short field_size; /* Size of a single element */
101 long no_elements; /* No of array elements */117 long no_elements; /* No of valid array elements */
102 union {118 union {
103 struct dbfl_val v;119 struct dbfl_val v;
104 struct dbfl_ref r;120 struct dbfl_ref r;
105 } u;121 } u;
106} db_field_log;122} db_field_log;
107123
108/*
109 * A db_field_log will in one of three types:
110 *
111 * dbfl_type_rec - Reference to record
112 * The field log stores no data itself. Data must instead be taken
113 * via the dbChannel* which must always be provided when along
114 * with the field log.
115 * For this type only the 'type' and 'ctx' members are used.
116 *
117 * dbfl_type_ref - Reference to outside value
118 * Used for variable size (array) data types. Meta-data
119 * is stored in the field log, but value data is stored externally
120 * (see struct dbfl_ref).
121 * For this type all meta-data members are used. The dbfl_ref side of the
122 * data union is used.
123 *
124 * dbfl_type_val - Internal value
125 * Used to store small scalar data. Meta-data and value are
126 * present in this structure and no external references are used.
127 * For this type all meta-data members are used. The dbfl_val side of the
128 * data union is used.
129 */
130
131#ifdef __cplusplus124#ifdef __cplusplus
132}125}
133#endif126#endif
diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
index f91708a..1e506a2 100644
--- a/modules/database/src/std/filters/arr.c
+++ b/modules/database/src/std/filters/arr.c
@@ -12,16 +12,14 @@
1212
13#include <stdio.h>13#include <stdio.h>
1414
15#include <freeList.h>15#include "chfPlugin.h"
16#include <dbAccess.h>16#include "dbAccessDefs.h"
17#include <dbExtractArray.h>17#include "dbExtractArray.h"
18#include <db_field_log.h>18#include "db_field_log.h"
19#include <dbLock.h>19#include "dbLock.h"
20#include <recSup.h>20#include "epicsExit.h"
21#include <epicsExit.h>21#include "freeList.h"
22#include <special.h>22#include "epicsExport.h"
23#include <chfPlugin.h>
24#include <epicsExport.h>
2523
26typedef struct myStruct {24typedef struct myStruct {
27 epicsInt32 start;25 epicsInt32 start;
@@ -45,6 +43,8 @@ static void * allocPvt(void)
45 myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);43 myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
46 if (!my) return NULL;44 if (!my) return NULL;
4745
46 /* defaults */
47 my->start = 0;
48 my->incr = 1;48 my->incr = 1;
49 my->end = -1;49 my->end = -1;
50 return (void *) my;50 return (void *) my;
@@ -76,8 +76,6 @@ static void freeArray(db_field_log *pfl)
76static long wrapArrayIndices(long *start, const long increment, long *end,76static long wrapArrayIndices(long *start, const long increment, long *end,
77 const long no_elements)77 const long no_elements)
78{78{
79 long len = 0;
80
81 if (*start < 0) *start = no_elements + *start;79 if (*start < 0) *start = no_elements + *start;
82 if (*start < 0) *start = 0;80 if (*start < 0) *start = 0;
83 if (*start > no_elements) *start = no_elements;81 if (*start > no_elements) *start = no_elements;
@@ -86,19 +84,22 @@ static long wrapArrayIndices(long *start, const long increment, long *end,
86 if (*end < 0) *end = 0;84 if (*end < 0) *end = 0;
87 if (*end >= no_elements) *end = no_elements - 1;85 if (*end >= no_elements) *end = no_elements - 1;
8886
89 if (*end - *start >= 0) len = 1 + (*end - *start) / increment;87 if (*end - *start >= 0)
90 return len;88 return 1 + (*end - *start) / increment;
89 else
90 return 0;
91}91}
9292
93static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)93static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
94{94{
95 myStruct *my = (myStruct*) pvt;95 myStruct *my = (myStruct*) pvt;
96 struct dbCommon *prec;96 int must_lock;
97 rset *prset;
98 long start = my->start;97 long start = my->start;
99 long end = my->end;98 long end = my->end;
100 long nTarget = 0;99 long nTarget;
100 void *pTarget;
101 long offset = 0;101 long offset = 0;
102<<<<<<< modules/database/src/std/filters/arr.c
102 long nSource = dbChannelElements(chan);103 long nSource = dbChannelElements(chan);
103 long capacity = nSource;104 long capacity = nSource;
104 void *pdst;105 void *pdst;
@@ -141,30 +142,52 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
141 dbScanUnlock(prec);142 dbScanUnlock(prec);
142 dbChannelField(chan) = pfieldsave;143 dbChannelField(chan) = pfieldsave;
143 }144 }
145=======
146 long nSource = pfl->no_elements;
147 void *pSource = pfl->u.r.field;
148
149 switch (pfl->type) {
150 case dbfl_type_val:
151 /* TODO Treat scalars as arrays with 1 element */
152>>>>>>> modules/database/src/std/filters/arr.c
144 break;153 break;
145154
146 /* Extract from buffer */
147 case dbfl_type_ref:155 case dbfl_type_ref:
148 pdst = NULL;156 must_lock = !pfl->u.r.dtor;
149 nSource = pfl->no_elements;157 if (must_lock) {
150 nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);158 dbScanLock(dbChannelRecord(chan));
151 pfl->no_elements = nTarget;159 dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
152 if (nTarget) {
153 /* Copy the data out */
154 void *psrc = pfl->u.r.field;
155
156 pdst = freeListCalloc(my->arrayFreeList);
157 if (!pdst) break;
158 offset = start;
159 dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type,
160 nTarget, nSource, offset, my->incr);
161 }160 }
162 if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);161 nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
163 if (nTarget) {162 if (nTarget > 0) {
163 /* copy the data */
164 pTarget = freeListCalloc(my->arrayFreeList);
165 if (!pTarget) break;
166 /* must do the wrap-around with the original no_elements */
167 offset = (offset + start) % pfl->no_elements;
168 dbExtractArray(pSource, pTarget, pfl->field_size,
169 nTarget, pfl->no_elements, offset, my->incr);
170 if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
171 pfl->u.r.field = pTarget;
164 pfl->u.r.dtor = freeArray;172 pfl->u.r.dtor = freeArray;
165 pfl->u.r.pvt = my->arrayFreeList;173 pfl->u.r.pvt = my->arrayFreeList;
166 pfl->u.r.field = pdst;
167 }174 }
175 /* Adjust no_elements to refer to the new pTarget.
176 *
177 * Setting pfl->no_elements outside of the "if" clause above is
178 * done to make requests fail if nTarget is zero, that is, if all
179 * elements selected by the filter are outside the array bounds.
180 * TODO:
181 * It would be possible to lift this restriction by interpreting
182 * a request with *no* number of elements (NULL pointer) as scalar
183 * (meaning: fail if we get less than one element); in contrast,
184 * a request that explicitly specifies one element would be
185 * interpreted as an array request, for which zero elements would
186 * be a normal expected result.
187 */
188 pfl->no_elements = nTarget;
189 if (must_lock)
190 dbScanUnlock(dbChannelRecord(chan));
168 break;191 break;
169 }192 }
170 return pfl;193 return pfl;
diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c
index 5925b0b..aed3b41 100644
--- a/modules/database/src/std/filters/ts.c
+++ b/modules/database/src/std/filters/ts.c
@@ -11,21 +11,44 @@
11 */11 */
1212
13#include <stdio.h>13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
1416
15#include <chfPlugin.h>17#include "chfPlugin.h"
16#include <dbLock.h>18#include "db_field_log.h"
17#include <db_field_log.h>19#include "dbExtractArray.h"
18#include <epicsExport.h>20#include "dbLock.h"
21#include "epicsExport.h"
22
23/*
24 * The size of the data is different for each channel, and can even
25 * change at runtime, so a freeList doesn't make much sense here.
26 */
27static void freeArray(db_field_log *pfl) {
28 free(pfl->u.r.field);
29}
1930
20static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {31static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
21 epicsTimeStamp now;32 epicsTimeStamp now;
22 epicsTimeGetCurrent(&now);33 epicsTimeGetCurrent(&now);
2334
24 /* If string or array, must make a copy (to ensure coherence between time and data) */35 /* If reference and not already copied,
25 if (pfl->type == dbfl_type_rec) {36 must make a copy (to ensure coherence between time and data) */
26 dbScanLock(dbChannelRecord(chan));37 if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
27 dbChannelMakeArrayCopy(pvt, pfl, chan);38 void *pTarget = calloc(pfl->no_elements, pfl->field_size);
28 dbScanUnlock(dbChannelRecord(chan));39 void *pSource = pfl->u.r.field;
40 if (pTarget) {
41 long offset = 0;
42 long nSource = pfl->no_elements;
43 dbScanLock(dbChannelRecord(chan));
44 dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
45 dbExtractArray(pSource, pTarget, pfl->field_size,
46 nSource, pfl->no_elements, offset, 1);
47 pfl->u.r.field = pTarget;
48 pfl->u.r.dtor = freeArray;
49 pfl->u.r.pvt = pvt;
50 dbScanUnlock(dbChannelRecord(chan));
51 }
29 }52 }
3053
31 pfl->time = now;54 pfl->time = now;
diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp
index 8255fdc..6ec6ea4 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) {
130 memset(buf, 0, sizeof(buf)); \130 memset(buf, 0, sizeof(buf)); \
131 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \131 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
132 pfl = db_create_read_log(pch); \132 pfl = db_create_read_log(pch); \
133 testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \133 testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
134 testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \134 testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
135 testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \135 testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
136 if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \136 if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
diff --git a/modules/database/test/std/filters/arrTest.cpp b/modules/database/test/std/filters/arrTest.cpp
index 1ec16b3..5db3757 100644
--- a/modules/database/test/std/filters/arrTest.cpp
+++ b/modules/database/test/std/filters/arrTest.cpp
@@ -72,9 +72,9 @@ static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
72 }72 }
73 break;73 break;
74 case DBR_STRING:74 case DBR_STRING:
75 if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {75 if (strtol(&((const char*)pfl1->u.r.field)[i*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) {
76 testDiag("at index=%d: field log has '%s', should be '%d'",76 testDiag("at index=%d: field log has '%s', should be '%d'",
77 i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);77 i, &((const char*)pfl1->u.r.field)[i*pfl1->field_size], ((epicsInt32*)p2)[i]);
78 return 0;78 return 0;
79 }79 }
80 break;80 break;
@@ -119,7 +119,7 @@ static void testHead (const char *title, const char *typ = "") {
119 off = Offset; \119 off = Offset; \
120 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \120 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
121 pfl = db_create_read_log(pch); \121 pfl = db_create_read_log(pch); \
122 testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \122 testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \
123 pfl2 = dbChannelRunPostChain(pch, pfl); \123 pfl2 = dbChannelRunPostChain(pch, pfl); \
124 testOk(pfl2 == pfl, "call does not drop or replace field_log"); \124 testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
125 testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \125 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)
129 dbEventCtx evtctx;129 dbEventCtx evtctx;
130 int logsFree, logsFinal;130 int logsFree, logsFinal;
131131
132 testPlan(77);132 testPlan(72);
133133
134 testdbPrepare();134 testdbPrepare();
135135
@@ -170,12 +170,9 @@ MAIN(dbndTest)
170 "dbnd has one filter with argument in pre chain");170 "dbnd has one filter with argument in pre chain");
171 testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");171 testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
172172
173 /* Field logs of type ref and rec: pass any update */173 /* Field logs of type ref: pass any update */
174
175 testHead("Field logs of type ref and rec");
176 fl1.type = dbfl_type_rec;
177 mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
178174
175 testHead("Field logs of type ref");
179 fl1.type = dbfl_type_ref;176 fl1.type = dbfl_type_ref;
180 mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);177 mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
181178

Subscribers

People subscribed via source and target branches