Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-07 09:25:39

0001 /// \file ROOT/RNTupleReader.hxx
0002 /// \ingroup NTuple
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2024-02-20
0005 
0006 /*************************************************************************
0007  * Copyright (C) 1995-2024, Rene Brun and Fons Rademakers.               *
0008  * All rights reserved.                                                  *
0009  *                                                                       *
0010  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0011  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0012  *************************************************************************/
0013 
0014 #ifndef ROOT_RNTupleReader
0015 #define ROOT_RNTupleReader
0016 
0017 #include <ROOT/RConfig.hxx> // for R__unlikely
0018 #include <ROOT/REntry.hxx>
0019 #include <ROOT/RError.hxx>
0020 #include <ROOT/RNTupleDescriptor.hxx>
0021 #include <ROOT/RNTupleMetrics.hxx>
0022 #include <ROOT/RNTupleModel.hxx>
0023 #include <ROOT/RNTupleReadOptions.hxx>
0024 #include <ROOT/RNTupleTypes.hxx>
0025 #include <ROOT/RNTupleView.hxx>
0026 #include <ROOT/RPageStorage.hxx>
0027 #include <ROOT/RSpan.hxx>
0028 
0029 #include <iostream>
0030 #include <iterator>
0031 #include <memory>
0032 #include <mutex>
0033 #include <string>
0034 #include <string_view>
0035 #include <unordered_map>
0036 
0037 namespace ROOT {
0038 class RNTuple;
0039 
0040 /// Listing of the different options that can be printed by RNTupleReader::GetInfo()
0041 enum class ENTupleInfo {
0042    kSummary,        // The RNTuple name, description, number of entries
0043    kStorageDetails, // size on storage, page sizes, compression factor, etc.
0044    kMetrics,        // internals performance counters, requires that EnableMetrics() was called
0045 };
0046 
0047 // clang-format off
0048 /**
0049 \class ROOT::RNTupleReader
0050 \ingroup NTuple
0051 \brief Reads RNTuple data from storage
0052 
0053 The RNTupleReader provides access to data stored in the RNTuple binary format as C++ objects, using an RNTupleModel.
0054 It infers this model from the RNTuple's on-disk metadata, or uses a model imposed by the user.
0055 The latter case allows users to read into a specialized RNTuple model that covers
0056 only a subset of the fields in the RNTuple. The RNTuple model is used when reading complete entries through LoadEntry().
0057 Individual fields can be read as well by instantiating a tree view.
0058 
0059 ~~~ {.cpp}
0060 #include <ROOT/RNTupleReader.hxx>
0061 #include <iostream>
0062 
0063 auto reader = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0064 std::cout << "myNTuple has " << reader->GetNEntries() << " entries\n";
0065 ~~~
0066 */
0067 // clang-format on
0068 class RNTupleReader {
0069 private:
0070    /// Shared data structure between the reader and all the issued active entry tokens.
0071    struct RActiveEntriesControlBlock {
0072       /// Points to the page source backing the associated RNTupleReader. When the reader is destructed, the
0073       /// page source is reset to nullptr. At that point, operations on remaining active entry tokens become noops.
0074       Internal::RPageSource *fPageSource = nullptr;
0075       /// Reference counter of clusters pinned in the page source due to entries being marked as active.
0076       std::unordered_map<ROOT::DescriptorId_t, std::uint64_t> fActiveClusters;
0077       std::mutex fLock;
0078 
0079       explicit RActiveEntriesControlBlock(Internal::RPageSource *pageSource) : fPageSource(pageSource) {}
0080    };
0081 
0082    /// Set as the page source's scheduler for parallel page decompression if implicit multi-threading (IMT) is on.
0083    /// Needs to be destructed after the page source is destructed (and thus be declared before)
0084    std::unique_ptr<Internal::RPageStorage::RTaskScheduler> fUnzipTasks;
0085 
0086    std::unique_ptr<Internal::RPageSource> fSource;
0087    /// Needs to be destructed before fSource
0088    std::unique_ptr<ROOT::RNTupleModel> fModel;
0089    /// We use a dedicated on-demand reader for Show(). Printing data uses all the fields
0090    /// from the full model even if the analysis code uses only a subset of fields. The display reader
0091    /// is a clone of the original reader.
0092    std::unique_ptr<RNTupleReader> fDisplayReader;
0093    /// The RNTuple descriptor in the page source is protected by a read-write lock. We don't expose that to the
0094    /// users of RNTupleReader::GetDescriptor().  Instead, if descriptor information is needed, we clone the
0095    /// descriptor.  Using the descriptor's generation number, we know if the cached descriptor is stale.
0096    /// Retrieving descriptor data from an RNTupleReader is supposed to be for testing and information purposes,
0097    /// not on a hot code path.
0098    std::optional<ROOT::RNTupleDescriptor> fCachedDescriptor;
0099    /// We know that the RNTupleReader is always reading a single RNTuple, so the number of entries is fixed.
0100    ROOT::NTupleSize_t fNEntries = 0;
0101    Experimental::Detail::RNTupleMetrics fMetrics;
0102    /// If not nullopt, these will be used when creating the model
0103    std::optional<ROOT::RNTupleDescriptor::RCreateModelOptions> fCreateModelOptions;
0104    /// Initialized when the page source is connected. It is then shared between the reader instance and all
0105    /// active entry tokens. When the reader destructs, it resets the page source pointer in the control block.
0106    std::shared_ptr<RActiveEntriesControlBlock> fActiveEntriesControlBlock;
0107 
0108    RNTupleReader(std::unique_ptr<ROOT::RNTupleModel> model, std::unique_ptr<Internal::RPageSource> source,
0109                  const ROOT::RNTupleReadOptions &options);
0110    /// The model is generated from the RNTuple metadata on storage.
0111    explicit RNTupleReader(std::unique_ptr<Internal::RPageSource> source, const ROOT::RNTupleReadOptions &options);
0112 
0113    void ConnectModel(ROOT::RNTupleModel &model, bool allowFieldSubstitutions);
0114    RNTupleReader *GetDisplayReader();
0115    void InitPageSource(bool enableMetrics);
0116 
0117    ROOT::DescriptorId_t RetrieveFieldId(std::string_view fieldName) const;
0118 
0119 public:
0120    // Browse through the entries
0121    class RIterator {
0122    private:
0123       ROOT::NTupleSize_t fIndex = ROOT::kInvalidNTupleIndex;
0124 
0125    public:
0126       using iterator = RIterator;
0127       using iterator_category = std::input_iterator_tag;
0128       using value_type = ROOT::NTupleSize_t;
0129       using difference_type = std::ptrdiff_t;
0130       using pointer = const ROOT::NTupleSize_t *;
0131       using reference = const ROOT::NTupleSize_t &;
0132 
0133       RIterator() = default;
0134       explicit RIterator(ROOT::NTupleSize_t index) : fIndex(index) {}
0135       ~RIterator() = default;
0136 
0137       iterator operator++(int) /* postfix */
0138       {
0139          auto r = *this;
0140          fIndex++;
0141          return r;
0142       }
0143       iterator &operator++() /* prefix */
0144       {
0145          ++fIndex;
0146          return *this;
0147       }
0148       reference operator*() const { return fIndex; }
0149       pointer operator->() const { return &fIndex; }
0150       bool operator==(const iterator &rh) const { return fIndex == rh.fIndex; }
0151       bool operator!=(const iterator &rh) const { return fIndex != rh.fIndex; }
0152    };
0153 
0154    /// An active entry token is a pledge for the data of a certain entry number not to be evicted from the
0155    /// page cache or cluster cache. An active entry token is linked to a specific reader through a control block
0156    /// shared by the reader and all tokens of that reader. Active entry tokens can be destructed before or after
0157    /// their reader is destructed. Once the corresponding reader is destructed, changing the entry number has no
0158    /// effect.
0159    /// Only the RNTuple reader can create an active entry token.
0160    class RActiveEntryToken {
0161       friend class RNTupleReader;
0162 
0163       std::shared_ptr<RActiveEntriesControlBlock> fPtrControlBlock;
0164       NTupleSize_t fEntryNumber = kInvalidNTupleIndex;
0165 
0166       void ActivateEntry(NTupleSize_t entryNumber);
0167       void DeactivateEntry(NTupleSize_t entryNumber);
0168 
0169       explicit RActiveEntryToken(std::shared_ptr<RActiveEntriesControlBlock> ptrControlBlock)
0170          : fPtrControlBlock(ptrControlBlock)
0171       {
0172       }
0173 
0174    public:
0175       ~RActiveEntryToken() { Reset(); }
0176       RActiveEntryToken(const RActiveEntryToken &other);
0177       RActiveEntryToken(RActiveEntryToken &&other);
0178       RActiveEntryToken &operator=(const RActiveEntryToken &other);
0179       RActiveEntryToken &operator=(RActiveEntryToken &&other);
0180 
0181       NTupleSize_t GetEntryNumber() const { return fEntryNumber; }
0182       /// Set or replace the entry number. If the entry number is replaced, the cluster corresponding to the new
0183       /// entry is pinned _before_ the cluster of the old entry number is unpinned.
0184       /// SetEntryNumber() should be called before the corresponding entry is used (through LoadEntry() or views).
0185       void SetEntryNumber(NTupleSize_t entryNumber);
0186       /// Release the entry number, i.e. allow the corresponding data to be evicted from caches.
0187       /// Called implicitly on destruction.
0188       void Reset();
0189    };
0190 
0191    /// Open an RNTuple for reading.
0192    ///
0193    /// Throws an RException if there is no RNTuple with the given name.
0194    ///
0195    /// **Example: open an RNTuple and print the number of entries**
0196    /// ~~~ {.cpp}
0197    /// #include <ROOT/RNTupleReader.hxx>
0198    /// #include <iostream>
0199    ///
0200    /// auto reader = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0201    /// std::cout << "myNTuple has " << reader->GetNEntries() << " entries\n";
0202    /// ~~~
0203    static std::unique_ptr<RNTupleReader> Open(std::string_view ntupleName, std::string_view storage,
0204                                               const ROOT::RNTupleReadOptions &options = ROOT::RNTupleReadOptions());
0205    static std::unique_ptr<RNTupleReader>
0206    Open(const RNTuple &ntuple, const ROOT::RNTupleReadOptions &options = ROOT::RNTupleReadOptions());
0207 
0208    /// The caller imposes a model, which must be compatible with the model found in the data on storage.
0209    static std::unique_ptr<RNTupleReader> Open(std::unique_ptr<ROOT::RNTupleModel> model, std::string_view ntupleName,
0210                                               std::string_view storage,
0211                                               const ROOT::RNTupleReadOptions &options = ROOT::RNTupleReadOptions());
0212    static std::unique_ptr<RNTupleReader> Open(std::unique_ptr<ROOT::RNTupleModel> model, const RNTuple &ntuple,
0213                                               const ROOT::RNTupleReadOptions &options = ROOT::RNTupleReadOptions());
0214 
0215    /// The caller imposes the way the model is reconstructed
0216    static std::unique_ptr<RNTupleReader> Open(const ROOT::RNTupleDescriptor::RCreateModelOptions &createModelOpts,
0217                                               std::string_view ntupleName, std::string_view storage,
0218                                               const ROOT::RNTupleReadOptions &options = ROOT::RNTupleReadOptions());
0219    static std::unique_ptr<RNTupleReader> Open(const ROOT::RNTupleDescriptor::RCreateModelOptions &createModelOpts,
0220                                               const RNTuple &ntuple,
0221                                               const ROOT::RNTupleReadOptions &options = ROOT::RNTupleReadOptions());
0222    std::unique_ptr<RNTupleReader> Clone()
0223    {
0224       auto options = ROOT::RNTupleReadOptions{};
0225       options.SetEnableMetrics(fMetrics.IsEnabled());
0226       return std::unique_ptr<RNTupleReader>(new RNTupleReader(fSource->Clone(), options));
0227    }
0228 
0229    RNTupleReader(const ROOT::RNTupleReader &) = delete;
0230    RNTupleReader &operator=(const ROOT::RNTupleReader &) = delete;
0231    RNTupleReader(ROOT::RNTupleReader &&) = delete;
0232    RNTupleReader &operator=(ROOT::RNTupleReader &&) = delete;
0233    ~RNTupleReader();
0234 
0235    /// Returns the number of entries in this RNTuple.
0236    /// Note that the recommended way to iterate the RNTuple is using
0237    /// ~~~ {.cpp}
0238    /// // RECOMMENDED way to iterate an ntuple
0239    /// for (auto i : reader->GetEntryRange()) { ... }
0240    /// ~~~
0241    /// instead of
0242    /// ~~~ {.cpp}
0243    /// // DISCOURAGED way to iterate an ntuple
0244    /// for (auto i = 0u; i < reader->GetNEntries(); ++i) { ... }
0245    /// ~~~
0246    /// The reason is that determining the number of entries, while currently cheap, may in the future be
0247    /// an expensive operation.
0248    ROOT::NTupleSize_t GetNEntries() const { return fNEntries; }
0249    const ROOT::RNTupleModel &GetModel();
0250    std::unique_ptr<ROOT::REntry> CreateEntry();
0251 
0252    /// Returns a cached copy of the page source descriptor. The returned pointer remains valid until the next call
0253    /// to LoadEntry() or to any of the views returned from the reader.
0254    const ROOT::RNTupleDescriptor &GetDescriptor();
0255 
0256    /// Prints a detailed summary of the RNTuple, including a list of fields.
0257    ///
0258    /// **Example: print summary information to stdout**
0259    /// ~~~ {.cpp}
0260    /// #include <ROOT/RNTupleReader.hxx>
0261    /// #include <iostream>
0262    ///
0263    /// auto reader = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0264    /// reader->PrintInfo();
0265    /// // or, equivalently:
0266    /// reader->PrintInfo(ROOT::ENTupleInfo::kSummary, std::cout);
0267    /// ~~~
0268    /// **Example: print detailed column storage data to stderr**
0269    /// ~~~ {.cpp}
0270    /// #include <ROOT/RNTupleReader.hxx>
0271    /// #include <iostream>
0272    ///
0273    /// auto reader = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0274    /// reader->PrintInfo(ROOT::ENTupleInfo::kStorageDetails, std::cerr);
0275    /// ~~~
0276    ///
0277    /// For use of ENTupleInfo::kMetrics, see #EnableMetrics.
0278    void PrintInfo(const ENTupleInfo what = ENTupleInfo::kSummary, std::ostream &output = std::cout) const;
0279 
0280    /// Shows the values of the i-th entry/row, starting with 0 for the first entry. By default,
0281    /// prints the output in JSON format.
0282    /// Uses the visitor pattern to traverse through each field of the given entry.
0283    void Show(ROOT::NTupleSize_t index, std::ostream &output = std::cout);
0284 
0285    /// Fills the default entry of the model.
0286    /// Raises an exception when `index` is greater than the number of entries present in the RNTuple
0287    void LoadEntry(ROOT::NTupleSize_t index)
0288    {
0289       // TODO(jblomer): can be templated depending on the factory method / constructor
0290       if (R__unlikely(!fModel)) {
0291          // Will create the fModel.
0292          GetModel();
0293       }
0294       LoadEntry(index, fModel->GetDefaultEntry());
0295    }
0296    /// Fills a user provided entry after checking that the entry has been instantiated from the RNTuple model
0297    void LoadEntry(ROOT::NTupleSize_t index, ROOT::REntry &entry)
0298    {
0299       if (R__unlikely(entry.GetModelId() != fModel->GetModelId()))
0300          throw RException(R__FAIL("mismatch between entry and model"));
0301 
0302       entry.Read(index);
0303    }
0304 
0305    /// Create a new active entry token, which will not be bound to any entry number initially.
0306    /// In order to bind the new token, its `SetEntryNumber()` must be called subsequently.
0307    RActiveEntryToken CreateActiveEntryToken() { return RActiveEntryToken(fActiveEntriesControlBlock); }
0308 
0309    /// Returns an iterator over the entry indices of the RNTuple.
0310    ///
0311    /// **Example: iterate over all entries and print each entry in JSON format**
0312    /// ~~~ {.cpp}
0313    /// #include <ROOT/RNTupleReader.hxx>
0314    /// #include <iostream>
0315    ///
0316    /// auto reader = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0317    /// for (auto i : ntuple->GetEntryRange()) {
0318    ///    reader->Show(i);
0319    /// }
0320    /// ~~~
0321    ROOT::RNTupleGlobalRange GetEntryRange() { return ROOT::RNTupleGlobalRange(0, GetNEntries()); }
0322 
0323    /// Provides access to an individual (sub)field,
0324    /// e.g. `GetView<Particle>("particle")`, `GetView<double>("particle.pt")` or
0325    /// `GetView<std::vector<Particle>>("particles")`. It is possible to directly get the size of a collection (without
0326    /// reading the collection itself) using RNTupleCardinality:
0327    /// `GetView<ROOT::RNTupleCardinality<std::uint64_t>>("particles")`.
0328    ///
0329    /// Raises an exception if there is no field with the given name.
0330    ///
0331    /// **Example: iterate over a field named "pt" of type `float`**
0332    /// ~~~ {.cpp}
0333    /// #include <ROOT/RNTupleReader.hxx>
0334    /// #include <iostream>
0335    ///
0336    /// auto reader = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0337    /// auto pt = reader->GetView<float>("pt");
0338    ///
0339    /// for (auto i : reader->GetEntryRange()) {
0340    ///    std::cout << i << ": " << pt(i) << "\n";
0341    /// }
0342    /// ~~~
0343    ///
0344    /// **Note**: if `T = void`, type checks are disabled. This is not really useful for this overload because
0345    /// RNTupleView<void> does not give access to the pointer. If required, it is possible to provide an `objPtr` of a
0346    /// dynamic type, for example via GetView(std::string_view, void *, std::string_view).
0347    template <typename T>
0348    ROOT::RNTupleView<T> GetView(std::string_view fieldName)
0349    {
0350       return GetView<T>(RetrieveFieldId(fieldName));
0351    }
0352 
0353    /// Provides access to an individual (sub)field, reading its values into `objPtr`.
0354    ///
0355    /// Raises an exception if there is no field with the given name.
0356    ///
0357    /// **Example: iterate over a field named "pt" of type `float`**
0358    /// ~~~ {.cpp}
0359    /// #include <ROOT/RNTupleReader.hxx>
0360    /// #include <iostream>
0361    ///
0362    /// auto reader = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0363    /// auto pt = std::make_shared<float>();
0364    /// auto ptView = reader->GetView("pt", pt);
0365    ///
0366    /// for (auto i : reader->GetEntryRange()) {
0367    ///    ptView(i);
0368    ///    std::cout << i << ": " << *pt << "\n";
0369    /// }
0370    /// ~~~
0371    ///
0372    /// **Note**: if `T = void`, type checks are disabled. It is the caller's responsibility to match the field and
0373    /// object types. It is strongly recommended to use an overload that allows passing the `typeName`, such as
0374    /// GetView(std::string_view, void *, std::string_view). This allows type checks with the on-disk metadata and
0375    /// enables automatic schema evolution and conversion rules.
0376    template <typename T>
0377    ROOT::RNTupleView<T> GetView(std::string_view fieldName, std::shared_ptr<T> objPtr)
0378    {
0379       return GetView<T>(RetrieveFieldId(fieldName), objPtr);
0380    }
0381 
0382    /// Provides access to an individual (sub)field, reading its values into `rawPtr`.
0383    ///
0384    /// \sa GetView(std::string_view, std::shared_ptr<T>)
0385    template <typename T>
0386    ROOT::RNTupleView<T> GetView(std::string_view fieldName, T *rawPtr)
0387    {
0388       return GetView<T>(RetrieveFieldId(fieldName), rawPtr);
0389    }
0390 
0391    /// Provides access to an individual (sub)field, reading its values into `rawPtr` as the type provided by `typeName`.
0392    ///
0393    /// \sa GetView(std::string_view, std::shared_ptr<T>)
0394    ROOT::RNTupleView<void> GetView(std::string_view fieldName, void *rawPtr, std::string_view typeName)
0395    {
0396       return GetView(RetrieveFieldId(fieldName), rawPtr, typeName);
0397    }
0398 
0399    /// Provides access to an individual (sub)field, reading its values into `rawPtr` as the type provided by `ti`.
0400    ///
0401    /// \sa GetView(std::string_view, std::shared_ptr<T>)
0402    ROOT::RNTupleView<void> GetView(std::string_view fieldName, void *rawPtr, const std::type_info &ti)
0403    {
0404       return GetView(RetrieveFieldId(fieldName), rawPtr, ROOT::Internal::GetRenormalizedTypeName(ti));
0405    }
0406 
0407    /// Provides access to an individual (sub)field from its on-disk ID.
0408    ///
0409    /// \sa GetView(std::string_view)
0410    template <typename T>
0411    ROOT::RNTupleView<T> GetView(ROOT::DescriptorId_t fieldId)
0412    {
0413       auto field = ROOT::RNTupleView<T>::CreateField(fieldId, *fSource);
0414       auto range = ROOT::Internal::GetFieldRange(*field, *fSource);
0415       return ROOT::RNTupleView<T>(std::move(field), range);
0416    }
0417 
0418    /// Provides access to an individual (sub)field from its on-disk ID, reading its values into `objPtr`.
0419    ///
0420    /// \sa GetView(std::string_view, std::shared_ptr<T>)
0421    template <typename T>
0422    ROOT::RNTupleView<T> GetView(ROOT::DescriptorId_t fieldId, std::shared_ptr<T> objPtr)
0423    {
0424       auto field = ROOT::RNTupleView<T>::CreateField(fieldId, *fSource);
0425       auto range = ROOT::Internal::GetFieldRange(*field, *fSource);
0426       return ROOT::RNTupleView<T>(std::move(field), range, objPtr);
0427    }
0428 
0429    /// Provides access to an individual (sub)field from its on-disk ID, reading its values into `rawPtr`.
0430    ///
0431    /// \sa GetView(std::string_view, std::shared_ptr<T>)
0432    template <typename T>
0433    ROOT::RNTupleView<T> GetView(ROOT::DescriptorId_t fieldId, T *rawPtr)
0434    {
0435       auto field = ROOT::RNTupleView<T>::CreateField(fieldId, *fSource);
0436       auto range = ROOT::Internal::GetFieldRange(*field, *fSource);
0437       return ROOT::RNTupleView<T>(std::move(field), range, rawPtr);
0438    }
0439 
0440    /// Provides access to an individual (sub)field from its on-disk ID, reading its values into `rawPtr` as the type
0441    /// provided by `typeName`.
0442    ///
0443    /// \sa GetView(std::string_view, std::shared_ptr<T>)
0444    ROOT::RNTupleView<void> GetView(ROOT::DescriptorId_t fieldId, void *rawPtr, std::string_view typeName)
0445    {
0446       auto field = RNTupleView<void>::CreateField(fieldId, *fSource, typeName);
0447       auto range = ROOT::Internal::GetFieldRange(*field, *fSource);
0448       return RNTupleView<void>(std::move(field), range, rawPtr);
0449    }
0450 
0451    /// Provides access to an individual (sub)field from its on-disk ID, reading its values into `objPtr` as the type
0452    /// provided by `ti`.
0453    ///
0454    /// \sa GetView(std::string_view, std::shared_ptr<T>)
0455    ROOT::RNTupleView<void> GetView(ROOT::DescriptorId_t fieldId, void *rawPtr, const std::type_info &ti)
0456    {
0457       return GetView(fieldId, rawPtr, ROOT::Internal::GetRenormalizedTypeName(ti));
0458    }
0459 
0460    /// Provides direct access to the I/O buffers of a **mappable** (sub)field.
0461    ///
0462    /// Raises an exception if there is no field with the given name.
0463    /// Attempting to access the values of a direct-access view for non-mappable fields will yield compilation errors.
0464    ///
0465    /// \sa GetView(std::string_view)
0466    template <typename T>
0467    ROOT::RNTupleDirectAccessView<T> GetDirectAccessView(std::string_view fieldName)
0468    {
0469       return GetDirectAccessView<T>(RetrieveFieldId(fieldName));
0470    }
0471 
0472    /// Provides direct access to the I/O buffers of a **mappable** (sub)field from its on-disk ID.
0473    ///
0474    /// \sa GetDirectAccessView(std::string_view)
0475    template <typename T>
0476    ROOT::RNTupleDirectAccessView<T> GetDirectAccessView(ROOT::DescriptorId_t fieldId)
0477    {
0478       auto field = ROOT::RNTupleDirectAccessView<T>::CreateField(fieldId, *fSource);
0479       auto range = ROOT::Internal::GetFieldRange(field, *fSource);
0480       return ROOT::RNTupleDirectAccessView<T>(std::move(field), range);
0481    }
0482 
0483    /// Provides access to a collection field, that can itself generate new RNTupleViews for its nested fields.
0484    ///
0485    /// Raises an exception if:
0486    /// * there is no field with the given name or,
0487    /// * the field is not a collection
0488    ///
0489    /// \sa GetView(std::string_view)
0490    ROOT::RNTupleCollectionView GetCollectionView(std::string_view fieldName)
0491    {
0492       auto fieldId = fSource->GetSharedDescriptorGuard()->FindFieldId(fieldName);
0493       if (fieldId == ROOT::kInvalidDescriptorId) {
0494          throw RException(R__FAIL("no field named '" + std::string(fieldName) + "' in RNTuple '" +
0495                                   fSource->GetSharedDescriptorGuard()->GetName() + "'"));
0496       }
0497       return GetCollectionView(fieldId);
0498    }
0499 
0500    /// Provides access to a collection field from its on-disk ID, that can itself generate new RNTupleViews for its
0501    /// nested fields.
0502    ///
0503    /// \sa GetCollectionView(std::string_view)
0504    ROOT::RNTupleCollectionView GetCollectionView(ROOT::DescriptorId_t fieldId)
0505    {
0506       return ROOT::RNTupleCollectionView::Create(fieldId, fSource.get());
0507    }
0508 
0509    RIterator begin() { return RIterator(0); }
0510    RIterator end() { return RIterator(GetNEntries()); }
0511 
0512    /// Enable performance measurements (decompression time, bytes read from storage, etc.)
0513    ///
0514    /// **Example: inspect the reader metrics after loading every entry**
0515    /// ~~~ {.cpp}
0516    /// #include <ROOT/RNTupleReader.hxx>
0517    /// #include <iostream>
0518    ///
0519    /// auto ntuple = ROOT::RNTupleReader::Open("myNTuple", "some/file.root");
0520    /// // metrics must be turned on beforehand
0521    /// reader->EnableMetrics();
0522    ///
0523    /// for (auto i : ntuple->GetEntryRange()) {
0524    ///    reader->LoadEntry(i);
0525    /// }
0526    /// reader->PrintInfo(ROOT::ENTupleInfo::kMetrics);
0527    /// ~~~
0528    void EnableMetrics() { fMetrics.Enable(); }
0529    const Experimental::Detail::RNTupleMetrics &GetMetrics() const { return fMetrics; }
0530 
0531    /// Looks for an attribute set with the given name and creates an RNTupleAttrSetReader for it, with the provided
0532    /// read options.
0533    /// The returned reader has an independent lifetime from this RNTupleReader.
0534    std::unique_ptr<Experimental::RNTupleAttrSetReader>
0535    OpenAttributeSet(std::string_view attrSetName, const ROOT::RNTupleReadOptions &options = {});
0536 }; // class RNTupleReader
0537 
0538 } // namespace ROOT
0539 
0540 #endif // ROOT_RNTupleReader