Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleDescriptor.cxx
Go to the documentation of this file.
1/// \file RNTupleDescriptor.cxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \author Javier Lopez-Gomez <javier.lopez.gomez@cern.ch>
4/// \date 2018-10-04
5
6/*************************************************************************
7 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#include <ROOT/RError.hxx>
15#include <ROOT/RFieldBase.hxx>
16#include <ROOT/RNTuple.hxx>
18#include <ROOT/RNTupleModel.hxx>
19#include <ROOT/RNTupleTypes.hxx>
20#include <ROOT/RNTupleUtils.hxx>
21#include <ROOT/RPage.hxx>
22#include <string_view>
23
24#include <RZip.h>
25#include <TError.h>
26
27#include <algorithm>
28#include <cstdint>
29#include <deque>
30#include <functional>
31#include <iostream>
32#include <set>
33#include <utility>
34
36{
37 return fFieldId == other.fFieldId && fFieldVersion == other.fFieldVersion && fTypeVersion == other.fTypeVersion &&
38 fFieldName == other.fFieldName && fFieldDescription == other.fFieldDescription &&
39 fTypeName == other.fTypeName && fTypeAlias == other.fTypeAlias && fNRepetitions == other.fNRepetitions &&
40 fStructure == other.fStructure && fParentId == other.fParentId &&
41 fProjectionSourceId == other.fProjectionSourceId && fLinkIds == other.fLinkIds &&
42 fLogicalColumnIds == other.fLogicalColumnIds && fTypeChecksum == other.fTypeChecksum &&
43 fIsSoACollection == other.fIsSoACollection;
44}
45
47{
48 RFieldDescriptor clone;
49 clone.fFieldId = fFieldId;
50 clone.fFieldVersion = fFieldVersion;
51 clone.fTypeVersion = fTypeVersion;
52 clone.fFieldName = fFieldName;
53 clone.fFieldDescription = fFieldDescription;
54 clone.fTypeName = fTypeName;
55 clone.fTypeAlias = fTypeAlias;
56 clone.fNRepetitions = fNRepetitions;
57 clone.fStructure = fStructure;
58 clone.fParentId = fParentId;
59 clone.fProjectionSourceId = fProjectionSourceId;
60 clone.fLinkIds = fLinkIds;
61 clone.fColumnCardinality = fColumnCardinality;
62 clone.fLogicalColumnIds = fLogicalColumnIds;
63 clone.fTypeChecksum = fTypeChecksum;
64 clone.fIsSoACollection = fIsSoACollection;
65 return clone;
66}
67
68std::unique_ptr<ROOT::RFieldBase>
70{
71 if (GetStructure() == ROOT::ENTupleStructure::kStreamer) {
72 auto streamerField = std::make_unique<ROOT::RStreamerField>(GetFieldName(), GetTypeName());
73 streamerField->SetOnDiskId(fFieldId);
74 return streamerField;
75 }
76
77 // The structure may be unknown if the descriptor comes from a deserialized field with an unknown structural role.
78 // For forward compatibility, we allow this case and return an InvalidField.
79 if (GetStructure() == ROOT::ENTupleStructure::kUnknown) {
80 if (options.GetReturnInvalidOnError()) {
81 auto invalidField = std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), "",
83 invalidField->SetOnDiskId(fFieldId);
84 return invalidField;
85 } else {
86 throw RException(R__FAIL("unexpected on-disk field structure value for field \"" + GetFieldName() + "\""));
87 }
88 }
89
90 // Untyped records and collections
91 if (GetTypeName().empty()) {
92 switch (GetStructure()) {
94 std::vector<std::unique_ptr<ROOT::RFieldBase>> memberFields;
95 memberFields.reserve(fLinkIds.size());
96 for (auto id : fLinkIds) {
97 const auto &memberDesc = ntplDesc.GetFieldDescriptor(id);
98 auto field = memberDesc.CreateField(ntplDesc, options);
100 return field;
101 memberFields.emplace_back(std::move(field));
102 }
103 auto recordField = std::make_unique<ROOT::RRecordField>(GetFieldName(), std::move(memberFields));
104 recordField->SetOnDiskId(fFieldId);
105 return recordField;
106 }
108 if (fLinkIds.size() != 1) {
109 throw RException(R__FAIL("unsupported untyped collection for field \"" + GetFieldName() + "\""));
110 }
111 auto itemField = ntplDesc.GetFieldDescriptor(fLinkIds[0]).CreateField(ntplDesc, options);
113 return itemField;
114 auto collectionField = ROOT::RVectorField::CreateUntyped(GetFieldName(), std::move(itemField));
115 collectionField->SetOnDiskId(fFieldId);
116 return collectionField;
117 }
118 default: throw RException(R__FAIL("unsupported untyped field structure for field \"" + GetFieldName() + "\""));
119 }
120 }
121
122 try {
123 const auto &fieldName = GetFieldName();
124 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
125 // NOTE: Unwrap() here may throw an exception, hence the try block.
126 // If options.fReturnInvalidOnError is false we just rethrow it, otherwise we return an InvalidField wrapping the
127 // error.
128 auto field = ROOT::Internal::CallFieldBaseCreate(fieldName, typeName, options, &ntplDesc, fFieldId).Unwrap();
129 field->SetOnDiskId(fFieldId);
130
131 for (auto &subfield : *field) {
132 const auto subfieldId = ntplDesc.FindFieldId(subfield.GetFieldName(), subfield.GetParent()->GetOnDiskId());
133 subfield.SetOnDiskId(subfieldId);
135 auto &invalidField = static_cast<ROOT::RInvalidField &>(subfield);
136 // A subfield being invalid "infects" its entire ancestry.
137 return invalidField.Clone(fieldName);
138 }
139 }
140
141 return field;
142 } catch (const RException &ex) {
143 if (options.GetReturnInvalidOnError())
144 return std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), ex.what(),
146 else
147 throw ex;
148 }
149}
150
151////////////////////////////////////////////////////////////////////////////////
152
154{
155 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
156 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
157 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
158 fValueRange == other.fValueRange;
159}
160
162{
163 RColumnDescriptor clone;
164 clone.fLogicalColumnId = fLogicalColumnId;
165 clone.fPhysicalColumnId = fPhysicalColumnId;
166 clone.fBitsOnStorage = fBitsOnStorage;
167 clone.fType = fType;
168 clone.fFieldId = fFieldId;
169 clone.fIndex = fIndex;
170 clone.fFirstElementIndex = fFirstElementIndex;
171 clone.fRepresentationIndex = fRepresentationIndex;
172 clone.fValueRange = fValueRange;
173 return clone;
174}
175
176////////////////////////////////////////////////////////////////////////////////
177
180{
181 if (!fCumulativeNElements) {
182 // Small range, just iterate through fPageInfos
185 for (const auto &pi : fPageInfos) {
186 if (firstInPage + pi.GetNElements() > idxInCluster) {
188 }
189 pageNumber++;
190 firstInPage += pi.GetNElements();
191 }
192 R__ASSERT(false);
193 }
194
195 const auto N = fCumulativeNElements->size();
196 R__ASSERT(N > 0);
197 R__ASSERT(N == fPageInfos.size());
198
199 std::size_t left = 0;
200 std::size_t right = N - 1;
201 std::size_t midpoint = N;
202 while (left <= right) {
203 midpoint = (left + right) / 2;
204 if ((*fCumulativeNElements)[midpoint] <= idxInCluster) {
205 left = midpoint + 1;
206 continue;
207 }
208
209 if ((midpoint == 0) || ((*fCumulativeNElements)[midpoint - 1] <= idxInCluster))
210 break;
211
212 right = midpoint - 1;
213 }
215
216 auto pageInfo = fPageInfos[midpoint];
217 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : (*fCumulativeNElements)[midpoint - 1];
219 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
221}
222
223std::size_t
226 std::size_t pageSize)
227{
228 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
229 R__ASSERT(!columnRange.IsSuppressed());
230
231 const auto nElements =
232 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
233 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
234 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
235
237 return 0U;
238 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
239
240 std::vector<RPageInfo> pageInfos;
241 // Synthesize new `RPageInfo`s as needed
242 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
246 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
249 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
250 pageInfo.SetLocator(locator);
251 pageInfos.emplace_back(pageInfo);
252 nRemainingElements -= pageInfo.GetNElements();
253 }
254
255 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
256 std::make_move_iterator(fPageInfos.end()));
257 std::swap(fPageInfos, pageInfos);
259}
260
262{
263 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
264 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
265}
266
268{
269 std::uint64_t nbytes = 0;
270 for (const auto &pr : fPageRanges) {
271 for (const auto &pi : pr.second.GetPageInfos()) {
272 nbytes += pi.GetLocator().GetNBytesOnStorage();
273 }
274 }
275 return nbytes;
276}
277
279{
280 RClusterDescriptor clone;
281 clone.fClusterId = fClusterId;
282 clone.fFirstEntryIndex = fFirstEntryIndex;
283 clone.fNEntries = fNEntries;
284 clone.fColumnRanges = fColumnRanges;
285 for (const auto &d : fPageRanges)
286 clone.fPageRanges.emplace(d.first, d.second.Clone());
287 return clone;
288}
289
290////////////////////////////////////////////////////////////////////////////////
291
293{
294 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
295}
296
298{
300 clone.fContentId = fContentId;
301 clone.fTypeVersion = fTypeVersion;
302 clone.fTypeName = fTypeName;
303 clone.fContent = fContent;
304 return clone;
305}
306
307////////////////////////////////////////////////////////////////////////////////
308
313
315{
316 // clang-format off
317 return fName == other.fName &&
318 fDescription == other.fDescription &&
319 fNEntries == other.fNEntries &&
320 fGeneration == other.fGeneration &&
321 fFieldZeroId == other.fFieldZeroId &&
322 fFieldDescriptors == other.fFieldDescriptors &&
323 fColumnDescriptors == other.fColumnDescriptors &&
324 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
325 fClusterDescriptors == other.fClusterDescriptors;
326 // clang-format on
327}
328
330{
332 for (const auto &cd : fClusterDescriptors) {
333 if (!cd.second.ContainsColumn(physicalColumnId))
334 continue;
335 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
336 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
337 }
338 return result;
339}
340
341////////////////////////////////////////////////////////////////////////////////
342/// Return the cluster boundaries for each cluster in this RNTuple.
343std::vector<ROOT::Internal::RNTupleClusterBoundaries>
345{
346 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
347 boundaries.reserve(desc.GetNClusters());
348 auto clusterId = desc.FindClusterId(0, 0);
350 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
351 R__ASSERT(clusterDesc.GetNEntries() > 0);
352 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
353 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
354 clusterId = desc.FindNextClusterId(clusterId);
355 }
356 return boundaries;
357}
358
361{
362 std::string leafName(fieldName);
363 auto posDot = leafName.find_last_of('.');
364 if (posDot != std::string::npos) {
365 auto parentName = leafName.substr(0, posDot);
366 leafName = leafName.substr(posDot + 1);
367 parentId = FindFieldId(parentName, parentId);
368 }
369 auto itrFieldDesc = fFieldDescriptors.find(parentId);
370 if (itrFieldDesc == fFieldDescriptors.end())
372 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
373 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
374 return linkId;
375 }
377}
378
380{
382 return "";
383
384 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
385 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
386 if (prefix.empty())
387 return fieldDescriptor.GetFieldName();
388 return prefix + "." + fieldDescriptor.GetFieldName();
389}
390
392{
393 R__ASSERT(fVersionEpoch == 1);
394 return fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1;
395}
396
398{
399 std::string typeName = fieldDesc.GetTypeName();
400
401 if (FieldTypeNamesMayNeedFixup()) {
402 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
403 }
404
405 return typeName;
406}
407
409{
410 return FindFieldId(fieldName, GetFieldZeroId());
411}
412
414 std::uint32_t columnIndex,
415 std::uint16_t representationIndex) const
416{
417 auto itr = fFieldDescriptors.find(fieldId);
418 if (itr == fFieldDescriptors.cend())
420 if (columnIndex >= itr->second.GetColumnCardinality())
422 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
423 if (itr->second.GetLogicalColumnIds().size() <= idx)
425 return itr->second.GetLogicalColumnIds()[idx];
426}
427
429 std::uint32_t columnIndex,
430 std::uint16_t representationIndex) const
431{
432 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
435 return GetColumnDescriptor(logicalId).GetPhysicalId();
436}
437
440{
441 if (GetNClusterGroups() == 0)
443
444 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
445
446 std::size_t cgLeft = 0;
447 std::size_t cgRight = GetNClusterGroups() - 1;
448 while (cgLeft <= cgRight) {
449 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
450 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
451 R__ASSERT(!clusterIds.empty());
452
453 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
454 // this may happen if the RNTuple has an empty schema
455 if (!clusterDesc.ContainsColumn(physicalColumnId))
457
458 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
460 // Look into the lower half of cluster groups
462 cgRight = cgMidpoint - 1;
463 continue;
464 }
465
466 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
467 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
468 // Look into the upper half of cluster groups
469 cgLeft = cgMidpoint + 1;
470 continue;
471 }
472
473 // Binary search in the current cluster group; since we already checked the element range boundaries,
474 // the element must be in that cluster group.
475 std::size_t clusterLeft = 0;
476 std::size_t clusterRight = clusterIds.size() - 1;
477 while (clusterLeft <= clusterRight) {
478 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
480 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
481
482 if (columnRange.Contains(index))
483 return clusterId;
484
485 if (columnRange.GetFirstElementIndex() > index) {
488 continue;
489 }
490
491 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
493 continue;
494 }
495 }
496 R__ASSERT(false);
497 }
499}
500
502{
503 if (GetNClusterGroups() == 0)
505
506 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
507
508 std::size_t cgLeft = 0;
509 std::size_t cgRight = GetNClusterGroups() - 1;
510 while (cgLeft <= cgRight) {
511 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
512 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
513
514 if (cgDesc.GetMinEntry() > entryIdx) {
516 cgRight = cgMidpoint - 1;
517 continue;
518 }
519
520 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
521 cgLeft = cgMidpoint + 1;
522 continue;
523 }
524
525 // Binary search in the current cluster group; since we already checked the element range boundaries,
526 // the element must be in that cluster group.
527 const auto &clusterIds = cgDesc.GetClusterIds();
528 R__ASSERT(!clusterIds.empty());
529 std::size_t clusterLeft = 0;
530 std::size_t clusterRight = clusterIds.size() - 1;
531 while (clusterLeft <= clusterRight) {
532 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
533 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
534
535 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
538 continue;
539 }
540
541 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
543 continue;
544 }
545
547 }
548 R__ASSERT(false);
549 }
551}
552
554{
555 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
556 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
557 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
558 // binary search code path remains tested.
559 const auto &clusterDesc = GetClusterDescriptor(clusterId);
560 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
561 return FindClusterId(firstEntryInNextCluster);
562}
563
565{
566 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
567 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
568 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
569 // binary search code path remains tested.
570 const auto &clusterDesc = GetClusterDescriptor(clusterId);
571 if (clusterDesc.GetFirstEntryIndex() == 0)
573 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
574}
575
576std::vector<ROOT::DescriptorId_t>
578{
579 std::vector<ROOT::DescriptorId_t> fields;
580 for (const auto fieldId : fFieldIdsOrder) {
581 if (fFieldIdsLookup.count(desc.GetFieldDescriptor(fieldId).GetParentId()) == 0)
582 fields.emplace_back(fieldId);
583 }
584 return fields;
585}
586
592
594 : fNTuple(ntuple)
595{
596 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
597
598 while (!fieldIdQueue.empty()) {
599 auto currFieldId = fieldIdQueue.front();
600 fieldIdQueue.pop_front();
601
602 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
603 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
604
605 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
606 auto fieldId = field.GetId();
607 fieldIdQueue.push_back(fieldId);
608 }
609 }
610}
611
612std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
613{
614 std::vector<std::uint64_t> result;
615 unsigned int base = 0;
616 std::uint64_t flags = 0;
617 for (auto f : fFeatureFlags) {
618 if ((f > 0) && ((f % 64) == 0))
619 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
620 while (f > base + 64) {
621 result.emplace_back(flags);
622 flags = 0;
623 base += 64;
624 }
625 // Note that in the following iterations of the outer loop over fFeatureFlags, we can never have the situation
626 // where base is larger than the feature flag, because they are stored ordered in the std::set.
627 assert(f >= base);
628 f -= base;
629 flags |= std::uint64_t(1) << f;
630 }
631 result.emplace_back(flags);
632 return result;
633}
634
636 std::vector<RClusterDescriptor> &clusterDescs)
637{
639 if (iter == fClusterGroupDescriptors.end())
640 return R__FAIL("invalid attempt to add details of unknown cluster group");
641 if (iter->second.HasClusterDetails())
642 return R__FAIL("invalid attempt to re-populate cluster group details");
643 if (iter->second.GetNClusters() != clusterDescs.size())
644 return R__FAIL("mismatch of number of clusters");
645
646 std::vector<ROOT::DescriptorId_t> clusterIds;
647 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
648 clusterIds.emplace_back(clusterDescs[i].GetId());
649 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
650 if (!success) {
651 return R__FAIL("invalid attempt to re-populate existing cluster");
652 }
653 }
655 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
656 });
658 cgBuilder.AddSortedClusters(clusterIds);
659 iter->second = cgBuilder.MoveDescriptor().Unwrap();
660 return RResult<void>::Success();
661}
662
664{
666 if (iter == fClusterGroupDescriptors.end())
667 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
668 if (!iter->second.HasClusterDetails())
669 return R__FAIL("invalid attempt to drop details of cluster group summary");
670
671 for (auto clusterId : iter->second.GetClusterIds())
673 iter->second = iter->second.CloneSummary();
674 return RResult<void>::Success();
675}
676
677std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
678{
679 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
680 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
681 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
682 for (const auto &colDesc : GetColumnIterable()) {
684 auto fieldId = colDesc.GetFieldId();
685 while (1) {
686 const auto &field = GetFieldDescriptor(fieldId);
687 if (field.GetParentId() == GetFieldZeroId())
688 break;
689 fieldId = field.GetParentId();
690 }
691 invalidFields.insert(fieldId);
692
693 // No need to look for all invalid fields if we're gonna error out anyway
694 if (!options.GetForwardCompatible())
695 break;
696 }
697 }
698
699 if (!options.GetForwardCompatible() && !invalidFields.empty())
701 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
702 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
703
704 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
705 fieldZero->SetOnDiskId(GetFieldZeroId());
706 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
707 : RNTupleModel::Create(std::move(fieldZero));
709 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
710 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
711 for (const auto &topDesc : GetTopLevelFields()) {
712 if (invalidFields.count(topDesc.GetId()) > 0) {
713 // Field contains invalid columns: skip it
714 continue;
715 }
716
717 auto field = topDesc.CreateField(*this, createFieldOpts);
718
719 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
720 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
721 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
722 // in the first place, so we don't need to check for that flag again).
723 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
724 const auto &invalid = static_cast<const RInvalidField &>(*field);
725 const auto cat = invalid.GetCategory();
727 if (mustThrow)
728 throw RException(R__FAIL(invalid.GetError()));
729
730 // Not a hard error: skip the field and go on.
731 continue;
732 }
733
734 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
735 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
736 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
737 });
738 } else {
739 model->AddField(std::move(field));
740 }
741 }
742 model->Freeze();
743 return model;
744}
745
747{
748 RNTupleDescriptor clone;
749 clone.fName = fName;
754 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
755 // and therefore not represent the actual sources's header.
756 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
757
758 for (const auto &d : fFieldDescriptors)
759 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
760 for (const auto &d : fColumnDescriptors)
761 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
762
763 for (const auto &d : fExtraTypeInfoDescriptors)
764 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
766 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
767
768 // In case we are copying the schema from a pre-1.0.0.1 RNTuple we need to patch all field type names
769 // to use the proper normalization.
771 std::vector<ROOT::DescriptorId_t> toVisit;
772 toVisit.push_back(GetFieldZeroId());
773 while (!toVisit.empty()) {
774 auto fieldId = toVisit.back();
775 toVisit.pop_back();
776 for (auto &field : clone.GetFieldIterable(fieldId)) {
778 toVisit.push_back(field.GetId());
779 }
780 }
781 }
782
783 return clone;
784}
785
787{
789
794
798 clone.fNEntries = fNEntries;
799 clone.fNClusters = fNClusters;
800 clone.fGeneration = fGeneration;
801 for (const auto &d : fClusterGroupDescriptors)
802 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
804 for (const auto &d : fClusterDescriptors)
805 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
806 for (const auto &d : fAttributeSets)
807 clone.fAttributeSets.emplace_back(d.Clone());
808 return clone;
809}
810
811////////////////////////////////////////////////////////////////////////////////
812
814{
815 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
816 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
817}
818
820{
822 clone.fClusterGroupId = fClusterGroupId;
823 clone.fPageListLocator = fPageListLocator;
824 clone.fPageListLength = fPageListLength;
825 clone.fMinEntry = fMinEntry;
826 clone.fEntrySpan = fEntrySpan;
827 clone.fNClusters = fNClusters;
828 return clone;
829}
830
832{
833 RClusterGroupDescriptor clone = CloneSummary();
834 clone.fClusterIds = fClusterIds;
835 return clone;
836}
837
838////////////////////////////////////////////////////////////////////////////////
839
842 std::uint64_t firstElementIndex,
843 std::uint32_t compressionSettings,
845{
846 if (physicalId != pageRange.fPhysicalColumnId)
847 return R__FAIL("column ID mismatch");
848 if (fCluster.fColumnRanges.count(physicalId) > 0)
849 return R__FAIL("column ID conflict");
851 for (const auto &pi : pageRange.fPageInfos) {
852 columnRange.IncrementNElements(pi.GetNElements());
853 }
854 fCluster.fPageRanges[physicalId] = pageRange.Clone();
855 fCluster.fColumnRanges[physicalId] = columnRange;
856 return RResult<void>::Success();
857}
858
861{
862 if (fCluster.fColumnRanges.count(physicalId) > 0)
863 return R__FAIL("column ID conflict");
864
866 columnRange.SetPhysicalColumnId(physicalId);
867 columnRange.SetIsSuppressed(true);
868 fCluster.fColumnRanges[physicalId] = columnRange;
869 return RResult<void>::Success();
870}
871
874{
875 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
876 if (!columnRange.IsSuppressed())
877 continue;
878 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
879
880 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
881 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
882 // We expect only few columns and column representations per field, so we do a linear search
883 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
884 const auto &otherColumnDesc = desc.GetColumnDescriptor(otherColumnLogicalId);
885 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
886 continue;
887 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
888 continue;
889
890 // Found corresponding column of a different column representation
891 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
892 if (otherColumnRange.IsSuppressed())
893 continue;
894
895 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
896 columnRange.SetNElements(otherColumnRange.GetNElements());
897 break;
898 }
899
900 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
901 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
902 std::to_string(columnRange.GetPhysicalColumnId()) +
903 ", cluster ID: " + std::to_string(fCluster.GetId()));
904 }
905 }
906 return RResult<void>::Success();
907}
908
911{
912 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
913 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
914 /// parent field in the hierarchy.
916 const auto &visitField, const auto &enterSubtree) -> void {
918 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
919 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
921 }
922 };
923
924 // Extended columns can only be part of the header extension
925 if (!desc.GetHeaderExtension())
926 return *this;
927
928 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
929 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
930 for (const auto &topLevelField : desc.GetTopLevelFields()) {
932 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
933 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
934 for (const auto &c : desc.GetColumnIterable(fieldId)) {
935 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
936 auto &columnRange = fCluster.fColumnRanges[physicalId];
937
938 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
939 // extension won't have on-disk metadata for the clusters that were already committed before the model
940 // was extended. Therefore, these need to be synthetically initialized upon reading.
941 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
942 columnRange.SetPhysicalColumnId(physicalId);
943 columnRange.SetFirstElementIndex(0);
944 columnRange.SetNElements(0);
945 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
946 }
947 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
948 // number of elements should have been if the column was not deferred; fix those and let
949 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
950 if (c.IsDeferredColumn()) {
951 if (c.GetRepresentationIndex() == 0) {
952 // Note that a deferred column (i.e, whose first element index is > 0) for the 0th representation
953 // index already met the criteria of `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a
954 // principal column reachable from the field zero excluding subfields of collection and variant
955 // fields.
956 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
957 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
958 } else {
959 // Deferred representations which are not the first cannot count on the number of elements being
960 // equal to Entries * nRepetitions because they might have been added in a later cluster. But they
961 // can rely on the first representation having the correct FirstElement/NElements (by definition
962 // the first representation cannot be an "extended" one), therefore they can just copy the value
963 // from it.
964 const auto &field = desc.GetFieldDescriptor(fieldId);
965 const auto firstReprColumnId = field.GetLogicalColumnIds()[c.GetIndex()];
966 const auto &firstReprColumnRange = fCluster.fColumnRanges[firstReprColumnId];
967 columnRange.SetFirstElementIndex(firstReprColumnRange.GetFirstElementIndex());
968 columnRange.SetNElements(firstReprColumnRange.GetNElements());
969 }
970 if (!columnRange.IsSuppressed()) {
971 auto &pageRange = fCluster.fPageRanges[physicalId];
972 pageRange.fPhysicalColumnId = physicalId;
973 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
974 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
975 }
976 } else if (!columnRange.IsSuppressed()) {
977 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
978 }
979 }
980 },
982 }
983 return *this;
984}
985
987{
988 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
989 return R__FAIL("unset cluster ID");
990 if (fCluster.fNEntries == 0)
991 return R__FAIL("empty cluster");
992 for (auto &pr : fCluster.fPageRanges) {
993 if (fCluster.fColumnRanges.count(pr.first) == 0) {
994 return R__FAIL("missing column range");
995 }
996 pr.second.fCumulativeNElements.reset();
997 const auto nPages = pr.second.fPageInfos.size();
999 pr.second.fCumulativeNElements = std::make_unique<std::vector<NTupleSize_t>>();
1000 pr.second.fCumulativeNElements->reserve(nPages);
1002 for (const auto &pi : pr.second.fPageInfos) {
1003 sum += pi.GetNElements();
1004 pr.second.fCumulativeNElements->emplace_back(sum);
1005 }
1006 }
1007 }
1009 std::swap(result, fCluster);
1010 return result;
1011}
1012
1013////////////////////////////////////////////////////////////////////////////////
1014
1017{
1019 builder.ClusterGroupId(clusterGroupDesc.GetId())
1020 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1021 .PageListLength(clusterGroupDesc.GetPageListLength())
1022 .MinEntry(clusterGroupDesc.GetMinEntry())
1023 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1024 .NClusters(clusterGroupDesc.GetNClusters());
1025 return builder;
1026}
1027
1029{
1030 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1031 return R__FAIL("unset cluster group ID");
1033 std::swap(result, fClusterGroup);
1034 return result;
1035}
1036
1037////////////////////////////////////////////////////////////////////////////////
1038
1040{
1041 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1042 throw RException(R__FAIL("invalid extra type info content id"));
1044 std::swap(result, fExtraTypeInfo);
1045 return result;
1046}
1047
1048////////////////////////////////////////////////////////////////////////////////
1049
1051{
1052 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1053 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1054 return RResult<void>::Success();
1055}
1056
1058{
1059 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1060 return R__FAIL("unset or unsupported RNTuple epoch version");
1061 }
1062
1063 // Reuse field name validity check
1064 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1065 if (!validName) {
1067 }
1068
1069 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1070 // parent not properly set?
1071 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1072 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1073 }
1074
1075 // Same number of columns in every column representation?
1076 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1077 if (columnCardinality == 0)
1078 continue;
1079
1080 // In AddColumn, we already checked that all but the last representation are complete.
1081 // Check that the last column representation is complete, i.e. has all columns.
1082 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1083 const auto nColumns = logicalColumnIds.size();
1084 // If we have only a single column representation, the following condition is true by construction
1085 if ((nColumns + 1) == columnCardinality)
1086 continue;
1087
1088 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1089 if (lastColumn.GetIndex() + 1 != columnCardinality)
1090 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1091 }
1092
1093 return RResult<void>::Success();
1094}
1095
1097{
1098 EnsureValidDescriptor().ThrowOnError();
1099 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1100 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1101 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1102 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1104 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1105 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1106 });
1108 std::swap(result, fDescriptor);
1109 return result;
1110}
1111
1113 std::uint16_t versionMinor, std::uint16_t versionPatch)
1114{
1116 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1117 }
1118 fDescriptor.fVersionEpoch = versionEpoch;
1119 fDescriptor.fVersionMajor = versionMajor;
1120 fDescriptor.fVersionMinor = versionMinor;
1121 fDescriptor.fVersionPatch = versionPatch;
1122}
1123
1125{
1126 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1127 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1128 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1129 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1130}
1131
1133{
1134 fDescriptor.fName = std::string(name);
1135 fDescriptor.fDescription = std::string(description);
1136}
1137
1139{
1140 if (flag > 0 && flag % 64 == 0)
1141 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1142 fDescriptor.fFeatureFlags.insert(flag);
1143}
1144
1147{
1148 if (fDesc.fName.empty())
1149 return R__FAIL("attribute set name cannot be empty");
1150 if (fDesc.fAnchorLength == 0)
1151 return R__FAIL("invalid anchor length");
1152 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1153 return R__FAIL("invalid locator type");
1154
1155 return std::move(fDesc);
1156}
1157
1159{
1160 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1161 return R__FAIL("invalid logical column id");
1162 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1163 return R__FAIL("invalid physical column id");
1164 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1165 return R__FAIL("invalid field id, dangling column");
1166
1167 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1168 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1169 // as we have no idea what the valid range is.
1170 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1171 // we try to read the actual data contained in it.
1172 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1173 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1174 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1175 return R__FAIL("invalid column bit width");
1176 }
1177
1178 return fColumn.Clone();
1179}
1180
1183{
1185 fieldDesc.FieldVersion(field.GetFieldVersion())
1186 .TypeVersion(field.GetTypeVersion())
1187 .FieldName(field.GetFieldName())
1188 .FieldDescription(field.GetDescription())
1189 .TypeName(field.GetTypeName())
1190 .TypeAlias(field.GetTypeAlias())
1191 .Structure(field.GetStructure())
1192 .NRepetitions(field.GetNRepetitions());
1194 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1195 if (field.GetTraits() & ROOT::RFieldBase::kTraitSoACollection) {
1196 assert(field.GetStructure() == ENTupleStructure::kCollection);
1197 fieldDesc.IsSoACollection(true);
1198 }
1199 return fieldDesc;
1200}
1201
1203{
1204 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1205 return R__FAIL("invalid field id");
1206 }
1207 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1208 return R__FAIL("invalid field structure");
1209 }
1210 if (fField.IsSoACollection() && (fField.GetStructure() != ROOT::ENTupleStructure::kCollection)) {
1211 return R__FAIL("invalid SoA flag on non-collection field");
1212 }
1213 // FieldZero is usually named "" and would be a false positive here
1214 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1215 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1216 if (!validName) {
1218 }
1219 if (fField.GetFieldName().empty()) {
1220 return R__FAIL("name cannot be empty string \"\"");
1221 }
1222 }
1223 return fField.Clone();
1224}
1225
1227{
1228 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1229 if (fDescriptor.fHeaderExtension)
1230 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1231 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1232 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1233 }
1234}
1235
1238{
1240 if (!(fieldExists = EnsureFieldExists(fieldId)))
1242 if (!(fieldExists = EnsureFieldExists(linkId)))
1243 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1244
1245 if (linkId == fDescriptor.GetFieldZeroId()) {
1246 return R__FAIL("cannot make FieldZero a child field");
1247 }
1248 // fail if field already has another valid parent
1249 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1251 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1252 }
1253 if (fieldId == linkId) {
1254 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1255 }
1256 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1257 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1258 return RResult<void>::Success();
1259}
1260
1263{
1265 if (!(fieldExists = EnsureFieldExists(sourceId)))
1267 if (!(fieldExists = EnsureFieldExists(targetId)))
1268 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1269
1270 if (targetId == fDescriptor.GetFieldZeroId()) {
1271 return R__FAIL("cannot make FieldZero a projected field");
1272 }
1273 if (sourceId == targetId) {
1274 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1275 }
1276 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1277 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1278 }
1279 // fail if target field already has another valid projection source
1280 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1281 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1282 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1283 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1284 }
1285 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1286 return RResult<void>::Success();
1287}
1288
1290{
1291 const auto fieldId = columnDesc.GetFieldId();
1292 const auto columnIndex = columnDesc.GetIndex();
1293 const auto representationIndex = columnDesc.GetRepresentationIndex();
1294
1295 auto fieldExists = EnsureFieldExists(fieldId);
1296 if (!fieldExists) {
1298 }
1299 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1300
1301 if (columnDesc.IsAliasColumn()) {
1302 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1303 return R__FAIL("alias column type mismatch");
1304 }
1305 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1306 return R__FAIL("column index clash");
1307 }
1308 if (columnIndex > 0) {
1309 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1310 return R__FAIL("out of bounds column index");
1311 }
1312 if (representationIndex > 0) {
1313 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1314 return R__FAIL("out of bounds representation index");
1315 }
1316 if (columnIndex == 0) {
1317 assert(fieldDesc.fColumnCardinality > 0);
1318 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1320 return R__FAIL("incomplete column representations");
1321 }
1322 } else {
1323 if (columnIndex >= fieldDesc.fColumnCardinality)
1324 return R__FAIL("irregular column representations");
1325 }
1326 } else {
1327 // This will set the column cardinality to the number of columns of the first representation
1328 fieldDesc.fColumnCardinality = columnIndex + 1;
1329 }
1330
1331 const auto logicalId = columnDesc.GetLogicalId();
1332 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1333
1334 if (!columnDesc.IsAliasColumn())
1335 fDescriptor.fNPhysicalColumns++;
1336 if (fDescriptor.fHeaderExtension)
1337 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1338 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1339
1340 return RResult<void>::Success();
1341}
1342
1344{
1345 const auto id = clusterGroup.GetId();
1346 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1347 return R__FAIL("cluster group id clash");
1348 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1349 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1350 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1351 return RResult<void>::Success();
1352}
1353
1355{
1356 fDescriptor = descriptor.CloneSchema();
1357}
1358
1360{
1361 if (!fDescriptor.fHeaderExtension)
1362 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1363}
1364
1366{
1367 if (fDescriptor.GetNLogicalColumns() == 0)
1368 return;
1369 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1370
1371 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1372 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1373 R__ASSERT(c.IsAliasColumn());
1374 R__ASSERT(id == c.GetLogicalId());
1375 fDescriptor.fColumnDescriptors.erase(id);
1376 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1377 if (link == c.fLogicalColumnId) {
1378 link += offset;
1379 break;
1380 }
1381 }
1382 c.fLogicalColumnId += offset;
1383 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1384 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1385 }
1386
1387 // Patch up column ids in the header extension
1388 if (auto &xHeader = fDescriptor.fHeaderExtension) {
1389 for (auto &columnId : xHeader->fExtendedColumnRepresentations) {
1390 if (columnId >= fDescriptor.GetNPhysicalColumns())
1391 columnId += offset;
1392 }
1393 }
1394}
1395
1397{
1398 auto clusterId = clusterDesc.GetId();
1399 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1400 return R__FAIL("cluster id clash");
1401 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1402 return RResult<void>::Success();
1403}
1404
1407{
1408 // Make sure we have no duplicates
1409 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1410 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1411 return R__FAIL("extra type info duplicates");
1412 }
1413 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1414 return RResult<void>::Success();
1415}
1416
1418{
1419 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1421 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1422 *it = std::move(extraTypeInfoDesc);
1423 else
1424 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1425}
1426
1429{
1430 auto &attrSets = fDescriptor.fAttributeSets;
1431 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1432 return desc.GetName() == name;
1433 }) != attrSets.end()) {
1434 return R__FAIL("attribute sets with duplicate names");
1435 }
1436 attrSets.push_back(std::move(attrSetDesc));
1437 return RResult<void>::Success();
1438}
1439
1444
1450
1457
1463
1470
1475
1481
1486
1492
1498
1503
1508
1513
1518
1520{
1521 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1522 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1523 fName == other.fName;
1524};
1525
1527{
1529 desc.fAnchorLength = fAnchorLength;
1530 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1531 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1532 desc.fAnchorLocator = fAnchorLocator;
1533 desc.fName = fName;
1534 return desc;
1535}
1536
1538{
1539 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1540 return false;
1541 if (fieldDesc.GetTypeName().rfind("std::", 0) == 0)
1542 return false;
1543
1544 auto subFieldId = desc.FindFieldId("_0", fieldDesc.GetId());
1546 return false;
1547
1548 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
1549 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
1550 "std::int64_t", "std::uint64_t"};
1551 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
1552 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
1553}
1554
1556{
1557 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1558 return false;
1559 return (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0);
1560}
1561
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:326
#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:322
#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:130
#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:142
#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
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(std::string_view name, 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
void SetFeature(unsigned int flag)
Sets the flag-th bit of the feature flag to 1.
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:78
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 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.
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:96
@ 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.
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.
ROOT::DescriptorId_t FindPhysicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
RExtraTypeInfoDescriptorIterable GetExtraTypeInfoIterable() const
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.
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:222
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:2335