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 && fTypeChecksum == other.fTypeChecksum &&
46 fIsSoACollection == other.fIsSoACollection;
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 clone.fIsSoACollection = fIsSoACollection;
68 return clone;
69}
70
71std::unique_ptr<ROOT::RFieldBase>
73{
74 if (GetStructure() == ROOT::ENTupleStructure::kStreamer) {
75 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
76 auto streamerField = std::make_unique<ROOT::RStreamerField>(GetFieldName(), typeName);
77 streamerField->SetOnDiskId(fFieldId);
78 return streamerField;
79 }
80
81 // The structure may be unknown if the descriptor comes from a deserialized field with an unknown structural role.
82 // For forward compatibility, we allow this case and return an InvalidField.
83 if (GetStructure() == ROOT::ENTupleStructure::kUnknown) {
84 if (options.GetReturnInvalidOnError()) {
85 auto invalidField = std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), "",
87 invalidField->SetOnDiskId(fFieldId);
88 return invalidField;
89 } else {
90 throw RException(R__FAIL("unexpected on-disk field structure value for field \"" + GetFieldName() + "\""));
91 }
92 }
93
94 // Untyped records and collections
95 if (GetTypeName().empty()) {
96 switch (GetStructure()) {
98 std::vector<std::unique_ptr<ROOT::RFieldBase>> memberFields;
99 memberFields.reserve(fLinkIds.size());
100 for (auto id : fLinkIds) {
101 const auto &memberDesc = ntplDesc.GetFieldDescriptor(id);
102 auto field = memberDesc.CreateField(ntplDesc, options);
104 return field;
105 memberFields.emplace_back(std::move(field));
106 }
107 auto recordField = std::make_unique<ROOT::RRecordField>(GetFieldName(), std::move(memberFields));
108 recordField->SetOnDiskId(fFieldId);
109 return recordField;
110 }
112 if (fLinkIds.size() != 1) {
113 throw RException(R__FAIL("unsupported untyped collection for field \"" + GetFieldName() + "\""));
114 }
115 auto itemField = ntplDesc.GetFieldDescriptor(fLinkIds[0]).CreateField(ntplDesc, options);
117 return itemField;
118 auto collectionField = ROOT::RVectorField::CreateUntyped(GetFieldName(), std::move(itemField));
119 collectionField->SetOnDiskId(fFieldId);
120 return collectionField;
121 }
122 default: throw RException(R__FAIL("unsupported untyped field structure for field \"" + GetFieldName() + "\""));
123 }
124 }
125
126 try {
127 const auto &fieldName = GetFieldName();
128 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
129 // NOTE: Unwrap() here may throw an exception, hence the try block.
130 // If options.fReturnInvalidOnError is false we just rethrow it, otherwise we return an InvalidField wrapping the
131 // error.
132 auto field = ROOT::Internal::CallFieldBaseCreate(fieldName, typeName, options, &ntplDesc, fFieldId).Unwrap();
133 field->SetOnDiskId(fFieldId);
134
135 for (auto &subfield : *field) {
136 const auto subfieldId = ntplDesc.FindFieldId(subfield.GetFieldName(), subfield.GetParent()->GetOnDiskId());
137 subfield.SetOnDiskId(subfieldId);
139 auto &invalidField = static_cast<ROOT::RInvalidField &>(subfield);
140 // A subfield being invalid "infects" its entire ancestry.
141 return invalidField.Clone(fieldName);
142 }
143 }
144
145 return field;
146 } catch (const RException &ex) {
147 if (options.GetReturnInvalidOnError())
148 return std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), ex.GetError().GetReport(),
150 else
151 throw ex;
152 }
153}
154
156{
158 return false;
159
160 // Skip untyped structs
161 if (fTypeName.empty())
162 return false;
163
164 if (fStructure == ROOT::ENTupleStructure::kRecord) {
165 if (fTypeName.compare(0, 10, "std::pair<") == 0)
166 return false;
167 if (fTypeName.compare(0, 11, "std::tuple<") == 0)
168 return false;
169 }
170
171 return true;
172}
173
175{
176 return Internal::IsCustomEnumFieldDesc(desc, *this);
177}
178
183
184////////////////////////////////////////////////////////////////////////////////
185
187{
188 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
189 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
190 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
191 fValueRange == other.fValueRange;
192}
193
195{
196 RColumnDescriptor clone;
197 clone.fLogicalColumnId = fLogicalColumnId;
198 clone.fPhysicalColumnId = fPhysicalColumnId;
199 clone.fBitsOnStorage = fBitsOnStorage;
200 clone.fType = fType;
201 clone.fFieldId = fFieldId;
202 clone.fIndex = fIndex;
203 clone.fFirstElementIndex = fFirstElementIndex;
204 clone.fRepresentationIndex = fRepresentationIndex;
205 clone.fValueRange = fValueRange;
206 return clone;
207}
208
209////////////////////////////////////////////////////////////////////////////////
210
213{
214 if (!fCumulativeNElements) {
215 // Small range, just iterate through fPageInfos
218 for (const auto &pi : fPageInfos) {
219 if (firstInPage + pi.GetNElements() > idxInCluster) {
221 }
222 pageNumber++;
223 firstInPage += pi.GetNElements();
224 }
225 R__ASSERT(false);
226 }
227
228 const auto N = fCumulativeNElements->size();
229 R__ASSERT(N > 0);
230 R__ASSERT(N == fPageInfos.size());
231
232 std::size_t left = 0;
233 std::size_t right = N - 1;
234 std::size_t midpoint = N;
235 while (left <= right) {
236 midpoint = (left + right) / 2;
237 if ((*fCumulativeNElements)[midpoint] <= idxInCluster) {
238 left = midpoint + 1;
239 continue;
240 }
241
242 if ((midpoint == 0) || ((*fCumulativeNElements)[midpoint - 1] <= idxInCluster))
243 break;
244
245 right = midpoint - 1;
246 }
248
249 auto pageInfo = fPageInfos[midpoint];
250 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : (*fCumulativeNElements)[midpoint - 1];
252 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
254}
255
256std::size_t
259 std::size_t pageSize)
260{
261 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
262 R__ASSERT(!columnRange.IsSuppressed());
263
264 const auto nElements =
265 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
266 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
267 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
268
270 return 0U;
271 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
272
273 std::vector<RPageInfo> pageInfos;
274 // Synthesize new `RPageInfo`s as needed
275 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
279 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
282 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
283 pageInfo.SetLocator(locator);
284 pageInfos.emplace_back(pageInfo);
285 nRemainingElements -= pageInfo.GetNElements();
286 }
287
288 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
289 std::make_move_iterator(fPageInfos.end()));
290 std::swap(fPageInfos, pageInfos);
292}
293
295{
296 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
297 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
298}
299
301{
302 std::uint64_t nbytes = 0;
303 for (const auto &pr : fPageRanges) {
304 for (const auto &pi : pr.second.GetPageInfos()) {
305 nbytes += pi.GetLocator().GetNBytesOnStorage();
306 }
307 }
308 return nbytes;
309}
310
312{
313 RClusterDescriptor clone;
314 clone.fClusterId = fClusterId;
315 clone.fFirstEntryIndex = fFirstEntryIndex;
316 clone.fNEntries = fNEntries;
317 clone.fColumnRanges = fColumnRanges;
318 for (const auto &d : fPageRanges)
319 clone.fPageRanges.emplace(d.first, d.second.Clone());
320 return clone;
321}
322
323////////////////////////////////////////////////////////////////////////////////
324
326{
327 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
328}
329
331{
333 clone.fContentId = fContentId;
334 clone.fTypeVersion = fTypeVersion;
335 clone.fTypeName = fTypeName;
336 clone.fContent = fContent;
337 return clone;
338}
339
340////////////////////////////////////////////////////////////////////////////////
341
346
348{
349 // clang-format off
350 return fName == other.fName &&
351 fDescription == other.fDescription &&
352 fNEntries == other.fNEntries &&
353 fGeneration == other.fGeneration &&
354 fFieldZeroId == other.fFieldZeroId &&
355 fFieldDescriptors == other.fFieldDescriptors &&
356 fColumnDescriptors == other.fColumnDescriptors &&
357 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
358 fClusterDescriptors == other.fClusterDescriptors;
359 // clang-format on
360}
361
363{
365 for (const auto &cd : fClusterDescriptors) {
366 if (!cd.second.ContainsColumn(physicalColumnId))
367 continue;
368 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
369 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
370 }
371 return result;
372}
373
374////////////////////////////////////////////////////////////////////////////////
375/// Return the cluster boundaries for each cluster in this RNTuple.
376std::vector<ROOT::Internal::RNTupleClusterBoundaries>
378{
379 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
380 boundaries.reserve(desc.GetNClusters());
381 auto clusterId = desc.FindClusterId(0, 0);
383 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
384 R__ASSERT(clusterDesc.GetNEntries() > 0);
385 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
386 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
388 }
389 return boundaries;
390}
391
394{
395 std::string leafName(fieldName);
396 auto posDot = leafName.find_last_of('.');
397 if (posDot != std::string::npos) {
398 auto parentName = leafName.substr(0, posDot);
399 leafName = leafName.substr(posDot + 1);
400 parentId = FindFieldId(parentName, parentId);
401 }
402 auto itrFieldDesc = fFieldDescriptors.find(parentId);
403 if (itrFieldDesc == fFieldDescriptors.end())
405 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
406 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
407 return linkId;
408 }
410}
411
413{
415 return "";
416
417 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
418 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
419 if (prefix.empty())
420 return fieldDescriptor.GetFieldName();
421 return prefix + "." + fieldDescriptor.GetFieldName();
422}
423
425{
426 R__ASSERT(fVersionEpoch == 1);
427 return fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1;
428}
429
431{
432 std::string typeName = fieldDesc.GetTypeName();
433
434 if (FieldTypeNamesMayNeedFixup()) {
435 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
436 }
437
438 return typeName;
439}
440
442{
443 return FindFieldId(fieldName, GetFieldZeroId());
444}
445
447 std::uint32_t columnIndex,
448 std::uint16_t representationIndex) const
449{
450 auto itr = fFieldDescriptors.find(fieldId);
451 if (itr == fFieldDescriptors.cend())
453 if (columnIndex >= itr->second.GetColumnCardinality())
455 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
456 if (itr->second.GetLogicalColumnIds().size() <= idx)
458 return itr->second.GetLogicalColumnIds()[idx];
459}
460
462 std::uint32_t columnIndex,
463 std::uint16_t representationIndex) const
464{
465 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
468 return GetColumnDescriptor(logicalId).GetPhysicalId();
469}
470
473{
474 if (GetNClusterGroups() == 0)
476
477 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
478
479 std::size_t cgLeft = 0;
480 std::size_t cgRight = GetNClusterGroups() - 1;
481 while (cgLeft <= cgRight) {
482 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
483 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
484 R__ASSERT(!clusterIds.empty());
485
486 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
487 // this may happen if the RNTuple has an empty schema
488 if (!clusterDesc.ContainsColumn(physicalColumnId))
490
491 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
493 // Look into the lower half of cluster groups
495 cgRight = cgMidpoint - 1;
496 continue;
497 }
498
499 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
500 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
501 // Look into the upper half of cluster groups
502 cgLeft = cgMidpoint + 1;
503 continue;
504 }
505
506 // Binary search in the current cluster group; since we already checked the element range boundaries,
507 // the element must be in that cluster group.
508 std::size_t clusterLeft = 0;
509 std::size_t clusterRight = clusterIds.size() - 1;
510 while (clusterLeft <= clusterRight) {
511 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
513 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
514
515 if (columnRange.Contains(index))
516 return clusterId;
517
518 if (columnRange.GetFirstElementIndex() > index) {
521 continue;
522 }
523
524 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
526 continue;
527 }
528 }
529 R__ASSERT(false);
530 }
532}
533
535{
536 if (GetNClusterGroups() == 0)
538
539 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
540
541 std::size_t cgLeft = 0;
542 std::size_t cgRight = GetNClusterGroups() - 1;
543 while (cgLeft <= cgRight) {
544 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
545 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
546
547 if (cgDesc.GetMinEntry() > entryIdx) {
549 cgRight = cgMidpoint - 1;
550 continue;
551 }
552
553 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
554 cgLeft = cgMidpoint + 1;
555 continue;
556 }
557
558 // Binary search in the current cluster group; since we already checked the element range boundaries,
559 // the element must be in that cluster group.
560 const auto &clusterIds = cgDesc.GetClusterIds();
561 R__ASSERT(!clusterIds.empty());
562 std::size_t clusterLeft = 0;
563 std::size_t clusterRight = clusterIds.size() - 1;
564 while (clusterLeft <= clusterRight) {
565 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
566 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
567
568 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
571 continue;
572 }
573
574 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
576 continue;
577 }
578
580 }
581 R__ASSERT(false);
582 }
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 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
594 return FindClusterId(firstEntryInNextCluster);
595}
596
598{
599 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
600 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
601 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
602 // binary search code path remains tested.
603 const auto &clusterDesc = GetClusterDescriptor(clusterId);
604 if (clusterDesc.GetFirstEntryIndex() == 0)
606 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
607}
608
609std::vector<ROOT::DescriptorId_t>
611{
612 std::vector<ROOT::DescriptorId_t> fields;
613 for (const auto fieldId : fFieldIdsOrder) {
614 if (fFieldIdsLookup.count(desc.GetFieldDescriptor(fieldId).GetParentId()) == 0)
615 fields.emplace_back(fieldId);
616 }
617 return fields;
618}
619
625
627 : fNTuple(ntuple)
628{
629 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
630
631 while (!fieldIdQueue.empty()) {
632 auto currFieldId = fieldIdQueue.front();
633 fieldIdQueue.pop_front();
634
635 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
636 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
637
638 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
639 auto fieldId = field.GetId();
640 fieldIdQueue.push_back(fieldId);
641 }
642 }
643}
644
645std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
646{
647 std::vector<std::uint64_t> result;
648 unsigned int base = 0;
649 std::uint64_t flags = 0;
650 for (auto f : fFeatureFlags) {
651 if ((f > 0) && ((f % 64) == 0))
652 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
653 while (f > base + 64) {
654 result.emplace_back(flags);
655 flags = 0;
656 base += 64;
657 }
658 f -= base;
659 flags |= std::uint64_t(1) << f;
660 }
661 result.emplace_back(flags);
662 return result;
663}
664
666 std::vector<RClusterDescriptor> &clusterDescs)
667{
669 if (iter == fClusterGroupDescriptors.end())
670 return R__FAIL("invalid attempt to add details of unknown cluster group");
671 if (iter->second.HasClusterDetails())
672 return R__FAIL("invalid attempt to re-populate cluster group details");
673 if (iter->second.GetNClusters() != clusterDescs.size())
674 return R__FAIL("mismatch of number of clusters");
675
676 std::vector<ROOT::DescriptorId_t> clusterIds;
677 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
678 clusterIds.emplace_back(clusterDescs[i].GetId());
679 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
680 if (!success) {
681 return R__FAIL("invalid attempt to re-populate existing cluster");
682 }
683 }
685 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
686 });
688 cgBuilder.AddSortedClusters(clusterIds);
689 iter->second = cgBuilder.MoveDescriptor().Unwrap();
690 return RResult<void>::Success();
691}
692
694{
696 if (iter == fClusterGroupDescriptors.end())
697 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
698 if (!iter->second.HasClusterDetails())
699 return R__FAIL("invalid attempt to drop details of cluster group summary");
700
701 for (auto clusterId : iter->second.GetClusterIds())
703 iter->second = iter->second.CloneSummary();
704 return RResult<void>::Success();
705}
706
707std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
708{
709 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
710 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
711 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
712 for (const auto &colDesc : GetColumnIterable()) {
714 auto fieldId = colDesc.GetFieldId();
715 while (1) {
716 const auto &field = GetFieldDescriptor(fieldId);
717 if (field.GetParentId() == GetFieldZeroId())
718 break;
719 fieldId = field.GetParentId();
720 }
721 invalidFields.insert(fieldId);
722
723 // No need to look for all invalid fields if we're gonna error out anyway
724 if (!options.GetForwardCompatible())
725 break;
726 }
727 }
728
729 if (!options.GetForwardCompatible() && !invalidFields.empty())
731 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
732 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
733
734 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
735 fieldZero->SetOnDiskId(GetFieldZeroId());
736 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
737 : RNTupleModel::Create(std::move(fieldZero));
739 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
740 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
741 for (const auto &topDesc : GetTopLevelFields()) {
742 if (invalidFields.count(topDesc.GetId()) > 0) {
743 // Field contains invalid columns: skip it
744 continue;
745 }
746
747 auto field = topDesc.CreateField(*this, createFieldOpts);
748
749 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
750 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
751 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
752 // in the first place, so we don't need to check for that flag again).
753 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
754 const auto &invalid = static_cast<const RInvalidField &>(*field);
755 const auto cat = invalid.GetCategory();
757 if (mustThrow)
758 throw invalid.GetError();
759
760 // Not a hard error: skip the field and go on.
761 continue;
762 }
763
764 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
765 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
766 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
767 });
768 } else {
769 model->AddField(std::move(field));
770 }
771 }
772 model->Freeze();
773 return model;
774}
775
777{
778 RNTupleDescriptor clone;
779 clone.fName = fName;
784 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
785 // and therefore not represent the actual sources's header.
786 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
787
788 for (const auto &d : fFieldDescriptors)
789 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
790 for (const auto &d : fColumnDescriptors)
791 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
792
793 for (const auto &d : fExtraTypeInfoDescriptors)
794 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
796 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
797
798 // In case we are copying the schema from a pre-1.0.0.1 RNTuple we need to patch all field type names
799 // to use the proper normalization.
801 std::vector<ROOT::DescriptorId_t> toVisit;
802 toVisit.push_back(GetFieldZeroId());
803 while (!toVisit.empty()) {
804 auto fieldId = toVisit.back();
805 toVisit.pop_back();
806 for (auto &field : clone.GetFieldIterable(fieldId)) {
808 toVisit.push_back(field.GetId());
809 }
810 }
811 }
812
813 return clone;
814}
815
817{
819
824
828 clone.fNEntries = fNEntries;
829 clone.fNClusters = fNClusters;
830 clone.fGeneration = fGeneration;
831 for (const auto &d : fClusterGroupDescriptors)
832 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
834 for (const auto &d : fClusterDescriptors)
835 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
836 for (const auto &d : fAttributeSets)
837 clone.fAttributeSets.emplace_back(d.Clone());
838 return clone;
839}
840
841////////////////////////////////////////////////////////////////////////////////
842
844{
845 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
846 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
847}
848
850{
852 clone.fClusterGroupId = fClusterGroupId;
853 clone.fPageListLocator = fPageListLocator;
854 clone.fPageListLength = fPageListLength;
855 clone.fMinEntry = fMinEntry;
856 clone.fEntrySpan = fEntrySpan;
857 clone.fNClusters = fNClusters;
858 return clone;
859}
860
862{
863 RClusterGroupDescriptor clone = CloneSummary();
864 clone.fClusterIds = fClusterIds;
865 return clone;
866}
867
868////////////////////////////////////////////////////////////////////////////////
869
872 std::uint64_t firstElementIndex,
873 std::uint32_t compressionSettings,
875{
876 if (physicalId != pageRange.fPhysicalColumnId)
877 return R__FAIL("column ID mismatch");
878 if (fCluster.fColumnRanges.count(physicalId) > 0)
879 return R__FAIL("column ID conflict");
881 for (const auto &pi : pageRange.fPageInfos) {
882 columnRange.IncrementNElements(pi.GetNElements());
883 }
884 fCluster.fPageRanges[physicalId] = pageRange.Clone();
885 fCluster.fColumnRanges[physicalId] = columnRange;
886 return RResult<void>::Success();
887}
888
891{
892 if (fCluster.fColumnRanges.count(physicalId) > 0)
893 return R__FAIL("column ID conflict");
894
896 columnRange.SetPhysicalColumnId(physicalId);
897 columnRange.SetIsSuppressed(true);
898 fCluster.fColumnRanges[physicalId] = columnRange;
899 return RResult<void>::Success();
900}
901
904{
905 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
906 if (!columnRange.IsSuppressed())
907 continue;
908 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
909
910 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
911 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
912 // We expect only few columns and column representations per field, so we do a linear search
913 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
915 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
916 continue;
917 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
918 continue;
919
920 // Found corresponding column of a different column representation
921 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
922 if (otherColumnRange.IsSuppressed())
923 continue;
924
925 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
926 columnRange.SetNElements(otherColumnRange.GetNElements());
927 break;
928 }
929
930 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
931 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
932 std::to_string(columnRange.GetPhysicalColumnId()) +
933 ", cluster ID: " + std::to_string(fCluster.GetId()));
934 }
935 }
936 return RResult<void>::Success();
937}
938
941{
942 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
943 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
944 /// parent field in the hierarchy.
946 const auto &visitField, const auto &enterSubtree) -> void {
948 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
949 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
951 }
952 };
953
954 // Extended columns can only be part of the header extension
955 if (!desc.GetHeaderExtension())
956 return *this;
957
958 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
959 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
960 for (const auto &topLevelField : desc.GetTopLevelFields()) {
962 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
963 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
964 for (const auto &c : desc.GetColumnIterable(fieldId)) {
965 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
966 auto &columnRange = fCluster.fColumnRanges[physicalId];
967
968 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
969 // extension won't have on-disk metadata for the clusters that were already committed before the model
970 // was extended. Therefore, these need to be synthetically initialized upon reading.
971 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
972 columnRange.SetPhysicalColumnId(physicalId);
973 columnRange.SetFirstElementIndex(0);
974 columnRange.SetNElements(0);
975 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
976 }
977 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
978 // number of elements should have been if the column was not deferred; fix those and let
979 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
980 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
981 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
982 // field zero excluding subfields of collection and variant fields.
983 if (c.IsDeferredColumn()) {
984 if (c.GetRepresentationIndex() == 0) {
985 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
986 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
987 } else {
988 // Deferred representations which are not the first cannot count on the number of elements being
989 // equal to Entries * nRepetitions because they might have been added in a later cluster. But they
990 // can rely on the first representation having the correct FirstElement/NElements (by definition
991 // the first representation cannot be an "extended" one), therefore they can just copy the value
992 // from it.
993 const auto &field = desc.GetFieldDescriptor(fieldId);
994 const auto firstReprColumnId = field.GetLogicalColumnIds()[c.GetIndex()];
995 const auto &firstReprColumnRange = fCluster.fColumnRanges[firstReprColumnId];
996 columnRange.SetFirstElementIndex(firstReprColumnRange.GetFirstElementIndex());
997 columnRange.SetNElements(firstReprColumnRange.GetNElements());
998 }
999 if (!columnRange.IsSuppressed()) {
1000 auto &pageRange = fCluster.fPageRanges[physicalId];
1001 pageRange.fPhysicalColumnId = physicalId;
1002 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
1003 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
1004 }
1005 } else if (!columnRange.IsSuppressed()) {
1006 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
1007 }
1008 }
1009 },
1011 }
1012 return *this;
1013}
1014
1016{
1017 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
1018 return R__FAIL("unset cluster ID");
1019 if (fCluster.fNEntries == 0)
1020 return R__FAIL("empty cluster");
1021 for (auto &pr : fCluster.fPageRanges) {
1022 if (fCluster.fColumnRanges.count(pr.first) == 0) {
1023 return R__FAIL("missing column range");
1024 }
1025 pr.second.fCumulativeNElements.reset();
1026 const auto nPages = pr.second.fPageInfos.size();
1028 pr.second.fCumulativeNElements = std::make_unique<std::vector<NTupleSize_t>>();
1029 pr.second.fCumulativeNElements->reserve(nPages);
1031 for (const auto &pi : pr.second.fPageInfos) {
1032 sum += pi.GetNElements();
1033 pr.second.fCumulativeNElements->emplace_back(sum);
1034 }
1035 }
1036 }
1038 std::swap(result, fCluster);
1039 return result;
1040}
1041
1042////////////////////////////////////////////////////////////////////////////////
1043
1046{
1048 builder.ClusterGroupId(clusterGroupDesc.GetId())
1049 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1050 .PageListLength(clusterGroupDesc.GetPageListLength())
1051 .MinEntry(clusterGroupDesc.GetMinEntry())
1052 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1053 .NClusters(clusterGroupDesc.GetNClusters());
1054 return builder;
1055}
1056
1058{
1059 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1060 return R__FAIL("unset cluster group ID");
1062 std::swap(result, fClusterGroup);
1063 return result;
1064}
1065
1066////////////////////////////////////////////////////////////////////////////////
1067
1069{
1070 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1071 throw RException(R__FAIL("invalid extra type info content id"));
1073 std::swap(result, fExtraTypeInfo);
1074 return result;
1075}
1076
1077////////////////////////////////////////////////////////////////////////////////
1078
1080{
1081 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1082 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1083 return RResult<void>::Success();
1084}
1085
1087{
1088 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1089 return R__FAIL("unset or unsupported RNTuple epoch version");
1090 }
1091
1092 // Reuse field name validity check
1093 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1094 if (!validName) {
1096 }
1097
1098 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1099 // parent not properly set?
1100 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1101 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1102 }
1103
1104 // Same number of columns in every column representation?
1105 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1106 if (columnCardinality == 0)
1107 continue;
1108
1109 // In AddColumn, we already checked that all but the last representation are complete.
1110 // Check that the last column representation is complete, i.e. has all columns.
1111 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1112 const auto nColumns = logicalColumnIds.size();
1113 // If we have only a single column representation, the following condition is true by construction
1114 if ((nColumns + 1) == columnCardinality)
1115 continue;
1116
1117 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1118 if (lastColumn.GetIndex() + 1 != columnCardinality)
1119 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1120 }
1121
1122 return RResult<void>::Success();
1123}
1124
1126{
1127 EnsureValidDescriptor().ThrowOnError();
1128 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1129 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1130 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1131 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1133 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1134 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1135 });
1137 std::swap(result, fDescriptor);
1138 return result;
1139}
1140
1142 std::uint16_t versionMinor, std::uint16_t versionPatch)
1143{
1145 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1146 }
1147 fDescriptor.fVersionEpoch = versionEpoch;
1148 fDescriptor.fVersionMajor = versionMajor;
1149 fDescriptor.fVersionMinor = versionMinor;
1150 fDescriptor.fVersionPatch = versionPatch;
1151}
1152
1154{
1155 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1156 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1157 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1158 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1159}
1160
1162 const std::string_view description)
1163{
1164 fDescriptor.fName = std::string(name);
1165 fDescriptor.fDescription = std::string(description);
1166}
1167
1169{
1170 if (flag % 64 == 0)
1171 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1172 fDescriptor.fFeatureFlags.insert(flag);
1173}
1174
1177{
1178 if (fDesc.fName.empty())
1179 return R__FAIL("attribute set name cannot be empty");
1180 if (fDesc.fAnchorLength == 0)
1181 return R__FAIL("invalid anchor length");
1182 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1183 return R__FAIL("invalid locator type");
1184
1185 return std::move(fDesc);
1186}
1187
1189{
1190 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1191 return R__FAIL("invalid logical column id");
1192 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1193 return R__FAIL("invalid physical column id");
1194 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1195 return R__FAIL("invalid field id, dangling column");
1196
1197 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1198 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1199 // as we have no idea what the valid range is.
1200 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1201 // we try to read the actual data contained in it.
1202 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1203 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1204 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1205 return R__FAIL("invalid column bit width");
1206 }
1207
1208 return fColumn.Clone();
1209}
1210
1213{
1215 fieldDesc.FieldVersion(field.GetFieldVersion())
1216 .TypeVersion(field.GetTypeVersion())
1217 .FieldName(field.GetFieldName())
1218 .FieldDescription(field.GetDescription())
1219 .TypeName(field.GetTypeName())
1220 .TypeAlias(field.GetTypeAlias())
1221 .Structure(field.GetStructure())
1222 .NRepetitions(field.GetNRepetitions());
1224 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1225 if (field.GetTraits() & ROOT::RFieldBase::kTraitSoACollection) {
1226 assert(field.GetStructure() == ENTupleStructure::kCollection);
1227 fieldDesc.IsSoACollection(true);
1228 }
1229 return fieldDesc;
1230}
1231
1233{
1234 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1235 return R__FAIL("invalid field id");
1236 }
1237 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1238 return R__FAIL("invalid field structure");
1239 }
1240 if (fField.IsSoACollection() && (fField.GetStructure() != ROOT::ENTupleStructure::kCollection)) {
1241 return R__FAIL("invalid SoA flag on non-collection field");
1242 }
1243 // FieldZero is usually named "" and would be a false positive here
1244 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1245 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1246 if (!validName) {
1248 }
1249 if (fField.GetFieldName().empty()) {
1250 return R__FAIL("name cannot be empty string \"\"");
1251 }
1252 }
1253 return fField.Clone();
1254}
1255
1257{
1258 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1259 if (fDescriptor.fHeaderExtension)
1260 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1261 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1262 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1263 }
1264}
1265
1268{
1270 if (!(fieldExists = EnsureFieldExists(fieldId)))
1272 if (!(fieldExists = EnsureFieldExists(linkId)))
1273 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1274
1275 if (linkId == fDescriptor.GetFieldZeroId()) {
1276 return R__FAIL("cannot make FieldZero a child field");
1277 }
1278 // fail if field already has another valid parent
1279 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1281 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1282 }
1283 if (fieldId == linkId) {
1284 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1285 }
1286 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1287 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1288 return RResult<void>::Success();
1289}
1290
1293{
1295 if (!(fieldExists = EnsureFieldExists(sourceId)))
1297 if (!(fieldExists = EnsureFieldExists(targetId)))
1298 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1299
1300 if (targetId == fDescriptor.GetFieldZeroId()) {
1301 return R__FAIL("cannot make FieldZero a projected field");
1302 }
1303 if (sourceId == targetId) {
1304 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1305 }
1306 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1307 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1308 }
1309 // fail if target field already has another valid projection source
1310 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1311 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1312 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1313 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1314 }
1315 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1316 return RResult<void>::Success();
1317}
1318
1320{
1321 const auto fieldId = columnDesc.GetFieldId();
1322 const auto columnIndex = columnDesc.GetIndex();
1323 const auto representationIndex = columnDesc.GetRepresentationIndex();
1324
1325 auto fieldExists = EnsureFieldExists(fieldId);
1326 if (!fieldExists) {
1328 }
1329 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1330
1331 if (columnDesc.IsAliasColumn()) {
1332 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1333 return R__FAIL("alias column type mismatch");
1334 }
1335 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1336 return R__FAIL("column index clash");
1337 }
1338 if (columnIndex > 0) {
1339 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1340 return R__FAIL("out of bounds column index");
1341 }
1342 if (representationIndex > 0) {
1343 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1344 return R__FAIL("out of bounds representation index");
1345 }
1346 if (columnIndex == 0) {
1347 assert(fieldDesc.fColumnCardinality > 0);
1348 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1350 return R__FAIL("incomplete column representations");
1351 }
1352 } else {
1353 if (columnIndex >= fieldDesc.fColumnCardinality)
1354 return R__FAIL("irregular column representations");
1355 }
1356 } else {
1357 // This will set the column cardinality to the number of columns of the first representation
1358 fieldDesc.fColumnCardinality = columnIndex + 1;
1359 }
1360
1361 const auto logicalId = columnDesc.GetLogicalId();
1362 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1363
1364 if (!columnDesc.IsAliasColumn())
1365 fDescriptor.fNPhysicalColumns++;
1366 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1367 if (fDescriptor.fHeaderExtension)
1368 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1369
1370 return RResult<void>::Success();
1371}
1372
1374{
1375 const auto id = clusterGroup.GetId();
1376 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1377 return R__FAIL("cluster group id clash");
1378 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1379 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1380 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1381 return RResult<void>::Success();
1382}
1383
1385{
1386 fDescriptor = descriptor.CloneSchema();
1387}
1388
1390{
1391 if (!fDescriptor.fHeaderExtension)
1392 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1393}
1394
1396{
1397 if (fDescriptor.GetNLogicalColumns() == 0)
1398 return;
1399 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1400
1401 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1402 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1403 R__ASSERT(c.IsAliasColumn());
1404 R__ASSERT(id == c.GetLogicalId());
1405 fDescriptor.fColumnDescriptors.erase(id);
1406 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1407 if (link == c.fLogicalColumnId) {
1408 link += offset;
1409 break;
1410 }
1411 }
1412 c.fLogicalColumnId += offset;
1413 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1414 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1415 }
1416
1417 // Patch up column ids in the header extension
1418 if (auto &xHeader = fDescriptor.fHeaderExtension) {
1419 for (auto &columnId : xHeader->fExtendedColumnRepresentations) {
1420 if (columnId >= fDescriptor.GetNPhysicalColumns())
1421 columnId += offset;
1422 }
1423 }
1424}
1425
1427{
1428 auto clusterId = clusterDesc.GetId();
1429 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1430 return R__FAIL("cluster id clash");
1431 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1432 return RResult<void>::Success();
1433}
1434
1437{
1438 // Make sure we have no duplicates
1439 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1440 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1441 return R__FAIL("extra type info duplicates");
1442 }
1443 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1444 return RResult<void>::Success();
1445}
1446
1448{
1449 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1451 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1452 *it = std::move(extraTypeInfoDesc);
1453 else
1454 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1455}
1456
1459{
1460 auto &attrSets = fDescriptor.fAttributeSets;
1461 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1462 return desc.GetName() == name;
1463 }) != attrSets.end()) {
1464 return R__FAIL("attribute sets with duplicate names");
1465 }
1466 attrSets.push_back(std::move(attrSetDesc));
1467 return RResult<void>::Success();
1468}
1469
1474
1480
1487
1493
1500
1505
1511
1516
1522
1528
1533
1538
1543
1548
1550{
1551 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1552 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1553 fName == other.fName;
1554};
1555
1557{
1559 desc.fAnchorLength = fAnchorLength;
1560 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1561 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1562 desc.fAnchorLocator = fAnchorLocator;
1563 desc.fName = fName;
1564 return desc;
1565}
1566
1568{
1569 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1570 return false;
1571 if (fieldDesc.GetTypeName().rfind("std::", 0) == 0)
1572 return false;
1573
1574 auto subFieldId = desc.FindFieldId("_0", fieldDesc.GetId());
1576 return false;
1577
1578 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
1579 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
1580 "std::int64_t", "std::uint64_t"};
1581 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
1582 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
1583}
1584
1586{
1587 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1588 return false;
1589 return (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0);
1590}
1591
#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:145
#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.
static constexpr std::size_t kLargeRangeThreshold
Create the fCumulativeNElements only when its needed, i.e. when there are many pages to search throug...
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.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ 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 R__DEPRECATED(6
bool operator==(const RFieldDescriptor &other) const
bool IsCustomClass() const R__DEPRECATED(6
bool IsStdAtomic() const R__DEPRECATED(6
std::string fFieldDescription
Free text set by the user.
ROOT::DescriptorId_t fParentId
Establishes sub field relationships, such as classes and collections.
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.
bool fIsSoACollection
Indicates if this is a collection that should be represented in memory by a SoA layout.
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:97
@ 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 > GetTopMostFields(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 ...
bool FieldTypeNamesMayNeedFixup() const
ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
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:82
static constexpr std::uint16_t kVersionMajor
Definition RNTuple.hxx:80
static constexpr std::uint16_t kVersionEpoch
Definition RNTuple.hxx:79
static constexpr std::uint16_t kVersionMinor
Definition RNTuple.hxx:81
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
ROOT::DescriptorId_t CallFindClusterIdOn(const ROOT::RNTupleDescriptor &desc, ROOT::NTupleSize_t entryIdx)
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)
void FixupFieldTypeName(ROOT::RFieldDescriptor &fieldDesc)
bool IsCustomEnumFieldDesc(const RNTupleDescriptor &desc, const RFieldDescriptor &fieldDesc)
Tells if the field describes a user-defined enum type.
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.
bool IsStdAtomicFieldDesc(const RFieldDescriptor &fieldDesc)
Tells if the field describes a std::atomic<T> type.
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:2338