Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:06:09

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