Back to home page

EIC code displayed by LXR

 
 

    


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

0001 #ifndef PODIO_FRAME_H
0002 #define PODIO_FRAME_H
0003 
0004 #include "podio/CollectionBase.h"
0005 #include "podio/CollectionIDTable.h"
0006 #include "podio/FrameCategories.h" // mainly for convenience
0007 #include "podio/GenericParameters.h"
0008 #include "podio/ICollectionProvider.h"
0009 #include "podio/SchemaEvolution.h"
0010 #include "podio/utilities/TypeHelpers.h"
0011 
0012 #include <initializer_list>
0013 #include <memory>
0014 #include <mutex>
0015 #include <optional>
0016 #include <set>
0017 #include <stdexcept>
0018 #include <string>
0019 #include <type_traits>
0020 #include <unordered_map>
0021 #include <vector>
0022 
0023 namespace podio {
0024 
0025 /// Concept for enabling overloads only for Collection r-values
0026 template <typename T>
0027 concept CollectionRValueType = CollectionType<T> && !std::is_lvalue_reference_v<T>;
0028 
0029 /// Concept for enabling overloads for r-values
0030 template <typename T>
0031 concept RValueType = !std::is_lvalue_reference_v<T>;
0032 
0033 namespace detail {
0034   /// The minimal interface for raw data types
0035   struct EmptyFrameData {
0036     podio::CollectionIDTable getIDTable() const {
0037       return {};
0038     }
0039 
0040     /// Try to get the buffers for a collection
0041     std::optional<podio::CollectionReadBuffers> getCollectionBuffers(const std::string&) {
0042       return std::nullopt;
0043     }
0044 
0045     /// Get the **still available**, i.e. yet unpacked, collections from the raw data
0046     std::vector<std::string> getAvailableCollections() const {
0047       return {};
0048     }
0049 
0050     /// Get the parameters that are stored in the raw data
0051     std::unique_ptr<podio::GenericParameters> getParameters() {
0052       return std::make_unique<podio::GenericParameters>();
0053     }
0054   };
0055 } // namespace detail
0056 
0057 template <typename FrameDataT>
0058 std::optional<podio::CollectionReadBuffers> unpack(FrameDataT* data, const std::string& name) {
0059   return data->getCollectionBuffers(name);
0060 }
0061 
0062 /// The Frame is a generalized (event) data container that aggregates all
0063 /// relevant data.
0064 ///
0065 /// It is possible to store collections as well as parameters / meta data in a
0066 /// Frame and all I/O facilities of podio operate on Frames.
0067 class Frame {
0068   /// Internal abstract interface for the type-erased implementation of the
0069   /// Frame class
0070   struct FrameConcept {
0071     virtual ~FrameConcept() = default;
0072     virtual const podio::CollectionBase* get(const std::string& name) const = 0;
0073     virtual const podio::CollectionBase* put(std::unique_ptr<podio::CollectionBase> coll, const std::string& name) = 0;
0074     virtual podio::GenericParameters& parameters() = 0;
0075     virtual const podio::GenericParameters& parameters() const = 0;
0076 
0077     virtual std::vector<std::string> availableCollections() const = 0;
0078 
0079     // Writing interface. Need this to be able to store all necessary information
0080     // TODO: Figure out whether this can be "hidden" somehow
0081     virtual podio::CollectionIDTable getIDTable() const = 0;
0082   };
0083 
0084   /// The interface implementation of the abstract FrameConcept that is
0085   /// necessary for a type-erased implementation of the Frame class
0086   template <typename FrameDataT>
0087   struct FrameModel final : FrameConcept, public ICollectionProvider {
0088 
0089     FrameModel(std::unique_ptr<FrameDataT> data);
0090     ~FrameModel() = default;
0091     FrameModel(const FrameModel&) = delete;
0092     FrameModel& operator=(const FrameModel&) = delete;
0093     FrameModel(FrameModel&&) = default;
0094     FrameModel& operator=(FrameModel&&) = default;
0095 
0096     /// Try and get the collection from the internal storage and return a
0097     /// pointer to it if found. Otherwise return a nullptr
0098     const podio::CollectionBase* get(const std::string& name) const final;
0099 
0100     /// Try and place the collection into the internal storage and return a
0101     /// pointer to it. If a collection already exists or insertion fails, return
0102     /// a nullptr
0103     const podio::CollectionBase* put(std::unique_ptr<CollectionBase> coll, const std::string& name) final;
0104 
0105     /// Get a reference to the internally used GenericParameters
0106     podio::GenericParameters& parameters() override {
0107       return *m_parameters;
0108     }
0109     /// Get a const reference to the internally used GenericParameters
0110     const podio::GenericParameters& parameters() const override {
0111       return *m_parameters;
0112     }
0113 
0114     bool get(uint32_t collectionID, podio::CollectionBase*& collection) const override;
0115 
0116     podio::CollectionIDTable getIDTable() const override {
0117       // Make a copy
0118       return {m_idTable.ids(), m_idTable.names()};
0119     }
0120 
0121     std::vector<std::string> availableCollections() const override;
0122 
0123   private:
0124     podio::CollectionBase* doGet(const std::string& name, bool setReferences = true) const;
0125 
0126     using CollectionMapT = std::unordered_map<std::string, std::unique_ptr<podio::CollectionBase>>;
0127 
0128     mutable CollectionMapT m_collections{};                 ///< The internal map for storing unpacked collections
0129     mutable std::unique_ptr<std::mutex> m_mapMtx{nullptr};  ///< The mutex for guarding the internal collection map
0130     std::unique_ptr<FrameDataT> m_data{nullptr};            ///< The raw data read from file
0131     mutable std::unique_ptr<std::mutex> m_dataMtx{nullptr}; ///< The mutex for guarding the raw data
0132     podio::CollectionIDTable m_idTable{};                   ///< The collection ID table
0133     std::unique_ptr<podio::GenericParameters> m_parameters{nullptr}; ///< The generic parameter store for this frame
0134     mutable std::set<uint32_t> m_retrievedIDs{}; ///< The IDs of the collections that we have already read (but not yet
0135                                                  ///< put into the map)
0136   };
0137 
0138   std::unique_ptr<FrameConcept> m_self; ///< The internal concept pointer through which all the work is done
0139 
0140 public:
0141   /// Empty Frame constructor
0142   Frame();
0143 
0144   /// Frame constructor from (almost) arbitrary raw data.
0145   ///
0146   /// @tparam FrameDataT Arbitrary data container that provides access to the
0147   ///                    collection buffers as well as the metadata, when
0148   ///                    requested by the Frame. The unique_ptr has to be checked
0149   ///                    for validity before calling this constructor.
0150   ///
0151   /// @throws std::invalid_argument if the passed pointer is a nullptr.
0152   template <typename FrameDataT>
0153   Frame(std::unique_ptr<FrameDataT>);
0154 
0155   /// Frame constructor from (almost) arbitrary raw data.
0156   ///
0157   /// This r-value overload is mainly present for enabling the python bindings,
0158   /// where cppyy seems to strip the std::unique_ptr somewhere in the process
0159   ///
0160   /// @tparam FrameDataT Arbitrary data container that provides access to the
0161   ///                    collection buffers as well as the metadata, when
0162   ///                    requested by the Frame.
0163   template <RValueType FrameDataT>
0164   Frame(FrameDataT&&);
0165 
0166   /// A Frame is move-only
0167   Frame(const Frame&) = delete;
0168   /// A Frame is move-only
0169   Frame& operator=(const Frame&) = delete;
0170 
0171   /// Frame move constructor
0172   Frame(Frame&&) = default;
0173 
0174   /// Frame move assignment operator
0175   Frame& operator=(Frame&&) = default;
0176 
0177   /// Frame destructor
0178   ///
0179   /// @note Since the Frame owns all the collections that have been put into it,
0180   /// or that can be obtained from it, this invalidates all references to these
0181   /// collections.
0182   ~Frame() = default;
0183 
0184   /// Get a collection from the Frame by name.
0185   ///
0186   /// @tparam CollT The type of the desired collection
0187   /// @param  name  The name of the collection
0188   ///
0189   /// @returns      A const reference to the collection if it is available or to
0190   ///               an empty (static) collection
0191   template <CollectionType CollT>
0192   const CollT& get(const std::string& name) const;
0193 
0194   /// Get a collection pointer from the Frame by name.
0195   ///
0196   /// This is a type-erased version that is also used by the python bindings.
0197   ///
0198   /// @returns A const pointer to a collection if it is available or a nullptr
0199   ///          if it is not
0200   const podio::CollectionBase* get(const std::string& name) const;
0201 
0202   /// (Destructively) move a collection into the Frame and get a reference to
0203   /// the inserted collection back for further use.
0204   ///
0205   /// The collection that is passed into the Frame has to be moved into it
0206   /// explicitly and the moved-from collection will be in the typical *valid but
0207   /// undefined state* in c++.
0208   ///
0209   /// @tparam CollT The type of the collection
0210   /// @param  coll  An rvalue reference to the collection to put into the Frame.
0211   /// @param  name  The name under which this collection should be stored in the
0212   ///               Frame
0213   ///
0214   /// @returns      A const reference to the collection that has just been
0215   ///               inserted
0216   template <CollectionRValueType CollT>
0217   const CollT& put(CollT&& coll, const std::string& name);
0218 
0219   /// (Destructively) move a collection into the Frame.
0220   ///
0221   /// @param coll The collection that should be moved into the Frame
0222   /// @param name The name under which this collection should be stored in the
0223   ///             Frame
0224   void put(std::unique_ptr<podio::CollectionBase> coll, const std::string& name);
0225 
0226   /// Add a value to the parameters of the Frame (if the type is supported).
0227   ///
0228   /// @tparam T    The type of the parameter. Has to be one of the types that
0229   ///              is supported by GenericParameters
0230   /// @param key   The name under which this parameter should be stored
0231   /// @param value The value of the parameter. A copy will be put into the Frame
0232   template <ValidGenericDataType T>
0233   inline void putParameter(const std::string& key, T value) {
0234     m_self->parameters().set(key, std::move(value));
0235   }
0236 
0237   /// Add a string value to the parameters of the Frame.
0238   ///
0239   /// This is a dedicated overload for enabling on-the-fly conversion from
0240   /// string literals.
0241   ///
0242   /// @param key   The name under which this parameter should be stored
0243   /// @param value The value of the parameter. A copy will be put into the Frame
0244   inline void putParameter(const std::string& key, std::string value) {
0245     putParameter<std::string>(key, std::move(value));
0246   }
0247 
0248   /// Add a vector of strings value the parameters of the Frame.
0249   ///
0250   /// This is a dedicated overload for enabling on-the-fly conversion from
0251   /// an initializer_list of string literals
0252   ///
0253   /// @param key    The name under which this parameter should be stored
0254   /// @param values The values of the parameter. A copy will be put into the Frame
0255   inline void putParameter(const std::string& key, std::vector<std::string> values) {
0256     putParameter<std::vector<std::string>>(key, std::move(values));
0257   }
0258 
0259   /// Add a vector of values to the parameters of the Frame (if the type is
0260   /// supported).
0261   ///
0262   /// This is a dedicated overload for enabling on-the-fly conversions of
0263   /// initializer_list of values
0264   ///
0265   /// @tparam T    The type of the parameter. Has to be one of the types that
0266   ///              is supported by GenericParameters
0267   /// @param key    The name under which this parameter should be stored
0268   /// @param values The values of the parameter. A copy will be put into the Frame
0269   template <ValidGenericDataType T>
0270   inline void putParameter(const std::string& key, std::initializer_list<T>&& values) {
0271     putParameter<std::vector<T>>(key, std::move(values));
0272   }
0273 
0274   /// Retrieve parameters via key from the internal store.
0275   ///
0276   /// @tparam T  The desired type of the parameter (can also be std::vector<T>)
0277   /// @param key The key under which the value is stored
0278   ///
0279   /// @returns   An optional holding the value if it is present
0280   template <ValidGenericDataType T>
0281   inline auto getParameter(const std::string& key) const {
0282     return m_self->parameters().get<T>(key);
0283   }
0284 
0285   /// Retrieve all parameters stored in this Frame.
0286   ///
0287   /// This is mainly intended for I/O purposes and we encourage to use the Frame
0288   /// functionality of getParameter or getParameterKeys in general.
0289   ///
0290   /// @returns The internally used GenericParameters
0291   inline const podio::GenericParameters& getParameters() const {
0292     return m_self->parameters();
0293   }
0294 
0295   /// Get the keys of all stored parameters for a given type
0296   ///
0297   /// @tparam T The desired parameter type
0298   ///
0299   /// @returns  A vector of keys for this parameter type
0300   template <ValidGenericDataType T>
0301   inline std::vector<std::string> getParameterKeys() const {
0302     return m_self->parameters().getKeys<T>();
0303   }
0304 
0305   /// Get all **currently** available collection names.
0306   ///
0307   /// @returns The names of all collections, including those that might still
0308   ///          need unpacking from the internal FrameData
0309   std::vector<std::string> getAvailableCollections() const {
0310     return m_self->availableCollections();
0311   }
0312 
0313   /// Get the name of the passed collection
0314   ///
0315   /// @param coll The collection for which the name should be obtained
0316   ///
0317   /// @returns The name of the collection or an empty optional if this
0318   ///          collection is not known to the Frame
0319   inline std::optional<std::string> getName(const podio::CollectionBase& coll) const {
0320     return getName(coll.getID());
0321   }
0322 
0323   /// Get the name for the passed collectionID
0324   ///
0325   /// @param collectionID The collection ID of the collection for which the name
0326   ///                     should be obtained
0327   /// @returns The name of the collection or an empty optional if this
0328   ///          collectionID is not known to the Frame
0329   inline std::optional<std::string> getName(const uint32_t collectionID) const {
0330     return m_self->getIDTable().name(collectionID);
0331   }
0332 
0333   // Interfaces for writing below
0334 
0335   /// Get a collection for writing.
0336   ///
0337   /// @note This method is intended for I/O purposes only and should not be used
0338   /// in other code.
0339   ///
0340   /// @returns The collection pointer in a prepared and "ready-to-write" state
0341   const podio::CollectionBase* getCollectionForWrite(const std::string& name) const {
0342     const auto* coll = m_self->get(name);
0343     if (coll) {
0344       coll->prepareForWrite();
0345     }
0346 
0347     return coll;
0348   }
0349 
0350   /// Get the internal CollectionIDTable for writing.
0351   ///
0352   /// @note This method is intended for I/O purposes only and should not be used
0353   /// in other code.
0354   ///
0355   /// @returns A copy of the internal collection id table
0356   podio::CollectionIDTable getCollectionIDTableForWrite() const {
0357     return m_self->getIDTable();
0358   }
0359 };
0360 
0361 // implementations below
0362 
0363 inline Frame::Frame() : Frame(std::make_unique<detail::EmptyFrameData>()) {
0364 }
0365 
0366 template <typename FrameDataT>
0367 Frame::Frame(std::unique_ptr<FrameDataT> data) : m_self(std::make_unique<FrameModel<FrameDataT>>(std::move(data))) {
0368 }
0369 
0370 template <RValueType FrameDataT>
0371 Frame::Frame(FrameDataT&& data) : Frame(std::make_unique<FrameDataT>(std::move(data))) {
0372 }
0373 
0374 template <CollectionType CollT>
0375 const CollT& Frame::get(const std::string& name) const {
0376   const auto* coll = dynamic_cast<const CollT*>(m_self->get(name));
0377   if (coll) {
0378     return *coll;
0379   }
0380   // TODO: Handle non-existing collections
0381   static const auto emptyColl = CollT();
0382   return emptyColl;
0383 }
0384 
0385 inline const podio::CollectionBase* Frame::get(const std::string& name) const {
0386   return m_self->get(name);
0387 }
0388 
0389 inline void Frame::put(std::unique_ptr<podio::CollectionBase> coll, const std::string& name) {
0390   const auto* retColl = m_self->put(std::move(coll), name);
0391   if (!retColl) {
0392     // TODO: Handle collisions
0393   }
0394 }
0395 
0396 template <CollectionRValueType CollT>
0397 const CollT& Frame::put(CollT&& coll, const std::string& name) {
0398   const auto* retColl = static_cast<const CollT*>(m_self->put(std::make_unique<CollT>(std::move(coll)), name));
0399   if (retColl) {
0400     return *retColl;
0401   }
0402   // TODO: Handle collision case
0403   static const auto emptyColl = CollT();
0404   return emptyColl;
0405 }
0406 
0407 template <typename FrameDataT>
0408 Frame::FrameModel<FrameDataT>::FrameModel(std::unique_ptr<FrameDataT> data) :
0409     m_mapMtx(std::make_unique<std::mutex>()), m_dataMtx(std::make_unique<std::mutex>()) {
0410   if (!data) {
0411     throw std::invalid_argument(
0412         "FrameData is a nullptr. If you are reading from a file it may be corrupted or you may reading beyond the end "
0413         "of the file, please check the validity of the data before creating a Frame.");
0414   }
0415   m_data = std::move(data);
0416   m_idTable = std::move(m_data->getIDTable());
0417   m_parameters = std::move(m_data->getParameters());
0418 }
0419 
0420 template <typename FrameDataT>
0421 const podio::CollectionBase* Frame::FrameModel<FrameDataT>::get(const std::string& name) const {
0422   return doGet(name);
0423 }
0424 
0425 template <typename FrameDataT>
0426 podio::CollectionBase* Frame::FrameModel<FrameDataT>::doGet(const std::string& name, bool setReferences) const {
0427   {
0428     // First check whether the collection is in the map already
0429     //
0430     // Collections only land here if they are fully unpacked, i.e.
0431     // prepareAfterRead has been called or it has been put into the Frame
0432     std::lock_guard lock{*m_mapMtx};
0433     if (const auto it = m_collections.find(name); it != m_collections.end()) {
0434       return it->second.get();
0435     }
0436   }
0437 
0438   podio::CollectionBase* retColl = nullptr;
0439 
0440   // Now try to get it from the raw data if we have the possibility
0441   if (m_data) {
0442     // Have the buffers in the outer scope here to hold the raw data lock as
0443     // briefly as possible
0444     auto buffers = std::optional<podio::CollectionReadBuffers>{std::nullopt};
0445     {
0446       std::lock_guard lock{*m_dataMtx};
0447       buffers = unpack(m_data.get(), name);
0448     }
0449     if (buffers) {
0450       std::unique_ptr<podio::CollectionBase> coll{nullptr};
0451       // Subset collections do not need schema evolution (by definition)
0452       if (buffers->data == nullptr) {
0453         coll = buffers->createCollection(buffers.value(), true);
0454       } else {
0455         auto evolvedBuffers = podio::SchemaEvolution::instance().evolveBuffers(buffers.value(), buffers->schemaVersion,
0456                                                                                std::string(buffers->type));
0457         coll = evolvedBuffers.createCollection(evolvedBuffers, false);
0458       }
0459 
0460       coll->prepareAfterRead();
0461       coll->setID(m_idTable.collectionID(name).value());
0462       {
0463         std::lock_guard mapLock{*m_mapMtx};
0464         auto [it, success] = m_collections.emplace(name, std::move(coll));
0465         // TODO: Check success? Or simply assume that everything is fine at this point?
0466         // TODO: Collision handling?
0467         retColl = it->second.get();
0468       }
0469 
0470       if (setReferences) {
0471         retColl->setReferences(this);
0472       }
0473     }
0474   }
0475 
0476   return retColl;
0477 }
0478 
0479 template <typename FrameDataT>
0480 bool Frame::FrameModel<FrameDataT>::get(uint32_t collectionID, CollectionBase*& collection) const {
0481   const auto name = m_idTable.name(collectionID);
0482   if (!name) {
0483     return false;
0484   }
0485   const auto& [_, inserted] = m_retrievedIDs.insert(collectionID);
0486 
0487   if (inserted) {
0488     auto coll = doGet(name.value());
0489     if (coll) {
0490       collection = coll;
0491       return true;
0492     }
0493   } else {
0494     auto coll = doGet(name.value(), false);
0495     if (coll) {
0496       collection = coll;
0497       return true;
0498     }
0499   }
0500 
0501   return false;
0502 }
0503 
0504 template <typename FrameDataT>
0505 const podio::CollectionBase* Frame::FrameModel<FrameDataT>::put(std::unique_ptr<podio::CollectionBase> coll,
0506                                                                 const std::string& name) {
0507   {
0508     std::lock_guard lock{*m_mapMtx};
0509     auto [it, success] = m_collections.try_emplace(name, std::move(coll));
0510     if (success) {
0511       // TODO: Check whether this collection is already known to the idTable
0512       // -> What to do on collision?
0513       // -> Check before we emplace it into the internal map to prevent possible
0514       //    collisions from collections that are potentially present from rawdata?
0515       it->second->setID(m_idTable.add(name));
0516       return it->second.get();
0517     } else {
0518       throw std::invalid_argument("An object with key " + name + " already exists in the frame");
0519     }
0520   }
0521 
0522   return nullptr;
0523 }
0524 
0525 template <typename FrameDataT>
0526 std::vector<std::string> Frame::FrameModel<FrameDataT>::availableCollections() const {
0527   // TODO: Check if there is a more efficient way to do this. Currently this is
0528   // done very conservatively, but in a way that should always work, regardless
0529   // of assumptions. It might be possible to simply return what is in the
0530   // idTable here, because that should in principle encompass everything that is
0531   // in the raw data as well as things that have been put into the frame
0532 
0533   // Lock both the internal map and the rawdata for this
0534   std::scoped_lock lock{*m_mapMtx, *m_dataMtx};
0535 
0536   auto collections = m_data->getAvailableCollections();
0537   collections.reserve(collections.size() + m_collections.size());
0538 
0539   for (const auto& [name, _] : m_collections) {
0540     collections.push_back(name);
0541   }
0542 
0543   return collections;
0544 }
0545 
0546 } // namespace podio
0547 
0548 #endif // PODIO_FRAME_H