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

Proposed by Ben Franksen
Status: Superseded
Proposed branch: ~bfrk/epics-base:write-filters-rebased
Merge into: ~epics-core/epics-base/+git/epics-base:7.0
Prerequisite: ~dirk.zimoch/epics-base:dbChannelForDBLinks
Diff against target: 1401 lines (+341/-376)
19 files modified
modules/database/src/ioc/db/dbAccess.c (+35/-29)
modules/database/src/ioc/db/dbAccessDefs.h (+1/-1)
modules/database/src/ioc/db/dbChannel.c (+55/-63)
modules/database/src/ioc/db/dbChannel.h (+10/-5)
modules/database/src/ioc/db/dbDbLink.c (+7/-34)
modules/database/src/ioc/db/dbEvent.c (+53/-33)
modules/database/src/ioc/db/dbExtractArray.c (+20/-49)
modules/database/src/ioc/db/dbExtractArray.h (+16/-5)
modules/database/src/ioc/db/dbLink.c (+8/-8)
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 (+30/-30)
modules/database/src/ioc/dbStatic/link.h (+0/-3)
modules/database/src/std/filters/arr.c (+43/-70)
modules/database/src/std/filters/ts.c (+27/-9)
modules/database/test/ioc/db/dbChArrTest.cpp (+2/-1)
modules/database/test/std/filters/arrTest.cpp (+8/-7)
modules/database/test/std/filters/dbndTest.c (+3/-6)
modules/database/test/std/rec/linkFilterTest.c (+5/-5)
Reviewer Review Type Date Requested Status
EPICS Core Developers Pending
Review via email: mp+381152@code.launchpad.net

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

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

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 :

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 :

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 :

> 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.

df1a6da... by Ben Franksen

move optimization for scalar requests from link to channel

The optimization is now available for all dbChannels, instead of only for DB
links, and also for requests involving filters. The code more clearly points
out where we optimize this special case and under what conditions.

80304ba... by Ben Franksen

allow array requests to succeed even if we return zero elements

This has been mentioned in a few comments in the code. The difficulty here
is that we must fail when a scalar value is requested but want to succeed if
it's an array request. But the type system for database requests
traditionally does not distinguish between scalar and array. The trick to
solve it is to interpret a missing (NULL) request length as a scalar
request, and as an array request only if an explicit request length is
given.

39413f1... by Ben Franksen

rename local variable dtyp to field_type in modules/database/src/ioc/db/dbLink.c

Unmerged commits

39413f1... by Ben Franksen

rename local variable dtyp to field_type in modules/database/src/ioc/db/dbLink.c

80304ba... by Ben Franksen

allow array requests to succeed even if we return zero elements

This has been mentioned in a few comments in the code. The difficulty here
is that we must fail when a scalar value is requested but want to succeed if
it's an array request. But the type system for database requests
traditionally does not distinguish between scalar and array. The trick to
solve it is to interpret a missing (NULL) request length as a scalar
request, and as an array request only if an explicit request length is
given.

df1a6da... by Ben Franksen

move optimization for scalar requests from link to channel

The optimization is now available for all dbChannels, instead of only for DB
links, and also for requests involving filters. The code more clearly points
out where we optimize this special case and under what conditions.

041a130... 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. This required adding
db_field_log.offset as a new member. It gets initialized, together with the
no_elements member, by a call to the rset.get_array_info, so we can continue
to support the wrap-around/ring-buffer feature for array fields.

It turned out that dbGetField was always called with a NULL pointer as its
db_field_log* argument, which is why I removed this parameter. This makes
sense because the extra db_field_log parameter of dbGet is an internal
implementation detail that should not affect 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 (the "PV$" notation).

e2d9953... by Ben Franksen

