Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-27 11:37:00

0001 /// \file ROOT/RNTupleView.hxx
0002 /// \ingroup NTuple
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2018-10-05
0005 
0006 /*************************************************************************
0007  * Copyright (C) 1995-2019, 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_RNTupleView
0015 #define ROOT_RNTupleView
0016 
0017 #include <ROOT/RError.hxx>
0018 #include <ROOT/RField.hxx>
0019 #include <ROOT/RNTupleRange.hxx>
0020 #include <ROOT/RNTupleTypes.hxx>
0021 #include <ROOT/RNTupleUtils.hxx>
0022 #include <string_view>
0023 
0024 #include <iterator>
0025 #include <memory>
0026 #include <type_traits>
0027 #include <utility>
0028 #include <unordered_map>
0029 
0030 namespace ROOT {
0031 
0032 class RNTupleReader;
0033 
0034 namespace Internal {
0035 
0036 /// Helper to get the iteration space of the given field that needs to be connected to the given page source.
0037 /// The indexes are given by the number of elements of the principal column of the field or, if none exists,
0038 /// by the number of elements of the first principal column found in the subfields searched by BFS.
0039 /// If the field hierarchy is empty on columns, the returned field range is invalid (start and end set to
0040 /// kInvalidNTupleIndex). An attempt to use such a field range in RNTupleViewBase::GetFieldRange will throw.
0041 ROOT::RNTupleGlobalRange GetFieldRange(const ROOT::RFieldBase &field, const ROOT::Internal::RPageSource &pageSource);
0042 
0043 } // namespace Internal
0044 
0045 // clang-format off
0046 /**
0047 \class ROOT::RNTupleViewBase
0048 \ingroup NTuple
0049 \brief An RNTupleView provides read-only access to a single field of an RNTuple
0050 
0051 \tparam T The type of the object that will be read by the view; can be void if unknown at compile time.
0052 
0053 The view owns a field and its underlying columns in order to fill an RField::RValue object with data. Data can be
0054 accessed by index. For top-level fields, the index refers to the entry number. Fields that are part of
0055 nested collections have global index numbers that are derived from their parent indexes (\see GetFieldRange()).
0056 
0057 View can only be created by a reader or by a collection view.
0058 
0059 **Example: read an RNTuple's field with a view**
0060 ~~~ {.cpp}
0061 auto reader = RNTupleReader::Open("myNtuple", "myntuple.root");
0062 auto viewFoo = reader->GetView<float>("foo");
0063 for (auto idx : reader->GetEntryRange()) {
0064    float foo = viewFoo(idx); // read field "foo" of the `idx`-th entry
0065    std::cout << foo << "\n";
0066 }
0067 ~~~
0068 
0069 **Example: read an RNTuple's collection subfield with a view**
0070 ~~~ {.cpp}
0071 auto reader = RNTupleReader::Open("myNtuple", "myntuple.root");
0072 // Assuming "v" is a std::vector<int>:
0073 auto view = reader->GetView<int>("v._0");
0074 // Effectively flattens all fields "v" in all entries and reads their elements.
0075 for (auto idx : view.GetFieldRange()) {
0076    int x = view(idx);
0077    std::cout << x << "\n";
0078 }
0079 ~~~
0080 */
0081 // clang-format on
0082 template <typename T>
0083 class RNTupleViewBase {
0084 protected:
0085    std::unique_ptr<ROOT::RFieldBase> fField;
0086    ROOT::RNTupleGlobalRange fFieldRange;
0087    ROOT::RFieldBase::RValue fValue;
0088 
0089    static std::unique_ptr<ROOT::RFieldBase>
0090    CreateField(ROOT::DescriptorId_t fieldId, Internal::RPageSource &pageSource, std::string_view typeName = "")
0091    {
0092       RFieldZero fieldZero;
0093       Internal::SetAllowFieldSubstitutions(fieldZero, true);
0094       std::unique_ptr<ROOT::RFieldBase> field;
0095       {
0096          const auto &desc = pageSource.GetSharedDescriptorGuard().GetRef();
0097          const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
0098          if constexpr (std::is_void_v<T>) {
0099             if (typeName.empty())
0100                field = fieldDesc.CreateField(desc);
0101             else
0102                field = ROOT::RFieldBase::Create(fieldDesc.GetFieldName(), std::string(typeName)).Unwrap();
0103          } else {
0104             field = std::make_unique<ROOT::RField<T>>(fieldDesc.GetFieldName());
0105          }
0106       }
0107       field->SetOnDiskId(fieldId);
0108       fieldZero.Attach(std::move(field));
0109       ROOT::Internal::CallConnectPageSourceOnField(fieldZero, pageSource);
0110       return std::move(fieldZero.ReleaseSubfields()[0]);
0111    }
0112 
0113    RNTupleViewBase(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range)
0114       : fField(std::move(field)), fFieldRange(range), fValue(fField->CreateValue())
0115    {
0116    }
0117 
0118    RNTupleViewBase(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, std::shared_ptr<T> objPtr)
0119       : fField(std::move(field)), fFieldRange(range), fValue(fField->BindValue(objPtr))
0120    {
0121    }
0122 
0123    RNTupleViewBase(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, T *rawPtr)
0124       : fField(std::move(field)),
0125         fFieldRange(range),
0126         fValue(fField->BindValue(ROOT::Internal::MakeAliasedSharedPtr(rawPtr)))
0127    {
0128    }
0129 
0130 public:
0131    RNTupleViewBase(const RNTupleViewBase &other) = delete;
0132    RNTupleViewBase(RNTupleViewBase &&other) = default;
0133    RNTupleViewBase &operator=(const RNTupleViewBase &other) = delete;
0134    RNTupleViewBase &operator=(RNTupleViewBase &&other) = default;
0135    ~RNTupleViewBase() = default;
0136 
0137    const ROOT::RFieldBase &GetField() const { return *fField; }
0138    ROOT::RFieldBase::RBulkValues CreateBulk() { return fField->CreateBulk(); }
0139 
0140    const ROOT::RFieldBase::RValue &GetValue() const { return fValue; }
0141    /// Returns the global field range of this view.
0142    /// This may differ from the RNTuple's entry range in case of subfields and can be used to iterate
0143    /// over all the concatenated elements of the subfield without caring which entry they belong to.
0144    /// Throws an RException if the underlying field of this view is empty, i.e. if it's a class or
0145    /// record field with no associated columns.
0146    ROOT::RNTupleGlobalRange GetFieldRange() const
0147    {
0148       if (!fFieldRange.IsValid()) {
0149          throw RException(R__FAIL("field iteration over empty fields is unsupported: " + fField->GetFieldName()));
0150       }
0151       return fFieldRange;
0152    }
0153 
0154    void Bind(std::shared_ptr<T> objPtr) { fValue.Bind(objPtr); }
0155    void BindRawPtr(T *rawPtr) { fValue.BindRawPtr(rawPtr); }
0156    void EmplaceNew() { fValue.EmplaceNew(); }
0157 };
0158 
0159 // clang-format off
0160 /**
0161 \class ROOT::RNTupleView
0162 \ingroup NTuple
0163 \brief An RNTupleView for a known type. See RNTupleViewBase.
0164 */
0165 // clang-format on
0166 template <typename T>
0167 class RNTupleView : public RNTupleViewBase<T> {
0168    friend class ROOT::RNTupleReader;
0169    friend class RNTupleCollectionView;
0170 
0171 protected:
0172    RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range)
0173       : RNTupleViewBase<T>(std::move(field), range)
0174    {
0175    }
0176 
0177    RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, std::shared_ptr<T> objPtr)
0178       : RNTupleViewBase<T>(std::move(field), range, objPtr)
0179    {
0180    }
0181 
0182    RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, T *rawPtr)
0183       : RNTupleViewBase<T>(std::move(field), range, rawPtr)
0184    {
0185    }
0186 
0187    const T &GetValueRef() const
0188    {
0189       // We created the RValue and know its type, avoid extra checks.
0190       void *ptr = RNTupleViewBase<T>::fValue.template GetPtr<void>().get();
0191       return *static_cast<T *>(ptr);
0192    }
0193 
0194 public:
0195    RNTupleView(const RNTupleView &other) = delete;
0196    RNTupleView(RNTupleView &&other) = default;
0197    RNTupleView &operator=(const RNTupleView &other) = delete;
0198    RNTupleView &operator=(RNTupleView &&other) = default;
0199    ~RNTupleView() = default;
0200 
0201    /// Reads the value of this view for the entry with the provided `globalIndex`.
0202    const T &operator()(ROOT::NTupleSize_t globalIndex)
0203    {
0204       RNTupleViewBase<T>::fValue.Read(globalIndex);
0205       return GetValueRef();
0206    }
0207 
0208    /// Reads the value of this view for the entry with the provided `localIndex`.
0209    /// See RNTupleLocalIndex for more details.
0210    const T &operator()(RNTupleLocalIndex localIndex)
0211    {
0212       RNTupleViewBase<T>::fValue.Read(localIndex);
0213       return GetValueRef();
0214    }
0215 };
0216 
0217 // clang-format off
0218 /**
0219 \class ROOT::RNTupleView
0220 \ingroup NTuple
0221 \brief An RNTupleView that can be used when the type is unknown at compile time. See RNTupleViewBase.
0222 */
0223 // clang-format on
0224 template <>
0225 class RNTupleView<void> final : public RNTupleViewBase<void> {
0226    friend class ROOT::RNTupleReader;
0227    friend class RNTupleCollectionView;
0228 
0229 protected:
0230    RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range)
0231       : RNTupleViewBase<void>(std::move(field), range)
0232    {
0233    }
0234 
0235    RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, std::shared_ptr<void> objPtr)
0236       : RNTupleViewBase<void>(std::move(field), range, objPtr)
0237    {
0238    }
0239 
0240    RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, void *rawPtr)
0241       : RNTupleViewBase<void>(std::move(field), range, rawPtr)
0242    {
0243    }
0244 
0245 public:
0246    RNTupleView(const RNTupleView &other) = delete;
0247    RNTupleView(RNTupleView &&other) = default;
0248    RNTupleView &operator=(const RNTupleView &other) = delete;
0249    RNTupleView &operator=(RNTupleView &&other) = default;
0250    ~RNTupleView() = default;
0251 
0252    /// \see RNTupleView::operator()(ROOT::NTupleSize_t)
0253    void operator()(ROOT::NTupleSize_t globalIndex) { fValue.Read(globalIndex); }
0254    /// \see RNTupleView::operator()(RNTupleLocalIndex)
0255    void operator()(RNTupleLocalIndex localIndex) { fValue.Read(localIndex); }
0256 };
0257 
0258 // clang-format off
0259 /**
0260 \class ROOT::RNTupleDirectAccessView
0261 \ingroup NTuple
0262 \brief A view variant that provides direct access to the I/O buffers. Only works for mappable fields.
0263 */
0264 // clang-format on
0265 template <typename T>
0266 class RNTupleDirectAccessView {
0267    friend class ROOT::RNTupleReader;
0268    friend class RNTupleCollectionView;
0269 
0270 protected:
0271    ROOT::RField<T> fField;
0272    ROOT::RNTupleGlobalRange fFieldRange;
0273 
0274    static ROOT::RField<T> CreateField(ROOT::DescriptorId_t fieldId, ROOT::Internal::RPageSource &pageSource)
0275    {
0276       const auto &desc = pageSource.GetSharedDescriptorGuard().GetRef();
0277       const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
0278       if (!Internal::IsMatchingFieldType<T>(fieldDesc.GetTypeName())) {
0279          throw RException(R__FAIL("type mismatch for field " + fieldDesc.GetFieldName() + ": " +
0280                                   fieldDesc.GetTypeName() + " vs. " + ROOT::RField<T>::TypeName()));
0281       }
0282       ROOT::RField<T> field(fieldDesc.GetFieldName());
0283       field.SetOnDiskId(fieldId);
0284       ROOT::Internal::CallConnectPageSourceOnField(field, pageSource);
0285       return field;
0286    }
0287 
0288    RNTupleDirectAccessView(ROOT::RField<T> field, ROOT::RNTupleGlobalRange range)
0289       : fField(std::move(field)), fFieldRange(range)
0290    {
0291    }
0292 
0293 public:
0294    RNTupleDirectAccessView(const RNTupleDirectAccessView &other) = delete;
0295    RNTupleDirectAccessView(RNTupleDirectAccessView &&other) = default;
0296    RNTupleDirectAccessView &operator=(const RNTupleDirectAccessView &other) = delete;
0297    RNTupleDirectAccessView &operator=(RNTupleDirectAccessView &&other) = default;
0298    ~RNTupleDirectAccessView() = default;
0299 
0300    const ROOT::RFieldBase &GetField() const { return fField; }
0301    /// \see RNTupleView::GetFieldRange()
0302    ROOT::RNTupleGlobalRange GetFieldRange() const { return fFieldRange; }
0303 
0304    /// \see RNTupleView::operator()(ROOT::NTupleSize_t)
0305    const T &operator()(ROOT::NTupleSize_t globalIndex) { return *fField.Map(globalIndex); }
0306    /// \see RNTupleView::operator()(RNTupleLocalIndex)
0307    const T &operator()(RNTupleLocalIndex localIndex) { return *fField.Map(localIndex); }
0308 };
0309 
0310 // clang-format off
0311 /**
0312 \class ROOT::RNTupleCollectionView
0313 \ingroup NTuple
0314 \brief A view for a collection, that can itself generate new ntuple views for its nested fields.
0315 */
0316 // clang-format on
0317 class RNTupleCollectionView {
0318    friend class ROOT::RNTupleReader;
0319 
0320 private:
0321    ROOT::Internal::RPageSource *fSource;
0322    ROOT::RField<RNTupleCardinality<std::uint64_t>> fField;
0323    ROOT::RFieldBase::RValue fValue;
0324 
0325    RNTupleCollectionView(ROOT::DescriptorId_t fieldId, const std::string &fieldName,
0326                          ROOT::Internal::RPageSource *source)
0327       : fSource(source), fField(fieldName), fValue(fField.CreateValue())
0328    {
0329       fField.SetOnDiskId(fieldId);
0330       ROOT::Internal::CallConnectPageSourceOnField(fField, *source);
0331    }
0332 
0333    static RNTupleCollectionView Create(ROOT::DescriptorId_t fieldId, ROOT::Internal::RPageSource *source)
0334    {
0335       std::string fieldName;
0336       {
0337          const auto &desc = source->GetSharedDescriptorGuard().GetRef();
0338          const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
0339          if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kCollection) {
0340             throw RException(
0341                R__FAIL("invalid attemt to create collection view on non-collection field " + fieldDesc.GetFieldName()));
0342          }
0343          fieldName = fieldDesc.GetFieldName();
0344       }
0345       return RNTupleCollectionView(fieldId, fieldName, source);
0346    }
0347 
0348    ROOT::DescriptorId_t GetFieldId(std::string_view fieldName)
0349    {
0350       auto descGuard = fSource->GetSharedDescriptorGuard();
0351       auto fieldId = descGuard->FindFieldId(fieldName, fField.GetOnDiskId());
0352       if (fieldId == ROOT::kInvalidDescriptorId) {
0353          throw RException(R__FAIL("no field named '" + std::string(fieldName) + "' in collection '" +
0354                                   descGuard->GetQualifiedFieldName(fField.GetOnDiskId()) + "'"));
0355       }
0356       return fieldId;
0357    }
0358 
0359    std::uint64_t GetCardinalityValue() const
0360    {
0361       // We created the RValue and know its type, avoid extra checks.
0362       void *ptr = fValue.GetPtr<void>().get();
0363       return *static_cast<RNTupleCardinality<std::uint64_t> *>(ptr);
0364    }
0365 
0366 public:
0367    RNTupleCollectionView(const RNTupleCollectionView &other) = delete;
0368    RNTupleCollectionView &operator=(const RNTupleCollectionView &other) = delete;
0369    RNTupleCollectionView(RNTupleCollectionView &&other)
0370       : fSource(other.fSource), fField(std::move(other.fField)), fValue(fField.CreateValue())
0371    {
0372    }
0373    RNTupleCollectionView &operator=(RNTupleCollectionView &&other)
0374    {
0375       if (this == &other)
0376          return *this;
0377       std::swap(fSource, other.fSource);
0378       std::swap(fField, other.fField);
0379       fValue = fField.CreateValue();
0380       return *this;
0381    }
0382    ~RNTupleCollectionView() = default;
0383 
0384    ROOT::RNTupleLocalRange GetCollectionRange(ROOT::NTupleSize_t globalIndex)
0385    {
0386       ROOT::NTupleSize_t size;
0387       RNTupleLocalIndex collectionStart;
0388       fField.GetCollectionInfo(globalIndex, &collectionStart, &size);
0389       return ROOT::RNTupleLocalRange(collectionStart.GetClusterId(), collectionStart.GetIndexInCluster(),
0390                                      collectionStart.GetIndexInCluster() + size);
0391    }
0392 
0393    ROOT::RNTupleLocalRange GetCollectionRange(RNTupleLocalIndex localIndex)
0394    {
0395       ROOT::NTupleSize_t size;
0396       RNTupleLocalIndex collectionStart;
0397       fField.GetCollectionInfo(localIndex, &collectionStart, &size);
0398       return ROOT::RNTupleLocalRange(collectionStart.GetClusterId(), collectionStart.GetIndexInCluster(),
0399                                      collectionStart.GetIndexInCluster() + size);
0400    }
0401 
0402    /// Provides access to an individual (sub)field.
0403    ///
0404    /// Raises an exception if there is no field with the given name.
0405    ///
0406    /// \sa ROOT::RNTupleReader::GetView(std::string_view)
0407    template <typename T>
0408    RNTupleView<T> GetView(std::string_view fieldName)
0409    {
0410       auto field = RNTupleView<T>::CreateField(GetFieldId(fieldName), *fSource);
0411       auto range = Internal::GetFieldRange(*field, *fSource);
0412       return RNTupleView<T>(std::move(field), range);
0413    }
0414 
0415    /// Provides direct access to the I/O buffers of a **mappable** (sub)field.
0416    ///
0417    /// Raises an exception if there is no field with the given name.
0418    /// Attempting to access the values of a direct-access view for non-mappable fields will yield compilation errors.
0419    ///
0420    /// \sa ROOT::RNTupleReader::DirectAccessView(std::string_view)
0421    template <typename T>
0422    RNTupleDirectAccessView<T> GetDirectAccessView(std::string_view fieldName)
0423    {
0424       auto field = RNTupleDirectAccessView<T>::CreateField(GetFieldId(fieldName), *fSource);
0425       auto range = Internal::GetFieldRange(field, *fSource);
0426       return RNTupleDirectAccessView<T>(std::move(field), range);
0427    }
0428 
0429    /// Provides access to a collection field, that can itself generate new RNTupleViews for its nested fields.
0430    ///
0431    /// Raises an exception if:
0432    /// * there is no field with the given name or,
0433    /// * the field is not a collection
0434    ///
0435    /// \sa ROOT::RNTupleReader::GetCollectionView(std::string_view)
0436    RNTupleCollectionView GetCollectionView(std::string_view fieldName)
0437    {
0438       return RNTupleCollectionView::Create(GetFieldId(fieldName), fSource);
0439    }
0440 
0441    /// \see RNTupleView::operator()(ROOT::NTupleSize_t)
0442    std::uint64_t operator()(ROOT::NTupleSize_t globalIndex)
0443    {
0444       fValue.Read(globalIndex);
0445       return GetCardinalityValue();
0446    }
0447 
0448    /// \see RNTupleView::operator()(RNTupleLocalIndex)
0449    std::uint64_t operator()(RNTupleLocalIndex localIndex)
0450    {
0451       fValue.Read(localIndex);
0452       return GetCardinalityValue();
0453    }
0454 };
0455 
0456 } // namespace ROOT
0457 
0458 #endif