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