Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleDescriptor.cxx
Go to the documentation of this file.
1/// \file RNTupleDescriptor.cxx
2/// \ingroup NTuple
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \author Javier Lopez-Gomez <javier.lopez.gomez@cern.ch>
5/// \date 2018-10-04
6
7/*************************************************************************
8 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
9 * All rights reserved. *
10 * *
11 * For the licensing terms see $ROOTSYS/LICENSE. *
12 * For the list of contributors see $ROOTSYS/README/CREDITS. *
13 *************************************************************************/
14
15#include <ROOT/RError.hxx>
16#include <ROOT/RFieldBase.hxx>
17#include <ROOT/RNTuple.hxx>
19#include <ROOT/RNTupleModel.hxx>
20#include <ROOT/RNTupleTypes.hxx>
21#include <ROOT/RNTupleUtils.hxx>
22#include <ROOT/RPage.hxx>
23#include <string_view>
24
25#include <RZip.h>
26#include <TError.h>
28
29#include <algorithm>
30#include <cstdint>
31#include <deque>
32#include <functional>
33#include <iostream>
34#include <set>
35#include <utility>
36
38
40{
41 return fFieldId == other.fFieldId && fFieldVersion == other.fFieldVersion && fTypeVersion == other.fTypeVersion &&
42 fFieldName == other.fFieldName && fFieldDescription == other.fFieldDescription &&
43 fTypeName == other.fTypeName && fTypeAlias == other.fTypeAlias && fNRepetitions == other.fNRepetitions &&
44 fStructure == other.fStructure && fParentId == other.fParentId &&
45 fProjectionSourceId == other.fProjectionSourceId && fLinkIds == other.fLinkIds &&
46 fLogicalColumnIds == other.fLogicalColumnIds && other.fTypeChecksum == other.fTypeChecksum;
47}
48
50{
51 RFieldDescriptor clone;
52 clone.fFieldId = fFieldId;
53 clone.fFieldVersion = fFieldVersion;
54 clone.fTypeVersion = fTypeVersion;
55 clone.fFieldName = fFieldName;
56 clone.fFieldDescription = fFieldDescription;
57 clone.fTypeName = fTypeName;
58 clone.fTypeAlias = fTypeAlias;
59 clone.fNRepetitions = fNRepetitions;
60 clone.fStructure = fStructure;
61 clone.fParentId = fParentId;
62 clone.fProjectionSourceId = fProjectionSourceId;
63 clone.fLinkIds = fLinkIds;
64 clone.fColumnCardinality = fColumnCardinality;
65 clone.fLogicalColumnIds = fLogicalColumnIds;
66 clone.fTypeChecksum = fTypeChecksum;
67 return clone;
68}
69
70std::unique_ptr<ROOT::RFieldBase>
72{
73 if (GetStructure() == ROOT::ENTupleStructure::kStreamer) {
74 auto streamerField = std::make_unique<ROOT::RStreamerField>(GetFieldName(), GetTypeName());
75 streamerField->SetOnDiskId(fFieldId);
76 return streamerField;
77 }
78
79 // The structure may be unknown if the descriptor comes from a deserialized field with an unknown structural role.
80 // For forward compatibility, we allow this case and return an InvalidField.
81 if (GetStructure() == ROOT::ENTupleStructure::kUnknown) {
82 if (options.GetReturnInvalidOnError()) {
83 auto invalidField = std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), "",
85 invalidField->SetOnDiskId(fFieldId);
86 return invalidField;
87 } else {
88 throw RException(R__FAIL("unexpected on-disk field structure value for field \"" + GetFieldName() + "\""));
89 }
90 }
91
92 // Untyped records and collections
93 if (GetTypeName().empty()) {
94 switch (GetStructure()) {
96 std::vector<std::unique_ptr<ROOT::RFieldBase>> memberFields;
97 memberFields.reserve(fLinkIds.size());
98 for (auto id : fLinkIds) {
99 const auto &memberDesc = ntplDesc.GetFieldDescriptor(id);
100 auto field = memberDesc.CreateField(ntplDesc, options);
102 return field;
103 memberFields.emplace_back(std::move(field));
104 }
105 auto recordField = std::make_unique<ROOT::RRecordField>(GetFieldName(), std::move(memberFields));
106 recordField->SetOnDiskId(fFieldId);
107 return recordField;
108 }
110 if (fLinkIds.size() != 1) {
111 throw RException(R__FAIL("unsupported untyped collection for field \"" + GetFieldName() + "\""));
112 }
113 auto itemField = ntplDesc.GetFieldDescriptor(fLinkIds[0]).CreateField(ntplDesc, options);
115 return itemField;
116 auto collectionField = ROOT::RVectorField::CreateUntyped(GetFieldName(), std::move(itemField));
117 collectionField->SetOnDiskId(fFieldId);
118 return collectionField;
119 }
120 default: throw RException(R__FAIL("unsupported untyped field structure for field \"" + GetFieldName() + "\""));
121 }
122 }
123
124 try {
125 const auto &fieldName = GetFieldName();
126 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
127 // NOTE: Unwrap() here may throw an exception, hence the try block.
128 // If options.fReturnInvalidOnError is false we just rethrow it, otherwise we return an InvalidField wrapping the
129 // error.
130 auto field = ROOT::Internal::CallFieldBaseCreate(fieldName, typeName, options, &ntplDesc, fFieldId).Unwrap();
131 field->SetOnDiskId(fFieldId);
132
133 for (auto &subfield : *field) {
134 const auto subfieldId = ntplDesc.FindFieldId(subfield.GetFieldName(), subfield.GetParent()->GetOnDiskId());
135 subfield.SetOnDiskId(subfieldId);
137 auto &invalidField = static_cast<ROOT::RInvalidField &>(subfield);
138 // A subfield being invalid "infects" its entire ancestry.
139 return invalidField.Clone(fieldName);
140 }
141 }
142
143 return field;
144 } catch (const RException &ex) {
145 if (options.GetReturnInvalidOnError())
146 return std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), ex.GetError().GetReport(),
148 else
149 throw ex;
150 }
151}
152
154{
156 return false;
157
158 // Skip untyped structs
159 if (fTypeName.empty())
160 return false;
161
162 if (fStructure == ROOT::ENTupleStructure::kRecord) {
163 if (fTypeName.compare(0, 10, "std::pair<") == 0)
164 return false;
165 if (fTypeName.compare(0, 11, "std::tuple<") == 0)
166 return false;
167 }
168
169 return true;
170}
171
173{
174 if (fStructure != ROOT::ENTupleStructure::kPlain)
175 return false;
176 if (fTypeName.rfind("std::", 0) == 0)
177 return false;
178
179 auto subFieldId = desc.FindFieldId("_0", fFieldId);
181 return false;
182
183 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
184 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
185 "std::int64_t", "std::uint64_t"};
186 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
187 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
188}
189
190////////////////////////////////////////////////////////////////////////////////
191
193{
194 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
195 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
196 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
197 fValueRange == other.fValueRange;
198}
199
201{
202 RColumnDescriptor clone;
203 clone.fLogicalColumnId = fLogicalColumnId;
204 clone.fPhysicalColumnId = fPhysicalColumnId;
205 clone.fBitsOnStorage = fBitsOnStorage;
206 clone.fType = fType;
207 clone.fFieldId = fFieldId;
208 clone.fIndex = fIndex;
209 clone.fFirstElementIndex = fFirstElementIndex;
210 clone.fRepresentationIndex = fRepresentationIndex;
211 clone.fValueRange = fValueRange;
212 return clone;
213}
214
215////////////////////////////////////////////////////////////////////////////////
216
219{
220 const auto N = fCumulativeNElements.size();
221 R__ASSERT(N > 0);
222 R__ASSERT(N == fPageInfos.size());
223
224 std::size_t left = 0;
225 std::size_t right = N - 1;
226 std::size_t midpoint = N;
227 while (left <= right) {
228 midpoint = (left + right) / 2;
229 if (fCumulativeNElements[midpoint] <= idxInCluster) {
230 left = midpoint + 1;
231 continue;
232 }
233
234 if ((midpoint == 0) || (fCumulativeNElements[midpoint - 1] <= idxInCluster))
235 break;
236
237 right = midpoint - 1;
238 }
240
241 auto pageInfo = fPageInfos[midpoint];
242 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : fCumulativeNElements[midpoint - 1];
244 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
246}
247
248std::size_t
251 std::size_t pageSize)
252{
253 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
254 R__ASSERT(!columnRange.IsSuppressed());
255
256 const auto nElements =
257 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
258 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
259 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
260
262 return 0U;
263 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
264
265 std::vector<RPageInfo> pageInfos;
266 // Synthesize new `RPageInfo`s as needed
267 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
271 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
274 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
275 pageInfo.SetLocator(locator);
276 pageInfos.emplace_back(pageInfo);
277 nRemainingElements -= pageInfo.GetNElements();
278 }
279
280 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
281 std::make_move_iterator(fPageInfos.end()));
282 std::swap(fPageInfos, pageInfos);
284}
285
287{
288 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
289 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
290}
291
293{
294 std::uint64_t nbytes = 0;
295 for (const auto &pr : fPageRanges) {
296 for (const auto &pi : pr.second.GetPageInfos()) {
297 nbytes += pi.GetLocator().GetNBytesOnStorage();
298 }
299 }
300 return nbytes;
301}
302
304{
305 RClusterDescriptor clone;
306 clone.fClusterId = fClusterId;
307 clone.fFirstEntryIndex = fFirstEntryIndex;
308 clone.fNEntries = fNEntries;
309 clone.fColumnRanges = fColumnRanges;
310 for (const auto &d : fPageRanges)
311 clone.fPageRanges.emplace(d.first, d.second.Clone());
312 return clone;
313}
314
315////////////////////////////////////////////////////////////////////////////////
316
318{
319 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
320}
321
323{
325 clone.fContentId = fContentId;
326 clone.fTypeVersion = fTypeVersion;
327 clone.fTypeName = fTypeName;
328 clone.fContent = fContent;
329 return clone;
330}
331
332////////////////////////////////////////////////////////////////////////////////
333
335{
336 // clang-format off
337 return fName == other.fName &&
338 fDescription == other.fDescription &&
339 fNEntries == other.fNEntries &&
340 fGeneration == other.fGeneration &&
341 fFieldZeroId == other.fFieldZeroId &&
342 fFieldDescriptors == other.fFieldDescriptors &&
343 fColumnDescriptors == other.fColumnDescriptors &&
344 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
345 fClusterDescriptors == other.fClusterDescriptors;
346 // clang-format on
347}
348
350{
352 for (const auto &cd : fClusterDescriptors) {
353 if (!cd.second.ContainsColumn(physicalColumnId))
354 continue;
355 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
356 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
357 }
358 return result;
359}
360
361////////////////////////////////////////////////////////////////////////////////
362/// Return the cluster boundaries for each cluster in this RNTuple.
363std::vector<ROOT::Internal::RNTupleClusterBoundaries>
365{
366 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
367 boundaries.reserve(desc.GetNClusters());
368 auto clusterId = desc.FindClusterId(0, 0);
370 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
371 R__ASSERT(clusterDesc.GetNEntries() > 0);
372 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
373 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
375 }
376 return boundaries;
377}
378
381{
382 std::string leafName(fieldName);
383 auto posDot = leafName.find_last_of('.');
384 if (posDot != std::string::npos) {
385 auto parentName = leafName.substr(0, posDot);
386 leafName = leafName.substr(posDot + 1);
387 parentId = FindFieldId(parentName, parentId);
388 }
389 auto itrFieldDesc = fFieldDescriptors.find(parentId);
390 if (itrFieldDesc == fFieldDescriptors.end())
392 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
393 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
394 return linkId;
395 }
397}
398
400{
402 return "";
403
404 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
405 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
406 if (prefix.empty())
407 return fieldDescriptor.GetFieldName();
408 return prefix + "." + fieldDescriptor.GetFieldName();
409}
410
412{
413 std::string typeName = fieldDesc.GetTypeName();
414
415 // ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
416 R__ASSERT(fVersionEpoch == 1);
417 if (fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1) {
418 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
419 }
420
421 return typeName;
422}
423
425{
426 return FindFieldId(fieldName, GetFieldZeroId());
427}
428
430 std::uint32_t columnIndex,
431 std::uint16_t representationIndex) const
432{
433 auto itr = fFieldDescriptors.find(fieldId);
434 if (itr == fFieldDescriptors.cend())
436 if (columnIndex >= itr->second.GetColumnCardinality())
438 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
439 if (itr->second.GetLogicalColumnIds().size() <= idx)
441 return itr->second.GetLogicalColumnIds()[idx];
442}
443
445 std::uint32_t columnIndex,
446 std::uint16_t representationIndex) const
447{
448 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
451 return GetColumnDescriptor(logicalId).GetPhysicalId();
452}
453
456{
457 if (GetNClusterGroups() == 0)
459
460 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
461
462 std::size_t cgLeft = 0;
463 std::size_t cgRight = GetNClusterGroups() - 1;
464 while (cgLeft <= cgRight) {
465 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
466 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
467 R__ASSERT(!clusterIds.empty());
468
469 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
470 // this may happen if the RNTuple has an empty schema
471 if (!clusterDesc.ContainsColumn(physicalColumnId))
473
474 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
476 // Look into the lower half of cluster groups
478 cgRight = cgMidpoint - 1;
479 continue;
480 }
481
482 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
483 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
484 // Look into the upper half of cluster groups
485 cgLeft = cgMidpoint + 1;
486 continue;
487 }
488
489 // Binary search in the current cluster group; since we already checked the element range boundaries,
490 // the element must be in that cluster group.
491 std::size_t clusterLeft = 0;
492 std::size_t clusterRight = clusterIds.size() - 1;
493 while (clusterLeft <= clusterRight) {
494 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
496 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
497
498 if (columnRange.Contains(index))
499 return clusterId;
500
501 if (columnRange.GetFirstElementIndex() > index) {
504 continue;
505 }
506
507 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
509 continue;
510 }
511 }
512 R__ASSERT(false);
513 }
515}
516
518{
519 if (GetNClusterGroups() == 0)
521
522 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
523
524 std::size_t cgLeft = 0;
525 std::size_t cgRight = GetNClusterGroups() - 1;
526 while (cgLeft <= cgRight) {
527 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
528 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
529
530 if (cgDesc.GetMinEntry() > entryIdx) {
532 cgRight = cgMidpoint - 1;
533 continue;
534 }
535
536 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
537 cgLeft = cgMidpoint + 1;
538 continue;
539 }
540
541 // Binary search in the current cluster group; since we already checked the element range boundaries,
542 // the element must be in that cluster group.
543 const auto &clusterIds = cgDesc.GetClusterIds();
544 R__ASSERT(!clusterIds.empty());
545 std::size_t clusterLeft = 0;
546 std::size_t clusterRight = clusterIds.size() - 1;
547 while (clusterLeft <= clusterRight) {
548 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
549 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
550
551 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
554 continue;
555 }
556
557 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
559 continue;
560 }
561
563 }
564 R__ASSERT(false);
565 }
567}
568
570{
571 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
572 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
573 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
574 // binary search code path remains tested.
575 const auto &clusterDesc = GetClusterDescriptor(clusterId);
576 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
577 return FindClusterId(firstEntryInNextCluster);
578}
579
581{
582 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
583 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
584 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
585 // binary search code path remains tested.
586 const auto &clusterDesc = GetClusterDescriptor(clusterId);
587 if (clusterDesc.GetFirstEntryIndex() == 0)
589 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
590}
591
592std::vector<ROOT::DescriptorId_t>
594{
595 auto fieldZeroId = desc.GetFieldZeroId();
596
597 std::vector<ROOT::DescriptorId_t> fields;
598 for (const auto fieldId : fFieldIdsOrder) {
599 if (desc.GetFieldDescriptor(fieldId).GetParentId() == fieldZeroId)
600 fields.emplace_back(fieldId);
601 }
602 return fields;
603}
604
610
612 : fNTuple(ntuple)
613{
614 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
615
616 while (!fieldIdQueue.empty()) {
617 auto currFieldId = fieldIdQueue.front();
618 fieldIdQueue.pop_front();
619
620 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
621 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
622
623 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
624 auto fieldId = field.GetId();
625 fieldIdQueue.push_back(fieldId);
626 }
627 }
628}
629
630std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
631{
632 std::vector<std::uint64_t> result;
633 unsigned int base = 0;
634 std::uint64_t flags = 0;
635 for (auto f : fFeatureFlags) {
636 if ((f > 0) && ((f % 64) == 0))
637 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
638 while (f > base + 64) {
639 result.emplace_back(flags);
640 flags = 0;
641 base += 64;
642 }
643 f -= base;
644 flags |= 1 << f;
645 }
646 result.emplace_back(flags);
647 return result;
648}
649
651 std::vector<RClusterDescriptor> &clusterDescs)
652{
654 if (iter == fClusterGroupDescriptors.end())
655 return R__FAIL("invalid attempt to add details of unknown cluster group");
656 if (iter->second.HasClusterDetails())
657 return R__FAIL("invalid attempt to re-populate cluster group details");
658 if (iter->second.GetNClusters() != clusterDescs.size())
659 return R__FAIL("mismatch of number of clusters");
660
661 std::vector<ROOT::DescriptorId_t> clusterIds;
662 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
663 clusterIds.emplace_back(clusterDescs[i].GetId());
664 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
665 if (!success) {
666 return R__FAIL("invalid attempt to re-populate existing cluster");
667 }
668 }
670 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
671 });
673 cgBuilder.AddSortedClusters(clusterIds);
674 iter->second = cgBuilder.MoveDescriptor().Unwrap();
675 return RResult<void>::Success();
676}
677
679{
681 if (iter == fClusterGroupDescriptors.end())
682 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
683 if (!iter->second.HasClusterDetails())
684 return R__FAIL("invalid attempt to drop details of cluster group summary");
685
686 for (auto clusterId : iter->second.GetClusterIds())
688 iter->second = iter->second.CloneSummary();
689 return RResult<void>::Success();
690}
691
692std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
693{
694 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
695 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
696 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
697 for (const auto &colDesc : GetColumnIterable()) {
699 auto fieldId = colDesc.GetFieldId();
700 while (1) {
701 const auto &field = GetFieldDescriptor(fieldId);
702 if (field.GetParentId() == GetFieldZeroId())
703 break;
704 fieldId = field.GetParentId();
705 }
706 invalidFields.insert(fieldId);
707
708 // No need to look for all invalid fields if we're gonna error out anyway
709 if (!options.GetForwardCompatible())
710 break;
711 }
712 }
713
714 if (!options.GetForwardCompatible() && !invalidFields.empty())
716 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
717 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
718
719 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
720 fieldZero->SetOnDiskId(GetFieldZeroId());
721 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
722 : RNTupleModel::Create(std::move(fieldZero));
724 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
725 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
726 for (const auto &topDesc : GetTopLevelFields()) {
727 if (invalidFields.count(topDesc.GetId()) > 0) {
728 // Field contains invalid columns: skip it
729 continue;
730 }
731
732 auto field = topDesc.CreateField(*this, createFieldOpts);
733
734 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
735 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
736 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
737 // in the first place, so we don't need to check for that flag again).
738 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
739 const auto &invalid = static_cast<const RInvalidField &>(*field);
740 const auto cat = invalid.GetCategory();
742 if (mustThrow)
743 throw invalid.GetError();
744
745 // Not a hard error: skip the field and go on.
746 continue;
747 }
748
749 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
750 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
751 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
752 });
753 } else {
754 model->AddField(std::move(field));
755 }
756 }
757 model->Freeze();
758 return model;
759}
760
762{
763 RNTupleDescriptor clone;
764 clone.fName = fName;
769 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
770 // and therefore not represent the actual sources's header.
771 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
772
773 for (const auto &d : fFieldDescriptors)
774 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
775 for (const auto &d : fColumnDescriptors)
776 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
777
778 for (const auto &d : fExtraTypeInfoDescriptors)
779 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
781 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
782
783 return clone;
784}
785
787{
789
794
798 clone.fNEntries = fNEntries;
799 clone.fNClusters = fNClusters;
800 clone.fGeneration = fGeneration;
801 for (const auto &d : fClusterGroupDescriptors)
802 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
804 for (const auto &d : fClusterDescriptors)
805 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
806 return clone;
807}
808
809////////////////////////////////////////////////////////////////////////////////
810
812{
813 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
814 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
815}
816
818{
820 clone.fClusterGroupId = fClusterGroupId;
821 clone.fPageListLocator = fPageListLocator;
822 clone.fPageListLength = fPageListLength;
823 clone.fMinEntry = fMinEntry;
824 clone.fEntrySpan = fEntrySpan;
825 clone.fNClusters = fNClusters;
826 return clone;
827}
828
830{
831 RClusterGroupDescriptor clone = CloneSummary();
832 clone.fClusterIds = fClusterIds;
833 return clone;
834}
835
836////////////////////////////////////////////////////////////////////////////////
837
840 std::uint64_t firstElementIndex,
841 std::uint32_t compressionSettings,
843{
844 if (physicalId != pageRange.fPhysicalColumnId)
845 return R__FAIL("column ID mismatch");
846 if (fCluster.fColumnRanges.count(physicalId) > 0)
847 return R__FAIL("column ID conflict");
849 for (const auto &pi : pageRange.fPageInfos) {
850 columnRange.IncrementNElements(pi.GetNElements());
851 }
852 fCluster.fPageRanges[physicalId] = pageRange.Clone();
853 fCluster.fColumnRanges[physicalId] = columnRange;
854 return RResult<void>::Success();
855}
856
859{
860 if (fCluster.fColumnRanges.count(physicalId) > 0)
861 return R__FAIL("column ID conflict");
862
864 columnRange.SetPhysicalColumnId(physicalId);
865 columnRange.SetIsSuppressed(true);
866 fCluster.fColumnRanges[physicalId] = columnRange;
867 return RResult<void>::Success();
868}
869
872{
873 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
874 if (!columnRange.IsSuppressed())
875 continue;
876 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
877
878 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
879 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
880 // We expect only few columns and column representations per field, so we do a linear search
881 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
883 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
884 continue;
885 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
886 continue;
887
888 // Found corresponding column of a different column representation
889 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
890 if (otherColumnRange.IsSuppressed())
891 continue;
892
893 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
894 columnRange.SetNElements(otherColumnRange.GetNElements());
895 break;
896 }
897
898 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
899 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
900 std::to_string(columnRange.GetPhysicalColumnId()) +
901 ", cluster ID: " + std::to_string(fCluster.GetId()));
902 }
903 }
904 return RResult<void>::Success();
905}
906
909{
910 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
911 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
912 /// parent field in the hierarchy.
914 const auto &visitField, const auto &enterSubtree) -> void {
916 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
917 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
919 }
920 };
921
922 // Extended columns can only be part of the header extension
923 if (!desc.GetHeaderExtension())
924 return *this;
925
926 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
927 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
928 for (const auto &topLevelField : desc.GetTopLevelFields()) {
930 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
931 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
932 for (const auto &c : desc.GetColumnIterable(fieldId)) {
933 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
934 auto &columnRange = fCluster.fColumnRanges[physicalId];
935
936 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
937 // extension won't have on-disk metadata for the clusters that were already committed before the model
938 // was extended. Therefore, these need to be synthetically initialized upon reading.
939 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
940 columnRange.SetPhysicalColumnId(physicalId);
941 columnRange.SetFirstElementIndex(0);
942 columnRange.SetNElements(0);
943 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
944 }
945 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
946 // number of elements should have been if the column was not deferred; fix those and let
947 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
948 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
949 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
950 // field zero excluding subfields of collection and variant fields.
951 if (c.IsDeferredColumn()) {
952 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
953 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
954 if (!columnRange.IsSuppressed()) {
955 auto &pageRange = fCluster.fPageRanges[physicalId];
956 pageRange.fPhysicalColumnId = physicalId;
957 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
958 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
959 }
960 } else if (!columnRange.IsSuppressed()) {
961 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
962 }
963 }
964 },
966 }
967 return *this;
968}
969
971{
972 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
973 return R__FAIL("unset cluster ID");
974 if (fCluster.fNEntries == 0)
975 return R__FAIL("empty cluster");
976 for (auto &pr : fCluster.fPageRanges) {
977 if (fCluster.fColumnRanges.count(pr.first) == 0) {
978 return R__FAIL("missing column range");
979 }
980 pr.second.fCumulativeNElements.clear();
981 pr.second.fCumulativeNElements.reserve(pr.second.fPageInfos.size());
983 for (const auto &pi : pr.second.fPageInfos) {
984 sum += pi.GetNElements();
985 pr.second.fCumulativeNElements.emplace_back(sum);
986 }
987 }
989 std::swap(result, fCluster);
990 return result;
991}
992
993////////////////////////////////////////////////////////////////////////////////
994
997{
999 builder.ClusterGroupId(clusterGroupDesc.GetId())
1000 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1001 .PageListLength(clusterGroupDesc.GetPageListLength())
1002 .MinEntry(clusterGroupDesc.GetMinEntry())
1003 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1004 .NClusters(clusterGroupDesc.GetNClusters());
1005 return builder;
1006}
1007
1009{
1010 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1011 return R__FAIL("unset cluster group ID");
1013 std::swap(result, fClusterGroup);
1014 return result;
1015}
1016
1017////////////////////////////////////////////////////////////////////////////////
1018
1020{
1021 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1022 throw RException(R__FAIL("invalid extra type info content id"));
1024 std::swap(result, fExtraTypeInfo);
1025 return result;
1026}
1027
1028////////////////////////////////////////////////////////////////////////////////
1029
1031{
1032 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1033 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1034 return RResult<void>::Success();
1035}
1036
1038{
1039 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1040 return R__FAIL("unset or unsupported RNTuple epoch version");
1041 }
1042
1043 // Reuse field name validity check
1044 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1045 if (!validName) {
1047 }
1048
1049 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1050 // parent not properly set?
1051 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1052 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1053 }
1054
1055 // Same number of columns in every column representation?
1056 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1057 if (columnCardinality == 0)
1058 continue;
1059
1060 // In AddColumn, we already checked that all but the last representation are complete.
1061 // Check that the last column representation is complete, i.e. has all columns.
1062 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1063 const auto nColumns = logicalColumnIds.size();
1064 // If we have only a single column representation, the following condition is true by construction
1065 if ((nColumns + 1) == columnCardinality)
1066 continue;
1067
1068 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1069 if (lastColumn.GetIndex() + 1 != columnCardinality)
1070 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1071 }
1072
1073 return RResult<void>::Success();
1074}
1075
1077{
1078 EnsureValidDescriptor().ThrowOnError();
1079 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1080 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1081 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1082 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1084 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1085 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1086 });
1088 std::swap(result, fDescriptor);
1089 return result;
1090}
1091
1093 std::uint16_t versionMinor, std::uint16_t versionPatch)
1094{
1096 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1097 }
1098 fDescriptor.fVersionEpoch = versionEpoch;
1099 fDescriptor.fVersionMajor = versionMajor;
1100 fDescriptor.fVersionMinor = versionMinor;
1101 fDescriptor.fVersionPatch = versionPatch;
1102}
1103
1105{
1106 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1107 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1108 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1109 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1110}
1111
1113 const std::string_view description)
1114{
1115 fDescriptor.fName = std::string(name);
1116 fDescriptor.fDescription = std::string(description);
1117}
1118
1120{
1121 if (flag % 64 == 0)
1122 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1123 fDescriptor.fFeatureFlags.insert(flag);
1124}
1125
1127{
1128 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1129 return R__FAIL("invalid logical column id");
1130 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1131 return R__FAIL("invalid physical column id");
1132 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1133 return R__FAIL("invalid field id, dangling column");
1134
1135 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1136 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1137 // as we have no idea what the valid range is.
1138 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1139 // we try to read the actual data contained in it.
1140 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1141 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1142 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1143 return R__FAIL("invalid column bit width");
1144 }
1145
1146 return fColumn.Clone();
1147}
1148
1151{
1153 fieldDesc.FieldVersion(field.GetFieldVersion())
1154 .TypeVersion(field.GetTypeVersion())
1155 .FieldName(field.GetFieldName())
1156 .FieldDescription(field.GetDescription())
1157 .TypeName(field.GetTypeName())
1158 .TypeAlias(field.GetTypeAlias())
1159 .Structure(field.GetStructure())
1160 .NRepetitions(field.GetNRepetitions());
1162 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1163 return fieldDesc;
1164}
1165
1167{
1168 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1169 return R__FAIL("invalid field id");
1170 }
1171 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1172 return R__FAIL("invalid field structure");
1173 }
1174 // FieldZero is usually named "" and would be a false positive here
1175 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1176 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1177 if (!validName) {
1179 }
1180 if (fField.GetFieldName().empty()) {
1181 return R__FAIL("name cannot be empty string \"\"");
1182 }
1183 }
1184 return fField.Clone();
1185}
1186
1188{
1189 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1190 if (fDescriptor.fHeaderExtension)
1191 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1192 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1193 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1194 }
1195}
1196
1199{
1201 if (!(fieldExists = EnsureFieldExists(fieldId)))
1203 if (!(fieldExists = EnsureFieldExists(linkId)))
1204 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1205
1206 if (linkId == fDescriptor.GetFieldZeroId()) {
1207 return R__FAIL("cannot make FieldZero a child field");
1208 }
1209 // fail if field already has another valid parent
1210 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1212 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1213 }
1214 if (fieldId == linkId) {
1215 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1216 }
1217 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1218 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1219 return RResult<void>::Success();
1220}
1221
1224{
1226 if (!(fieldExists = EnsureFieldExists(sourceId)))
1228 if (!(fieldExists = EnsureFieldExists(targetId)))
1229 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1230
1231 if (targetId == fDescriptor.GetFieldZeroId()) {
1232 return R__FAIL("cannot make FieldZero a projected field");
1233 }
1234 if (sourceId == targetId) {
1235 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1236 }
1237 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1238 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1239 }
1240 // fail if target field already has another valid projection source
1241 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1242 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1243 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1244 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1245 }
1246 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1247 return RResult<void>::Success();
1248}
1249
1251{
1252 const auto fieldId = columnDesc.GetFieldId();
1253 const auto columnIndex = columnDesc.GetIndex();
1254 const auto representationIndex = columnDesc.GetRepresentationIndex();
1255
1256 auto fieldExists = EnsureFieldExists(fieldId);
1257 if (!fieldExists) {
1259 }
1260 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1261
1262 if (columnDesc.IsAliasColumn()) {
1263 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1264 return R__FAIL("alias column type mismatch");
1265 }
1266 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1267 return R__FAIL("column index clash");
1268 }
1269 if (columnIndex > 0) {
1270 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1271 return R__FAIL("out of bounds column index");
1272 }
1273 if (representationIndex > 0) {
1274 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1275 return R__FAIL("out of bounds representation index");
1276 }
1277 if (columnIndex == 0) {
1278 assert(fieldDesc.fColumnCardinality > 0);
1279 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1281 return R__FAIL("incomplete column representations");
1282 }
1283 } else {
1284 if (columnIndex >= fieldDesc.fColumnCardinality)
1285 return R__FAIL("irregular column representations");
1286 }
1287 } else {
1288 // This will set the column cardinality to the number of columns of the first representation
1289 fieldDesc.fColumnCardinality = columnIndex + 1;
1290 }
1291
1292 const auto logicalId = columnDesc.GetLogicalId();
1293 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1294
1295 if (!columnDesc.IsAliasColumn())
1296 fDescriptor.fNPhysicalColumns++;
1297 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1298 if (fDescriptor.fHeaderExtension)
1299 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1300
1301 return RResult<void>::Success();
1302}
1303
1305{
1306 const auto id = clusterGroup.GetId();
1307 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1308 return R__FAIL("cluster group id clash");
1309 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1310 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1311 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1312 return RResult<void>::Success();
1313}
1314
1319
1321{
1322 if (!fDescriptor.fHeaderExtension)
1323 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1324}
1325
1327{
1328 if (fDescriptor.GetNLogicalColumns() == 0)
1329 return;
1330 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1331
1332 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1333 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1334 R__ASSERT(c.IsAliasColumn());
1335 R__ASSERT(id == c.GetLogicalId());
1336 fDescriptor.fColumnDescriptors.erase(id);
1337 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1338 if (link == c.fLogicalColumnId) {
1339 link += offset;
1340 break;
1341 }
1342 }
1343 c.fLogicalColumnId += offset;
1344 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1345 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1346 }
1347}
1348
1350{
1351 auto clusterId = clusterDesc.GetId();
1352 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1353 return R__FAIL("cluster id clash");
1354 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1355 return RResult<void>::Success();
1356}
1357
1360{
1361 // Make sure we have no duplicates
1362 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1363 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1364 return R__FAIL("extra type info duplicates");
1365 }
1366 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1367 return RResult<void>::Success();
1368}
1369
1371{
1372 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1374 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1375 *it = std::move(extraTypeInfoDesc);
1376 else
1377 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1378}
1379
1381{
1383 const auto &desc = GetDescriptor();
1384
1385 std::function<void(const RFieldDescriptor &)> fnWalkFieldTree;
1387 if (fieldDesc.IsCustomClass()) {
1388 // Add streamer info for this class to streamerInfoMap
1389 auto cl = TClass::GetClass(fieldDesc.GetTypeName().c_str());
1390 if (!cl) {
1391 throw RException(R__FAIL(std::string("cannot get TClass for ") + fieldDesc.GetTypeName()));
1392 }
1393 auto streamerInfo = cl->GetStreamerInfo(fieldDesc.GetTypeVersion());
1394 if (!streamerInfo) {
1395 throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + fieldDesc.GetTypeName()));
1396 }
1398 }
1399
1400 // Recursively traverse sub fields
1401 for (const auto &subFieldDesc : desc.GetFieldIterable(fieldDesc)) {
1403 }
1404 };
1405
1406 fnWalkFieldTree(desc.GetFieldZero());
1407
1408 // Add the streamer info records from streamer fields: because of runtime polymorphism we may need to add additional
1409 // types not covered by the type names stored in the field headers
1410 for (const auto &extraTypeInfo : desc.GetExtraTypeInfoIterable()) {
1411 if (extraTypeInfo.GetContentId() != EExtraTypeInfoIds::kStreamerInfo)
1412 continue;
1413 // Ideally, we would avoid deserializing the streamer info records of the streamer fields that we just serialized.
1414 // However, this happens only once at the end of writing and only when streamer fields are used, so the
1415 // preference here is for code simplicity.
1417 }
1418
1419 return streamerInfoMap;
1420}
1421
1426
1432
1439
1445
1452
1457
1463
1468
1474
1480
1485
1490
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:304
#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:300
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
#define N
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 Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
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 Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
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
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
char name[80]
Definition TGX11.cxx:110
#define _(A, B)
Definition cfortran.h:108
A helper class for piece-wise construction of an RClusterDescriptor.
RResult< void > MarkSuppressedColumnRange(ROOT::DescriptorId_t physicalId)
Books the given column ID as being suppressed in this cluster.
RResult< void > CommitColumnRange(ROOT::DescriptorId_t physicalId, std::uint64_t firstElementIndex, std::uint32_t compressionSettings, const RClusterDescriptor::RPageRange &pageRange)
RClusterDescriptorBuilder & AddExtendedColumnRanges(const RNTupleDescriptor &desc)
Add column and page ranges for columns created during late model extension missing in this cluster.
RResult< void > CommitSuppressedColumnRanges(const RNTupleDescriptor &desc)
Sets the first element index and number of elements for all the suppressed column ranges.
RResult< RClusterDescriptor > MoveDescriptor()
Move out the full cluster descriptor including page locations.
A helper class for piece-wise construction of an RClusterGroupDescriptor.
RClusterGroupDescriptorBuilder & EntrySpan(std::uint64_t entrySpan)
RClusterGroupDescriptorBuilder & PageListLocator(const RNTupleLocator &pageListLocator)
static RClusterGroupDescriptorBuilder FromSummary(const RClusterGroupDescriptor &clusterGroupDesc)
RClusterGroupDescriptorBuilder & PageListLength(std::uint64_t pageListLength)
RClusterGroupDescriptorBuilder & MinEntry(std::uint64_t minEntry)
RResult< RClusterGroupDescriptor > MoveDescriptor()
RClusterGroupDescriptorBuilder & ClusterGroupId(ROOT::DescriptorId_t clusterGroupId)
RClusterGroupDescriptorBuilder & NClusters(std::uint32_t nClusters)
RResult< RColumnDescriptor > MakeDescriptor() const
Attempt to make a column descriptor.
A column element encapsulates the translation between basic C++ types and their column representation...
static std::pair< std::uint16_t, std::uint16_t > GetValidBitRange(ROOT::ENTupleColumnType type)
Most types have a fixed on-disk bit width.
RResult< RExtraTypeInfoDescriptor > MoveDescriptor()
A helper class for piece-wise construction of an RFieldDescriptor.
RResult< RFieldDescriptor > MakeDescriptor() const
Attempt to make a field descriptor.
static RFieldDescriptorBuilder FromField(const ROOT::RFieldBase &field)
Make a new RFieldDescriptorBuilder based off a live RNTuple field.
void SetNTuple(const std::string_view name, const std::string_view description)
void SetSchemaFromExisting(const RNTupleDescriptor &descriptor)
Copies the "schema" part of descriptor into the builder's descriptor.
RResult< void > AddColumn(RColumnDescriptor &&columnDesc)
RResult< void > AddFieldProjection(ROOT::DescriptorId_t sourceId, ROOT::DescriptorId_t targetId)
void ReplaceExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
RResult< void > AddExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
void ShiftAliasColumns(std::uint32_t offset)
Shift column IDs of alias columns by offset
void SetVersion(std::uint16_t versionEpoch, std::uint16_t versionMajor, std::uint16_t versionMinor, std::uint16_t versionPatch)
void BeginHeaderExtension()
Mark the beginning of the header extension; any fields and columns added after a call to this functio...
RResult< void > AddCluster(RClusterDescriptor &&clusterDesc)
RResult< void > EnsureValidDescriptor() const
Checks whether invariants hold:
RResult< void > AddFieldLink(ROOT::DescriptorId_t fieldId, ROOT::DescriptorId_t linkId)
void AddField(const RFieldDescriptor &fieldDesc)
ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t BuildStreamerInfos() const
Get the streamer info records for custom classes. Currently requires the corresponding dictionaries t...
RResult< void > AddClusterGroup(RClusterGroupDescriptor &&clusterGroup)
RResult< void > EnsureFieldExists(ROOT::DescriptorId_t fieldId) const
A helper class for serializing and deserialization of the RNTuple binary format.
std::map< Int_t, TVirtualStreamerInfo * > StreamerInfoMap_t
static RResult< StreamerInfoMap_t > DeserializeStreamerInfos(const std::string &extraTypeInfoContent)
The window of element indexes of a particular column in a particular cluster.
Records the partition of data into pages for a particular column in a particular cluster.
RPageInfoExtended Find(ROOT::NTupleSize_t idxInCluster) const
Find the page in the RPageRange that contains the given element. The element must exist.
std::size_t ExtendToFitColumnRange(const RColumnRange &columnRange, const ROOT::Internal::RColumnElementBase &element, std::size_t pageSize)
Extend this RPageRange to fit the given RColumnRange.
Metadata for RNTuple clusters.
ROOT::NTupleSize_t fFirstEntryIndex
Clusters can be swapped by adjusting the entry offsets of the cluster and all ranges.
std::unordered_map< ROOT::DescriptorId_t, RColumnRange > fColumnRanges
ROOT::DescriptorId_t fClusterId
RClusterDescriptor Clone() const
bool operator==(const RClusterDescriptor &other) const
RColumnRangeIterable GetColumnRangeIterable() const
Returns an iterator over pairs { columnId, columnRange }. The iteration order is unspecified.
std::unordered_map< ROOT::DescriptorId_t, RPageRange > fPageRanges
std::uint64_t GetNBytesOnStorage() const
Clusters are bundled in cluster groups.
RNTupleLocator fPageListLocator
The page list that corresponds to the cluster group.
RClusterGroupDescriptor Clone() const
std::vector< ROOT::DescriptorId_t > fClusterIds
The cluster IDs can be empty if the corresponding page list is not loaded.
std::uint64_t fMinEntry
The minimum first entry number of the clusters in the cluster group.
std::uint32_t fNClusters
Number of clusters is always known even if the cluster IDs are not (yet) populated.
std::uint64_t fPageListLength
Uncompressed size of the page list.
std::uint64_t fEntrySpan
Number of entries that are (partially for sharded clusters) covered by this cluster group.
bool operator==(const RClusterGroupDescriptor &other) const
RClusterGroupDescriptor CloneSummary() const
Creates a clone without the cluster IDs.
Metadata stored for every column of an RNTuple.
ROOT::DescriptorId_t fPhysicalColumnId
Usually identical to the logical column ID, except for alias columns where it references the shadowed...
bool operator==(const RColumnDescriptor &other) const
ROOT::DescriptorId_t fLogicalColumnId
The actual column identifier, which is the link to the corresponding field.
ROOT::DescriptorId_t fFieldId
Every column belongs to one and only one field.
std::int64_t fFirstElementIndex
The absolute value specifies the index for the first stored element for this column.
std::uint32_t fIndex
A field can be serialized into several columns, which are numbered from zero to $n$.
std::uint16_t fBitsOnStorage
The size in bits of elements of this column.
std::uint16_t fRepresentationIndex
A field may use multiple column representations, which are numbered from zero to $m$.
ROOT::ENTupleColumnType fType
The on-disk column type.
std::optional< RValueRange > fValueRange
Optional value range (used e.g. by quantized real fields)
RColumnDescriptor Clone() const
Get a copy of the descriptor.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
bool operator==(const RExtraTypeInfoDescriptor &other) const
RExtraTypeInfoDescriptor Clone() const
EExtraTypeInfoIds fContentId
Specifies the meaning of the extra information.
std::string fTypeName
The type name the extra information refers to; empty for RNTuple-wide extra information.
std::string fContent
The content format depends on the content ID and may be binary.
std::uint32_t fTypeVersion
Type version the extra type information is bound to.
A field translates read and write calls from/to underlying columns to/from tree values.
@ kTraitInvalidField
This field is an instance of RInvalidField and can be safely static_cast to it.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
std::unique_ptr< ROOT::RFieldBase > CreateField(const RNTupleDescriptor &ntplDesc, const ROOT::RCreateFieldOptions &options={}) const
In general, we create a field simply from the C++ type name.
std::uint32_t fFieldVersion
The version of the C++-type-to-column translation mechanics.
ROOT::DescriptorId_t fFieldId
RFieldDescriptor Clone() const
Get a copy of the descriptor.
std::uint64_t fNRepetitions
The number of elements per entry for fixed-size arrays.
std::uint32_t fColumnCardinality
The number of columns in the column representations of the field.
ROOT::DescriptorId_t fProjectionSourceId
For projected fields, the source field ID.
bool IsCustomEnum(const RNTupleDescriptor &desc) const
Tells if the field describes a user-defined enum type.
bool operator==(const RFieldDescriptor &other) const
std::string fFieldDescription
Free text set by the user.
ROOT::DescriptorId_t fParentId
Establishes sub field relationships, such as classes and collections.
bool IsCustomClass() const
Tells if the field describes a user-defined class rather than a fundamental type, a collection,...
std::string fTypeAlias
A typedef or using directive that resolved to the type name during field creation.
ROOT::ENTupleStructure fStructure
The structural information carried by this field in the data model tree.
std::vector< ROOT::DescriptorId_t > fLinkIds
The pointers in the other direction from parent to children.
std::string fFieldName
The leaf name, not including parent fields.
std::uint32_t fTypeVersion
The version of the C++ type itself.
std::string fTypeName
The C++ type that was used when writing the field.
std::vector< ROOT::DescriptorId_t > fLogicalColumnIds
The ordered list of columns attached to this field: first by representation index then by column inde...
std::optional< std::uint32_t > fTypeChecksum
For custom classes, we store the ROOT TClass reported checksum to facilitate the use of I/O rules tha...
Used in RFieldBase::Check() to record field creation failures.
Definition RField.hxx:93
@ kGeneric
Generic unrecoverable error.
@ kUnknownStructure
The field could not be created because its descriptor had an unknown structural role.
Used to loop over all the clusters of an RNTuple (in unspecified order)
Used to loop over all the cluster groups of an RNTuple (in unspecified order)
Used to loop over a field's associated columns.
std::vector< ROOT::DescriptorId_t > fColumns
The descriptor ids of the columns ordered by field, representation, and column index.
RColumnDescriptorIterable(const RNTupleDescriptor &ntuple, const RFieldDescriptor &fieldDesc)
Used to loop over all the extra type info record of an RNTuple (in unspecified order)
Used to loop over a field's child fields.
std::vector< ROOT::DescriptorId_t > GetTopLevelFields(const RNTupleDescriptor &desc) const
Return a vector containing the IDs of the top-level fields defined in the extension header,...
The on-storage metadata of an RNTuple.
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
ROOT::DescriptorId_t FindNextClusterId(ROOT::DescriptorId_t clusterId) const
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
std::set< unsigned int > fFeatureFlags
std::unordered_map< ROOT::DescriptorId_t, RClusterGroupDescriptor > fClusterGroupDescriptors
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::uint64_t fNPhysicalColumns
Updated by the descriptor builder when columns are added.
ROOT::DescriptorId_t fFieldZeroId
Set by the descriptor builder.
std::uint64_t fNEntries
Updated by the descriptor builder when the cluster groups are added.
RClusterGroupDescriptorIterable GetClusterGroupIterable() const
RColumnDescriptorIterable GetColumnIterable() const
bool operator==(const RNTupleDescriptor &other) const
std::uint64_t fOnDiskFooterSize
Like fOnDiskHeaderSize, contains both cluster summaries and page locations.
std::uint16_t fVersionMinor
Set by the descriptor builder when deserialized.
ROOT::DescriptorId_t FindClusterId(ROOT::NTupleSize_t entryIdx) const
std::vector< std::uint64_t > GetFeatureFlags() const
ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
std::unique_ptr< ROOT::RNTupleModel > CreateModel(const RCreateModelOptions &options=RCreateModelOptions()) const
Re-create the C++ model from the stored metadata.
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
std::unordered_map< ROOT::DescriptorId_t, RClusterDescriptor > fClusterDescriptors
Potentially a subset of all the available clusters.
std::size_t GetNClusters() const
ROOT::DescriptorId_t FindPhysicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
RExtraTypeInfoDescriptorIterable GetExtraTypeInfoIterable() const
const RHeaderExtension * GetHeaderExtension() const
Return header extension information; if the descriptor does not have a header extension,...
std::uint64_t fNClusters
Updated by the descriptor builder when the cluster groups are added.
std::uint64_t fOnDiskHeaderXxHash3
Set by the descriptor builder when deserialized.
const RClusterDescriptor & GetClusterDescriptor(ROOT::DescriptorId_t clusterId) const
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
std::string fName
The RNTuple name needs to be unique in a given storage location (file)
std::uint64_t fOnDiskHeaderSize
Set by the descriptor builder when deserialized.
RResult< void > DropClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId)
std::uint16_t fVersionMajor
Set by the descriptor builder when deserialized.
std::vector< ROOT::DescriptorId_t > fSortedClusterGroupIds
References cluster groups sorted by entry range and thus allows for binary search.
std::unordered_map< ROOT::DescriptorId_t, RColumnDescriptor > fColumnDescriptors
ROOT::DescriptorId_t FindLogicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
std::unordered_map< ROOT::DescriptorId_t, RFieldDescriptor > fFieldDescriptors
ROOT::NTupleSize_t GetNElements(ROOT::DescriptorId_t physicalColumnId) const
RResult< void > AddClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId, std::vector< RClusterDescriptor > &clusterDescs)
Methods to load and drop cluster group details (cluster IDs and page locations)
std::uint16_t fVersionPatch
Set by the descriptor builder when deserialized.
std::string fDescription
Free text from the user.
RFieldDescriptorIterable GetTopLevelFields() const
std::uint16_t fVersionEpoch
Set by the descriptor builder when deserialized.
std::vector< RExtraTypeInfoDescriptor > fExtraTypeInfoDescriptors
RNTupleDescriptor Clone() const
std::string GetQualifiedFieldName(ROOT::DescriptorId_t fieldId) const
Walks up the parents of the field ID and returns a field name of the form a.b.c.d In case of invalid ...
RClusterDescriptorIterable GetClusterIterable() const
RNTupleDescriptor CloneSchema() const
Creates a descriptor containing only the schema information about this RNTuple, i....
std::uint64_t fGeneration
The generation of the descriptor.
ROOT::DescriptorId_t FindPrevClusterId(ROOT::DescriptorId_t clusterId) const
std::unique_ptr< RHeaderExtension > fHeaderExtension
Generic information about the physical location of data.
static std::unique_ptr< RNTupleModel > Create()
static std::unique_ptr< RNTupleModel > CreateBare()
Creates a "bare model", i.e. an RNTupleModel with no default entry.
static constexpr std::uint16_t kVersionPatch
Definition RNTuple.hxx:81
static constexpr std::uint16_t kVersionMajor
Definition RNTuple.hxx:79
static constexpr std::uint16_t kVersionEpoch
Definition RNTuple.hxx:78
static constexpr std::uint16_t kVersionMinor
Definition RNTuple.hxx:80
const_iterator begin() const
const_iterator end() const
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:198
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2973
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
ROOT::RResult< std::unique_ptr< ROOT::RFieldBase > > CallFieldBaseCreate(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
std::vector< ROOT::Internal::RNTupleClusterBoundaries > GetClusterBoundaries(const RNTupleDescriptor &desc)
Return the cluster boundaries for each cluster in this RNTuple.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr NTupleSize_t kInvalidNTupleIndex
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
Additional information about a page in an in-memory RPageRange.
Information about a single page in the context of a cluster's page range.
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2339