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
Diff against target: 3074 lines (+1008/-798)
27 files modified
documentation/RELEASE_NOTES.md (+9/-0)
modules/database/src/ioc/db/dbAccess.c (+467/-439)
modules/database/src/ioc/db/dbAccessDefs.h (+1/-1)
modules/database/src/ioc/db/dbChannel.c (+2/-59)
modules/database/src/ioc/db/dbChannel.h (+3/-3)
modules/database/src/ioc/db/dbDbLink.c (+86/-53)
modules/database/src/ioc/db/dbDbLink.h (+1/-1)
modules/database/src/ioc/db/dbEvent.c (+73/-40)
modules/database/src/ioc/db/dbEvent.h (+1/-0)
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 (+1/-1)
modules/database/src/ioc/db/dbLink.h (+2/-1)
modules/database/src/ioc/db/dbLock.c (+3/-3)
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/std/dev/devAiSoft.c (+2/-1)
modules/database/src/std/filters/arr.c (+52/-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/Makefile (+7/-0)
modules/database/test/std/rec/linkFilterTest.c (+157/-0)
modules/database/test/std/rec/linkFilterTest.db (+16/-0)
modules/database/test/std/rec/regressTest.c (+1/-1)
Reviewer Review Type Date Requested Status
EPICS Core Developers Pending
Review via email: mp+381125@code.launchpad.net

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 :

Note that I have based this on Dirk's addition of filters for DB links.

I wonder if I should have listed Dirk's repo+branch as a requisite when creating the merge request.
Is it possible to do that after the fact or do I have to re-submit?

Revision history for this message
Andrew Johnson (anj) wrote :

Yes, please hit the "Resubmit proposal" towards the top right of the window and enter Dirk's branch as a prerequisite. That way launchpad doesn't mix your changes in with his and we can review and comment on them separately.

Unmerged commits

13fb313... 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).

6aeddc3... by Ben Franksen

reformat modules/database/src/ioc/db/dbAccess.c

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

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

4960efa... by Dirk Zimoch

