Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleDescriptor.cxx
Go to the documentation of this file.
1/// \file RNTupleDescriptor.cxx
2/// \ingroup NTuple
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \author Javier Lopez-Gomez <javier.lopez.gomez@cern.ch>
5/// \date 2018-10-04
6
7/*************************************************************************
8 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
9 * All rights reserved. *
10 * *
11 * For the licensing terms see $ROOTSYS/LICENSE. *
12 * For the list of contributors see $ROOTSYS/README/CREDITS. *
13 *************************************************************************/
14
15#include <ROOT/RError.hxx>
16#include <ROOT/RFieldBase.hxx>
17#include <ROOT/RNTuple.hxx>
19#include <ROOT/RNTupleModel.hxx>
20#include <ROOT/RNTupleTypes.hxx>
21#include <ROOT/RNTupleUtils.hxx>
22#include <ROOT/RPage.hxx>
23#include <string_view>
24
25#include <RZip.h>
26#include <TError.h>
27
28#include <algorithm>
29#include <cstdint>
30#include <deque>
31#include <functional>
32#include <iostream>
33#include <set>
34#include <utility>
35
37
39{
40 return fFieldId == other.fFieldId && fFieldVersion == other.fFieldVersion && fTypeVersion == other.fTypeVersion &&
41 fFieldName == other.fFieldName && fFieldDescription == other.fFieldDescription &&
42 fTypeName == other.fTypeName && fTypeAlias == other.fTypeAlias && fNRepetitions == other.fNRepetitions &&
43 fStructure == other.fStructure && fParentId == other.fParentId &&
44 fProjectionSourceId == other.fProjectionSourceId && fLinkIds == other.fLinkIds &&
45 fLogicalColumnIds == other.fLogicalColumnIds && fTypeChecksum == other.fTypeChecksum &&
46 fIsSoACollection == other.fIsSoACollection;
47}
48
50{
51 RFieldDescriptor clone;
52 clone.fFieldId = fFieldId;
53 clone.fFieldVersion = fFieldVersion;
54 clone.fTypeVersion = fTypeVersion;
55 clone.fFieldName = fFieldName;
56 clone.fFieldDescription = fFieldDescription;
57 clone.fTypeName = fTypeName;
58 clone.fTypeAlias = fTypeAlias;
59 clone.fNRepetitions = fNRepetitions;
60 clone.fStructure = fStructure;
61 clone.fParentId = fParentId;
62 clone.fProjectionSourceId = fProjectionSourceId;
63 clone.fLinkIds = fLinkIds;
64 clone.fColumnCardinality = fColumnCardinality;
65 clone.fLogicalColumnIds = fLogicalColumnIds;
66 clone.fTypeChecksum = fTypeChecksum;
67 clone.fIsSoACollection = fIsSoACollection;
68 return clone;
69}
70
71std::unique_ptr<ROOT::RFieldBase>
73{
74 if (GetStructure() == ROOT::ENTupleStructure::kStreamer) {
75 auto streamerField = std::make_unique<ROOT::RStreamerField>(GetFieldName(), GetTypeName());
76 streamerField->SetOnDiskId(fFieldId);
77 return streamerField;
78 }
79
80 // The structure may be unknown if the descriptor comes from a deserialized field with an unknown structural role.
81 // For forward compatibility, we allow this case and return an InvalidField.
82 if (GetStructure() == ROOT::ENTupleStructure::kUnknown) {
83 if (options.GetReturnInvalidOnError()) {
84 auto invalidField = std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), "",
86 invalidField->SetOnDiskId(fFieldId);
87 return invalidField;
88 } else {
89 throw RException(R__FAIL("unexpected on-disk field structure value for field \"" + GetFieldName() + "\""));
90 }
91 }
92
93 // Untyped records and collections
94 if (GetTypeName().empty()) {
95 switch (GetStructure()) {
97 std::vector<std::unique_ptr<ROOT::RFieldBase>> memberFields;
98 memberFields.reserve(fLinkIds.size());
99 for (auto id : fLinkIds) {
100 const auto &memberDesc = ntplDesc.GetFieldDescriptor(id);
101 auto field = memberDesc.CreateField(ntplDesc, options);
103 return field;
104 memberFields.emplace_back(std::move(field));
105 }
106 auto recordField = std::make_unique<ROOT::RRecordField>(GetFieldName(), std::move(memberFields));
107 recordField->SetOnDiskId(fFieldId);
108 return recordField;
109 }
111 if (fLinkIds.size() != 1) {
112 throw RException(R__FAIL("unsupported untyped collection for field \"" + GetFieldName() + "\""));
113 }
114 auto itemField = ntplDesc.GetFieldDescriptor(fLinkIds[0]).CreateField(ntplDesc, options);
116 return itemField;
117 auto collectionField = ROOT::RVectorField::CreateUntyped(GetFieldName(), std::move(itemField));
118 collectionField->SetOnDiskId(fFieldId);
119 return collectionField;
120 }
121 default: throw RException(R__FAIL("unsupported untyped field structure for field \"" + GetFieldName() + "\""));
122 }
123 }
124
125 try {
126 const auto &fieldName = GetFieldName();
127 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
128 // NOTE: Unwrap() here may throw an exception, hence the try block.
129 // If options.fReturnInvalidOnError is false we just rethrow it, otherwise we return an InvalidField wrapping the
130 // error.
131 auto field = ROOT::Internal::CallFieldBaseCreate(fieldName, typeName, options, &ntplDesc, fFieldId).Unwrap();
132 field->SetOnDiskId(fFieldId);
133
134 for (auto &subfield : *field) {
135 const auto subfieldId = ntplDesc.FindFieldId(subfield.GetFieldName(), subfield.GetParent()->GetOnDiskId());
136 subfield.SetOnDiskId(subfieldId);
138 auto &invalidField = static_cast<ROOT::RInvalidField &>(subfield);
139 // A subfield being invalid "infects" its entire ancestry.
140 return invalidField.Clone(fieldName);
141 }
142 }
143
144 return field;
145 } catch (const RException &ex) {
146 if (options.GetReturnInvalidOnError())
147 return std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), ex.GetError().GetReport(),
149 else
150 throw ex;
151 }
152}
153
154////////////////////////////////////////////////////////////////////////////////
155
157{
158 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
159 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
160 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
161 fValueRange == other.fValueRange;
162}
163
165{
166 RColumnDescriptor clone;
167 clone.fLogicalColumnId = fLogicalColumnId;
168 clone.fPhysicalColumnId = fPhysicalColumnId;
169 clone.fBitsOnStorage = fBitsOnStorage;
170 clone.fType = fType;
171 clone.fFieldId = fFieldId;
172 clone.fIndex = fIndex;
173 clone.fFirstElementIndex = fFirstElementIndex;
174 clone.fRepresentationIndex = fRepresentationIndex;
175 clone.fValueRange = fValueRange;
176 return clone;
177}
178
179////////////////////////////////////////////////////////////////////////////////
180
183{
184 if (!fCumulativeNElements) {
185 // Small range, just iterate through fPageInfos
188 for (const auto &pi : fPageInfos) {
189 if (firstInPage + pi.GetNElements() > idxInCluster) {
191 }
192 pageNumber++;
193 firstInPage += pi.GetNElements();
194 }
195 R__ASSERT(false);
196 }
197
198 const auto N = fCumulativeNElements->size();
199 R__ASSERT(N > 0);
200 R__ASSERT(N == fPageInfos.size());
201
202 std::size_t left = 0;
203 std::size_t right = N - 1;
204 std::size_t midpoint = N;
205 while (left <= right) {
206 midpoint = (left + right) / 2;
207 if ((*fCumulativeNElements)[midpoint] <= idxInCluster) {
208 left = midpoint + 1;
209 continue;
210 }
211
212 if ((midpoint == 0) || ((*fCumulativeNElements)[midpoint - 1] <= idxInCluster))
213 break;
214
215 right = midpoint - 1;
216 }
218
219 auto pageInfo = fPageInfos[midpoint];
220 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : (*fCumulativeNElements)[midpoint - 1];
222 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
224}
225
226std::size_t
229 std::size_t pageSize)
230{
231 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
232 R__ASSERT(!columnRange.IsSuppressed());
233
234 const auto nElements =
235 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
236 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
237 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
238
240 return 0U;
241 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
242
243 std::vector<RPageInfo> pageInfos;
244 // Synthesize new `RPageInfo`s as needed
245 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
249 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
252 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
253 pageInfo.SetLocator(locator);
254 pageInfos.emplace_back(pageInfo);
255 nRemainingElements -= pageInfo.GetNElements();
256 }
257
258 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
259 std::make_move_iterator(fPageInfos.end()));
260 std::swap(fPageInfos, pageInfos);
262}
263
265{
266 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
267 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
268}
269
271{
272 std::uint64_t nbytes = 0;
273 for (const auto &pr : fPageRanges) {
274 for (const auto &pi : pr.second.GetPageInfos()) {
275 nbytes += pi.GetLocator().GetNBytesOnStorage();
276 }
277 }
278 return nbytes;
279}
280
282{
283 RClusterDescriptor clone;
284 clone.fClusterId = fClusterId;
285 clone.fFirstEntryIndex = fFirstEntryIndex;
286 clone.fNEntries = fNEntries;
287 clone.fColumnRanges = fColumnRanges;
288 for (const auto &d : fPageRanges)
289 clone.fPageRanges.emplace(d.first, d.second.Clone());
290 return clone;
291}
292
293////////////////////////////////////////////////////////////////////////////////
294
296{
297 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
298}
299
301{
303 clone.fContentId = fContentId;
304 clone.fTypeVersion = fTypeVersion;
305 clone.fTypeName = fTypeName;
306 clone.fContent = fContent;
307 return clone;
308}
309
310////////////////////////////////////////////////////////////////////////////////
311
316
318{
319 // clang-format off
320 return fName == other.fName &&
321 fDescription == other.fDescription &&
322 fNEntries == other.fNEntries &&
323 fGeneration == other.fGeneration &&
324 fFieldZeroId == other.fFieldZeroId &&
325 fFieldDescriptors == other.fFieldDescriptors &&
326 fColumnDescriptors == other.fColumnDescriptors &&
327 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
328 fClusterDescriptors == other.fClusterDescriptors;
329 // clang-format on
330}
331
333{
335 for (const auto &cd : fClusterDescriptors) {
336 if (!cd.second.ContainsColumn(physicalColumnId))
337 continue;
338 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
339 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
340 }
341 return result;
342}
343
344////////////////////////////////////////////////////////////////////////////////
345/// Return the cluster boundaries for each cluster in this RNTuple.
346std::vector<ROOT::Internal::RNTupleClusterBoundaries>
348{
349 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
350 boundaries.reserve(desc.GetNClusters());
351 auto clusterId = desc.FindClusterId(0, 0);
353 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
354 R__ASSERT(clusterDesc.GetNEntries() > 0);
355 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
356 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
358 }
359 return boundaries;
360}
361
364{
365 std::string leafName(fieldName);
366 auto posDot = leafName.find_last_of('.');
367 if (posDot != std::string::npos) {
368 auto parentName = leafName.substr(0, posDot);
369 leafName = leafName.substr(posDot + 1);
370 parentId = FindFieldId(parentName, parentId);
371 }
372 auto itrFieldDesc = fFieldDescriptors.find(parentId);
373 if (itrFieldDesc == fFieldDescriptors.end())
375 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
376 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
377 return linkId;
378 }
380}
381
383{
385 return "";
386
387 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
388 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
389 if (prefix.empty())
390 return fieldDescriptor.GetFieldName();
391 return prefix + "." + fieldDescriptor.GetFieldName();
392}
393
395{
396 std::string typeName = fieldDesc.GetTypeName();
397
398 // ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
399 R__ASSERT(fVersionEpoch == 1);
400 if (fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1) {
401 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
402 }
403
404 return typeName;
405}
406
408{
409 return FindFieldId(fieldName, GetFieldZeroId());
410}
411
413 std::uint32_t columnIndex,
414 std::uint16_t representationIndex) const
415{
416 auto itr = fFieldDescriptors.find(fieldId);
417 if (itr == fFieldDescriptors.cend())
419 if (columnIndex >= itr->second.GetColumnCardinality())
421 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
422 if (itr->second.GetLogicalColumnIds().size() <= idx)
424 return itr->second.GetLogicalColumnIds()[idx];
425}
426
428 std::uint32_t columnIndex,
429 std::uint16_t representationIndex) const
430{
431 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
434 return GetColumnDescriptor(logicalId).GetPhysicalId();
435}
436
439{
440 if (GetNClusterGroups() == 0)
442
443 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
444
445 std::size_t cgLeft = 0;
446 std::size_t cgRight = GetNClusterGroups() - 1;
447 while (cgLeft <= cgRight) {
448 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
449 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
450 R__ASSERT(!clusterIds.empty());
451
452 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
453 // this may happen if the RNTuple has an empty schema
454 if (!clusterDesc.ContainsColumn(physicalColumnId))
456
457 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
459 // Look into the lower half of cluster groups
461 cgRight = cgMidpoint - 1;
462 continue;
463 }
464
465 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
466 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
467 // Look into the upper half of cluster groups
468 cgLeft = cgMidpoint + 1;
469 continue;
470 }
471
472 // Binary search in the current cluster group; since we already checked the element range boundaries,
473 // the element must be in that cluster group.
474 std::size_t clusterLeft = 0;
475 std::size_t clusterRight = clusterIds.size() - 1;
476 while (clusterLeft <= clusterRight) {
477 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
479 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
480
481 if (columnRange.Contains(index))
482 return clusterId;
483
484 if (columnRange.GetFirstElementIndex() > index) {
487 continue;
488 }
489
490 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
492 continue;
493 }
494 }
495 R__ASSERT(false);
496 }
498}
499
501{
502 if (GetNClusterGroups() == 0)
504
505 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
506
507 std::size_t cgLeft = 0;
508 std::size_t cgRight = GetNClusterGroups() - 1;
509 while (cgLeft <= cgRight) {
510 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
511 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
512
513 if (cgDesc.GetMinEntry() > entryIdx) {
515 cgRight = cgMidpoint - 1;
516 continue;
517 }
518
519 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
520 cgLeft = cgMidpoint + 1;
521 continue;
522 }
523
524 // Binary search in the current cluster group; since we already checked the element range boundaries,
525 // the element must be in that cluster group.
526 const auto &clusterIds = cgDesc.GetClusterIds();
527 R__ASSERT(!clusterIds.empty());
528 std::size_t clusterLeft = 0;
529 std::size_t clusterRight = clusterIds.size() - 1;
530 while (clusterLeft <= clusterRight) {
531 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
532 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
533
534 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
537 continue;
538 }
539
540 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
542 continue;
543 }
544
546 }
547 R__ASSERT(false);
548 }
550}
551
553{
554 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
555 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
556 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
557 // binary search code path remains tested.
558 const auto &clusterDesc = GetClusterDescriptor(clusterId);
559 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
560 return FindClusterId(firstEntryInNextCluster);
561}
562
564{
565 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
566 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
567 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
568 // binary search code path remains tested.
569 const auto &clusterDesc = GetClusterDescriptor(clusterId);
570 if (clusterDesc.GetFirstEntryIndex() == 0)
572 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
573}
574
575std::vector<ROOT::DescriptorId_t>
577{
578 std::vector<ROOT::DescriptorId_t> fields;
579 for (const auto fieldId : fFieldIdsOrder) {
580 if (fFieldIdsLookup.count(desc.GetFieldDescriptor(fieldId).GetParentId()) == 0)
581 fields.emplace_back(fieldId);
582 }
583 return fields;
584}
585
591
593 : fNTuple(ntuple)
594{
595 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
596
597 while (!fieldIdQueue.empty()) {
598 auto currFieldId = fieldIdQueue.front();
599 fieldIdQueue.pop_front();
600
601 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
602 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
603
604 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
605 auto fieldId = field.GetId();
606 fieldIdQueue.push_back(fieldId);
607 }
608 }
609}
610
611std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
612{
613 std::vector<std::uint64_t> result;
614 unsigned int base = 0;
615 std::uint64_t flags = 0;
616 for (auto f : fFeatureFlags) {
617 if ((f > 0) && ((f % 64) == 0))
618 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
619 while (f > base + 64) {
620 result.emplace_back(flags);
621 flags = 0;
622 base += 64;
623 }
624 f -= base;
625 flags |= std::uint64_t(1) << f;
626 }
627 result.emplace_back(flags);
628 return result;
629}
630
632 std::vector<RClusterDescriptor> &clusterDescs)
633{
635 if (iter == fClusterGroupDescriptors.end())
636 return R__FAIL("invalid attempt to add details of unknown cluster group");
637 if (iter->second.HasClusterDetails())
638 return R__FAIL("invalid attempt to re-populate cluster group details");
639 if (iter->second.GetNClusters() != clusterDescs.size())
640 return R__FAIL("mismatch of number of clusters");
641
642 std::vector<ROOT::DescriptorId_t> clusterIds;
643 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
644 clusterIds.emplace_back(clusterDescs[i].GetId());
645 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
646 if (!success) {
647 return R__FAIL("invalid attempt to re-populate existing cluster");
648 }
649 }
651 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
652 });
654 cgBuilder.AddSortedClusters(clusterIds);
655 iter->second = cgBuilder.MoveDescriptor().Unwrap();
656 return RResult<void>::Success();
657}
658
660{
662 if (iter == fClusterGroupDescriptors.end())
663 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
664 if (!iter->second.HasClusterDetails())
665 return R__FAIL("invalid attempt to drop details of cluster group summary");
666
667 for (auto clusterId : iter->second.GetClusterIds())
669 iter->second = iter->second.CloneSummary();
670 return RResult<void>::Success();
671}
672
673std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
674{
675 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
676 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
677 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
678 for (const auto &colDesc : GetColumnIterable()) {
680 auto fieldId = colDesc.GetFieldId();
681 while (1) {
682 const auto &field = GetFieldDescriptor(fieldId);
683 if (field.GetParentId() == GetFieldZeroId())
684 break;
685 fieldId = field.GetParentId();
686 }
687 invalidFields.insert(fieldId);
688
689 // No need to look for all invalid fields if we're gonna error out anyway
690 if (!options.GetForwardCompatible())
691 break;
692 }
693 }
694
695 if (!options.GetForwardCompatible() && !invalidFields.empty())
697 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
698 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
699
700 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
701 fieldZero->SetOnDiskId(GetFieldZeroId());
702 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
703 : RNTupleModel::Create(std::move(fieldZero));
705 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
706 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
707 for (const auto &topDesc : GetTopLevelFields()) {
708 if (invalidFields.count(topDesc.GetId()) > 0) {
709 // Field contains invalid columns: skip it
710 continue;
711 }
712
713 auto field = topDesc.CreateField(*this, createFieldOpts);
714
715 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
716 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
717 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
718 // in the first place, so we don't need to check for that flag again).
719 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
720 const auto &invalid = static_cast<const RInvalidField &>(*field);
721 const auto cat = invalid.GetCategory();
723 if (mustThrow)
724 throw invalid.GetError();
725
726 // Not a hard error: skip the field and go on.
727 continue;
728 }
729
730 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
731 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
732 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
733 });
734 } else {
735 model->AddField(std::move(field));
736 }
737 }
738 model->Freeze();
739 return model;
740}
741
743{
744 RNTupleDescriptor clone;
745 clone.fName = fName;
750 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
751 // and therefore not represent the actual sources's header.
752 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
753
754 for (const auto &d : fFieldDescriptors)
755 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
756 for (const auto &d : fColumnDescriptors)
757 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
758
759 for (const auto &d : fExtraTypeInfoDescriptors)
760 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
762 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
763
764 return clone;
765}
766
768{
770
775
779 clone.fNEntries = fNEntries;
780 clone.fNClusters = fNClusters;
781 clone.fGeneration = fGeneration;
782 for (const auto &d : fClusterGroupDescriptors)
783 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
785 for (const auto &d : fClusterDescriptors)
786 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
787 for (const auto &d : fAttributeSets)
788 clone.fAttributeSets.emplace_back(d.Clone());
789 return clone;
790}
791
792////////////////////////////////////////////////////////////////////////////////
793
795{
796 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
797 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
798}
799
801{
803 clone.fClusterGroupId = fClusterGroupId;
804 clone.fPageListLocator = fPageListLocator;
805 clone.fPageListLength = fPageListLength;
806 clone.fMinEntry = fMinEntry;
807 clone.fEntrySpan = fEntrySpan;
808 clone.fNClusters = fNClusters;
809 return clone;
810}
811
813{
814 RClusterGroupDescriptor clone = CloneSummary();
815 clone.fClusterIds = fClusterIds;
816 return clone;
817}
818
819////////////////////////////////////////////////////////////////////////////////
820
823 std::uint64_t firstElementIndex,
824 std::uint32_t compressionSettings,
826{
827 if (physicalId != pageRange.fPhysicalColumnId)
828 return R__FAIL("column ID mismatch");
829 if (fCluster.fColumnRanges.count(physicalId) > 0)
830 return R__FAIL("column ID conflict");
832 for (const auto &pi : pageRange.fPageInfos) {
833 columnRange.IncrementNElements(pi.GetNElements());
834 }
835 fCluster.fPageRanges[physicalId] = pageRange.Clone();
836 fCluster.fColumnRanges[physicalId] = columnRange;
837 return RResult<void>::Success();
838}
839
842{
843 if (fCluster.fColumnRanges.count(physicalId) > 0)
844 return R__FAIL("column ID conflict");
845
847 columnRange.SetPhysicalColumnId(physicalId);
848 columnRange.SetIsSuppressed(true);
849 fCluster.fColumnRanges[physicalId] = columnRange;
850 return RResult<void>::Success();
851}
852
855{
856 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
857 if (!columnRange.IsSuppressed())
858 continue;
859 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
860
861 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
862 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
863 // We expect only few columns and column representations per field, so we do a linear search
864 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
866 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
867 continue;
868 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
869 continue;
870
871 // Found corresponding column of a different column representation
872 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
873 if (otherColumnRange.IsSuppressed())
874 continue;
875
876 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
877 columnRange.SetNElements(otherColumnRange.GetNElements());
878 break;
879 }
880
881 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
882 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
883 std::to_string(columnRange.GetPhysicalColumnId()) +
884 ", cluster ID: " + std::to_string(fCluster.GetId()));
885 }
886 }
887 return RResult<void>::Success();
888}
889
892{
893 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
894 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
895 /// parent field in the hierarchy.
897 const auto &visitField, const auto &enterSubtree) -> void {
899 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
900 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
902 }
903 };
904
905 // Extended columns can only be part of the header extension
906 if (!desc.GetHeaderExtension())
907 return *this;
908
909 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
910 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
911 for (const auto &topLevelField : desc.GetTopLevelFields()) {
913 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
914 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
915 for (const auto &c : desc.GetColumnIterable(fieldId)) {
916 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
917 auto &columnRange = fCluster.fColumnRanges[physicalId];
918
919 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
920 // extension won't have on-disk metadata for the clusters that were already committed before the model
921 // was extended. Therefore, these need to be synthetically initialized upon reading.
922 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
923 columnRange.SetPhysicalColumnId(physicalId);
924 columnRange.SetFirstElementIndex(0);
925 columnRange.SetNElements(0);
926 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
927 }
928 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
929 // number of elements should have been if the column was not deferred; fix those and let
930 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
931 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
932 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
933 // field zero excluding subfields of collection and variant fields.
934 if (c.IsDeferredColumn()) {
935 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
936 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
937 if (!columnRange.IsSuppressed()) {
938 auto &pageRange = fCluster.fPageRanges[physicalId];
939 pageRange.fPhysicalColumnId = physicalId;
940 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
941 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
942 }
943 } else if (!columnRange.IsSuppressed()) {
944 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
945 }
946 }
947 },
949 }
950 return *this;
951}
952
954{
955 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
956 return R__FAIL("unset cluster ID");
957 if (fCluster.fNEntries == 0)
958 return R__FAIL("empty cluster");
959 for (auto &pr : fCluster.fPageRanges) {
960 if (fCluster.fColumnRanges.count(pr.first) == 0) {
961 return R__FAIL("missing column range");
962 }
963 pr.second.fCumulativeNElements.reset();
964 const auto nPages = pr.second.fPageInfos.size();
966 pr.second.fCumulativeNElements = std::make_unique<std::vector<NTupleSize_t>>();
967 pr.second.fCumulativeNElements->reserve(nPages);
969 for (const auto &pi : pr.second.fPageInfos) {
970 sum += pi.GetNElements();
971 pr.second.fCumulativeNElements->emplace_back(sum);
972 }
973 }
974 }
976 std::swap(result, fCluster);
977 return result;
978}
979
980////////////////////////////////////////////////////////////////////////////////
981
984{
986 builder.ClusterGroupId(clusterGroupDesc.GetId())
987 .PageListLocator(clusterGroupDesc.GetPageListLocator())
988 .PageListLength(clusterGroupDesc.GetPageListLength())
989 .MinEntry(clusterGroupDesc.GetMinEntry())
990 .EntrySpan(clusterGroupDesc.GetEntrySpan())
991 .NClusters(clusterGroupDesc.GetNClusters());
992 return builder;
993}
994
996{
997 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
998 return R__FAIL("unset cluster group ID");
1000 std::swap(result, fClusterGroup);
1001 return result;
1002}
1003
1004////////////////////////////////////////////////////////////////////////////////
1005
1007{
1008 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1009 throw RException(R__FAIL("invalid extra type info content id"));
1011 std::swap(result, fExtraTypeInfo);
1012 return result;
1013}
1014
1015////////////////////////////////////////////////////////////////////////////////
1016
1018{
1019 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1020 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1021 return RResult<void>::Success();
1022}
1023
1025{
1026 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1027 return R__FAIL("unset or unsupported RNTuple epoch version");
1028 }
1029
1030 // Reuse field name validity check
1031 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1032 if (!validName) {
1034 }
1035
1036 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1037 // parent not properly set?
1038 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1039 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1040 }
1041
1042 // Same number of columns in every column representation?
1043 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1044 if (columnCardinality == 0)
1045 continue;
1046
1047 // In AddColumn, we already checked that all but the last representation are complete.
1048 // Check that the last column representation is complete, i.e. has all columns.
1049 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1050 const auto nColumns = logicalColumnIds.size();
1051 // If we have only a single column representation, the following condition is true by construction
1052 if ((nColumns + 1) == columnCardinality)
1053 continue;
1054
1055 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1056 if (lastColumn.GetIndex() + 1 != columnCardinality)
1057 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1058 }
1059
1060 return RResult<void>::Success();
1061}
1062
1064{
1065 EnsureValidDescriptor().ThrowOnError();
1066 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1067 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1068 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1069 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1071 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1072 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1073 });
1075 std::swap(result, fDescriptor);
1076 return result;
1077}
1078
1080 std::uint16_t versionMinor, std::uint16_t versionPatch)
1081{
1083 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1084 }
1085 fDescriptor.fVersionEpoch = versionEpoch;
1086 fDescriptor.fVersionMajor = versionMajor;
1087 fDescriptor.fVersionMinor = versionMinor;
1088 fDescriptor.fVersionPatch = versionPatch;
1089}
1090
1092{
1093 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1094 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1095 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1096 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1097}
1098
1100 const std::string_view description)
1101{
1102 fDescriptor.fName = std::string(name);
1103 fDescriptor.fDescription = std::string(description);
1104}
1105
1107{
1108 if (flag > 0 && flag % 64 == 0)
1109 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1110 fDescriptor.fFeatureFlags.insert(flag);
1111}
1112
1115{
1116 if (fDesc.fName.empty())
1117 return R__FAIL("attribute set name cannot be empty");
1118 if (fDesc.fAnchorLength == 0)
1119 return R__FAIL("invalid anchor length");
1120 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1121 return R__FAIL("invalid locator type");
1122
1123 return std::move(fDesc);
1124}
1125
1127{
1128 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1129 return R__FAIL("invalid logical column id");
1130 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1131 return R__FAIL("invalid physical column id");
1132 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1133 return R__FAIL("invalid field id, dangling column");
1134
1135 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1136 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1137 // as we have no idea what the valid range is.
1138 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1139 // we try to read the actual data contained in it.
1140 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1141 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1142 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1143 return R__FAIL("invalid column bit width");
1144 }
1145
1146 return fColumn.Clone();
1147}
1148
1151{
1153 fieldDesc.FieldVersion(field.GetFieldVersion())
1154 .TypeVersion(field.GetTypeVersion())
1155 .FieldName(field.GetFieldName())
1156 .FieldDescription(field.GetDescription())
1157 .TypeName(field.GetTypeName())
1158 .TypeAlias(field.GetTypeAlias())
1159 .Structure(field.GetStructure())
1160 .NRepetitions(field.GetNRepetitions());
1162 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1163 if (field.GetTraits() & ROOT::RFieldBase::kTraitSoACollection) {
1164 assert(field.GetStructure() == ENTupleStructure::kCollection);
1165 fieldDesc.IsSoACollection(true);
1166 }
1167 return fieldDesc;
1168}
1169
1171{
1172 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1173 return R__FAIL("invalid field id");
1174 }
1175 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1176 return R__FAIL("invalid field structure");
1177 }
1178 if (fField.IsSoACollection() && (fField.GetStructure() != ROOT::ENTupleStructure::kCollection)) {
1179 return R__FAIL("invalid SoA flag on non-collection field");
1180 }
1181 // FieldZero is usually named "" and would be a false positive here
1182 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1183 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1184 if (!validName) {
1186 }
1187 if (fField.GetFieldName().empty()) {
1188 return R__FAIL("name cannot be empty string \"\"");
1189 }
1190 }
1191 return fField.Clone();
1192}
1193
1195{
1196 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1197 if (fDescriptor.fHeaderExtension)
1198 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1199 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1200 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1201 }
1202}
1203
1206{
1208 if (!(fieldExists = EnsureFieldExists(fieldId)))
1210 if (!(fieldExists = EnsureFieldExists(linkId)))
1211 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1212
1213 if (linkId == fDescriptor.GetFieldZeroId()) {
1214 return R__FAIL("cannot make FieldZero a child field");
1215 }
1216 // fail if field already has another valid parent
1217 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1219 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1220 }
1221 if (fieldId == linkId) {
1222 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1223 }
1224 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1225 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1226 return RResult<void>::Success();
1227}
1228
1231{
1233 if (!(fieldExists = EnsureFieldExists(sourceId)))
1235 if (!(fieldExists = EnsureFieldExists(targetId)))
1236 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1237
1238 if (targetId == fDescriptor.GetFieldZeroId()) {
1239 return R__FAIL("cannot make FieldZero a projected field");
1240 }
1241 if (sourceId == targetId) {
1242 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1243 }
1244 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1245 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1246 }
1247 // fail if target field already has another valid projection source
1248 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1249 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1250 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1251 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1252 }
1253 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1254 return RResult<void>::Success();
1255}
1256
1258{
1259 const auto fieldId = columnDesc.GetFieldId();
1260 const auto columnIndex = columnDesc.GetIndex();
1261 const auto representationIndex = columnDesc.GetRepresentationIndex();
1262
1263 auto fieldExists = EnsureFieldExists(fieldId);
1264 if (!fieldExists) {
1266 }
1267 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1268
1269 if (columnDesc.IsAliasColumn()) {
1270 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1271 return R__FAIL("alias column type mismatch");
1272 }
1273 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1274 return R__FAIL("column index clash");
1275 }
1276 if (columnIndex > 0) {
1277 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1278 return R__FAIL("out of bounds column index");
1279 }
1280 if (representationIndex > 0) {
1281 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1282 return R__FAIL("out of bounds representation index");
1283 }
1284 if (columnIndex == 0) {
1285 assert(fieldDesc.fColumnCardinality > 0);
1286 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1288 return R__FAIL("incomplete column representations");
1289 }
1290 } else {
1291 if (columnIndex >= fieldDesc.fColumnCardinality)
1292 return R__FAIL("irregular column representations");
1293 }
1294 } else {
1295 // This will set the column cardinality to the number of columns of the first representation
1296 fieldDesc.fColumnCardinality = columnIndex + 1;
1297 }
1298
1299 const auto logicalId = columnDesc.GetLogicalId();
1300 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1301
1302 if (!columnDesc.IsAliasColumn())
1303 fDescriptor.fNPhysicalColumns++;
1304 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1305 if (fDescriptor.fHeaderExtension)
1306 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1307
1308 return RResult<void>::Success();
1309}
1310
1312{
1313 const auto id = clusterGroup.GetId();
1314 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1315 return R__FAIL("cluster group id clash");
1316 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1317 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1318 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1319 return RResult<void>::Success();
1320}
1321
1326
1328{
1329 if (!fDescriptor.fHeaderExtension)
1330 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1331}
1332
1334{
1335 if (fDescriptor.GetNLogicalColumns() == 0)
1336 return;
1337 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1338
1339 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1340 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1341 R__ASSERT(c.IsAliasColumn());
1342 R__ASSERT(id == c.GetLogicalId());
1343 fDescriptor.fColumnDescriptors.erase(id);
1344 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1345 if (link == c.fLogicalColumnId) {
1346 link += offset;
1347 break;
1348 }
1349 }
1350 c.fLogicalColumnId += offset;
1351 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1352 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1353 }
1354}
1355
1357{
1358 auto clusterId = clusterDesc.GetId();
1359 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1360 return R__FAIL("cluster id clash");
1361 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1362 return RResult<void>::Success();
1363}
1364
1367{
1368 // Make sure we have no duplicates
1369 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1370 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1371 return R__FAIL("extra type info duplicates");
1372 }
1373 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1374 return RResult<void>::Success();
1375}
1376
1378{
1379 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1381 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1382 *it = std::move(extraTypeInfoDesc);
1383 else
1384 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1385}
1386
1389{
1390 auto &attrSets = fDescriptor.fAttributeSets;
1391 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1392 return desc.GetName() == name;
1393 }) != attrSets.end()) {
1394 return R__FAIL("attribute sets with duplicate names");
1395 }
1396 attrSets.push_back(std::move(attrSetDesc));
1397 return RResult<void>::Success();
1398}
1399
1404
1410
1417
1423
1430
1435
1441
1446
1452
1458
1463
1468
1473
1478
1480{
1481 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1482 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1483 fName == other.fName;
1484};
1485
1487{
1489 desc.fAnchorLength = fAnchorLength;
1490 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1491 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1492 desc.fAnchorLocator = fAnchorLocator;
1493 desc.fName = fName;
1494 return desc;
1495}
1496
1498{
1499 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1500 return false;
1501 if (fieldDesc.GetTypeName().rfind("std::", 0) == 0)
1502 return false;
1503
1504 auto subFieldId = desc.FindFieldId("_0", fieldDesc.GetId());
1506 return false;
1507
1508 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
1509 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
1510 "std::int64_t", "std::uint64_t"};
1511 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
1512 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
1513}
1514
1516{
1517 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1518 return false;
1519 return (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0);
1520}
#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:148
#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
void SetFeature(unsigned int flag)
Sets the flag-th bit of the feature flag to 1.
A helper class for serializing and deserialization of the RNTuple binary format.
The window of element indexes of a particular column in a particular cluster.
Records the partition of data into pages for a particular column in a particular cluster.
static constexpr std::size_t kLargeRangeThreshold
Create the fCumulativeNElements only when its needed, i.e. when there are many pages to search throug...
RPageInfoExtended Find(ROOT::NTupleSize_t idxInCluster) const
Find the page in the RPageRange that contains the given element. The element must exist.
std::size_t ExtendToFitColumnRange(const RColumnRange &columnRange, const ROOT::Internal::RColumnElementBase &element, std::size_t pageSize)
Extend this RPageRange to fit the given RColumnRange.
Metadata for RNTuple clusters.
ROOT::NTupleSize_t fFirstEntryIndex
Clusters can be swapped by adjusting the entry offsets of the cluster and all ranges.
std::unordered_map< ROOT::DescriptorId_t, RColumnRange > fColumnRanges
ROOT::DescriptorId_t fClusterId
RClusterDescriptor Clone() const
bool operator==(const RClusterDescriptor &other) const
RColumnRangeIterable GetColumnRangeIterable() const
Returns an iterator over pairs { columnId, columnRange }. The iteration order is unspecified.
std::unordered_map< ROOT::DescriptorId_t, RPageRange > fPageRanges
std::uint64_t GetNBytesOnStorage() const
Clusters are bundled in cluster groups.
RNTupleLocator fPageListLocator
The page list that corresponds to the cluster group.
RClusterGroupDescriptor Clone() const
std::vector< ROOT::DescriptorId_t > fClusterIds
The cluster IDs can be empty if the corresponding page list is not loaded.
std::uint64_t fMinEntry
The minimum first entry number of the clusters in the cluster group.
std::uint32_t fNClusters
Number of clusters is always known even if the cluster IDs are not (yet) populated.
std::uint64_t fPageListLength
Uncompressed size of the page list.
std::uint64_t fEntrySpan
Number of entries that are (partially for sharded clusters) covered by this cluster group.
bool operator==(const RClusterGroupDescriptor &other) const
RClusterGroupDescriptor CloneSummary() const
Creates a clone without the cluster IDs.
Metadata stored for every column of an RNTuple.
ROOT::DescriptorId_t fPhysicalColumnId
Usually identical to the logical column ID, except for alias columns where it references the shadowed...
bool operator==(const RColumnDescriptor &other) const
ROOT::DescriptorId_t fLogicalColumnId
The actual column identifier, which is the link to the corresponding field.
ROOT::DescriptorId_t fFieldId
Every column belongs to one and only one field.
std::int64_t fFirstElementIndex
The absolute value specifies the index for the first stored element for this column.
std::uint32_t fIndex
A field can be serialized into several columns, which are numbered from zero to $n$.
std::uint16_t fBitsOnStorage
The size in bits of elements of this column.
std::uint16_t fRepresentationIndex
A field may use multiple column representations, which are numbered from zero to $m$.
ROOT::ENTupleColumnType fType
The on-disk column type.
std::optional< RValueRange > fValueRange
Optional value range (used e.g. by quantized real fields)
RColumnDescriptor Clone() const
Get a copy of the descriptor.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
bool operator==(const RExtraTypeInfoDescriptor &other) const
RExtraTypeInfoDescriptor Clone() const
EExtraTypeInfoIds fContentId
Specifies the meaning of the extra information.
std::string fTypeName
The type name the extra information refers to; empty for RNTuple-wide extra information.
std::string fContent
The content format depends on the content ID and may be binary.
std::uint32_t fTypeVersion
Type version the extra type information is bound to.
A field translates read and write calls from/to underlying columns to/from tree values.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ kTraitInvalidField
This field is an instance of RInvalidField and can be safely static_cast to it.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
std::unique_ptr< ROOT::RFieldBase > CreateField(const RNTupleDescriptor &ntplDesc, const ROOT::RCreateFieldOptions &options={}) const
In general, we create a field simply from the C++ type name.
std::uint32_t fFieldVersion
The version of the C++-type-to-column translation mechanics.
ROOT::DescriptorId_t fFieldId
RFieldDescriptor Clone() const
Get a copy of the descriptor.
std::uint64_t fNRepetitions
The number of elements per entry for fixed-size arrays.
std::uint32_t fColumnCardinality
The number of columns in the column representations of the field.
ROOT::DescriptorId_t fProjectionSourceId
For projected fields, the source field ID.
bool operator==(const RFieldDescriptor &other) const
std::string fFieldDescription
Free text set by the user.
ROOT::DescriptorId_t fParentId
Establishes sub field relationships, such as classes and collections.
std::string fTypeAlias
A typedef or using directive that resolved to the type name during field creation.
ROOT::ENTupleStructure fStructure
The structural information carried by this field in the data model tree.
std::vector< ROOT::DescriptorId_t > fLinkIds
The pointers in the other direction from parent to children.
std::string fFieldName
The leaf name, not including parent fields.
bool fIsSoACollection
Indicates if this is a collection that should be represented in memory by a SoA layout.
std::uint32_t fTypeVersion
The version of the C++ type itself.
std::string fTypeName
The C++ type that was used when writing the field.
std::vector< ROOT::DescriptorId_t > fLogicalColumnIds
The ordered list of columns attached to this field: first by representation index then by column inde...
std::optional< std::uint32_t > fTypeChecksum
For custom classes, we store the ROOT TClass reported checksum to facilitate the use of I/O rules tha...
Used in RFieldBase::Check() to record field creation failures.
Definition RField.hxx: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:82
static constexpr std::uint16_t kVersionMajor
Definition RNTuple.hxx:80
static constexpr std::uint16_t kVersionEpoch
Definition RNTuple.hxx:79
static constexpr std::uint16_t kVersionMinor
Definition RNTuple.hxx:81
const_iterator begin() const
const_iterator end() const
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:198
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
ROOT::DescriptorId_t CallFindClusterIdOn(const ROOT::RNTupleDescriptor &desc, ROOT::NTupleSize_t entryIdx)
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
ROOT::RResult< std::unique_ptr< ROOT::RFieldBase > > CallFieldBaseCreate(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
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