Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-18 09:25:07

0001 #ifndef PODIO_DETAIL_LINKCOLLECTIONIMPL_H
0002 #define PODIO_DETAIL_LINKCOLLECTIONIMPL_H
0003 
0004 #include "podio/ObjectID.h"
0005 #include "podio/detail/Link.h"
0006 #include "podio/detail/LinkCollectionData.h"
0007 #include "podio/detail/LinkCollectionIterator.h"
0008 #include "podio/detail/LinkFwd.h"
0009 #include "podio/detail/LinkObj.h"
0010 
0011 #include "podio/CollectionBase.h"
0012 #include "podio/CollectionBufferFactory.h"
0013 #include "podio/CollectionBuffers.h"
0014 #include "podio/DatamodelRegistry.h"
0015 #include "podio/ICollectionProvider.h"
0016 #include "podio/SchemaEvolution.h"
0017 #include "podio/utilities/MaybeSharedPtr.h"
0018 #include "podio/utilities/StaticConcatenate.h"
0019 #include "podio/utilities/TypeHelpers.h"
0020 
0021 #include <iterator>
0022 #include <string_view>
0023 
0024 #ifdef PODIO_JSON_OUTPUT
0025   #include "nlohmann/json.hpp"
0026 #endif
0027 
0028 #include <iomanip>
0029 #include <memory>
0030 #include <mutex>
0031 #include <ostream>
0032 #include <string>
0033 #include <type_traits>
0034 
0035 namespace podio {
0036 
0037 template <typename FromT, typename ToT>
0038 class LinkCollection : public podio::CollectionBase {
0039   static_assert(std::is_same_v<FromT, detail::GetDefaultHandleType<FromT>>,
0040                 "Links need to be instantiated with the default types!");
0041   static_assert(std::is_same_v<ToT, detail::GetDefaultHandleType<ToT>>,
0042                 "Links need to be instantiated with the default types!");
0043 
0044   // convenience typedefs
0045   using CollectionDataT = podio::LinkCollectionData<FromT, ToT>;
0046 
0047 public:
0048   using from_type = FromT;
0049   using to_type = ToT;
0050   using value_type = Link<FromT, ToT>;
0051   using mutable_type = MutableLink<FromT, ToT>;
0052   using const_iterator = LinkCollectionIterator<FromT, ToT>;
0053   using iterator = LinkMutableCollectionIterator<FromT, ToT>;
0054   using difference_type = ptrdiff_t;
0055   using size_type = size_t;
0056   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
0057   using reverse_iterator = std::reverse_iterator<iterator>;
0058 
0059   LinkCollection() = default;
0060 
0061   LinkCollection(CollectionDataT&& data, bool isSubsetColl) :
0062       m_isSubsetColl(isSubsetColl), m_collectionID(0), m_storage(std::move(data)) {
0063   }
0064 
0065   // Move-only type
0066   LinkCollection(const LinkCollection&) = delete;
0067   LinkCollection& operator=(const LinkCollection&) = delete;
0068   LinkCollection(LinkCollection&&) = default;
0069   LinkCollection& operator=(LinkCollection&&) = default;
0070 
0071   ~LinkCollection() {
0072     // Need to tell the storage how to clean up
0073     m_storage.clear(m_isSubsetColl);
0074   }
0075 
0076   /// Append a new link to the collection and return this object
0077   mutable_type create() {
0078     if (m_isSubsetColl) {
0079       throw std::logic_error("Cannot create new elements on a subset collection");
0080     }
0081 
0082     auto obj = m_storage.entries.emplace_back(new LinkObj<FromT, ToT>());
0083     obj->id = {int(m_storage.entries.size() - 1), m_collectionID};
0084     return mutable_type(podio::utils::MaybeSharedPtr(obj));
0085   }
0086 
0087   /// Returns the immutable object of given index
0088   value_type operator[](unsigned int index) const {
0089     return value_type(m_storage.entries[index]);
0090   }
0091   /// Returns the mutable object of given index
0092   mutable_type operator[](unsigned int index) {
0093     return mutable_type(podio::utils::MaybeSharedPtr(m_storage.entries[index]));
0094   }
0095   /// Returns the immutable object of given index
0096   value_type at(unsigned int index) const {
0097     return value_type(m_storage.entries.at(index));
0098   }
0099   /// Returns the mutable object of given index
0100   mutable_type at(unsigned int index) {
0101     return mutable_type(podio::utils::MaybeSharedPtr(m_storage.at(index)));
0102   }
0103 
0104   void push_back(mutable_type object) {
0105     // We have to do different things here depending on whether this is a
0106     // subset collection or not. A normal collection cannot collect objects
0107     // that are already part of another collection, while a subset collection
0108     // can only collect such objects
0109     if (!m_isSubsetColl) {
0110       auto obj = object.m_obj;
0111       if (obj->id.index == podio::ObjectID::untracked) {
0112         const auto size = m_storage.entries.size();
0113         obj->id = {(int)size, m_collectionID};
0114         m_storage.entries.push_back(obj.release());
0115       } else {
0116         throw std::invalid_argument("Object already in a collection. Cannot add it to a second collection");
0117       }
0118 
0119     } else {
0120       push_back(value_type(object));
0121     }
0122   }
0123 
0124   void push_back(value_type object) {
0125     if (!m_isSubsetColl) {
0126       throw std::invalid_argument("Can only add immutable objects to subset collections");
0127     }
0128     auto obj = object.m_obj;
0129     if (obj->id.index < 0) {
0130       throw std::invalid_argument(
0131           "Object needs to be tracked by another collection in order for it to be storable in a subset collection");
0132     }
0133     m_storage.entries.push_back(obj.release());
0134   }
0135 
0136   /// Number of elements in the collection
0137   size_t size() const override {
0138     return m_storage.entries.size();
0139   }
0140 
0141   /// maximal number of elements in the collection
0142   std::size_t max_size() const override {
0143     const auto maxStorage = m_storage.entries.max_size();
0144     if (!m_isSubsetColl) {
0145       // non-subset collections shouldn't have more elements than the maximum index of ObjectID
0146       const auto maxIndex = std::numeric_limits<decltype(ObjectID::index)>::max();
0147       return std::min<size_t>(maxIndex, maxStorage);
0148     }
0149     return maxStorage;
0150   }
0151 
0152   /// Is the collection empty
0153   bool empty() const override {
0154     return m_storage.entries.empty();
0155   }
0156 
0157   void clear() override {
0158     m_storage.clear(m_isSubsetColl);
0159     m_isPrepared = false;
0160   }
0161 
0162   void print(std::ostream& os = std::cout, bool flush = true) const override {
0163     os << *this;
0164     if (flush) {
0165       os.flush();
0166     }
0167   }
0168 
0169   // support for the iterator protocol
0170   const_iterator begin() const {
0171     return const_iterator(0, &m_storage.entries);
0172   }
0173   const_iterator cbegin() const {
0174     return begin();
0175   }
0176   const_iterator end() const {
0177     return const_iterator(m_storage.entries.size(), &m_storage.entries);
0178   }
0179   const_iterator cend() const {
0180     return end();
0181   }
0182   iterator begin() {
0183     return iterator(0, &m_storage.entries);
0184   }
0185   iterator end() {
0186     return iterator(m_storage.entries.size(), &m_storage.entries);
0187   }
0188   // reverse iterators
0189   reverse_iterator rbegin() {
0190     return reverse_iterator(end());
0191   }
0192   const_reverse_iterator rbegin() const {
0193     return const_reverse_iterator(end());
0194   }
0195   const_reverse_iterator crbegin() const {
0196     return rbegin();
0197   }
0198   reverse_iterator rend() {
0199     return reverse_iterator(begin());
0200   }
0201   const_reverse_iterator rend() const {
0202     return const_reverse_iterator(begin());
0203   }
0204   const_reverse_iterator crend() const {
0205     return rend();
0206   }
0207 
0208   bool isValid() const override {
0209     return m_isValid;
0210   }
0211 
0212   podio::CollectionWriteBuffers getBuffers() override {
0213     return m_storage.getCollectionBuffers(m_isSubsetColl);
0214   }
0215 
0216   constexpr static std::string_view typeName =
0217       podio::utils::static_concatenate_v<podio::detail::link_coll_name_prefix, FromT::typeName,
0218                                          podio::detail::link_name_infix, ToT::typeName,
0219                                          podio::detail::link_name_suffix>;
0220 
0221   constexpr static std::string_view valueTypeName = value_type::typeName;
0222   constexpr static std::string_view dataTypeName = "podio::LinkData";
0223 
0224   const std::string_view getTypeName() const override {
0225     return typeName;
0226   }
0227 
0228   const std::string_view getValueTypeName() const override {
0229     return valueTypeName;
0230   }
0231 
0232   const std::string_view getDataTypeName() const override {
0233     return dataTypeName;
0234   }
0235 
0236   bool isSubsetCollection() const override {
0237     return m_isSubsetColl;
0238   }
0239 
0240   void setSubsetCollection(bool setSubset = true) override {
0241     if (m_isSubsetColl != setSubset && !m_storage.entries.empty()) {
0242       throw std::logic_error("Cannot change the character of a collection that already contains elements");
0243     }
0244 
0245     if (setSubset) {
0246       m_storage.makeSubsetCollection();
0247     }
0248     m_isSubsetColl = setSubset;
0249   }
0250 
0251   void setID(unsigned id) override {
0252     m_collectionID = id;
0253     if (!m_isSubsetColl) {
0254       std::ranges::for_each(m_storage.entries, [id](auto* obj) { obj->id = {obj->id.index, id}; });
0255     }
0256     m_isValid = true;
0257   }
0258 
0259   unsigned getID() const override {
0260     return m_collectionID;
0261   }
0262 
0263   void prepareForWrite() const override {
0264     std::lock_guard lock{*m_storageMtx};
0265     // by the time we acquire the lock another thread might have already
0266     // succeeded, so we need to check again now
0267     if (m_isPrepared) {
0268       return;
0269     }
0270     m_storage.prepareForWrite(m_isSubsetColl);
0271     m_isPrepared = true;
0272   }
0273 
0274   void prepareAfterRead() override {
0275     // No need to go through this again if we have already done it
0276     if (m_isPrepared) {
0277       return;
0278     }
0279 
0280     if (!m_isSubsetColl) {
0281       // Subset collections do not store any data that would require post-processing
0282       m_storage.prepareAfterRead(m_collectionID);
0283     }
0284     // Preparing a collection doesn't affect the underlying I/O buffers, so this
0285     // collection is still prepared
0286     m_isPrepared = true;
0287   }
0288 
0289   bool setReferences(const ICollectionProvider* collectionProvider) override {
0290     return m_storage.setReferences(collectionProvider, m_isSubsetColl);
0291   }
0292 
0293   static constexpr SchemaVersionT schemaVersion = 1;
0294 
0295   SchemaVersionT getSchemaVersion() const override {
0296     return schemaVersion;
0297   }
0298 
0299   size_t getDatamodelRegistryIndex() const override {
0300     return podio::DatamodelRegistry::NoDefinitionNecessary;
0301   }
0302 
0303 private:
0304   // For setReferences, we need to give our own CollectionData access to our
0305   // private entries. Otherwise we would need to expose a public member function
0306   // that gives access to the Obj* which is definitely not what we want
0307   friend CollectionDataT;
0308 
0309   bool m_isValid{false};
0310   mutable bool m_isPrepared{false};
0311   bool m_isSubsetColl{false};
0312   uint32_t m_collectionID{0};
0313   mutable std::unique_ptr<std::mutex> m_storageMtx{std::make_unique<std::mutex>()};
0314   mutable CollectionDataT m_storage{};
0315 };
0316 
0317 template <typename FromT, typename ToT>
0318 std::ostream& operator<<(std::ostream& o, const LinkCollection<FromT, ToT>& v) {
0319   const auto old_flags = o.flags();
0320   o << "          id:      weight:" << '\n';
0321   for (const auto&& el : v) {
0322     o << std::scientific << std::showpos << std::setw(12) << el.id() << " " << std::setw(12) << " " << el.getWeight()
0323       << '\n';
0324 
0325     o << "     from : ";
0326     o << el.getFrom().id() << std::endl;
0327     o << "       to : ";
0328     o << el.getTo().id() << std::endl;
0329   }
0330 
0331   o.flags(old_flags);
0332   return o;
0333 }
0334 
0335 namespace detail {
0336   template <typename FromT, typename ToT>
0337   podio::CollectionReadBuffers createLinkBuffers(bool subsetColl) {
0338     auto readBuffers = podio::CollectionReadBuffers{};
0339     readBuffers.type = podio::LinkCollection<FromT, ToT>::typeName;
0340     readBuffers.schemaVersion = podio::LinkCollection<FromT, ToT>::schemaVersion;
0341     readBuffers.data = subsetColl ? nullptr : new LinkDataContainer();
0342 
0343     // Either it is a subset collection or we have two relations
0344     const auto nRefs = subsetColl ? 1 : 2;
0345     readBuffers.references = new podio::CollRefCollection(nRefs);
0346     for (auto& ref : *readBuffers.references) {
0347       // Make sure to place usable buffer pointers here
0348       ref = std::make_unique<std::vector<podio::ObjectID>>();
0349     }
0350 
0351     readBuffers.createCollection = [](podio::CollectionReadBuffers buffers, bool isSubsetColl) {
0352       LinkCollectionData<FromT, ToT> data(buffers, isSubsetColl);
0353       return std::make_unique<LinkCollection<FromT, ToT>>(std::move(data), isSubsetColl);
0354     };
0355 
0356     readBuffers.recast = [](podio::CollectionReadBuffers& buffers) {
0357       if (buffers.data) {
0358         buffers.data = podio::CollectionWriteBuffers::asVector<float>(buffers.data);
0359       }
0360     };
0361 
0362     readBuffers.deleteBuffers = [](const podio::CollectionReadBuffers& buffers) {
0363       if (buffers.data) {
0364         // If we have data then we are not a subset collection and we have
0365         // to clean up all type erased buffers by casting them back to
0366         // something that we can delete
0367         delete static_cast<LinkDataContainer*>(buffers.data);
0368       }
0369       delete buffers.references;
0370       delete buffers.vectorMembers;
0371     };
0372 
0373     return readBuffers;
0374   }
0375 
0376   template <typename FromT, typename ToT>
0377   bool registerLinkCollection(const std::string_view linkTypeName) {
0378     const static auto reg = [&linkTypeName]() {
0379       const auto schemaVersion = LinkCollection<FromT, ToT>::schemaVersion;
0380 
0381       auto& factory = CollectionBufferFactory::mutInstance();
0382       factory.registerCreationFunc(std::string(linkTypeName), schemaVersion, createLinkBuffers<FromT, ToT>);
0383 
0384       // For now passing the same schema version for from and current version
0385       // simply to make SchemaEvolution aware of LinkCollections
0386       podio::SchemaEvolution::mutInstance().registerEvolutionFunc(std::string(linkTypeName), schemaVersion,
0387                                                                   schemaVersion, SchemaEvolution::noOpSchemaEvolution,
0388                                                                   SchemaEvolution::Priority::AutoGenerated);
0389 
0390       return true;
0391     }();
0392     return reg;
0393   }
0394 } // namespace detail
0395 
0396 #if defined(PODIO_JSON_OUTPUT) && !defined(__CLING__)
0397 template <typename FromT, typename ToT>
0398 void to_json(nlohmann::json& j, const podio::LinkCollection<FromT, ToT>& collection) {
0399   j = nlohmann::json::array();
0400   for (auto&& elem : collection) {
0401     j.emplace_back(elem);
0402   }
0403 }
0404 #endif
0405 
0406 } // namespace podio
0407 
0408 #endif // PODIO_DETAIL_LINKCOLLECTIONIMPL_H