Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
RNTupleView.hxx
Go to the documentation of this file.
1/// \file ROOT/RNTupleView.hxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2018-10-05
4
5/*************************************************************************
6 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#ifndef ROOT_RNTupleView
14#define ROOT_RNTupleView
15
16#include <ROOT/RError.hxx>
17#include <ROOT/RField.hxx>
18#include <ROOT/RNTupleRange.hxx>
19#include <ROOT/RNTupleTypes.hxx>
20#include <ROOT/RNTupleUtils.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 {
30
31class RNTupleReader;
32
33namespace Internal {
34
35/// Helper to get the iteration space of the given field that needs to be connected to the given page source.
36/// The indexes are given by the number of elements of the principal column of the field or, if none exists,
37/// by the number of elements of the first principal column found in the subfields searched by BFS.
38/// If the field hierarchy is empty on columns, the returned field range is invalid (start and end set to
39/// kInvalidNTupleIndex). An attempt to use such a field range in RNTupleViewBase::GetFieldRange will throw.
40ROOT::RNTupleGlobalRange GetFieldRange(const ROOT::RFieldBase &field, const ROOT::Internal::RPageSource &pageSource);
41
42} // namespace Internal
43
44// clang-format off
45/**
46\class ROOT::RNTupleViewBase
47\ingroup NTuple
48\brief An RNTupleView provides read-only access to a single field of an RNTuple
49
50\tparam T The type of the object that will be read by the view; can be void if unknown at compile time.
51
52The view owns a field and its underlying columns in order to fill an RField::RValue object with data. Data can be
53accessed by index. For top-level fields, the index refers to the entry number. Fields that are part of
54nested collections have global index numbers that are derived from their parent indexes (\see GetFieldRange()).
55
56View can only be created by a reader or by a collection view.
57
58**Example: read an RNTuple's field with a view**
59~~~ {.cpp}
60auto reader = RNTupleReader::Open("myNtuple", "myntuple.root");
61auto viewFoo = reader->GetView<float>("foo");
62for (auto idx : reader->GetEntryRange()) {
63 float foo = viewFoo(idx); // read field "foo" of the `idx`-th entry
64 std::cout << foo << "\n";
65}
66~~~
67
68**Example: read an RNTuple's collection subfield with a view**
69~~~ {.cpp}
70auto reader = RNTupleReader::Open("myNtuple", "myntuple.root");
71// Assuming "v" is a std::vector<int>:
72auto view = reader->GetView<int>("v._0");
73// Effectively flattens all fields "v" in all entries and reads their elements.
74for (auto idx : view.GetFieldRange()) {
75 int x = view(idx);
76 std::cout << x << "\n";
77}
78~~~
79*/
80// clang-format on
81template <typename T>
83protected:
84 std::unique_ptr<ROOT::RFieldBase> fField;
87
88 static std::unique_ptr<ROOT::RFieldBase>
89 CreateField(ROOT::DescriptorId_t fieldId, Internal::RPageSource &pageSource, std::string_view typeName = "")
90 {
91 RFieldZero fieldZero;
93 std::unique_ptr<ROOT::RFieldBase> field;
94 {
95 const auto &desc = pageSource.GetSharedDescriptorGuard().GetRef();
96 const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
97 if constexpr (std::is_void_v<T>) {
98 if (typeName.empty())
99 field = fieldDesc.CreateField(desc);
100 else
101 field = ROOT::RFieldBase::Create(fieldDesc.GetFieldName(), std::string(typeName)).Unwrap();
102 } else {
103 field = std::make_unique<ROOT::RField<T>>(fieldDesc.GetFieldName());
104 }
105 }
106 field->SetOnDiskId(fieldId);
107 fieldZero.Attach(std::move(field));
109 return std::move(fieldZero.ReleaseSubfields()[0]);
110 }
111
112 RNTupleViewBase(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range)
113 : fField(std::move(field)), fFieldRange(range), fValue(fField->CreateValue())
114 {
115 }
116
117 RNTupleViewBase(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, std::shared_ptr<T> objPtr)
118 : fField(std::move(field)), fFieldRange(range), fValue(fField->BindValue(objPtr))
119 {
120 }
121
122 RNTupleViewBase(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, T *rawPtr)
123 : fField(std::move(field)),
124 fFieldRange(range),
125 fValue(fField->BindValue(ROOT::Internal::MakeAliasedSharedPtr(rawPtr)))
126 {
127 }
128
129public:
130 RNTupleViewBase(const RNTupleViewBase &other) = delete;
134 ~RNTupleViewBase() = default;
135
136 const ROOT::RFieldBase &GetField() const { return *fField; }
138
139 const ROOT::RFieldBase::RValue &GetValue() const { return fValue; }
140 /// Returns the global field range of this view.
141 /// This may differ from the RNTuple's entry range in case of subfields and can be used to iterate
142 /// over all the concatenated elements of the subfield without caring which entry they belong to.
143 /// Throws an RException if the underlying field of this view is empty, i.e. if it's a class or
144 /// record field with no associated columns.
146 {
147 if (!fFieldRange.IsValid()) {
148 throw RException(R__FAIL("field iteration over empty fields is unsupported: " + fField->GetFieldName()));
149 }
150 return fFieldRange;
151 }
152
153 void Bind(std::shared_ptr<T> objPtr) { fValue.Bind(objPtr); }
154 void BindRawPtr(T *rawPtr) { fValue.BindRawPtr(rawPtr); }
155 void EmplaceNew() { fValue.EmplaceNew(); }
156};
157
158// clang-format off
159/**
160\class ROOT::RNTupleView
161\ingroup NTuple
162\brief An RNTupleView for a known type. See RNTupleViewBase.
163*/
164// clang-format on
165template <typename T>
166class RNTupleView : public RNTupleViewBase<T> {
169
170protected:
171 RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range)
172 : RNTupleViewBase<T>(std::move(field), range)
173 {
174 }
175
176 RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, std::shared_ptr<T> objPtr)
177 : RNTupleViewBase<T>(std::move(field), range, objPtr)
178 {
179 }
180
181 RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, T *rawPtr)
182 : RNTupleViewBase<T>(std::move(field), range, rawPtr)
183 {
184 }
185
186 const T &GetValueRef() const
187 {
188 // We created the RValue and know its type, avoid extra checks.
189 void *ptr = RNTupleViewBase<T>::fValue.template GetPtr<void>().get();
190 return *static_cast<T *>(ptr);
191 }
192
193public:
194 RNTupleView(const RNTupleView &other) = delete;
195 RNTupleView(RNTupleView &&other) = default;
196 RNTupleView &operator=(const RNTupleView &other) = delete;
197 RNTupleView &operator=(RNTupleView &&other) = default;
198 ~RNTupleView() = default;
199
200 /// Reads the value of this view for the entry with the provided `globalIndex`.
201 const T &operator()(ROOT::NTupleSize_t globalIndex)
202 {
203 RNTupleViewBase<T>::fValue.Read(globalIndex);
204 return GetValueRef();
205 }
206
207 /// Reads the value of this view for the entry with the provided `localIndex`.
208 /// See RNTupleLocalIndex for more details.
209 const T &operator()(RNTupleLocalIndex localIndex)
210 {
211 RNTupleViewBase<T>::fValue.Read(localIndex);
212 return GetValueRef();
213 }
214};
215
216// clang-format off
217/**
218\class ROOT::RNTupleView
219\ingroup NTuple
220\brief An RNTupleView that can be used when the type is unknown at compile time. See RNTupleViewBase.
221*/
222// clang-format on
223template <>
224class RNTupleView<void> final : public RNTupleViewBase<void> {
227
228protected:
229 RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range)
230 : RNTupleViewBase<void>(std::move(field), range)
231 {
232 }
233
234 RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, std::shared_ptr<void> objPtr)
235 : RNTupleViewBase<void>(std::move(field), range, objPtr)
236 {
237 }
238
239 RNTupleView(std::unique_ptr<ROOT::RFieldBase> field, ROOT::RNTupleGlobalRange range, void *rawPtr)
240 : RNTupleViewBase<void>(std::move(field), range, rawPtr)
241 {
242 }
243
244public:
245 RNTupleView(const RNTupleView &other) = delete;
246 RNTupleView(RNTupleView &&other) = default;
247 RNTupleView &operator=(const RNTupleView &other) = delete;
248 RNTupleView &operator=(RNTupleView &&other) = default;
249 ~RNTupleView() = default;
250
251 /// \see RNTupleView::operator()(ROOT::NTupleSize_t)
252 void operator()(ROOT::NTupleSize_t globalIndex) { fValue.Read(globalIndex); }
253 /// \see RNTupleView::operator()(RNTupleLocalIndex)
254 void operator()(RNTupleLocalIndex localIndex) { fValue.Read(localIndex); }
255};
256
257// clang-format off
258/**
259\class ROOT::RNTupleDirectAccessView
260\ingroup NTuple
261\brief A view variant that provides direct access to the I/O buffers. Only works for mappable fields.
262*/
263// clang-format on
264template <typename T>
268
269protected:
272
274 {
275 const auto &desc = pageSource.GetSharedDescriptorGuard().GetRef();
276 const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
277 if (!Internal::IsMatchingFieldType<T>(fieldDesc.GetTypeName())) {
278 throw RException(R__FAIL("type mismatch for field " + fieldDesc.GetFieldName() + ": " +
279 fieldDesc.GetTypeName() + " vs. " + ROOT::RField<T>::TypeName()));
280 }
281 ROOT::RField<T> field(fieldDesc.GetFieldName());
282 field.SetOnDiskId(fieldId);
284 return field;
285 }
286
288 : fField(std::move(field)), fFieldRange(range)
289 {
290 }
291
292public:
298
299 const ROOT::RFieldBase &GetField() const { return fField; }
300 /// \see RNTupleView::GetFieldRange()
302
303 /// \see RNTupleView::operator()(ROOT::NTupleSize_t)
304 const T &operator()(ROOT::NTupleSize_t globalIndex) { return *fField.Map(globalIndex); }
305 /// \see RNTupleView::operator()(RNTupleLocalIndex)
306 const T &operator()(RNTupleLocalIndex localIndex) { return *fField.Map(localIndex); }
307};
308
309// clang-format off
310/**
311\class ROOT::RNTupleCollectionView
312\ingroup NTuple
313\brief A view for a collection, that can itself generate new ntuple views for its nested fields.
314*/
315// clang-format on
318
319private:
323
324 RNTupleCollectionView(ROOT::DescriptorId_t fieldId, const std::string &fieldName,
326 : fSource(source), fField(fieldName), fValue(fField.CreateValue())
327 {
328 fField.SetOnDiskId(fieldId);
330 }
331
333 {
334 std::string fieldName;
335 {
336 const auto &desc = source->GetSharedDescriptorGuard().GetRef();
337 const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
338 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kCollection) {
339 throw RException(
340 R__FAIL("invalid attemt to create collection view on non-collection field " + fieldDesc.GetFieldName()));
341 }
342 fieldName = fieldDesc.GetFieldName();
343 }
344 return RNTupleCollectionView(fieldId, fieldName, source);
345 }
346
347 ROOT::DescriptorId_t GetFieldId(std::string_view fieldName)
348 {
349 auto descGuard = fSource->GetSharedDescriptorGuard();
350 auto fieldId = descGuard->FindFieldId(fieldName, fField.GetOnDiskId());
351 if (fieldId == ROOT::kInvalidDescriptorId) {
352 throw RException(R__FAIL("no field named '" + std::string(fieldName) + "' in collection '" +
353 descGuard->GetQualifiedFieldName(fField.GetOnDiskId()) + "'"));
354 }
355 return fieldId;
356 }
357
358 std::uint64_t GetCardinalityValue() const
359 {
360 // We created the RValue and know its type, avoid extra checks.
361 void *ptr = fValue.GetPtr<void>().get();
362 return *static_cast<RNTupleCardinality<std::uint64_t> *>(ptr);
363 }
364
365public:
369 : fSource(other.fSource), fField(std::move(other.fField)), fValue(fField.CreateValue())
370 {
371 }
373 {
374 if (this == &other)
375 return *this;
376 std::swap(fSource, other.fSource);
377 std::swap(fField, other.fField);
378 fValue = fField.CreateValue();
379 return *this;
380 }
382
384 {
386 RNTupleLocalIndex collectionStart;
387 fField.GetCollectionInfo(globalIndex, &collectionStart, &size);
388 return ROOT::RNTupleLocalRange(collectionStart.GetClusterId(), collectionStart.GetIndexInCluster(),
389 collectionStart.GetIndexInCluster() + size);
390 }
391
393 {
395 RNTupleLocalIndex collectionStart;
396 fField.GetCollectionInfo(localIndex, &collectionStart, &size);
397 return ROOT::RNTupleLocalRange(collectionStart.GetClusterId(), collectionStart.GetIndexInCluster(),
398 collectionStart.GetIndexInCluster() + size);
399 }
400
401 /// Provides access to an individual (sub)field.
402 ///
403 /// Raises an exception if there is no field with the given name.
404 ///
405 /// \sa ROOT::RNTupleReader::GetView(std::string_view)
406 template <typename T>
407 RNTupleView<T> GetView(std::string_view fieldName)
408 {
409 auto field = RNTupleView<T>::CreateField(GetFieldId(fieldName), *fSource);
410 auto range = Internal::GetFieldRange(*field, *fSource);
411 return RNTupleView<T>(std::move(field), range);
412 }
413
414 /// Provides direct access to the I/O buffers of a **mappable** (sub)field.
415 ///
416 /// Raises an exception if there is no field with the given name.
417 /// Attempting to access the values of a direct-access view for non-mappable fields will yield compilation errors.
418 ///
419 /// \sa ROOT::RNTupleReader::DirectAccessView(std::string_view)
420 template <typename T>
422 {
424 auto range = Internal::GetFieldRange(field, *fSource);
425 return RNTupleDirectAccessView<T>(std::move(field), range);
426 }
427
428 /// Provides access to a collection field, that can itself generate new RNTupleViews for its nested fields.
429 ///
430 /// Raises an exception if:
431 /// * there is no field with the given name or,
432 /// * the field is not a collection
433 ///
434 /// \sa ROOT::RNTupleReader::GetCollectionView(std::string_view)
435 RNTupleCollectionView GetCollectionView(std::string_view fieldName)
436 {
438 }
439
440 /// \see RNTupleView::operator()(ROOT::NTupleSize_t)
441 std::uint64_t operator()(ROOT::NTupleSize_t globalIndex)
442 {
443 fValue.Read(globalIndex);
444 return GetCardinalityValue();
445 }
446
447 /// \see RNTupleView::operator()(RNTupleLocalIndex)
448 std::uint64_t operator()(RNTupleLocalIndex localIndex)
449 {
450 fValue.Read(localIndex);
451 return GetCardinalityValue();
452 }
453};
454
455} // namespace ROOT
456
457#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:299
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const ROOT::RNTupleDescriptor & GetRef() const
Abstract interface to read data from an ntuple.
const RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Points to an array of objects with RNTuple I/O support, used for bulk reading.
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
void SetOnDiskId(ROOT::DescriptorId_t id)
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:58
std::vector< std::unique_ptr< RFieldBase > > ReleaseSubfields()
Moves all subfields into the returned vector.
Definition RField.cxx:64
void Attach(std::unique_ptr< RFieldBase > child)
A public version of the Attach method that allows piece-wise construction of the zero field.
Definition RField.cxx:40
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:320
static std::string TypeName()
Definition RField.hxx:322
ROOT::DescriptorId_t GetFieldId(std::string_view fieldName)
RNTupleCollectionView & operator=(const RNTupleCollectionView &other)=delete
std::uint64_t GetCardinalityValue() const
RNTupleView< T > GetView(std::string_view fieldName)
Provides access to an individual (sub)field.
RNTupleCollectionView(const RNTupleCollectionView &other)=delete
RNTupleCollectionView(ROOT::DescriptorId_t fieldId, const std::string &fieldName, ROOT::Internal::RPageSource *source)
ROOT::Internal::RPageSource * fSource
ROOT::RNTupleLocalRange GetCollectionRange(ROOT::NTupleSize_t globalIndex)
std::uint64_t operator()(ROOT::NTupleSize_t globalIndex)
RNTupleCollectionView GetCollectionView(std::string_view fieldName)
Provides access to a collection field, that can itself generate new RNTupleViews for its nested field...
ROOT::RField< RNTupleCardinality< std::uint64_t > > fField
RNTupleCollectionView & operator=(RNTupleCollectionView &&other)
ROOT::RFieldBase::RValue fValue
static RNTupleCollectionView Create(ROOT::DescriptorId_t fieldId, ROOT::Internal::RPageSource *source)
RNTupleDirectAccessView< T > GetDirectAccessView(std::string_view fieldName)
Provides direct access to the I/O buffers of a mappable (sub)field.
RNTupleCollectionView(RNTupleCollectionView &&other)
std::uint64_t operator()(RNTupleLocalIndex localIndex)
ROOT::RNTupleLocalRange GetCollectionRange(RNTupleLocalIndex localIndex)
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
A view variant that provides direct access to the I/O buffers.
ROOT::RNTupleGlobalRange GetFieldRange() const
RNTupleDirectAccessView & operator=(RNTupleDirectAccessView &&other)=default
RNTupleDirectAccessView(ROOT::RField< T > field, ROOT::RNTupleGlobalRange range)
RNTupleDirectAccessView & operator=(const RNTupleDirectAccessView &other)=delete
static ROOT::RField< T > CreateField(ROOT::DescriptorId_t fieldId, ROOT::Internal::RPageSource &pageSource)
ROOT::RNTupleGlobalRange fFieldRange
RNTupleDirectAccessView(const RNTupleDirectAccessView &other)=delete
const T & operator()(RNTupleLocalIndex localIndex)
const T & operator()(ROOT::NTupleSize_t globalIndex)
RNTupleDirectAccessView(RNTupleDirectAccessView &&other)=default
const ROOT::RFieldBase & GetField() const
Used to loop over indexes (entries or collections) between start and end.
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
ROOT::NTupleSize_t GetIndexInCluster() const
ROOT::DescriptorId_t GetClusterId() const
Used to loop over entries of collections in a single cluster.
Reads RNTuple data from storage.
const ROOT::RFieldBase & GetField() const
const ROOT::RFieldBase::RValue & GetValue() const
void BindRawPtr(T *rawPtr)
RNTupleViewBase & operator=(const RNTupleViewBase &other)=delete
static std::unique_ptr< ROOT::RFieldBase > CreateField(ROOT::DescriptorId_t fieldId, Internal::RPageSource &pageSource, std::string_view typeName="")
std::unique_ptr< ROOT::RFieldBase > fField
RNTupleViewBase(RNTupleViewBase &&other)=default
ROOT::RNTupleGlobalRange GetFieldRange() const
Returns the global field range of this view.
ROOT::RFieldBase::RBulkValues CreateBulk()
ROOT::RNTupleGlobalRange fFieldRange
ROOT::RFieldBase::RValue fValue
RNTupleViewBase & operator=(RNTupleViewBase &&other)=default
~RNTupleViewBase()=default
RNTupleViewBase(const RNTupleViewBase &other)=delete
RNTupleViewBase(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range)
void Bind(std::shared_ptr< T > objPtr)
RNTupleViewBase(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range, std::shared_ptr< T > objPtr)
RNTupleViewBase(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range, T *rawPtr)
friend class RNTupleCollectionView
RNTupleView(const RNTupleView &other)=delete
void operator()(ROOT::NTupleSize_t globalIndex)
RNTupleView(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range, void *rawPtr)
RNTupleView & operator=(const RNTupleView &other)=delete
RNTupleView(RNTupleView &&other)=default
RNTupleView(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range)
void operator()(RNTupleLocalIndex localIndex)
RNTupleView & operator=(RNTupleView &&other)=default
RNTupleView(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range, std::shared_ptr< void > objPtr)
An RNTupleView for a known type.
RNTupleView & operator=(const RNTupleView &other)=delete
friend class RNTupleCollectionView
RNTupleView(RNTupleView &&other)=default
RNTupleView(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range, T *rawPtr)
RNTupleView(const RNTupleView &other)=delete
const T & GetValueRef() const
const T & operator()(RNTupleLocalIndex localIndex)
Reads the value of this view for the entry with the provided localIndex.
RNTupleView & operator=(RNTupleView &&other)=default
RNTupleView(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range)
const T & operator()(ROOT::NTupleSize_t globalIndex)
Reads the value of this view for the entry with the provided globalIndex.
RNTupleView(std::unique_ptr< ROOT::RFieldBase > field, ROOT::RNTupleGlobalRange range, std::shared_ptr< T > objPtr)
~RNTupleView()=default
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:35
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
ROOT::RNTupleGlobalRange GetFieldRange(const ROOT::RFieldBase &field, const ROOT::Internal::RPageSource &pageSource)
Helper to get the iteration space of the given field that needs to be connected to the given page sou...
auto MakeAliasedSharedPtr(T *rawPtr)
bool IsMatchingFieldType(const std::string &actualTypeName)
Helper to check if a given type name is the one expected of Field<T>.
Definition RField.hxx:559
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
Helper types to present an offset column as array of collection sizes.