Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-13 09:15:00

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2025 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_READER_H
0007 #define YODA_READER_H
0008 
0009 #include "YODA/Config/BuildConfig.h"
0010 #include "YODA/Utils/ReaderUtils.h"
0011 #include "YODA/Utils/Traits.h"
0012 
0013 #ifdef HAVE_HDF5
0014 #include <YODA/Config/BuildConfig.h>
0015 #ifdef WITH_HIGHFIVE
0016 #include <YODA/highfive/H5File.hpp>
0017 #else
0018 #include <highfive/H5File.hpp>
0019 #define YODA_H5 HighFive
0020 #endif
0021 #endif
0022 
0023 #include <memory>
0024 #include <fstream>
0025 #include <iostream>
0026 #include <sstream>
0027 #include <type_traits>
0028 #include <unordered_map>
0029 #include <vector>
0030 
0031 namespace YODA {
0032 
0033 
0034   /// Pure virtual base class for various output writers.
0035   class Reader {
0036 
0037   public:
0038 
0039     /// Convenience alias for AO Reader
0040     using TypeRegisterItr = typename std::unordered_map<std::string, std::unique_ptr<AOReaderBase>>::const_iterator;
0041 
0042     /// Virtual destructor
0043     virtual ~Reader() {
0044       // This is technically leaking memory, but since this
0045       // is a (static) singleton, there should be no issue.
0046       // Cython relies on it for data-structure alignment, too.
0047       for (auto& aor : _register) { aor.second.release(); }
0048     }
0049 
0050 
0051     /// @name Reading multiple analysis objects,
0052     /// @{
0053 
0054     /// @brief Read in a collection of objects @a objs from output stream @a stream.
0055     ///
0056     /// This version fills (actually, appends to) a variable supplied container
0057     /// Note: SFINAE is used to check for a void push_back(const AnalysisObject*) method
0058     ///
0059     /// @todo Extend SFINAE Pushable cf. Writer to allow adding to containers of smart ptr type
0060     template<typename CONT>
0061     typename std::enable_if_t<YODA::Pushable<CONT,AnalysisObject*>::value>
0062     read(std::istream& stream, CONT& aos, const std::string& match = "", const std::string& unmatch = "") {
0063       // if CONT==std::vector<AnalysisObject*>, the compiler should select
0064       // the virtual method below, since it prefers non-templated methods in the lookup
0065       // otherwise we would enter a endless recursion. Check in case of problems.
0066       std::vector<AnalysisObject*> v_aos;
0067       read(stream, v_aos, match, unmatch);
0068       for (const AnalysisObject* ao : v_aos) aos.push_back(ao);
0069     }
0070 
0071     /// @brief Read in a collection of objects @a objs from output stream @a stream.
0072     ///
0073     /// This version fills (actually, appends to) a supplied vector, avoiding copying,
0074     /// and is hence CPU efficient.
0075     ///
0076     virtual void read(std::istream& stream, std::vector<AnalysisObject*>& aos,
0077                       const std::string& match = "", const std::string& unmatch = "") = 0;
0078 
0079     /// @brief Read in a collection of objects @a objs from an HDF5 @a file.
0080     ///
0081     /// This version fills (actually, appends to) a supplied vector, avoiding copying,
0082     /// and is hence CPU efficient.
0083     ///
0084     #ifdef HAVE_HDF5
0085     virtual void read(const YODA_H5::File& file, std::vector<AnalysisObject*>& aos,
0086                       const std::string& match = "", const std::string& unmatch = "") = 0;
0087     #endif
0088 
0089     /// @brief Read in a collection of objects from output stream @a stream.
0090     ///
0091     /// This version returns a vector by value, involving copying, and is hence less
0092     /// CPU efficient than the alternative version where a vector is filled by reference.
0093     std::vector<AnalysisObject*> read(std::istream& stream, const std::string& match = "",
0094                                                             const std::string& unmatch = "") {
0095       std::vector<AnalysisObject*> rtn;
0096       read(stream, rtn, match, unmatch);
0097       return rtn;
0098     }
0099 
0100 
0101     /// @brief Read in a collection of objects from an H5 @a file.
0102     ///
0103     /// This version returns a vector by value, involving copying, and is hence less
0104     /// CPU efficient than the alternative version where a vector is filled by reference.
0105     #ifdef HAVE_HDF5
0106     std::vector<AnalysisObject*> read(const YODA_H5::File& file, const std::string& match = "",
0107                                                                   const std::string& unmatch = "") {
0108       std::vector<AnalysisObject*> rtn;
0109       read(file, rtn, match, unmatch);
0110       return rtn;
0111     }
0112     #endif
0113 
0114 
0115     /// @brief Read in a collection of objects @a objs from file @a filename.
0116     ///
0117     ///
0118     /// This version fills (actually, appends to) a variable supplied container
0119     /// Note: SFINAE is used to check for a void push_back(const AnalysisObject*) method
0120     ///
0121     /// @todo Extend SFINAE Pushable cf. Writer to allow adding to containers of smart ptr type
0122     template<typename CONT>
0123     typename std::enable_if_t<YODA::Pushable<CONT,AnalysisObject*>::value>
0124     read(const std::string& filename, CONT& aos, const std::string& match = "", const std::string& unmatch = "") {
0125       // if CONT==std::vector<AnalysisObject*>, the compiler should select
0126       // the virtual method below, since it prefers non-templated methods in the lookup
0127       // otherwise we would enter a endless recursion. Check in case of problems.
0128       std::vector<AnalysisObject*> v_aos;
0129       read(filename, v_aos, match, unmatch);
0130       for (const AnalysisObject* ao : v_aos) aos.push_back(ao);
0131     }
0132 
0133     /// @brief Read in a collection of objects @a objs from file @a filename.
0134     ///
0135     /// This version fills (actually, appends to) a supplied vector, avoiding copying,
0136     /// and is hence CPU efficient.
0137     ///
0138     void read(const std::string& filename, std::vector<AnalysisObject*>& aos,
0139               const std::string& match = "", const std::string& unmatch = "") {
0140       if (filename != "-") {
0141         try {
0142           const size_t lastdot = filename.find_last_of(".");
0143           std::string fmt = Utils::toLower(lastdot == std::string::npos ? filename : filename.substr(lastdot+1));
0144           #ifdef HAVE_HDF5
0145           // check if the requested format is H5
0146           if (Utils::startswith(fmt, "h5")) {
0147             try {
0148               const YODA_H5::File h5(filename, YODA_H5::File::ReadOnly);
0149               read(h5, aos, match, unmatch);
0150             } catch (YODA::ReadError& e) {
0151               throw ReadError("Reading from filename " + filename + " failed: " + e.what());
0152             }
0153             return;
0154           }
0155           #endif
0156           std::ifstream instream;
0157           instream.open(filename.c_str());
0158           if (instream.fail())
0159             throw ReadError("Reading from filename " + filename + " failed");
0160           read(instream, aos, match, unmatch);
0161           instream.close();
0162         } catch (std::ifstream::failure& e) {
0163           throw ReadError("Reading from filename " + filename + " failed: " + e.what());
0164         }
0165       } else {
0166         try {
0167           read(std::cin, aos, match, unmatch);
0168         } catch (std::runtime_error& e) {
0169           throw ReadError("Reading from stdin failed: " + std::string(e.what()));
0170         }
0171       }
0172     }
0173 
0174     /// @brief Read in a collection of objects from output stream @a stream.
0175     ///
0176     /// This version returns a vector by value, involving copying, and is hence less
0177     /// CPU efficient than the alternative version where a vector is filled by reference.
0178     std::vector<AnalysisObject*> read(const std::string& filename, const std::string& match = "",
0179                                                                    const std::string& unmatch = "") {
0180       std::vector<AnalysisObject*> rtn;
0181       read(filename, rtn, match, unmatch);
0182       return rtn;
0183     }
0184 
0185     /// @}
0186 
0187     /// @name Utilities
0188     /// @{
0189 
0190     /// @brief AO type registration
0191     template<typename T>
0192     void registerType() {
0193       const string key = Utils::toUpper(T().type());
0194       const TypeRegisterItr& res = _register.find(key);
0195       if (res == _register.end())  _register[key] = std::make_unique<AOReader<T>>();
0196     }
0197 
0198     /// @brief Check if a string matches any of the given @a patterns,
0199     /// and that it doesn't match any @a unpatterns (for path filtering)
0200     bool patternCheck(const std::string& path, const std::vector<std::regex>& patterns,
0201                                                const std::vector<std::regex>& unpatterns) {
0202       bool skip = false;
0203       if (patterns.size()) {
0204         skip = true;
0205         for (const std::regex& re : patterns) {
0206           if (std::regex_search(path, re)) { skip = false; break; }
0207         }
0208       }
0209       if (!skip && unpatterns.size()) {
0210         for (const std::regex& re : unpatterns) {
0211           if (std::regex_search(path, re)) { skip = true; break; }
0212         }
0213       }
0214       return !skip;
0215     }
0216 
0217     /// @}
0218 
0219     /// Map of registered AO Readers
0220     std::unordered_map<string, std::unique_ptr<AOReaderBase>> _register;
0221 
0222   };
0223 
0224 
0225   /// Factory function to make a reader object by format name or a filename
0226   Reader& mkReader(const std::string& format_name);
0227 
0228 
0229 }
0230 
0231 #endif