This is Unofficial EPICS BASE Doxygen Site
requestmapper.cpp
Go to the documentation of this file.
1 /*
2  * Copyright information and license terms for this software can be
3  * found in the file LICENSE that is included with the distribution
4  */
5 
6 #include <sstream>
7 
8 #include <epicsAssert.h>
9 #include <epicsTypes.h>
10 #include <epicsVersion.h>
11 #include <epicsConvert.h>
12 #include <epicsAssert.h>
13 #include <epicsMutex.h>
14 #include <epicsGuard.h>
15 
16 #define epicsExportSharedSymbols
17 #include <pv/createRequest.h>
18 #include <pv/epicsException.h>
19 #include <pv/bitSet.h>
20 
21 // Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
22 static const unsigned maxDepth = 5;
23 
24 namespace epics{namespace pvData {
25 
27 
29  const PVStructure &pvRequest,
30  mode_t mode)
31 {
32  compute(base, pvRequest, mode);
33 }
34 
36 {
37  if(!typeRequested)
38  THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
39  return typeRequested->build();
40 }
41 
43 {
44  if(!typeBase)
45  THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
46  return typeBase->build();
47 }
48 
50  const PVStructure &pvRequest,
51  mode_t mode)
52 {
53  if(base.getFieldOffset()!=0)
54  THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
55 
56  bool ok = true;
57 
58  // we want to be transactional, which requires a second copy of everything.
59  PVRequestMapper temp;
60 
61  // whether to preserve IDs of partial structures.
62  bool keepids = false;
63  PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
64  try {
65  if(pbp) keepids = pbp->getAs<boolean>();
66  }catch(std::runtime_error& e){
67  std::ostringstream msg;
68  msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
69  temp.messages+=msg.str();
70  }
71 
72  PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
73  if(!fields || fields->getPVFields().empty()) {
74  // not selection, or empty selection, treated as select all
75  temp.typeBase = temp.typeRequested = base.getStructure();
76 
77  for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
78  temp.maskRequested.set(i);
79 
80  } else {
81  FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
82 
83  if(keepids)
84  builder = builder->setId(base.getStructure()->getID());
85 
86  ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
87 
88  temp.typeBase = base.getStructure();
89  temp.typeRequested = builder->createStructure();
90  // possible that typeBase==typeRequested if all fields explicitly selected
91  }
92 
93  if(mode==Mask) {
94  // short circuit use of masked Structure, but keep maskRequested
95  temp.typeRequested = temp.typeBase;
96  }
97 
98  {
99  PVStructurePtr proto(temp.typeRequested->build());
100 
101  // base -> request may be sparce mapping
102  temp.base2req.resize(base.getNextFieldOffset());
103  // request -> base is dense mapping
104  temp.req2base.resize(proto->getNextFieldOffset());
105 
106  // special handling for whole structure mapping. in part because getSubField(0) isn't allowed
107  temp.base2req[0] = Mapping(0, false);
108  temp.req2base[0] = Mapping(0, false);
109 
110  // Iterate prototype of requested to map with base field offsets.
111  // which is handled as a special case below.
112  // We also don't try to prevent redundant copies if both leaf and compress bits are set.
113  for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
114  PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
115  fld_base(base.getSubFieldT(fld_req->getFullName()));
116  const size_t b = fld_base->getFieldOffset();
117 
118  if(!temp.requestedMask().get(b))
119  continue;
120 
121  bool leaf = fld_base->getField()->getType()!=structure;
122 
123  // initialize mapping when our bit is set
124  temp.base2req[b] = Mapping(r, leaf);
125  temp.req2base[r] = Mapping(b, leaf);
126 
127  // add ourself to all "compress" bit mappings of enclosing structures
128  for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
129  temp.req2base[parent->getFieldOffset()].tomask .set(b);
130  temp.req2base[parent->getFieldOffset()].frommask.set(r);
131  }
132 
133  for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
134  temp.base2req[parent->getFieldOffset()].tomask .set(r);
135  temp.base2req[parent->getFieldOffset()].frommask.set(b);
136  }
137  }
138  }
139 
140  temp.maskRequested.set(0);
141 
142  if(temp.maskRequested.nextSetBit(1)==-1) {
143  ok = false;
144  temp.messages+="Empty field selection";
145  }
146 
147  if(!ok)
148  throw std::runtime_error(temp.messages);
149 
150  swap(temp);
151 }
152 
153 bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
154  FieldBuilderPtr& builder, bool keepids, unsigned depth)
155 {
156  bool ok = true;
157  const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
158 
159  for(size_t i=0, N=reqNames.size(); i<N; i++) {
160  // iterate through requested fields
161 
162  PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
163  const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
164 
165  if(subReq->getType()!=structure) {
166  // pvRequest .field was not properly composed
167  std::ostringstream msg;
168  // not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
169  // but getFullName() whould prefix with 'field.', which would probably cause
170  // more frequent confusion...
171  msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
172  messages+=msg.str();
173  ok = false;
174 
175  } else if(!subtype) {
176  // requested field does not actually exist in base
177  std::ostringstream msg;
178  msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
179  messages+=msg.str();
180 
181  } else if(depth>=maxDepth // exceeds max recursion depth
182  || subtype->getField()->getType()!=structure // requested field is a leaf
183  || static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
184  )
185  {
186  // just add the whole thing
187  builder = builder->add(reqNames[i], subtype->getField());
188  for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
189  maskRequested.set(j);
190 
191  if(subtype->getField()->getType()!=structure
192  && !static_cast<const Structure&>(*subReq).getFieldNames().empty())
193  {
194  // attempt to select below a leaf field
195  std::ostringstream msg;
196  msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
197  messages+=msg.str();
198  } else if(depth>=maxDepth) {
199  std::ostringstream msg;
200  msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
201  messages+=msg.str();
202  }
203 
204  } else {
205  // recurse into sub-structure
206  const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
207 
208  builder = builder->addNestedStructure(reqNames[i]);
209  maskRequested.set(substruct.getFieldOffset());
210 
211  if(keepids)
212  builder = builder->setId(substruct.getStructure()->getID());
213 
214  _compute(substruct,
215  static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
216  builder, keepids, depth+1u);
217 
218  builder = builder->endNested();
219  }
220  }
221  return ok;
222 }
223 
225  const PVStructure& base,
226  const BitSet& baseMask,
228  BitSet& requestMask
229 ) const {
230  assert(base.getStructure()==typeBase);
231  assert(request.getStructure()==typeRequested);
232  _map(base, baseMask, request, requestMask, false);
233 }
234 
236  PVStructure& base,
237  BitSet& baseMask,
238  const PVStructure& request,
239  const BitSet& requestMask
240 ) const {
241  assert(base.getStructure()==typeBase);
242  assert(request.getStructure()==typeRequested);
243  _map(request, requestMask, base, baseMask, true);
244 }
245 
246 void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
247  PVStructure& dest, BitSet& maskDest,
248  bool dir_r2b) const
249 {
250  {
251  scratch = maskSrc;
252  const mapping_t& map = dir_r2b ? req2base : base2req;
253 
254  assert(map.size()==src.getNumberFields());
255 
256  for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
257  const Mapping& M = map[i];
258  if(!M.valid) {
259  assert(!dir_r2b); // only base -> requested mapping can have holes
260 
261  } else if(M.leaf) {
262  // just copy
263  dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
264  maskDest.set(M.to);
265 
266  } else {
267  // set bits of all sub-fields (in requested structure)
268  // these indicies are always >i
269  scratch |= M.frommask;
270 
271  // we will also set the individual bits, but if a compress bit is set in the input,
272  // then set the corresponding bit in the output.
273  maskDest.set(M.to);
274  }
275  }
276  }
277 }
278 
279 void PVRequestMapper::_mapMask(const BitSet& maskSrc,
280  BitSet& maskDest,
281  bool dir_r2b) const
282 {
283  if(maskSrc.isEmpty()) {
284  // no-op
285 
286  } else {
287  const mapping_t& map = dir_r2b ? req2base : base2req;
288 
289  for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
290  const Mapping& M = map[i];
291  if(!M.valid) {
292  assert(!dir_r2b); // only base -> requested mapping can have holes
293 
294  } else {
295  maskDest.set(M.to);
296 
297  if(!M.leaf) {
298  maskDest |= M.tomask;
299  }
300  }
301  }
302  }
303 
304 }
305 
307 {
308  typeBase.swap(other.typeBase);
309  typeRequested.swap(other.typeRequested);
310  maskRequested.swap(other.maskRequested);
311  base2req.swap(other.base2req);
312  req2base.swap(other.req2base);
313  messages.swap(other.messages);
314  scratch.swap(other.scratch); // paranoia
315 }
316 
318 {
319  typeBase.reset();
320  typeRequested.reset();
321  maskRequested.clear();
322  base2req.clear();
323  req2base.clear();
324  messages.clear();
325  scratch.clear(); // paranoia
326 }
327 
328 }} //namespace epics::pvData
FORCE_INLINE std::tr1::shared_ptr< PVField > getSubField(A a)
Definition: pvData.h:744
const PVFieldPtrArray & getPVFields() const
Definition: pvData.h:736
void copyBaseToRequested(const PVStructure &base, const BitSet &baseMask, PVStructure &request, BitSet &requestMask) const
PVScalar is the base class for each scalar field.
Definition: pvData.h:272
std::string request
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
BitSet & clear(uint32 bitIndex)
Definition: bitSet.cpp:112
const BitSet & requestedMask() const
bool get(uint32 bitIndex) const
Definition: bitSet.cpp:130
#define THROW_EXCEPTION2(TYPE, MSG)
An EPICS-specific replacement for ANSI C&#39;s assert.
int i
Definition: scan.c:967
void compute(const PVStructure &base, const PVStructure &pvRequest, mode_t mode=Mask)
PVStructurePtr buildRequested() const
TODO only here because of the Lockable.
Definition: ntaggregate.cpp:16
A vector of bits.
Definition: bitSet.h:56
void swap(PVRequestMapper &other)
Exchange contents of two mappers. O(0) and never throws.
std::tr1::shared_ptr< FieldBuilder > FieldBuilderPtr
std::size_t getFieldOffset() const
Definition: PVField.cpp:44
std::string getFullName() const
Definition: PVField.cpp:97
This class implements introspection object for a structure.
Definition: pvIntrospect.h:697
const StructureConstPtr & base() const
APIs for the epicsMutex mutual exclusion semaphore.
int32 nextSetBit(uint32 fromIndex) const
Definition: bitSet.cpp:164
bool isEmpty() const
Definition: bitSet.cpp:199
Data interface for a structure,.
Definition: pvData.h:712
std::tr1::shared_ptr< const Field > FieldConstPtr
Definition: pvIntrospect.h:137
FORCE_INLINE const FieldCreatePtr & getFieldCreate()
std::tr1::shared_ptr< PVStructure > PVStructurePtr
Definition: pvData.h:87
FORCE_INLINE std::tr1::shared_ptr< PVField > getSubFieldT(A a)
Definition: pvData.h:786
std::vector< std::string > StringArray
Definition: pvType.h:110
void swap(BitSet &set)
Swap contents.
Definition: bitSet.cpp:277
PVStructurePtr buildBase() const
const StructureConstPtr & getStructure() const
Definition: pvData.h:731
BitSet & set(uint32 bitIndex)
Definition: bitSet.cpp:103
PVStructure * getParent()
Definition: pvData.h:213
void copyBaseFromRequested(PVStructure &base, BitSet &baseMask, const PVStructure &request, const BitSet &requestMask) const
int32_t int32
Definition: pvType.h:83
std::size_t getNextFieldOffset() const
Definition: PVField.cpp:50
StringArray const & getFieldNames() const
Definition: pvIntrospect.h:828
void reset()
return to state of default ctor
std::size_t getNumberFields() const
Definition: PVField.cpp:56