Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-26 09:24:15

0001 /// \file ROOT/RFile.hxx
0002 /// \ingroup Base ROOT7
0003 /// \author Giacomo Parolini <giacomo.parolini@cern.ch>
0004 /// \date 2025-03-19
0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. Feedback
0006 /// is welcome!
0007 
0008 #ifndef ROOT7_RFile
0009 #define ROOT7_RFile
0010 
0011 #include <Compression.h>
0012 #include <ROOT/RError.hxx>
0013 
0014 #include <deque>
0015 #include <functional>
0016 #include <iostream>
0017 #include <memory>
0018 #include <string_view>
0019 #include <typeinfo>
0020 #include <variant>
0021 
0022 class TFile;
0023 class TIterator;
0024 class TKey;
0025 
0026 namespace ROOT {
0027 namespace Experimental {
0028 
0029 class RKeyInfo;
0030 class RFile;
0031 
0032 namespace Internal {
0033 
0034 ROOT::RLogChannel &RFileLog();
0035 
0036 /// Returns an **owning** pointer to the object referenced by `key`. The caller must delete this pointer.
0037 /// This method is meant to only be used by the pythonization.
0038 [[nodiscard]] void *RFile_GetObjectFromKey(RFile &file, const RKeyInfo &key);
0039 
0040 TFile *GetRFileTFile(RFile &rfile);
0041 
0042 } // namespace Internal
0043 
0044 namespace Detail {
0045 
0046 /// Given a "path-like" string (like foo/bar/baz), returns a pair `{ dirName, baseName }`.
0047 /// `baseName` will be empty if the string ends with '/'.
0048 /// `dirName` will be empty if the string contains no '/'.
0049 /// `dirName`, if not empty, always ends with a '/'.
0050 /// NOTE: this function does no semantic checking or path expansion, nor does it interact with the
0051 /// filesystem in any way (so it won't follow symlink or anything like that).
0052 /// Moreover it doesn't trim the path in any way, so any leading or trailing whitespaces will be preserved.
0053 /// This function does not perform any copy: the returned string_views have the same lifetime as `path`.
0054 std::pair<std::string_view, std::string_view> DecomposePath(std::string_view path);
0055 
0056 }
0057 
0058 class RFileKeyIterable;
0059 
0060 /**
0061 \class ROOT::Experimental::RKeyInfo
0062 \ingroup RFile
0063 \brief Information about an RFile object's Key.
0064 
0065 Every object inside a ROOT file has an associated "Key" which contains metadata on the object, such as its name, type
0066 etc.
0067 Querying this information can be done via RFile::ListKeys(). Reading an object's Key
0068 doesn't deserialize the full object, so it's a relatively lightweight operation.
0069 */
0070 class RKeyInfo final {
0071    friend class ROOT::Experimental::RFile;
0072    friend class ROOT::Experimental::RFileKeyIterable;
0073 
0074 public:
0075    enum class ECategory : std::uint16_t {
0076       kInvalid,
0077       kObject,
0078       kDirectory
0079    };
0080 
0081 private:
0082    std::string fPath;
0083    std::string fTitle;
0084    std::string fClassName;
0085    std::uint16_t fCycle = 0;
0086    ECategory fCategory = ECategory::kInvalid;
0087    std::uint64_t fLenObj = 0;
0088    std::uint64_t fNBytesObj = 0;
0089    std::uint64_t fNBytesKey = 0;
0090    std::uint64_t fSeekKey = 0;
0091    std::uint64_t fSeekParentDir = 0;
0092 
0093    explicit RKeyInfo(const TKey &key);
0094 
0095 public:
0096    RKeyInfo() = default;
0097 
0098    /// Returns the absolute path of this key, i.e. the directory part plus the object name.
0099    const std::string &GetPath() const { return fPath; }
0100    /// Returns the base name of this key, i.e. the name of the object without the directory part.
0101    std::string GetBaseName() const { return std::string(Detail::DecomposePath(fPath).second); }
0102    const std::string &GetTitle() const { return fTitle; }
0103    const std::string &GetClassName() const { return fClassName; }
0104    std::uint16_t GetCycle() const { return fCycle; }
0105    ECategory GetCategory() const { return fCategory; }
0106    /// Returns the in-memory size of the uncompressed object.
0107 
0108    std::uint64_t GetLenObj() const { return fLenObj; }
0109    /// Returns the on-disk size of the (potentially compressed) object, excluding its key.
0110    std::uint64_t GetNBytesObj() const { return fNBytesObj; }
0111 
0112    /// Returns the on-disk size of this object's key.
0113    std::uint64_t GetNBytesKey() const { return fNBytesKey; }
0114    /// Returns the on-disk offset of this object's key.
0115    std::uint64_t GetSeekKey() const { return fSeekKey; }
0116 
0117    /// Returns the on-disk offset of this object's parent directory key.
0118    std::uint64_t GetSeekParentDir() const { return fSeekParentDir; }
0119 };
0120 
0121 /// The iterable returned by RFile::ListKeys()
0122 class RFileKeyIterable final {
0123    using Pattern_t = std::string;
0124 
0125    TFile *fFile = nullptr;
0126    Pattern_t fPattern;
0127    std::uint32_t fFlags = 0;
0128 
0129 public:
0130    class RIterator {
0131       friend class RFileKeyIterable;
0132 
0133       struct RIterStackElem {
0134          // This is ugly, but TList returns an (owning) pointer to a polymorphic TIterator...and we need this class
0135          // to be copy-constructible.
0136          std::shared_ptr<TIterator> fIter;
0137          std::string fDirPath;
0138 
0139          // Outlined to avoid including TIterator.h
0140          RIterStackElem(TIterator *it, const std::string &path = "");
0141          // Outlined to avoid including TIterator.h
0142          ~RIterStackElem();
0143 
0144          // fDirPath doesn't need to be compared because it's implied by fIter.
0145          bool operator==(const RIterStackElem &other) const { return fIter == other.fIter; }
0146       };
0147 
0148       // Using a deque to have pointer stability
0149       std::deque<RIterStackElem> fIterStack;
0150       Pattern_t fPattern;
0151       const TKey *fCurKey = nullptr;
0152       std::uint16_t fRootDirNesting = 0;
0153       std::uint32_t fFlags = 0;
0154 
0155       void Advance();
0156 
0157       // NOTE: `iter` here is an owning pointer (or null)
0158       RIterator(TIterator *iter, Pattern_t pattern, std::uint32_t flags);
0159 
0160    public:
0161       using iterator = RIterator;
0162       using iterator_category = std::input_iterator_tag;
0163       using difference_type = std::ptrdiff_t;
0164       using value_type = RKeyInfo;
0165       using pointer = const value_type *;
0166       using reference = const value_type &;
0167 
0168       iterator &operator++()
0169       {
0170          Advance();
0171          return *this;
0172       }
0173       value_type operator*();
0174       bool operator!=(const iterator &rh) const { return !(*this == rh); }
0175       bool operator==(const iterator &rh) const { return fIterStack == rh.fIterStack; }
0176    };
0177 
0178    RFileKeyIterable(TFile *file, std::string_view rootDir, std::uint32_t flags)
0179       : fFile(file), fPattern(std::string(rootDir)), fFlags(flags)
0180    {
0181    }
0182 
0183    RIterator begin() const;
0184    RIterator end() const;
0185 };
0186 
0187 /**
0188 \class ROOT::Experimental::RFile
0189 \ingroup RFile
0190 \brief An interface to read from, or write to, a ROOT file, as well as performing other common operations.
0191 
0192 Please refer to the documentation of TFile for the details related to how data and executable code can be stored
0193 in ROOT files.
0194 
0195 ## When and why should you use RFile
0196 
0197 RFile is a modern and minimalistic interface to ROOT files, both local and remote, that can be used instead of TFile
0198 when you only need basic Put/Get operations and don't need the more advanced TFile/TDirectory functionalities.
0199 It provides:
0200 - a simple interface that makes it easy to do things right and hard to do things wrong;
0201 - more robustness and better error reporting for those operations;
0202 - clearer ownership semantics expressed through the type system.
0203 
0204 RFile doesn't cover the entirety of use cases covered by TFile/TDirectory/TDirectoryFile and is not
0205 a 1:1 replacement for them.  It is meant to simplify the most common use cases by following newer standard C++
0206 practices.
0207 
0208 ## Ownership model
0209 
0210 RFile handles ownership via smart pointers, typically std::unique_ptr.
0211 
0212 When getting an object from the file (via RFile::Get) you get back a unique copy of the object. Calling `Get` on the
0213 same object twice produces two independent clones of the object. The ownership over that object is solely on the caller
0214 and not shared with the RFile. Therefore, the object will remain valid after closing or destroying the RFile that
0215 generated it. This also means that any modification done to the object are **not** reflected to the file automatically:
0216 to update the object in the file you need to write it again (via RFile::Overwrite).
0217 
0218 RFile::Put and RFile::Overwrite are the way to write objects to the file. Both methods take a const reference to the
0219 object to write and don't change the ownership of the object in any way. Calling Put or Overwrite doesn't guarantee that
0220 the object is immediately written to the underlying storage: to ensure that, you need to call RFile::Flush (or close the
0221 file).
0222 
0223 ## Directories
0224 
0225 Even though there is no equivalent of TDirectory in the RFile API, directories are still an existing concept in RFile
0226 (since they are a concept in the ROOT binary format). However they are for now only interacted with indirectly, via the
0227 use of filesystem-like string-based paths. If you Put an object in an RFile under the path "path/to/object", "object"
0228 will be stored under directory "to" which is in turn stored under directory "path". This hierarchy is encoded in the
0229 ROOT file itself and it can provide some optimization and/or conveniences when querying objects.
0230 
0231 For the most part, it is convenient to think about RFile in terms of a key-value storage where string-based paths are
0232 used to refer to arbitrary objects. However, given the hierarchical nature of ROOT files, certain filesystem-like
0233 properties are applied to paths, for ease of use: the '/' character is treated specially as the directory separator;
0234 multiple '/' in a row are collapsed into one (since RFile doesn't allow directories with empty names).
0235 
0236 At the moment, RFile doesn't allow getting directories via Get, nor writing ones via Put (this may change in the
0237 future).
0238 
0239 ## Sample usage
0240 Opening an RFile (for writing) and writing an object to it:
0241 ~~~{.cpp}
0242 auto rfile = ROOT::RFile::Recreate("my_file.root");
0243 auto myObj = TH1D("h", "h", 10, 0, 1);
0244 rfile->Put(myObj.GetName(), myObj);
0245 ~~~
0246 
0247 Opening an RFile (for reading) and reading an object from it:
0248 ~~~{.cpp}
0249 auto rfile = ROOT::RFile::Open("my_file.root");
0250 auto myObj = file->Get<TH1D>("h");
0251 ~~~
0252 */
0253 class RFile final {
0254    friend void *Internal::RFile_GetObjectFromKey(RFile &file, const RKeyInfo &key);
0255    friend TFile *Internal::GetRFileTFile(RFile &rfile);
0256 
0257    /// Flags used in PutInternal()
0258    enum PutFlags {
0259       /// When encountering an object at the specified path, overwrite it with the new one instead of erroring out.
0260       kPutAllowOverwrite = 0x1,
0261       /// When overwriting an object, preserve the existing one and create a new cycle, rather than removing it.
0262       kPutOverwriteKeepCycle = 0x2,
0263    };
0264 
0265    std::unique_ptr<TFile> fFile;
0266 
0267    // Outlined to avoid including TFile.h
0268    explicit RFile(std::unique_ptr<TFile> file);
0269 
0270    /// Gets object `path` from the file and returns an **owning** pointer to it.
0271    /// The caller should immediately wrap it into a unique_ptr of the type described by `type`.
0272    [[nodiscard]] void *GetUntyped(std::string_view path,
0273                                   std::variant<const char *, std::reference_wrapper<const std::type_info>> type) const;
0274 
0275    /// Writes `obj` to file, without taking its ownership.
0276    void PutUntyped(std::string_view path, const std::type_info &type, const void *obj, std::uint32_t flags);
0277 
0278    /// \see Put
0279    template <typename T>
0280    void PutInternal(std::string_view path, const T &obj, std::uint32_t flags)
0281    {
0282       PutUntyped(path, typeid(T), &obj, flags);
0283    }
0284 
0285    /// Given `path`, returns the TKey corresponding to the object at that path (assuming the path is fully split, i.e.
0286    /// "a/b/c" always means "object 'c' inside directory 'b' inside directory 'a'").
0287    /// IMPORTANT: `path` must have been validated/normalized via ValidateAndNormalizePath() (see RFile.cxx).
0288    TKey *GetTKey(std::string_view path) const;
0289 
0290 public:
0291    enum EListKeyFlags {
0292       kListObjects = 1 << 0,
0293       kListDirs = 1 << 1,
0294       kListRecursive = 1 << 2,
0295    };
0296 
0297    struct RRecreateOptions {
0298       /// See core/zip/inc/Compression.h for the meaning of the `compression` argument.
0299       /// Default compression is 505 (ZSTD level 10).
0300       int fCompressionSettings = ROOT::RCompressionSetting::EDefaults::kUseGeneralPurpose;
0301 
0302       RRecreateOptions();
0303    };
0304 
0305    // This is arbitrary, but it's useful to avoid pathological cases
0306    static constexpr int kMaxPathNesting = 1000;
0307 
0308    ///// Factory methods /////
0309 
0310    /// Opens the file for reading. `path` may be a regular file path or a remote URL.
0311    /// \throw ROOT::RException if the file at `path` could not be opened.
0312    static std::unique_ptr<RFile> Open(std::string_view path);
0313 
0314    /// Opens the file for reading/writing, overwriting it if it already exists.
0315    /// \throw ROOT::RException if a file could not be created at `path` (e.g. if the specified
0316    /// directory tree does not exist).
0317    static std::unique_ptr<RFile> Recreate(std::string_view path, const RRecreateOptions &opts = RRecreateOptions());
0318 
0319    /// Opens the file for updating, creating a new one if it doesn't exist.
0320    /// \throw ROOT::RException if the file at `path` could neither be read nor created
0321    /// (e.g. if the specified directory tree does not exist).
0322    static std::unique_ptr<RFile> Update(std::string_view path);
0323 
0324    ///// Instance methods /////
0325 
0326    // Outlined to avoid including TFile.h
0327    ~RFile();
0328 
0329    /// Retrieves an object from the file.
0330    /// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown.
0331    /// See \ref ValidateAndNormalizePath() for info about valid path names.
0332    /// If the object is not there returns a null pointer.
0333    template <typename T>
0334    std::unique_ptr<T> Get(std::string_view path) const
0335    {
0336       void *obj = GetUntyped(path, typeid(T));
0337       return std::unique_ptr<T>(static_cast<T *>(obj));
0338    }
0339 
0340    /// Puts an object into the file.
0341    /// The application retains ownership of the object.
0342    /// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown.
0343    /// See \ref ValidateAndNormalizePath() for info about valid path names.
0344    ///
0345    /// Throws a RException if `path` already identifies a valid object or directory.
0346    /// Throws a RException if the file was opened in read-only mode.
0347    template <typename T>
0348    void Put(std::string_view path, const T &obj)
0349    {
0350       PutInternal(path, obj, /* flags = */ 0);
0351    }
0352 
0353    /// Puts an object into the file, overwriting any previously-existing object at that path.
0354    /// The application retains ownership of the object.
0355    ///
0356    /// If an object already exists at that path, it is kept as a backup cycle unless `backupPrevious` is false.
0357    /// Note that even if `backupPrevious` is false, any existing cycle except the latest will be preserved.
0358    ///
0359    /// Throws a RException if `path` is already the path of a directory.
0360    /// Throws a RException if the file was opened in read-only mode.
0361    template <typename T>
0362    void Overwrite(std::string_view path, const T &obj, bool backupPrevious = true)
0363    {
0364       std::uint32_t flags = kPutAllowOverwrite;
0365       flags |= backupPrevious * kPutOverwriteKeepCycle;
0366       PutInternal(path, obj, flags);
0367    }
0368 
0369    /// Writes all objects and the file structure to disk.
0370    /// Returns the number of bytes written.
0371    size_t Flush();
0372 
0373    /// Flushes the RFile if needed and closes it, disallowing any further reading or writing.
0374    void Close();
0375 
0376    /// Returns an iterable over all keys of objects and/or directories written into this RFile starting at path
0377    /// `basePath` (defaulting to include the content of all subdirectories).
0378    /// By default, keys referring to directories are not returned: only those referring to leaf objects are.
0379    /// If `basePath` is the path of a leaf object, only `basePath` itself will be returned.
0380    /// `flags` is a bitmask specifying the listing mode.
0381    /// If `(flags & kListObjects) != 0`, the listing will include keys of non-directory objects (default);
0382    /// If `(flags & kListDirs) != 0`, the listing will include keys of directory objects;
0383    /// If `(flags & kListRecursive) != 0`, the listing will recurse on all subdirectories of `basePath` (default),
0384    /// otherwise it will only list immediate children of `basePath`.
0385    ///
0386    /// Example usage:
0387    /// ~~~{.cpp}
0388    /// for (RKeyInfo key : file->ListKeys()) {
0389    ///     /* iterate over all objects in the RFile */
0390    ///     cout << key.GetPath() << ";" << key.GetCycle() << " of type " << key.GetClassName() << "\n";
0391    /// }
0392    /// for (RKeyInfo key : file->ListKeys("", kListDirs|kListObjects|kListRecursive)) {
0393    ///     /* iterate over all objects and directories in the RFile */
0394    /// }
0395    /// for (RKeyInfo key : file->ListKeys("a/b", kListObjects)) {
0396    ///     /* iterate over all objects that are immediate children of directory "a/b" */
0397    /// }
0398    /// for (RKeyInfo key : file->ListKeys("foo", kListDirs|kListRecursive)) {
0399    ///     /* iterate over all directories under directory "foo", recursively */
0400    /// }
0401    /// ~~~
0402    RFileKeyIterable ListKeys(std::string_view basePath = "", std::uint32_t flags = kListObjects | kListRecursive) const
0403    {
0404       return RFileKeyIterable(fFile.get(), basePath, flags);
0405    }
0406 
0407    /// Retrieves information about the key of object at `path`, if one exists.
0408    std::optional<RKeyInfo> GetKeyInfo(std::string_view path) const;
0409 
0410    /// Prints the internal structure of this RFile to the given stream.
0411    void Print(std::ostream &out = std::cout) const;
0412 };
0413 
0414 } // namespace Experimental
0415 } // namespace ROOT
0416 
0417 #endif