CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_7/src/DataFormats/Common/interface/MultiAssociation.h

Go to the documentation of this file.
00001 #ifndef DataFormats_Common_MultiAssociation_h
00002 #define DataFormats_Common_MultiAssociation_h
00003 /* \class MultiAssociation
00004  *
00005  * \author Giovanni Petrucciani, SNS Pisa and CERN PH-CMG
00006  * 
00007  * One-to-Many variant of edm::Association<C> / edm::ValueMap<T>, 
00008  *
00009  * Given a key, it will return a range of iterators within a collection (fast access), or collection by value (slow access mode)
00010  *
00011  * The range of iterators is handled through boost::sub_range, so it should feel like a collection:
00012  *   1) it has a '::const_iterator', a 'begin()' and an 'end()'
00013  *   2) it has a 'size()' and an 'empty()'
00014  *   3) it has a 'front()', 'back()'
00015  *   4) it works as an array  (i.e. you can use range[index] to pick an element)
00016  *   5) if your MultiAssociation is not const, you can modify the values associated to each key
00017  *      (but you can't push_back new values for a given key)
00018  * ( details at http://www.boost.org/doc/libs/1_37_0/libs/range/doc/utility_class.html#sub_range )
00019  *
00020  * 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>...
00021  * The collection must be compatible with sub_range and support a few other features. Usually you need:
00022  *  - that it has a default constructor
00023  *  - that it has a const_iterator, and "begin() const" returns such const_iterator. 
00024  *  - that it has an iterator, and "begin()" returns such iterator (note: it doesn't have to be writable, it can be const as well) 
00025  *  - that it has a swap method
00026  *  - that 'begin() + offset' is legal (and fast, otherwise this thing is will be awfully slow)
00027  *  - that you can push_back on a C the * of a C::const_iterator. Namely this should be legal
00028  *    <code>
00029  *       C::const_iterator it = ..something..
00030  *       C someOtherC = ...
00031  *       someOtherC.push_back(*it);
00032  *    </code>
00033  *
00034  * It can be filled through a FastFiller or a LazyFiller. 
00035  * FastFiller is probably faster, but has many constraints:
00036  * - you must fill only one product id at time
00037  * - you must fill items in strict key order
00038  * - there is no way to abort a filling operation
00039  * Try LazyFiller first, unless you're doing some sort of batch task that satisfies the FastFiller requirements
00040  *
00041  * It stores:
00042  *  - for each collection of keys: a product id and an offset  
00043  *  - for each key (even those not mapped to any value): one offset
00044  *  - for each value: one value
00045  * With respect to ValueMap / Association, there is one extra int32 for each key (but we don't store null values)
00046  *
00047  * Its backbone is given by edm::helper::IndexRangeAssociation, that maps keys to ranges, and is not templated.
00048  * \version $Id: MultiAssociation.h,v 1.3 2013/02/15 14:36:18 chrjones Exp $
00049  *
00050  */
00051 
00052 #include <vector>
00053 #include <map>
00054 #include <boost/shared_ptr.hpp>
00055 #include <boost/utility.hpp>
00056 #include <boost/range.hpp>
00057 #include "DataFormats/Common/interface/CMS_CLASS_VERSION.h"
00058 #include "DataFormats/Provenance/interface/ProductID.h"
00059 #include "FWCore/Utilities/interface/GCC11Compatibility.h"
00060 
00061 namespace edm {
00062   namespace helper {
00067 
00074       class IndexRangeAssociation {
00075           public:
00076               typedef std::pair<unsigned int, unsigned int> range;
00077             
00078               IndexRangeAssociation() : isFilling_(false) {}
00079 
00082               template<typename RefKey>
00083               range operator[](const RefKey &r) const {  
00084                   return get(r.id(), r.key()); 
00085               }
00086               
00089               range get(const edm::ProductID & id, unsigned int t) const ;
00090 
00092               bool  contains(ProductID id) const ;
00093 
00095               unsigned int size() const { return ref_offsets_.empty() ? 0 : ref_offsets_.size() - 1; }
00096             
00098               bool empty() const { return ref_offsets_.empty(); }
00099 
00104               class FastFiller : boost::noncopyable {
00105                   public:
00107                       FastFiller(IndexRangeAssociation &assoc, ProductID id, unsigned int size) ;
00108 
00110                       ~FastFiller() ;
00111 
00113                       template<typename RefKey> 
00114                       void insert(const RefKey &r, unsigned int startingOffset, unsigned int size) {
00115                          insert(r.id(), r.key(), startingOffset, size);
00116                       }
00117 
00119                       void insert(edm::ProductID id, unsigned int key, unsigned int startingOffset, unsigned int size) ;
00120                   private:
00121                       IndexRangeAssociation & assoc_;
00122                       const ProductID         id_;
00123                       unsigned int start_, end_; // indices into assoc_.ref_offsets_ (end_ points to the end marker, so it's valid)
00125                       int lastKey_; 
00126               }; // FastFiller
00127               friend class FastFiller;
00128 
00129               void swap(IndexRangeAssociation &other) ;
00130 
00131               static void throwUnexpectedProductID(ProductID found, ProductID expected, const char *where) ;
00132           private:
00133               typedef std::pair<edm::ProductID,unsigned int> id_off_pair;
00134               typedef std::vector<id_off_pair> id_offset_vector; // sorted by product id
00135               typedef std::vector<int> offset_vector; 
00136               id_offset_vector id_offsets_;  
00137               offset_vector    ref_offsets_; 
00138 
00139               bool isFilling_; // transient, to check no two fillers exist at the same time
00140               struct IDComparator { 
00141                 bool operator()(const id_off_pair &p, const edm::ProductID &id) const {  return p.first < id; }
00142               }; // IDComparator
00143 
00144               
00145     };
00146 
00147     // Free swap function
00148     inline void swap(IndexRangeAssociation& lhs, IndexRangeAssociation& rhs) { lhs.swap(rhs); }
00149 
00150   } // Helper Namespace
00151  
00152   template<typename C>
00153   class MultiAssociation {
00154   public:
00155     typedef C Collection;
00156     typedef boost::sub_range<const Collection> const_range;
00157     typedef boost::sub_range<Collection>       range;
00158 
00159     MultiAssociation() {}
00160 
00162     template<typename RefKey> 
00163     const_range operator[](const RefKey &r) const { 
00164         return get(r.id(), r.key()); 
00165     }
00166     // ---- and the non-const sister
00168     template<typename RefKey> 
00169     range operator[](const RefKey &r) { 
00170         return get(r.id(), r.key()); 
00171     }
00172 
00174     template<typename RefKey> 
00175     Collection getValues(const RefKey &r) const { 
00176         return getValues(r.id(), r.key()); 
00177     }
00178 
00180     bool contains(const edm::ProductID &id) const { return indices_.contains(id); }
00181 
00183     const_range get(const edm::ProductID &id, unsigned int t) const ;
00184     // ---- and the non-const sister
00186     range get(const edm::ProductID &id, unsigned int t) ;
00187 
00189     Collection getValues(const edm::ProductID &id, unsigned int t) const ;
00190     
00191     void swap(MultiAssociation &other) {
00192         indices_.swap(other.indices_);
00193         data_.swap(other.data_);
00194     }
00195 
00197     unsigned int dataSize()    const { return data_.size();    }
00198 
00200     unsigned int size() const { return indices_.size(); }
00201 
00203     bool empty() const { return indices_.empty(); }
00204 
00209     class FastFiller {
00210         public:
00211             template<typename HandleType>
00212             FastFiller(MultiAssociation &assoc, const HandleType &handle) : 
00213                 assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, handle.id(), handle->size())) {}
00214 
00215             FastFiller(MultiAssociation &assoc, edm::ProductID id, unsigned int size) : 
00216                 assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, id, size)) {}
00217 
00218             ~FastFiller() {}
00219         
00221             template<typename KeyRef>
00222             void setValues(const KeyRef &k, const Collection &refs) { setValues(k.id(), k.key(), refs); }
00223 
00225             void setValues(const edm::ProductID &id, unsigned int key, const Collection &refs);
00226         private:
00227             MultiAssociation &  assoc_;
00228             typedef edm::helper::IndexRangeAssociation::FastFiller IndexFiller;
00229             boost::shared_ptr<IndexFiller> indexFiller_;
00230 
00231     }; // FastFiller
00232     friend class FastFiller;
00233 
00234     template<typename HandleType>
00235     FastFiller fastFiller(const HandleType &handle) { return FastFiller(*this, handle); }
00236 
00242     class LazyFiller {
00243         public:
00244             template<typename HandleType>
00245             LazyFiller(MultiAssociation &assoc, const HandleType &handle, bool fillOnExit=false) :
00246                 assoc_(assoc), 
00247                 id_(handle.id()), size_(handle->size()), 
00248                 tempValues_(new TempValues()), fillOnExit_(fillOnExit) {}
00249             ~LazyFiller() noexcept(false) { if (fillOnExit_) fill(); } 
00250 
00255             void fill() noexcept(false);
00256 
00258             void setFillOnExit(bool fillOnExit) { fillOnExit_ = fillOnExit; }
00259 
00261             template<typename KeyRef>
00262             void setValues(const KeyRef &k, const Collection &refs) ;
00263 
00266             template<typename KeyRef>
00267             void swapValues(const KeyRef &k, Collection &refs) ;
00268         private:
00269             typedef std::map<unsigned int, Collection> TempValues;
00270             MultiAssociation & assoc_;
00271             ProductID id_; 
00272             unsigned int size_;
00273             boost::shared_ptr<TempValues> tempValues_;
00274             bool fillOnExit_;
00275     }; // LazyFiller
00276     friend class LazyFiller;
00277 
00278     template<typename HandleType>
00279     LazyFiller lazyFiller(const HandleType &h, bool fillOnExit=false) { return LazyFiller(*this, h, fillOnExit); }
00280 
00281     //Used by ROOT storage
00282     CMS_CLASS_VERSION(10)
00283 
00284   private:
00285     typedef helper::IndexRangeAssociation Indices;
00286     Indices    indices_;
00287     Collection data_;
00288 
00289 }; // class
00290 
00291   // Free swap function
00292   template <typename C>
00293   inline void swap(MultiAssociation<C>& lhs, MultiAssociation<C>& rhs) { lhs.swap(rhs); }
00294 
00295   //============= IMPLEMENTATION OF THE METHODS =============
00296   template<typename C>
00297   typename MultiAssociation<C>::const_range
00298   MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) const {
00299     Indices::range idxrange = indices_.get(id,key);
00300     return const_range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
00301   }
00302 
00303   template<typename C>
00304   typename MultiAssociation<C>::range
00305   MultiAssociation<C>::get(const edm::ProductID & id, unsigned int key) {
00306     Indices::range idxrange = indices_.get(id,key);
00307     return range(data_.begin()+idxrange.first, data_.begin()+idxrange.second);
00308   }
00309 
00310 
00311   template<typename C>
00312   typename MultiAssociation<C>::Collection
00313   MultiAssociation<C>::getValues(const edm::ProductID & id, unsigned int key) const {
00314      Collection ret;
00315      const_range values = get(id,key);
00316      for (typename const_range::const_iterator it = values.begin(), ed = values.end(); it != ed; ++it) {
00317         ret.push_back(*it);
00318      }
00319      return ret;
00320   }
00321 
00322   template<typename C>
00323   void MultiAssociation<C>::FastFiller::setValues(const edm::ProductID & id, unsigned int key, const Collection &vals) {
00324      indexFiller_->insert(id, key, assoc_.data_.size(), vals.size());
00325      for (typename Collection::const_iterator it = vals.begin(), ed = vals.end(); it != ed; ++it) {
00326         assoc_.data_.push_back(*it);
00327      }
00328   } 
00329   
00330   template<typename C>
00331   template<typename KeyRef>
00332   void MultiAssociation<C>::LazyFiller::setValues(const KeyRef &k, const Collection &vals) {
00333      if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");  
00334      (*tempValues_)[k.key()] = vals;
00335   }
00336 
00337   template<typename C>
00338   template<typename KeyRef>
00339   void MultiAssociation<C>::LazyFiller::swapValues(const KeyRef &k, Collection &vals) {
00340      if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(),id_,"LazyFiller::insert");  
00341      vals.swap((*tempValues_)[k.key()]);
00342   }
00343 
00344 
00345   template<typename C>
00346   void MultiAssociation<C>::LazyFiller::fill() {
00347      if (id_ != ProductID()) { // protection against double filling
00348         typename MultiAssociation<C>::FastFiller filler(assoc_, id_, size_);
00349         for (typename TempValues::const_iterator it = tempValues_->begin(), ed = tempValues_->end(); it != ed; ++it) {
00350            filler.setValues(id_, it->first, it->second);                
00351         }
00352         id_ = ProductID();     // protection against double filling
00353      }
00354   }
00355 
00356  
00357 } // namespace
00358 
00359 #endif