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