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>
27
28#include <algorithm>
29#include <cstdint>
30#include <deque>
31#include <functional>
32#include <iostream>
33#include <set>
34#include <utility>
35
37
39{
40 return fFieldId == other.fFieldId && fFieldVersion == other.fFieldVersion && fTypeVersion == other.fTypeVersion &&
41 fFieldName == other.fFieldName && fFieldDescription == other.fFieldDescription &&
42 fTypeName == other.fTypeName && fTypeAlias == other.fTypeAlias && fNRepetitions == other.fNRepetitions &&
43 fStructure == other.fStructure && fParentId == other.fParentId &&
44 fProjectionSourceId == other.fProjectionSourceId && fLinkIds == other.fLinkIds &&
45 fLogicalColumnIds == other.fLogicalColumnIds && other.fTypeChecksum == other.fTypeChecksum;
46}
47
49{
50 RFieldDescriptor clone;
51 clone.fFieldId = fFieldId;
52 clone.fFieldVersion = fFieldVersion;
53 clone.fTypeVersion = fTypeVersion;
54 clone.fFieldName = fFieldName;
55 clone.fFieldDescription = fFieldDescription;
56 clone.fTypeName = fTypeName;
57 clone.fTypeAlias = fTypeAlias;
58 clone.fNRepetitions = fNRepetitions;
59 clone.fStructure = fStructure;
60 clone.fParentId = fParentId;
61 clone.fProjectionSourceId = fProjectionSourceId;
62 clone.fLinkIds = fLinkIds;
63 clone.fColumnCardinality = fColumnCardinality;
64 clone.fLogicalColumnIds = fLogicalColumnIds;
65 clone.fTypeChecksum = fTypeChecksum;
66 return clone;
67}
68
69std::unique_ptr<ROOT::RFieldBase>
71{
72 if (GetStructure() == ROOT::ENTupleStructure::kStreamer) {
73 auto streamerField = std::make_unique<ROOT::RStreamerField>(GetFieldName(), GetTypeName());
74 streamerField->SetOnDiskId(fFieldId);
75 return streamerField;
76 }
77
78 // The structure may be unknown if the descriptor comes from a deserialized field with an unknown structural role.
79 // For forward compatibility, we allow this case and return an InvalidField.
80 if (GetStructure() == ROOT::ENTupleStructure::kUnknown) {
81 if (options.GetReturnInvalidOnError()) {
82 auto invalidField = std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), "",
84 invalidField->SetOnDiskId(fFieldId);
85 return invalidField;
86 } else {
87 throw RException(R__FAIL("unexpected on-disk field structure value for field \"" + GetFieldName() + "\""));
88 }
89 }
90
91 // Untyped records and collections
92 if (GetTypeName().empty()) {
93 switch (GetStructure()) {
95 std::vector<std::unique_ptr<ROOT::RFieldBase>> memberFields;
96 memberFields.reserve(fLinkIds.size());
97 for (auto id : fLinkIds) {
98 const auto &memberDesc = ntplDesc.GetFieldDescriptor(id);
99 auto field = memberDesc.CreateField(ntplDesc, options);
101 return field;
102 memberFields.emplace_back(std::move(field));
103 }
104 auto recordField = std::make_unique<ROOT::RRecordField>(GetFieldName(), std::move(memberFields));
105 recordField->SetOnDiskId(fFieldId);
106 return recordField;
107 }
109 if (fLinkIds.size() != 1) {
110 throw RException(R__FAIL("unsupported untyped collection for field \"" + GetFieldName() + "\""));
111 }
112 auto itemField = ntplDesc.GetFieldDescriptor(fLinkIds[0]).CreateField(ntplDesc, options);
114 return itemField;
115 auto collectionField = ROOT::RVectorField::CreateUntyped(GetFieldName(), std::move(itemField));
116 collectionField->SetOnDiskId(fFieldId);
117 return collectionField;
118 }
119 default: throw RException(R__FAIL("unsupported untyped field structure for field \"" + GetFieldName() + "\""));
120 }
121 }
122
123 try {
124 const auto &fieldName = GetFieldName();
125 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
126 // NOTE: Unwrap() here may throw an exception, hence the try block.
127 // If options.fReturnInvalidOnError is false we just rethrow it, otherwise we return an InvalidField wrapping the
128 // error.
129 auto field = ROOT::Internal::CallFieldBaseCreate(fieldName, typeName, options, &ntplDesc, fFieldId).Unwrap();
130 field->SetOnDiskId(fFieldId);
131
132 for (auto &subfield : *field) {
133 const auto subfieldId = ntplDesc.FindFieldId(subfield.GetFieldName(), subfield.GetParent()->GetOnDiskId());
134 subfield.SetOnDiskId(subfieldId);
136 auto &invalidField = static_cast<ROOT::RInvalidField &>(subfield);
137 // A subfield being invalid "infects" its entire ancestry.
138 return invalidField.Clone(fieldName);
139 }
140 }
141
142 return field;
143 } catch (const RException &ex) {
144 if (options.GetReturnInvalidOnError())
145 return std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), ex.GetError().GetReport(),
147 else
148 throw ex;
149 }
150}
151
153{
155 return false;
156
157 // Skip untyped structs
158 if (fTypeName.empty())
159 return false;
160
161 if (fStructure == ROOT::ENTupleStructure::kRecord) {
162 if (fTypeName.compare(0, 10, "std::pair<") == 0)
163 return false;
164 if (fTypeName.compare(0, 11, "std::tuple<") == 0)
165 return false;
166 }
167
168 return true;
169}
170
172{
173 if (fStructure != ROOT::ENTupleStructure::kPlain)
174 return false;
175 if (fTypeName.rfind("std::", 0) == 0)
176 return false;
177
178 auto subFieldId = desc.FindFieldId("_0", fFieldId);
180 return false;
181
182 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
183 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
184 "std::int64_t", "std::uint64_t"};
185 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
186 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
187}
188
190{
191 if (fStructure != ROOT::ENTupleStructure::kPlain)
192 return false;
193 return (fTypeName.rfind("std::atomic<", 0) == 0);
194}
195
196////////////////////////////////////////////////////////////////////////////////
197
199{
200 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
201 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
202 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
203 fValueRange == other.fValueRange;
204}
205
207{
208 RColumnDescriptor clone;
209 clone.fLogicalColumnId = fLogicalColumnId;
210 clone.fPhysicalColumnId = fPhysicalColumnId;
211 clone.fBitsOnStorage = fBitsOnStorage;
212 clone.fType = fType;
213 clone.fFieldId = fFieldId;
214 clone.fIndex = fIndex;
215 clone.fFirstElementIndex = fFirstElementIndex;
216 clone.fRepresentationIndex = fRepresentationIndex;
217 clone.fValueRange = fValueRange;
218 return clone;
219}
220
221////////////////////////////////////////////////////////////////////////////////
222
225{
226 const auto N = fCumulativeNElements.size();
227 R__ASSERT(N > 0);
228 R__ASSERT(N == fPageInfos.size());
229
230 std::size_t left = 0;
231 std::size_t right = N - 1;
232 std::size_t midpoint = N;
233 while (left <= right) {
234 midpoint = (left + right) / 2;
235 if (fCumulativeNElements[midpoint] <= idxInCluster) {
236 left = midpoint + 1;
237 continue;
238 }
239
240 if ((midpoint == 0) || (fCumulativeNElements[midpoint - 1] <= idxInCluster))
241 break;
242
243 right = midpoint - 1;
244 }
246
247 auto pageInfo = fPageInfos[midpoint];
248 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : fCumulativeNElements[midpoint - 1];
250 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
252}
253
254std::size_t
257 std::size_t pageSize)
258{
259 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
260 R__ASSERT(!columnRange.IsSuppressed());
261
262 const auto nElements =
263 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
264 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
265 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
266
268 return 0U;
269 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
270
271 std::vector<RPageInfo> pageInfos;
272 // Synthesize new `RPageInfo`s as needed
273 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
277 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
280 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
281 pageInfo.SetLocator(locator);
282 pageInfos.emplace_back(pageInfo);
283 nRemainingElements -= pageInfo.GetNElements();
284 }
285
286 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
287 std::make_move_iterator(fPageInfos.end()));
288 std::swap(fPageInfos, pageInfos);
290}
291
293{
294 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
295 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
296}
297
299{
300 std::uint64_t nbytes = 0;
301 for (const auto &pr : fPageRanges) {
302 for (const auto &pi : pr.second.GetPageInfos()) {
303 nbytes += pi.GetLocator().GetNBytesOnStorage();
304 }
305 }
306 return nbytes;
307}
308
310{
311 RClusterDescriptor clone;
312 clone.fClusterId = fClusterId;
313 clone.fFirstEntryIndex = fFirstEntryIndex;
314 clone.fNEntries = fNEntries;
315 clone.fColumnRanges = fColumnRanges;
316 for (const auto &d : fPageRanges)
317 clone.fPageRanges.emplace(d.first, d.second.Clone());
318 return clone;
319}
320
321////////////////////////////////////////////////////////////////////////////////
322
324{
325 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
326}
327
329{
331 clone.fContentId = fContentId;
332 clone.fTypeVersion = fTypeVersion;
333 clone.fTypeName = fTypeName;
334 clone.fContent = fContent;
335 return clone;
336}
337
338////////////////////////////////////////////////////////////////////////////////
339
341{
342 // clang-format off
343 return fName == other.fName &&
344 fDescription == other.fDescription &&
345 fNEntries == other.fNEntries &&
346 fGeneration == other.fGeneration &&
347 fFieldZeroId == other.fFieldZeroId &&
348 fFieldDescriptors == other.fFieldDescriptors &&
349 fColumnDescriptors == other.fColumnDescriptors &&
350 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
351 fClusterDescriptors == other.fClusterDescriptors;
352 // clang-format on
353}
354
356{
358 for (const auto &cd : fClusterDescriptors) {
359 if (!cd.second.ContainsColumn(physicalColumnId))
360 continue;
361 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
362 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
363 }
364 return result;
365}
366
367////////////////////////////////////////////////////////////////////////////////
368/// Return the cluster boundaries for each cluster in this RNTuple.
369std::vector<ROOT::Internal::RNTupleClusterBoundaries>
371{
372 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
373 boundaries.reserve(desc.GetNClusters());
374 auto clusterId = desc.FindClusterId(0, 0);
376 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
377 R__ASSERT(clusterDesc.GetNEntries() > 0);
378 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
379 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
381 }
382 return boundaries;
383}
384
387{
388 std::string leafName(fieldName);
389 auto posDot = leafName.find_last_of('.');
390 if (posDot != std::string::npos) {
391 auto parentName = leafName.substr(0, posDot);
392 leafName = leafName.substr(posDot + 1);
393 parentId = FindFieldId(parentName, parentId);
394 }
395 auto itrFieldDesc = fFieldDescriptors.find(parentId);
396 if (itrFieldDesc == fFieldDescriptors.end())
398 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
399 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
400 return linkId;
401 }
403}
404
406{
408 return "";
409
410 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
411 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
412 if (prefix.empty())
413 return fieldDescriptor.GetFieldName();
414 return prefix + "." + fieldDescriptor.GetFieldName();
415}
416
418{
419 std::string typeName = fieldDesc.GetTypeName();
420
421 // ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
422 R__ASSERT(fVersionEpoch == 1);
423 if (fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1) {
424 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
425 }
426
427 return typeName;
428}
429
431{
432 return FindFieldId(fieldName, GetFieldZeroId());
433}
434
436 std::uint32_t columnIndex,
437 std::uint16_t representationIndex) const
438{
439 auto itr = fFieldDescriptors.find(fieldId);
440 if (itr == fFieldDescriptors.cend())
442 if (columnIndex >= itr->second.GetColumnCardinality())
444 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
445 if (itr->second.GetLogicalColumnIds().size() <= idx)
447 return itr->second.GetLogicalColumnIds()[idx];
448}
449
451 std::uint32_t columnIndex,
452 std::uint16_t representationIndex) const
453{
454 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
457 return GetColumnDescriptor(logicalId).GetPhysicalId();
458}
459
462{
463 if (GetNClusterGroups() == 0)
465
466 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
467
468 std::size_t cgLeft = 0;
469 std::size_t cgRight = GetNClusterGroups() - 1;
470 while (cgLeft <= cgRight) {
471 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
472 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
473 R__ASSERT(!clusterIds.empty());
474
475 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
476 // this may happen if the RNTuple has an empty schema
477 if (!clusterDesc.ContainsColumn(physicalColumnId))
479
480 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
482 // Look into the lower half of cluster groups
484 cgRight = cgMidpoint - 1;
485 continue;
486 }
487
488 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
489 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
490 // Look into the upper half of cluster groups
491 cgLeft = cgMidpoint + 1;
492 continue;
493 }
494
495 // Binary search in the current cluster group; since we already checked the element range boundaries,
496 // the element must be in that cluster group.
497 std::size_t clusterLeft = 0;
498 std::size_t clusterRight = clusterIds.size() - 1;
499 while (clusterLeft <= clusterRight) {
500 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
502 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
503
504 if (columnRange.Contains(index))
505 return clusterId;
506
507 if (columnRange.GetFirstElementIndex() > index) {
510 continue;
511 }
512
513 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
515 continue;
516 }
517 }
518 R__ASSERT(false);
519 }
521}
522
524{
525 if (GetNClusterGroups() == 0)
527
528 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
529
530 std::size_t cgLeft = 0;
531 std::size_t cgRight = GetNClusterGroups() - 1;
532 while (cgLeft <= cgRight) {
533 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
534 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
535
536 if (cgDesc.GetMinEntry() > entryIdx) {
538 cgRight = cgMidpoint - 1;
539 continue;
540 }
541
542 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
543 cgLeft = cgMidpoint + 1;
544 continue;
545 }
546
547 // Binary search in the current cluster group; since we already checked the element range boundaries,
548 // the element must be in that cluster group.
549 const auto &clusterIds = cgDesc.GetClusterIds();
550 R__ASSERT(!clusterIds.empty());
551 std::size_t clusterLeft = 0;
552 std::size_t clusterRight = clusterIds.size() - 1;
553 while (clusterLeft <= clusterRight) {
554 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
555 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
556
557 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
560 continue;
561 }
562
563 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
565 continue;
566 }
567
569 }
570 R__ASSERT(false);
571 }
573}
574
576{
577 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
578 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
579 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
580 // binary search code path remains tested.
581 const auto &clusterDesc = GetClusterDescriptor(clusterId);
582 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
583 return FindClusterId(firstEntryInNextCluster);
584}
585
587{
588 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
589 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
590 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
591 // binary search code path remains tested.
592 const auto &clusterDesc = GetClusterDescriptor(clusterId);
593 if (clusterDesc.GetFirstEntryIndex() == 0)
595 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
596}
597
598std::vector<ROOT::DescriptorId_t>
600{
601 auto fieldZeroId = desc.GetFieldZeroId();
602
603 std::vector<ROOT::DescriptorId_t> fields;
604 for (const auto fieldId : fFieldIdsOrder) {
605 if (desc.GetFieldDescriptor(fieldId).GetParentId() == fieldZeroId)
606 fields.emplace_back(fieldId);
607 }
608 return fields;
609}
610
616
618 : fNTuple(ntuple)
619{
620 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
621
622 while (!fieldIdQueue.empty()) {
623 auto currFieldId = fieldIdQueue.front();
624 fieldIdQueue.pop_front();
625
626 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
627 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
628
629 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
630 auto fieldId = field.GetId();
631 fieldIdQueue.push_back(fieldId);
632 }
633 }
634}
635
636std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
637{
638 std::vector<std::uint64_t> result;
639 unsigned int base = 0;
640 std::uint64_t flags = 0;
641 for (auto f : fFeatureFlags) {
642 if ((f > 0) && ((f % 64) == 0))
643 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
644 while (f > base + 64) {
645 result.emplace_back(flags);
646 flags = 0;
647 base += 64;
648 }
649 f -= base;
650 flags |= 1 << f;
651 }
652 result.emplace_back(flags);
653 return result;
654}
655
657 std::vector<RClusterDescriptor> &clusterDescs)
658{
660 if (iter == fClusterGroupDescriptors.end())
661 return R__FAIL("invalid attempt to add details of unknown cluster group");
662 if (iter->second.HasClusterDetails())
663 return R__FAIL("invalid attempt to re-populate cluster group details");
664 if (iter->second.GetNClusters() != clusterDescs.size())
665 return R__FAIL("mismatch of number of clusters");
666
667 std::vector<ROOT::DescriptorId_t> clusterIds;
668 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
669 clusterIds.emplace_back(clusterDescs[i].GetId());
670 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
671 if (!success) {
672 return R__FAIL("invalid attempt to re-populate existing cluster");
673 }
674 }
676 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
677 });
679 cgBuilder.AddSortedClusters(clusterIds);
680 iter->second = cgBuilder.MoveDescriptor().Unwrap();
681 return RResult<void>::Success();
682}
683
685{
687 if (iter == fClusterGroupDescriptors.end())
688 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
689 if (!iter->second.HasClusterDetails())
690 return R__FAIL("invalid attempt to drop details of cluster group summary");
691
692 for (auto clusterId : iter->second.GetClusterIds())
694 iter->second = iter->second.CloneSummary();
695 return RResult<void>::Success();
696}
697
698std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
699{
700 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
701 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
702 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
703 for (const auto &colDesc : GetColumnIterable()) {
705 auto fieldId = colDesc.GetFieldId();
706 while (1) {
707 const auto &field = GetFieldDescriptor(fieldId);
708 if (field.GetParentId() == GetFieldZeroId())
709 break;
710 fieldId = field.GetParentId();
711 }
712 invalidFields.insert(fieldId);
713
714 // No need to look for all invalid fields if we're gonna error out anyway
715 if (!options.GetForwardCompatible())
716 break;
717 }
718 }
719
720 if (!options.GetForwardCompatible() && !invalidFields.empty())
722 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
723 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
724
725 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
726 fieldZero->SetOnDiskId(GetFieldZeroId());
727 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
728 : RNTupleModel::Create(std::move(fieldZero));
730 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
731 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
732 for (const auto &topDesc : GetTopLevelFields()) {
733 if (invalidFields.count(topDesc.GetId()) > 0) {
734 // Field contains invalid columns: skip it
735 continue;
736 }
737
738 auto field = topDesc.CreateField(*this, createFieldOpts);
739
740 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
741 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
742 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
743 // in the first place, so we don't need to check for that flag again).
744 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
745 const auto &invalid = static_cast<const RInvalidField &>(*field);
746 const auto cat = invalid.GetCategory();
748 if (mustThrow)
749 throw invalid.GetError();
750
751 // Not a hard error: skip the field and go on.
752 continue;
753 }
754
755 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
756 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
757 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
758 });
759 } else {
760 model->AddField(std::move(field));
761 }
762 }
763 model->Freeze();
764 return model;
765}
766
768{
769 RNTupleDescriptor clone;
770 clone.fName = fName;
775 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
776 // and therefore not represent the actual sources's header.
777 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
778
779 for (const auto &d : fFieldDescriptors)
780 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
781 for (const auto &d : fColumnDescriptors)
782 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
783
784 for (const auto &d : fExtraTypeInfoDescriptors)
785 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
787 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
788
789 return clone;
790}
791
793{
795
800
804 clone.fNEntries = fNEntries;
805 clone.fNClusters = fNClusters;
806 clone.fGeneration = fGeneration;
807 for (const auto &d : fClusterGroupDescriptors)
808 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
810 for (const auto &d : fClusterDescriptors)
811 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
812 for (const auto &d : fAttributeSets)
813 clone.fAttributeSets.emplace_back(d.Clone());
814 return clone;
815}
816
817////////////////////////////////////////////////////////////////////////////////
818
820{
821 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
822 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
823}
824
826{
828 clone.fClusterGroupId = fClusterGroupId;
829 clone.fPageListLocator = fPageListLocator;
830 clone.fPageListLength = fPageListLength;
831 clone.fMinEntry = fMinEntry;
832 clone.fEntrySpan = fEntrySpan;
833 clone.fNClusters = fNClusters;
834 return clone;
835}
836
838{
839 RClusterGroupDescriptor clone = CloneSummary();
840 clone.fClusterIds = fClusterIds;
841 return clone;
842}
843
844////////////////////////////////////////////////////////////////////////////////
845
848 std::uint64_t firstElementIndex,
849 std::uint32_t compressionSettings,
851{
852 if (physicalId != pageRange.fPhysicalColumnId)
853 return R__FAIL("column ID mismatch");
854 if (fCluster.fColumnRanges.count(physicalId) > 0)
855 return R__FAIL("column ID conflict");
857 for (const auto &pi : pageRange.fPageInfos) {
858 columnRange.IncrementNElements(pi.GetNElements());
859 }
860 fCluster.fPageRanges[physicalId] = pageRange.Clone();
861 fCluster.fColumnRanges[physicalId] = columnRange;
862 return RResult<void>::Success();
863}
864
867{
868 if (fCluster.fColumnRanges.count(physicalId) > 0)
869 return R__FAIL("column ID conflict");
870
872 columnRange.SetPhysicalColumnId(physicalId);
873 columnRange.SetIsSuppressed(true);
874 fCluster.fColumnRanges[physicalId] = columnRange;
875 return RResult<void>::Success();
876}
877
880{
881 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
882 if (!columnRange.IsSuppressed())
883 continue;
884 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
885
886 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
887 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
888 // We expect only few columns and column representations per field, so we do a linear search
889 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
891 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
892 continue;
893 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
894 continue;
895
896 // Found corresponding column of a different column representation
897 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
898 if (otherColumnRange.IsSuppressed())
899 continue;
900
901 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
902 columnRange.SetNElements(otherColumnRange.GetNElements());
903 break;
904 }
905
906 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
907 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
908 std::to_string(columnRange.GetPhysicalColumnId()) +
909 ", cluster ID: " + std::to_string(fCluster.GetId()));
910 }
911 }
912 return RResult<void>::Success();
913}
914
917{
918 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
919 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
920 /// parent field in the hierarchy.
922 const auto &visitField, const auto &enterSubtree) -> void {
924 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
925 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
927 }
928 };
929
930 // Extended columns can only be part of the header extension
931 if (!desc.GetHeaderExtension())
932 return *this;
933
934 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
935 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
936 for (const auto &topLevelField : desc.GetTopLevelFields()) {
938 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
939 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
940 for (const auto &c : desc.GetColumnIterable(fieldId)) {
941 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
942 auto &columnRange = fCluster.fColumnRanges[physicalId];
943
944 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
945 // extension won't have on-disk metadata for the clusters that were already committed before the model
946 // was extended. Therefore, these need to be synthetically initialized upon reading.
947 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
948 columnRange.SetPhysicalColumnId(physicalId);
949 columnRange.SetFirstElementIndex(0);
950 columnRange.SetNElements(0);
951 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
952 }
953 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
954 // number of elements should have been if the column was not deferred; fix those and let
955 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
956 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
957 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
958 // field zero excluding subfields of collection and variant fields.
959 if (c.IsDeferredColumn()) {
960 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
961 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
962 if (!columnRange.IsSuppressed()) {
963 auto &pageRange = fCluster.fPageRanges[physicalId];
964 pageRange.fPhysicalColumnId = physicalId;
965 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
966 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
967 }
968 } else if (!columnRange.IsSuppressed()) {
969 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
970 }
971 }
972 },
974 }
975 return *this;
976}
977
979{
980 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
981 return R__FAIL("unset cluster ID");
982 if (fCluster.fNEntries == 0)
983 return R__FAIL("empty cluster");
984 for (auto &pr : fCluster.fPageRanges) {
985 if (fCluster.fColumnRanges.count(pr.first) == 0) {
986 return R__FAIL("missing column range");
987 }
988 pr.second.fCumulativeNElements.clear();
989 pr.second.fCumulativeNElements.reserve(pr.second.fPageInfos.size());
991 for (const auto &pi : pr.second.fPageInfos) {
992 sum += pi.GetNElements();
993 pr.second.fCumulativeNElements.emplace_back(sum);
994 }
995 }
997 std::swap(result, fCluster);
998 return result;
999}
1000
1001////////////////////////////////////////////////////////////////////////////////
1002
1005{
1007 builder.ClusterGroupId(clusterGroupDesc.GetId())
1008 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1009 .PageListLength(clusterGroupDesc.GetPageListLength())
1010 .MinEntry(clusterGroupDesc.GetMinEntry())
1011 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1012 .NClusters(clusterGroupDesc.GetNClusters());
1013 return builder;
1014}
1015
1017{
1018 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1019 return R__FAIL("unset cluster group ID");
1021 std::swap(result, fClusterGroup);
1022 return result;
1023}
1024
1025////////////////////////////////////////////////////////////////////////////////
1026
1028{
1029 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1030 throw RException(R__FAIL("invalid extra type info content id"));
1032 std::swap(result, fExtraTypeInfo);
1033 return result;
1034}
1035
1036////////////////////////////////////////////////////////////////////////////////
1037
1039{
1040 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1041 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1042 return RResult<void>::Success();
1043}
1044
1046{
1047 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1048 return R__FAIL("unset or unsupported RNTuple epoch version");
1049 }
1050
1051 // Reuse field name validity check
1052 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1053 if (!validName) {
1055 }
1056
1057 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1058 // parent not properly set?
1059 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1060 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1061 }
1062
1063 // Same number of columns in every column representation?
1064 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1065 if (columnCardinality == 0)
1066 continue;
1067
1068 // In AddColumn, we already checked that all but the last representation are complete.
1069 // Check that the last column representation is complete, i.e. has all columns.
1070 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1071 const auto nColumns = logicalColumnIds.size();
1072 // If we have only a single column representation, the following condition is true by construction
1073 if ((nColumns + 1) == columnCardinality)
1074 continue;
1075
1076 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1077 if (lastColumn.GetIndex() + 1 != columnCardinality)
1078 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1079 }
1080
1081 return RResult<void>::Success();
1082}
1083
1085{
1086 EnsureValidDescriptor().ThrowOnError();
1087 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1088 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1089 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1090 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1092 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1093 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1094 });
1096 std::swap(result, fDescriptor);
1097 return result;
1098}
1099
1101 std::uint16_t versionMinor, std::uint16_t versionPatch)
1102{
1104 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1105 }
1106 fDescriptor.fVersionEpoch = versionEpoch;
1107 fDescriptor.fVersionMajor = versionMajor;
1108 fDescriptor.fVersionMinor = versionMinor;
1109 fDescriptor.fVersionPatch = versionPatch;
1110}
1111
1113{
1114 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1115 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1116 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1117 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1118}
1119
1121 const std::string_view description)
1122{
1123 fDescriptor.fName = std::string(name);
1124 fDescriptor.fDescription = std::string(description);
1125}
1126
1128{
1129 if (flag % 64 == 0)
1130 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1131 fDescriptor.fFeatureFlags.insert(flag);
1132}
1133
1136{
1137 if (fDesc.fName.empty())
1138 return R__FAIL("attribute set name cannot be empty");
1139 if (fDesc.fAnchorLength == 0)
1140 return R__FAIL("invalid anchor length");
1141 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1142 return R__FAIL("invalid locator type");
1143
1144 return std::move(fDesc);
1145}
1146
1148{
1149 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1150 return R__FAIL("invalid logical column id");
1151 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1152 return R__FAIL("invalid physical column id");
1153 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1154 return R__FAIL("invalid field id, dangling column");
1155
1156 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1157 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1158 // as we have no idea what the valid range is.
1159 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1160 // we try to read the actual data contained in it.
1161 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1162 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1163 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1164 return R__FAIL("invalid column bit width");
1165 }
1166
1167 return fColumn.Clone();
1168}
1169
1172{
1174 fieldDesc.FieldVersion(field.GetFieldVersion())
1175 .TypeVersion(field.GetTypeVersion())
1176 .FieldName(field.GetFieldName())
1177 .FieldDescription(field.GetDescription())
1178 .TypeName(field.GetTypeName())
1179 .TypeAlias(field.GetTypeAlias())
1180 .Structure(field.GetStructure())
1181 .NRepetitions(field.GetNRepetitions());
1183 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1184 return fieldDesc;
1185}
1186
1188{
1189 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1190 return R__FAIL("invalid field id");
1191 }
1192 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1193 return R__FAIL("invalid field structure");
1194 }
1195 // FieldZero is usually named "" and would be a false positive here
1196 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1197 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1198 if (!validName) {
1200 }
1201 if (fField.GetFieldName().empty()) {
1202 return R__FAIL("name cannot be empty string \"\"");
1203 }
1204 }
1205 return fField.Clone();
1206}
1207
1209{
1210 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1211 if (fDescriptor.fHeaderExtension)
1212 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1213 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1214 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1215 }
1216}
1217
1220{
1222 if (!(fieldExists = EnsureFieldExists(fieldId)))
1224 if (!(fieldExists = EnsureFieldExists(linkId)))
1225 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1226
1227 if (linkId == fDescriptor.GetFieldZeroId()) {
1228 return R__FAIL("cannot make FieldZero a child field");
1229 }
1230 // fail if field already has another valid parent
1231 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1233 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1234 }
1235 if (fieldId == linkId) {
1236 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1237 }
1238 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1239 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1240 return RResult<void>::Success();
1241}
1242
1245{
1247 if (!(fieldExists = EnsureFieldExists(sourceId)))
1249 if (!(fieldExists = EnsureFieldExists(targetId)))
1250 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1251
1252 if (targetId == fDescriptor.GetFieldZeroId()) {
1253 return R__FAIL("cannot make FieldZero a projected field");
1254 }
1255 if (sourceId == targetId) {
1256 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1257 }
1258 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1259 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1260 }
1261 // fail if target field already has another valid projection source
1262 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1263 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1264 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1265 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1266 }
1267 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1268 return RResult<void>::Success();
1269}
1270
1272{
1273 const auto fieldId = columnDesc.GetFieldId();
1274 const auto columnIndex = columnDesc.GetIndex();
1275 const auto representationIndex = columnDesc.GetRepresentationIndex();
1276
1277 auto fieldExists = EnsureFieldExists(fieldId);
1278 if (!fieldExists) {
1280 }
1281 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1282
1283 if (columnDesc.IsAliasColumn()) {
1284 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1285 return R__FAIL("alias column type mismatch");
1286 }
1287 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1288 return R__FAIL("column index clash");
1289 }
1290 if (columnIndex > 0) {
1291 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1292 return R__FAIL("out of bounds column index");
1293 }
1294 if (representationIndex > 0) {
1295 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1296 return R__FAIL("out of bounds representation index");
1297 }
1298 if (columnIndex == 0) {
1299 assert(fieldDesc.fColumnCardinality > 0);
1300 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1302 return R__FAIL("incomplete column representations");
1303 }
1304 } else {
1305 if (columnIndex >= fieldDesc.fColumnCardinality)
1306 return R__FAIL("irregular column representations");
1307 }
1308 } else {
1309 // This will set the column cardinality to the number of columns of the first representation
1310 fieldDesc.fColumnCardinality = columnIndex + 1;
1311 }
1312
1313 const auto logicalId = columnDesc.GetLogicalId();
1314 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1315
1316 if (!columnDesc.IsAliasColumn())
1317 fDescriptor.fNPhysicalColumns++;
1318 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1319 if (fDescriptor.fHeaderExtension)
1320 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1321
1322 return RResult<void>::Success();
1323}
1324
1326{
1327 const auto id = clusterGroup.GetId();
1328 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1329 return R__FAIL("cluster group id clash");
1330 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1331 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1332 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1333 return RResult<void>::Success();
1334}
1335
1340
1342{
1343 if (!fDescriptor.fHeaderExtension)
1344 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1345}
1346
1348{
1349 if (fDescriptor.GetNLogicalColumns() == 0)
1350 return;
1351 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1352
1353 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1354 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1355 R__ASSERT(c.IsAliasColumn());
1356 R__ASSERT(id == c.GetLogicalId());
1357 fDescriptor.fColumnDescriptors.erase(id);
1358 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1359 if (link == c.fLogicalColumnId) {
1360 link += offset;
1361 break;
1362 }
1363 }
1364 c.fLogicalColumnId += offset;
1365 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1366 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1367 }
1368}
1369
1371{
1372 auto clusterId = clusterDesc.GetId();
1373 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1374 return R__FAIL("cluster id clash");
1375 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1376 return RResult<void>::Success();
1377}
1378
1381{
1382 // Make sure we have no duplicates
1383 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1384 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1385 return R__FAIL("extra type info duplicates");
1386 }
1387 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1388 return RResult<void>::Success();
1389}
1390
1392{
1393 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1395 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1396 *it = std::move(extraTypeInfoDesc);
1397 else
1398 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1399}
1400
1403{
1404 auto &attrSets = fDescriptor.fAttributeSets;
1405 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1406 return desc.GetName() == name;
1407 }) != attrSets.end()) {
1408 return R__FAIL("attribute sets with duplicate names");
1409 }
1410 attrSets.push_back(std::move(attrSetDesc));
1411 return RResult<void>::Success();
1412}
1413
1418
1424
1431
1437
1444
1449
1455
1460
1466
1472
1477
1482
1487
1492
1494{
1495 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1496 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1497 fName == other.fName;
1498};
1499
1501{
1503 desc.fAnchorLength = fAnchorLength;
1504 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1505 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1506 desc.fAnchorLocator = fAnchorLocator;
1507 desc.fName = fName;
1508 return desc;
1509}
#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
RResult< ROOT::Experimental::RNTupleAttrSetDescriptor > MoveDescriptor()
Attempt to make an AttributeSet descriptor.
Used to loop over all the Attribute Sets linked to an RNTuple.
Metadata stored for every Attribute Set linked to an RNTuple.
bool operator==(const RNTupleAttrSetDescriptor &other) const
std::uint32_t fAnchorLength
uncompressed size of the linked anchor
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 > AddAttributeSet(Experimental::RNTupleAttrSetDescriptor &&attrSetDesc)
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)
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.
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.
std::vector< Experimental::RNTupleAttrSetDescriptor > fAttributeSets
List of AttributeSets linked to this RNTuple.
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.
ROOT::Experimental::RNTupleAttrSetDescriptorIterable GetAttrSetIterable() const
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)
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