Merge branch 'dbChannelForDBLinks' into write-filters

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
2index 9b149dd..c159826 100644
3--- a/modules/database/src/ioc/db/dbAccess.c
4+++ b/modules/database/src/ioc/db/dbAccess.c
5@@ -338,7 +338,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
6 dbCommon *pcommon;
7 char *pbuffer = *poriginal;
8
9- if (!pfl || pfl->type == dbfl_type_rec)
10+ if (!pfl)
11 field_type = paddr->field_type;
12 else
13 field_type = pfl->field_type;
14@@ -348,7 +348,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
15 if( (*options) & DBR_STATUS ) {
16 unsigned short *pushort = (unsigned short *)pbuffer;
17
18- if (!pfl || pfl->type == dbfl_type_rec) {
19+ if (!pfl) {
20 *pushort++ = pcommon->stat;
21 *pushort++ = pcommon->sevr;
22 } else {
23@@ -382,7 +382,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options,
24 if( (*options) & DBR_TIME ) {
25 epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
26
27- if (!pfl || pfl->type == dbfl_type_rec) {
28+ if (!pfl) {
29 *ptime++ = pcommon->time.secPastEpoch;
30 *ptime++ = pcommon->time.nsec;
31 } else {
32@@ -867,14 +867,14 @@ static long getAttrValue(DBADDR *paddr, short dbrType,
33 return 0;
34 }
35
36-long dbGetField(DBADDR *paddr,short dbrType,
37- void *pbuffer, long *options, long *nRequest, void *pflin)
38+long dbGetField(DBADDR *paddr, short dbrType,
39+ void *pbuffer, long *options, long *nRequest)
40 {
41 dbCommon *precord = paddr->precord;
42 long status = 0;
43
44 dbScanLock(precord);
45- status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin);
46+ status = dbGet(paddr, dbrType, pbuffer, options, nRequest, NULL);
47 dbScanUnlock(precord);
48 return status;
49 }
50@@ -887,15 +887,22 @@ long dbGet(DBADDR *paddr, short dbrType,
51 db_field_log *pfl = (db_field_log *)pflin;
52 short field_type;
53 long capacity, no_elements, offset;
54+ int is_scalar_request = !nRequest;
55+ long n_scalar_request = 1;
56 rset *prset;
57 long status = 0;
58
59 if (options && *options)
60 getOptions(paddr, &pbuf, options, pflin);
61- if (nRequest && *nRequest == 0)
62+
63+ if (is_scalar_request)
64+ nRequest = &n_scalar_request;
65+ else if (*nRequest == 0)
66+ /* trivially succeed for zero length array request */
67 return 0;
68+ assert(nRequest && *nRequest > 0);
69
70- if (!pfl || pfl->type == dbfl_type_rec) {
71+ if (!pfl) {
72 field_type = paddr->field_type;
73 no_elements = capacity = paddr->no_elements;
74
75@@ -911,7 +918,8 @@ long dbGet(DBADDR *paddr, short dbrType,
76 } else {
77 field_type = pfl->field_type;
78 no_elements = capacity = pfl->no_elements;
79- offset = 0;
80+ offset = pfl->offset;
81+ /* we already called get_array_info when pfl was created */
82 }
83
84 if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) {
85@@ -934,22 +942,23 @@ long dbGet(DBADDR *paddr, short dbrType,
86 goto done;
87 }
88
89- if (offset == 0 && (!nRequest || no_elements == 1)) {
90- if (nRequest)
91- *nRequest = 1;
92- if (!pfl || pfl->type == dbfl_type_rec) {
93+ /* A scalar request that cannot be met is an error. */
94+ if (is_scalar_request && no_elements < 1) {
95+ status = S_db_badField;
96+ goto done;
97+ }
98+
99+ if (offset == 0 && (is_scalar_request || no_elements == 1)) {
100+ *nRequest = 1;
101+ if (!pfl) {
102 status = dbFastGetConvertRoutine[field_type][dbrType]
103 (paddr->pfield, pbuf, paddr);
104 } else {
105 DBADDR localAddr = *paddr; /* Structure copy */
106
107- if (pfl->no_elements < 1) {
108- status = S_db_badField;
109- goto done;
110- }
111-
112 localAddr.field_type = pfl->field_type;
113 localAddr.field_size = pfl->field_size;
114+ /* not used by dbFastConvert: */
115 localAddr.no_elements = pfl->no_elements;
116 if (pfl->type == dbfl_type_val)
117 localAddr.pfield = (char *) &pfl->u.v.field;
118@@ -962,13 +971,9 @@ long dbGet(DBADDR *paddr, short dbrType,
119 long n;
120 GETCONVERTFUNC convert;
121
122- if (nRequest) {
123- if (no_elements < *nRequest)
124- *nRequest = no_elements;
125- n = *nRequest;
126- } else {
127- n = 1;
128- }
129+ if (no_elements < *nRequest)
130+ *nRequest = no_elements;
131+ n = *nRequest;
132 convert = dbGetConvertRoutine[field_type][dbrType];
133 if (!convert) {
134 char message[80];
135@@ -981,14 +986,15 @@ long dbGet(DBADDR *paddr, short dbrType,
136 }
137 /* convert data into the caller's buffer */
138 if (n <= 0) {
139- ;/*do nothing*/
140- } else if (!pfl || pfl->type == dbfl_type_rec) {
141+ ; /*do nothing */
142+ } else if (!pfl) {
143 status = convert(paddr, pbuf, n, capacity, offset);
144 } else {
145 DBADDR localAddr = *paddr; /* Structure copy */
146
147 localAddr.field_type = pfl->field_type;
148 localAddr.field_size = pfl->field_size;
149+ /* not used by dbConvert, it uses the passed capacity instead: */
150 localAddr.no_elements = pfl->no_elements;
151 if (pfl->type == dbfl_type_val)
152 localAddr.pfield = (char *) &pfl->u.v.field;
153@@ -997,10 +1003,10 @@ long dbGet(DBADDR *paddr, short dbrType,
154 status = convert(&localAddr, pbuf, n, capacity, offset);
155 }
156
157- if(!status && dbrType==DBF_CHAR && nRequest &&
158+ if(!status && dbrType==DBF_CHAR && !is_scalar_request &&
159 paddr->pfldDes && paddr->pfldDes->field_type==DBF_STRING)
160 {
161- /* long string ensure nil and truncate to actual length */
162+ /* long string: ensure termination and truncate to actual length */
163 long nReq = *nRequest;
164 pbuf[nReq-1] = '\0';
165 *nRequest = strlen(pbuf)+1;
166diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
167index 805dfd4..4a95989 100644
168--- a/modules/database/src/ioc/db/dbAccessDefs.h
169+++ b/modules/database/src/ioc/db/dbAccessDefs.h
170@@ -243,7 +243,7 @@ epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
171 epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset);
172 epicsShareFunc long dbGetField(
173 struct dbAddr *,short dbrType,void *pbuffer,long *options,
174- long *nRequest,void *pfl);
175+ long *nRequest);
176 epicsShareFunc long dbGet(
177 struct dbAddr *,short dbrType,void *pbuffer,long *options,
178 long *nRequest,void *pfl);
179diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
180index a8bfbf1..e22ea2f 100644
181--- a/modules/database/src/ioc/db/dbChannel.c
182+++ b/modules/database/src/ioc/db/dbChannel.c
183@@ -31,6 +31,7 @@
184 #include "dbBase.h"
185 #include "dbChannel.h"
186 #include "dbCommon.h"
187+#include "dbConvertFast.h"
188 #include "dbEvent.h"
189 #include "dbLock.h"
190 #include "dbStaticLib.h"
191@@ -49,14 +50,12 @@ typedef struct parseContext {
192
193 static void *dbChannelFreeList;
194 static void *chFilterFreeList;
195-static void *dbchStringFreeList;
196
197 void dbChannelExit(void)
198 {
199 freeListCleanup(dbChannelFreeList);
200 freeListCleanup(chFilterFreeList);
201- freeListCleanup(dbchStringFreeList);
202- dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
203+ dbChannelFreeList = chFilterFreeList = NULL;
204 }
205
206 void dbChannelInit (void)
207@@ -66,7 +65,6 @@ void dbChannelInit (void)
208
209 freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
210 freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
211- freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
212 db_init_event_freelists();
213 }
214
215@@ -446,28 +444,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
216 return status;
217 }
218
219-/* Stolen from dbAccess.c: */
220-static short mapDBFToDBR[DBF_NTYPES] =
221- {
222- /* DBF_STRING => */DBR_STRING,
223- /* DBF_CHAR => */DBR_CHAR,
224- /* DBF_UCHAR => */DBR_UCHAR,
225- /* DBF_SHORT => */DBR_SHORT,
226- /* DBF_USHORT => */DBR_USHORT,
227- /* DBF_LONG => */DBR_LONG,
228- /* DBF_ULONG => */DBR_ULONG,
229- /* DBF_INT64 => */DBR_INT64,
230- /* DBF_UINT64 => */DBR_UINT64,
231- /* DBF_FLOAT => */DBR_FLOAT,
232- /* DBF_DOUBLE => */DBR_DOUBLE,
233- /* DBF_ENUM, => */DBR_ENUM,
234- /* DBF_MENU, => */DBR_ENUM,
235- /* DBF_DEVICE => */DBR_ENUM,
236- /* DBF_INLINK => */DBR_STRING,
237- /* DBF_OUTLINK => */DBR_STRING,
238- /* DBF_FWDLINK => */DBR_STRING,
239- /* DBF_NOACCESS => */DBR_NOACCESS };
240-
241 dbChannel * dbChannelCreate(const char *name)
242 {
243 const char *pname = name;
244@@ -648,14 +624,63 @@ long dbChannelOpen(dbChannel *chan)
245 }
246
247 /* Only use dbChannelGet() if the record is already locked. */
248-long dbChannelGet(dbChannel *chan, short type, void *pbuffer,
249- long *options, long *nRequest, void *pfl)
250+long dbChannelGet(dbChannel *chan, short dbrType, void *pbuffer,
251+ long *options, long *nRequest, db_field_log *pfl)
252 {
253- return dbGet(&chan->addr, type, pbuffer, options, nRequest, pfl);
254+ dbAddr addr = chan->addr; /* structure copy */
255+
256+ if (pfl) {
257+ addr.field_size = pfl->field_size;
258+ addr.field_type = pfl->field_type;
259+ addr.no_elements = pfl->no_elements;
260+ switch ((enum dbfl_type)pfl->type) {
261+ case dbfl_type_val: addr.pfield = &pfl->u.v.field; break;
262+ case dbfl_type_ref: addr.pfield = pfl->u.r.field; break;
263+ }
264+ }
265+
266+ /*
267+ * Note: if pfl is not NULL then we have already called get_array_info, and
268+ * therefore dbGet doesn't. This means we already know the final number of
269+ * elements and can therefore apply the special scalar optimization even if
270+ * we have addr.special == SPC_DBADDR.
271+ */
272+ if (options
273+ || addr.no_elements > 1
274+ || (nRequest && *nRequest > 1)
275+ || (!pfl && addr.special == SPC_DBADDR)
276+ || addr.special == SPC_ATTRIBUTE)
277+ {
278+ chan->getCvt = NULL;
279+ return dbGet(&addr, dbrType, pbuffer, options, nRequest, pfl);
280+ }
281+
282+ /* If this is a scalar request, fail if it cannot be met */
283+ if (!nRequest && addr.no_elements < 1)
284+ return S_db_badField;
285+
286+ /*
287+ * Optimization for scalar requests with no options,
288+ * no special attributes, and where the channel has only one element.
289+ */
290+ if (chan->getCvt && chan->lastGetdbrType == dbrType) {
291+ return chan->getCvt(addr.pfield, pbuffer, &addr);
292+ } else {
293+ unsigned short dbfType = addr.field_type;
294+
295+ if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
296+ return S_db_badDbrtype;
297+
298+ chan->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
299+ chan->lastGetdbrType = dbrType;
300+ if (nRequest)
301+ *nRequest = addr.no_elements;
302+ return chan->getCvt(addr.pfield, pbuffer, &addr);
303+ }
304 }
305
306 long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer,
307- long *options, long *nRequest, void *pfl)
308+ long *options, long *nRequest, db_field_log *pfl)
309 {
310 dbCommon *precord = chan->addr.precord;
311 long status = 0;
312@@ -736,39 +761,6 @@ void dbChannelDelete(dbChannel *chan)
313 freeListFree(dbChannelFreeList, chan);
314 }
315
316-static void freeArray(db_field_log *pfl) {
317- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
318- freeListFree(dbchStringFreeList, pfl->u.r.field);
319- } else {
320- free(pfl->u.r.field);
321- }
322-}
323-
324-void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
325-{
326- void *p;
327- struct dbCommon *prec = dbChannelRecord(chan);
328-
329- if (pfl->type != dbfl_type_rec) return;
330-
331- pfl->type = dbfl_type_ref;
332- pfl->stat = prec->stat;
333- pfl->sevr = prec->sevr;
334- pfl->time = prec->time;
335- pfl->field_type = chan->addr.field_type;
336- pfl->no_elements = chan->addr.no_elements;
337- pfl->field_size = chan->addr.field_size;
338- pfl->u.r.dtor = freeArray;
339- pfl->u.r.pvt = pvt;
340- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
341- p = freeListCalloc(dbchStringFreeList);
342- } else {
343- p = calloc(pfl->no_elements, pfl->field_size);
344- }
345- if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
346- pfl->u.r.field = p;
347-}
348-
349 /* FIXME: Do these belong in a different file? */
350
351 void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser)
352diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
353index fab9c66..48ec10b 100644
354--- a/modules/database/src/ioc/db/dbChannel.h
355+++ b/modules/database/src/ioc/db/dbChannel.h
356@@ -49,6 +49,8 @@ typedef struct evSubscrip {
357
358 typedef struct chFilter chFilter;
359
360+typedef long fastConvert(void *from, void *to, const dbAddr *paddr);
361+
362 /* A dbChannel points to a record field, and can have multiple filters */
363 typedef struct dbChannel {
364 const char *name;
365@@ -59,13 +61,17 @@ typedef struct dbChannel {
366 ELLLIST filters; /* list of filters as created from JSON */
367 ELLLIST pre_chain; /* list of filters to be called pre-event-queue */
368 ELLLIST post_chain; /* list of filters to be called post-event-queue */
369+ /* Support for optimizing scalar requests */
370+ fastConvert *getCvt; /* fast get conversion function */
371+ short lastGetdbrType; /* last dbChannelGet dbrType */
372 } dbChannel;
373
374 /* Prototype for the channel event function that is called in filter stacks
375 *
376 * When invoked the scan lock for the record associated with 'chan' _may_ be locked.
377- * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying
378- * data out of the associated record.
379+ * If pLog->type==dbfl_type_ref and pLog->u.r.dtor is NULL, then dbScanLock() must
380+ * be called before accessing the data, as this indicates the data is owned by the
381+ * record.
382 *
383 * This function has ownership of the field log pLog, if it wishes to discard
384 * this update it should free the field log with db_delete_field_log() and
385@@ -207,9 +213,9 @@ epicsShareExtern unsigned short dbDBRnewToDBRold[];
386
387
388 epicsShareFunc long dbChannelGet(dbChannel *chan, short type,
389- void *pbuffer, long *options, long *nRequest, void *pfl);
390+ void *pbuffer, long *options, long *nRequest, db_field_log *pfl);
391 epicsShareFunc long dbChannelGetField(dbChannel *chan, short type,
392- void *pbuffer, long *options, long *nRequest, void *pfl);
393+ void *pbuffer, long *options, long *nRequest, db_field_log *pfl);
394 epicsShareFunc long dbChannelPut(dbChannel *chan, short type,
395 const void *pbuffer, long nRequest);
396 epicsShareFunc long dbChannelPutField(dbChannel *chan, short type,
397@@ -224,7 +230,6 @@ epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, voi
398 epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
399 epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
400 epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
401-epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
402
403 #ifdef __cplusplus
404 }
405diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
406index 466552d..21dea0c 100644
407--- a/modules/database/src/ioc/db/dbDbLink.c
408+++ b/modules/database/src/ioc/db/dbDbLink.c
409@@ -57,7 +57,6 @@
410 #include "dbBase.h"
411 #include "dbBkpt.h"
412 #include "dbCommonPvt.h"
413-#include "dbConvertFast.h"
414 #include "dbConvert.h"
415 #include "db_field_log.h"
416 #include "db_access_routines.h"
417@@ -134,9 +133,7 @@ static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
418 /* locker is NULL when an isolated IOC is closing its links */
419 if (locker) {
420 plink->value.pv_link.pvt = 0;
421- plink->value.pv_link.getCvt = 0;
422 plink->value.pv_link.pvlMask = 0;
423- plink->value.pv_link.lastGetdbrType = 0;
424 ellDelete(&precord->bklnk, &plink->value.pv_link.backlinknode);
425 dbLockSetSplit(locker, plink->precord, precord);
426 }
427@@ -166,9 +163,9 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
428 {
429 struct pv_link *ppv_link = &plink->value.pv_link;
430 dbChannel *chan = linkChannel(plink);
431- DBADDR *paddr = &chan->addr;
432 dbCommon *precord = plink->precord;
433 long status;
434+ db_field_log *pfl = NULL;
435
436 /* scan passive records if link is process passive */
437 if (ppv_link->pvlMask & pvlOptPP) {
438@@ -179,43 +176,19 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
439
440 /* If filters are involved in a read, create field log and run filters */
441 if (ellCount(&chan->filters)) {
442- db_field_log *pfl;
443-
444- /* For the moment, empty arrays are not supported by EPICS */
445- if (dbChannelFinalElements(chan) <= 0) /* empty array request */
446- return S_db_badField;
447 pfl = db_create_read_log(chan);
448 if (!pfl)
449 return S_db_noMemory;
450 pfl = dbChannelRunPreChain(chan, pfl);
451 pfl = dbChannelRunPostChain(chan, pfl);
452- status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl);
453- db_delete_field_log(pfl);
454- if (status)
455- return status;
456- if (pnRequest && *pnRequest <= 0) /* empty array result */
457- return S_db_badField;
458- } else if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
459- status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr);
460- } else {
461- unsigned short dbfType = dbChannelFinalFieldType(chan);
462-
463- if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
464- return S_db_badDbrtype;
465-
466- if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1)
467- && dbChannelSpecial(chan) != SPC_DBADDR
468- && dbChannelSpecial(chan) != SPC_ATTRIBUTE) {
469- ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
470- status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr);
471- } else {
472- ppv_link->getCvt = NULL;
473- status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
474- }
475- ppv_link->lastGetdbrType = dbrType;
476 }
477+ status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl);
478+ if (pfl)
479+ db_delete_field_log(pfl);
480+ if (status)
481+ return status;
482
483- if (!status && precord != dbChannelRecord(chan))
484+ if (precord != dbChannelRecord(chan))
485 recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
486 plink->precord,
487 dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr);
488diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
489index ed73529..532c2ba 100644
490--- a/modules/database/src/ioc/db/dbEvent.c
491+++ b/modules/database/src/ioc/db/dbEvent.c
492@@ -667,27 +667,22 @@ int db_post_extra_labor (dbEventCtx ctx)
493 return DB_EVENT_OK;
494 }
495
496-/*
497- * DB_CREATE_EVENT_LOG()
498- *
499- * NOTE: This assumes that the db scan lock is already applied
500- * (as it copies data from the record)
501- */
502-db_field_log* db_create_event_log (struct evSubscrip *pevent)
503+static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
504 {
505 db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
506
507 if (pLog) {
508- struct dbChannel *chan = pevent->chan;
509 struct dbCommon *prec = dbChannelRecord(chan);
510- pLog->ctx = dbfl_context_event;
511- if (pevent->useValque) {
512+ pLog->stat = prec->stat;
513+ pLog->sevr = prec->sevr;
514+ pLog->time = prec->time;
515+ pLog->field_type = dbChannelFieldType(chan);
516+ pLog->field_size = dbChannelFieldSize(chan);
517+ pLog->no_elements = dbChannelElements(chan);
518+ pLog->offset = 0;
519+
520+ if (use_val) {
521 pLog->type = dbfl_type_val;
522- pLog->stat = prec->stat;
523- pLog->sevr = prec->sevr;
524- pLog->time = prec->time;
525- pLog->field_type = dbChannelFieldType(chan);
526- pLog->no_elements = dbChannelElements(chan);
527 /*
528 * use memcpy to avoid a bus error on
529 * union copy of char in the db at an odd
530@@ -697,23 +692,62 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent)
531 dbChannelField(chan),
532 dbChannelFieldSize(chan));
533 } else {
534- pLog->type = dbfl_type_rec;
535+ rset *prset;
536+
537+ pLog->type = dbfl_type_ref;
538+
539+ if (dbChannelSpecial(chan) == SPC_DBADDR &&
540+ (prset = dbGetRset(&chan->addr)) &&
541+ prset->get_array_info)
542+ /* This condition implies !use_val, see db_add_event */
543+ {
544+ void *pfieldsave = dbChannelField(chan);
545+ prec = dbChannelRecord(chan);
546+ prset->get_array_info(&chan->addr, &pLog->no_elements, &pLog->offset);
547+ /* don't make a copy yet, just reference the field value */
548+ pLog->u.r.field = dbChannelField(chan);
549+ dbChannelField(chan) = pfieldsave;
550+ }
551+ else {
552+ /* don't make a copy yet, just reference the field value */
553+ pLog->u.r.field = dbChannelField(chan);
554+ }
555+ /* indicate field value still owned by record */
556+ pLog->u.r.dtor = NULL;
557+ /* no private data yet, may be set by a filter */
558+ pLog->u.r.pvt = NULL;
559 }
560 }
561 return pLog;
562 }
563
564 /*
565+ * DB_CREATE_EVENT_LOG()
566+ *
567+ * NOTE: This assumes that the db scan lock is already applied
568+ * (as it calls rset->get_array_info)
569+ */
570+db_field_log* db_create_event_log (struct evSubscrip *pevent)
571+{
572+ db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque);
573+ if (pLog) {
574+ pLog->ctx = dbfl_context_event;
575+ }
576+ return pLog;
577+}
578+
579+/*
580 * DB_CREATE_READ_LOG()
581 *
582 */
583 db_field_log* db_create_read_log (struct dbChannel *chan)
584 {
585- db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
586-
587+ db_field_log *pLog = db_create_field_log(chan,
588+ dbChannelElements(chan) == 1 &&
589+ dbChannelSpecial(chan) != SPC_DBADDR &&
590+ dbChannelFieldSize(chan) <= sizeof(union native_value));
591 if (pLog) {
592 pLog->ctx = dbfl_context_read;
593- pLog->type = dbfl_type_rec;
594 }
595 return pLog;
596 }
597@@ -737,20 +771,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
598 LOCKEVQUE (ev_que);
599
600 /*
601- * if we have an event on the queue and both the last
602- * event on the queue and the current event are emtpy
603- * (i.e. of type dbfl_type_rec), simply ignore duplicate
604- * events (saving empty events serves no purpose)
605- */
606- if (pevent->npend > 0u &&
607- (*pevent->pLastLog)->type == dbfl_type_rec &&
608- pLog->type == dbfl_type_rec) {
609- db_delete_field_log(pLog);
610- UNLOCKEVQUE (ev_que);
611- return;
612- }
613-
614- /*
615 * add to task local event que
616 */
617
618diff --git a/modules/database/src/ioc/db/dbExtractArray.c b/modules/database/src/ioc/db/dbExtractArray.c
619index e16ab4c..f0ab281 100644
620--- a/modules/database/src/ioc/db/dbExtractArray.c
621+++ b/modules/database/src/ioc/db/dbExtractArray.c
622@@ -13,11 +13,12 @@
623 /*
624 * Author: Ralph Lange <Ralph.Lange@bessy.de>
625 *
626- * based on dbConvert.c
627+ * based on dbConvert.c, see copyNoConvert
628 * written by: Bob Dalesio, Marty Kraimer
629 */
630
631 #include <string.h>
632+#include <assert.h>
633
634 #include "epicsTypes.h"
635
636@@ -25,61 +26,31 @@
637 #include "dbAddr.h"
638 #include "dbExtractArray.h"
639
640-void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
641- long nRequest, long no_elements, long offset, long increment)
642+void dbExtractArray(const void *pfrom, void *pto,
643+ short field_size, short field_type,
644+ long nRequest, long no_elements, long offset, long increment)
645 {
646 char *pdst = (char *) pto;
647- char *psrc = (char *) paddr->pfield;
648- long nUpperPart;
649- int i;
650- short srcSize = paddr->field_size;
651- short dstSize = srcSize;
652- char isString = (paddr->field_type == DBF_STRING);
653+ const char *psrc = (char *) pfrom;
654
655- if (nRequest > no_elements) nRequest = no_elements;
656- if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
657+ /* assert preconditions */
658+ assert(nRequest >= 0);
659+ assert(no_elements >= 0);
660+ assert(increment > 0);
661+ assert(0 <= offset);
662+ assert(offset < no_elements);
663
664- if (increment == 1 && dstSize == srcSize) {
665- nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
666- memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
667+ if (increment == 1) {
668+ long nUpperPart =
669+ nRequest < no_elements - offset ? nRequest : no_elements - offset;
670+ memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart);
671 if (nRequest > nUpperPart)
672- memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
673- if (isString)
674- for (i = 1; i <= nRequest; i++)
675- pdst[dstSize*i-1] = '\0';
676+ memcpy(pdst + (field_size * nUpperPart), psrc,
677+ field_size * (nRequest - nUpperPart));
678 } else {
679- for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
680+ for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) {
681 offset %= no_elements;
682- memcpy(pdst, &psrc[offset*srcSize], dstSize);
683- if (isString) pdst[dstSize-1] = '\0';
684- }
685- }
686-}
687-
688-void dbExtractArrayFromBuf(const void *pfrom, void *pto,
689- short field_size, short field_type,
690- long nRequest, long no_elements, long offset, long increment)
691-{
692- char *pdst = (char *) pto;
693- char *psrc = (char *) pfrom;
694- int i;
695- short srcSize = field_size;
696- short dstSize = srcSize;
697- char isString = (field_type == DBF_STRING);
698-
699- if (nRequest > no_elements) nRequest = no_elements;
700- if (offset > no_elements - 1) offset = no_elements - 1;
701- if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
702-
703- if (increment == 1) {
704- memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
705- if (isString)
706- for (i = 1; i <= nRequest; i++)
707- pdst[dstSize*i] = '\0';
708- } else {
709- for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
710- memcpy(pdst, &psrc[offset*srcSize], dstSize);
711- if (isString) pdst[dstSize] = '\0';
712+ memcpy(pdst, psrc + (offset * field_size), field_size);
713 }
714 }
715 }
716diff --git a/modules/database/src/ioc/db/dbExtractArray.h b/modules/database/src/ioc/db/dbExtractArray.h
717index 7ed3584..9680660 100644
718--- a/modules/database/src/ioc/db/dbExtractArray.h
719+++ b/modules/database/src/ioc/db/dbExtractArray.h
720@@ -21,11 +21,22 @@
721 extern "C" {
722 #endif
723
724-epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
725- long nRequest, long no_elements, long offset, long increment);
726-epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
727- short field_size, short field_type,
728- long nRequest, long no_elements, long offset, long increment);
729+/*
730+ * This function does not do any conversion. This means we don't have to
731+ * truncate the field_size to MAX_STRING_SIZE or add extra null terminators
732+ * for string values. All of this is done by the dbConvert routines which
733+ * will be called whether or not a filter is active.
734+ *
735+ * Checked preconditions:
736+ * - nRequest >= 0, no_elements >= 0, increment > 0
737+ * - 0 <= offset < no_elements
738+ * Unchecked preconditions:
739+ * - pto points to a buffer with at least field_size*nRequest bytes
740+ * - pfrom points to a buffer with at least field_size*no_elements bytes
741+ */
742+epicsShareFunc void dbExtractArray(const void *pfrom, void *pto,
743+ short field_size, short field_type,
744+ long nRequest, long no_elements, long offset, long increment);
745
746 #ifdef __cplusplus
747 }
748diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c
749index aa72dc8..7b4f2ab 100644
750--- a/modules/database/src/ioc/db/dbLink.c
751+++ b/modules/database/src/ioc/db/dbLink.c
752@@ -475,15 +475,15 @@ long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn,
753 long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
754 epicsUInt32 *plen)
755 {
756- int dtyp = dbGetLinkDBFtype(plink);
757+ int field_type = dbGetLinkDBFtype(plink);
758 long len = size;
759 long status;
760
761- if (dtyp < 0) /* Not connected */
762+ if (field_type < 0) /* Not connected */
763 return 0;
764
765- if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR) {
766- status = dbGetLink(plink, dtyp, pbuffer, 0, &len);
767+ if (field_type == DBR_CHAR || field_type == DBF_UCHAR) {
768+ status = dbGetLink(plink, field_type, pbuffer, 0, &len);
769 }
770 else if (size >= MAX_STRING_SIZE)
771 status = dbGetLink(plink, DBR_STRING, pbuffer, 0, 0);
772@@ -504,13 +504,13 @@ long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
773
774 long dbPutLinkLS(struct link *plink, char *pbuffer, epicsUInt32 len)
775 {
776- int dtyp = dbGetLinkDBFtype(plink);
777+ int field_type = dbGetLinkDBFtype(plink);
778
779- if (dtyp < 0)
780+ if (field_type < 0)
781 return 0; /* Not connected */
782
783- if (dtyp == DBR_CHAR || dtyp == DBF_UCHAR)
784- return dbPutLink(plink, dtyp, pbuffer, len);
785+ if (field_type == DBR_CHAR || field_type == DBF_UCHAR)
786+ return dbPutLink(plink, field_type, pbuffer, len);
787
788 return dbPutLink(plink, DBR_STRING, pbuffer, 1);
789 }
790diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
791index 1193954..206d6c9 100644
792--- a/modules/database/src/ioc/db/dbTest.c
793+++ b/modules/database/src/ioc/db/dbTest.c
794@@ -341,14 +341,14 @@ long dbgf(const char *pname)
795 no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
796 if (addr.dbr_field_type == DBR_ENUM) {
797 long status = dbGetField(&addr, DBR_STRING, pbuffer,
798- &options, &no_elements, NULL);
799+ &options, &no_elements);
800
801 printBuffer(status, DBR_STRING, pbuffer, 0L, 0L,
802 no_elements, &msg_Buff, 10);
803 }
804 else {
805 long status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
806- &options, &no_elements, NULL);
807+ &options, &no_elements);
808
809 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
810 no_elements, &msg_Buff, 10);
811@@ -487,7 +487,7 @@ long dbtgf(const char *pname)
812 ret_options = req_options;
813 no_elements = 0;
814 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
815- &ret_options, &no_elements, NULL);
816+ &ret_options, &no_elements);
817 printBuffer(status, addr.dbr_field_type, pbuffer,
818 req_options, ret_options, no_elements, pMsgBuff, tab_size);
819
820@@ -496,62 +496,62 @@ long dbtgf(const char *pname)
821
822 dbr_type = DBR_STRING;
823 no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
824- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
825+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
826 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
827
828 dbr_type = DBR_CHAR;
829 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
830- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
831+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
832 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
833
834 dbr_type = DBR_UCHAR;
835 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
836- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
837+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
838 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
839
840 dbr_type = DBR_SHORT;
841 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
842- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
843+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
844 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
845
846 dbr_type = DBR_USHORT;
847 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
848- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
849+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
850 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
851
852 dbr_type = DBR_LONG;
853 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
854- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
855+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
856 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
857
858 dbr_type = DBR_ULONG;
859 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
860- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
861+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
862 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
863
864 dbr_type = DBR_INT64;
865 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
866- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
867+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
868 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
869
870 dbr_type = DBR_UINT64;
871 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
872- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
873+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
874 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
875
876 dbr_type = DBR_FLOAT;
877 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
878- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
879+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
880 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
881
882 dbr_type = DBR_DOUBLE;
883 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
884- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
885+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
886 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
887
888 dbr_type = DBR_ENUM;
889 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
890- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
891+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
892 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
893
894 pmsg[0] = '\0';
895@@ -651,7 +651,7 @@ long dbtpf(const char *pname, const char *pvalue)
896
897 printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
898 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
899- &options, &no_elements, NULL);
900+ &options, &no_elements);
901 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
902 no_elements, pMsgBuff, tab_size);
903 }
904diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c
905index 6846ef5..458a28b 100644
906--- a/modules/database/src/ioc/db/dbUnitTest.c
907+++ b/modules/database/src/ioc/db/dbUnitTest.c
908@@ -197,7 +197,7 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
909 return;
910 }
911
912- status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
913+ status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq);
914 if (status) {
915 testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
916 return;
917@@ -270,7 +270,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
918 return;
919 }
920
921- status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);
922+ status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest);
923 if (status) {
924 testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
925
926diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
927index 1534517..4b3b82d 100644
928--- a/modules/database/src/ioc/db/db_field_log.h
929+++ b/modules/database/src/ioc/db/db_field_log.h
930@@ -56,20 +56,31 @@ union native_value {
931 struct db_field_log;
932 typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
933
934-/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */
935+/*
936+ * A db_field_log has one of two types:
937+ *
938+ * dbfl_type_ref - Reference to value
939+ * Used for variable size (array) data types. Meta-data
940+ * is stored in the field log, but value data is stored externally.
941+ * Only the dbfl_ref side of the data union is valid.
942+ *
943+ * dbfl_type_val - Internal value
944+ * Used to store small scalar data. Meta-data and value are
945+ * present in this structure and no external references are used.
946+ * Only the dbfl_val side of the data union is valid.
947+ */
948 typedef enum dbfl_type {
949- dbfl_type_rec = 0,
950 dbfl_type_val,
951 dbfl_type_ref
952 } dbfl_type;
953
954 /* Context of db_field_log: event = subscription update, read = read reply */
955 typedef enum dbfl_context {
956- dbfl_context_read = 0,
957+ dbfl_context_read,
958 dbfl_context_event
959 } dbfl_context;
960
961-#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")
962+#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref")
963
964 struct dbfl_val {
965 union native_value field; /* Field value */
966@@ -81,6 +92,8 @@ struct dbfl_val {
967 * db_delete_field_log(). Any code which changes a dbfl_type_ref
968 * field log to another type, or to reference different data,
969 * must explicitly call the dtor function.
970+ * If the dtor is NULL, then this means the array data is still owned
971+ * by a record.
972 */
973 struct dbfl_ref {
974 dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
975@@ -88,8 +101,17 @@ struct dbfl_ref {
976 void *field; /* Field value */
977 };
978
979+/*
980+ * Note: The offset member is understood to apply an implicit index mapping
981+ *
982+ * i' = (i + offset) % no_elements
983+ *
984+ * of request index i. The resulting i' is used to to index into u.r.field.
985+ *
986+ * Also note that field_size may be larger than MAX_STRING_SIZE.
987+ */
988 typedef struct db_field_log {
989- unsigned int type:2; /* type (union) selector */
990+ unsigned int type:1; /* type (union) selector */
991 /* ctx is used for all types */
992 unsigned int ctx:1; /* context (operation type) */
993 /* the following are used for value and reference types */
994@@ -97,37 +119,15 @@ typedef struct db_field_log {
995 unsigned short stat; /* Alarm Status */
996 unsigned short sevr; /* Alarm Severity */
997 short field_type; /* DBF type of data */
998- short field_size; /* Data size */
999- long no_elements; /* No of array elements */
1000+ short field_size; /* Size of a single element */
1001+ long no_elements; /* No of valid array elements */
1002+ long offset; /* See above */
1003 union {
1004 struct dbfl_val v;
1005 struct dbfl_ref r;
1006 } u;
1007 } db_field_log;
1008
1009-/*
1010- * A db_field_log will in one of three types:
1011- *
1012- * dbfl_type_rec - Reference to record
1013- * The field log stores no data itself. Data must instead be taken
1014- * via the dbChannel* which must always be provided when along
1015- * with the field log.
1016- * For this type only the 'type' and 'ctx' members are used.
1017- *
1018- * dbfl_type_ref - Reference to outside value
1019- * Used for variable size (array) data types. Meta-data
1020- * is stored in the field log, but value data is stored externally
1021- * (see struct dbfl_ref).
1022- * For this type all meta-data members are used. The dbfl_ref side of the
1023- * data union is used.
1024- *
1025- * dbfl_type_val - Internal value
1026- * Used to store small scalar data. Meta-data and value are
1027- * present in this structure and no external references are used.
1028- * For this type all meta-data members are used. The dbfl_val side of the
1029- * data union is used.
1030- */
1031-
1032 #ifdef __cplusplus
1033 }
1034 #endif
1035diff --git a/modules/database/src/ioc/dbStatic/link.h b/modules/database/src/ioc/dbStatic/link.h
1036index 2e3c778..a55900d 100644
1037--- a/modules/database/src/ioc/dbStatic/link.h
1038+++ b/modules/database/src/ioc/dbStatic/link.h
1039@@ -77,15 +77,12 @@ struct macro_link {
1040 };
1041
1042 struct dbCommon;
1043-typedef long (*LINKCVT)();
1044
1045 struct pv_link {
1046 ELLNODE backlinknode;
1047 char *pvname; /* pvname link points to */
1048 void *pvt; /* CA or DB private */
1049- LINKCVT getCvt; /* input conversion function */
1050 short pvlMask; /* Options mask */
1051- short lastGetdbrType; /* last dbrType for DB or CA get */
1052 };
1053
1054 struct jlink;
1055diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
1056index f91708a..299bbe0 100644
1057--- a/modules/database/src/std/filters/arr.c
1058+++ b/modules/database/src/std/filters/arr.c
1059@@ -12,16 +12,13 @@
1060
1061 #include <stdio.h>
1062
1063-#include <freeList.h>
1064-#include <dbAccess.h>
1065-#include <dbExtractArray.h>
1066-#include <db_field_log.h>
1067-#include <dbLock.h>
1068-#include <recSup.h>
1069-#include <epicsExit.h>
1070-#include <special.h>
1071-#include <chfPlugin.h>
1072-#include <epicsExport.h>
1073+#include "chfPlugin.h"
1074+#include "dbExtractArray.h"
1075+#include "db_field_log.h"
1076+#include "dbLock.h"
1077+#include "epicsExit.h"
1078+#include "freeList.h"
1079+#include "epicsExport.h"
1080
1081 typedef struct myStruct {
1082 epicsInt32 start;
1083@@ -45,6 +42,8 @@ static void * allocPvt(void)
1084 myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
1085 if (!my) return NULL;
1086
1087+ /* defaults */
1088+ my->start = 0;
1089 my->incr = 1;
1090 my->end = -1;
1091 return (void *) my;
1092@@ -93,78 +92,52 @@ static long wrapArrayIndices(long *start, const long increment, long *end,
1093 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
1094 {
1095 myStruct *my = (myStruct*) pvt;
1096- struct dbCommon *prec;
1097- rset *prset;
1098+ int must_lock;
1099 long start = my->start;
1100 long end = my->end;
1101- long nTarget = 0;
1102- long offset = 0;
1103- long nSource = dbChannelElements(chan);
1104- long capacity = nSource;
1105- void *pdst;
1106+ long nTarget;
1107+ void *pTarget;
1108+ /* initial values for the source array */
1109+ long offset = pfl->offset;
1110+ long nSource = pfl->no_elements;
1111
1112 switch (pfl->type) {
1113 case dbfl_type_val:
1114- /* Only filter arrays */
1115+ /* TODO Treat scalars as arrays with 1 element */
1116 break;
1117
1118- case dbfl_type_rec:
1119- /* Extract from record */
1120- if (dbChannelSpecial(chan) == SPC_DBADDR &&
1121- nSource > 1 &&
1122- (prset = dbGetRset(&chan->addr)) &&
1123- prset->get_array_info)
1124- {
1125- void *pfieldsave = dbChannelField(chan);
1126- prec = dbChannelRecord(chan);
1127- dbScanLock(prec);
1128- prset->get_array_info(&chan->addr, &nSource, &offset);
1129- nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
1130- pfl->type = dbfl_type_ref;
1131- pfl->stat = prec->stat;
1132- pfl->sevr = prec->sevr;
1133- pfl->time = prec->time;
1134- pfl->field_type = dbChannelFieldType(chan);
1135- pfl->field_size = dbChannelFieldSize(chan);
1136- pfl->no_elements = nTarget;
1137- if (nTarget) {
1138- pdst = freeListCalloc(my->arrayFreeList);
1139- if (pdst) {
1140- pfl->u.r.dtor = freeArray;
1141- pfl->u.r.pvt = my->arrayFreeList;
1142- offset = (offset + start) % dbChannelElements(chan);
1143- dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity,
1144- offset, my->incr);
1145- pfl->u.r.field = pdst;
1146- }
1147- }
1148- dbScanUnlock(prec);
1149- dbChannelField(chan) = pfieldsave;
1150- }
1151- break;
1152-
1153- /* Extract from buffer */
1154 case dbfl_type_ref:
1155- pdst = NULL;
1156- nSource = pfl->no_elements;
1157+ must_lock = !pfl->u.r.dtor;
1158+ if (must_lock)
1159+ dbScanLock(dbChannelRecord(chan));
1160 nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
1161- pfl->no_elements = nTarget;
1162- if (nTarget) {
1163- /* Copy the data out */
1164- void *psrc = pfl->u.r.field;
1165-
1166- pdst = freeListCalloc(my->arrayFreeList);
1167- if (!pdst) break;
1168- offset = start;
1169- dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type,
1170- nTarget, nSource, offset, my->incr);
1171- }
1172- if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
1173- if (nTarget) {
1174+ /*
1175+ * Side note: it would be nice if we could avoid the copying in
1176+ * the case of my->incr==1. This is currently not possible due to
1177+ * the way the offset member is interpreted (namely as shifting the
1178+ * array in a ring-buffer style).
1179+ */
1180+ if (nTarget > 0) {
1181+ /* copy the data */
1182+ void *pSource = pfl->u.r.field;
1183+ pTarget = freeListCalloc(my->arrayFreeList);
1184+ if (!pTarget) break;
1185+ offset = (offset + start) % nSource;
1186+ dbExtractArray(pSource, pTarget, pfl->field_size,
1187+ pfl->field_type, nTarget, nSource, offset, my->incr);
1188+ if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
1189+ pfl->u.r.field = pTarget;
1190 pfl->u.r.dtor = freeArray;
1191 pfl->u.r.pvt = my->arrayFreeList;
1192- pfl->u.r.field = pdst;
1193 }
1194+ /*
1195+ * Adjust offset and no_elements to refer to the new pTarget.
1196+ * (If zero elements remain, they need not refer to anything.)
1197+ */
1198+ pfl->offset = 0;
1199+ pfl->no_elements = nTarget;
1200+ if (must_lock)
1201+ dbScanUnlock(dbChannelRecord(chan));
1202 break;
1203 }
1204 return pfl;
1205diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c
1206index 5925b0b..56c9f5b 100644
1207--- a/modules/database/src/std/filters/ts.c
1208+++ b/modules/database/src/std/filters/ts.c
1209@@ -11,21 +11,39 @@
1210 */
1211
1212 #include <stdio.h>
1213+#include <stdlib.h>
1214+#include <string.h>
1215
1216-#include <chfPlugin.h>
1217-#include <dbLock.h>
1218-#include <db_field_log.h>
1219-#include <epicsExport.h>
1220+#include "chfPlugin.h"
1221+#include "db_field_log.h"
1222+#include "dbLock.h"
1223+#include "epicsExport.h"
1224+
1225+/*
1226+ * The size of the data is different for each channel, and can even
1227+ * change at runtime, so a freeList doesn't make much sense here.
1228+ */
1229+static void freeArray(db_field_log *pfl) {
1230+ free(pfl->u.r.field);
1231+}
1232
1233 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
1234 epicsTimeStamp now;
1235 epicsTimeGetCurrent(&now);
1236
1237- /* If string or array, must make a copy (to ensure coherence between time and data) */
1238- if (pfl->type == dbfl_type_rec) {
1239- dbScanLock(dbChannelRecord(chan));
1240- dbChannelMakeArrayCopy(pvt, pfl, chan);
1241- dbScanUnlock(dbChannelRecord(chan));
1242+ /* If reference and not already copied,
1243+ must make a copy (to ensure coherence between time and data) */
1244+ if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
1245+ void *pTarget = calloc(pfl->no_elements, pfl->field_size);
1246+ void *pSource = pfl->u.r.field;
1247+ if (pTarget) {
1248+ dbScanLock(dbChannelRecord(chan));
1249+ memcpy(pTarget, pSource, pfl->field_size * pfl->no_elements);
1250+ pfl->u.r.field = pTarget;
1251+ pfl->u.r.dtor = freeArray;
1252+ pfl->u.r.pvt = pvt;
1253+ dbScanUnlock(dbChannelRecord(chan));
1254+ }
1255 }
1256
1257 pfl->time = now;
1258diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp
1259index 8255fdc..ff74e01 100644
1260--- a/modules/database/test/ioc/db/dbChArrTest.cpp
1261+++ b/modules/database/test/ioc/db/dbChArrTest.cpp
1262@@ -130,7 +130,7 @@ static void check(short dbr_type) {
1263 memset(buf, 0, sizeof(buf)); \
1264 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
1265 pfl = db_create_read_log(pch); \
1266- testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
1267+ testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
1268 testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
1269 testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
1270 if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
1271@@ -178,6 +178,7 @@ static void check(short dbr_type) {
1272 pfl->field_type = DBF_CHAR; \
1273 pfl->field_size = 1; \
1274 pfl->no_elements = 26; \
1275+ pfl->offset = 0; \
1276 pfl->u.r.dtor = freeArray; \
1277 pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
1278 testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
1279diff --git a/modules/database/test/std/filters/arrTest.cpp b/modules/database/test/std/filters/arrTest.cpp
1280index 1ec16b3..08ba07b 100644
1281--- a/modules/database/test/std/filters/arrTest.cpp
1282+++ b/modules/database/test/std/filters/arrTest.cpp
1283@@ -56,25 +56,26 @@ const char *server_port = CA_SERVER_PORT;
1284
1285 static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
1286 for (int i = 0; i < pfl1->no_elements; i++) {
1287+ int j = (i + pfl1->offset) % pfl1->no_elements;
1288 switch (type) {
1289 case DBR_DOUBLE:
1290- if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
1291+ if (((epicsFloat64*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
1292 testDiag("at index=%d: field log has %g, should be %d",
1293- i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
1294+ i, ((epicsFloat64*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
1295 return 0;
1296 }
1297 break;
1298 case DBR_LONG:
1299- if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
1300+ if (((epicsInt32*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
1301 testDiag("at index=%d: field log has %d, should be %d",
1302- i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
1303+ i, ((epicsInt32*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
1304 return 0;
1305 }
1306 break;
1307 case DBR_STRING:
1308- if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
1309+ if (strtol(&((const char*)pfl1->u.r.field)[j*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) {
1310 testDiag("at index=%d: field log has '%s', should be '%d'",
1311- i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
1312+ i, &((const char*)pfl1->u.r.field)[j*pfl1->field_size], ((epicsInt32*)p2)[i]);
1313 return 0;
1314 }
1315 break;
1316@@ -119,7 +120,7 @@ static void testHead (const char *title, const char *typ = "") {
1317 off = Offset; \
1318 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
1319 pfl = db_create_read_log(pch); \
1320- testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
1321+ testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \
1322 pfl2 = dbChannelRunPostChain(pch, pfl); \
1323 testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
1324 testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
1325diff --git a/modules/database/test/std/filters/dbndTest.c b/modules/database/test/std/filters/dbndTest.c
1326index 4d70f83..fd4a472 100644
1327--- a/modules/database/test/std/filters/dbndTest.c
1328+++ b/modules/database/test/std/filters/dbndTest.c
1329@@ -129,7 +129,7 @@ MAIN(dbndTest)
1330 dbEventCtx evtctx;
1331 int logsFree, logsFinal;
1332
1333- testPlan(77);
1334+ testPlan(72);
1335
1336 testdbPrepare();
1337
1338@@ -170,12 +170,9 @@ MAIN(dbndTest)
1339 "dbnd has one filter with argument in pre chain");
1340 testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
1341
1342- /* Field logs of type ref and rec: pass any update */
1343-
1344- testHead("Field logs of type ref and rec");
1345- fl1.type = dbfl_type_rec;
1346- mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
1347+ /* Field logs of type ref: pass any update */
1348
1349+ testHead("Field logs of type ref");
1350 fl1.type = dbfl_type_ref;
1351 mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
1352
1353diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c
1354index 6f38d24..c43373f 100644
1355--- a/modules/database/test/std/rec/linkFilterTest.c
1356+++ b/modules/database/test/std/rec/linkFilterTest.c
1357@@ -105,7 +105,7 @@ MAIN(linkFilterTest)
1358 testDiag("backward range");
1359 changeRange(5,3,0);
1360 expectProcFailure("ai");
1361- expectProcFailure("wf");
1362+ expectProcSuccess("wf");
1363
1364 testDiag("step 2");
1365 changeRange(1,6,2);
1366@@ -116,7 +116,7 @@ MAIN(linkFilterTest)
1367 testDiag("range start beyond src.NORD");
1368 changeRange(8,9,0);
1369 expectProcFailure("ai");
1370- expectProcFailure("wf");
1371+ expectProcSuccess("wf");
1372
1373 testDiag("range end beyond src.NORD");
1374 changeRange(3,9,0);
1375@@ -127,7 +127,7 @@ MAIN(linkFilterTest)
1376 testDiag("range start beyond src.NELM");
1377 changeRange(11,12,0);
1378 expectProcFailure("ai");
1379- expectProcFailure("wf");
1380+ expectProcSuccess("wf");
1381
1382 testDiag("range end beyond src.NELM");
1383 changeRange(4,12,0);
1384@@ -138,7 +138,7 @@ MAIN(linkFilterTest)
1385 testDiag("single value beyond src.NORD");
1386 changeRange(8,0,0);
1387 expectProcFailure("ai");
1388- expectProcFailure("wf");
1389+ expectProcSuccess("wf");
1390
1391 testDiag("single value");
1392 changeRange(5,0,0);
1393@@ -149,7 +149,7 @@ MAIN(linkFilterTest)
1394 testDiag("single beyond rec.NELM");
1395 changeRange(12,0,0);
1396 expectProcFailure("ai");
1397- expectProcFailure("wf");
1398+ expectProcSuccess("wf");
1399
1400 testIocShutdownOk();
1401 testdbCleanup();

Subscribers

People subscribed via source and target branches