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
172////////////////////////////////////////////////////////////////////////////////
173
175{
176 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
177 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
178 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
179 fValueRange == other.fValueRange;
180}
181
183{
184 RColumnDescriptor clone;
185 clone.fLogicalColumnId = fLogicalColumnId;
186 clone.fPhysicalColumnId = fPhysicalColumnId;
187 clone.fBitsOnStorage = fBitsOnStorage;
188 clone.fType = fType;
189 clone.fFieldId = fFieldId;
190 clone.fIndex = fIndex;
191 clone.fFirstElementIndex = fFirstElementIndex;
192 clone.fRepresentationIndex = fRepresentationIndex;
193 clone.fValueRange = fValueRange;
194 return clone;
195}
196
197////////////////////////////////////////////////////////////////////////////////
198
201{
202 const auto N = fCumulativeNElements.size();
203 R__ASSERT(N > 0);
204 R__ASSERT(N == fPageInfos.size());
205
206 std::size_t left = 0;
207 std::size_t right = N - 1;
208 std::size_t midpoint = N;
209 while (left <= right) {
210 midpoint = (left + right) / 2;
211 if (fCumulativeNElements[midpoint] <= idxInCluster) {
212 left = midpoint + 1;
213 continue;
214 }
215
216 if ((midpoint == 0) || (fCumulativeNElements[midpoint - 1] <= idxInCluster))
217 break;
218
219 right = midpoint - 1;
220 }
222
223 auto pageInfo = fPageInfos[midpoint];
224 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : fCumulativeNElements[midpoint - 1];
226 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
228}
229
230std::size_t
233 std::size_t pageSize)
234{
235 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
236 R__ASSERT(!columnRange.IsSuppressed());
237
238 const auto nElements =
239 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
240 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
241 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
242
244 return 0U;
245 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
246
247 std::vector<RPageInfo> pageInfos;
248 // Synthesize new `RPageInfo`s as needed
249 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
253 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
256 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
257 pageInfo.SetLocator(locator);
258 pageInfos.emplace_back(pageInfo);
259 nRemainingElements -= pageInfo.GetNElements();
260 }
261
262 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
263 std::make_move_iterator(fPageInfos.end()));
264 std::swap(fPageInfos, pageInfos);
266}
267
269{
270 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
271 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
272}
273
275{
276 std::uint64_t nbytes = 0;
277 for (const auto &pr : fPageRanges) {
278 for (const auto &pi : pr.second.GetPageInfos()) {
279 nbytes += pi.GetLocator().GetNBytesOnStorage();
280 }
281 }
282 return nbytes;
283}
284
286{
287 RClusterDescriptor clone;
288 clone.fClusterId = fClusterId;
289 clone.fFirstEntryIndex = fFirstEntryIndex;
290 clone.fNEntries = fNEntries;
291 clone.fColumnRanges = fColumnRanges;
292 for (const auto &d : fPageRanges)
293 clone.fPageRanges.emplace(d.first, d.second.Clone());
294 return clone;
295}
296
297////////////////////////////////////////////////////////////////////////////////
298
300{
301 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
302}
303
305{
307 clone.fContentId = fContentId;
308 clone.fTypeVersion = fTypeVersion;
309 clone.fTypeName = fTypeName;
310 clone.fContent = fContent;
311 return clone;
312}
313
314////////////////////////////////////////////////////////////////////////////////
315
317{
318 // clang-format off
319 return fName == other.fName &&
320 fDescription == other.fDescription &&
321 fNEntries == other.fNEntries &&
322 fGeneration == other.fGeneration &&
323 fFieldZeroId == other.fFieldZeroId &&
324 fFieldDescriptors == other.fFieldDescriptors &&
325 fColumnDescriptors == other.fColumnDescriptors &&
326 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
327 fClusterDescriptors == other.fClusterDescriptors;
328 // clang-format on
329}
330
332{
334 for (const auto &cd : fClusterDescriptors) {
335 if (!cd.second.ContainsColumn(physicalColumnId))
336 continue;
337 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
338 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
339 }
340 return result;
341}
342
343////////////////////////////////////////////////////////////////////////////////
344/// Return the cluster boundaries for each cluster in this RNTuple.
345std::vector<ROOT::Internal::RNTupleClusterBoundaries>
347{
348 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
349 boundaries.reserve(desc.GetNClusters());
350 auto clusterId = desc.FindClusterId(0, 0);
352 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
353 R__ASSERT(clusterDesc.GetNEntries() > 0);
354 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
355 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
357 }
358 return boundaries;
359}
360
363{
364 std::string leafName(fieldName);
365 auto posDot = leafName.find_last_of('.');
366 if (posDot != std::string::npos) {
367 auto parentName = leafName.substr(0, posDot);
368 leafName = leafName.substr(posDot + 1);
369 parentId = FindFieldId(parentName, parentId);
370 }
371 auto itrFieldDesc = fFieldDescriptors.find(parentId);
372 if (itrFieldDesc == fFieldDescriptors.end())
374 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
375 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
376 return linkId;
377 }
379}
380
382{
384 return "";
385
386 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
387 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
388 if (prefix.empty())
389 return fieldDescriptor.GetFieldName();
390 return prefix + "." + fieldDescriptor.GetFieldName();
391}
392
394{
395 std::string typeName = fieldDesc.GetTypeName();
396
397 // ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
398 R__ASSERT(fVersionEpoch == 1);
399 if (fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1) {
400 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
401 }
402
403 return typeName;
404}
405
407{
408 return FindFieldId(fieldName, GetFieldZeroId());
409}
410
412 std::uint32_t columnIndex,
413 std::uint16_t representationIndex) const
414{
415 auto itr = fFieldDescriptors.find(fieldId);
416 if (itr == fFieldDescriptors.cend())
418 if (columnIndex >= itr->second.GetColumnCardinality())
420 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
421 if (itr->second.GetLogicalColumnIds().size() <= idx)
423 return itr->second.GetLogicalColumnIds()[idx];
424}
425
427 std::uint32_t columnIndex,
428 std::uint16_t representationIndex) const
429{
430 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
433 return GetColumnDescriptor(logicalId).GetPhysicalId();
434}
435
438{
439 if (GetNClusterGroups() == 0)
441
442 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
443
444 std::size_t cgLeft = 0;
445 std::size_t cgRight = GetNClusterGroups() - 1;
446 while (cgLeft <= cgRight) {
447 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
448 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
449 R__ASSERT(!clusterIds.empty());
450
451 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
452 // this may happen if the RNTuple has an empty schema
453 if (!clusterDesc.ContainsColumn(physicalColumnId))
455
456 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
458 // Look into the lower half of cluster groups
460 cgRight = cgMidpoint - 1;
461 continue;
462 }
463
464 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
465 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
466 // Look into the upper half of cluster groups
467 cgLeft = cgMidpoint + 1;
468 continue;
469 }
470
471 // Binary search in the current cluster group; since we already checked the element range boundaries,
472 // the element must be in that cluster group.
473 std::size_t clusterLeft = 0;
474 std::size_t clusterRight = clusterIds.size() - 1;
475 while (clusterLeft <= clusterRight) {
476 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
478 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
479
480 if (columnRange.Contains(index))
481 return clusterId;
482
483 if (columnRange.GetFirstElementIndex() > index) {
486 continue;
487 }
488
489 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
491 continue;
492 }
493 }
494 R__ASSERT(false);
495 }
497}
498
500{
501 if (GetNClusterGroups() == 0)
503
504 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
505
506 std::size_t cgLeft = 0;
507 std::size_t cgRight = GetNClusterGroups() - 1;
508 while (cgLeft <= cgRight) {
509 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
510 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
511
512 if (cgDesc.GetMinEntry() > entryIdx) {
514 cgRight = cgMidpoint - 1;
515 continue;
516 }
517
518 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
519 cgLeft = cgMidpoint + 1;
520 continue;
521 }
522
523 // Binary search in the current cluster group; since we already checked the element range boundaries,
524 // the element must be in that cluster group.
525 const auto &clusterIds = cgDesc.GetClusterIds();
526 R__ASSERT(!clusterIds.empty());
527 std::size_t clusterLeft = 0;
528 std::size_t clusterRight = clusterIds.size() - 1;
529 while (clusterLeft <= clusterRight) {
530 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
531 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
532
533 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
536 continue;
537 }
538
539 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
541 continue;
542 }
543
545 }
546 R__ASSERT(false);
547 }
549}
550
552{
553 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
554 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
555 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
556 // binary search code path remains tested.
557 const auto &clusterDesc = GetClusterDescriptor(clusterId);
558 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
559 return FindClusterId(firstEntryInNextCluster);
560}
561
563{
564 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
565 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
566 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
567 // binary search code path remains tested.
568 const auto &clusterDesc = GetClusterDescriptor(clusterId);
569 if (clusterDesc.GetFirstEntryIndex() == 0)
571 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
572}
573
574std::vector<ROOT::DescriptorId_t>
576{
577 auto fieldZeroId = desc.GetFieldZeroId();
578
579 std::vector<ROOT::DescriptorId_t> fields;
580 for (const auto fieldId : fFieldIdsOrder) {
581 if (desc.GetFieldDescriptor(fieldId).GetParentId() == fieldZeroId)
582 fields.emplace_back(fieldId);
583 }
584 return fields;
585}
586
592
594 : fNTuple(ntuple)
595{
596 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
597
598 while (!fieldIdQueue.empty()) {
599 auto currFieldId = fieldIdQueue.front();
600 fieldIdQueue.pop_front();
601
602 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
603 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
604
605 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
606 auto fieldId = field.GetId();
607 fieldIdQueue.push_back(fieldId);
608 }
609 }
610}
611
612std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
613{
614 std::vector<std::uint64_t> result;
615 unsigned int base = 0;
616 std::uint64_t flags = 0;
617 for (auto f : fFeatureFlags) {
618 if ((f > 0) && ((f % 64) == 0))
619 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
620 while (f > base + 64) {
621 result.emplace_back(flags);
622 flags = 0;
623 base += 64;
624 }
625 f -= base;
626 flags |= 1 << f;
627 }
628 result.emplace_back(flags);
629 return result;
630}
631
633 std::vector<RClusterDescriptor> &clusterDescs)
634{
636 if (iter == fClusterGroupDescriptors.end())
637 return R__FAIL("invalid attempt to add details of unknown cluster group");
638 if (iter->second.HasClusterDetails())
639 return R__FAIL("invalid attempt to re-populate cluster group details");
640 if (iter->second.GetNClusters() != clusterDescs.size())
641 return R__FAIL("mismatch of number of clusters");
642
643 std::vector<ROOT::DescriptorId_t> clusterIds;
644 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
645 clusterIds.emplace_back(clusterDescs[i].GetId());
646 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
647 if (!success) {
648 return R__FAIL("invalid attempt to re-populate existing cluster");
649 }
650 }
652 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
653 });
655 cgBuilder.AddSortedClusters(clusterIds);
656 iter->second = cgBuilder.MoveDescriptor().Unwrap();
657 return RResult<void>::Success();
658}
659
661{
663 if (iter == fClusterGroupDescriptors.end())
664 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
665 if (!iter->second.HasClusterDetails())
666 return R__FAIL("invalid attempt to drop details of cluster group summary");
667
668 for (auto clusterId : iter->second.GetClusterIds())
670 iter->second = iter->second.CloneSummary();
671 return RResult<void>::Success();
672}
673
674std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
675{
676 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
677 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
678 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
679 for (const auto &colDesc : GetColumnIterable()) {
681 auto fieldId = colDesc.GetFieldId();
682 while (1) {
683 const auto &field = GetFieldDescriptor(fieldId);
684 if (field.GetParentId() == GetFieldZeroId())
685 break;
686 fieldId = field.GetParentId();
687 }
688 invalidFields.insert(fieldId);
689
690 // No need to look for all invalid fields if we're gonna error out anyway
691 if (!options.GetForwardCompatible())
692 break;
693 }
694 }
695
696 if (!options.GetForwardCompatible() && !invalidFields.empty())
698 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
699 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
700
701 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
702 fieldZero->SetOnDiskId(GetFieldZeroId());
703 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
704 : RNTupleModel::Create(std::move(fieldZero));
706 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
707 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
708 for (const auto &topDesc : GetTopLevelFields()) {
709 if (invalidFields.count(topDesc.GetId()) > 0) {
710 // Field contains invalid columns: skip it
711 continue;
712 }
713
714 auto field = topDesc.CreateField(*this, createFieldOpts);
715
716 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
717 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
718 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
719 // in the first place, so we don't need to check for that flag again).
720 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
721 const auto &invalid = static_cast<const RInvalidField &>(*field);
722 const auto cat = invalid.GetCategory();
724 if (mustThrow)
725 throw invalid.GetError();
726
727 // Not a hard error: skip the field and go on.
728 continue;
729 }
730
731 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
732 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
733 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
734 });
735 } else {
736 model->AddField(std::move(field));
737 }
738 }
739 model->Freeze();
740 return model;
741}
742
744{
745 RNTupleDescriptor clone;
746 clone.fName = fName;
751 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
752 // and therefore not represent the actual sources's header.
753 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
754
755 for (const auto &d : fFieldDescriptors)
756 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
757 for (const auto &d : fColumnDescriptors)
758 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
759
760 for (const auto &d : fExtraTypeInfoDescriptors)
761 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
763 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
764
765 return clone;
766}
767
769{
771
776
780 clone.fNEntries = fNEntries;
781 clone.fNClusters = fNClusters;
782 clone.fGeneration = fGeneration;
783 for (const auto &d : fClusterGroupDescriptors)
784 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
786 for (const auto &d : fClusterDescriptors)
787 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
788 return clone;
789}
790
791////////////////////////////////////////////////////////////////////////////////
792
794{
795 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
796 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
797}
798
800{
802 clone.fClusterGroupId = fClusterGroupId;
803 clone.fPageListLocator = fPageListLocator;
804 clone.fPageListLength = fPageListLength;
805 clone.fMinEntry = fMinEntry;
806 clone.fEntrySpan = fEntrySpan;
807 clone.fNClusters = fNClusters;
808 return clone;
809}
810
812{
813 RClusterGroupDescriptor clone = CloneSummary();
814 clone.fClusterIds = fClusterIds;
815 return clone;
816}
817
818////////////////////////////////////////////////////////////////////////////////
819
822 std::uint64_t firstElementIndex,
823 std::uint32_t compressionSettings,
825{
826 if (physicalId != pageRange.fPhysicalColumnId)
827 return R__FAIL("column ID mismatch");
828 if (fCluster.fColumnRanges.count(physicalId) > 0)
829 return R__FAIL("column ID conflict");
831 for (const auto &pi : pageRange.fPageInfos) {
832 columnRange.IncrementNElements(pi.GetNElements());
833 }
834 fCluster.fPageRanges[physicalId] = pageRange.Clone();
835 fCluster.fColumnRanges[physicalId] = columnRange;
836 return RResult<void>::Success();
837}
838
841{
842 if (fCluster.fColumnRanges.count(physicalId) > 0)
843 return R__FAIL("column ID conflict");
844
846 columnRange.SetPhysicalColumnId(physicalId);
847 columnRange.SetIsSuppressed(true);
848 fCluster.fColumnRanges[physicalId] = columnRange;
849 return RResult<void>::Success();
850}
851
854{
855 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
856 if (!columnRange.IsSuppressed())
857 continue;
858 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
859
860 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
861 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
862 // We expect only few columns and column representations per field, so we do a linear search
863 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
865 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
866 continue;
867 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
868 continue;
869
870 // Found corresponding column of a different column representation
871 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
872 if (otherColumnRange.IsSuppressed())
873 continue;
874
875 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
876 columnRange.SetNElements(otherColumnRange.GetNElements());
877 break;
878 }
879
880 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
881 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
882 std::to_string(columnRange.GetPhysicalColumnId()) +
883 ", cluster ID: " + std::to_string(fCluster.GetId()));
884 }
885 }
886 return RResult<void>::Success();
887}
888
891{
892 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
893 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
894 /// parent field in the hierarchy.
896 const auto &visitField, const auto &enterSubtree) -> void {
898 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
899 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
901 }
902 };
903
904 // Extended columns can only be part of the header extension
905 if (!desc.GetHeaderExtension())
906 return *this;
907
908 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
909 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
910 for (const auto &topLevelField : desc.GetTopLevelFields()) {
912 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
913 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
914 for (const auto &c : desc.GetColumnIterable(fieldId)) {
915 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
916 auto &columnRange = fCluster.fColumnRanges[physicalId];
917
918 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
919 // extension won't have on-disk metadata for the clusters that were already committed before the model
920 // was extended. Therefore, these need to be synthetically initialized upon reading.
921 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
922 columnRange.SetPhysicalColumnId(physicalId);
923 columnRange.SetFirstElementIndex(0);
924 columnRange.SetNElements(0);
925 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
926 }
927 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
928 // number of elements should have been if the column was not deferred; fix those and let
929 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
930 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
931 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
932 // field zero excluding subfields of collection and variant fields.
933 if (c.IsDeferredColumn()) {
934 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
935 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
936 if (!columnRange.IsSuppressed()) {
937 auto &pageRange = fCluster.fPageRanges[physicalId];
938 pageRange.fPhysicalColumnId = physicalId;
939 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
940 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
941 }
942 } else if (!columnRange.IsSuppressed()) {
943 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
944 }
945 }
946 },
948 }
949 return *this;
950}
951
953{
954 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
955 return R__FAIL("unset cluster ID");
956 if (fCluster.fNEntries == 0)
957 return R__FAIL("empty cluster");
958 for (auto &pr : fCluster.fPageRanges) {
959 if (fCluster.fColumnRanges.count(pr.first) == 0) {
960 return R__FAIL("missing column range");
961 }
962 pr.second.fCumulativeNElements.clear();
963 pr.second.fCumulativeNElements.reserve(pr.second.fPageInfos.size());
965 for (const auto &pi : pr.second.fPageInfos) {
966 sum += pi.GetNElements();
967 pr.second.fCumulativeNElements.emplace_back(sum);
968 }
969 }
971 std::swap(result, fCluster);
972 return result;
973}
974
975////////////////////////////////////////////////////////////////////////////////
976
979{
981 builder.ClusterGroupId(clusterGroupDesc.GetId())
982 .PageListLocator(clusterGroupDesc.GetPageListLocator())
983 .PageListLength(clusterGroupDesc.GetPageListLength())
984 .MinEntry(clusterGroupDesc.GetMinEntry())
985 .EntrySpan(clusterGroupDesc.GetEntrySpan())
986 .NClusters(clusterGroupDesc.GetNClusters());
987 return builder;
988}
989
991{
992 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
993 return R__FAIL("unset cluster group ID");
995 std::swap(result, fClusterGroup);
996 return result;
997}
998
999////////////////////////////////////////////////////////////////////////////////
1000
1002{
1003 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1004 throw RException(R__FAIL("invalid extra type info content id"));
1006 std::swap(result, fExtraTypeInfo);
1007 return result;
1008}
1009
1010////////////////////////////////////////////////////////////////////////////////
1011
1013{
1014 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1015 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1016 return RResult<void>::Success();
1017}
1018
1020{
1021 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1022 return R__FAIL("unset or unsupported RNTuple epoch version");
1023 }
1024
1025 // Reuse field name validity check
1026 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1027 if (!validName) {
1029 }
1030
1031 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1032 // parent not properly set?
1033 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1034 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1035 }
1036
1037 // Same number of columns in every column representation?
1038 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1039 if (columnCardinality == 0)
1040 continue;
1041
1042 // In AddColumn, we already checked that all but the last representation are complete.
1043 // Check that the last column representation is complete, i.e. has all columns.
1044 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1045 const auto nColumns = logicalColumnIds.size();
1046 // If we have only a single column representation, the following condition is true by construction
1047 if ((nColumns + 1) == columnCardinality)
1048 continue;
1049
1050 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1051 if (lastColumn.GetIndex() + 1 != columnCardinality)
1052 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1053 }
1054
1055 return RResult<void>::Success();
1056}
1057
1059{
1060 EnsureValidDescriptor().ThrowOnError();
1061 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1062 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1063 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1064 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1066 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1067 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1068 });
1070 std::swap(result, fDescriptor);
1071 return result;
1072}
1073
1075 std::uint16_t versionMinor, std::uint16_t versionPatch)
1076{
1078 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1079 }
1080 fDescriptor.fVersionEpoch = versionEpoch;
1081 fDescriptor.fVersionMajor = versionMajor;
1082 fDescriptor.fVersionMinor = versionMinor;
1083 fDescriptor.fVersionPatch = versionPatch;
1084}
1085
1087{
1088 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1089 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1090 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1091 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1092}
1093
1095 const std::string_view description)
1096{
1097 fDescriptor.fName = std::string(name);
1098 fDescriptor.fDescription = std::string(description);
1099}
1100
1102{
1103 if (flag % 64 == 0)
1104 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1105 fDescriptor.fFeatureFlags.insert(flag);
1106}
1107
1109{
1110 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1111 return R__FAIL("invalid logical column id");
1112 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1113 return R__FAIL("invalid physical column id");
1114 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1115 return R__FAIL("invalid field id, dangling column");
1116
1117 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1118 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1119 // as we have no idea what the valid range is.
1120 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1121 // we try to read the actual data contained in it.
1122 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1123 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1124 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1125 return R__FAIL("invalid column bit width");
1126 }
1127
1128 return fColumn.Clone();
1129}
1130
1138
1141{
1143 fieldDesc.FieldVersion(field.GetFieldVersion())
1144 .TypeVersion(field.GetTypeVersion())
1145 .FieldName(field.GetFieldName())
1146 .FieldDescription(field.GetDescription())
1147 .TypeName(field.GetTypeName())
1148 .TypeAlias(field.GetTypeAlias())
1149 .Structure(field.GetStructure())
1150 .NRepetitions(field.GetNRepetitions());
1152 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1153 return fieldDesc;
1154}
1155
1157{
1158 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1159 return R__FAIL("invalid field id");
1160 }
1161 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1162 return R__FAIL("invalid field structure");
1163 }
1164 // FieldZero is usually named "" and would be a false positive here
1165 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1166 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1167 if (!validName) {
1169 }
1170 if (fField.GetFieldName().empty()) {
1171 return R__FAIL("name cannot be empty string \"\"");
1172 }
1173 }
1174 return fField.Clone();
1175}
1176
1178{
1179 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1180 if (fDescriptor.fHeaderExtension)
1181 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1182 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1183 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1184 }
1185}
1186
1189{
1191 if (!(fieldExists = EnsureFieldExists(fieldId)))
1193 if (!(fieldExists = EnsureFieldExists(linkId)))
1194 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1195
1196 if (linkId == fDescriptor.GetFieldZeroId()) {
1197 return R__FAIL("cannot make FieldZero a child field");
1198 }
1199 // fail if field already has another valid parent
1200 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1202 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1203 }
1204 if (fieldId == linkId) {
1205 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1206 }
1207 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1208 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1209 return RResult<void>::Success();
1210}
1211
1214{
1216 if (!(fieldExists = EnsureFieldExists(sourceId)))
1218 if (!(fieldExists = EnsureFieldExists(targetId)))
1219 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1220
1221 if (targetId == fDescriptor.GetFieldZeroId()) {
1222 return R__FAIL("cannot make FieldZero a projected field");
1223 }
1224 if (sourceId == targetId) {
1225 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1226 }
1227 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1228 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1229 }
1230 // fail if target field already has another valid projection source
1231 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1232 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1233 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1234 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1235 }
1236 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1237 return RResult<void>::Success();
1238}
1239
1241{
1242 const auto fieldId = columnDesc.GetFieldId();
1243 const auto columnIndex = columnDesc.GetIndex();
1244 const auto representationIndex = columnDesc.GetRepresentationIndex();
1245
1246 auto fieldExists = EnsureFieldExists(fieldId);
1247 if (!fieldExists) {
1249 }
1250 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1251
1252 if (columnDesc.IsAliasColumn()) {
1253 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1254 return R__FAIL("alias column type mismatch");
1255 }
1256 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1257 return R__FAIL("column index clash");
1258 }
1259 if (columnIndex > 0) {
1260 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1261 return R__FAIL("out of bounds column index");
1262 }
1263 if (representationIndex > 0) {
1264 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1265 return R__FAIL("out of bounds representation index");
1266 }
1267 if (columnIndex == 0) {
1268 assert(fieldDesc.fColumnCardinality > 0);
1269 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1271 return R__FAIL("incomplete column representations");
1272 }
1273 } else {
1274 if (columnIndex >= fieldDesc.fColumnCardinality)
1275 return R__FAIL("irregular column representations");
1276 }
1277 } else {
1278 // This will set the column cardinality to the number of columns of the first representation
1279 fieldDesc.fColumnCardinality = columnIndex + 1;
1280 }
1281
1282 const auto logicalId = columnDesc.GetLogicalId();
1283 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1284
1285 if (!columnDesc.IsAliasColumn())
1286 fDescriptor.fNPhysicalColumns++;
1287 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1288 if (fDescriptor.fHeaderExtension)
1289 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1290
1291 return RResult<void>::Success();
1292}
1293
1295{
1296 const auto id = clusterGroup.GetId();
1297 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1298 return R__FAIL("cluster group id clash");
1299 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1300 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1301 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1302 return RResult<void>::Success();
1303}
1304
1306{
1307 fDescriptor.fName = "";
1308 fDescriptor.fDescription = "";
1309 fDescriptor.fFieldDescriptors.clear();
1310 fDescriptor.fColumnDescriptors.clear();
1311 fDescriptor.fClusterDescriptors.clear();
1312 fDescriptor.fClusterGroupDescriptors.clear();
1313 fDescriptor.fHeaderExtension.reset();
1314}
1315
1320
1322{
1323 if (!fDescriptor.fHeaderExtension)
1324 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1325}
1326
1328{
1329 if (fDescriptor.GetNLogicalColumns() == 0)
1330 return;
1331 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1332
1333 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1334 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1335 R__ASSERT(c.IsAliasColumn());
1336 R__ASSERT(id == c.GetLogicalId());
1337 fDescriptor.fColumnDescriptors.erase(id);
1338 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1339 if (link == c.fLogicalColumnId) {
1340 link += offset;
1341 break;
1342 }
1343 }
1344 c.fLogicalColumnId += offset;
1345 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1346 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1347 }
1348}
1349
1351{
1352 auto clusterId = clusterDesc.GetId();
1353 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1354 return R__FAIL("cluster id clash");
1355 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1356 return RResult<void>::Success();
1357}
1358
1361{
1362 // Make sure we have no duplicates
1363 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1364 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1365 return R__FAIL("extra type info duplicates");
1366 }
1367 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1368 return RResult<void>::Success();
1369}
1370
1372{
1373 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1375 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1376 *it = std::move(extraTypeInfoDesc);
1377 else
1378 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1379}
1380
1382{
1384 const auto &desc = GetDescriptor();
1385
1386 std::function<void(const RFieldDescriptor &)> fnWalkFieldTree;
1388 if (fieldDesc.IsCustomClass()) {
1389 // Add streamer info for this class to streamerInfoMap
1390 auto cl = TClass::GetClass(fieldDesc.GetTypeName().c_str());
1391 if (!cl) {
1392 throw RException(R__FAIL(std::string("cannot get TClass for ") + fieldDesc.GetTypeName()));
1393 }
1394 auto streamerInfo = cl->GetStreamerInfo(fieldDesc.GetTypeVersion());
1395 if (!streamerInfo) {
1396 throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + fieldDesc.GetTypeName()));
1397 }
1399 }
1400
1401 // Recursively traverse sub fields
1402 for (const auto &subFieldDesc : desc.GetFieldIterable(fieldDesc)) {
1404 }
1405 };
1406
1407 fnWalkFieldTree(desc.GetFieldZero());
1408
1409 // Add the streamer info records from streamer fields: because of runtime polymorphism we may need to add additional
1410 // types not covered by the type names stored in the field headers
1411 for (const auto &extraTypeInfo : desc.GetExtraTypeInfoIterable()) {
1412 if (extraTypeInfo.GetContentId() != EExtraTypeInfoIds::kStreamerInfo)
1413 continue;
1414 // Ideally, we would avoid deserializing the streamer info records of the streamer fields that we just serialized.
1415 // However, this happens only once at the end of writing and only when streamer fields are used, so the
1416 // preference here is for code simplicity.
1418 }
1419
1420 return streamerInfoMap;
1421}
1422
1427
1433
1440
1443{
1444 return GetFieldIterable(GetFieldDescriptor(fieldId));
1445}
1446
1453
1455{
1456 return GetFieldIterable(GetFieldZeroId());
1457}
1458
1460 const std::function<bool(ROOT::DescriptorId_t, ROOT::DescriptorId_t)> &comparator) const
1461{
1462 return GetFieldIterable(GetFieldZeroId(), comparator);
1463}
1464
1469
1475
1481
1486
1491
#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.
RFieldDescriptorBuilder()=default
Make an empty dangling field descriptor.
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 Reset()
Clears so-far stored clusters, fields, and columns and return to a pristine RNTupleDescriptor.
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 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:72
@ 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:2974
struct void * fTypeName
Definition cppyy.h:9
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:2340