CMS 3D CMS Logo

MultiAssociation.h
Go to the documentation of this file.
1 #ifndef DataFormats_Common_MultiAssociation_h
2 #define DataFormats_Common_MultiAssociation_h
3 /* \class MultiAssociation
4  *
5  * \author Giovanni Petrucciani, SNS Pisa and CERN PH-CMG
6  *
7  * One-to-Many variant of edm::Association<C> / edm::ValueMap<T>,
8  *
9  * Given a key, it will return a range of iterators within a collection (fast access), or collection by value (slow access mode)
10  *
11  * The range of iterators is handled through boost::sub_range, so it should feel like a collection:
12  * 1) it has a '::const_iterator', a 'begin()' and an 'end()'
13  * 2) it has a 'size()' and an 'empty()'
14  * 3) it has a 'front()', 'back()'
15  * 4) it works as an array (i.e. you can use range[index] to pick an element)
16  * 5) if your MultiAssociation is not const, you can modify the values associated to each key
17  * (but you can't push_back new values for a given key)
18  * ( details at http://www.boost.org/doc/libs/1_37_0/libs/range/doc/utility_class.html#sub_range )
19  *
20  * The collection can be a RefVector<C> (to work a la edm::Association<C>), a std::vector<T> (to work a la ValueMap<T>), a PtrVector<T>...
21  * The collection must be compatible with sub_range and support a few other features. Usually you need:
22  * - that it has a default constructor
23  * - that it has a const_iterator, and "begin() const" returns such const_iterator.
24  * - that it has an iterator, and "begin()" returns such iterator (note: it doesn't have to be writable, it can be const as well)
25  * - that it has a swap method
26  * - that 'begin() + offset' is legal (and fast, otherwise this thing is will be awfully slow)
27  * - that you can push_back on a C the * of a C::const_iterator. Namely this should be legal
28  * <code>
29  * C::const_iterator it = ..something..
30  * C someOtherC = ...
31  * someOtherC.push_back(*it);
32  * </code>
33  *
34  * It can be filled through a FastFiller or a LazyFiller.
35  * FastFiller is probably faster, but has many constraints:
36  * - you must fill only one product id at time
37  * - you must fill items in strict key order
38  * - there is no way to abort a filling operation
39  * Try LazyFiller first, unless you're doing some sort of batch task that satisfies the FastFiller requirements
40  *
41  * It stores:
42  * - for each collection of keys: a product id and an offset
43  * - for each key (even those not mapped to any value): one offset
44  * - for each value: one value
45  * With respect to ValueMap / Association, there is one extra int32 for each key (but we don't store null values)
46  *
47  * Its backbone is given by edm::helper::IndexRangeAssociation, that maps keys to ranges, and is not templated.
48  *
49  */
50 
51 #include <vector>
52 #include <map>
53 #include <memory>
54 #include <boost/utility.hpp>
55 #include <boost/range.hpp>
59 
60 namespace edm {
61  namespace helper {
66 
74  public:
75  typedef std::pair<unsigned int, unsigned int> range;
76 
78 
81  template<typename RefKey>
82  range operator[](const RefKey &r) const {
83  return get(r.id(), r.key());
84  }
85 
88  range get(const edm::ProductID & id, unsigned int t) const ;
89 
91  bool contains(ProductID id) const ;
92 
94  unsigned int size() const { return ref_offsets_.empty() ? 0 : ref_offsets_.size() - 1; }
95 
97  bool empty() const { return ref_offsets_.empty(); }
98 
103  class FastFiller : boost::noncopyable {
104  public:
106  FastFiller(IndexRangeAssociation &assoc, ProductID id, unsigned int size) ;
107 
109  ~FastFiller() ;
110 
112  template<typename RefKey>
113  void insert(const RefKey &r, unsigned int startingOffset, unsigned int size) {
114  insert(r.id(), r.key(), startingOffset, size);
115  }
116 
118  void insert(edm::ProductID id, unsigned int key, unsigned int startingOffset, unsigned int size) ;
119  private:
121  const ProductID id_;
122  unsigned int start_, end_; // indices into assoc_.ref_offsets_ (end_ points to the end marker, so it's valid)
124  int lastKey_;
125  }; // FastFiller
126  friend class FastFiller;
127 
129 
130  static void throwUnexpectedProductID(ProductID found, ProductID expected, const char *where) ;
131  private:
132  typedef std::pair<edm::ProductID,unsigned int> id_off_pair;
133  typedef std::vector<id_off_pair> id_offset_vector; // sorted by product id
134  typedef std::vector<int> offset_vector;
135  id_offset_vector id_offsets_;
136  offset_vector ref_offsets_;
137 
138  bool isFilling_; // transient, to check no two fillers exist at the same time
139  struct IDComparator {
140  bool operator()(const id_off_pair &p, const edm::ProductID &id) const { return p.first < id; }
141  }; // IDComparator
142 
143 
144  };
145 
146  // Free swap function
147  inline void swap(IndexRangeAssociation& lhs, IndexRangeAssociation& rhs) { lhs.swap(rhs); }
148 
149  } // Helper Namespace
150 
151  template<typename C>
153  public:
154  typedef C Collection;
155  typedef boost::sub_range<const Collection> const_range;
156  typedef boost::sub_range<Collection> range;
157 
159 
161  template<typename RefKey>
162  const_range operator[](const RefKey &r) const {
163  return get(r.id(), r.key());
164  }
165  // ---- and the non-const sister
167  template<typename RefKey>
168  range operator[](const RefKey &r) {
169  return get(r.id(), r.key());
170  }
171 
173  template<typename RefKey>
174  Collection getValues(const RefKey &r) const {
175  return getValues(r.id(), r.key());
176  }
177 
179  bool contains(const edm::ProductID &id) const { return indices_.contains(id); }
180 
182  const_range get(const edm::ProductID &id, unsigned int t) const ;
183  // ---- and the non-const sister
185  range get(const edm::ProductID &id, unsigned int t) ;
186 
188  Collection getValues(const edm::ProductID &id, unsigned int t) const ;
189 
191  indices_.swap(other.indices_);
192  data_.swap(other.data_);
193  }
194 
196  unsigned int dataSize() const { return data_.size(); }
197 
199  unsigned int size() const { return indices_.size(); }
200 
202  bool empty() const { return indices_.empty(); }
203 
208  class FastFiller {
209  public:
210  template<typename HandleType>
211  FastFiller(MultiAssociation &assoc, const HandleType &handle) :
212  assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, handle.id(), handle->size())) {}
213 
215  assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, id, size)) {}
216 
218 
220  template<typename KeyRef>
221  void setValues(const KeyRef &k, const Collection &refs) { setValues(k.id(), k.key(), refs); }
222 
224  void setValues(const edm::ProductID &id, unsigned int key, const Collection &refs);
225  private:
228  std::shared_ptr<IndexFiller> indexFiller_;
229 
230  }; // FastFiller
231  friend class FastFiller;
232 
233  template<typename HandleType>
234  FastFiller fastFiller(const HandleType &handle) { return FastFiller(*this, handle); }
235 
241  class LazyFiller {
242  public:
243  template<typename HandleType>
244  LazyFiller(MultiAssociation &assoc, const HandleType &handle, bool fillOnExit=false) :
245  assoc_(assoc),
246  id_(handle.id()), size_(handle->size()),
247  tempValues_(new TempValues()), fillOnExit_(fillOnExit) {}
248  ~LazyFiller() noexcept(false) { if (fillOnExit_) fill(); }
249 
254  void fill() noexcept(false);
255 
257  void setFillOnExit(bool fillOnExit) { fillOnExit_ = fillOnExit; }
258 
260  template<typename KeyRef>
261  void setValues(const KeyRef &k, const Collection &refs) ;
262 
265  template<typename KeyRef>
266  void swapValues(const KeyRef &k, Collection &refs) ;
267  private:
268  typedef std::map<unsigned int, Collection> TempValues;
271  unsigned int size_;
272  std::shared_ptr<TempValues> tempValues_;
274  }; // LazyFiller
275  friend class LazyFiller;
276 
277  template<typename HandleType>
278  LazyFiller lazyFiller(const HandleType &h, bool fillOnExit=false) { return LazyFiller(*this, h, fillOnExit); }
279 
280  //Used by ROOT storage
282 
283  private:
285  Indices indices_;
286  Collection data_;
287 
288 }; // class
289 
290  // Free swap function
291  template <typename C>
292  inline void swap(MultiAssociation<C>& lhs, MultiAssociation<C>& rhs) { lhs.swap(rhs); }
293 
294  //============= IMPLEMENTATION OF THE METHODS =============
295  template<typename C>
297  MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) const {
298  Indices::range idxrange = indices_.get(id,key);
299  return const_range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
300  }
301 
302  template<typename C>
304  MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) {
305  Indices::range idxrange = indices_.get(id,key);
306  return range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
307  }
308 
309 
310  template<typename C>
312  MultiAssociation<C>::getValues(const edm::ProductID & id, unsigned int key) const {
313  Collection ret;
314  const_range values = get(id,key);
315  for (typename const_range::const_iterator it = values.begin(), ed = values.end(); it != ed; ++it) {
316  ret.push_back(*it);
317  }
318  return ret;
319  }
320 
321  template<typename C>
322  void MultiAssociation<C>::FastFiller::setValues(const edm::ProductID & id, unsigned int key, const Collection &vals) {
323  indexFiller_->insert(id, key, assoc_.data_.size(), vals.size());
324  for (typename Collection::const_iterator it = vals.begin(), ed = vals.end(); it != ed; ++it) {
325  assoc_.data_.push_back(*it);
326  }
327  }
328 
329  template<typename C>
330  template<typename KeyRef>
331  void MultiAssociation<C>::LazyFiller::setValues(const KeyRef &k, const Collection &vals) {
332  if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");
333  (*tempValues_)[k.key()] = vals;
334  }
335 
336  template<typename C>
337  template<typename KeyRef>
338  void MultiAssociation<C>::LazyFiller::swapValues(const KeyRef &k, Collection &vals) {
339  if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");
340  vals.swap((*tempValues_)[k.key()]);
341  }
342 
343 
344  template<typename C>
346  if (id_ != ProductID()) { // protection against double filling
348  for (typename TempValues::const_iterator it = tempValues_->begin(), ed = tempValues_->end(); it != ed; ++it) {
349  filler.setValues(id_, it->first, it->second);
350  }
351  id_ = ProductID(); // protection against double filling
352  }
353  }
354 
355 
356 } // namespace
357 
358 #endif
unsigned int dataSize() const
Returns the number of values.
static void throwUnexpectedProductID(ProductID found, ProductID expected, const char *where)
boost::sub_range< const Collection > const_range
range operator[](const RefKey &r)
Get a range of values for this key (fast)
FastFiller(MultiAssociation &assoc, edm::ProductID id, unsigned int size)
void swapValues(const KeyRef &k, Collection &refs)
const_range get(const edm::ProductID &id, unsigned int t) const
Get a range of values for this product id and index (fast)
Definition: helper.py:1
Collection getValues(const RefKey &r) const
Get a copy of the values for this key (slow!)
bool empty() const
True if it&#39;s empty (no keys)
LazyFiller(MultiAssociation &assoc, const HandleType &handle, bool fillOnExit=false)
unsigned int size() const
Returns the number of keys.
FastFiller(IndexRangeAssociation &assoc, ProductID id, unsigned int size)
Make a filler for a collection with a given product id and size.
std::map< unsigned int, Collection > TempValues
LazyFiller lazyFiller(const HandleType &h, bool fillOnExit=false)
#define noexcept
#define CMS_CLASS_VERSION(_version_)
Definition: classes.h:31
boost::sub_range< Collection > range
void swap(IndexRangeAssociation &other)
std::shared_ptr< TempValues > tempValues_
edm::helper::IndexRangeAssociation::FastFiller IndexFiller
unsigned int size() const
Size of this collection (number of keys)
int lastKey_
last key used to fill (to check that the new key must be strictly greater than lastKey_) ...
~FastFiller()
When the FastFiller goes out of scope, it unlocks the map so you can make a new one.
range operator[](const RefKey &r) const
def template(fileName, svg, replaceme="REPLACEME")
Definition: svgfig.py:520
bool contains(ProductID id) const
True if this IndexRangeAssociation has info for this product id.
const_range operator[](const RefKey &r) const
Get a range of values for this key (fast)
std::pair< unsigned int, unsigned int > range
void insert(const RefKey &r, unsigned int startingOffset, unsigned int size)
Sets the starting offset for this key.
int k[5][pyjets_maxn]
std::shared_ptr< IndexFiller > indexFiller_
FastFiller fastFiller(const HandleType &handle)
void setFillOnExit(bool fillOnExit)
If set to true, the LazyFiller wil call &#39;fill()&#39; when it goes out of scope.
bool operator()(const id_off_pair &p, const edm::ProductID &id) const
HLT enums.
FastFiller(MultiAssociation &assoc, const HandleType &handle)
void swap(MultiAssociation &other)
bool contains(const edm::ProductID &id) const
True if there are keys from this product id.
bool empty() const
Returns true if there are no keys.
std::pair< edm::ProductID, unsigned int > id_off_pair
std::vector< id_off_pair > id_offset_vector
void setValues(const KeyRef &k, const Collection &refs)
Sets the Collection values associated to this key, making copies of those in refs.
void setValues(const KeyRef &k, const Collection &refs)
Sets the Collection values associated to this key, making copies of those in refs.