Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleView.hxx
Go to the documentation of this file.
1/// \file ROOT/RNTupleView.hxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-05
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#ifndef ROOT7_RNTupleView
17#define ROOT7_RNTupleView
18
19#include <ROOT/RField.hxx>
20#include <ROOT/RNTupleUtil.hxx>
21#include <string_view>
22
23#include <iterator>
24#include <memory>
25#include <type_traits>
26#include <utility>
27#include <unordered_map>
28
29namespace ROOT {
30namespace Experimental {
31
32
33// clang-format off
34/**
35\class ROOT::Experimental::RNTupleGlobalRange
36\ingroup NTuple
37\brief Used to loop over indexes (entries or collections) between start and end
38*/
39// clang-format on
41private:
44public:
45 class RIterator {
46 private:
48 public:
50 using iterator_category = std::forward_iterator_tag;
55
56 RIterator() = default;
58 ~RIterator() = default;
59
60 iterator operator++(int) /* postfix */ { auto r = *this; fIndex++; return r; }
61 iterator& operator++() /* prefix */ { ++fIndex; return *this; }
63 pointer operator->() { return &fIndex; }
64 bool operator==(const iterator& rh) const { return fIndex == rh.fIndex; }
65 bool operator!=(const iterator& rh) const { return fIndex != rh.fIndex; }
66 };
67
70 RIterator end() { return RIterator(fEnd); }
71};
72
73
74// clang-format off
75/**
76\class ROOT::Experimental::RNTupleClusterRange
77\ingroup NTuple
78\brief Used to loop over entries of collections in a single cluster
79*/
80// clang-format on
82private:
86public:
87 class RIterator {
88 private:
90 public:
92 using iterator_category = std::forward_iterator_tag;
97
98 RIterator() = default;
100 ~RIterator() = default;
101
102 iterator operator++(int) /* postfix */ { auto r = *this; fIndex++; return r; }
103 iterator& operator++() /* prefix */ { fIndex++; return *this; }
105 pointer operator->() { return &fIndex; }
106 bool operator==(const iterator& rh) const { return fIndex == rh.fIndex; }
107 bool operator!=(const iterator& rh) const { return fIndex != rh.fIndex; }
108 };
109
111 : fClusterId(clusterId), fStart(start), fEnd(end) {}
114};
115
116
117namespace Internal {
118// TODO(bgruber): convert this trait into a requires clause in C++20
119template <typename FieldT, typename SFINAE = void>
120inline constexpr bool isMappable = false;
121
122template <typename FieldT>
123inline constexpr bool isMappable<FieldT, std::void_t<decltype(std::declval<FieldT>().Map(NTupleSize_t{}))>> = true;
124} // namespace Internal
125
126
127// clang-format off
128/**
129\class ROOT::Experimental::RNTupleView
130\ingroup NTuple
131\brief An RNTupleView provides read-only access to a single field of the ntuple
132
133\tparam T The type of the object that will be read by the view
134\tparam UserProvidedAddress Whether the user provided an external memory location to read data into
135
136The view owns a field and its underlying columns in order to fill an ntuple value object with data. Data can be
137accessed by index. For top-level fields, the index refers to the entry number. Fields that are part of
138nested collections have global index numbers that are derived from their parent indexes.
139
140Fields of simple types with a Map() method will use that and thus expose zero-copy access.
141*/
142// clang-format on
143template <typename T, bool UserProvidedAddress>
145 friend class RNTupleReader;
147
149
150private:
151 /// fFieldId has fParent always set to null; views access nested fields without looking at the parent
153 /// Used as a Read() destination for fields that are not mappable
155
157 {
158 fField.SetOnDiskId(fieldId);
160 if constexpr (!UserProvidedAddress) {
161 if ((fField.GetTraits() & RFieldBase::kTraitMappable) && fField.HasReadCallbacks())
162 throw RException(R__FAIL("view disallowed on field with mappable type and read callback"));
163 }
164 }
165
167 : fField(pageSource->GetSharedDescriptorGuard()->GetFieldDescriptor(fieldId).GetFieldName()),
168 fValue(fField.CreateValue())
169 {
170 SetupField(fieldId, pageSource);
171 }
172
173 RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, std::shared_ptr<T> objPtr)
174 : fField(pageSource->GetSharedDescriptorGuard()->GetFieldDescriptor(fieldId).GetFieldName()),
175 fValue(fField.BindValue(objPtr))
176 {
177 SetupField(fieldId, pageSource);
178 }
179
180 RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, T *rawPtr)
181 : fField(pageSource->GetSharedDescriptorGuard()->GetFieldDescriptor(fieldId).GetFieldName()),
182 fValue(fField.BindValue(Internal::MakeAliasedSharedPtr(rawPtr)))
183 {
184 SetupField(fieldId, pageSource);
185 }
186
187public:
188 RNTupleView(const RNTupleView& other) = delete;
189 RNTupleView(RNTupleView&& other) = default;
190 RNTupleView& operator=(const RNTupleView& other) = delete;
191 RNTupleView& operator=(RNTupleView&& other) = default;
192 ~RNTupleView() = default;
193
194 const FieldT &GetField() const { return fField; }
195 RNTupleGlobalRange GetFieldRange() const { return RNTupleGlobalRange(0, fField.GetNElements()); }
196
197 const T &operator()(NTupleSize_t globalIndex)
198 {
199 if constexpr (Internal::isMappable<FieldT> && !UserProvidedAddress) {
200 return *fField.Map(globalIndex);
201 } else {
202 fValue.Read(globalIndex);
203 return fValue.GetRef<T>();
204 }
205 }
206
207 const T &operator()(RClusterIndex clusterIndex)
208 {
209 if constexpr (Internal::isMappable<FieldT> && !UserProvidedAddress) {
210 return *fField.Map(clusterIndex);
211 } else {
212 fValue.Read(clusterIndex);
213 return fValue.GetRef<T>();
214 }
215 }
216
217 // TODO(bgruber): turn enable_if into requires clause with C++20
218 template <typename C = T, std::enable_if_t<Internal::isMappable<FieldT>, C*> = nullptr>
219 const C *MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems)
220 {
221 return fField.MapV(globalIndex, nItems);
222 }
223
224 // TODO(bgruber): turn enable_if into requires clause with C++20
225 template <typename C = T, std::enable_if_t<Internal::isMappable<FieldT>, C *> = nullptr>
226 const C *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
227 {
228 return fField.MapV(clusterIndex, nItems);
229 }
230
231 void Bind(std::shared_ptr<T> objPtr)
232 {
233 static_assert(
234 UserProvidedAddress,
235 "Only views which were created with an external memory location at construction time can be bound to a "
236 "different memory location afterwards. Call the RNTupleReader::GetView overload with a shared_ptr.");
237 fValue.Bind(objPtr);
238 }
239
240 void BindRawPtr(T *rawPtr)
241 {
242 static_assert(
243 UserProvidedAddress,
244 "Only views which were created with an external memory location at construction time can be bound to a "
245 "different memory location afterwards. Call the RNTupleReader::GetView overload with a shared_ptr.");
246 fValue.BindRawPtr(rawPtr);
247 }
248
250 {
251 // Let the user know they are misusing this function in case the field is
252 // mappable and they are providing an external address after construction.
253 // This would mean creating a new RValue member but not using it since
254 // reading would be done by RField::Map directly.
255 static_assert(!(Internal::isMappable<FieldT> && !UserProvidedAddress),
256 "Cannot emplace a new value into a view of a mappable type, unless an external memory location is "
257 "provided at construction time.");
259 }
260};
261
262// clang-format off
263/**
264\class ROOT::Experimental::RNTupleView<void>
265\ingroup NTuple
266\brief An RNTupleView where the type is not known at compile time.
267
268Can be used to read individual fields whose type is unknown. The void view gives access to the RValue
269in addition to the field, so that the read object can be retrieved.
270*/
271// clang-format on
272template <bool UserProvidedAddress>
273class RNTupleView<void, UserProvidedAddress> {
274 friend class RNTupleReader;
276
277private:
278 std::unique_ptr<RFieldBase> fField;
280
281 static std::unique_ptr<RFieldBase> CreateField(DescriptorId_t fieldId, const RNTupleDescriptor &desc)
282 {
283 return desc.GetFieldDescriptor(fieldId).CreateField(desc);
284 }
285
287 {
288 fField->SetOnDiskId(fieldId);
290 }
291
293 : fField(CreateField(fieldId, pageSource->GetSharedDescriptorGuard().GetRef())), fValue(fField->CreateValue())
294 {
295 SetupField(fieldId, pageSource);
296 }
297
298 RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, std::shared_ptr<void> objPtr)
299 : fField(CreateField(fieldId, pageSource->GetSharedDescriptorGuard().GetRef())), fValue(fField->BindValue(objPtr))
300 {
301 SetupField(fieldId, pageSource);
302 }
303
304 RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, void *rawPtr)
305 : fField(CreateField(fieldId, pageSource->GetSharedDescriptorGuard().GetRef())),
306 fValue(fField->BindValue(Internal::MakeAliasedSharedPtr(rawPtr)))
307 {
308 SetupField(fieldId, pageSource);
309 }
310
311public:
312 RNTupleView(const RNTupleView &other) = delete;
313 RNTupleView(RNTupleView &&other) = default;
314 RNTupleView &operator=(const RNTupleView &other) = delete;
315 RNTupleView &operator=(RNTupleView &&other) = default;
316 ~RNTupleView() = default;
317
318 const RFieldBase &GetField() const { return *fField; }
319 const RFieldBase::RValue &GetValue() const { return fValue; }
320 RNTupleGlobalRange GetFieldRange() const { return RNTupleGlobalRange(0, fField->GetNElements()); }
321
322 void operator()(NTupleSize_t globalIndex) { fValue.Read(globalIndex); }
323 void operator()(RClusterIndex clusterIndex) { fValue.Read(clusterIndex); }
324
325 void Bind(std::shared_ptr<void> objPtr)
326 {
327 static_assert(
328 UserProvidedAddress,
329 "Only views which were created with an external memory location at construction time can be bound to a "
330 "different memory location afterwards. Call the RNTupleReader::GetView overload with a shared_ptr.");
331 fValue.Bind(objPtr);
332 }
333
334 void BindRawPtr(void *rawPtr)
335 {
336 static_assert(
337 UserProvidedAddress,
338 "Only views which were created with an external memory location at construction time can be bound to a "
339 "different memory location afterwards. Call the RNTupleReader::GetView overload with a shared_ptr.");
340 fValue.BindRawPtr(rawPtr);
341 }
342
344};
345
346// clang-format off
347/**
348\class ROOT::Experimental::RNTupleCollectionView
349\ingroup NTuple
350\brief A view for a collection, that can itself generate new ntuple views for its nested fields.
351*/
352// clang-format on
353class RNTupleCollectionView : public RNTupleView<ClusterSize_t, false> {
354 friend class RNTupleReader;
355
356private:
359
361 : RNTupleView<ClusterSize_t, false>(fieldId, source), fSource(source), fCollectionFieldId(fieldId)
362 {}
363
364public:
370
373 RClusterIndex collectionStart;
374 fField.GetCollectionInfo(globalIndex, &collectionStart, &size);
375 return RNTupleClusterRange(collectionStart.GetClusterId(), collectionStart.GetIndex(),
376 collectionStart.GetIndex() + size);
377 }
379 {
381 RClusterIndex collectionStart;
382 fField.GetCollectionInfo(clusterIndex, &collectionStart, &size);
383 return RNTupleClusterRange(collectionStart.GetClusterId(), collectionStart.GetIndex(),
384 collectionStart.GetIndex() + size);
385 }
386
387 /// Raises an exception if there is no field with the given name.
388 template <typename T>
389 RNTupleView<T, false> GetView(std::string_view fieldName)
390 {
391 auto fieldId = fSource->GetSharedDescriptorGuard()->FindFieldId(fieldName, fCollectionFieldId);
392 if (fieldId == kInvalidDescriptorId) {
393 throw RException(R__FAIL("no field named '" + std::string(fieldName) + "' in RNTuple '" +
395 }
396 return RNTupleView<T, false>(fieldId, fSource);
397 }
398 /// Raises an exception if there is no field with the given name.
399 RNTupleCollectionView GetCollectionView(std::string_view fieldName)
400 {
401 auto fieldId = fSource->GetSharedDescriptorGuard()->FindFieldId(fieldName, fCollectionFieldId);
402 if (fieldId == kInvalidDescriptorId) {
403 throw RException(R__FAIL("no field named '" + std::string(fieldName) + "' in RNTuple '" +
405 }
406 return RNTupleCollectionView(fieldId, fSource);
407 }
408
411 RClusterIndex collectionStart;
412 fField.GetCollectionInfo(globalIndex, &collectionStart, &size);
413 return size;
414 }
416 {
418 RClusterIndex collectionStart;
419 fField.GetCollectionInfo(clusterIndex, &collectionStart, &size);
420 return size;
421 }
422};
423
424} // namespace Experimental
425} // namespace ROOT
426
427#endif
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:290
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Abstract interface to read data from an ntuple.
const RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
DescriptorId_t GetClusterId() const
ClusterSize_t::ValueType GetIndex() const
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
Definition RField.hxx:185
void Read(NTupleSize_t globalIndex)
Definition RField.hxx:202
void EmplaceNew()
Replace the current object pointer by a pointer to a new object constructed by the field.
Definition RField.hxx:207
void Bind(std::shared_ptr< void > objPtr)
Definition RField.hxx:204
A field translates read and write calls from/to underlying columns to/from tree values.
Definition RField.hxx:93
static constexpr int kTraitMappable
A field of a fundamental type that can be directly mapped via RField<T>::Map(), i....
Definition RField.hxx:144
std::unique_ptr< RFieldBase > CreateField(const RNTupleDescriptor &ntplDesc) const
In general, we create a field simply from the C++ type name.
void GetCollectionInfo(NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *size)
Special help for offset fields.
Definition RField.hxx:1754
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:1469
Used to loop over entries of collections in a single cluster.
const ClusterSize_t::ValueType fStart
RNTupleClusterRange(DescriptorId_t clusterId, ClusterSize_t::ValueType start, ClusterSize_t::ValueType end)
const ClusterSize_t::ValueType fEnd
A view for a collection, that can itself generate new ntuple views for its nested fields.
RNTupleCollectionView & operator=(RNTupleCollectionView &&other)=default
RNTupleClusterRange GetCollectionRange(RClusterIndex clusterIndex)
ClusterSize_t operator()(RClusterIndex clusterIndex)
RNTupleCollectionView(RNTupleCollectionView &&other)=default
ClusterSize_t operator()(NTupleSize_t globalIndex)
RNTupleCollectionView GetCollectionView(std::string_view fieldName)
Raises an exception if there is no field with the given name.
RNTupleCollectionView & operator=(const RNTupleCollectionView &other)=delete
RNTupleView< T, false > GetView(std::string_view fieldName)
Raises an exception if there is no field with the given name.
RNTupleCollectionView(DescriptorId_t fieldId, Internal::RPageSource *source)
RNTupleClusterRange GetCollectionRange(NTupleSize_t globalIndex)
RNTupleCollectionView(const RNTupleCollectionView &other)=delete
The on-storage meta-data of an ntuple.
DescriptorId_t FindFieldId(std::string_view fieldName, DescriptorId_t parentId) const
const RFieldDescriptor & GetFieldDescriptor(DescriptorId_t fieldId) const
Used to loop over indexes (entries or collections) between start and end.
RNTupleGlobalRange(NTupleSize_t start, NTupleSize_t end)
An RNTuple that is used to read data from storage.
RNTupleView & operator=(const RNTupleView &other)=delete
RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, void *rawPtr)
RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource)
RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, std::shared_ptr< void > objPtr)
void SetupField(DescriptorId_t fieldId, Internal::RPageSource *pageSource)
static std::unique_ptr< RFieldBase > CreateField(DescriptorId_t fieldId, const RNTupleDescriptor &desc)
RNTupleView & operator=(RNTupleView &&other)=default
An RNTupleView provides read-only access to a single field of the ntuple.
RNTupleView & operator=(RNTupleView &&other)=default
const T & operator()(RClusterIndex clusterIndex)
void SetupField(DescriptorId_t fieldId, Internal::RPageSource *pageSource)
RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, std::shared_ptr< T > objPtr)
const C * MapV(NTupleSize_t globalIndex, NTupleSize_t &nItems)
RNTupleView & operator=(const RNTupleView &other)=delete
RFieldBase::RValue fValue
Used as a Read() destination for fields that are not mappable.
FieldT fField
fFieldId has fParent always set to null; views access nested fields without looking at the parent
RNTupleView(const RNTupleView &other)=delete
const C * MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
void Bind(std::shared_ptr< T > objPtr)
RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource, T *rawPtr)
const FieldT & GetField() const
RNTupleGlobalRange GetFieldRange() const
RNTupleView(RNTupleView &&other)=default
const T & operator()(NTupleSize_t globalIndex)
RNTupleView(DescriptorId_t fieldId, Internal::RPageSource *pageSource)
void CallConnectPageSourceOnField(RFieldBase &, RPageSource &)
Definition RField.cxx:347
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr NTupleSize_t kInvalidNTupleIndex
constexpr DescriptorId_t kInvalidDescriptorId
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
Wrap the integer in a struct in order to avoid template specialization clash with std::uint64_t.