initialize free lists when starting dbChannel

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md
2index bdd2468..681495e 100644
3--- a/documentation/RELEASE_NOTES.md
4+++ b/documentation/RELEASE_NOTES.md
5@@ -36,6 +36,15 @@ As long as all support modules and IOCs are rebuilt from source after updating
6 them to use this release of EPICS Base, these changes should not have any
7 affect.
8
9+### Filters in database links
10+
11+Input links can now use filters, most importantly array element and sub array
12+access, even if they are not channel access links.
13+
14+### ai Soft Channel support
15+
16+The Soft Channel device support for ai records now returns failure when
17+fetching the INP link fails.
18
19 ### logClient reliability
20
21diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
22index 19f6038..a6c31f2 100644
23--- a/modules/database/src/ioc/db/dbAccess.c
24+++ b/modules/database/src/ioc/db/dbAccess.c
25@@ -34,7 +34,7 @@
26 #include "errlog.h"
27 #include "errMdef.h"
28
29-#include "epicsExport.h" /* #define epicsExportSharedSymbols */
30+#include "epicsExport.h" /* #define epicsExportSharedSymbols */
31 #include "caeventmask.h"
32 #include "callback.h"
33 #include "dbAccessDefs.h"
34@@ -63,7 +63,7 @@
35 #include "special.h"
36
37 epicsShareDef struct dbBase *pdbbase = 0;
38-epicsShareDef volatile int interruptAccept=FALSE;
39+epicsShareDef volatile int interruptAccept = FALSE;
40
41 epicsShareDef int dbAccessDebugPUTF = 0;
42 epicsExportAddress(int, dbAccessDebugPUTF);
43@@ -73,24 +73,24 @@ epicsExportAddress(int, dbAccessDebugPUTF);
44 epicsShareDef DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
45
46 static short mapDBFToDBR[DBF_NTYPES] = {
47- /* DBF_STRING => */ DBR_STRING,
48- /* DBF_CHAR => */ DBR_CHAR,
49- /* DBF_UCHAR => */ DBR_UCHAR,
50- /* DBF_SHORT => */ DBR_SHORT,
51- /* DBF_USHORT => */ DBR_USHORT,
52- /* DBF_LONG => */ DBR_LONG,
53- /* DBF_ULONG => */ DBR_ULONG,
54- /* DBF_INT64 => */ DBR_INT64,
55- /* DBF_UINT64 => */ DBR_UINT64,
56- /* DBF_FLOAT => */ DBR_FLOAT,
57- /* DBF_DOUBLE => */ DBR_DOUBLE,
58- /* DBF_ENUM, => */ DBR_ENUM,
59- /* DBF_MENU, => */ DBR_ENUM,
60- /* DBF_DEVICE => */ DBR_ENUM,
61- /* DBF_INLINK => */ DBR_STRING,
62- /* DBF_OUTLINK => */ DBR_STRING,
63- /* DBF_FWDLINK => */ DBR_STRING,
64- /* DBF_NOACCESS => */ DBR_NOACCESS
65+ /* DBF_STRING => */ DBR_STRING,
66+ /* DBF_CHAR => */ DBR_CHAR,
67+ /* DBF_UCHAR => */ DBR_UCHAR,
68+ /* DBF_SHORT => */ DBR_SHORT,
69+ /* DBF_USHORT => */ DBR_USHORT,
70+ /* DBF_LONG => */ DBR_LONG,
71+ /* DBF_ULONG => */ DBR_ULONG,
72+ /* DBF_INT64 => */ DBR_INT64,
73+ /* DBF_UINT64 => */ DBR_UINT64,
74+ /* DBF_FLOAT => */ DBR_FLOAT,
75+ /* DBF_DOUBLE => */ DBR_DOUBLE,
76+ /* DBF_ENUM, => */ DBR_ENUM,
77+ /* DBF_MENU, => */ DBR_ENUM,
78+ /* DBF_DEVICE => */ DBR_ENUM,
79+ /* DBF_INLINK => */ DBR_STRING,
80+ /* DBF_OUTLINK => */ DBR_STRING,
81+ /* DBF_FWDLINK => */ DBR_STRING,
82+ /* DBF_NOACCESS => */ DBR_NOACCESS
83 };
84
85 /*
86@@ -109,217 +109,219 @@ void dbSpcAsRegisterCallback(SPC_ASCALLBACK func)
87 spcAsCallback = func;
88 }
89
90-long dbPutSpecial(DBADDR *paddr,int pass)
91+long dbPutSpecial(DBADDR *paddr, int pass)
92 {
93- long int (*pspecial)()=NULL;
94- rset *prset;
95- dbCommon *precord = paddr->precord;
96- long status=0;
97- long special=paddr->special;
98+ long int (*pspecial) () = NULL;
99+ rset *prset;
100+ dbCommon *precord = paddr->precord;
101+ long status = 0;
102+ long special = paddr->special;
103
104 prset = dbGetRset(paddr);
105- if(special<100) { /*global processing*/
106- if((special==SPC_NOMOD) && (pass==0)) {
107- status = S_db_noMod;
108- recGblDbaddrError(status,paddr,"dbPut");
109- return(status);
110- }else if(special==SPC_SCAN){
111- if(pass==0)
112- scanDelete(precord);
113- else
114- scanAdd(precord);
115- }else if((special==SPC_AS) && (pass==1)) {
116- if(spcAsCallback) (*spcAsCallback)(precord);
117- }
118- }else {
119- if( prset && (pspecial = (prset->special))) {
120- status=(*pspecial)(paddr,pass);
121- if(status) return(status);
122- } else if(pass==0){
123- recGblRecSupError(S_db_noSupport,paddr,"dbPut", "special");
124- return(S_db_noSupport);
125- }
126+ if (special < 100) { /*global processing */
127+ if ((special == SPC_NOMOD) && (pass == 0)) {
128+ status = S_db_noMod;
129+ recGblDbaddrError(status, paddr, "dbPut");
130+ return (status);
131+ } else if (special == SPC_SCAN) {
132+ if (pass == 0)
133+ scanDelete(precord);
134+ else
135+ scanAdd(precord);
136+ } else if ((special == SPC_AS) && (pass == 1)) {
137+ if (spcAsCallback)
138+ (*spcAsCallback) (precord);
139+ }
140+ } else {
141+ if (prset && (pspecial = (prset->special))) {
142+ status = (*pspecial) (paddr, pass);
143+ if (status)
144+ return (status);
145+ } else if (pass == 0) {
146+ recGblRecSupError(S_db_noSupport, paddr, "dbPut", "special");
147+ return (S_db_noSupport);
148+ }
149 }
150- return(0);
151+ return (0);
152 }
153
154 static void get_enum_strs(DBADDR *paddr, char **ppbuffer,
155- rset *prset,long *options)
156+ rset * prset, long *options)
157 {
158- short field_type=paddr->field_type;
159- dbFldDes *pdbFldDes = paddr->pfldDes;
160- dbMenu *pdbMenu;
161- dbDeviceMenu *pdbDeviceMenu;
162- char **papChoice;
163- unsigned long no_str;
164- char *ptemp;
165- struct dbr_enumStrs *pdbr_enumStrs=(struct dbr_enumStrs*)(*ppbuffer);
166+ short field_type = paddr->field_type;
167+ dbFldDes *pdbFldDes = paddr->pfldDes;
168+ dbMenu *pdbMenu;
169+ dbDeviceMenu *pdbDeviceMenu;
170+ char **papChoice;
171+ unsigned long no_str;
172+ char *ptemp;
173+ struct dbr_enumStrs *pdbr_enumStrs = (struct dbr_enumStrs *)(*ppbuffer);
174 unsigned int i;
175
176- memset(pdbr_enumStrs,'\0',dbr_enumStrs_size);
177- switch(field_type) {
178- case DBF_ENUM:
179- if( prset && prset->get_enum_strs ) {
180- (*prset->get_enum_strs)(paddr,pdbr_enumStrs);
181- } else {
182- *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/
183- }
184- break;
185- case DBF_MENU:
186- pdbMenu = (dbMenu *)pdbFldDes->ftPvt;
187- no_str = pdbMenu->nChoice;
188- papChoice= pdbMenu->papChoiceValue;
189- goto choice_common;
190- case DBF_DEVICE:
191- pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt;
192- if(!pdbDeviceMenu) {
193- *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/
194- break;
195- }
196- no_str = pdbDeviceMenu->nChoice;
197- papChoice = pdbDeviceMenu->papChoice;
198- goto choice_common;
199-choice_common:
200- i = sizeof(pdbr_enumStrs->strs)/
201- sizeof(pdbr_enumStrs->strs[0]);
202- if(i<no_str) no_str = i;
203- pdbr_enumStrs->no_str = no_str;
204- ptemp = &(pdbr_enumStrs->strs[0][0]);
205- for (i=0; i<no_str; i++) {
206- if(papChoice[i]==NULL) *ptemp=0;
207- else {
208- strncpy(ptemp,papChoice[i],
209- sizeof(pdbr_enumStrs->strs[0]));
210- *(ptemp+sizeof(pdbr_enumStrs->strs[0])-1) = 0;
211- }
212- ptemp += sizeof(pdbr_enumStrs->strs[0]);
213- }
214- break;
215- default:
216- *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/
217- break;
218- }
219- *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size;
220- return;
221+ memset(pdbr_enumStrs, '\0', dbr_enumStrs_size);
222+ switch (field_type) {
223+ case DBF_ENUM:
224+ if (prset && prset->get_enum_strs) {
225+ (*prset->get_enum_strs) (paddr, pdbr_enumStrs);
226+ } else {
227+ *options = (*options) ^ DBR_ENUM_STRS; /*Turn off option */
228+ }
229+ break;
230+ case DBF_MENU:
231+ pdbMenu = (dbMenu *) pdbFldDes->ftPvt;
232+ no_str = pdbMenu->nChoice;
233+ papChoice = pdbMenu->papChoiceValue;
234+ goto choice_common;
235+ case DBF_DEVICE:
236+ pdbDeviceMenu = (dbDeviceMenu *) pdbFldDes->ftPvt;
237+ if (!pdbDeviceMenu) {
238+ *options = (*options) ^ DBR_ENUM_STRS; /*Turn off option */
239+ break;
240+ }
241+ no_str = pdbDeviceMenu->nChoice;
242+ papChoice = pdbDeviceMenu->papChoice;
243+ goto choice_common;
244+ choice_common:
245+ i = sizeof(pdbr_enumStrs->strs) / sizeof(pdbr_enumStrs->strs[0]);
246+ if (i < no_str)
247+ no_str = i;
248+ pdbr_enumStrs->no_str = no_str;
249+ ptemp = &(pdbr_enumStrs->strs[0][0]);
250+ for (i = 0; i < no_str; i++) {
251+ if (papChoice[i] == NULL)
252+ *ptemp = 0;
253+ else {
254+ strncpy(ptemp, papChoice[i], sizeof(pdbr_enumStrs->strs[0]));
255+ *(ptemp + sizeof(pdbr_enumStrs->strs[0]) - 1) = 0;
256+ }
257+ ptemp += sizeof(pdbr_enumStrs->strs[0]);
258+ }
259+ break;
260+ default:
261+ *options = (*options) ^ DBR_ENUM_STRS; /*Turn off option */
262+ break;
263+ }
264+ *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size;
265+ return;
266 }
267
268 static void get_graphics(DBADDR *paddr, char **ppbuffer,
269- rset *prset,long *options)
270+ rset * prset, long *options)
271 {
272- struct dbr_grDouble grd;
273- int got_data=FALSE;
274-
275- grd.upper_disp_limit = grd.lower_disp_limit = 0.0;
276- if( prset && prset->get_graphic_double ) {
277- (*prset->get_graphic_double)(paddr,&grd);
278- got_data=TRUE;
279- }
280- if( (*options) & (DBR_GR_LONG) ) {
281- char *pbuffer=*ppbuffer;
282-
283- if(got_data) {
284- struct dbr_grLong *pgr=(struct dbr_grLong*)pbuffer;
285- pgr->upper_disp_limit = (epicsInt32)grd.upper_disp_limit;
286- pgr->lower_disp_limit = (epicsInt32)grd.lower_disp_limit;
287- } else {
288- memset(pbuffer,'\0',dbr_grLong_size);
289- *options = (*options) ^ DBR_GR_LONG; /*Turn off option*/
290- }
291- *ppbuffer = ((char *)*ppbuffer) + dbr_grLong_size;
292- }
293- if( (*options) & (DBR_GR_DOUBLE) ) {
294- char *pbuffer=*ppbuffer;
295-
296- if(got_data) {
297- struct dbr_grDouble *pgr=(struct dbr_grDouble*)pbuffer;
298- pgr->upper_disp_limit = grd.upper_disp_limit;
299- pgr->lower_disp_limit = grd.lower_disp_limit;
300- } else {
301- memset(pbuffer,'\0',dbr_grDouble_size);
302- *options = (*options) ^ DBR_GR_DOUBLE; /*Turn off option*/
303- }
304- *ppbuffer = ((char *)*ppbuffer) + dbr_grDouble_size;
305- }
306- return;
307+ struct dbr_grDouble grd;
308+ int got_data = FALSE;
309+
310+ grd.upper_disp_limit = grd.lower_disp_limit = 0.0;
311+ if (prset && prset->get_graphic_double) {
312+ (*prset->get_graphic_double) (paddr, &grd);
313+ got_data = TRUE;
314+ }
315+ if ((*options) & (DBR_GR_LONG)) {
316+ char *pbuffer = *ppbuffer;
317+
318+ if (got_data) {
319+ struct dbr_grLong *pgr = (struct dbr_grLong *)pbuffer;
320+ pgr->upper_disp_limit = (epicsInt32) grd.upper_disp_limit;
321+ pgr->lower_disp_limit = (epicsInt32) grd.lower_disp_limit;
322+ } else {
323+ memset(pbuffer, '\0', dbr_grLong_size);
324+ *options = (*options) ^ DBR_GR_LONG; /*Turn off option */
325+ }
326+ *ppbuffer = ((char *)*ppbuffer) + dbr_grLong_size;
327+ }
328+ if ((*options) & (DBR_GR_DOUBLE)) {
329+ char *pbuffer = *ppbuffer;
330+
331+ if (got_data) {
332+ struct dbr_grDouble *pgr = (struct dbr_grDouble *)pbuffer;
333+ pgr->upper_disp_limit = grd.upper_disp_limit;
334+ pgr->lower_disp_limit = grd.lower_disp_limit;
335+ } else {
336+ memset(pbuffer, '\0', dbr_grDouble_size);
337+ *options = (*options) ^ DBR_GR_DOUBLE; /*Turn off option */
338+ }
339+ *ppbuffer = ((char *)*ppbuffer) + dbr_grDouble_size;
340+ }
341+ return;
342 }
343
344 static void get_control(DBADDR *paddr, char **ppbuffer,
345- rset *prset,long *options)
346+ rset * prset, long *options)
347 {
348- struct dbr_ctrlDouble ctrld;
349- int got_data=FALSE;
350-
351- ctrld.upper_ctrl_limit = ctrld.lower_ctrl_limit = 0.0;
352- if( prset && prset->get_control_double ) {
353- (*prset->get_control_double)(paddr,&ctrld);
354- got_data=TRUE;
355- }
356- if( (*options) & (DBR_CTRL_LONG) ) {
357- char *pbuffer=*ppbuffer;
358-
359- if(got_data) {
360- struct dbr_ctrlLong *pctrl=(struct dbr_ctrlLong*)pbuffer;
361- pctrl->upper_ctrl_limit = (epicsInt32)ctrld.upper_ctrl_limit;
362- pctrl->lower_ctrl_limit = (epicsInt32)ctrld.lower_ctrl_limit;
363- } else {
364- memset(pbuffer,'\0',dbr_ctrlLong_size);
365- *options = (*options) ^ DBR_CTRL_LONG; /*Turn off option*/
366- }
367- *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlLong_size;
368- }
369- if( (*options) & (DBR_CTRL_DOUBLE) ) {
370- char *pbuffer=*ppbuffer;
371-
372- if(got_data) {
373- struct dbr_ctrlDouble *pctrl=(struct dbr_ctrlDouble*)pbuffer;
374- pctrl->upper_ctrl_limit = ctrld.upper_ctrl_limit;
375- pctrl->lower_ctrl_limit = ctrld.lower_ctrl_limit;
376- } else {
377- memset(pbuffer,'\0',dbr_ctrlDouble_size);
378- *options = (*options) ^ DBR_CTRL_DOUBLE; /*Turn off option*/
379- }
380- *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlDouble_size;
381- }
382- return;
383+ struct dbr_ctrlDouble ctrld;
384+ int got_data = FALSE;
385+
386+ ctrld.upper_ctrl_limit = ctrld.lower_ctrl_limit = 0.0;
387+ if (prset && prset->get_control_double) {
388+ (*prset->get_control_double) (paddr, &ctrld);
389+ got_data = TRUE;
390+ }
391+ if ((*options) & (DBR_CTRL_LONG)) {
392+ char *pbuffer = *ppbuffer;
393+
394+ if (got_data) {
395+ struct dbr_ctrlLong *pctrl = (struct dbr_ctrlLong *)pbuffer;
396+ pctrl->upper_ctrl_limit = (epicsInt32) ctrld.upper_ctrl_limit;
397+ pctrl->lower_ctrl_limit = (epicsInt32) ctrld.lower_ctrl_limit;
398+ } else {
399+ memset(pbuffer, '\0', dbr_ctrlLong_size);
400+ *options = (*options) ^ DBR_CTRL_LONG; /*Turn off option */
401+ }
402+ *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlLong_size;
403+ }
404+ if ((*options) & (DBR_CTRL_DOUBLE)) {
405+ char *pbuffer = *ppbuffer;
406+
407+ if (got_data) {
408+ struct dbr_ctrlDouble *pctrl = (struct dbr_ctrlDouble *)pbuffer;
409+ pctrl->upper_ctrl_limit = ctrld.upper_ctrl_limit;
410+ pctrl->lower_ctrl_limit = ctrld.lower_ctrl_limit;
411+ } else {
412+ memset(pbuffer, '\0', dbr_ctrlDouble_size);
413+ *options = (*options) ^ DBR_CTRL_DOUBLE; /*Turn off option */
414+ }
415+ *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlDouble_size;
416+ }
417+ return;
418 }
419
420 static void get_alarm(DBADDR *paddr, char **ppbuffer,
421- rset *prset, long *options)
422+ rset * prset, long *options)
423 {
424 char *pbuffer = *ppbuffer;
425- struct dbr_alDouble ald = {epicsNAN, epicsNAN, epicsNAN, epicsNAN};
426+ struct dbr_alDouble ald = { epicsNAN, epicsNAN, epicsNAN, epicsNAN };
427 long no_data = TRUE;
428
429 if (prset && prset->get_alarm_double)
430 no_data = prset->get_alarm_double(paddr, &ald);
431
432 if (*options & DBR_AL_LONG) {
433- struct dbr_alLong *pal = (struct dbr_alLong*) pbuffer;
434+ struct dbr_alLong *pal = (struct dbr_alLong *)pbuffer;
435
436- pal->upper_alarm_limit = finite(ald.upper_alarm_limit) ?
437+ pal->upper_alarm_limit = finite(ald.upper_alarm_limit) ?
438 (epicsInt32) ald.upper_alarm_limit : 0;
439 pal->upper_warning_limit = finite(ald.upper_warning_limit) ?
440 (epicsInt32) ald.upper_warning_limit : 0;
441 pal->lower_warning_limit = finite(ald.lower_warning_limit) ?
442 (epicsInt32) ald.lower_warning_limit : 0;
443- pal->lower_alarm_limit = finite(ald.lower_alarm_limit) ?
444+ pal->lower_alarm_limit = finite(ald.lower_alarm_limit) ?
445 (epicsInt32) ald.lower_alarm_limit : 0;
446
447 if (no_data)
448- *options ^= DBR_AL_LONG; /*Turn off option*/
449+ *options ^= DBR_AL_LONG; /*Turn off option */
450
451 *ppbuffer += dbr_alLong_size;
452 }
453 if (*options & DBR_AL_DOUBLE) {
454- struct dbr_alDouble *pal = (struct dbr_alDouble*) pbuffer;
455+ struct dbr_alDouble *pal = (struct dbr_alDouble *)pbuffer;
456
457- pal->upper_alarm_limit = ald.upper_alarm_limit;
458+ pal->upper_alarm_limit = ald.upper_alarm_limit;
459 pal->upper_warning_limit = ald.upper_warning_limit;
460 pal->lower_warning_limit = ald.lower_warning_limit;
461- pal->lower_alarm_limit = ald.lower_alarm_limit;
462+ pal->lower_alarm_limit = ald.lower_alarm_limit;
463
464 if (no_data)
465- *options ^= DBR_AL_DOUBLE; /*Turn off option*/
466+ *options ^= DBR_AL_DOUBLE; /*Turn off option */
467
468 *ppbuffer += dbr_alDouble_size;
469 }
470@@ -330,88 +332,89 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer,
471 * blocks only changing the buffer pointer in a way that does not break alignment.
472 */
473 static void getOptions(DBADDR *paddr, char **poriginal, long *options,
474- void *pflin)
475+ void *pflin)
476 {
477- db_field_log *pfl= (db_field_log *)pflin;
478- rset *prset;
479- short field_type;
480- dbCommon *pcommon;
481- char *pbuffer = *poriginal;
482-
483- if (!pfl || pfl->type == dbfl_type_rec)
484- field_type = paddr->field_type;
485- else
486- field_type = pfl->field_type;
487- prset=dbGetRset(paddr);
488- /* Process options */
489- pcommon = paddr->precord;
490- if( (*options) & DBR_STATUS ) {
491- unsigned short *pushort = (unsigned short *)pbuffer;
492-
493- if (!pfl || pfl->type == dbfl_type_rec) {
494- *pushort++ = pcommon->stat;
495- *pushort++ = pcommon->sevr;
496- } else {
497- *pushort++ = pfl->stat;
498- *pushort++ = pfl->sevr;
499- }
500- *pushort++ = pcommon->acks;
501- *pushort++ = pcommon->ackt;
502- pbuffer = (char *)pushort;
503- }
504- if( (*options) & DBR_UNITS ) {
505- memset(pbuffer,'\0',dbr_units_size);
506- if( prset && prset->get_units ){
507- (*prset->get_units)(paddr, pbuffer);
508- pbuffer[DB_UNITS_SIZE-1] = '\0';
509- } else {
510- *options ^= DBR_UNITS; /*Turn off DBR_UNITS*/
511- }
512- pbuffer += dbr_units_size;
513- }
514- if( (*options) & DBR_PRECISION ) {
515- memset(pbuffer, '\0', dbr_precision_size);
516- if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE)
517- && prset && prset->get_precision ){
518- (*prset->get_precision)(paddr,(long *)pbuffer);
519- } else {
520- *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/
521- }
522- pbuffer += dbr_precision_size;
523- }
524- if( (*options) & DBR_TIME ) {
525- epicsUInt32 *ptime = (epicsUInt32 *)pbuffer;
526-
527- if (!pfl || pfl->type == dbfl_type_rec) {
528- *ptime++ = pcommon->time.secPastEpoch;
529- *ptime++ = pcommon->time.nsec;
530- } else {
531- *ptime++ = pfl->time.secPastEpoch;
532- *ptime++ = pfl->time.nsec;
533- }
534- pbuffer = (char *)ptime;
535- }
536- if( (*options) & DBR_ENUM_STRS )
537- get_enum_strs(paddr, &pbuffer, prset, options);
538- if( (*options) & (DBR_GR_LONG|DBR_GR_DOUBLE ))
539- get_graphics(paddr, &pbuffer, prset, options);
540- if((*options) & (DBR_CTRL_LONG | DBR_CTRL_DOUBLE ))
541- get_control(paddr, &pbuffer, prset, options);
542- if((*options) & (DBR_AL_LONG | DBR_AL_DOUBLE ))
543- get_alarm(paddr, &pbuffer, prset, options);
544- *poriginal = pbuffer;
545+ db_field_log *pfl = (db_field_log *) pflin;
546+ rset *prset;
547+ short field_type;
548+ dbCommon *pcommon;
549+ char *pbuffer = *poriginal;
550+
551+ if (!pfl)
552+ field_type = paddr->field_type;
553+ else
554+ field_type = pfl->field_type;
555+ prset = dbGetRset(paddr);
556+ /* Process options */
557+ pcommon = paddr->precord;
558+ if ((*options) & DBR_STATUS) {
559+ unsigned short *pushort = (unsigned short *)pbuffer;
560+
561+ if (!pfl) {
562+ *pushort++ = pcommon->stat;
563+ *pushort++ = pcommon->sevr;
564+ } else {
565+ *pushort++ = pfl->stat;
566+ *pushort++ = pfl->sevr;
567+ }
568+ *pushort++ = pcommon->acks;
569+ *pushort++ = pcommon->ackt;
570+ pbuffer = (char *)pushort;
571+ }
572+ if ((*options) & DBR_UNITS) {
573+ memset(pbuffer, '\0', dbr_units_size);
574+ if (prset && prset->get_units) {
575+ (*prset->get_units) (paddr, pbuffer);
576+ pbuffer[DB_UNITS_SIZE - 1] = '\0';
577+ } else {
578+ *options ^= DBR_UNITS; /*Turn off DBR_UNITS */
579+ }
580+ pbuffer += dbr_units_size;
581+ }
582+ if ((*options) & DBR_PRECISION) {
583+ memset(pbuffer, '\0', dbr_precision_size);
584+ if ((field_type == DBF_FLOAT || field_type == DBF_DOUBLE)
585+ && prset && prset->get_precision) {
586+ (*prset->get_precision) (paddr, (long *)pbuffer);
587+ } else {
588+ *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION */
589+ }
590+ pbuffer += dbr_precision_size;
591+ }
592+ if ((*options) & DBR_TIME) {
593+ epicsUInt32 *ptime = (epicsUInt32 *) pbuffer;
594+
595+ if (!pfl) {
596+ *ptime++ = pcommon->time.secPastEpoch;
597+ *ptime++ = pcommon->time.nsec;
598+ } else {
599+ *ptime++ = pfl->time.secPastEpoch;
600+ *ptime++ = pfl->time.nsec;
601+ }
602+ pbuffer = (char *)ptime;
603+ }
604+ if ((*options) & DBR_ENUM_STRS)
605+ get_enum_strs(paddr, &pbuffer, prset, options);
606+ if ((*options) & (DBR_GR_LONG | DBR_GR_DOUBLE))
607+ get_graphics(paddr, &pbuffer, prset, options);
608+ if ((*options) & (DBR_CTRL_LONG | DBR_CTRL_DOUBLE))
609+ get_control(paddr, &pbuffer, prset, options);
610+ if ((*options) & (DBR_AL_LONG | DBR_AL_DOUBLE))
611+ get_alarm(paddr, &pbuffer, prset, options);
612+ *poriginal = pbuffer;
613 }
614
615-rset * dbGetRset(const struct dbAddr *paddr)
616+rset *dbGetRset(const struct dbAddr *paddr)
617 {
618- struct dbFldDes *pfldDes = paddr->pfldDes;
619+ struct dbFldDes *pfldDes = paddr->pfldDes;
620
621- if(!pfldDes) return(0);
622- return(pfldDes->pdbRecordType->prset);
623+ if (!pfldDes)
624+ return (0);
625+ return (pfldDes->pdbRecordType->prset);
626 }
627
628-long dbPutAttribute(
629- const char *recordTypename, const char *name, const char *value)
630+long dbPutAttribute(const char *recordTypename, const char *name,
631+ const char *value)
632 {
633 DBENTRY dbEntry;
634 DBENTRY *pdbEntry = &dbEntry;
635@@ -430,7 +433,7 @@ long dbPutAttribute(
636 if (!status)
637 status = dbPutRecordAttribute(pdbEntry, name, value);
638 dbFinishEntry(pdbEntry);
639-done:
640+ done:
641 if (status)
642 errMessage(status, "dbPutAttribute failure");
643 return status;
644@@ -458,7 +461,7 @@ int dbGetFieldIndex(const struct dbAddr *paddr)
645 * 5. Run the process routine specific to the record type.
646 * 6. Check to see if record contents should be automatically printed.
647 */
648-long dbProcess(dbCommon *precord)
649+long dbProcess(dbCommon * precord)
650 {
651 rset *prset = precord->rset;
652 dbRecordType *pdbRecordType = precord->rdes;
653@@ -466,7 +469,7 @@ long dbProcess(dbCommon *precord)
654 char context[40] = "";
655 long status = 0;
656 int *ptrace;
657- int set_trace = FALSE;
658+ int set_trace = FALSE;
659 dbFldDes *pdbFldDes;
660 int callNotifyCompletion = FALSE;
661
662@@ -492,7 +495,7 @@ long dbProcess(dbCommon *precord)
663 goto all_done;
664 }
665
666- /* check for trace processing*/
667+ /* check for trace processing */
668 if (tpro) {
669 if (!*ptrace) {
670 *ptrace = 1;
671@@ -519,19 +522,17 @@ long dbProcess(dbCommon *precord)
672
673 /* raise scan alarm after MAX_LOCK times */
674 if ((precord->stat == SCAN_ALARM) ||
675- (precord->lcnt++ < MAX_LOCK) ||
676- (precord->sevr >= INVALID_ALARM)) goto all_done;
677+ (precord->lcnt++ < MAX_LOCK) || (precord->sevr >= INVALID_ALARM))
678+ goto all_done;
679
680 recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM);
681 monitor_mask = recGblResetAlarms(precord);
682- monitor_mask |= DBE_VALUE|DBE_LOG;
683+ monitor_mask |= DBE_VALUE | DBE_LOG;
684 pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
685 db_post_events(precord,
686- (void *)(((char *)precord) + pdbFldDes->offset),
687- monitor_mask);
688+ (void *)(((char *)precord) + pdbFldDes->offset), monitor_mask);
689 goto all_done;
690- }
691- else
692+ } else
693 precord->lcnt = 0;
694
695 /*
696@@ -547,7 +548,7 @@ long dbProcess(dbCommon *precord)
697 printf("%s: dbProcess of Disabled '%s'\n",
698 context, precord->name);
699
700- /*take care of caching and notifyCompletion*/
701+ /*take care of caching and notifyCompletion */
702 precord->rpro = FALSE;
703 precord->putf = FALSE;
704 callNotifyCompletion = TRUE;
705@@ -564,8 +565,8 @@ long dbProcess(dbCommon *precord)
706 db_post_events(precord, &precord->sevr, DBE_VALUE);
707 pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes];
708 db_post_events(precord,
709- (void *)(((char *)precord) + pdbFldDes->offset),
710- DBE_VALUE|DBE_ALARM);
711+ (void *)(((char *)precord) + pdbFldDes->offset),
712+ DBE_VALUE | DBE_ALARM);
713 goto all_done;
714 }
715
716@@ -573,7 +574,7 @@ long dbProcess(dbCommon *precord)
717 /* FIXME: put this in iocInit() !!! */
718 if (!prset || !prset->process) {
719 callNotifyCompletion = TRUE;
720- precord->pact = 1;/*set pact so error is issued only once*/
721+ precord->pact = 1; /*set pact so error is issued only once */
722 recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess");
723 status = S_db_noRSET;
724 if (*ptrace)
725@@ -592,7 +593,7 @@ long dbProcess(dbCommon *precord)
726 dbPrint(precord);
727 }
728
729-all_done:
730+ all_done:
731 if (set_trace)
732 *ptrace = 0;
733 if (callNotifyCompletion && precord->ppn)
734@@ -601,7 +602,7 @@ all_done:
735 return status;
736 }
737
738-long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
739+long dbEntryToAddr(const DBENTRY * pdbentry, DBADDR *paddr)
740 {
741 dbFldDes *pflddes = pdbentry->pflddes;
742 short dbfType = pflddes->field_type;
743@@ -610,9 +611,9 @@ long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
744 paddr->pfield = pdbentry->pfield;
745 paddr->pfldDes = pflddes;
746 paddr->no_elements = 1;
747- paddr->field_type = dbfType;
748- paddr->field_size = pflddes->size;
749- paddr->special = pflddes->special;
750+ paddr->field_type = dbfType;
751+ paddr->field_size = pflddes->size;
752+ paddr->special = pflddes->special;
753 paddr->dbr_field_type = mapDBFToDBR[dbfType];
754
755 if (paddr->special == SPC_DBADDR) {
756@@ -643,16 +644,20 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
757
758 dbInitEntry(pdbbase, &dbEntry);
759 status = dbFindRecordPart(&dbEntry, &pname);
760- if (status) goto finish;
761+ if (status)
762+ goto finish;
763
764- if (*pname == '.') ++pname;
765+ if (*pname == '.')
766+ ++pname;
767 status = dbFindFieldPart(&dbEntry, &pname);
768 if (status == S_dbLib_fieldNotFound)
769 status = dbGetAttributePart(&dbEntry, &pname);
770- if (status) goto finish;
771+ if (status)
772+ goto finish;
773
774 status = dbEntryToAddr(&dbEntry, paddr);
775- if (status) goto finish;
776+ if (status)
777+ goto finish;
778
779 /* Handle field modifiers */
780 if (*pname++ == '$') {
781@@ -664,29 +669,27 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
782 paddr->field_type = DBF_CHAR;
783 paddr->field_size = 1;
784 paddr->dbr_field_type = DBR_CHAR;
785- }
786- else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
787+ } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
788 /* Clients see a char array, but keep original dbfType */
789 paddr->no_elements = PVLINK_STRINGSZ;
790 paddr->field_size = 1;
791 paddr->dbr_field_type = DBR_CHAR;
792- }
793- else {
794+ } else {
795 status = S_dbLib_fieldNotFound;
796 }
797 }
798
799-finish:
800+ finish:
801 dbFinishEntry(&dbEntry);
802 return status;
803 }
804
805-void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
806+void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY * pdbentry)
807 {
808 struct dbCommon *prec = paddr->precord;
809 dbCommonPvt *ppvt = dbRec2Pvt(prec);
810
811- memset((char *)pdbentry,'\0',sizeof(DBENTRY));
812+ memset((char *)pdbentry, '\0', sizeof(DBENTRY));
813
814 pdbentry->pdbbase = pdbbase;
815 pdbentry->precordType = prec->rdes;
816@@ -696,24 +699,24 @@ void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
817 pdbentry->indfield = paddr->pfldDes->indRecordType;
818 }
819
820-void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
821+void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY * pdbentry)
822 {
823 dbCommonPvt *ppvt = dbRec2Pvt(prec);
824
825- memset((char *)pdbentry,'\0',sizeof(DBENTRY));
826+ memset((char *)pdbentry, '\0', sizeof(DBENTRY));
827
828 pdbentry->pdbbase = pdbbase;
829 pdbentry->precordType = prec->rdes;
830 pdbentry->precnode = ppvt->recnode;
831 }
832
833-struct link* dbGetDevLink(struct dbCommon* prec)
834+struct link *dbGetDevLink(struct dbCommon *prec)
835 {
836 DBLINK *plink = 0;
837 DBENTRY entry;
838 dbInitEntryFromRecord(prec, &entry);
839- if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) {
840- plink = (DBLINK*)entry.pfield;
841+ if (dbFindField(&entry, "INP") == 0 || dbFindField(&entry, "OUT") == 0) {
842+ plink = (DBLINK *) entry.pfield;
843 }
844 dbFinishEntry(&entry);
845 return plink;
846@@ -723,41 +726,53 @@ long dbValueSize(short dbr_type)
847 {
848 /* sizes for value associated with each DBR request type */
849 static long size[] = {
850- MAX_STRING_SIZE, /* STRING */
851- sizeof(epicsInt8), /* CHAR */
852- sizeof(epicsUInt8), /* UCHAR */
853- sizeof(epicsInt16), /* SHORT */
854- sizeof(epicsUInt16), /* USHORT */
855- sizeof(epicsInt32), /* LONG */
856- sizeof(epicsUInt32), /* ULONG */
857- sizeof(epicsInt64), /* INT64 */
858- sizeof(epicsUInt64), /* UINT64 */
859- sizeof(epicsFloat32), /* FLOAT */
860- sizeof(epicsFloat64), /* DOUBLE */
861- sizeof(epicsEnum16)}; /* ENUM */
862-
863- return(size[dbr_type]);
864+ MAX_STRING_SIZE, /* STRING */
865+ sizeof(epicsInt8), /* CHAR */
866+ sizeof(epicsUInt8), /* UCHAR */
867+ sizeof(epicsInt16), /* SHORT */
868+ sizeof(epicsUInt16), /* USHORT */
869+ sizeof(epicsInt32), /* LONG */
870+ sizeof(epicsUInt32), /* ULONG */
871+ sizeof(epicsInt64), /* INT64 */
872+ sizeof(epicsUInt64), /* UINT64 */
873+ sizeof(epicsFloat32), /* FLOAT */
874+ sizeof(epicsFloat64), /* DOUBLE */
875+ sizeof(epicsEnum16)
876+ }; /* ENUM */
877+
878+ return (size[dbr_type]);
879 }
880
881-
882 long dbBufferSize(short dbr_type, long options, long no_elements)
883 {
884- long nbytes=0;
885+ long nbytes = 0;
886
887 nbytes += dbValueSize(dbr_type) * no_elements;
888- if (options & DBR_STATUS) nbytes += dbr_status_size;
889- if (options & DBR_UNITS) nbytes += dbr_units_size;
890- if (options & DBR_PRECISION) nbytes += dbr_precision_size;
891- if (options & DBR_TIME) nbytes += dbr_time_size;
892- if (options & DBR_ENUM_STRS) nbytes += dbr_enumStrs_size;
893- if (options & DBR_GR_LONG) nbytes += dbr_grLong_size;
894- if (options & DBR_GR_DOUBLE) nbytes += dbr_grDouble_size;
895- if (options & DBR_CTRL_LONG) nbytes += dbr_ctrlLong_size;
896- if (options & DBR_CTRL_DOUBLE) nbytes += dbr_ctrlDouble_size;
897- if (options & DBR_AL_LONG) nbytes += dbr_alLong_size;
898- if (options & DBR_AL_DOUBLE) nbytes += dbr_alDouble_size;
899- return(nbytes);
900+ if (options & DBR_STATUS)
901+ nbytes += dbr_status_size;
902+ if (options & DBR_UNITS)
903+ nbytes += dbr_units_size;
904+ if (options & DBR_PRECISION)
905+ nbytes += dbr_precision_size;
906+ if (options & DBR_TIME)
907+ nbytes += dbr_time_size;
908+ if (options & DBR_ENUM_STRS)
909+ nbytes += dbr_enumStrs_size;
910+ if (options & DBR_GR_LONG)
911+ nbytes += dbr_grLong_size;
912+ if (options & DBR_GR_DOUBLE)
913+ nbytes += dbr_grDouble_size;
914+ if (options & DBR_CTRL_LONG)
915+ nbytes += dbr_ctrlLong_size;
916+ if (options & DBR_CTRL_DOUBLE)
917+ nbytes += dbr_ctrlDouble_size;
918+ if (options & DBR_AL_LONG)
919+ nbytes += dbr_alLong_size;
920+ if (options & DBR_AL_DOUBLE)
921+ nbytes += dbr_alDouble_size;
922+ return (nbytes);
923 }
924+
925 int dbLoadDatabase(const char *file, const char *path, const char *subs)
926 {
927 if (!file) {
928@@ -767,7 +782,7 @@ int dbLoadDatabase(const char *file, const char *path, const char *subs)
929 return dbReadDatabase(&pdbbase, file, path, subs);
930 }
931
932-int dbLoadRecords(const char* file, const char* subs)
933+int dbLoadRecords(const char *file, const char *subs)
934 {
935 int status;
936
937@@ -781,7 +796,6 @@ int dbLoadRecords(const char* file, const char* subs)
938 return status;
939 }
940
941-
942 static long getLinkValue(DBADDR *paddr, short dbrType,
943 char *pbuf, long *nRequest)
944 {
945@@ -797,7 +811,7 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
946 * valid DBADDR, so no point to check again.
947 * Request for zero elements always succeeds
948 */
949- if(!nReq)
950+ if (!nReq)
951 return 0;
952
953 switch (dbrType) {
954@@ -806,8 +820,9 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
955 nReq = 1;
956 break;
957
958- case DBR_DOUBLE: /* Needed for dbCa links */
959- if (nRequest) *nRequest = 1;
960+ case DBR_DOUBLE: /* Needed for dbCa links */
961+ if (nRequest)
962+ *nRequest = 1;
963 *(double *)pbuf = epicsNAN;
964 return 0;
965
966@@ -821,27 +836,30 @@ static long getLinkValue(DBADDR *paddr, short dbrType,
967
968 dbInitEntry(pdbbase, &dbEntry);
969 status = dbFindRecord(&dbEntry, precord->name);
970- if (!status) status = dbFindField(&dbEntry, pfldDes->name);
971+ if (!status)
972+ status = dbFindField(&dbEntry, pfldDes->name);
973 if (!status) {
974 const char *rtnString = dbGetString(&dbEntry);
975
976- strncpy(pbuf, rtnString, maxlen-1);
977- pbuf[maxlen-1] = 0;
978- if(dbrType!=DBR_STRING)
979- nReq = strlen(pbuf)+1;
980- if(nRequest) *nRequest = nReq;
981+ strncpy(pbuf, rtnString, maxlen - 1);
982+ pbuf[maxlen - 1] = 0;
983+ if (dbrType != DBR_STRING)
984+ nReq = strlen(pbuf) + 1;
985+ if (nRequest)
986+ *nRequest = nReq;
987 }
988 dbFinishEntry(&dbEntry);
989 return status;
990 }
991
992 static long getAttrValue(DBADDR *paddr, short dbrType,
993- char *pbuf, long *nRequest)
994+ char *pbuf, long *nRequest)
995 {
996 int maxlen;
997 long nReq = nRequest ? *nRequest : 1;
998
999- if (!paddr->pfield) return S_db_badField;
1000+ if (!paddr->pfield)
1001+ return S_db_badField;
1002
1003 switch (dbrType) {
1004 case DBR_STRING:
1005@@ -859,22 +877,23 @@ static long getAttrValue(DBADDR *paddr, short dbrType,
1006 return S_db_badDbrtype;
1007 }
1008
1009- strncpy(pbuf, paddr->pfield, maxlen-1);
1010- pbuf[maxlen-1] = 0;
1011- if(dbrType!=DBR_STRING)
1012- nReq = strlen(pbuf)+1;
1013- if(nRequest) *nRequest = nReq;
1014+ strncpy(pbuf, paddr->pfield, maxlen - 1);
1015+ pbuf[maxlen - 1] = 0;
1016+ if (dbrType != DBR_STRING)
1017+ nReq = strlen(pbuf) + 1;
1018+ if (nRequest)
1019+ *nRequest = nReq;
1020 return 0;
1021 }
1022
1023-long dbGetField(DBADDR *paddr,short dbrType,
1024- void *pbuffer, long *options, long *nRequest, void *pflin)
1025+long dbGetField(DBADDR *paddr, short dbrType,
1026+ void *pbuffer, long *options, long *nRequest)
1027 {
1028 dbCommon *precord = paddr->precord;
1029 long status = 0;
1030
1031 dbScanLock(precord);
1032- status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin);
1033+ status = dbGet(paddr, dbrType, pbuffer, options, nRequest, NULL);
1034 dbScanUnlock(precord);
1035 return status;
1036 }
1037@@ -884,7 +903,7 @@ long dbGet(DBADDR *paddr, short dbrType,
1038 {
1039 char *pbuf = pbuffer;
1040 void *pfieldsave = paddr->pfield;
1041- db_field_log *pfl = (db_field_log *)pflin;
1042+ db_field_log *pfl = (db_field_log *) pflin;
1043 short field_type;
1044 long capacity, no_elements, offset;
1045 rset *prset;
1046@@ -895,7 +914,7 @@ long dbGet(DBADDR *paddr, short dbrType,
1047 if (nRequest && *nRequest == 0)
1048 return 0;
1049
1050- if (!pfl || pfl->type == dbfl_type_rec) {
1051+ if (!pfl) {
1052 field_type = paddr->field_type;
1053 no_elements = capacity = paddr->no_elements;
1054
1055@@ -903,15 +922,14 @@ long dbGet(DBADDR *paddr, short dbrType,
1056 * may modify paddr->pfield
1057 */
1058 if (paddr->pfldDes->special == SPC_DBADDR &&
1059- (prset = dbGetRset(paddr)) &&
1060- prset->get_array_info) {
1061+ (prset = dbGetRset(paddr)) && prset->get_array_info) {
1062 status = prset->get_array_info(paddr, &no_elements, &offset);
1063 } else
1064 offset = 0;
1065 } else {
1066 field_type = pfl->field_type;
1067 no_elements = capacity = pfl->no_elements;
1068- offset = 0;
1069+ offset = pfl->offset;
1070 }
1071
1072 if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) {
1073@@ -937,19 +955,25 @@ long dbGet(DBADDR *paddr, short dbrType,
1074 if (offset == 0 && (!nRequest || no_elements == 1)) {
1075 if (nRequest)
1076 *nRequest = 1;
1077- if (!pfl || pfl->type == dbfl_type_rec) {
1078+ if (!pfl) {
1079 status = dbFastGetConvertRoutine[field_type][dbrType]
1080 (paddr->pfield, pbuf, paddr);
1081 } else {
1082- DBADDR localAddr = *paddr; /* Structure copy */
1083+ DBADDR localAddr = *paddr; /* Structure copy */
1084+
1085+ if (pfl->no_elements < 1) {
1086+ status = S_db_badField;
1087+ goto done;
1088+ }
1089
1090 localAddr.field_type = pfl->field_type;
1091 localAddr.field_size = pfl->field_size;
1092+ /* not used by dbFastConvert: */
1093 localAddr.no_elements = pfl->no_elements;
1094 if (pfl->type == dbfl_type_val)
1095- localAddr.pfield = (char *) &pfl->u.v.field;
1096+ localAddr.pfield = (char *)&pfl->u.v.field;
1097 else
1098- localAddr.pfield = (char *) pfl->u.r.field;
1099+ localAddr.pfield = (char *)pfl->u.r.field;
1100 status = dbFastGetConvertRoutine[field_type][dbrType]
1101 (localAddr.pfield, pbuf, &localAddr);
1102 }
1103@@ -969,52 +993,55 @@ long dbGet(DBADDR *paddr, short dbrType,
1104 char message[80];
1105
1106 sprintf(message, "dbGet: Missing conversion for [%d][%d]\n",
1107- field_type, dbrType);
1108+ field_type, dbrType);
1109 recGblDbaddrError(S_db_badDbrtype, paddr, message);
1110 status = S_db_badDbrtype;
1111 goto done;
1112 }
1113 /* convert data into the caller's buffer */
1114 if (n <= 0) {
1115- ;/*do nothing*/
1116- } else if (!pfl || pfl->type == dbfl_type_rec) {
1117+ ; /*do nothing */
1118+ } else if (!pfl) {
1119 status = convert(paddr, pbuf, n, capacity, offset);
1120 } else {
1121- DBADDR localAddr = *paddr; /* Structure copy */
1122+ DBADDR localAddr = *paddr; /* Structure copy */
1123
1124 localAddr.field_type = pfl->field_type;
1125 localAddr.field_size = pfl->field_size;
1126+ /* not used by dbConvert, it uses the passed capacity instead: */
1127 localAddr.no_elements = pfl->no_elements;
1128 if (pfl->type == dbfl_type_val)
1129- localAddr.pfield = (char *) &pfl->u.v.field;
1130+ localAddr.pfield = (char *)&pfl->u.v.field;
1131 else
1132- localAddr.pfield = (char *) pfl->u.r.field;
1133+ localAddr.pfield = (char *)pfl->u.r.field;
1134 status = convert(&localAddr, pbuf, n, capacity, offset);
1135 }
1136
1137- if(!status && dbrType==DBF_CHAR && nRequest &&
1138- paddr->pfldDes && paddr->pfldDes->field_type==DBF_STRING)
1139- {
1140+ if (!status && dbrType == DBF_CHAR && nRequest &&
1141+ paddr->pfldDes && paddr->pfldDes->field_type == DBF_STRING) {
1142 /* long string ensure nil and truncate to actual length */
1143 long nReq = *nRequest;
1144- pbuf[nReq-1] = '\0';
1145- *nRequest = strlen(pbuf)+1;
1146+ pbuf[nReq - 1] = '\0';
1147+ *nRequest = strlen(pbuf) + 1;
1148 }
1149 }
1150-done:
1151+ done:
1152 paddr->pfield = pfieldsave;
1153 return status;
1154 }
1155
1156-devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp) {
1157- return (devSup *)ellNth(&prdes->devList, dtyp+1);
1158+devSup *dbDTYPtoDevSup(dbRecordType * prdes, int dtyp)
1159+{
1160+ return (devSup *) ellNth(&prdes->devList, dtyp + 1);
1161 }
1162
1163-devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset) {
1164- devSup *pdevSup = (devSup *)ellFirst(&prdes->devList);
1165+devSup *dbDSETtoDevSup(dbRecordType * prdes, dset * pdset)
1166+{
1167+ devSup *pdevSup = (devSup *) ellFirst(&prdes->devList);
1168 while (pdevSup) {
1169- if (pdset == pdevSup->pdset) return pdevSup;
1170- pdevSup = (devSup *)ellNext(&pdevSup->node);
1171+ if (pdset == pdevSup->pdset)
1172+ return pdevSup;
1173+ pdevSup = (devSup *) ellNext(&pdevSup->node);
1174 }
1175 return NULL;
1176 }
1177@@ -1022,24 +1049,24 @@ devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset) {
1178 static long dbPutFieldLink(DBADDR *paddr,
1179 short dbrType, const void *pbuffer, long nRequest)
1180 {
1181- dbLinkInfo link_info;
1182- DBADDR *pdbaddr = NULL;
1183- dbCommon *precord = paddr->precord;
1184- dbCommon *lockrecs[2];
1185- dbLocker locker;
1186- dbFldDes *pfldDes = paddr->pfldDes;
1187- long special = paddr->special;
1188+ dbLinkInfo link_info;
1189+ dbChannel *chan = NULL;
1190+ dbCommon *precord = paddr->precord;
1191+ dbCommon *lockrecs[2];
1192+ dbLocker locker;
1193+ dbFldDes *pfldDes = paddr->pfldDes;
1194+ long special = paddr->special;
1195 struct link *plink = (struct link *)paddr->pfield;
1196- const char *pstring = (const char *)pbuffer;
1197+ const char *pstring = (const char *)pbuffer;
1198 struct dsxt *old_dsxt = NULL;
1199 dset *new_dset = NULL;
1200 struct dsxt *new_dsxt = NULL;
1201- devSup *new_devsup = NULL;
1202- long status;
1203- int isDevLink;
1204- short scan;
1205+ devSup *new_devsup = NULL;
1206+ long status;
1207+ int isDevLink;
1208+ short scan;
1209
1210- STATIC_ASSERT(DBLOCKER_NALLOC>=2);
1211+ STATIC_ASSERT(DBLOCKER_NALLOC >= 2);
1212
1213 switch (dbrType) {
1214 case DBR_CHAR:
1215@@ -1061,25 +1088,20 @@ static long dbPutFieldLink(DBADDR *paddr,
1216
1217 if (link_info.ltype == PV_LINK &&
1218 (link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) {
1219- DBADDR tempaddr;
1220-
1221- if (dbNameToAddr(link_info.target, &tempaddr)==0) {
1222- /* This will become a DB link. */
1223- pdbaddr = malloc(sizeof(*pdbaddr));
1224- if (!pdbaddr) {
1225- status = S_db_noMemory;
1226- goto cleanup;
1227- }
1228- *pdbaddr = tempaddr; /* struct copy */
1229+ chan = dbChannelCreate(link_info.target);
1230+ if (chan && dbChannelOpen(chan) != 0) {
1231+ errlogPrintf
1232+ ("ERROR: dbPutFieldLink %s.%s=%s: dbChannelOpen() failed\n",
1233+ precord->name, pfldDes->name, link_info.target);
1234+ goto cleanup;
1235 }
1236 }
1237
1238- isDevLink = ellCount(&precord->rdes->devList) > 0 &&
1239- pfldDes->isDevLink;
1240+ isDevLink = ellCount(&precord->rdes->devList) > 0 && pfldDes->isDevLink;
1241
1242 memset(&locker, 0, sizeof(locker));
1243 lockrecs[0] = precord;
1244- lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL;
1245+ lockrecs[1] = chan ? dbChannelRecord(chan) : NULL;
1246 dbLockerPrepare(&locker, lockrecs, 2);
1247
1248 dbScanLockMany(&locker);
1249@@ -1130,17 +1152,19 @@ static long dbPutFieldLink(DBADDR *paddr,
1250
1251 if (dbLinkIsDefined(plink)) {
1252 dbRemoveLink(&locker, plink); /* Clear out old link */
1253- }
1254- else if (!isDevLink) {
1255+ } else if (!isDevLink) {
1256 status = S_db_badHWaddr;
1257 goto restoreScan;
1258 }
1259
1260- if (special) status = dbPutSpecial(paddr, 0);
1261+ if (special)
1262+ status = dbPutSpecial(paddr, 0);
1263
1264- if (!status) status = dbSetLink(plink, &link_info, new_devsup);
1265+ if (!status)
1266+ status = dbSetLink(plink, &link_info, new_devsup);
1267
1268- if (!status && special) status = dbPutSpecial(paddr, 1);
1269+ if (!status && special)
1270+ status = dbPutSpecial(paddr, 1);
1271
1272 if (status) {
1273 if (isDevLink) {
1274@@ -1163,19 +1187,20 @@ static long dbPutFieldLink(DBADDR *paddr,
1275 }
1276 }
1277
1278- switch (plink->type) { /* New link type */
1279+ switch (plink->type) { /* New link type */
1280 case PV_LINK:
1281 case CONSTANT:
1282 case JSON_LINK:
1283- dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
1284+ dbAddLink(&locker, plink, pfldDes->field_type, chan);
1285+ chan = NULL; /* don't clean it up */
1286 break;
1287
1288 case DB_LINK:
1289 case CA_LINK:
1290 case MACRO_LINK:
1291- break; /* should never get here */
1292+ break; /* should never get here */
1293
1294- default: /* Hardware address */
1295+ default: /* Hardware address */
1296 if (!isDevLink) {
1297 status = S_db_badHWaddr;
1298 goto postScanEvent;
1299@@ -1184,19 +1209,20 @@ static long dbPutFieldLink(DBADDR *paddr,
1300 }
1301 db_post_events(precord, plink, DBE_VALUE | DBE_LOG);
1302
1303-restoreScan:
1304- if (isDevLink &&
1305- scan == menuScanI_O_Intr) { /* undo scanDelete() */
1306+ restoreScan:
1307+ if (isDevLink && scan == menuScanI_O_Intr) { /* undo scanDelete() */
1308 precord->scan = scan;
1309 scanAdd(precord);
1310 }
1311-postScanEvent:
1312+ postScanEvent:
1313 if (scan != precord->scan)
1314 db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG);
1315-unlock:
1316+ unlock:
1317 dbScanUnlockMany(&locker);
1318 dbLockerFinalize(&locker);
1319-cleanup:
1320+ cleanup:
1321+ if (chan)
1322+ dbChannelDelete(chan);
1323 free(link_info.target);
1324 return status;
1325 }
1326@@ -1204,16 +1230,16 @@ cleanup:
1327 long dbPutField(DBADDR *paddr, short dbrType,
1328 const void *pbuffer, long nRequest)
1329 {
1330- long status = 0;
1331- long special = paddr->special;
1332- dbFldDes *pfldDes = paddr->pfldDes;
1333- dbCommon *precord = paddr->precord;
1334- short dbfType = paddr->field_type;
1335+ long status = 0;
1336+ long special = paddr->special;
1337+ dbFldDes *pfldDes = paddr->pfldDes;
1338+ dbCommon *precord = paddr->precord;
1339+ short dbfType = paddr->field_type;
1340
1341 if (special == SPC_ATTRIBUTE)
1342 return S_db_noMod;
1343
1344- /*check for putField disabled*/
1345+ /*check for putField disabled */
1346 if (precord->disp && paddr->pfield != &precord->disp)
1347 return S_db_putDisabled;
1348
1349@@ -1225,8 +1251,7 @@ long dbPutField(DBADDR *paddr, short dbrType,
1350 if (status == 0) {
1351 if (paddr->pfield == &precord->proc ||
1352 (pfldDes->process_passive &&
1353- precord->scan == 0 &&
1354- dbrType < DBR_PUT_ACKT)) {
1355+ precord->scan == 0 && dbrType < DBR_PUT_ACKT)) {
1356 if (precord->pact) {
1357 if (dbAccessDebugPUTF && precord->tpro)
1358 printf("%s: dbPutField to Active '%s', setting RPRO=1\n",
1359@@ -1249,11 +1274,11 @@ static long putAckt(DBADDR *paddr, const void *pbuffer, long nRequest,
1360 dbCommon *precord = paddr->precord;
1361 const unsigned short *ptrans = pbuffer;
1362
1363- if (*ptrans == precord->ackt) return 0;
1364+ if (*ptrans == precord->ackt)
1365+ return 0;
1366 precord->ackt = *ptrans;
1367 db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM);
1368- if (!precord->ackt &&
1369- precord->acks > precord->sevr) {
1370+ if (!precord->ackt && precord->acks > precord->sevr) {
1371 precord->acks = precord->sevr;
1372 db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM);
1373 }
1374@@ -1275,14 +1300,13 @@ static long putAcks(DBADDR *paddr, const void *pbuffer, long nRequest,
1375 return 0;
1376 }
1377
1378-long dbPut(DBADDR *paddr, short dbrType,
1379- const void *pbuffer, long nRequest)
1380+long dbPut(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest)
1381 {
1382 dbCommon *precord = paddr->precord;
1383- short field_type = paddr->field_type;
1384- long no_elements = paddr->no_elements;
1385- long special = paddr->special;
1386- void *pfieldsave = paddr->pfield;
1387+ short field_type = paddr->field_type;
1388+ long no_elements = paddr->no_elements;
1389+ long special = paddr->special;
1390+ void *pfieldsave = paddr->pfield;
1391 rset *prset = dbGetRset(paddr);
1392 long status = 0;
1393 long offset;
1394@@ -1306,7 +1330,8 @@ long dbPut(DBADDR *paddr, short dbrType,
1395
1396 if (special) {
1397 status = dbPutSpecial(paddr, 0);
1398- if (status) return status;
1399+ if (status)
1400+ return status;
1401 }
1402
1403 if (paddr->pfldDes->special == SPC_DBADDR &&
1404@@ -1315,18 +1340,19 @@ long dbPut(DBADDR *paddr, short dbrType,
1405
1406 status = prset->get_array_info(paddr, &dummy, &offset);
1407 /* paddr->pfield may be modified */
1408- if (status) goto done;
1409+ if (status)
1410+ goto done;
1411 } else
1412 offset = 0;
1413
1414 if (no_elements <= 1) {
1415- status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer,
1416+ status = dbFastPutConvertRoutine[dbrType][field_type] (pbuffer,
1417 paddr->pfield, paddr);
1418 nRequest = 1;
1419 } else {
1420 if (no_elements < nRequest)
1421 nRequest = no_elements;
1422- status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
1423+ status = dbPutConvertRoutine[dbrType][field_type] (paddr, pbuffer,
1424 nRequest, no_elements, offset);
1425 }
1426
1427@@ -1340,17 +1366,19 @@ long dbPut(DBADDR *paddr, short dbrType,
1428 /* Always do special processing if needed */
1429 if (special) {
1430 long status2 = dbPutSpecial(paddr, 1);
1431- if (status2) goto done;
1432+ if (status2)
1433+ goto done;
1434 }
1435- if (status) goto done;
1436+ if (status)
1437+ goto done;
1438
1439 /* Propagate monitor events for this field, */
1440 /* unless the field is VAL and PP is true. */
1441 pfldDes = paddr->pfldDes;
1442 isValueField = dbIsValueField(pfldDes);
1443- if (isValueField) precord->udf = FALSE;
1444- if (precord->mlis.count &&
1445- !(isValueField && pfldDes->process_passive))
1446+ if (isValueField)
1447+ precord->udf = FALSE;
1448+ if (precord->mlis.count && !(isValueField && pfldDes->process_passive))
1449 db_post_events(precord, pfieldsave, DBE_VALUE | DBE_LOG);
1450 /* If this field is a property (metadata) field,
1451 * then post a property change event (even if the field
1452@@ -1358,7 +1386,7 @@ long dbPut(DBADDR *paddr, short dbrType,
1453 */
1454 if (precord->mlis.count && pfldDes->prop)
1455 db_post_events(precord, NULL, DBE_PROPERTY);
1456-done:
1457+ done:
1458 paddr->pfield = pfieldsave;
1459 return status;
1460 }
1461diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
1462index 805dfd4..4a95989 100644
1463--- a/modules/database/src/ioc/db/dbAccessDefs.h
1464+++ b/modules/database/src/ioc/db/dbAccessDefs.h
1465@@ -243,7 +243,7 @@ epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
1466 epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, dset *pdset);
1467 epicsShareFunc long dbGetField(
1468 struct dbAddr *,short dbrType,void *pbuffer,long *options,
1469- long *nRequest,void *pfl);
1470+ long *nRequest);
1471 epicsShareFunc long dbGet(
1472 struct dbAddr *,short dbrType,void *pbuffer,long *options,
1473 long *nRequest,void *pfl);
1474diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c
1475index bc3c8e4..e361f3b 100644
1476--- a/modules/database/src/ioc/db/dbChannel.c
1477+++ b/modules/database/src/ioc/db/dbChannel.c
1478@@ -49,14 +49,12 @@ typedef struct parseContext {
1479
1480 static void *dbChannelFreeList;
1481 static void *chFilterFreeList;
1482-static void *dbchStringFreeList;
1483
1484 void dbChannelExit(void)
1485 {
1486 freeListCleanup(dbChannelFreeList);
1487 freeListCleanup(chFilterFreeList);
1488- freeListCleanup(dbchStringFreeList);
1489- dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
1490+ dbChannelFreeList = chFilterFreeList = NULL;
1491 }
1492
1493 void dbChannelInit (void)
1494@@ -66,7 +64,7 @@ void dbChannelInit (void)
1495
1496 freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
1497 freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
1498- freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
1499+ db_init_event_freelists();
1500 }
1501
1502 static void chf_value(parseContext *parser, parse_result *presult)
1503@@ -445,28 +443,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn
1504 return status;
1505 }
1506
1507-/* Stolen from dbAccess.c: */
1508-static short mapDBFToDBR[DBF_NTYPES] =
1509- {
1510- /* DBF_STRING => */DBR_STRING,
1511- /* DBF_CHAR => */DBR_CHAR,
1512- /* DBF_UCHAR => */DBR_UCHAR,
1513- /* DBF_SHORT => */DBR_SHORT,
1514- /* DBF_USHORT => */DBR_USHORT,
1515- /* DBF_LONG => */DBR_LONG,
1516- /* DBF_ULONG => */DBR_ULONG,
1517- /* DBF_INT64 => */DBR_INT64,
1518- /* DBF_UINT64 => */DBR_UINT64,
1519- /* DBF_FLOAT => */DBR_FLOAT,
1520- /* DBF_DOUBLE => */DBR_DOUBLE,
1521- /* DBF_ENUM, => */DBR_ENUM,
1522- /* DBF_MENU, => */DBR_ENUM,
1523- /* DBF_DEVICE => */DBR_ENUM,
1524- /* DBF_INLINK => */DBR_STRING,
1525- /* DBF_OUTLINK => */DBR_STRING,
1526- /* DBF_FWDLINK => */DBR_STRING,
1527- /* DBF_NOACCESS => */DBR_NOACCESS };
1528-
1529 dbChannel * dbChannelCreate(const char *name)
1530 {
1531 const char *pname = name;
1532@@ -735,39 +711,6 @@ void dbChannelDelete(dbChannel *chan)
1533 freeListFree(dbChannelFreeList, chan);
1534 }
1535
1536-static void freeArray(db_field_log *pfl) {
1537- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
1538- freeListFree(dbchStringFreeList, pfl->u.r.field);
1539- } else {
1540- free(pfl->u.r.field);
1541- }
1542-}
1543-
1544-void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan)
1545-{
1546- void *p;
1547- struct dbCommon *prec = dbChannelRecord(chan);
1548-
1549- if (pfl->type != dbfl_type_rec) return;
1550-
1551- pfl->type = dbfl_type_ref;
1552- pfl->stat = prec->stat;
1553- pfl->sevr = prec->sevr;
1554- pfl->time = prec->time;
1555- pfl->field_type = chan->addr.field_type;
1556- pfl->no_elements = chan->addr.no_elements;
1557- pfl->field_size = chan->addr.field_size;
1558- pfl->u.r.dtor = freeArray;
1559- pfl->u.r.pvt = pvt;
1560- if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) {
1561- p = freeListCalloc(dbchStringFreeList);
1562- } else {
1563- p = calloc(pfl->no_elements, pfl->field_size);
1564- }
1565- if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL);
1566- pfl->u.r.field = p;
1567-}
1568-
1569 /* FIXME: Do these belong in a different file? */
1570
1571 void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser)
1572diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
1573index fab9c66..6e98dee 100644
1574--- a/modules/database/src/ioc/db/dbChannel.h
1575+++ b/modules/database/src/ioc/db/dbChannel.h
1576@@ -64,8 +64,9 @@ typedef struct dbChannel {
1577 /* Prototype for the channel event function that is called in filter stacks
1578 *
1579 * When invoked the scan lock for the record associated with 'chan' _may_ be locked.
1580- * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying
1581- * data out of the associated record.
1582+ * If pLog->type==dbfl_type_ref and pLog->u.r.dtor is NULL, then dbScanLock() must
1583+ * be called before accessing the data, as this indicates the data is owned by the
1584+ * record.
1585 *
1586 * This function has ownership of the field log pLog, if it wishes to discard
1587 * this update it should free the field log with db_delete_field_log() and
1588@@ -224,7 +225,6 @@ epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, voi
1589 epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
1590 epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
1591 epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len);
1592-epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan);
1593
1594 #ifdef __cplusplus
1595 }
1596diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
1597index f2cb0c5..8677add 100644
1598--- a/modules/database/src/ioc/db/dbDbLink.c
1599+++ b/modules/database/src/ioc/db/dbDbLink.c
1600@@ -60,6 +60,7 @@
1601 #include "dbConvertFast.h"
1602 #include "dbConvert.h"
1603 #include "db_field_log.h"
1604+#include "db_access_routines.h"
1605 #include "dbFldTypes.h"
1606 #include "dbLink.h"
1607 #include "dbLockPvt.h"
1608@@ -73,7 +74,7 @@
1609 #include "recSup.h"
1610 #include "special.h"
1611 #include "dbDbLink.h"
1612-
1613+#include "dbChannel.h"
1614
1615 /***************************** Database Links *****************************/
1616
1617@@ -82,45 +83,51 @@ static lset dbDb_lset;
1618
1619 static long processTarget(dbCommon *psrc, dbCommon *pdst);
1620
1621+#define linkChannel(plink) ((dbChannel *) (plink)->value.pv_link.pvt)
1622+
1623 long dbDbInitLink(struct link *plink, short dbfType)
1624 {
1625- DBADDR dbaddr;
1626 long status;
1627- DBADDR *pdbAddr;
1628+ dbChannel *chan;
1629+ dbCommon *precord;
1630
1631- status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
1632+ chan = dbChannelCreate(plink->value.pv_link.pvname);
1633+ if (!chan)
1634+ return S_db_notFound;
1635+ status = dbChannelOpen(chan);
1636 if (status)
1637 return status;
1638
1639+ precord = dbChannelRecord(chan);
1640+
1641 plink->lset = &dbDb_lset;
1642 plink->type = DB_LINK;
1643- pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
1644- *pdbAddr = dbaddr; /* structure copy */
1645- plink->value.pv_link.pvt = pdbAddr;
1646- ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
1647+ plink->value.pv_link.pvt = chan;
1648+ ellAdd(&precord->bklnk, &plink->value.pv_link.backlinknode);
1649 /* merging into the same lockset is deferred to the caller.
1650 * cf. initPVLinks()
1651 */
1652- dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
1653- assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
1654+ dbLockSetMerge(NULL, plink->precord, precord);
1655+ assert(plink->precord->lset->plockSet == precord->lset->plockSet);
1656 return 0;
1657 }
1658
1659 void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
1660- DBADDR *ptarget)
1661+ dbChannel *chan)
1662 {
1663 plink->lset = &dbDb_lset;
1664 plink->type = DB_LINK;
1665- plink->value.pv_link.pvt = ptarget;
1666- ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
1667+ plink->value.pv_link.pvt = chan;
1668+ ellAdd(&dbChannelRecord(chan)->bklnk, &plink->value.pv_link.backlinknode);
1669
1670 /* target record is already locked in dbPutFieldLink() */
1671- dbLockSetMerge(locker, plink->precord, ptarget->precord);
1672+ dbLockSetMerge(locker, plink->precord, dbChannelRecord(chan));
1673 }
1674
1675 static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
1676 {
1677- DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
1678+ dbChannel *chan = linkChannel(plink);
1679+ dbCommon *precord = dbChannelRecord(chan);
1680
1681 plink->type = PV_LINK;
1682
1683@@ -130,10 +137,10 @@ static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
1684 plink->value.pv_link.getCvt = 0;
1685 plink->value.pv_link.pvlMask = 0;
1686 plink->value.pv_link.lastGetdbrType = 0;
1687- ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
1688- dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
1689+ ellDelete(&precord->bklnk, &plink->value.pv_link.backlinknode);
1690+ dbLockSetSplit(locker, plink->precord, precord);
1691 }
1692- free(pdbAddr);
1693+ dbChannelDelete(chan);
1694 }
1695
1696 static int dbDbIsConnected(const struct link *plink)
1697@@ -143,16 +150,14 @@ static int dbDbIsConnected(const struct link *plink)
1698
1699 static int dbDbGetDBFtype(const struct link *plink)
1700 {
1701- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1702-
1703- return paddr->field_type;
1704+ dbChannel *chan = linkChannel(plink);
1705+ return dbChannelFinalFieldType(chan);
1706 }
1707
1708 static long dbDbGetElements(const struct link *plink, long *nelements)
1709 {
1710- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1711-
1712- *nelements = paddr->no_elements;
1713+ dbChannel *chan = linkChannel(plink);
1714+ *nelements = dbChannelFinalElements(chan);
1715 return 0;
1716 }
1717
1718@@ -160,30 +165,52 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
1719 long *pnRequest)
1720 {
1721 struct pv_link *ppv_link = &plink->value.pv_link;
1722- DBADDR *paddr = ppv_link->pvt;
1723+ dbChannel *chan = linkChannel(plink);
1724+ DBADDR *paddr = &chan->addr;
1725 dbCommon *precord = plink->precord;
1726 long status;
1727
1728 /* scan passive records if link is process passive */
1729 if (ppv_link->pvlMask & pvlOptPP) {
1730- status = dbScanPassive(precord, paddr->precord);
1731+ status = dbScanPassive(precord, dbChannelRecord(chan));
1732 if (status)
1733 return status;
1734 }
1735
1736- if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
1737- status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
1738+ /* If filters are involved in a read, create field log and run filters */
1739+ if (ellCount(&chan->filters)) {
1740+ db_field_log *pfl;
1741+
1742+ /*
1743+ * For the moment, empty arrays are not supported by EPICS.
1744+ * See the remark in src/std/filters/arr.c for details.
1745+ */
1746+ if (dbChannelFinalElements(chan) <= 0) /* empty array request */
1747+ return S_db_badField;
1748+ pfl = db_create_read_log(chan);
1749+ if (!pfl)
1750+ return S_db_noMemory;
1751+ pfl = dbChannelRunPreChain(chan, pfl);
1752+ pfl = dbChannelRunPostChain(chan, pfl);
1753+ status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl);
1754+ db_delete_field_log(pfl);
1755+ if (status)
1756+ return status;
1757+ if (pnRequest && *pnRequest <= 0) /* empty array result */
1758+ return S_db_badField;
1759+ } else if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
1760+ status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr);
1761 } else {
1762- unsigned short dbfType = paddr->field_type;
1763+ unsigned short dbfType = dbChannelFinalFieldType(chan);
1764
1765 if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
1766 return S_db_badDbrtype;
1767
1768- if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
1769- && paddr->special != SPC_DBADDR
1770- && paddr->special != SPC_ATTRIBUTE) {
1771+ if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1)
1772+ && dbChannelSpecial(chan) != SPC_DBADDR
1773+ && dbChannelSpecial(chan) != SPC_ATTRIBUTE) {
1774 ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
1775- status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
1776+ status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr);
1777 } else {
1778 ppv_link->getCvt = NULL;
1779 status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
1780@@ -191,16 +218,18 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
1781 ppv_link->lastGetdbrType = dbrType;
1782 }
1783
1784- if (!status && precord != paddr->precord)
1785+ if (!status && precord != dbChannelRecord(chan))
1786 recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
1787- plink->precord, paddr->precord->stat, paddr->precord->sevr);
1788+ plink->precord,
1789+ dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr);
1790 return status;
1791 }
1792
1793 static long dbDbGetControlLimits(const struct link *plink, double *low,
1794 double *high)
1795 {
1796- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1797+ dbChannel *chan = linkChannel(plink);
1798+ DBADDR *paddr = &chan->addr;
1799 struct buffer {
1800 DBRctrlDouble
1801 double value;
1802@@ -221,7 +250,8 @@ static long dbDbGetControlLimits(const struct link *plink, double *low,
1803 static long dbDbGetGraphicLimits(const struct link *plink, double *low,
1804 double *high)
1805 {
1806- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1807+ dbChannel *chan = linkChannel(plink);
1808+ DBADDR *paddr = &chan->addr;
1809 struct buffer {
1810 DBRgrDouble
1811 double value;
1812@@ -242,7 +272,8 @@ static long dbDbGetGraphicLimits(const struct link *plink, double *low,
1813 static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
1814 double *low, double *high, double *hihi)
1815 {
1816- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1817+ dbChannel *chan = linkChannel(plink);
1818+ DBADDR *paddr = &chan->addr;
1819 struct buffer {
1820 DBRalDouble
1821 double value;
1822@@ -264,7 +295,8 @@ static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
1823
1824 static long dbDbGetPrecision(const struct link *plink, short *precision)
1825 {
1826- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1827+ dbChannel *chan = linkChannel(plink);
1828+ DBADDR *paddr = &chan->addr;
1829 struct buffer {
1830 DBRprecision
1831 double value;
1832@@ -283,7 +315,8 @@ static long dbDbGetPrecision(const struct link *plink, short *precision)
1833
1834 static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
1835 {
1836- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1837+ dbChannel *chan = linkChannel(plink);
1838+ DBADDR *paddr = &chan->addr;
1839 struct buffer {
1840 DBRunits
1841 double value;
1842@@ -303,20 +336,20 @@ static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
1843 static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status,
1844 epicsEnum16 *severity)
1845 {
1846- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1847-
1848+ dbChannel *chan = linkChannel(plink);
1849+ dbCommon *precord = dbChannelRecord(chan);
1850 if (status)
1851- *status = paddr->precord->stat;
1852+ *status = precord->stat;
1853 if (severity)
1854- *severity = paddr->precord->sevr;
1855+ *severity = precord->sevr;
1856 return 0;
1857 }
1858
1859 static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
1860 {
1861- DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1862-
1863- *pstamp = paddr->precord->time;
1864+ dbChannel *chan = linkChannel(plink);
1865+ dbCommon *precord = dbChannelRecord(chan);
1866+ *pstamp = precord->time;
1867 return 0;
1868 }
1869
1870@@ -324,9 +357,10 @@ static long dbDbPutValue(struct link *plink, short dbrType,
1871 const void *pbuffer, long nRequest)
1872 {
1873 struct pv_link *ppv_link = &plink->value.pv_link;
1874+ dbChannel *chan = linkChannel(plink);
1875 struct dbCommon *psrce = plink->precord;
1876- DBADDR *paddr = (DBADDR *) ppv_link->pvt;
1877- dbCommon *pdest = paddr->precord;
1878+ DBADDR *paddr = &chan->addr;
1879+ dbCommon *pdest = dbChannelRecord(chan);
1880 long status = dbPut(paddr, dbrType, pbuffer, nRequest);
1881
1882 recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
1883@@ -334,7 +368,7 @@ static long dbDbPutValue(struct link *plink, short dbrType,
1884 if (status)
1885 return status;
1886
1887- if (paddr->pfield == (void *) &pdest->proc ||
1888+ if (dbChannelField(chan) == (void *) &pdest->proc ||
1889 (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
1890 status = processTarget(psrce, pdest);
1891 }
1892@@ -345,9 +379,8 @@ static long dbDbPutValue(struct link *plink, short dbrType,
1893 static void dbDbScanFwdLink(struct link *plink)
1894 {
1895 dbCommon *precord = plink->precord;
1896- dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
1897-
1898- dbScanPassive(precord, paddr->precord);
1899+ dbChannel *chan = linkChannel(plink);
1900+ dbScanPassive(precord, dbChannelRecord(chan));
1901 }
1902
1903 static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv)
1904diff --git a/modules/database/src/ioc/db/dbDbLink.h b/modules/database/src/ioc/db/dbDbLink.h
1905index c367720..27972d8 100644
1906--- a/modules/database/src/ioc/db/dbDbLink.h
1907+++ b/modules/database/src/ioc/db/dbDbLink.h
1908@@ -26,7 +26,7 @@ struct dbLocker;
1909
1910 epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType);
1911 epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink,
1912- short dbfType, DBADDR *ptarget);
1913+ short dbfType, dbChannel *ptarget);
1914
1915 #ifdef __cplusplus
1916 }
1917diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
1918index 0d96e12..532c2ba 100644
1919--- a/modules/database/src/ioc/db/dbEvent.c
1920+++ b/modules/database/src/ioc/db/dbEvent.c
1921@@ -251,18 +251,15 @@ int dbel ( const char *pname, unsigned level )
1922 }
1923
1924 /*
1925- * DB_INIT_EVENTS()
1926+ * DB_INIT_EVENT_FREELISTS()
1927 *
1928 *
1929- * Initialize the event facility for this task. Must be called at least once
1930- * by each task which uses the db event facility
1931+ * Initialize the free lists used by the event facility.
1932+ * Safe to be called multiple times.
1933 *
1934- * returns: ptr to event user block or NULL if memory can't be allocated
1935 */
1936-dbEventCtx db_init_events (void)
1937+void db_init_event_freelists (void)
1938 {
1939- struct event_user * evUser;
1940-
1941 if (!dbevEventUserFreeList) {
1942 freeListInitPvt(&dbevEventUserFreeList,
1943 sizeof(struct event_user),8);
1944@@ -279,6 +276,22 @@ dbEventCtx db_init_events (void)
1945 freeListInitPvt(&dbevFieldLogFreeList,
1946 sizeof(struct db_field_log),2048);
1947 }
1948+}
1949+
1950+/*
1951+ * DB_INIT_EVENTS()
1952+ *
1953+ *
1954+ * Initialize the event facility for this task. Must be called at least once
1955+ * by each task which uses the db event facility
1956+ *
1957+ * returns: ptr to event user block or NULL if memory can't be allocated
1958+ */
1959+dbEventCtx db_init_events (void)
1960+{
1961+ struct event_user * evUser;
1962+
1963+ db_init_event_freelists();
1964
1965 evUser = (struct event_user *)
1966 freeListCalloc(dbevEventUserFreeList);
1967@@ -654,27 +667,22 @@ int db_post_extra_labor (dbEventCtx ctx)
1968 return DB_EVENT_OK;
1969 }
1970
1971-/*
1972- * DB_CREATE_EVENT_LOG()
1973- *
1974- * NOTE: This assumes that the db scan lock is already applied
1975- * (as it copies data from the record)
1976- */
1977-db_field_log* db_create_event_log (struct evSubscrip *pevent)
1978+static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
1979 {
1980 db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
1981
1982 if (pLog) {
1983- struct dbChannel *chan = pevent->chan;
1984 struct dbCommon *prec = dbChannelRecord(chan);
1985- pLog->ctx = dbfl_context_event;
1986- if (pevent->useValque) {
1987+ pLog->stat = prec->stat;
1988+ pLog->sevr = prec->sevr;
1989+ pLog->time = prec->time;
1990+ pLog->field_type = dbChannelFieldType(chan);
1991+ pLog->field_size = dbChannelFieldSize(chan);
1992+ pLog->no_elements = dbChannelElements(chan);
1993+ pLog->offset = 0;
1994+
1995+ if (use_val) {
1996 pLog->type = dbfl_type_val;
1997- pLog->stat = prec->stat;
1998- pLog->sevr = prec->sevr;
1999- pLog->time = prec->time;
2000- pLog->field_type = dbChannelFieldType(chan);
2001- pLog->no_elements = dbChannelElements(chan);
2002 /*
2003 * use memcpy to avoid a bus error on
2004 * union copy of char in the db at an odd
2005@@ -684,23 +692,62 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent)
2006 dbChannelField(chan),
2007 dbChannelFieldSize(chan));
2008 } else {
2009- pLog->type = dbfl_type_rec;
2010+ rset *prset;
2011+
2012+ pLog->type = dbfl_type_ref;
2013+
2014+ if (dbChannelSpecial(chan) == SPC_DBADDR &&
2015+ (prset = dbGetRset(&chan->addr)) &&
2016+ prset->get_array_info)
2017+ /* This condition implies !use_val, see db_add_event */
2018+ {
2019+ void *pfieldsave = dbChannelField(chan);
2020+ prec = dbChannelRecord(chan);
2021+ prset->get_array_info(&chan->addr, &pLog->no_elements, &pLog->offset);
2022+ /* don't make a copy yet, just reference the field value */
2023+ pLog->u.r.field = dbChannelField(chan);
2024+ dbChannelField(chan) = pfieldsave;
2025+ }
2026+ else {
2027+ /* don't make a copy yet, just reference the field value */
2028+ pLog->u.r.field = dbChannelField(chan);
2029+ }
2030+ /* indicate field value still owned by record */
2031+ pLog->u.r.dtor = NULL;
2032+ /* no private data yet, may be set by a filter */
2033+ pLog->u.r.pvt = NULL;
2034 }
2035 }
2036 return pLog;
2037 }
2038
2039 /*
2040+ * DB_CREATE_EVENT_LOG()
2041+ *
2042+ * NOTE: This assumes that the db scan lock is already applied
2043+ * (as it calls rset->get_array_info)
2044+ */
2045+db_field_log* db_create_event_log (struct evSubscrip *pevent)
2046+{
2047+ db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque);
2048+ if (pLog) {
2049+ pLog->ctx = dbfl_context_event;
2050+ }
2051+ return pLog;
2052+}
2053+
2054+/*
2055 * DB_CREATE_READ_LOG()
2056 *
2057 */
2058 db_field_log* db_create_read_log (struct dbChannel *chan)
2059 {
2060- db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);
2061-
2062+ db_field_log *pLog = db_create_field_log(chan,
2063+ dbChannelElements(chan) == 1 &&
2064+ dbChannelSpecial(chan) != SPC_DBADDR &&
2065+ dbChannelFieldSize(chan) <= sizeof(union native_value));
2066 if (pLog) {
2067 pLog->ctx = dbfl_context_read;
2068- pLog->type = dbfl_type_rec;
2069 }
2070 return pLog;
2071 }
2072@@ -724,20 +771,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
2073 LOCKEVQUE (ev_que);
2074
2075 /*
2076- * if we have an event on the queue and both the last
2077- * event on the queue and the current event are emtpy
2078- * (i.e. of type dbfl_type_rec), simply ignore duplicate
2079- * events (saving empty events serves no purpose)
2080- */
2081- if (pevent->npend > 0u &&
2082- (*pevent->pLastLog)->type == dbfl_type_rec &&
2083- pLog->type == dbfl_type_rec) {
2084- db_delete_field_log(pLog);
2085- UNLOCKEVQUE (ev_que);
2086- return;
2087- }
2088-
2089- /*
2090 * add to task local event que
2091 */
2092
2093diff --git a/modules/database/src/ioc/db/dbEvent.h b/modules/database/src/ioc/db/dbEvent.h
2094index 374e849..175b0e4 100644
2095--- a/modules/database/src/ioc/db/dbEvent.h
2096+++ b/modules/database/src/ioc/db/dbEvent.h
2097@@ -50,6 +50,7 @@ epicsShareFunc int db_post_events (
2098 typedef void * dbEventCtx;
2099
2100 typedef void EXTRALABORFUNC (void *extralabor_arg);
2101+void db_init_event_freelists (void);
2102 epicsShareFunc dbEventCtx db_init_events (void);
2103 epicsShareFunc int db_start_events (
2104 dbEventCtx ctx, const char *taskname, void (*init_func)(void *),
2105diff --git a/modules/database/src/ioc/db/dbExtractArray.c b/modules/database/src/ioc/db/dbExtractArray.c
2106index e16ab4c..f0ab281 100644
2107--- a/modules/database/src/ioc/db/dbExtractArray.c
2108+++ b/modules/database/src/ioc/db/dbExtractArray.c
2109@@ -13,11 +13,12 @@
2110 /*
2111 * Author: Ralph Lange <Ralph.Lange@bessy.de>
2112 *
2113- * based on dbConvert.c
2114+ * based on dbConvert.c, see copyNoConvert
2115 * written by: Bob Dalesio, Marty Kraimer
2116 */
2117
2118 #include <string.h>
2119+#include <assert.h>
2120
2121 #include "epicsTypes.h"
2122
2123@@ -25,61 +26,31 @@
2124 #include "dbAddr.h"
2125 #include "dbExtractArray.h"
2126
2127-void dbExtractArrayFromRec(const dbAddr *paddr, void *pto,
2128- long nRequest, long no_elements, long offset, long increment)
2129+void dbExtractArray(const void *pfrom, void *pto,
2130+ short field_size, short field_type,
2131+ long nRequest, long no_elements, long offset, long increment)
2132 {
2133 char *pdst = (char *) pto;
2134- char *psrc = (char *) paddr->pfield;
2135- long nUpperPart;
2136- int i;
2137- short srcSize = paddr->field_size;
2138- short dstSize = srcSize;
2139- char isString = (paddr->field_type == DBF_STRING);
2140+ const char *psrc = (char *) pfrom;
2141
2142- if (nRequest > no_elements) nRequest = no_elements;
2143- if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE;
2144+ /* assert preconditions */
2145+ assert(nRequest >= 0);
2146+ assert(no_elements >= 0);
2147+ assert(increment > 0);
2148+ assert(0 <= offset);
2149+ assert(offset < no_elements);
2150
2151- if (increment == 1 && dstSize == srcSize) {
2152- nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset;
2153- memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart);
2154+ if (increment == 1) {
2155+ long nUpperPart =
2156+ nRequest < no_elements - offset ? nRequest : no_elements - offset;
2157+ memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart);
2158 if (nRequest > nUpperPart)
2159- memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart));
2160- if (isString)
2161- for (i = 1; i <= nRequest; i++)
2162- pdst[dstSize*i-1] = '\0';
2163+ memcpy(pdst + (field_size * nUpperPart), psrc,
2164+ field_size * (nRequest - nUpperPart));
2165 } else {
2166- for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) {
2167+ for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) {
2168 offset %= no_elements;
2169- memcpy(pdst, &psrc[offset*srcSize], dstSize);
2170- if (isString) pdst[dstSize-1] = '\0';
2171- }
2172- }
2173-}
2174-
2175-void dbExtractArrayFromBuf(const void *pfrom, void *pto,
2176- short field_size, short field_type,
2177- long nRequest, long no_elements, long offset, long increment)
2178-{
2179- char *pdst = (char *) pto;
2180- char *psrc = (char *) pfrom;
2181- int i;
2182- short srcSize = field_size;
2183- short dstSize = srcSize;
2184- char isString = (field_type == DBF_STRING);
2185-
2186- if (nRequest > no_elements) nRequest = no_elements;
2187- if (offset > no_elements - 1) offset = no_elements - 1;
2188- if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1;
2189-
2190- if (increment == 1) {
2191- memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest);
2192- if (isString)
2193- for (i = 1; i <= nRequest; i++)
2194- pdst[dstSize*i] = '\0';
2195- } else {
2196- for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) {
2197- memcpy(pdst, &psrc[offset*srcSize], dstSize);
2198- if (isString) pdst[dstSize] = '\0';
2199+ memcpy(pdst, psrc + (offset * field_size), field_size);
2200 }
2201 }
2202 }
2203diff --git a/modules/database/src/ioc/db/dbExtractArray.h b/modules/database/src/ioc/db/dbExtractArray.h
2204index 7ed3584..9680660 100644
2205--- a/modules/database/src/ioc/db/dbExtractArray.h
2206+++ b/modules/database/src/ioc/db/dbExtractArray.h
2207@@ -21,11 +21,22 @@
2208 extern "C" {
2209 #endif
2210
2211-epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto,
2212- long nRequest, long no_elements, long offset, long increment);
2213-epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto,
2214- short field_size, short field_type,
2215- long nRequest, long no_elements, long offset, long increment);
2216+/*
2217+ * This function does not do any conversion. This means we don't have to
2218+ * truncate the field_size to MAX_STRING_SIZE or add extra null terminators
2219+ * for string values. All of this is done by the dbConvert routines which
2220+ * will be called whether or not a filter is active.
2221+ *
2222+ * Checked preconditions:
2223+ * - nRequest >= 0, no_elements >= 0, increment > 0
2224+ * - 0 <= offset < no_elements
2225+ * Unchecked preconditions:
2226+ * - pto points to a buffer with at least field_size*nRequest bytes
2227+ * - pfrom points to a buffer with at least field_size*no_elements bytes
2228+ */
2229+epicsShareFunc void dbExtractArray(const void *pfrom, void *pto,
2230+ short field_size, short field_type,
2231+ long nRequest, long no_elements, long offset, long increment);
2232
2233 #ifdef __cplusplus
2234 }
2235diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c
2236index 7c37058..aa72dc8 100644
2237--- a/modules/database/src/ioc/db/dbLink.c
2238+++ b/modules/database/src/ioc/db/dbLink.c
2239@@ -143,7 +143,7 @@ void dbInitLink(struct link *plink, short dbfType)
2240 }
2241
2242 void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
2243- DBADDR *ptarget)
2244+ dbChannel *ptarget)
2245 {
2246 struct dbCommon *precord = plink->precord;
2247
2248diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h
2249index 94e1f32..4255a56 100644
2250--- a/modules/database/src/ioc/db/dbLink.h
2251+++ b/modules/database/src/ioc/db/dbLink.h
2252@@ -20,6 +20,7 @@
2253 #include "epicsTypes.h"
2254 #include "epicsTime.h"
2255 #include "dbAddr.h"
2256+#include "dbChannel.h"
2257
2258 #ifdef __cplusplus
2259 extern "C" {
2260@@ -368,7 +369,7 @@ epicsShareFunc const char * dbLinkFieldName(const struct link *plink);
2261
2262 epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
2263 epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink,
2264- short dbfType, DBADDR *ptarget);
2265+ short dbfType, dbChannel *ptarget);
2266
2267 epicsShareFunc void dbLinkOpen(struct link *plink);
2268 epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
2269diff --git a/modules/database/src/ioc/db/dbLock.c b/modules/database/src/ioc/db/dbLock.c
2270index 8df755b..19cb1c0 100644
2271--- a/modules/database/src/ioc/db/dbLock.c
2272+++ b/modules/database/src/ioc/db/dbLock.c
2273@@ -743,14 +743,14 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond)
2274 for(i=0; i<rtype->no_links; i++) {
2275 dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]];
2276 DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset);
2277- DBADDR *ptarget;
2278+ dbChannel *chan;
2279 lockRecord *lr;
2280
2281 if(plink->type!=DB_LINK)
2282 continue;
2283
2284- ptarget = plink->value.pv_link.pvt;
2285- lr = ptarget->precord->lset;
2286+ chan = plink->value.pv_link.pvt;
2287+ lr = dbChannelRecord(chan)->lset;
2288 assert(lr);
2289
2290 if(lr->precord==pfirst) {
2291diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
2292index 1193954..206d6c9 100644
2293--- a/modules/database/src/ioc/db/dbTest.c
2294+++ b/modules/database/src/ioc/db/dbTest.c
2295@@ -341,14 +341,14 @@ long dbgf(const char *pname)
2296 no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size);
2297 if (addr.dbr_field_type == DBR_ENUM) {
2298 long status = dbGetField(&addr, DBR_STRING, pbuffer,
2299- &options, &no_elements, NULL);
2300+ &options, &no_elements);
2301
2302 printBuffer(status, DBR_STRING, pbuffer, 0L, 0L,
2303 no_elements, &msg_Buff, 10);
2304 }
2305 else {
2306 long status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
2307- &options, &no_elements, NULL);
2308+ &options, &no_elements);
2309
2310 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
2311 no_elements, &msg_Buff, 10);
2312@@ -487,7 +487,7 @@ long dbtgf(const char *pname)
2313 ret_options = req_options;
2314 no_elements = 0;
2315 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
2316- &ret_options, &no_elements, NULL);
2317+ &ret_options, &no_elements);
2318 printBuffer(status, addr.dbr_field_type, pbuffer,
2319 req_options, ret_options, no_elements, pMsgBuff, tab_size);
2320
2321@@ -496,62 +496,62 @@ long dbtgf(const char *pname)
2322
2323 dbr_type = DBR_STRING;
2324 no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
2325- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2326+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2327 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2328
2329 dbr_type = DBR_CHAR;
2330 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
2331- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2332+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2333 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2334
2335 dbr_type = DBR_UCHAR;
2336 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
2337- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2338+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2339 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2340
2341 dbr_type = DBR_SHORT;
2342 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
2343- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2344+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2345 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2346
2347 dbr_type = DBR_USHORT;
2348 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
2349- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2350+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2351 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2352
2353 dbr_type = DBR_LONG;
2354 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
2355- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2356+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2357 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2358
2359 dbr_type = DBR_ULONG;
2360 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
2361- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2362+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2363 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2364
2365 dbr_type = DBR_INT64;
2366 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
2367- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2368+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2369 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2370
2371 dbr_type = DBR_UINT64;
2372 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
2373- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2374+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2375 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2376
2377 dbr_type = DBR_FLOAT;
2378 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
2379- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2380+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2381 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2382
2383 dbr_type = DBR_DOUBLE;
2384 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
2385- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2386+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2387 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2388
2389 dbr_type = DBR_ENUM;
2390 no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
2391- status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
2392+ status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements);
2393 printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
2394
2395 pmsg[0] = '\0';
2396@@ -651,7 +651,7 @@ long dbtpf(const char *pname, const char *pvalue)
2397
2398 printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]);
2399 status = dbGetField(&addr, addr.dbr_field_type, pbuffer,
2400- &options, &no_elements, NULL);
2401+ &options, &no_elements);
2402 printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L,
2403 no_elements, pMsgBuff, tab_size);
2404 }
2405diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c
2406index 6846ef5..458a28b 100644
2407--- a/modules/database/src/ioc/db/dbUnitTest.c
2408+++ b/modules/database/src/ioc/db/dbUnitTest.c
2409@@ -197,7 +197,7 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
2410 return;
2411 }
2412
2413- status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
2414+ status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq);
2415 if (status) {
2416 testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
2417 return;
2418@@ -270,7 +270,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
2419 return;
2420 }
2421
2422- status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL);
2423+ status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest);
2424 if (status) {
2425 testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status);
2426
2427diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
2428index 1534517..4b3b82d 100644
2429--- a/modules/database/src/ioc/db/db_field_log.h
2430+++ b/modules/database/src/ioc/db/db_field_log.h
2431@@ -56,20 +56,31 @@ union native_value {
2432 struct db_field_log;
2433 typedef void (dbfl_freeFunc)(struct db_field_log *pfl);
2434
2435-/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */
2436+/*
2437+ * A db_field_log has one of two types:
2438+ *
2439+ * dbfl_type_ref - Reference to value
2440+ * Used for variable size (array) data types. Meta-data
2441+ * is stored in the field log, but value data is stored externally.
2442+ * Only the dbfl_ref side of the data union is valid.
2443+ *
2444+ * dbfl_type_val - Internal value
2445+ * Used to store small scalar data. Meta-data and value are
2446+ * present in this structure and no external references are used.
2447+ * Only the dbfl_val side of the data union is valid.
2448+ */
2449 typedef enum dbfl_type {
2450- dbfl_type_rec = 0,
2451 dbfl_type_val,
2452 dbfl_type_ref
2453 } dbfl_type;
2454
2455 /* Context of db_field_log: event = subscription update, read = read reply */
2456 typedef enum dbfl_context {
2457- dbfl_context_read = 0,
2458+ dbfl_context_read,
2459 dbfl_context_event
2460 } dbfl_context;
2461
2462-#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref")
2463+#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref")
2464
2465 struct dbfl_val {
2466 union native_value field; /* Field value */
2467@@ -81,6 +92,8 @@ struct dbfl_val {
2468 * db_delete_field_log(). Any code which changes a dbfl_type_ref
2469 * field log to another type, or to reference different data,
2470 * must explicitly call the dtor function.
2471+ * If the dtor is NULL, then this means the array data is still owned
2472+ * by a record.
2473 */
2474 struct dbfl_ref {
2475 dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
2476@@ -88,8 +101,17 @@ struct dbfl_ref {
2477 void *field; /* Field value */
2478 };
2479
2480+/*
2481+ * Note: The offset member is understood to apply an implicit index mapping
2482+ *
2483+ * i' = (i + offset) % no_elements
2484+ *
2485+ * of request index i. The resulting i' is used to to index into u.r.field.
2486+ *
2487+ * Also note that field_size may be larger than MAX_STRING_SIZE.
2488+ */
2489 typedef struct db_field_log {
2490- unsigned int type:2; /* type (union) selector */
2491+ unsigned int type:1; /* type (union) selector */
2492 /* ctx is used for all types */
2493 unsigned int ctx:1; /* context (operation type) */
2494 /* the following are used for value and reference types */
2495@@ -97,37 +119,15 @@ typedef struct db_field_log {
2496 unsigned short stat; /* Alarm Status */
2497 unsigned short sevr; /* Alarm Severity */
2498 short field_type; /* DBF type of data */
2499- short field_size; /* Data size */
2500- long no_elements; /* No of array elements */
2501+ short field_size; /* Size of a single element */
2502+ long no_elements; /* No of valid array elements */
2503+ long offset; /* See above */
2504 union {
2505 struct dbfl_val v;
2506 struct dbfl_ref r;
2507 } u;
2508 } db_field_log;
2509
2510-/*
2511- * A db_field_log will in one of three types:
2512- *
2513- * dbfl_type_rec - Reference to record
2514- * The field log stores no data itself. Data must instead be taken
2515- * via the dbChannel* which must always be provided when along
2516- * with the field log.
2517- * For this type only the 'type' and 'ctx' members are used.
2518- *
2519- * dbfl_type_ref - Reference to outside value
2520- * Used for variable size (array) data types. Meta-data
2521- * is stored in the field log, but value data is stored externally
2522- * (see struct dbfl_ref).
2523- * For this type all meta-data members are used. The dbfl_ref side of the
2524- * data union is used.
2525- *
2526- * dbfl_type_val - Internal value
2527- * Used to store small scalar data. Meta-data and value are
2528- * present in this structure and no external references are used.
2529- * For this type all meta-data members are used. The dbfl_val side of the
2530- * data union is used.
2531- */
2532-
2533 #ifdef __cplusplus
2534 }
2535 #endif
2536diff --git a/modules/database/src/std/dev/devAiSoft.c b/modules/database/src/std/dev/devAiSoft.c
2537index 0ecc1b1..5f9923e 100644
2538--- a/modules/database/src/std/dev/devAiSoft.c
2539+++ b/modules/database/src/std/dev/devAiSoft.c
2540@@ -96,9 +96,10 @@ static long read_ai(aiRecord *prec)
2541
2542 prec->udf = FALSE;
2543 prec->dpvt = &devAiSoft; /* Any non-zero value */
2544+ return 2;
2545 }
2546 else
2547 prec->dpvt = NULL;
2548
2549- return 2;
2550+ return status;
2551 }
2552diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
2553index f91708a..b90335e 100644
2554--- a/modules/database/src/std/filters/arr.c
2555+++ b/modules/database/src/std/filters/arr.c
2556@@ -12,16 +12,13 @@
2557
2558 #include <stdio.h>
2559
2560-#include <freeList.h>
2561-#include <dbAccess.h>
2562-#include <dbExtractArray.h>
2563-#include <db_field_log.h>
2564-#include <dbLock.h>
2565-#include <recSup.h>
2566-#include <epicsExit.h>
2567-#include <special.h>
2568-#include <chfPlugin.h>
2569-#include <epicsExport.h>
2570+#include "chfPlugin.h"
2571+#include "dbExtractArray.h"
2572+#include "db_field_log.h"
2573+#include "dbLock.h"
2574+#include "epicsExit.h"
2575+#include "freeList.h"
2576+#include "epicsExport.h"
2577
2578 typedef struct myStruct {
2579 epicsInt32 start;
2580@@ -45,6 +42,8 @@ static void * allocPvt(void)
2581 myStruct *my = (myStruct*) freeListCalloc(myStructFreeList);
2582 if (!my) return NULL;
2583
2584+ /* defaults */
2585+ my->start = 0;
2586 my->incr = 1;
2587 my->end = -1;
2588 return (void *) my;
2589@@ -93,78 +92,61 @@ static long wrapArrayIndices(long *start, const long increment, long *end,
2590 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
2591 {
2592 myStruct *my = (myStruct*) pvt;
2593- struct dbCommon *prec;
2594- rset *prset;
2595+ int must_lock;
2596 long start = my->start;
2597 long end = my->end;
2598- long nTarget = 0;
2599- long offset = 0;
2600- long nSource = dbChannelElements(chan);
2601- long capacity = nSource;
2602- void *pdst;
2603+ long nTarget;
2604+ void *pTarget;
2605+ /* initial values for the source array */
2606+ long offset = pfl->offset;
2607+ long nSource = pfl->no_elements;
2608
2609 switch (pfl->type) {
2610 case dbfl_type_val:
2611- /* Only filter arrays */
2612+ /* TODO Treat scalars as arrays with 1 element */
2613 break;
2614
2615- case dbfl_type_rec:
2616- /* Extract from record */
2617- if (dbChannelSpecial(chan) == SPC_DBADDR &&
2618- nSource > 1 &&
2619- (prset = dbGetRset(&chan->addr)) &&
2620- prset->get_array_info)
2621- {
2622- void *pfieldsave = dbChannelField(chan);
2623- prec = dbChannelRecord(chan);
2624- dbScanLock(prec);
2625- prset->get_array_info(&chan->addr, &nSource, &offset);
2626- nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
2627- pfl->type = dbfl_type_ref;
2628- pfl->stat = prec->stat;
2629- pfl->sevr = prec->sevr;
2630- pfl->time = prec->time;
2631- pfl->field_type = dbChannelFieldType(chan);
2632- pfl->field_size = dbChannelFieldSize(chan);
2633- pfl->no_elements = nTarget;
2634- if (nTarget) {
2635- pdst = freeListCalloc(my->arrayFreeList);
2636- if (pdst) {
2637- pfl->u.r.dtor = freeArray;
2638- pfl->u.r.pvt = my->arrayFreeList;
2639- offset = (offset + start) % dbChannelElements(chan);
2640- dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity,
2641- offset, my->incr);
2642- pfl->u.r.field = pdst;
2643- }
2644- }
2645- dbScanUnlock(prec);
2646- dbChannelField(chan) = pfieldsave;
2647- }
2648- break;
2649-
2650- /* Extract from buffer */
2651 case dbfl_type_ref:
2652- pdst = NULL;
2653- nSource = pfl->no_elements;
2654+ must_lock = !pfl->u.r.dtor;
2655+ if (must_lock)
2656+ dbScanLock(dbChannelRecord(chan));
2657 nTarget = wrapArrayIndices(&start, my->incr, &end, nSource);
2658- pfl->no_elements = nTarget;
2659- if (nTarget) {
2660- /* Copy the data out */
2661- void *psrc = pfl->u.r.field;
2662-
2663- pdst = freeListCalloc(my->arrayFreeList);
2664- if (!pdst) break;
2665- offset = start;
2666- dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type,
2667- nTarget, nSource, offset, my->incr);
2668- }
2669- if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
2670- if (nTarget) {
2671+ /*
2672+ * Side note: it would be nice if we could avoid the copying in
2673+ * the case of my->incr==1. This is currently not possible due to
2674+ * the way the offset member is interpreted (namely as shifting the
2675+ * array in a ring-buffer style).
2676+ */
2677+ if (nTarget > 0) {
2678+ /* copy the data */
2679+ void *pSource = pfl->u.r.field;
2680+ pTarget = freeListCalloc(my->arrayFreeList);
2681+ if (!pTarget) break;
2682+ offset = (offset + start) % nSource;
2683+ dbExtractArray(pSource, pTarget, pfl->field_size,
2684+ pfl->field_type, nTarget, nSource, offset, my->incr);
2685+ if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
2686+ pfl->u.r.field = pTarget;
2687 pfl->u.r.dtor = freeArray;
2688 pfl->u.r.pvt = my->arrayFreeList;
2689- pfl->u.r.field = pdst;
2690 }
2691+ /* adjust offset and no_elements to refer to the new pTarget */
2692+ pfl->offset = 0;
2693+ /*
2694+ * Setting pfl->no_elements outside of the "if" clause above is
2695+ * done to make requests fail if nTarget is zero, that is, if all
2696+ * elements selected by the filter are outside the array bounds.
2697+ * TODO:
2698+ * It would be possible to lift this restriction by interpreting
2699+ * a request with *no* number of elements (NULL pointer) as scalar
2700+ * (meaning: fail if we get less than one element); in contrast,
2701+ * a request that explicitly specifies one element would be
2702+ * interpreted as an array request, for which zero elements would
2703+ * be a normal expected result.
2704+ */
2705+ pfl->no_elements = nTarget;
2706+ if (must_lock)
2707+ dbScanUnlock(dbChannelRecord(chan));
2708 break;
2709 }
2710 return pfl;
2711diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c
2712index 5925b0b..56c9f5b 100644
2713--- a/modules/database/src/std/filters/ts.c
2714+++ b/modules/database/src/std/filters/ts.c
2715@@ -11,21 +11,39 @@
2716 */
2717
2718 #include <stdio.h>
2719+#include <stdlib.h>
2720+#include <string.h>
2721
2722-#include <chfPlugin.h>
2723-#include <dbLock.h>
2724-#include <db_field_log.h>
2725-#include <epicsExport.h>
2726+#include "chfPlugin.h"
2727+#include "db_field_log.h"
2728+#include "dbLock.h"
2729+#include "epicsExport.h"
2730+
2731+/*
2732+ * The size of the data is different for each channel, and can even
2733+ * change at runtime, so a freeList doesn't make much sense here.
2734+ */
2735+static void freeArray(db_field_log *pfl) {
2736+ free(pfl->u.r.field);
2737+}
2738
2739 static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
2740 epicsTimeStamp now;
2741 epicsTimeGetCurrent(&now);
2742
2743- /* If string or array, must make a copy (to ensure coherence between time and data) */
2744- if (pfl->type == dbfl_type_rec) {
2745- dbScanLock(dbChannelRecord(chan));
2746- dbChannelMakeArrayCopy(pvt, pfl, chan);
2747- dbScanUnlock(dbChannelRecord(chan));
2748+ /* If reference and not already copied,
2749+ must make a copy (to ensure coherence between time and data) */
2750+ if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
2751+ void *pTarget = calloc(pfl->no_elements, pfl->field_size);
2752+ void *pSource = pfl->u.r.field;
2753+ if (pTarget) {
2754+ dbScanLock(dbChannelRecord(chan));
2755+ memcpy(pTarget, pSource, pfl->field_size * pfl->no_elements);
2756+ pfl->u.r.field = pTarget;
2757+ pfl->u.r.dtor = freeArray;
2758+ pfl->u.r.pvt = pvt;
2759+ dbScanUnlock(dbChannelRecord(chan));
2760+ }
2761 }
2762
2763 pfl->time = now;
2764diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp
2765index 8255fdc..ff74e01 100644
2766--- a/modules/database/test/ioc/db/dbChArrTest.cpp
2767+++ b/modules/database/test/ioc/db/dbChArrTest.cpp
2768@@ -130,7 +130,7 @@ static void check(short dbr_type) {
2769 memset(buf, 0, sizeof(buf)); \
2770 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
2771 pfl = db_create_read_log(pch); \
2772- testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \
2773+ testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \
2774 testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
2775 testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \
2776 if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \
2777@@ -178,6 +178,7 @@ static void check(short dbr_type) {
2778 pfl->field_type = DBF_CHAR; \
2779 pfl->field_size = 1; \
2780 pfl->no_elements = 26; \
2781+ pfl->offset = 0; \
2782 pfl->u.r.dtor = freeArray; \
2783 pfl->u.r.field = epicsStrDup("abcdefghijklmnopqrsstuvwxyz"); \
2784 testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \
2785diff --git a/modules/database/test/std/filters/arrTest.cpp b/modules/database/test/std/filters/arrTest.cpp
2786index 1ec16b3..08ba07b 100644
2787--- a/modules/database/test/std/filters/arrTest.cpp
2788+++ b/modules/database/test/std/filters/arrTest.cpp
2789@@ -56,25 +56,26 @@ const char *server_port = CA_SERVER_PORT;
2790
2791 static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) {
2792 for (int i = 0; i < pfl1->no_elements; i++) {
2793+ int j = (i + pfl1->offset) % pfl1->no_elements;
2794 switch (type) {
2795 case DBR_DOUBLE:
2796- if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
2797+ if (((epicsFloat64*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
2798 testDiag("at index=%d: field log has %g, should be %d",
2799- i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
2800+ i, ((epicsFloat64*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
2801 return 0;
2802 }
2803 break;
2804 case DBR_LONG:
2805- if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) {
2806+ if (((epicsInt32*)pfl1->u.r.field)[j] != ((epicsInt32*)p2)[i]) {
2807 testDiag("at index=%d: field log has %d, should be %d",
2808- i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]);
2809+ i, ((epicsInt32*)pfl1->u.r.field)[j], ((epicsInt32*)p2)[i]);
2810 return 0;
2811 }
2812 break;
2813 case DBR_STRING:
2814- if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) {
2815+ if (strtol(&((const char*)pfl1->u.r.field)[j*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) {
2816 testDiag("at index=%d: field log has '%s', should be '%d'",
2817- i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]);
2818+ i, &((const char*)pfl1->u.r.field)[j*pfl1->field_size], ((epicsInt32*)p2)[i]);
2819 return 0;
2820 }
2821 break;
2822@@ -119,7 +120,7 @@ static void testHead (const char *title, const char *typ = "") {
2823 off = Offset; \
2824 (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \
2825 pfl = db_create_read_log(pch); \
2826- testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \
2827+ testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \
2828 pfl2 = dbChannelRunPostChain(pch, pfl); \
2829 testOk(pfl2 == pfl, "call does not drop or replace field_log"); \
2830 testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \
2831diff --git a/modules/database/test/std/filters/dbndTest.c b/modules/database/test/std/filters/dbndTest.c
2832index 4d70f83..fd4a472 100644
2833--- a/modules/database/test/std/filters/dbndTest.c
2834+++ b/modules/database/test/std/filters/dbndTest.c
2835@@ -129,7 +129,7 @@ MAIN(dbndTest)
2836 dbEventCtx evtctx;
2837 int logsFree, logsFinal;
2838
2839- testPlan(77);
2840+ testPlan(72);
2841
2842 testdbPrepare();
2843
2844@@ -170,12 +170,9 @@ MAIN(dbndTest)
2845 "dbnd has one filter with argument in pre chain");
2846 testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain");
2847
2848- /* Field logs of type ref and rec: pass any update */
2849-
2850- testHead("Field logs of type ref and rec");
2851- fl1.type = dbfl_type_rec;
2852- mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0);
2853+ /* Field logs of type ref: pass any update */
2854
2855+ testHead("Field logs of type ref");
2856 fl1.type = dbfl_type_ref;
2857 mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0);
2858
2859diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile
2860index 8c087b3..3d7cff9 100644
2861--- a/modules/database/test/std/rec/Makefile
2862+++ b/modules/database/test/std/rec/Makefile
2863@@ -156,6 +156,13 @@ asyncproctest_SRCS += asyncproctest_registerRecordDeviceDriver.cpp
2864 TESTFILES += $(COMMON_DIR)/asyncproctest.dbd ../asyncproctest.db
2865 TESTS += asyncproctest
2866
2867+TESTPROD_HOST += linkFilterTest
2868+linkFilterTest_SRCS += linkFilterTest.c
2869+linkFilterTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
2870+testHarness_SRCS += linkFilterTest.c
2871+TESTFILES += ../linkFilterTest.db
2872+TESTS += linkFilterTest
2873+
2874 # dbHeader* is only a compile test
2875 # no need to actually run
2876 TESTPROD += dbHeaderTest
2877diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c
2878new file mode 100644
2879index 0000000..6f38d24
2880--- /dev/null
2881+++ b/modules/database/test/std/rec/linkFilterTest.c
2882@@ -0,0 +1,157 @@
2883+/*************************************************************************\
2884+* Copyright (c) 2020 Dirk Zimoch
2885+* EPICS BASE is distributed subject to a Software License Agreement found
2886+* in file LICENSE that is included with this distribution.
2887+\*************************************************************************/
2888+
2889+#include <string.h>
2890+
2891+#include "dbAccess.h"
2892+#include "devSup.h"
2893+#include "alarm.h"
2894+#include "dbUnitTest.h"
2895+#include "errlog.h"
2896+#include "epicsThread.h"
2897+
2898+#include "longinRecord.h"
2899+
2900+#include "testMain.h"
2901+
2902+void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
2903+
2904+static void startTestIoc(const char *dbfile)
2905+{
2906+ testdbPrepare();
2907+ testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
2908+ recTestIoc_registerRecordDeviceDriver(pdbbase);
2909+ testdbReadDatabase(dbfile, NULL, NULL);
2910+
2911+ eltc(0);
2912+ testIocInitOk();
2913+ eltc(1);
2914+}
2915+
2916+static void expectProcSuccess(const char *rec)
2917+{
2918+ char fieldname[20];
2919+ testDiag("expecting success from %s", rec);
2920+ sprintf(fieldname, "%s.PROC", rec);
2921+ testdbPutFieldOk(fieldname, DBF_LONG, 1);
2922+ sprintf(fieldname, "%s.SEVR", rec);
2923+ testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM);
2924+ sprintf(fieldname, "%s.STAT", rec);
2925+ testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM);
2926+}
2927+
2928+static void expectProcFailure(const char *rec)
2929+{
2930+ char fieldname[20];
2931+ testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, rec);
2932+ sprintf(fieldname, "%s.PROC", rec);
2933+ testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1);
2934+ sprintf(fieldname, "%s.SEVR", rec);
2935+ testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM);
2936+ sprintf(fieldname, "%s.STAT", rec);
2937+ testdbGetFieldEqual(fieldname, DBF_LONG, LINK_ALARM);
2938+}
2939+
2940+static void changeRange(long start, long stop, long step)
2941+{
2942+ char linkstring[60];
2943+ if (step)
2944+ sprintf(linkstring, "src.[%ld:%ld:%ld]", start, step, stop);
2945+ else if (stop)
2946+ sprintf(linkstring, "src.[%ld:%ld]", start, stop);
2947+ else
2948+ sprintf(linkstring, "src.[%ld]", start);
2949+ testDiag("modifying link: %s", linkstring);
2950+ testdbPutFieldOk("ai.INP", DBF_STRING, linkstring);
2951+ testdbPutFieldOk("wf.INP", DBF_STRING, linkstring);
2952+}
2953+
2954+static double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6};
2955+
2956+static void expectRange(long start, long end)
2957+{
2958+ long n = end-start+1;
2959+ testdbGetFieldEqual("ai.VAL", DBF_DOUBLE, buf[start]);
2960+ testdbGetFieldEqual("wf.NORD", DBF_LONG, n);
2961+ testdbGetArrFieldEqual("wf.VAL", DBF_DOUBLE, n+2, n, buf+start);
2962+}
2963+
2964+#if 0
2965+static void expectEmptyArray(void)
2966+{
2967+ /* empty arrays are now allowed at the moment */
2968+ testDiag("expecting empty array");
2969+ testdbGetFieldEqual("wf.NORD", DBF_LONG, 0);
2970+}
2971+#endif
2972+
2973+MAIN(linkFilterTest)
2974+{
2975+ testPlan(98);
2976+ startTestIoc("linkFilterTest.db");
2977+
2978+ testDiag("PINI");
2979+ expectRange(2,4);
2980+
2981+ testDiag("modify range");
2982+ changeRange(3,6,0);
2983+ expectProcSuccess("ai");
2984+ expectProcSuccess("wf");
2985+ expectRange(3,6);
2986+
2987+ testDiag("backward range");
2988+ changeRange(5,3,0);
2989+ expectProcFailure("ai");
2990+ expectProcFailure("wf");
2991+
2992+ testDiag("step 2");
2993+ changeRange(1,6,2);
2994+ expectProcSuccess("ai");
2995+ expectProcSuccess("wf");
2996+ expectRange(10,12);
2997+
2998+ testDiag("range start beyond src.NORD");
2999+ changeRange(8,9,0);
3000+ expectProcFailure("ai");
3001+ expectProcFailure("wf");
3002+
3003+ testDiag("range end beyond src.NORD");
3004+ changeRange(3,9,0);
3005+ expectProcSuccess("ai");
3006+ expectProcSuccess("wf");
3007+ expectRange(3,7); /* clipped range */
3008+
3009+ testDiag("range start beyond src.NELM");
3010+ changeRange(11,12,0);
3011+ expectProcFailure("ai");
3012+ expectProcFailure("wf");
3013+
3014+ testDiag("range end beyond src.NELM");
3015+ changeRange(4,12,0);
3016+ expectProcSuccess("ai");
3017+ expectProcSuccess("wf");
3018+ expectRange(4,7); /* clipped range */
3019+
3020+ testDiag("single value beyond src.NORD");
3021+ changeRange(8,0,0);
3022+ expectProcFailure("ai");
3023+ expectProcFailure("wf");
3024+
3025+ testDiag("single value");
3026+ changeRange(5,0,0);
3027+ expectProcSuccess("ai");
3028+ expectProcSuccess("wf");
3029+ expectRange(5,5);
3030+
3031+ testDiag("single beyond rec.NELM");
3032+ changeRange(12,0,0);
3033+ expectProcFailure("ai");
3034+ expectProcFailure("wf");
3035+
3036+ testIocShutdownOk();
3037+ testdbCleanup();
3038+ return testDone();
3039+}
3040diff --git a/modules/database/test/std/rec/linkFilterTest.db b/modules/database/test/std/rec/linkFilterTest.db
3041new file mode 100644
3042index 0000000..5ee371e
3043--- /dev/null
3044+++ b/modules/database/test/std/rec/linkFilterTest.db
3045@@ -0,0 +1,16 @@
3046+record(waveform, "src") {
3047+ field(NELM, "10")
3048+ field(FTVL, "SHORT")
3049+ field(INP, [1, 2, 3, 4, 5, 6, 7, 8])
3050+}
3051+record(ai, "ai") {
3052+ field(INP, "src.[2]") # expect 3
3053+ field(PINI, "YES")
3054+}
3055+record(waveform, "wf") {
3056+ field(NELM, "5")
3057+ field(FTVL, "DOUBLE")
3058+ field(INP, "src.[2:4]") # expect 3,4,5
3059+ field(PINI, "YES")
3060+}
3061+
3062diff --git a/modules/database/test/std/rec/regressTest.c b/modules/database/test/std/rec/regressTest.c
3063index 6614639..fd4f0a8 100644
3064--- a/modules/database/test/std/rec/regressTest.c
3065+++ b/modules/database/test/std/rec/regressTest.c
3066@@ -132,7 +132,7 @@ void testCADisconn(void)
3067
3068 startRegressTestIoc("badCaLink.db");
3069
3070- testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1);
3071+ testdbPutFieldFail(-1, "ai:disconn.PROC", DBF_LONG, 1);
3072 testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM);
3073 testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM);
3074 }

Subscribers

People subscribed via source and target branches