/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
DataFormats/Common/interface/MultiAssociation.h
371 строка
15 KB
Matti Kortelainen
Move stored types in io_v1 namespace, reset class versions to 3
22 май 2026, 18:18
22 май 2026, 18:18
909f324
Код
Авторство
О чём код?
#ifndef DataFormats_Common_MultiAssociation_h #define DataFormats_Common_MultiAssociation_h /* \class MultiAssociation * * \author Giovanni Petrucciani, SNS Pisa and CERN PH-CMG * * One-to-Many variant of edm::Association<C> / edm::ValueMap<T>, * * Given a key, it will return a range of iterators within a collection (fast access), or collection by value (slow access mode) * * The range of iterators is handled through boost::sub_range, so it should feel like a collection: * 1) it has a '::const_iterator', a 'begin()' and an 'end()' * 2) it has a 'size()' and an 'empty()' * 3) it has a 'front()', 'back()' * 4) it works as an array (i.e. you can use range[index] to pick an element) * 5) if your MultiAssociation is not const, you can modify the values associated to each key * (but you can't push_back new values for a given key) * ( details at http://www.boost.org/doc/libs/1_37_0/libs/range/doc/utility_class.html#sub_range ) * * 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>... * The collection must be compatible with sub_range and support a few other features. Usually you need: * - that it has a default constructor * - that it has a const_iterator, and "begin() const" returns such const_iterator. * - that it has an iterator, and "begin()" returns such iterator (note: it doesn't have to be writable, it can be const as well) * - that it has a swap method * - that 'begin() + offset' is legal (and fast, otherwise this thing is will be awfully slow) * - that you can push_back on a C the * of a C::const_iterator. Namely this should be legal * <code> * C::const_iterator it = ..something.. * C someOtherC = ... * someOtherC.push_back(*it); * </code> * * It can be filled through a FastFiller or a LazyFiller. * FastFiller is probably faster, but has many constraints: * - you must fill only one product id at time * - you must fill items in strict key order * - there is no way to abort a filling operation * Try LazyFiller first, unless you're doing some sort of batch task that satisfies the FastFiller requirements * * It stores: * - for each collection of keys: a product id and an offset * - for each key (even those not mapped to any value): one offset * - for each value: one value * With respect to ValueMap / Association, there is one extra int32 for each key (but we don't store null values) * * Its backbone is given by edm::helper::IndexRangeAssociation, that maps keys to ranges, and is not templated. * */ #include <vector> #include <map> #include <memory> #include <boost/range.hpp> #include "DataFormats/Common/interface/CMS_CLASS_VERSION.h" #include "DataFormats/Provenance/interface/ProductID.h" namespace edm { namespace helper { /// Base class to map to items one a range within a target collection. /// All items in a target collection must be mapped to one and only one key. /// The target collection will normally be a RefVector (also PtrVector and std::vector could work) /// To be used by MultiAssociation. Any other usage beyond MultiAssociation is not supported. /** This class holds: - ref_offsets_: A non decreasing list of offsets in the target collection, with one item for each key *plus one end item*. A range is given by a consecutive pair of offsets. While filling the map, some items can be '-1', but not the last one. - id_offsets_ : A list of pairs, mapping a product id to its associated range in ref_offsets_ This class can be filled through a FastFiller, that requires to fill keys and offsets in strictly increasing order, and without gaps. Only one FastFiller can exist at time, and each FastFiller fills only for a given key collection. */ class IndexRangeAssociation { public: typedef std::pair<unsigned int, unsigned int> range; IndexRangeAssociation() : isFilling_(false) {} /// Get a range of indices for this key. RefKey can be edm::Ref, edm::Ptr, edm::RefToBase /// And end value of -1 means 'until the end of the other collection, AFAICT' template <typename RefKey> range operator[](const RefKey &r) const { return get(r.id(), r.key()); } /// Get a range of indices for this product id and key (non-template version) /// And end value of -1 means 'until the end of the other collection, AFAICT' range get(const edm::ProductID &id, unsigned int t) const; /// True if this IndexRangeAssociation has info for this product id bool contains(ProductID id) const; /// Size of this collection (number of keys) unsigned int size() const { return ref_offsets_.empty() ? 0 : ref_offsets_.size() - 1; } /// True if it's empty (no keys) bool empty() const { return ref_offsets_.empty(); } /// FastFiller for the IndexRangeAssociation: /// It requires to fill items in strict key order. /// You can have a single FastFiller for a given map at time /// You can't access the map for this collection while filling it class FastFiller { public: FastFiller(const FastFiller &) = delete; FastFiller &operator=(const FastFiller &) = delete; /// Make a filler for a collection with a given product id and size FastFiller(IndexRangeAssociation &assoc, ProductID id, unsigned int size); /// When the FastFiller goes out of scope, it unlocks the map so you can make a new one ~FastFiller(); /// Sets the starting offset for this key. template <typename RefKey> void insert(const RefKey &r, unsigned int startingOffset, unsigned int size) { insert(r.id(), r.key(), startingOffset, size); } /// Sets the starting offset for this key (non-templated variant) void insert(edm::ProductID id, unsigned int key, unsigned int startingOffset, unsigned int size); private: IndexRangeAssociation &assoc_; const ProductID id_; unsigned int start_, end_; // indices into assoc_.ref_offsets_ (end_ points to the end marker, so it's valid) /// last key used to fill (to check that the new key must be strictly greater than lastKey_) int lastKey_; }; // FastFiller friend class FastFiller; void swap(IndexRangeAssociation &other); static void throwUnexpectedProductID(ProductID found, ProductID expected, const char *where); private: typedef std::pair<edm::ProductID, unsigned int> id_off_pair; typedef std::vector<id_off_pair> id_offset_vector; // sorted by product id typedef std::vector<int> offset_vector; id_offset_vector id_offsets_; offset_vector ref_offsets_; bool isFilling_; // transient, to check no two fillers exist at the same time struct IDComparator { bool operator()(const id_off_pair &p, const edm::ProductID &id) const { return p.first < id; } }; // IDComparator }; // Free swap function inline void swap(IndexRangeAssociation &lhs, IndexRangeAssociation &rhs) { lhs.swap(rhs); } } // namespace helper template <typename C> class MultiAssociation { public: typedef C Collection; typedef boost::sub_range<const Collection> const_range; typedef boost::sub_range<Collection> range; MultiAssociation() {} /// Get a range of values for this key (fast) template <typename RefKey> const_range operator[](const RefKey &r) const { return get(r.id(), r.key()); } // ---- and the non-const sister /// Get a range of values for this key (fast) template <typename RefKey> range operator[](const RefKey &r) { return get(r.id(), r.key()); } /// Get a copy of the values for this key (slow!) template <typename RefKey> Collection getValues(const RefKey &r) const { return getValues(r.id(), r.key()); } /// True if there are keys from this product id bool contains(const edm::ProductID &id) const { return indices_.contains(id); } /// Get a range of values for this product id and index (fast) const_range get(const edm::ProductID &id, unsigned int t) const; // ---- and the non-const sister /// Get a range of values for this product id and index (fast) range get(const edm::ProductID &id, unsigned int t); /// Get a copy of the values for this product id and index (slow!) Collection getValues(const edm::ProductID &id, unsigned int t) const; void swap(MultiAssociation &other) { indices_.swap(other.indices_); data_.swap(other.data_); } /// Returns the number of values unsigned int dataSize() const { return data_.size(); } /// Returns the number of keys unsigned int size() const { return indices_.size(); } /// Returns true if there are no keys bool empty() const { return indices_.empty(); } /// FastFiller for the MultiAssociation. /// It is fast, but it requires to fill items in strict key order. /// You can have a single FastFiller for a given map at time /// You can't access the map for this collection while filling it class FastFiller { public: template <typename HandleType> FastFiller(MultiAssociation &assoc, const HandleType &handle) : assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, handle.id(), handle->size())) {} FastFiller(MultiAssociation &assoc, edm::ProductID id, unsigned int size) : assoc_(assoc), indexFiller_(new IndexFiller(assoc_.indices_, id, size)) {} ~FastFiller() {} /// Sets the Collection values associated to this key, making copies of those in refs template <typename KeyRef> void setValues(const KeyRef &k, const Collection &refs) { setValues(k.id(), k.key(), refs); } /// Sets the Collection values associated to this key, making copies of those in refs void setValues(const edm::ProductID &id, unsigned int key, const Collection &refs); private: MultiAssociation &assoc_; typedef edm::helper::IndexRangeAssociation::FastFiller IndexFiller; std::shared_ptr<IndexFiller> indexFiller_; }; // FastFiller friend class FastFiller; template <typename HandleType> FastFiller fastFiller(const HandleType &handle) { return FastFiller(*this, handle); } /// LazyFiller for the MultiAssociation. /// It is slower than FastFiller, as it keeps a copy of the input before filling it (unless you use swapValues) /// Anyway it has no constraints on the order of the keys, or the number of fillers /// It can also be copied around by value without cloning its keys (e.g to put it into a std::vector) /// If you set fillOnExit to 'true', it will fill the MultiAssociation automatically when going out of scope class LazyFiller { public: template <typename HandleType> LazyFiller(MultiAssociation &assoc, const HandleType &handle, bool fillOnExit = false) : assoc_(assoc), id_(handle.id()), size_(handle->size()), tempValues_(new TempValues()), fillOnExit_(fillOnExit) {} ~LazyFiller() noexcept(false) { if (fillOnExit_) fill(); } /// Does the real filling. Until this is called, the map is not modified at all. /// Calling this twice won't have any effect (but you can't modify a LazyFiller /// after calling 'fill()') /// Implementation note: inside, it just makes a FastFiller and uses it. void fill() noexcept(false); /// If set to true, the LazyFiller wil call 'fill()' when it goes out of scope void setFillOnExit(bool fillOnExit) { fillOnExit_ = fillOnExit; } /// Sets the Collection values associated to this key, making copies of those in refs template <typename KeyRef> void setValues(const KeyRef &k, const Collection &refs); /// Swaps the Collection values associated to this key with the ones in 'ref' /// This is expected to be faster than 'setValues'. template <typename KeyRef> void swapValues(const KeyRef &k, Collection &refs); private: typedef std::map<unsigned int, Collection> TempValues; MultiAssociation &assoc_; ProductID id_; unsigned int size_; std::shared_ptr<TempValues> tempValues_; bool fillOnExit_; }; // LazyFiller friend class LazyFiller; template <typename HandleType> LazyFiller lazyFiller(const HandleType &h, bool fillOnExit = false) { return LazyFiller(*this, h, fillOnExit); } //Used by ROOT storage CMS_CLASS_VERSION(3) private: typedef helper::IndexRangeAssociation Indices; Indices indices_; Collection data_; }; // class // Free swap function template <typename C> inline void swap(MultiAssociation<C> &lhs, MultiAssociation<C> &rhs) { lhs.swap(rhs); } //============= IMPLEMENTATION OF THE METHODS ============= template <typename C> typename MultiAssociation<C>::const_range MultiAssociation<C>::get(const edm::ProductID &id, unsigned int key) const { Indices::range idxrange = indices_.get(id, key); return const_range(data_.begin() + idxrange.first, data_.begin() + idxrange.second); } template <typename C> typename MultiAssociation<C>::range MultiAssociation<C>::get(const edm::ProductID &id, unsigned int key) { Indices::range idxrange = indices_.get(id, key); return range(data_.begin() + idxrange.first, data_.begin() + idxrange.second); } template <typename C> typename MultiAssociation<C>::Collection MultiAssociation<C>::getValues(const edm::ProductID &id, unsigned int key) const { Collection ret; const_range values = get(id, key); for (typename const_range::const_iterator it = values.begin(), ed = values.end(); it != ed; ++it) { ret.push_back(*it); } return ret; } template <typename C> void MultiAssociation<C>::FastFiller::setValues(const edm::ProductID &id, unsigned int key, const Collection &vals) { indexFiller_->insert(id, key, assoc_.data_.size(), vals.size()); for (typename Collection::const_iterator it = vals.begin(), ed = vals.end(); it != ed; ++it) { assoc_.data_.push_back(*it); } } template <typename C> template <typename KeyRef> void MultiAssociation<C>::LazyFiller::setValues(const KeyRef &k, const Collection &vals) { if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(), id_, "LazyFiller::insert"); (*tempValues_)[k.key()] = vals; } template <typename C> template <typename KeyRef> void MultiAssociation<C>::LazyFiller::swapValues(const KeyRef &k, Collection &vals) { if (k.id() != id_) Indices::throwUnexpectedProductID(k.id(), id_, "LazyFiller::insert"); vals.swap((*tempValues_)[k.key()]); } template <typename C> void MultiAssociation<C>::LazyFiller::fill() { if (id_ != ProductID()) { // protection against double filling typename MultiAssociation<C>::FastFiller filler(assoc_, id_, size_); for (typename TempValues::const_iterator it = tempValues_->begin(), ed = tempValues_->end(); it != ed; ++it) { filler.setValues(id_, it->first, it->second); } id_ = ProductID(); // protection against double filling } } } // namespace edm #endif