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
1diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
2index 9b149dd..3709049 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@@ -895,22 +895,23 @@ long dbGet(DBADDR *paddr, short dbrType,
51 if (nRequest && *nRequest == 0)
52 return 0;
53
54- if (!pfl || pfl->type == dbfl_type_rec) {
55+ if (!pfl) {
56 field_type = paddr->field_type;
57 no_elements = capacity = paddr->no_elements;
58-
59- /* Update field info from record
60- * may modify paddr->pfield
61- */
62- if (paddr->pfldDes->special == SPC_DBADDR &&
63- (prset = dbGetRset(paddr)) &&
64- prset->get_array_info) {
65- status = prset->get_array_info(paddr, &no_elements, &offset);
66- } else
67- offset = 0;
68 } else {
69 field_type = pfl->field_type;
70 no_elements = capacity = pfl->no_elements;
71+ }
72+
73+ /* Update field info from record
74+ * may modify paddr->pfield
75+ */
76+ if ((!pfl || (pfl->type==dbfl_type_ref && !pfl->u.r.dtor)) &&
77+ paddr->pfldDes->special == SPC_DBADDR &&
78+ (prset = dbGetRset(paddr)) &&
79+ prset->get_array_info) {
80+ status = prset->get_array_info(paddr, &no_elements, &offset);
81+ } else {
82 offset = 0;
83 }
84
85@@ -937,7 +938,7 @@ long dbGet(DBADDR *paddr, short dbrType,
86 if (offset == 0 && (!nRequest || no_elements == 1)) {
87 if (nRequest)
88 *nRequest = 1;
89- if (!pfl || pfl->type == dbfl_type_rec) {
90+ if (!pfl) {
91 status = dbFastGetConvertRoutine[field_type][dbrType]
92 (paddr->pfield, pbuf, paddr);
93 } else {
94@@ -950,6 +951,7 @@ long dbGet(DBADDR *paddr, short dbrType,
95
96 localAddr.field_type = pfl->field_type;
97 localAddr.field_size = pfl->field_size;
98+ /* not used by dbFastConvert: */
99 localAddr.no_elements = pfl->no_elements;
100 if (pfl->type == dbfl_type_val)
101 localAddr.pfield = (char *) &pfl->u.v.field;
102@@ -965,6 +967,8 @@ long dbGet(DBADDR *paddr, short dbrType,
103 if (nRequest) {
104 if (no_elements < *nRequest)
105 *nRequest = no_elements;
106+ if (capacity < *nRequest)
107+ *nRequest = capacity;
108 n = *nRequest;
109 } else {
110 n = 1;
111@@ -981,14 +985,15 @@ long dbGet(DBADDR *paddr, short dbrType,
112 }
113 /* convert data into the caller's buffer */
114 if (n <= 0) {
115- ;/*do nothing*/
116- } else if (!pfl || pfl->type == dbfl_type_rec) {
117+ ; /*do nothing */
118+ } else if (!pfl) {
119 status = convert(paddr, pbuf, n, capacity, offset);
120 } else {
121 DBADDR localAddr = *paddr; /* Structure copy */
122
123 localAddr.field_type = pfl->field_type;
124 localAddr.field_size = pfl->field_size;
125+ /* not used by dbConvert, it uses the passed capacity instead: */
126 localAddr.no_elements = pfl->no_elements;
127 if (pfl->type == dbfl_type_val)
128 localAddr.pfield = (char *) &pfl->u.v.field;
129diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
130index 805dfd4..4a95989 100644
131--- a/modules/database/src/ioc/db/dbAccessDefs.h
132+++ b/modules/database/src/ioc/db/dbAccessDefs.h
133@@ -243,7 +243,7 @@ epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
134 epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset);
135 epicsShareFunc long dbGetField(
136 struct dbAddr *,short dbrType,void *pbuffer,long *options,
137- long *nRequest,void *pfl);
138+ long *nRequest);
139 epicsShareFunc long dbGet(
140 struct dbAddr *,short dbrType,void *pbuffer,long *options,
141 long *nRequest,void *pfl);
142diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
143index a8bfbf1..4c1215c 100644
144--- a/modules/database/src/ioc/db/dbChannel.c
145+++ b/modules/database/src/ioc/db/dbChannel.c
146@@ -49,14 +49,12 @@ typedef struct parseContext {
147
148 static void *dbChannelFreeList;
149 static void *chFilterFreeList;
150-static void *dbchStringFreeList;
151
152 void dbChannelExit(void)
153 {
154 freeListCleanup(dbChannelFreeList);
155 freeListCleanup(chFilterFreeList);
156- freeListCleanup(dbchStringFreeList);
157- dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
158+ dbChannelFreeList = chFilterFreeList = NULL;
159 }
160
161 void dbChannelInit (void)
162@@ -66,7 +64,6 @@ void dbChannelInit (void)
163
164 freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
165 freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
166- freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
167 db_init_event_freelists();
168 }
169
170@@ -446,28 +443,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
171 return status;
172 }
173
174-/* Stolen from dbAccess.c: */
175-static short mapDBFToDBR[DBF_NTYPES] =
176- {
177- /* DBF_STRING => */DBR_STRING,
178- /* DBF_CHAR => */DBR_CHAR,
179- /* DBF_UCHAR => */DBR_UCHAR,
180- /* DBF_SHORT => */DBR_SHORT,
181- /* DBF_USHORT => */DBR_USHORT,
182- /* DBF_LONG => */DBR_LONG,
183- /* DBF_ULONG => */DBR_ULONG,
184- /* DBF_INT64 => */DBR_INT64,
185- /* DBF_UINT64 => */DBR_UINT64,
186- /* DBF_FLOAT => */DBR_FLOAT,
187- /* DBF_DOUBLE => */DBR_DOUBLE,
188- /* DBF_ENUM, => */DBR_ENUM,
189- /* DBF_MENU, => */DBR_ENUM,
190- /* DBF_DEVICE => */DBR_ENUM,
191- /* DBF_INLINK => */DBR_STRING,
192- /* DBF_OUTLINK => */DBR_STRING,
193- /* DBF_FWDLINK => */DBR_STRING,
194- /* DBF_NOACCESS => */DBR_NOACCESS };
195-
196 dbChannel * dbChannelCreate(const char *name)
197 {
198 const char *pname = name;
199@@ -736,37 +711,24 @@ void dbChannelDelete(dbChannel *chan)
200 freeListFree(dbChannelFreeList, chan);
201 }
202
203-static void freeArray(db_field_log *pfl) {
204- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
205- freeListFree(dbchStringFreeList, pfl->u.r.field);
206- } else {
207- free(pfl->u.r.field);
208- }
209-}
210-
211-void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
212+/*
213+ * Helper function to adjust no_elements, offset, and pfield
214+ * when copying an array from a record.
215+ */
216+void dbChannelGetArrayInfo(dbChannel *chan,
217+ void **pfield, long *no_elements, long *offset)
218 {
219- void *p;
220- struct dbCommon *prec = dbChannelRecord(chan);
221-
222- if (pfl->type != dbfl_type_rec) return;
223-
224- pfl->type = dbfl_type_ref;
225- pfl->stat = prec->stat;
226- pfl->sevr = prec->sevr;
227- pfl->time = prec->time;
228- pfl->field_type = chan->addr.field_type;
229- pfl->no_elements = chan->addr.no_elements;
230- pfl->field_size = chan->addr.field_size;
231- pfl->u.r.dtor = freeArray;
232- pfl->u.r.pvt = pvt;
233- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
234- p = freeListCalloc(dbchStringFreeList);
235- } else {
236- p = calloc(pfl->no_elements, pfl->field_size);
237+ rset *prset;
238+ if (dbChannelSpecial(chan) == SPC_DBADDR &&
239+ (prset = dbGetRset(&chan->addr)) &&
240+ prset->get_array_info)
241+ {
242+ void *pfieldsave = dbChannelField(chan);
243+ /* it is expected that this call always succeeds */
244+ prset->get_array_info(&chan->addr, no_elements, offset);
245+ *pfield = dbChannelField(chan);
246+ dbChannelField(chan) = pfieldsave;
247 }
248- if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
249- pfl->u.r.field = p;
250 }
251
252 /* FIXME: Do these belong in a different file? */
253diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
254index fab9c66..7bfc9b0 100644
255--- a/modules/database/src/ioc/db/dbChannel.h
256+++ b/modules/database/src/ioc/db/dbChannel.h
257@@ -64,8 +64,9 @@ typedef struct dbChannel {
258 /* Prototype for the channel event function that is called in filter stacks
259 *
260 * When invoked the scan lock for the record associated with 'chan' _may_ be locked.
261- * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying
262- * data out of the associated record.
263+ * If pLog->type==dbfl_type_ref and pLog->u.r.dtor is NULL, then dbScanLock() must
264+ * be called before accessing the data, as this indicates the data is owned by the
265+ * record.
266 *
267 * This function has ownership of the field log pLog, if it wishes to discard
268 * this update it should free the field log with db_delete_field_log() and
269@@ -224,7 +225,8 @@ epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, voi
270 epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
271 epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
272 epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
273-epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
274+epicsShareFunc void dbChannelGetArrayInfo(dbChannel *chan,
275+ void **pfield, long *no_elements, long *offset);
276
277 #ifdef __cplusplus
278 }
279diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
280index 688cb7a..4e1dc69 100644
281--- a/modules/database/src/ioc/db/dbDbLink.c
282+++ b/modules/database/src/ioc/db/dbDbLink.c
283@@ -203,7 +203,10 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
284 /* filter, array, or special */
285 ppv_link->getCvt = NULL;
286
287- /* For the moment, empty arrays are not supported by EPICS */
288+ /*
289+ * For the moment, empty arrays are not supported by EPICS.
290+ * See the remark in src/std/filters/arr.c for details.
291+ */
292 if (dbChannelFinalElements(chan) <= 0) /* empty array request */
293 return S_db_badField;
294
295diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
296index ed73529..f3a6907 100644
297--- a/modules/database/src/ioc/db/dbEvent.c
298+++ b/modules/database/src/ioc/db/dbEvent.c
299@@ -667,27 +667,21 @@ int db_post_extra_labor (dbEventCtx ctx)
300 return DB_EVENT_OK;
301 }
302
303-/*
304- * DB_CREATE_EVENT_LOG()
305- *
306- * NOTE: This assumes that the db scan lock is already applied
307- * (as it copies data from the record)
308- */
309-db_field_log* db_create_event_log (struct evSubscrip *pevent)
310+static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
311 {
312 db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
313
314 if (pLog) {
315- struct dbChannel *chan = pevent->chan;
316 struct dbCommon *prec = dbChannelRecord(chan);
317- pLog->ctx = dbfl_context_event;
318- if (pevent->useValque) {
319+ pLog->stat = prec->stat;
320+ pLog->sevr = prec->sevr;
321+ pLog->time = prec->time;
322+ pLog->field_type = dbChannelFieldType(chan);
323+ pLog->field_size = dbChannelFieldSize(chan);
324+ pLog->no_elements = dbChannelElements(chan);
325+
326+ if (use_val) {
327 pLog->type = dbfl_type_val;
328- pLog->stat = prec->stat;
329- pLog->sevr = prec->sevr;
330- pLog->time = prec->time;
331- pLog->field_type = dbChannelFieldType(chan);
332- pLog->no_elements = dbChannelElements(chan);
333 /*
334 * use memcpy to avoid a bus error on
335 * union copy of char in the db at an odd
336@@ -697,23 +691,46 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent)
337 dbChannelField(chan),
338 dbChannelFieldSize(chan));
339 } else {
340- pLog->type = dbfl_type_rec;
341+ pLog->type = dbfl_type_ref;
342+
343+ /* don't make a copy yet, just reference the field value */
344+ pLog->u.r.field = dbChannelField(chan);
345+ /* indicate field value still owned by record */
346+ pLog->u.r.dtor = NULL;
347+ /* no private data yet, may be set by a filter */
348+ pLog->u.r.pvt = NULL;
349 }
350 }
351 return pLog;
352 }
353
354 /*
355+ * DB_CREATE_EVENT_LOG()
356+ *
357+ * NOTE: This assumes that the db scan lock is already applied
358+ * (as it calls rset->get_array_info)
359+ */
360+db_field_log* db_create_event_log (struct evSubscrip *pevent)
361+{
362+ db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque);
363+ if (pLog) {
364+ pLog->ctx = dbfl_context_event;
365+ }
366+ return pLog;
367+}
368+
369+/*
370 * DB_CREATE_READ_LOG()
371 *
372 */
373 db_field_log* db_create_read_log (struct dbChannel *chan)
374 {
375- db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
376-
377+ db_field_log *pLog = db_create_field_log(chan,
378+ dbChannelElements(chan) == 1 &&
379+ dbChannelSpecial(chan) != SPC_DBADDR &&
380+ dbChannelFieldSize(chan) <= sizeof(union native_value));
381 if (pLog) {
382 pLog->ctx = dbfl_context_read;
383- pLog->type = dbfl_type_rec;
384 }
385 return pLog;
386 }
387@@ -737,20 +754,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
388 LOCKEVQUE (ev_que);
389
390 /*
391- * if we have an event on the queue and both the last
392- * event on the queue and the current event are emtpy
393- * (i.e. of type dbfl_type_rec), simply ignore duplicate
394- * events (saving empty events serves no purpose)
395- */
396- if (pevent->npend > 0u &&
397- (*pevent->pLastLog)->type == dbfl_type_rec &&
398- pLog->type == dbfl_type_rec) {
399- db_delete_field_log(pLog);
400- UNLOCKEVQUE (ev_que);
401- return;
402- }
403-
404- /*
405 * add to task local event que
406 */
407
408diff --git a/modules/database/src/ioc/db/dbExtractArray.c b/modules/database/src/ioc/db/dbExtractArray.c
409index e16ab4c..667de24 100644
410--- a/modules/database/src/ioc/db/dbExtractArray.c
411+++ b/modules/database/src/ioc/db/dbExtractArray.c
412@@ -13,11 +13,12 @@
413 /*
414 * Author: Ralph Lange <Ralph.Lange@bessy.de>
415 *
416- * based on dbConvert.c
417+ * based on dbConvert.c, see copyNoConvert
418 * written by: Bob Dalesio, Marty Kraimer
419 */
420
421 #include <string.h>
422+#include <assert.h>
423
424 #include "epicsTypes.h"
425
426@@ -25,61 +26,30 @@
427 #include "dbAddr.h"
428 #include "dbExtractArray.h"
429
430-void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
431- long nRequest, long no_elements, long offset, long increment)
432+void dbExtractArray(const void *pfrom, void *pto, short field_size,
433+ long nRequest, long no_elements, long offset, long increment)
434 {
435 char *pdst = (char *) pto;
436- char *psrc = (char *) paddr->pfield;
437- long nUpperPart;
438- int i;
439- short srcSize = paddr->field_size;
440- short dstSize = srcSize;
441- char isString = (paddr->field_type == DBF_STRING);
442+ const char *psrc = (char *) pfrom;
443
444- if (nRequest > no_elements) nRequest = no_elements;
445- if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
446+ /* assert preconditions */
447+ assert(nRequest >= 0);
448+ assert(no_elements >= 0);
449+ assert(increment > 0);
450+ assert(0 <= offset);
451+ assert(offset < no_elements);
452
453- if (increment == 1 && dstSize == srcSize) {
454- nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
455- memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
456+ if (increment == 1) {
457+ long nUpperPart =
458+ nRequest < no_elements - offset ? nRequest : no_elements - offset;
459+ memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart);
460 if (nRequest > nUpperPart)
461- memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
462- if (isString)
463- for (i = 1; i <= nRequest; i++)
464- pdst[dstSize*i-1] = '\0';
465+ memcpy(pdst + (field_size * nUpperPart), psrc,
466+ field_size * (nRequest - nUpperPart));
467 } else {
468- for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
469+ for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) {
470 offset %= no_elements;
471- memcpy(pdst, &psrc[offset*srcSize], dstSize);
472- if (isString) pdst[dstSize-1] = '\0';
473- }
474- }
475-}
476-
477-void dbExtractArrayFromBuf(const void *pfrom, void *pto,
478- short field_size, short field_type,
479- long nRequest, long no_elements, long offset, long increment)
480-{
481- char *pdst = (char *) pto;
482- char *psrc = (char *) pfrom;
483- int i;
484- short srcSize = field_size;
485- short dstSize = srcSize;
486- char isString = (field_type == DBF_STRING);
487-
488- if (nRequest > no_elements) nRequest = no_elements;
489- if (offset > no_elements - 1) offset = no_elements - 1;
490- if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
491-
492- if (increment == 1) {
493- memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
494- if (isString)
495- for (i = 1; i <= nRequest; i++)
496- pdst[dstSize*i] = '\0';
497- } else {
498- for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
499- memcpy(pdst, &psrc[offset*srcSize], dstSize);
500- if (isString) pdst[dstSize] = '\0';
501+ memcpy(pdst, psrc + (offset * field_size), field_size);
502 }
503 }
504 }
505diff --git a/modules/database/src/ioc/db/dbExtractArray.h b/modules/database/src/ioc/db/dbExtractArray.h
506index 7ed3584..374d1d1 100644
507--- a/modules/database/src/ioc/db/dbExtractArray.h
508+++ b/modules/database/src/ioc/db/dbExtractArray.h
509@@ -21,11 +21,36 @@
510 extern "C" {
511 #endif
512
513-epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
514- long nRequest, long no_elements, long offset, long increment);
515-epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
516- short field_size, short field_type,
517- long nRequest, long no_elements, long offset, long increment);
518+/** @brief Make a copy of parts of an array.
519+ *
520+ * The source array may or may not be a record field.
521+ *
522+ * The increment parameter is used to support array filters; it
523+ * means: copy only every increment'th element, starting at offset.
524+ *
525+ * The offset and no_elements parameters are used to support the
526+ * circular buffer feature of record fields: elements before offset
527+ * are treated as if they came right after no_elements.
528+ *
529+ * This function does not do any conversion on the array elements.
530+ *
531+ * Preconditions:
532+ * nRequest >= 0, no_elements >= 0, increment > 0
533+ * 0 <= offset < no_elements
534+ * pto points to a buffer with at least field_size*nRequest bytes
535+ * pfrom points to a buffer with exactly field_size*no_elements bytes
536+ *
537+ * @param pfrom Pointer to source array.
538+ * @param pto Pointer to target array.
539+ * @param field_size Size of an array element.
540+ * @param nRequest Number of elements to copy.
541+ * @param no_elements Number of elements in source array.
542+ * @param offset Wrap-around point in source array.
543+ * @param increment Copy only every increment'th element.
544+ */
545+epicsShareFunc void dbExtractArray(const void *pfrom, void *pto,
546+ short field_size, long nRequest, long no_elements, long offset,
547+ long increment);
548
549 #ifdef __cplusplus
550 }
551diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
552index 1193954..206d6c9 100644
553--- a/modules/database/src/ioc/db/dbTest.c
554+++ b/modules/database/src/ioc/db/dbTest.c
555@@ -341,14 +341,14 @@ long dbgf(const char *pname)
556 no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
557 if (addr.dbr_field_type == DBR_ENUM) {
558 long status = dbGetField(&addr, DBR_STRING, pbuffer,
559- &options, &no_elements, NULL);
560+ &options, &no_elements);
561
562 printBuffer(status, DBR_STRING, pbuffer, 0L, 0L,
563 no_elements, &msg_Buff, 10);
564 }
565 else {
566 long status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
567- &options, &no_elements, NULL);
568+ &options, &no_elements);
569
570 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
571 no_elements, &msg_Buff, 10);
572@@ -487,7 +487,7 @@ long dbtgf(const char *pname)
573 ret_options = req_options;
574 no_elements = 0;
575 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
576- &ret_options, &no_elements, NULL);
577+ &ret_options, &no_elements);
578 printBuffer(status, addr.dbr_field_type, pbuffer,
579 req_options, ret_options, no_elements, pMsgBuff, tab_size);
580
581@@ -496,62 +496,62 @@ long dbtgf(const char *pname)
582
583 dbr_type = DBR_STRING;
584 no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
585- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
586+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
587 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
588
589 dbr_type = DBR_CHAR;
590 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
591- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
592+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
593 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
594
595 dbr_type = DBR_UCHAR;
596 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
597- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
598+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
599 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
600
601 dbr_type = DBR_SHORT;
602 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
603- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
604+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
605 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
606
607 dbr_type = DBR_USHORT;
608 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
609- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
610+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
611 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
612
613 dbr_type = DBR_LONG;
614 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
615- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
616+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
617 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
618
619 dbr_type = DBR_ULONG;
620 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
621- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
622+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
623 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
624
625 dbr_type = DBR_INT64;
626 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
627- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
628+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
629 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
630
631 dbr_type = DBR_UINT64;
632 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
633- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
634+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
635 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
636
637 dbr_type = DBR_FLOAT;
638 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
639- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
640+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
641 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
642
643 dbr_type = DBR_DOUBLE;
644 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
645- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
646+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
647 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
648
649 dbr_type = DBR_ENUM;
650 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
651- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
652+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
653 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
654
655 pmsg[0] = '\0';
656@@ -651,7 +651,7 @@ long dbtpf(const char *pname, const char *pvalue)
657
658 printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
659 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
660- &options, &no_elements, NULL);
661+ &options, &no_elements);
662 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
663 no_elements, pMsgBuff, tab_size);
664 }
665diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c
666index 6846ef5..458a28b 100644
667--- a/modules/database/src/ioc/db/dbUnitTest.c
668+++ b/modules/database/src/ioc/db/dbUnitTest.c
669@@ -197,7 +197,7 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
670 return;
671 }
672
673- status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
674+ status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq);
675 if (status) {
676 testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
677 return;
678@@ -270,7 +270,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
679 return;
680 }
681
682- status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);
683+ status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest);
684 if (status) {
685 testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
686
687diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
688index 1534517..a801142 100644
689--- a/modules/database/src/ioc/db/db_field_log.h
690+++ b/modules/database/src/ioc/db/db_field_log.h
691@@ -56,20 +56,31 @@ union native_value {
692 struct db_field_log;
693 typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
694
695-/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */
696+/*
697+ * A db_field_log has one of two types:
698+ *
699+ * dbfl_type_ref - Reference to value
700+ * Used for variable size (array) data types. Meta-data
701+ * is stored in the field log, but value data is stored externally.
702+ * Only the dbfl_ref side of the data union is valid.
703+ *
704+ * dbfl_type_val - Internal value
705+ * Used to store small scalar data. Meta-data and value are
706+ * present in this structure and no external references are used.
707+ * Only the dbfl_val side of the data union is valid.
708+ */
709 typedef enum dbfl_type {
710- dbfl_type_rec = 0,
711 dbfl_type_val,
712 dbfl_type_ref
713 } dbfl_type;
714
715 /* Context of db_field_log: event = subscription update, read = read reply */
716 typedef enum dbfl_context {
717- dbfl_context_read = 0,
718+ dbfl_context_read,
719 dbfl_context_event
720 } dbfl_context;
721
722-#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")
723+#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref")
724
725 struct dbfl_val {
726 union native_value field; /* Field value */
727@@ -81,6 +92,8 @@ struct dbfl_val {
728 * db_delete_field_log(). Any code which changes a dbfl_type_ref
729 * field log to another type, or to reference different data,
730 * must explicitly call the dtor function.
731+ * If the dtor is NULL, then this means the array data is still owned
732+ * by a record.
733 */
734 struct dbfl_ref {
735 dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
736@@ -88,8 +101,11 @@ struct dbfl_ref {
737 void *field; /* Field value */
738 };
739
740+/*
741+ * Note that field_size may be larger than MAX_STRING_SIZE.
742+ */
743 typedef struct db_field_log {
744- unsigned int type:2; /* type (union) selector */
745+ unsigned int type:1; /* type (union) selector */
746 /* ctx is used for all types */
747 unsigned int ctx:1; /* context (operation type) */
748 /* the following are used for value and reference types */
749@@ -97,37 +113,14 @@ typedef struct db_field_log {
750 unsigned short stat; /* Alarm Status */
751 unsigned short sevr; /* Alarm Severity */
752 short field_type; /* DBF type of data */
753- short field_size; /* Data size */
754- long no_elements; /* No of array elements */
755+ short field_size; /* Size of a single element */
756+ long no_elements; /* No of valid array elements */
757 union {
758 struct dbfl_val v;
759 struct dbfl_ref r;
760 } u;
761 } db_field_log;
762
763-/*
764- * A db_field_log will in one of three types:
765- *
766- * dbfl_type_rec - Reference to record
767- * The field log stores no data itself. Data must instead be taken
768- * via the dbChannel* which must always be provided when along
769- * with the field log.
770- * For this type only the 'type' and 'ctx' members are used.
771- *
772- * dbfl_type_ref - Reference to outside value
773- * Used for variable size (array) data types. Meta-data
774- * is stored in the field log, but value data is stored externally
775- * (see struct dbfl_ref).
776- * For this type all meta-data members are used. The dbfl_ref side of the
777- * data union is used.
778- *
779- * dbfl_type_val - Internal value
780- * Used to store small scalar data. Meta-data and value are
781- * present in this structure and no external references are used.
782- * For this type all meta-data members are used. The dbfl_val side of the
783- * data union is used.
784- */
785-
786 #ifdef __cplusplus
787 }
788 #endif
789diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
790index f91708a..1e506a2 100644
791--- a/modules/database/src/std/filters/arr.c
792+++ b/modules/database/src/std/filters/arr.c
793@@ -12,16 +12,14 @@
794
795 #include <stdio.h>
796
797-#include <freeList.h>
798-#include <dbAccess.h>
799-#include <dbExtractArray.h>
800-#include <db_field_log.h>
801-#include <dbLock.h>
802-#include <recSup.h>
803-#include <epicsExit.h>
804-#include <special.h>
805-#include <chfPlugin.h>
806-#include <epicsExport.h>
807+#include "chfPlugin.h"
808+#include "dbAccessDefs.h"
809+#include "dbExtractArray.h"
810+#include "db_field_log.h"
811+#include "dbLock.h"
812+#include "epicsExit.h"
813+#include "freeList.h"
814+#include "epicsExport.h"
815
816 typedef struct myStruct {
817 epicsInt32 start;
818@@ -45,6 +43,8 @@ static void * allocPvt(void)
819 myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
820 if (!my) return NULL;
821
822+ /* defaults */
823+ my->start = 0;
824 my->incr = 1;
825 my->end = -1;
826 return (void *) my;
827@@ -76,8 +76,6 @@ static void freeArray(db_field_log *pfl)
828 static long wrapArrayIndices(long *start, const long increment, long *end,
829 const long no_elements)
830 {
831- long len = 0;
832-
833 if (*start < 0) *start = no_elements + *start;
834 if (*start < 0) *start = 0;
835 if (*start > no_elements) *start = no_elements;
836@@ -86,19 +84,22 @@ static long wrapArrayIndices(long *start, const long increment, long *end,
837 if (*end < 0) *end = 0;
838 if (*end >= no_elements) *end = no_elements - 1;
839
840- if (*end - *start >= 0) len = 1 + (*end - *start) / increment;
841- return len;
842+ if (*end - *start >= 0)
843+ return 1 + (*end - *start) / increment;
844+ else
845+ return 0;
846 }
847
848 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
849 {
850 myStruct *my = (myStruct*) pvt;
851- struct dbCommon *prec;
852- rset *prset;
853+ int must_lock;
854 long start = my->start;
855 long end = my->end;
856- long nTarget = 0;
857+ long nTarget;
858+ void *pTarget;
859 long offset = 0;
860+<<<<<<< modules/database/src/std/filters/arr.c
861 long nSource = dbChannelElements(chan);
862 long capacity = nSource;
863 void *pdst;
864@@ -141,30 +142,52 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
865 dbScanUnlock(prec);
866 dbChannelField(chan) = pfieldsave;
867 }
868+=======
869+ long nSource = pfl->no_elements;
870+ void *pSource = pfl->u.r.field;
871+
872+ switch (pfl->type) {
873+ case dbfl_type_val:
874+ /* TODO Treat scalars as arrays with 1 element */
875+>>>>>>> modules/database/src/std/filters/arr.c
876 break;
877
878- /* Extract from buffer */
879 case dbfl_type_ref:
880- pdst = NULL;
881- nSource = pfl->no_elements;
882- nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
883- pfl->no_elements = nTarget;
884- if (nTarget) {
885- /* Copy the data out */
886- void *psrc = pfl->u.r.field;
887-
888- pdst = freeListCalloc(my->arrayFreeList);
889- if (!pdst) break;
890- offset = start;
891- dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type,
892- nTarget, nSource, offset, my->incr);
893+ must_lock = !pfl->u.r.dtor;
894+ if (must_lock) {
895+ dbScanLock(dbChannelRecord(chan));
896+ dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
897 }
898- if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
899- if (nTarget) {
900+ nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
901+ if (nTarget > 0) {
902+ /* copy the data */
903+ pTarget = freeListCalloc(my->arrayFreeList);
904+ if (!pTarget) break;
905+ /* must do the wrap-around with the original no_elements */
906+ offset = (offset + start) % pfl->no_elements;
907+ dbExtractArray(pSource, pTarget, pfl->field_size,
908+ nTarget, pfl->no_elements, offset, my->incr);
909+ if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
910+ pfl->u.r.field = pTarget;
911 pfl->u.r.dtor = freeArray;
912 pfl->u.r.pvt = my->arrayFreeList;
913- pfl->u.r.field = pdst;
914 }
915+ /* Adjust no_elements to refer to the new pTarget.
916+ *
917+ * Setting pfl->no_elements outside of the "if" clause above is
918+ * done to make requests fail if nTarget is zero, that is, if all
919+ * elements selected by the filter are outside the array bounds.
920+ * TODO:
921+ * It would be possible to lift this restriction by interpreting
922+ * a request with *no* number of elements (NULL pointer) as scalar
923+ * (meaning: fail if we get less than one element); in contrast,
924+ * a request that explicitly specifies one element would be
925+ * interpreted as an array request, for which zero elements would
926+ * be a normal expected result.
927+ */
928+ pfl->no_elements = nTarget;
929+ if (must_lock)
930+ dbScanUnlock(dbChannelRecord(chan));
931 break;
932 }
933 return pfl;
934diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c
935index 5925b0b..aed3b41 100644
936--- a/modules/database/src/std/filters/ts.c
937+++ b/modules/database/src/std/filters/ts.c
938@@ -11,21 +11,44 @@
939 */
940
941 #include <stdio.h>
942+#include <stdlib.h>
943+#include <string.h>
944
945-#include <chfPlugin.h>
946-#include <dbLock.h>
947-#include <db_field_log.h>
948-#include <epicsExport.h>
949+#include "chfPlugin.h"
950+#include "db_field_log.h"
951+#include "dbExtractArray.h"
952+#include "dbLock.h"
953+#include "epicsExport.h"
954+
955+/*
956+ * The size of the data is different for each channel, and can even
957+ * change at runtime, so a freeList doesn't make much sense here.
958+ */
959+static void freeArray(db_field_log *pfl) {
960+ free(pfl->u.r.field);
961+}
962
963 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
964 epicsTimeStamp now;
965 epicsTimeGetCurrent(&now);
966
967- /* If string or array, must make a copy (to ensure coherence between time and data) */
968- if (pfl->type == dbfl_type_rec) {
969- dbScanLock(dbChannelRecord(chan));
970- dbChannelMakeArrayCopy(pvt, pfl, chan);
971- dbScanUnlock(dbChannelRecord(chan));
972+ /* If reference and not already copied,
973+ must make a copy (to ensure coherence between time and data) */
974+ if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
975+ void *pTarget = calloc(pfl->no_elements, pfl->field_size);
976+ void *pSource = pfl->u.r.field;
977+ if (pTarget) {
978+ long offset = 0;
979+ long nSource = pfl->no_elements;
980+ dbScanLock(dbChannelRecord(chan));
981+ dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
982+ dbExtractArray(pSource, pTarget, pfl->field_size,
983+ nSource, pfl->no_elements, offset, 1);
984+ pfl->u.r.field = pTarget;
985+ pfl->u.r.dtor = freeArray;
986+ pfl->u.r.pvt = pvt;
987+ dbScanUnlock(dbChannelRecord(chan));
988+ }
989 }
990
991 pfl->time = now;
992diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp
993index 8255fdc..6ec6ea4 100644
994--- a/modules/database/test/ioc/db/dbChArrTest.cpp
995+++ b/modules/database/test/ioc/db/dbChArrTest.cpp
996@@ -130,7 +130,7 @@ static void check(short dbr_type) {
997 memset(buf, 0, sizeof(buf)); \
998 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
999 pfl = db_create_read_log(pch); \
1000- testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
1001+ testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
1002 testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
1003 testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
1004 if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
1005diff --git a/modules/database/test/std/filters/arrTest.cpp b/modules/database/test/std/filters/arrTest.cpp
1006index 1ec16b3..5db3757 100644
1007--- a/modules/database/test/std/filters/arrTest.cpp
1008+++ b/modules/database/test/std/filters/arrTest.cpp
1009@@ -72,9 +72,9 @@ static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
1010 }
1011 break;
1012 case DBR_STRING:
1013- if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
1014+ if (strtol(&((const char*)pfl1->u.r.field)[i*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) {
1015 testDiag("at index=%d: field log has '%s', should be '%d'",
1016- i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
1017+ i, &((const char*)pfl1->u.r.field)[i*pfl1->field_size], ((epicsInt32*)p2)[i]);
1018 return 0;
1019 }
1020 break;
1021@@ -119,7 +119,7 @@ static void testHead (const char *title, const char *typ = "") {
1022 off = Offset; \
1023 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
1024 pfl = db_create_read_log(pch); \
1025- testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
1026+ testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \
1027 pfl2 = dbChannelRunPostChain(pch, pfl); \
1028 testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
1029 testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
1030diff --git a/modules/database/test/std/filters/dbndTest.c b/modules/database/test/std/filters/dbndTest.c
1031index 4d70f83..fd4a472 100644
1032--- a/modules/database/test/std/filters/dbndTest.c
1033+++ b/modules/database/test/std/filters/dbndTest.c
1034@@ -129,7 +129,7 @@ MAIN(dbndTest)
1035 dbEventCtx evtctx;
1036 int logsFree, logsFinal;
1037
1038- testPlan(77);
1039+ testPlan(72);
1040
1041 testdbPrepare();
1042
1043@@ -170,12 +170,9 @@ MAIN(dbndTest)
1044 "dbnd has one filter with argument in pre chain");
1045 testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
1046
1047- /* Field logs of type ref and rec: pass any update */
1048-
1049- testHead("Field logs of type ref and rec");
1050- fl1.type = dbfl_type_rec;
1051- mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
1052+ /* Field logs of type ref: pass any update */
1053
1054+ testHead("Field logs of type ref");
1055 fl1.type = dbfl_type_ref;
1056 mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
1057

Subscribers

People subscribed via source and target branches