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 if (!fCumulativeNElements) {
212 // Small range, just iterate through fPageInfos
215 for (const auto &pi : fPageInfos) {
216 if (firstInPage + pi.GetNElements() > idxInCluster) {
218 }
219 pageNumber++;
220 firstInPage += pi.GetNElements();
221 }
222 R__ASSERT(false);
223 }
224
225 const auto N = fCumulativeNElements->size();
226 R__ASSERT(N > 0);
227 R__ASSERT(N == fPageInfos.size());
228
229 std::size_t left = 0;
230 std::size_t right = N - 1;
231 std::size_t midpoint = N;
232 while (left <= right) {
233 midpoint = (left + right) / 2;
234 if ((*fCumulativeNElements)[midpoint] <= idxInCluster) {
235 left = midpoint + 1;
236 continue;
237 }
238
239 if ((midpoint == 0) || ((*fCumulativeNElements)[midpoint - 1] <= idxInCluster))
240 break;
241
242 right = midpoint - 1;
243 }
245
246 auto pageInfo = fPageInfos[midpoint];
247 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : (*fCumulativeNElements)[midpoint - 1];
249 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
251}
252
253std::size_t
256 std::size_t pageSize)
257{
258 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
259 R__ASSERT(!columnRange.IsSuppressed());
260
261 const auto nElements =
262 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
263 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
264 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
265
267 return 0U;
268 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
269
270 std::vector<RPageInfo> pageInfos;
271 // Synthesize new `RPageInfo`s as needed
272 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
276 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
279 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
280 pageInfo.SetLocator(locator);
281 pageInfos.emplace_back(pageInfo);
282 nRemainingElements -= pageInfo.GetNElements();
283 }
284
285 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
286 std::make_move_iterator(fPageInfos.end()));
287 std::swap(fPageInfos, pageInfos);
289}
290
292{
293 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
294 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
295}
296
298{
299 std::uint64_t nbytes = 0;
300 for (const auto &pr : fPageRanges) {
301 for (const auto &pi : pr.second.GetPageInfos()) {
302 nbytes += pi.GetLocator().GetNBytesOnStorage();
303 }
304 }
305 return nbytes;
306}
307
309{
310 RClusterDescriptor clone;
311 clone.fClusterId = fClusterId;
312 clone.fFirstEntryIndex = fFirstEntryIndex;
313 clone.fNEntries = fNEntries;
314 clone.fColumnRanges = fColumnRanges;
315 for (const auto &d : fPageRanges)
316 clone.fPageRanges.emplace(d.first, d.second.Clone());
317 return clone;
318}
319
320////////////////////////////////////////////////////////////////////////////////
321
323{
324 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
325}
326
328{
330 clone.fContentId = fContentId;
331 clone.fTypeVersion = fTypeVersion;
332 clone.fTypeName = fTypeName;
333 clone.fContent = fContent;
334 return clone;
335}
336
337////////////////////////////////////////////////////////////////////////////////
338
343
345{
346 // clang-format off
347 return fName == other.fName &&
348 fDescription == other.fDescription &&
349 fNEntries == other.fNEntries &&
350 fGeneration == other.fGeneration &&
351 fFieldZeroId == other.fFieldZeroId &&
352 fFieldDescriptors == other.fFieldDescriptors &&
353 fColumnDescriptors == other.fColumnDescriptors &&
354 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
355 fClusterDescriptors == other.fClusterDescriptors;
356 // clang-format on
357}
358
360{
362 for (const auto &cd : fClusterDescriptors) {
363 if (!cd.second.ContainsColumn(physicalColumnId))
364 continue;
365 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
366 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
367 }
368 return result;
369}
370
371////////////////////////////////////////////////////////////////////////////////
372/// Return the cluster boundaries for each cluster in this RNTuple.
373std::vector<ROOT::Internal::RNTupleClusterBoundaries>
375{
376 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
377 boundaries.reserve(desc.GetNClusters());
378 auto clusterId = desc.FindClusterId(0, 0);
380 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
381 R__ASSERT(clusterDesc.GetNEntries() > 0);
382 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
383 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
385 }
386 return boundaries;
387}
388
391{
392 std::string leafName(fieldName);
393 auto posDot = leafName.find_last_of('.');
394 if (posDot != std::string::npos) {
395 auto parentName = leafName.substr(0, posDot);
396 leafName = leafName.substr(posDot + 1);
397 parentId = FindFieldId(parentName, parentId);
398 }
399 auto itrFieldDesc = fFieldDescriptors.find(parentId);
400 if (itrFieldDesc == fFieldDescriptors.end())
402 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
403 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
404 return linkId;
405 }
407}
408
410{
412 return "";
413
414 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
415 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
416 if (prefix.empty())
417 return fieldDescriptor.GetFieldName();
418 return prefix + "." + fieldDescriptor.GetFieldName();
419}
420
422{
423 std::string typeName = fieldDesc.GetTypeName();
424
425 // ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
426 R__ASSERT(fVersionEpoch == 1);
427 if (fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1) {
428 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
429 }
430
431 return typeName;
432}
433
435{
436 return FindFieldId(fieldName, GetFieldZeroId());
437}
438
440 std::uint32_t columnIndex,
441 std::uint16_t representationIndex) const
442{
443 auto itr = fFieldDescriptors.find(fieldId);
444 if (itr == fFieldDescriptors.cend())
446 if (columnIndex >= itr->second.GetColumnCardinality())
448 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
449 if (itr->second.GetLogicalColumnIds().size() <= idx)
451 return itr->second.GetLogicalColumnIds()[idx];
452}
453
455 std::uint32_t columnIndex,
456 std::uint16_t representationIndex) const
457{
458 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
461 return GetColumnDescriptor(logicalId).GetPhysicalId();
462}
463
466{
467 if (GetNClusterGroups() == 0)
469
470 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
471
472 std::size_t cgLeft = 0;
473 std::size_t cgRight = GetNClusterGroups() - 1;
474 while (cgLeft <= cgRight) {
475 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
476 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
477 R__ASSERT(!clusterIds.empty());
478
479 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
480 // this may happen if the RNTuple has an empty schema
481 if (!clusterDesc.ContainsColumn(physicalColumnId))
483
484 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
486 // Look into the lower half of cluster groups
488 cgRight = cgMidpoint - 1;
489 continue;
490 }
491
492 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
493 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
494 // Look into the upper half of cluster groups
495 cgLeft = cgMidpoint + 1;
496 continue;
497 }
498
499 // Binary search in the current cluster group; since we already checked the element range boundaries,
500 // the element must be in that cluster group.
501 std::size_t clusterLeft = 0;
502 std::size_t clusterRight = clusterIds.size() - 1;
503 while (clusterLeft <= clusterRight) {
504 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
506 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
507
508 if (columnRange.Contains(index))
509 return clusterId;
510
511 if (columnRange.GetFirstElementIndex() > index) {
514 continue;
515 }
516
517 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
519 continue;
520 }
521 }
522 R__ASSERT(false);
523 }
525}
526
528{
529 if (GetNClusterGroups() == 0)
531
532 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
533
534 std::size_t cgLeft = 0;
535 std::size_t cgRight = GetNClusterGroups() - 1;
536 while (cgLeft <= cgRight) {
537 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
538 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
539
540 if (cgDesc.GetMinEntry() > entryIdx) {
542 cgRight = cgMidpoint - 1;
543 continue;
544 }
545
546 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
547 cgLeft = cgMidpoint + 1;
548 continue;
549 }
550
551 // Binary search in the current cluster group; since we already checked the element range boundaries,
552 // the element must be in that cluster group.
553 const auto &clusterIds = cgDesc.GetClusterIds();
554 R__ASSERT(!clusterIds.empty());
555 std::size_t clusterLeft = 0;
556 std::size_t clusterRight = clusterIds.size() - 1;
557 while (clusterLeft <= clusterRight) {
558 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
559 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
560
561 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
564 continue;
565 }
566
567 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
569 continue;
570 }
571
573 }
574 R__ASSERT(false);
575 }
577}
578
580{
581 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
582 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
583 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
584 // binary search code path remains tested.
585 const auto &clusterDesc = GetClusterDescriptor(clusterId);
586 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
587 return FindClusterId(firstEntryInNextCluster);
588}
589
591{
592 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
593 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
594 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
595 // binary search code path remains tested.
596 const auto &clusterDesc = GetClusterDescriptor(clusterId);
597 if (clusterDesc.GetFirstEntryIndex() == 0)
599 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
600}
601
602std::vector<ROOT::DescriptorId_t>
604{
605 std::vector<ROOT::DescriptorId_t> fields;
606 for (const auto fieldId : fFieldIdsOrder) {
607 if (fFieldIdsLookup.count(desc.GetFieldDescriptor(fieldId).GetParentId()) == 0)
608 fields.emplace_back(fieldId);
609 }
610 return fields;
611}
612
618
620 : fNTuple(ntuple)
621{
622 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
623
624 while (!fieldIdQueue.empty()) {
625 auto currFieldId = fieldIdQueue.front();
626 fieldIdQueue.pop_front();
627
628 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
629 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
630
631 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
632 auto fieldId = field.GetId();
633 fieldIdQueue.push_back(fieldId);
634 }
635 }
636}
637
638std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
639{
640 std::vector<std::uint64_t> result;
641 unsigned int base = 0;
642 std::uint64_t flags = 0;
643 for (auto f : fFeatureFlags) {
644 if ((f > 0) && ((f % 64) == 0))
645 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
646 while (f > base + 64) {
647 result.emplace_back(flags);
648 flags = 0;
649 base += 64;
650 }
651 f -= base;
652 flags |= 1 << f;
653 }
654 result.emplace_back(flags);
655 return result;
656}
657
659 std::vector<RClusterDescriptor> &clusterDescs)
660{
662 if (iter == fClusterGroupDescriptors.end())
663 return R__FAIL("invalid attempt to add details of unknown cluster group");
664 if (iter->second.HasClusterDetails())
665 return R__FAIL("invalid attempt to re-populate cluster group details");
666 if (iter->second.GetNClusters() != clusterDescs.size())
667 return R__FAIL("mismatch of number of clusters");
668
669 std::vector<ROOT::DescriptorId_t> clusterIds;
670 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
671 clusterIds.emplace_back(clusterDescs[i].GetId());
672 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
673 if (!success) {
674 return R__FAIL("invalid attempt to re-populate existing cluster");
675 }
676 }
678 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
679 });
681 cgBuilder.AddSortedClusters(clusterIds);
682 iter->second = cgBuilder.MoveDescriptor().Unwrap();
683 return RResult<void>::Success();
684}
685
687{
689 if (iter == fClusterGroupDescriptors.end())
690 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
691 if (!iter->second.HasClusterDetails())
692 return R__FAIL("invalid attempt to drop details of cluster group summary");
693
694 for (auto clusterId : iter->second.GetClusterIds())
696 iter->second = iter->second.CloneSummary();
697 return RResult<void>::Success();
698}
699
700std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
701{
702 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
703 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
704 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
705 for (const auto &colDesc : GetColumnIterable()) {
707 auto fieldId = colDesc.GetFieldId();
708 while (1) {
709 const auto &field = GetFieldDescriptor(fieldId);
710 if (field.GetParentId() == GetFieldZeroId())
711 break;
712 fieldId = field.GetParentId();
713 }
714 invalidFields.insert(fieldId);
715
716 // No need to look for all invalid fields if we're gonna error out anyway
717 if (!options.GetForwardCompatible())
718 break;
719 }
720 }
721
722 if (!options.GetForwardCompatible() && !invalidFields.empty())
724 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
725 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
726
727 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
728 fieldZero->SetOnDiskId(GetFieldZeroId());
729 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
730 : RNTupleModel::Create(std::move(fieldZero));
732 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
733 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
734 for (const auto &topDesc : GetTopLevelFields()) {
735 if (invalidFields.count(topDesc.GetId()) > 0) {
736 // Field contains invalid columns: skip it
737 continue;
738 }
739
740 auto field = topDesc.CreateField(*this, createFieldOpts);
741
742 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
743 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
744 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
745 // in the first place, so we don't need to check for that flag again).
746 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
747 const auto &invalid = static_cast<const RInvalidField &>(*field);
748 const auto cat = invalid.GetCategory();
750 if (mustThrow)
751 throw invalid.GetError();
752
753 // Not a hard error: skip the field and go on.
754 continue;
755 }
756
757 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
758 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
759 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
760 });
761 } else {
762 model->AddField(std::move(field));
763 }
764 }
765 model->Freeze();
766 return model;
767}
768
770{
771 RNTupleDescriptor clone;
772 clone.fName = fName;
777 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
778 // and therefore not represent the actual sources's header.
779 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
780
781 for (const auto &d : fFieldDescriptors)
782 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
783 for (const auto &d : fColumnDescriptors)
784 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
785
786 for (const auto &d : fExtraTypeInfoDescriptors)
787 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
789 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
790
791 return clone;
792}
793
795{
797
802
806 clone.fNEntries = fNEntries;
807 clone.fNClusters = fNClusters;
808 clone.fGeneration = fGeneration;
809 for (const auto &d : fClusterGroupDescriptors)
810 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
812 for (const auto &d : fClusterDescriptors)
813 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
814 for (const auto &d : fAttributeSets)
815 clone.fAttributeSets.emplace_back(d.Clone());
816 return clone;
817}
818
819////////////////////////////////////////////////////////////////////////////////
820
822{
823 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
824 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
825}
826
828{
830 clone.fClusterGroupId = fClusterGroupId;
831 clone.fPageListLocator = fPageListLocator;
832 clone.fPageListLength = fPageListLength;
833 clone.fMinEntry = fMinEntry;
834 clone.fEntrySpan = fEntrySpan;
835 clone.fNClusters = fNClusters;
836 return clone;
837}
838
840{
841 RClusterGroupDescriptor clone = CloneSummary();
842 clone.fClusterIds = fClusterIds;
843 return clone;
844}
845
846////////////////////////////////////////////////////////////////////////////////
847
850 std::uint64_t firstElementIndex,
851 std::uint32_t compressionSettings,
853{
854 if (physicalId != pageRange.fPhysicalColumnId)
855 return R__FAIL("column ID mismatch");
856 if (fCluster.fColumnRanges.count(physicalId) > 0)
857 return R__FAIL("column ID conflict");
859 for (const auto &pi : pageRange.fPageInfos) {
860 columnRange.IncrementNElements(pi.GetNElements());
861 }
862 fCluster.fPageRanges[physicalId] = pageRange.Clone();
863 fCluster.fColumnRanges[physicalId] = columnRange;
864 return RResult<void>::Success();
865}
866
869{
870 if (fCluster.fColumnRanges.count(physicalId) > 0)
871 return R__FAIL("column ID conflict");
872
874 columnRange.SetPhysicalColumnId(physicalId);
875 columnRange.SetIsSuppressed(true);
876 fCluster.fColumnRanges[physicalId] = columnRange;
877 return RResult<void>::Success();
878}
879
882{
883 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
884 if (!columnRange.IsSuppressed())
885 continue;
886 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
887
888 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
889 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
890 // We expect only few columns and column representations per field, so we do a linear search
891 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
893 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
894 continue;
895 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
896 continue;
897
898 // Found corresponding column of a different column representation
899 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
900 if (otherColumnRange.IsSuppressed())
901 continue;
902
903 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
904 columnRange.SetNElements(otherColumnRange.GetNElements());
905 break;
906 }
907
908 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
909 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
910 std::to_string(columnRange.GetPhysicalColumnId()) +
911 ", cluster ID: " + std::to_string(fCluster.GetId()));
912 }
913 }
914 return RResult<void>::Success();
915}
916
919{
920 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
921 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
922 /// parent field in the hierarchy.
924 const auto &visitField, const auto &enterSubtree) -> void {
926 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
927 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
929 }
930 };
931
932 // Extended columns can only be part of the header extension
933 if (!desc.GetHeaderExtension())
934 return *this;
935
936 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
937 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
938 for (const auto &topLevelField : desc.GetTopLevelFields()) {
940 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
941 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
942 for (const auto &c : desc.GetColumnIterable(fieldId)) {
943 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
944 auto &columnRange = fCluster.fColumnRanges[physicalId];
945
946 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
947 // extension won't have on-disk metadata for the clusters that were already committed before the model
948 // was extended. Therefore, these need to be synthetically initialized upon reading.
949 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
950 columnRange.SetPhysicalColumnId(physicalId);
951 columnRange.SetFirstElementIndex(0);
952 columnRange.SetNElements(0);
953 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
954 }
955 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
956 // number of elements should have been if the column was not deferred; fix those and let
957 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
958 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
959 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
960 // field zero excluding subfields of collection and variant fields.
961 if (c.IsDeferredColumn()) {
962 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
963 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
964 if (!columnRange.IsSuppressed()) {
965 auto &pageRange = fCluster.fPageRanges[physicalId];
966 pageRange.fPhysicalColumnId = physicalId;
967 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
968 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
969 }
970 } else if (!columnRange.IsSuppressed()) {
971 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
972 }
973 }
974 },
976 }
977 return *this;
978}
979
981{
982 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
983 return R__FAIL("unset cluster ID");
984 if (fCluster.fNEntries == 0)
985 return R__FAIL("empty cluster");
986 for (auto &pr : fCluster.fPageRanges) {
987 if (fCluster.fColumnRanges.count(pr.first) == 0) {
988 return R__FAIL("missing column range");
989 }
990 pr.second.fCumulativeNElements.reset();
991 const auto nPages = pr.second.fPageInfos.size();
993 pr.second.fCumulativeNElements = std::make_unique<std::vector<NTupleSize_t>>();
994 pr.second.fCumulativeNElements->reserve(nPages);
996 for (const auto &pi : pr.second.fPageInfos) {
997 sum += pi.GetNElements();
998 pr.second.fCumulativeNElements->emplace_back(sum);
999 }
1000 }
1001 }
1003 std::swap(result, fCluster);
1004 return result;
1005}
1006
1007////////////////////////////////////////////////////////////////////////////////
1008
1011{
1013 builder.ClusterGroupId(clusterGroupDesc.GetId())
1014 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1015 .PageListLength(clusterGroupDesc.GetPageListLength())
1016 .MinEntry(clusterGroupDesc.GetMinEntry())
1017 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1018 .NClusters(clusterGroupDesc.GetNClusters());
1019 return builder;
1020}
1021
1023{
1024 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1025 return R__FAIL("unset cluster group ID");
1027 std::swap(result, fClusterGroup);
1028 return result;
1029}
1030
1031////////////////////////////////////////////////////////////////////////////////
1032
1034{
1035 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1036 throw RException(R__FAIL("invalid extra type info content id"));
1038 std::swap(result, fExtraTypeInfo);
1039 return result;
1040}
1041
1042////////////////////////////////////////////////////////////////////////////////
1043
1045{
1046 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1047 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1048 return RResult<void>::Success();
1049}
1050
1052{
1053 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1054 return R__FAIL("unset or unsupported RNTuple epoch version");
1055 }
1056
1057 // Reuse field name validity check
1058 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1059 if (!validName) {
1061 }
1062
1063 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1064 // parent not properly set?
1065 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1066 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1067 }
1068
1069 // Same number of columns in every column representation?
1070 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1071 if (columnCardinality == 0)
1072 continue;
1073
1074 // In AddColumn, we already checked that all but the last representation are complete.
1075 // Check that the last column representation is complete, i.e. has all columns.
1076 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1077 const auto nColumns = logicalColumnIds.size();
1078 // If we have only a single column representation, the following condition is true by construction
1079 if ((nColumns + 1) == columnCardinality)
1080 continue;
1081
1082 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1083 if (lastColumn.GetIndex() + 1 != columnCardinality)
1084 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1085 }
1086
1087 return RResult<void>::Success();
1088}
1089
1091{
1092 EnsureValidDescriptor().ThrowOnError();
1093 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1094 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1095 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1096 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1098 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1099 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1100 });
1102 std::swap(result, fDescriptor);
1103 return result;
1104}
1105
1107 std::uint16_t versionMinor, std::uint16_t versionPatch)
1108{
1110 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1111 }
1112 fDescriptor.fVersionEpoch = versionEpoch;
1113 fDescriptor.fVersionMajor = versionMajor;
1114 fDescriptor.fVersionMinor = versionMinor;
1115 fDescriptor.fVersionPatch = versionPatch;
1116}
1117
1119{
1120 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1121 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1122 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1123 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1124}
1125
1127 const std::string_view description)
1128{
1129 fDescriptor.fName = std::string(name);
1130 fDescriptor.fDescription = std::string(description);
1131}
1132
1134{
1135 if (flag % 64 == 0)
1136 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1137 fDescriptor.fFeatureFlags.insert(flag);
1138}
1139
1142{
1143 if (fDesc.fName.empty())
1144 return R__FAIL("attribute set name cannot be empty");
1145 if (fDesc.fAnchorLength == 0)
1146 return R__FAIL("invalid anchor length");
1147 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1148 return R__FAIL("invalid locator type");
1149
1150 return std::move(fDesc);
1151}
1152
1154{
1155 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1156 return R__FAIL("invalid logical column id");
1157 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1158 return R__FAIL("invalid physical column id");
1159 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1160 return R__FAIL("invalid field id, dangling column");
1161
1162 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1163 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1164 // as we have no idea what the valid range is.
1165 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1166 // we try to read the actual data contained in it.
1167 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1168 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1169 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1170 return R__FAIL("invalid column bit width");
1171 }
1172
1173 return fColumn.Clone();
1174}
1175
1178{
1180 fieldDesc.FieldVersion(field.GetFieldVersion())
1181 .TypeVersion(field.GetTypeVersion())
1182 .FieldName(field.GetFieldName())
1183 .FieldDescription(field.GetDescription())
1184 .TypeName(field.GetTypeName())
1185 .TypeAlias(field.GetTypeAlias())
1186 .Structure(field.GetStructure())
1187 .NRepetitions(field.GetNRepetitions());
1189 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1190 return fieldDesc;
1191}
1192
1194{
1195 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1196 return R__FAIL("invalid field id");
1197 }
1198 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1199 return R__FAIL("invalid field structure");
1200 }
1201 // FieldZero is usually named "" and would be a false positive here
1202 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1203 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1204 if (!validName) {
1206 }
1207 if (fField.GetFieldName().empty()) {
1208 return R__FAIL("name cannot be empty string \"\"");
1209 }
1210 }
1211 return fField.Clone();
1212}
1213
1215{
1216 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1217 if (fDescriptor.fHeaderExtension)
1218 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1219 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1220 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1221 }
1222}
1223
1226{
1228 if (!(fieldExists = EnsureFieldExists(fieldId)))
1230 if (!(fieldExists = EnsureFieldExists(linkId)))
1231 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1232
1233 if (linkId == fDescriptor.GetFieldZeroId()) {
1234 return R__FAIL("cannot make FieldZero a child field");
1235 }
1236 // fail if field already has another valid parent
1237 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1239 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1240 }
1241 if (fieldId == linkId) {
1242 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1243 }
1244 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1245 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1246 return RResult<void>::Success();
1247}
1248
1251{
1253 if (!(fieldExists = EnsureFieldExists(sourceId)))
1255 if (!(fieldExists = EnsureFieldExists(targetId)))
1256 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1257
1258 if (targetId == fDescriptor.GetFieldZeroId()) {
1259 return R__FAIL("cannot make FieldZero a projected field");
1260 }
1261 if (sourceId == targetId) {
1262 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1263 }
1264 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1265 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1266 }
1267 // fail if target field already has another valid projection source
1268 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1269 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1270 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1271 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1272 }
1273 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1274 return RResult<void>::Success();
1275}
1276
1278{
1279 const auto fieldId = columnDesc.GetFieldId();
1280 const auto columnIndex = columnDesc.GetIndex();
1281 const auto representationIndex = columnDesc.GetRepresentationIndex();
1282
1283 auto fieldExists = EnsureFieldExists(fieldId);
1284 if (!fieldExists) {
1286 }
1287 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1288
1289 if (columnDesc.IsAliasColumn()) {
1290 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1291 return R__FAIL("alias column type mismatch");
1292 }
1293 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1294 return R__FAIL("column index clash");
1295 }
1296 if (columnIndex > 0) {
1297 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1298 return R__FAIL("out of bounds column index");
1299 }
1300 if (representationIndex > 0) {
1301 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1302 return R__FAIL("out of bounds representation index");
1303 }
1304 if (columnIndex == 0) {
1305 assert(fieldDesc.fColumnCardinality > 0);
1306 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1308 return R__FAIL("incomplete column representations");
1309 }
1310 } else {
1311 if (columnIndex >= fieldDesc.fColumnCardinality)
1312 return R__FAIL("irregular column representations");
1313 }
1314 } else {
1315 // This will set the column cardinality to the number of columns of the first representation
1316 fieldDesc.fColumnCardinality = columnIndex + 1;
1317 }
1318
1319 const auto logicalId = columnDesc.GetLogicalId();
1320 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1321
1322 if (!columnDesc.IsAliasColumn())
1323 fDescriptor.fNPhysicalColumns++;
1324 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1325 if (fDescriptor.fHeaderExtension)
1326 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1327
1328 return RResult<void>::Success();
1329}
1330
1332{
1333 const auto id = clusterGroup.GetId();
1334 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1335 return R__FAIL("cluster group id clash");
1336 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1337 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1338 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1339 return RResult<void>::Success();
1340}
1341
1346
1348{
1349 if (!fDescriptor.fHeaderExtension)
1350 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1351}
1352
1354{
1355 if (fDescriptor.GetNLogicalColumns() == 0)
1356 return;
1357 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1358
1359 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1360 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1361 R__ASSERT(c.IsAliasColumn());
1362 R__ASSERT(id == c.GetLogicalId());
1363 fDescriptor.fColumnDescriptors.erase(id);
1364 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1365 if (link == c.fLogicalColumnId) {
1366 link += offset;
1367 break;
1368 }
1369 }
1370 c.fLogicalColumnId += offset;
1371 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1372 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1373 }
1374}
1375
1377{
1378 auto clusterId = clusterDesc.GetId();
1379 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1380 return R__FAIL("cluster id clash");
1381 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1382 return RResult<void>::Success();
1383}
1384
1387{
1388 // Make sure we have no duplicates
1389 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1390 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1391 return R__FAIL("extra type info duplicates");
1392 }
1393 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1394 return RResult<void>::Success();
1395}
1396
1398{
1399 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1401 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1402 *it = std::move(extraTypeInfoDesc);
1403 else
1404 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1405}
1406
1409{
1410 auto &attrSets = fDescriptor.fAttributeSets;
1411 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1412 return desc.GetName() == name;
1413 }) != attrSets.end()) {
1414 return R__FAIL("attribute sets with duplicate names");
1415 }
1416 attrSets.push_back(std::move(attrSetDesc));
1417 return RResult<void>::Success();
1418}
1419
1424
1430
1437
1443
1450
1455
1461
1466
1472
1478
1483
1488
1493
1498
1500{
1501 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1502 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1503 fName == other.fName;
1504};
1505
1507{
1509 desc.fAnchorLength = fAnchorLength;
1510 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1511 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1512 desc.fAnchorLocator = fAnchorLocator;
1513 desc.fName = fName;
1514 return desc;
1515}
1516
1518{
1519 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1520 return false;
1521 if (fieldDesc.GetTypeName().rfind("std::", 0) == 0)
1522 return false;
1523
1524 auto subFieldId = desc.FindFieldId("_0", fieldDesc.GetId());
1526 return false;
1527
1528 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
1529 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
1530 "std::int64_t", "std::uint64_t"};
1531 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
1532 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
1533}
1534
1536{
1537 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1538 return false;
1539 return (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0);
1540}
#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.
static constexpr std::size_t kLargeRangeThreshold
Create the fCumulativeNElements only when its needed, i.e. when there are many pages to search throug...
RPageInfoExtended Find(ROOT::NTupleSize_t idxInCluster) const
Find the page in the RPageRange that contains the given element. The element must exist.
std::size_t ExtendToFitColumnRange(const RColumnRange &columnRange, const ROOT::Internal::RColumnElementBase &element, std::size_t pageSize)
Extend this RPageRange to fit the given RColumnRange.
Metadata for RNTuple clusters.
ROOT::NTupleSize_t fFirstEntryIndex
Clusters can be swapped by adjusting the entry offsets of the cluster and all ranges.
std::unordered_map< ROOT::DescriptorId_t, RColumnRange > fColumnRanges
ROOT::DescriptorId_t fClusterId
RClusterDescriptor Clone() const
bool operator==(const RClusterDescriptor &other) const
RColumnRangeIterable GetColumnRangeIterable() const
Returns an iterator over pairs { columnId, columnRange }. The iteration order is unspecified.
std::unordered_map< ROOT::DescriptorId_t, RPageRange > fPageRanges
std::uint64_t GetNBytesOnStorage() const
Clusters are bundled in cluster groups.
RNTupleLocator fPageListLocator
The page list that corresponds to the cluster group.
RClusterGroupDescriptor Clone() const
std::vector< ROOT::DescriptorId_t > fClusterIds
The cluster IDs can be empty if the corresponding page list is not loaded.
std::uint64_t fMinEntry
The minimum first entry number of the clusters in the cluster group.
std::uint32_t fNClusters
Number of clusters is always known even if the cluster IDs are not (yet) populated.
std::uint64_t fPageListLength
Uncompressed size of the page list.
std::uint64_t fEntrySpan
Number of entries that are (partially for sharded clusters) covered by this cluster group.
bool operator==(const RClusterGroupDescriptor &other) const
RClusterGroupDescriptor CloneSummary() const
Creates a clone without the cluster IDs.
Metadata stored for every column of an RNTuple.
ROOT::DescriptorId_t fPhysicalColumnId
Usually identical to the logical column ID, except for alias columns where it references the shadowed...
bool operator==(const RColumnDescriptor &other) const
ROOT::DescriptorId_t fLogicalColumnId
The actual column identifier, which is the link to the corresponding field.
ROOT::DescriptorId_t fFieldId
Every column belongs to one and only one field.
std::int64_t fFirstElementIndex
The absolute value specifies the index for the first stored element for this column.
std::uint32_t fIndex
A field can be serialized into several columns, which are numbered from zero to $n$.
std::uint16_t fBitsOnStorage
The size in bits of elements of this column.
std::uint16_t fRepresentationIndex
A field may use multiple column representations, which are numbered from zero to $m$.
ROOT::ENTupleColumnType fType
The on-disk column type.
std::optional< RValueRange > fValueRange
Optional value range (used e.g. by quantized real fields)
RColumnDescriptor Clone() const
Get a copy of the descriptor.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
bool operator==(const RExtraTypeInfoDescriptor &other) const
RExtraTypeInfoDescriptor Clone() const
EExtraTypeInfoIds fContentId
Specifies the meaning of the extra information.
std::string fTypeName
The type name the extra information refers to; empty for RNTuple-wide extra information.
std::string fContent
The content format depends on the content ID and may be binary.
std::uint32_t fTypeVersion
Type version the extra type information is bound to.
A field translates read and write calls from/to underlying columns to/from tree values.
@ 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:97
@ kGeneric
Generic unrecoverable error.
@ kUnknownStructure
The field could not be created because its descriptor had an unknown structural role.
Used to loop over all the clusters of an RNTuple (in unspecified order)
Used to loop over all the cluster groups of an RNTuple (in unspecified order)
Used to loop over a field's associated columns.
std::vector< ROOT::DescriptorId_t > fColumns
The descriptor ids of the columns ordered by field, representation, and column index.
RColumnDescriptorIterable(const RNTupleDescriptor &ntuple, const RFieldDescriptor &fieldDesc)
Used to loop over all the extra type info record of an RNTuple (in unspecified order)
Used to loop over a field's child fields.
std::vector< ROOT::DescriptorId_t > GetTopMostFields(const RNTupleDescriptor &desc) const
Return a vector containing the IDs of the top-level fields defined in the extension header,...
The on-storage metadata of an RNTuple.
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
ROOT::DescriptorId_t FindNextClusterId(ROOT::DescriptorId_t clusterId) const
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
std::set< unsigned int > fFeatureFlags
std::unordered_map< ROOT::DescriptorId_t, RClusterGroupDescriptor > fClusterGroupDescriptors
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::uint64_t fNPhysicalColumns
Updated by the descriptor builder when columns are added.
std::vector< Experimental::RNTupleAttrSetDescriptor > fAttributeSets
List of AttributeSets linked to this RNTuple.
ROOT::DescriptorId_t fFieldZeroId
Set by the descriptor builder.
std::uint64_t fNEntries
Updated by the descriptor builder when the cluster groups are added.
RClusterGroupDescriptorIterable GetClusterGroupIterable() const
RColumnDescriptorIterable GetColumnIterable() const
bool operator==(const RNTupleDescriptor &other) const
std::uint64_t fOnDiskFooterSize
Like fOnDiskHeaderSize, contains both cluster summaries and page locations.
std::uint16_t fVersionMinor
Set by the descriptor builder when deserialized.
ROOT::DescriptorId_t FindClusterId(ROOT::NTupleSize_t entryIdx) const
std::vector< std::uint64_t > GetFeatureFlags() const
ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
std::unique_ptr< ROOT::RNTupleModel > CreateModel(const RCreateModelOptions &options=RCreateModelOptions()) const
Re-create the C++ model from the stored metadata.
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
std::unordered_map< ROOT::DescriptorId_t, RClusterDescriptor > fClusterDescriptors
Potentially a subset of all the available clusters.
std::size_t GetNClusters() const
ROOT::DescriptorId_t FindPhysicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
RExtraTypeInfoDescriptorIterable GetExtraTypeInfoIterable() const
const RHeaderExtension * GetHeaderExtension() const
Return header extension information; if the descriptor does not have a header extension,...
std::uint64_t fNClusters
Updated by the descriptor builder when the cluster groups are added.
std::uint64_t fOnDiskHeaderXxHash3
Set by the descriptor builder when deserialized.
const RClusterDescriptor & GetClusterDescriptor(ROOT::DescriptorId_t clusterId) const
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
std::string fName
The RNTuple name needs to be unique in a given storage location (file)
std::uint64_t fOnDiskHeaderSize
Set by the descriptor builder when deserialized.
RResult< void > DropClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId)
std::uint16_t fVersionMajor
Set by the descriptor builder when deserialized.
std::vector< ROOT::DescriptorId_t > fSortedClusterGroupIds
References cluster groups sorted by entry range and thus allows for binary search.
std::unordered_map< ROOT::DescriptorId_t, RColumnDescriptor > fColumnDescriptors
ROOT::DescriptorId_t FindLogicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
std::unordered_map< ROOT::DescriptorId_t, RFieldDescriptor > fFieldDescriptors
ROOT::NTupleSize_t GetNElements(ROOT::DescriptorId_t physicalColumnId) const
RResult< void > AddClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId, std::vector< RClusterDescriptor > &clusterDescs)
Methods to load and drop cluster group details (cluster IDs and page locations)
std::uint16_t fVersionPatch
Set by the descriptor builder when deserialized.
std::string fDescription
Free text from the user.
ROOT::Experimental::RNTupleAttrSetDescriptorIterable GetAttrSetIterable() const
RFieldDescriptorIterable GetTopLevelFields() const
std::uint16_t fVersionEpoch
Set by the descriptor builder when deserialized.
std::vector< RExtraTypeInfoDescriptor > fExtraTypeInfoDescriptors
RNTupleDescriptor Clone() const
std::string GetQualifiedFieldName(ROOT::DescriptorId_t fieldId) const
Walks up the parents of the field ID and returns a field name of the form a.b.c.d In case of invalid ...
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