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
155{
157 return false;
158
159 // Skip untyped structs
160 if (fTypeName.empty())
161 return false;
162
163 if (fStructure == ROOT::ENTupleStructure::kRecord) {
164 if (fTypeName.compare(0, 10, "std::pair<") == 0)
165 return false;
166 if (fTypeName.compare(0, 11, "std::tuple<") == 0)
167 return false;
168 }
169
170 return true;
171}
172
174{
175 return Internal::IsCustomEnumFieldDesc(desc, *this);
176}
177
182
183////////////////////////////////////////////////////////////////////////////////
184
186{
187 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
188 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
189 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
190 fValueRange == other.fValueRange;
191}
192
194{
195 RColumnDescriptor clone;
196 clone.fLogicalColumnId = fLogicalColumnId;
197 clone.fPhysicalColumnId = fPhysicalColumnId;
198 clone.fBitsOnStorage = fBitsOnStorage;
199 clone.fType = fType;
200 clone.fFieldId = fFieldId;
201 clone.fIndex = fIndex;
202 clone.fFirstElementIndex = fFirstElementIndex;
203 clone.fRepresentationIndex = fRepresentationIndex;
204 clone.fValueRange = fValueRange;
205 return clone;
206}
207
208////////////////////////////////////////////////////////////////////////////////
209
212{
213 if (!fCumulativeNElements) {
214 // Small range, just iterate through fPageInfos
217 for (const auto &pi : fPageInfos) {
218 if (firstInPage + pi.GetNElements() > idxInCluster) {
220 }
221 pageNumber++;
222 firstInPage += pi.GetNElements();
223 }
224 R__ASSERT(false);
225 }
226
227 const auto N = fCumulativeNElements->size();
228 R__ASSERT(N > 0);
229 R__ASSERT(N == fPageInfos.size());
230
231 std::size_t left = 0;
232 std::size_t right = N - 1;
233 std::size_t midpoint = N;
234 while (left <= right) {
235 midpoint = (left + right) / 2;
236 if ((*fCumulativeNElements)[midpoint] <= idxInCluster) {
237 left = midpoint + 1;
238 continue;
239 }
240
241 if ((midpoint == 0) || ((*fCumulativeNElements)[midpoint - 1] <= idxInCluster))
242 break;
243
244 right = midpoint - 1;
245 }
247
248 auto pageInfo = fPageInfos[midpoint];
249 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : (*fCumulativeNElements)[midpoint - 1];
251 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
253}
254
255std::size_t
258 std::size_t pageSize)
259{
260 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
261 R__ASSERT(!columnRange.IsSuppressed());
262
263 const auto nElements =
264 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
265 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
266 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
267
269 return 0U;
270 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
271
272 std::vector<RPageInfo> pageInfos;
273 // Synthesize new `RPageInfo`s as needed
274 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
278 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
281 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
282 pageInfo.SetLocator(locator);
283 pageInfos.emplace_back(pageInfo);
284 nRemainingElements -= pageInfo.GetNElements();
285 }
286
287 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
288 std::make_move_iterator(fPageInfos.end()));
289 std::swap(fPageInfos, pageInfos);
291}
292
294{
295 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
296 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
297}
298
300{
301 std::uint64_t nbytes = 0;
302 for (const auto &pr : fPageRanges) {
303 for (const auto &pi : pr.second.GetPageInfos()) {
304 nbytes += pi.GetLocator().GetNBytesOnStorage();
305 }
306 }
307 return nbytes;
308}
309
311{
312 RClusterDescriptor clone;
313 clone.fClusterId = fClusterId;
314 clone.fFirstEntryIndex = fFirstEntryIndex;
315 clone.fNEntries = fNEntries;
316 clone.fColumnRanges = fColumnRanges;
317 for (const auto &d : fPageRanges)
318 clone.fPageRanges.emplace(d.first, d.second.Clone());
319 return clone;
320}
321
322////////////////////////////////////////////////////////////////////////////////
323
325{
326 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
327}
328
330{
332 clone.fContentId = fContentId;
333 clone.fTypeVersion = fTypeVersion;
334 clone.fTypeName = fTypeName;
335 clone.fContent = fContent;
336 return clone;
337}
338
339////////////////////////////////////////////////////////////////////////////////
340
345
347{
348 // clang-format off
349 return fName == other.fName &&
350 fDescription == other.fDescription &&
351 fNEntries == other.fNEntries &&
352 fGeneration == other.fGeneration &&
353 fFieldZeroId == other.fFieldZeroId &&
354 fFieldDescriptors == other.fFieldDescriptors &&
355 fColumnDescriptors == other.fColumnDescriptors &&
356 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
357 fClusterDescriptors == other.fClusterDescriptors;
358 // clang-format on
359}
360
362{
364 for (const auto &cd : fClusterDescriptors) {
365 if (!cd.second.ContainsColumn(physicalColumnId))
366 continue;
367 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
368 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
369 }
370 return result;
371}
372
373////////////////////////////////////////////////////////////////////////////////
374/// Return the cluster boundaries for each cluster in this RNTuple.
375std::vector<ROOT::Internal::RNTupleClusterBoundaries>
377{
378 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
379 boundaries.reserve(desc.GetNClusters());
380 auto clusterId = desc.FindClusterId(0, 0);
382 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
383 R__ASSERT(clusterDesc.GetNEntries() > 0);
384 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
385 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
387 }
388 return boundaries;
389}
390
393{
394 std::string leafName(fieldName);
395 auto posDot = leafName.find_last_of('.');
396 if (posDot != std::string::npos) {
397 auto parentName = leafName.substr(0, posDot);
398 leafName = leafName.substr(posDot + 1);
399 parentId = FindFieldId(parentName, parentId);
400 }
401 auto itrFieldDesc = fFieldDescriptors.find(parentId);
402 if (itrFieldDesc == fFieldDescriptors.end())
404 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
405 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
406 return linkId;
407 }
409}
410
412{
414 return "";
415
416 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
417 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
418 if (prefix.empty())
419 return fieldDescriptor.GetFieldName();
420 return prefix + "." + fieldDescriptor.GetFieldName();
421}
422
424{
425 R__ASSERT(fVersionEpoch == 1);
426 return fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1;
427}
428
430{
431 std::string typeName = fieldDesc.GetTypeName();
432
433 if (FieldTypeNamesMayNeedFixup()) {
434 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
435 }
436
437 return typeName;
438}
439
441{
442 return FindFieldId(fieldName, GetFieldZeroId());
443}
444
446 std::uint32_t columnIndex,
447 std::uint16_t representationIndex) const
448{
449 auto itr = fFieldDescriptors.find(fieldId);
450 if (itr == fFieldDescriptors.cend())
452 if (columnIndex >= itr->second.GetColumnCardinality())
454 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
455 if (itr->second.GetLogicalColumnIds().size() <= idx)
457 return itr->second.GetLogicalColumnIds()[idx];
458}
459
461 std::uint32_t columnIndex,
462 std::uint16_t representationIndex) const
463{
464 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
467 return GetColumnDescriptor(logicalId).GetPhysicalId();
468}
469
472{
473 if (GetNClusterGroups() == 0)
475
476 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
477
478 std::size_t cgLeft = 0;
479 std::size_t cgRight = GetNClusterGroups() - 1;
480 while (cgLeft <= cgRight) {
481 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
482 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
483 R__ASSERT(!clusterIds.empty());
484
485 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
486 // this may happen if the RNTuple has an empty schema
487 if (!clusterDesc.ContainsColumn(physicalColumnId))
489
490 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
492 // Look into the lower half of cluster groups
494 cgRight = cgMidpoint - 1;
495 continue;
496 }
497
498 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
499 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
500 // Look into the upper half of cluster groups
501 cgLeft = cgMidpoint + 1;
502 continue;
503 }
504
505 // Binary search in the current cluster group; since we already checked the element range boundaries,
506 // the element must be in that cluster group.
507 std::size_t clusterLeft = 0;
508 std::size_t clusterRight = clusterIds.size() - 1;
509 while (clusterLeft <= clusterRight) {
510 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
512 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
513
514 if (columnRange.Contains(index))
515 return clusterId;
516
517 if (columnRange.GetFirstElementIndex() > index) {
520 continue;
521 }
522
523 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
525 continue;
526 }
527 }
528 R__ASSERT(false);
529 }
531}
532
534{
535 if (GetNClusterGroups() == 0)
537
538 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
539
540 std::size_t cgLeft = 0;
541 std::size_t cgRight = GetNClusterGroups() - 1;
542 while (cgLeft <= cgRight) {
543 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
544 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
545
546 if (cgDesc.GetMinEntry() > entryIdx) {
548 cgRight = cgMidpoint - 1;
549 continue;
550 }
551
552 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
553 cgLeft = cgMidpoint + 1;
554 continue;
555 }
556
557 // Binary search in the current cluster group; since we already checked the element range boundaries,
558 // the element must be in that cluster group.
559 const auto &clusterIds = cgDesc.GetClusterIds();
560 R__ASSERT(!clusterIds.empty());
561 std::size_t clusterLeft = 0;
562 std::size_t clusterRight = clusterIds.size() - 1;
563 while (clusterLeft <= clusterRight) {
564 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
565 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
566
567 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
570 continue;
571 }
572
573 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
575 continue;
576 }
577
579 }
580 R__ASSERT(false);
581 }
583}
584
586{
587 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
588 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
589 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
590 // binary search code path remains tested.
591 const auto &clusterDesc = GetClusterDescriptor(clusterId);
592 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
593 return FindClusterId(firstEntryInNextCluster);
594}
595
597{
598 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
599 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
600 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
601 // binary search code path remains tested.
602 const auto &clusterDesc = GetClusterDescriptor(clusterId);
603 if (clusterDesc.GetFirstEntryIndex() == 0)
605 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
606}
607
608std::vector<ROOT::DescriptorId_t>
610{
611 std::vector<ROOT::DescriptorId_t> fields;
612 for (const auto fieldId : fFieldIdsOrder) {
613 if (fFieldIdsLookup.count(desc.GetFieldDescriptor(fieldId).GetParentId()) == 0)
614 fields.emplace_back(fieldId);
615 }
616 return fields;
617}
618
624
626 : fNTuple(ntuple)
627{
628 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
629
630 while (!fieldIdQueue.empty()) {
631 auto currFieldId = fieldIdQueue.front();
632 fieldIdQueue.pop_front();
633
634 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
635 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
636
637 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
638 auto fieldId = field.GetId();
639 fieldIdQueue.push_back(fieldId);
640 }
641 }
642}
643
644std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
645{
646 std::vector<std::uint64_t> result;
647 unsigned int base = 0;
648 std::uint64_t flags = 0;
649 for (auto f : fFeatureFlags) {
650 if ((f > 0) && ((f % 64) == 0))
651 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
652 while (f > base + 64) {
653 result.emplace_back(flags);
654 flags = 0;
655 base += 64;
656 }
657 f -= base;
658 flags |= std::uint64_t(1) << f;
659 }
660 result.emplace_back(flags);
661 return result;
662}
663
665 std::vector<RClusterDescriptor> &clusterDescs)
666{
668 if (iter == fClusterGroupDescriptors.end())
669 return R__FAIL("invalid attempt to add details of unknown cluster group");
670 if (iter->second.HasClusterDetails())
671 return R__FAIL("invalid attempt to re-populate cluster group details");
672 if (iter->second.GetNClusters() != clusterDescs.size())
673 return R__FAIL("mismatch of number of clusters");
674
675 std::vector<ROOT::DescriptorId_t> clusterIds;
676 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
677 clusterIds.emplace_back(clusterDescs[i].GetId());
678 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
679 if (!success) {
680 return R__FAIL("invalid attempt to re-populate existing cluster");
681 }
682 }
684 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
685 });
687 cgBuilder.AddSortedClusters(clusterIds);
688 iter->second = cgBuilder.MoveDescriptor().Unwrap();
689 return RResult<void>::Success();
690}
691
693{
695 if (iter == fClusterGroupDescriptors.end())
696 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
697 if (!iter->second.HasClusterDetails())
698 return R__FAIL("invalid attempt to drop details of cluster group summary");
699
700 for (auto clusterId : iter->second.GetClusterIds())
702 iter->second = iter->second.CloneSummary();
703 return RResult<void>::Success();
704}
705
706std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
707{
708 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
709 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
710 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
711 for (const auto &colDesc : GetColumnIterable()) {
713 auto fieldId = colDesc.GetFieldId();
714 while (1) {
715 const auto &field = GetFieldDescriptor(fieldId);
716 if (field.GetParentId() == GetFieldZeroId())
717 break;
718 fieldId = field.GetParentId();
719 }
720 invalidFields.insert(fieldId);
721
722 // No need to look for all invalid fields if we're gonna error out anyway
723 if (!options.GetForwardCompatible())
724 break;
725 }
726 }
727
728 if (!options.GetForwardCompatible() && !invalidFields.empty())
730 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
731 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
732
733 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
734 fieldZero->SetOnDiskId(GetFieldZeroId());
735 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
736 : RNTupleModel::Create(std::move(fieldZero));
738 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
739 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
740 for (const auto &topDesc : GetTopLevelFields()) {
741 if (invalidFields.count(topDesc.GetId()) > 0) {
742 // Field contains invalid columns: skip it
743 continue;
744 }
745
746 auto field = topDesc.CreateField(*this, createFieldOpts);
747
748 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
749 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
750 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
751 // in the first place, so we don't need to check for that flag again).
752 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
753 const auto &invalid = static_cast<const RInvalidField &>(*field);
754 const auto cat = invalid.GetCategory();
756 if (mustThrow)
757 throw invalid.GetError();
758
759 // Not a hard error: skip the field and go on.
760 continue;
761 }
762
763 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
764 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
765 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
766 });
767 } else {
768 model->AddField(std::move(field));
769 }
770 }
771 model->Freeze();
772 return model;
773}
774
776{
777 RNTupleDescriptor clone;
778 clone.fName = fName;
783 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
784 // and therefore not represent the actual sources's header.
785 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
786
787 for (const auto &d : fFieldDescriptors)
788 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
789 for (const auto &d : fColumnDescriptors)
790 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
791
792 for (const auto &d : fExtraTypeInfoDescriptors)
793 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
795 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
796
797 // In case we are copying the schema from a pre-1.0.0.1 RNTuple we need to patch all field type names
798 // to use the proper normalization.
800 std::vector<ROOT::DescriptorId_t> toVisit;
801 toVisit.push_back(GetFieldZeroId());
802 while (!toVisit.empty()) {
803 auto fieldId = toVisit.back();
804 toVisit.pop_back();
805 for (auto &field : clone.GetFieldIterable(fieldId)) {
807 toVisit.push_back(field.GetId());
808 }
809 }
810 }
811
812 return clone;
813}
814
816{
818
823
827 clone.fNEntries = fNEntries;
828 clone.fNClusters = fNClusters;
829 clone.fGeneration = fGeneration;
830 for (const auto &d : fClusterGroupDescriptors)
831 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
833 for (const auto &d : fClusterDescriptors)
834 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
835 for (const auto &d : fAttributeSets)
836 clone.fAttributeSets.emplace_back(d.Clone());
837 return clone;
838}
839
840////////////////////////////////////////////////////////////////////////////////
841
843{
844 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
845 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
846}
847
849{
851 clone.fClusterGroupId = fClusterGroupId;
852 clone.fPageListLocator = fPageListLocator;
853 clone.fPageListLength = fPageListLength;
854 clone.fMinEntry = fMinEntry;
855 clone.fEntrySpan = fEntrySpan;
856 clone.fNClusters = fNClusters;
857 return clone;
858}
859
861{
862 RClusterGroupDescriptor clone = CloneSummary();
863 clone.fClusterIds = fClusterIds;
864 return clone;
865}
866
867////////////////////////////////////////////////////////////////////////////////
868
871 std::uint64_t firstElementIndex,
872 std::uint32_t compressionSettings,
874{
875 if (physicalId != pageRange.fPhysicalColumnId)
876 return R__FAIL("column ID mismatch");
877 if (fCluster.fColumnRanges.count(physicalId) > 0)
878 return R__FAIL("column ID conflict");
880 for (const auto &pi : pageRange.fPageInfos) {
881 columnRange.IncrementNElements(pi.GetNElements());
882 }
883 fCluster.fPageRanges[physicalId] = pageRange.Clone();
884 fCluster.fColumnRanges[physicalId] = columnRange;
885 return RResult<void>::Success();
886}
887
890{
891 if (fCluster.fColumnRanges.count(physicalId) > 0)
892 return R__FAIL("column ID conflict");
893
895 columnRange.SetPhysicalColumnId(physicalId);
896 columnRange.SetIsSuppressed(true);
897 fCluster.fColumnRanges[physicalId] = columnRange;
898 return RResult<void>::Success();
899}
900
903{
904 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
905 if (!columnRange.IsSuppressed())
906 continue;
907 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
908
909 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
910 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
911 // We expect only few columns and column representations per field, so we do a linear search
912 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
914 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
915 continue;
916 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
917 continue;
918
919 // Found corresponding column of a different column representation
920 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
921 if (otherColumnRange.IsSuppressed())
922 continue;
923
924 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
925 columnRange.SetNElements(otherColumnRange.GetNElements());
926 break;
927 }
928
929 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
930 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
931 std::to_string(columnRange.GetPhysicalColumnId()) +
932 ", cluster ID: " + std::to_string(fCluster.GetId()));
933 }
934 }
935 return RResult<void>::Success();
936}
937
940{
941 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
942 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
943 /// parent field in the hierarchy.
945 const auto &visitField, const auto &enterSubtree) -> void {
947 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
948 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
950 }
951 };
952
953 // Extended columns can only be part of the header extension
954 if (!desc.GetHeaderExtension())
955 return *this;
956
957 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
958 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
959 for (const auto &topLevelField : desc.GetTopLevelFields()) {
961 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
962 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
963 for (const auto &c : desc.GetColumnIterable(fieldId)) {
964 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
965 auto &columnRange = fCluster.fColumnRanges[physicalId];
966
967 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
968 // extension won't have on-disk metadata for the clusters that were already committed before the model
969 // was extended. Therefore, these need to be synthetically initialized upon reading.
970 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
971 columnRange.SetPhysicalColumnId(physicalId);
972 columnRange.SetFirstElementIndex(0);
973 columnRange.SetNElements(0);
974 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
975 }
976 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
977 // number of elements should have been if the column was not deferred; fix those and let
978 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
979 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
980 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
981 // field zero excluding subfields of collection and variant fields.
982 if (c.IsDeferredColumn()) {
983 if (c.GetRepresentationIndex() == 0) {
984 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
985 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
986 } else {
987 // Deferred representations which are not the first cannot count on the number of elements being
988 // equal to Entries * nRepetitions because they might have been added in a later cluster. But they
989 // can rely on the first representation having the correct FirstElement/NElements (by definition
990 // the first representation cannot be an "extended" one), therefore they can just copy the value
991 // from it.
992 const auto &field = desc.GetFieldDescriptor(fieldId);
993 const auto firstReprColumnId = field.GetLogicalColumnIds()[c.GetIndex()];
994 const auto &firstReprColumnRange = fCluster.fColumnRanges[firstReprColumnId];
995 columnRange.SetFirstElementIndex(firstReprColumnRange.GetFirstElementIndex());
996 columnRange.SetNElements(firstReprColumnRange.GetNElements());
997 }
998 if (!columnRange.IsSuppressed()) {
999 auto &pageRange = fCluster.fPageRanges[physicalId];
1000 pageRange.fPhysicalColumnId = physicalId;
1001 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
1002 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
1003 }
1004 } else if (!columnRange.IsSuppressed()) {
1005 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
1006 }
1007 }
1008 },
1010 }
1011 return *this;
1012}
1013
1015{
1016 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
1017 return R__FAIL("unset cluster ID");
1018 if (fCluster.fNEntries == 0)
1019 return R__FAIL("empty cluster");
1020 for (auto &pr : fCluster.fPageRanges) {
1021 if (fCluster.fColumnRanges.count(pr.first) == 0) {
1022 return R__FAIL("missing column range");
1023 }
1024 pr.second.fCumulativeNElements.reset();
1025 const auto nPages = pr.second.fPageInfos.size();
1027 pr.second.fCumulativeNElements = std::make_unique<std::vector<NTupleSize_t>>();
1028 pr.second.fCumulativeNElements->reserve(nPages);
1030 for (const auto &pi : pr.second.fPageInfos) {
1031 sum += pi.GetNElements();
1032 pr.second.fCumulativeNElements->emplace_back(sum);
1033 }
1034 }
1035 }
1037 std::swap(result, fCluster);
1038 return result;
1039}
1040
1041////////////////////////////////////////////////////////////////////////////////
1042
1045{
1047 builder.ClusterGroupId(clusterGroupDesc.GetId())
1048 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1049 .PageListLength(clusterGroupDesc.GetPageListLength())
1050 .MinEntry(clusterGroupDesc.GetMinEntry())
1051 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1052 .NClusters(clusterGroupDesc.GetNClusters());
1053 return builder;
1054}
1055
1057{
1058 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1059 return R__FAIL("unset cluster group ID");
1061 std::swap(result, fClusterGroup);
1062 return result;
1063}
1064
1065////////////////////////////////////////////////////////////////////////////////
1066
1068{
1069 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1070 throw RException(R__FAIL("invalid extra type info content id"));
1072 std::swap(result, fExtraTypeInfo);
1073 return result;
1074}
1075
1076////////////////////////////////////////////////////////////////////////////////
1077
1079{
1080 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1081 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1082 return RResult<void>::Success();
1083}
1084
1086{
1087 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1088 return R__FAIL("unset or unsupported RNTuple epoch version");
1089 }
1090
1091 // Reuse field name validity check
1092 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1093 if (!validName) {
1095 }
1096
1097 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1098 // parent not properly set?
1099 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1100 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1101 }
1102
1103 // Same number of columns in every column representation?
1104 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1105 if (columnCardinality == 0)
1106 continue;
1107
1108 // In AddColumn, we already checked that all but the last representation are complete.
1109 // Check that the last column representation is complete, i.e. has all columns.
1110 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1111 const auto nColumns = logicalColumnIds.size();
1112 // If we have only a single column representation, the following condition is true by construction
1113 if ((nColumns + 1) == columnCardinality)
1114 continue;
1115
1116 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1117 if (lastColumn.GetIndex() + 1 != columnCardinality)
1118 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1119 }
1120
1121 return RResult<void>::Success();
1122}
1123
1125{
1126 EnsureValidDescriptor().ThrowOnError();
1127 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1128 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1129 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1130 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1132 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1133 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1134 });
1136 std::swap(result, fDescriptor);
1137 return result;
1138}
1139
1141 std::uint16_t versionMinor, std::uint16_t versionPatch)
1142{
1144 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1145 }
1146 fDescriptor.fVersionEpoch = versionEpoch;
1147 fDescriptor.fVersionMajor = versionMajor;
1148 fDescriptor.fVersionMinor = versionMinor;
1149 fDescriptor.fVersionPatch = versionPatch;
1150}
1151
1153{
1154 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1155 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1156 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1157 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1158}
1159
1161 const std::string_view description)
1162{
1163 fDescriptor.fName = std::string(name);
1164 fDescriptor.fDescription = std::string(description);
1165}
1166
1168{
1169 if (flag % 64 == 0)
1170 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1171 fDescriptor.fFeatureFlags.insert(flag);
1172}
1173
1176{
1177 if (fDesc.fName.empty())
1178 return R__FAIL("attribute set name cannot be empty");
1179 if (fDesc.fAnchorLength == 0)
1180 return R__FAIL("invalid anchor length");
1181 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1182 return R__FAIL("invalid locator type");
1183
1184 return std::move(fDesc);
1185}
1186
1188{
1189 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1190 return R__FAIL("invalid logical column id");
1191 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1192 return R__FAIL("invalid physical column id");
1193 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1194 return R__FAIL("invalid field id, dangling column");
1195
1196 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1197 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1198 // as we have no idea what the valid range is.
1199 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1200 // we try to read the actual data contained in it.
1201 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1202 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1203 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1204 return R__FAIL("invalid column bit width");
1205 }
1206
1207 return fColumn.Clone();
1208}
1209
1212{
1214 fieldDesc.FieldVersion(field.GetFieldVersion())
1215 .TypeVersion(field.GetTypeVersion())
1216 .FieldName(field.GetFieldName())
1217 .FieldDescription(field.GetDescription())
1218 .TypeName(field.GetTypeName())
1219 .TypeAlias(field.GetTypeAlias())
1220 .Structure(field.GetStructure())
1221 .NRepetitions(field.GetNRepetitions());
1223 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1224 if (field.GetTraits() & ROOT::RFieldBase::kTraitSoACollection) {
1225 assert(field.GetStructure() == ENTupleStructure::kCollection);
1226 fieldDesc.IsSoACollection(true);
1227 }
1228 return fieldDesc;
1229}
1230
1232{
1233 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1234 return R__FAIL("invalid field id");
1235 }
1236 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1237 return R__FAIL("invalid field structure");
1238 }
1239 if (fField.IsSoACollection() && (fField.GetStructure() != ROOT::ENTupleStructure::kCollection)) {
1240 return R__FAIL("invalid SoA flag on non-collection field");
1241 }
1242 // FieldZero is usually named "" and would be a false positive here
1243 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1244 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1245 if (!validName) {
1247 }
1248 if (fField.GetFieldName().empty()) {
1249 return R__FAIL("name cannot be empty string \"\"");
1250 }
1251 }
1252 return fField.Clone();
1253}
1254
1256{
1257 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1258 if (fDescriptor.fHeaderExtension)
1259 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1260 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1261 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1262 }
1263}
1264
1267{
1269 if (!(fieldExists = EnsureFieldExists(fieldId)))
1271 if (!(fieldExists = EnsureFieldExists(linkId)))
1272 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1273
1274 if (linkId == fDescriptor.GetFieldZeroId()) {
1275 return R__FAIL("cannot make FieldZero a child field");
1276 }
1277 // fail if field already has another valid parent
1278 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1280 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1281 }
1282 if (fieldId == linkId) {
1283 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1284 }
1285 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1286 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1287 return RResult<void>::Success();
1288}
1289
1292{
1294 if (!(fieldExists = EnsureFieldExists(sourceId)))
1296 if (!(fieldExists = EnsureFieldExists(targetId)))
1297 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1298
1299 if (targetId == fDescriptor.GetFieldZeroId()) {
1300 return R__FAIL("cannot make FieldZero a projected field");
1301 }
1302 if (sourceId == targetId) {
1303 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1304 }
1305 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1306 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1307 }
1308 // fail if target field already has another valid projection source
1309 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1310 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1311 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1312 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1313 }
1314 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1315 return RResult<void>::Success();
1316}
1317
1319{
1320 const auto fieldId = columnDesc.GetFieldId();
1321 const auto columnIndex = columnDesc.GetIndex();
1322 const auto representationIndex = columnDesc.GetRepresentationIndex();
1323
1324 auto fieldExists = EnsureFieldExists(fieldId);
1325 if (!fieldExists) {
1327 }
1328 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1329
1330 if (columnDesc.IsAliasColumn()) {
1331 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1332 return R__FAIL("alias column type mismatch");
1333 }
1334 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1335 return R__FAIL("column index clash");
1336 }
1337 if (columnIndex > 0) {
1338 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1339 return R__FAIL("out of bounds column index");
1340 }
1341 if (representationIndex > 0) {
1342 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1343 return R__FAIL("out of bounds representation index");
1344 }
1345 if (columnIndex == 0) {
1346 assert(fieldDesc.fColumnCardinality > 0);
1347 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1349 return R__FAIL("incomplete column representations");
1350 }
1351 } else {
1352 if (columnIndex >= fieldDesc.fColumnCardinality)
1353 return R__FAIL("irregular column representations");
1354 }
1355 } else {
1356 // This will set the column cardinality to the number of columns of the first representation
1357 fieldDesc.fColumnCardinality = columnIndex + 1;
1358 }
1359
1360 const auto logicalId = columnDesc.GetLogicalId();
1361 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1362
1363 if (!columnDesc.IsAliasColumn())
1364 fDescriptor.fNPhysicalColumns++;
1365 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1366 if (fDescriptor.fHeaderExtension)
1367 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1368
1369 return RResult<void>::Success();
1370}
1371
1373{
1374 const auto id = clusterGroup.GetId();
1375 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1376 return R__FAIL("cluster group id clash");
1377 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1378 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1379 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1380 return RResult<void>::Success();
1381}
1382
1384{
1385 fDescriptor = descriptor.CloneSchema();
1386}
1387
1389{
1390 if (!fDescriptor.fHeaderExtension)
1391 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1392}
1393
1395{
1396 if (fDescriptor.GetNLogicalColumns() == 0)
1397 return;
1398 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1399
1400 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1401 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1402 R__ASSERT(c.IsAliasColumn());
1403 R__ASSERT(id == c.GetLogicalId());
1404 fDescriptor.fColumnDescriptors.erase(id);
1405 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1406 if (link == c.fLogicalColumnId) {
1407 link += offset;
1408 break;
1409 }
1410 }
1411 c.fLogicalColumnId += offset;
1412 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1413 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1414 }
1415
1416 // Patch up column ids in the header extension
1417 if (auto &xHeader = fDescriptor.fHeaderExtension) {
1418 for (auto &columnId : xHeader->fExtendedColumnRepresentations) {
1419 if (columnId >= fDescriptor.GetNPhysicalColumns())
1420 columnId += offset;
1421 }
1422 }
1423}
1424
1426{
1427 auto clusterId = clusterDesc.GetId();
1428 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1429 return R__FAIL("cluster id clash");
1430 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1431 return RResult<void>::Success();
1432}
1433
1436{
1437 // Make sure we have no duplicates
1438 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1439 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1440 return R__FAIL("extra type info duplicates");
1441 }
1442 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1443 return RResult<void>::Success();
1444}
1445
1447{
1448 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1450 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1451 *it = std::move(extraTypeInfoDesc);
1452 else
1453 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1454}
1455
1458{
1459 auto &attrSets = fDescriptor.fAttributeSets;
1460 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1461 return desc.GetName() == name;
1462 }) != attrSets.end()) {
1463 return R__FAIL("attribute sets with duplicate names");
1464 }
1465 attrSets.push_back(std::move(attrSetDesc));
1466 return RResult<void>::Success();
1467}
1468
1473
1479
1486
1492
1499
1504
1510
1515
1521
1527
1532
1537
1542
1547
1549{
1550 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1551 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1552 fName == other.fName;
1553};
1554
1556{
1558 desc.fAnchorLength = fAnchorLength;
1559 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1560 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1561 desc.fAnchorLocator = fAnchorLocator;
1562 desc.fName = fName;
1563 return desc;
1564}
1565
1567{
1568 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1569 return false;
1570 if (fieldDesc.GetTypeName().rfind("std::", 0) == 0)
1571 return false;
1572
1573 auto subFieldId = desc.FindFieldId("_0", fieldDesc.GetId());
1575 return false;
1576
1577 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
1578 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
1579 "std::int64_t", "std::uint64_t"};
1580 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
1581 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
1582}
1583
1585{
1586 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1587 return false;
1588 return (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0);
1589}
1590
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:304
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
char name[80]
Definition TGX11.cxx:145
#define _(A, B)
Definition cfortran.h:108
RResult< ROOT::Experimental::RNTupleAttrSetDescriptor > MoveDescriptor()
Attempt to make an AttributeSet descriptor.
Used to loop over all the Attribute Sets linked to an RNTuple.
Metadata stored for every Attribute Set linked to an RNTuple.
bool operator==(const RNTupleAttrSetDescriptor &other) const
std::uint32_t fAnchorLength
uncompressed size of the linked anchor
A helper class for piece-wise construction of an RClusterDescriptor.
RResult< void > MarkSuppressedColumnRange(ROOT::DescriptorId_t physicalId)
Books the given column ID as being suppressed in this cluster.
RResult< void > CommitColumnRange(ROOT::DescriptorId_t physicalId, std::uint64_t firstElementIndex, std::uint32_t compressionSettings, const RClusterDescriptor::RPageRange &pageRange)
RClusterDescriptorBuilder & AddExtendedColumnRanges(const RNTupleDescriptor &desc)
Add column and page ranges for columns created during late model extension missing in this cluster.
RResult< void > CommitSuppressedColumnRanges(const RNTupleDescriptor &desc)
Sets the first element index and number of elements for all the suppressed column ranges.
RResult< RClusterDescriptor > MoveDescriptor()
Move out the full cluster descriptor including page locations.
A helper class for piece-wise construction of an RClusterGroupDescriptor.
RClusterGroupDescriptorBuilder & EntrySpan(std::uint64_t entrySpan)
RClusterGroupDescriptorBuilder & PageListLocator(const RNTupleLocator &pageListLocator)
static RClusterGroupDescriptorBuilder FromSummary(const RClusterGroupDescriptor &clusterGroupDesc)
RClusterGroupDescriptorBuilder & PageListLength(std::uint64_t pageListLength)
RClusterGroupDescriptorBuilder & MinEntry(std::uint64_t minEntry)
RResult< RClusterGroupDescriptor > MoveDescriptor()
RClusterGroupDescriptorBuilder & ClusterGroupId(ROOT::DescriptorId_t clusterGroupId)
RClusterGroupDescriptorBuilder & NClusters(std::uint32_t nClusters)
RResult< RColumnDescriptor > MakeDescriptor() const
Attempt to make a column descriptor.
A column element encapsulates the translation between basic C++ types and their column representation...
static std::pair< std::uint16_t, std::uint16_t > GetValidBitRange(ROOT::ENTupleColumnType type)
Most types have a fixed on-disk bit width.
RResult< RExtraTypeInfoDescriptor > MoveDescriptor()
A helper class for piece-wise construction of an RFieldDescriptor.
RResult< RFieldDescriptor > MakeDescriptor() const
Attempt to make a field descriptor.
static RFieldDescriptorBuilder FromField(const ROOT::RFieldBase &field)
Make a new RFieldDescriptorBuilder based off a live RNTuple field.
void SetNTuple(const std::string_view name, const std::string_view description)
void SetSchemaFromExisting(const RNTupleDescriptor &descriptor)
Copies the "schema" part of descriptor into the builder's descriptor.
RResult< void > AddColumn(RColumnDescriptor &&columnDesc)
RResult< void > AddAttributeSet(Experimental::RNTupleAttrSetDescriptor &&attrSetDesc)
RResult< void > AddFieldProjection(ROOT::DescriptorId_t sourceId, ROOT::DescriptorId_t targetId)
void ReplaceExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
RResult< void > AddExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
void ShiftAliasColumns(std::uint32_t offset)
Shift column IDs of alias columns by offset
void SetVersion(std::uint16_t versionEpoch, std::uint16_t versionMajor, std::uint16_t versionMinor, std::uint16_t versionPatch)
void BeginHeaderExtension()
Mark the beginning of the header extension; any fields and columns added after a call to this functio...
RResult< void > AddCluster(RClusterDescriptor &&clusterDesc)
RResult< void > EnsureValidDescriptor() const
Checks whether invariants hold:
RResult< void > AddFieldLink(ROOT::DescriptorId_t fieldId, ROOT::DescriptorId_t linkId)
void AddField(const RFieldDescriptor &fieldDesc)
RResult< void > AddClusterGroup(RClusterGroupDescriptor &&clusterGroup)
RResult< void > EnsureFieldExists(ROOT::DescriptorId_t fieldId) const
A helper class for serializing and deserialization of the RNTuple binary format.
The window of element indexes of a particular column in a particular cluster.
Records the partition of data into pages for a particular column in a particular cluster.
static constexpr std::size_t kLargeRangeThreshold
Create the fCumulativeNElements only when its needed, i.e. when there are many pages to search throug...
RPageInfoExtended Find(ROOT::NTupleSize_t idxInCluster) const
Find the page in the RPageRange that contains the given element. The element must exist.
std::size_t ExtendToFitColumnRange(const RColumnRange &columnRange, const ROOT::Internal::RColumnElementBase &element, std::size_t pageSize)
Extend this RPageRange to fit the given RColumnRange.
Metadata for RNTuple clusters.
ROOT::NTupleSize_t fFirstEntryIndex
Clusters can be swapped by adjusting the entry offsets of the cluster and all ranges.
std::unordered_map< ROOT::DescriptorId_t, RColumnRange > fColumnRanges
ROOT::DescriptorId_t fClusterId
RClusterDescriptor Clone() const
bool operator==(const RClusterDescriptor &other) const
RColumnRangeIterable GetColumnRangeIterable() const
Returns an iterator over pairs { columnId, columnRange }. The iteration order is unspecified.
std::unordered_map< ROOT::DescriptorId_t, RPageRange > fPageRanges
std::uint64_t GetNBytesOnStorage() const
Clusters are bundled in cluster groups.
RNTupleLocator fPageListLocator
The page list that corresponds to the cluster group.
RClusterGroupDescriptor Clone() const
std::vector< ROOT::DescriptorId_t > fClusterIds
The cluster IDs can be empty if the corresponding page list is not loaded.
std::uint64_t fMinEntry
The minimum first entry number of the clusters in the cluster group.
std::uint32_t fNClusters
Number of clusters is always known even if the cluster IDs are not (yet) populated.
std::uint64_t fPageListLength
Uncompressed size of the page list.
std::uint64_t fEntrySpan
Number of entries that are (partially for sharded clusters) covered by this cluster group.
bool operator==(const RClusterGroupDescriptor &other) const
RClusterGroupDescriptor CloneSummary() const
Creates a clone without the cluster IDs.
Metadata stored for every column of an RNTuple.
ROOT::DescriptorId_t fPhysicalColumnId
Usually identical to the logical column ID, except for alias columns where it references the shadowed...
bool operator==(const RColumnDescriptor &other) const
ROOT::DescriptorId_t fLogicalColumnId
The actual column identifier, which is the link to the corresponding field.
ROOT::DescriptorId_t fFieldId
Every column belongs to one and only one field.
std::int64_t fFirstElementIndex
The absolute value specifies the index for the first stored element for this column.
std::uint32_t fIndex
A field can be serialized into several columns, which are numbered from zero to $n$.
std::uint16_t fBitsOnStorage
The size in bits of elements of this column.
std::uint16_t fRepresentationIndex
A field may use multiple column representations, which are numbered from zero to $m$.
ROOT::ENTupleColumnType fType
The on-disk column type.
std::optional< RValueRange > fValueRange
Optional value range (used e.g. by quantized real fields)
RColumnDescriptor Clone() const
Get a copy of the descriptor.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
bool operator==(const RExtraTypeInfoDescriptor &other) const
RExtraTypeInfoDescriptor Clone() const
EExtraTypeInfoIds fContentId
Specifies the meaning of the extra information.
std::string fTypeName
The type name the extra information refers to; empty for RNTuple-wide extra information.
std::string fContent
The content format depends on the content ID and may be binary.
std::uint32_t fTypeVersion
Type version the extra type information is bound to.
A field translates read and write calls from/to underlying columns to/from tree values.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ kTraitInvalidField
This field is an instance of RInvalidField and can be safely static_cast to it.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
std::unique_ptr< ROOT::RFieldBase > CreateField(const RNTupleDescriptor &ntplDesc, const ROOT::RCreateFieldOptions &options={}) const
In general, we create a field simply from the C++ type name.
std::uint32_t fFieldVersion
The version of the C++-type-to-column translation mechanics.
ROOT::DescriptorId_t fFieldId
RFieldDescriptor Clone() const
Get a copy of the descriptor.
std::uint64_t fNRepetitions
The number of elements per entry for fixed-size arrays.
std::uint32_t fColumnCardinality
The number of columns in the column representations of the field.
ROOT::DescriptorId_t fProjectionSourceId
For projected fields, the source field ID.
bool IsCustomEnum(const RNTupleDescriptor &desc) const R__DEPRECATED(6
bool operator==(const RFieldDescriptor &other) const
bool IsCustomClass() const R__DEPRECATED(6
bool IsStdAtomic() const R__DEPRECATED(6
std::string fFieldDescription
Free text set by the user.
ROOT::DescriptorId_t fParentId
Establishes sub field relationships, such as classes and collections.
std::string fTypeAlias
A typedef or using directive that resolved to the type name during field creation.
ROOT::ENTupleStructure fStructure
The structural information carried by this field in the data model tree.
std::vector< ROOT::DescriptorId_t > fLinkIds
The pointers in the other direction from parent to children.
std::string fFieldName
The leaf name, not including parent fields.
bool fIsSoACollection
Indicates if this is a collection that should be represented in memory by a SoA layout.
std::uint32_t fTypeVersion
The version of the C++ type itself.
std::string fTypeName
The C++ type that was used when writing the field.
std::vector< ROOT::DescriptorId_t > fLogicalColumnIds
The ordered list of columns attached to this field: first by representation index then by column inde...
std::optional< std::uint32_t > fTypeChecksum
For custom classes, we store the ROOT TClass reported checksum to facilitate the use of I/O rules tha...
Used in RFieldBase::Check() to record field creation failures.
Definition RField.hxx:97
@ kGeneric
Generic unrecoverable error.
@ kUnknownStructure
The field could not be created because its descriptor had an unknown structural role.
Used to loop over all the clusters of an RNTuple (in unspecified order)
Used to loop over all the cluster groups of an RNTuple (in unspecified order)
Used to loop over a field's associated columns.
std::vector< ROOT::DescriptorId_t > fColumns
The descriptor ids of the columns ordered by field, representation, and column index.
RColumnDescriptorIterable(const RNTupleDescriptor &ntuple, const RFieldDescriptor &fieldDesc)
Used to loop over all the extra type info record of an RNTuple (in unspecified order)
Used to loop over a field's child fields.
std::vector< ROOT::DescriptorId_t > GetTopMostFields(const RNTupleDescriptor &desc) const
Return a vector containing the IDs of the top-level fields defined in the extension header,...
The on-storage metadata of an RNTuple.
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
ROOT::DescriptorId_t FindNextClusterId(ROOT::DescriptorId_t clusterId) const
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
std::set< unsigned int > fFeatureFlags
std::unordered_map< ROOT::DescriptorId_t, RClusterGroupDescriptor > fClusterGroupDescriptors
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::uint64_t fNPhysicalColumns
Updated by the descriptor builder when columns are added.
std::vector< Experimental::RNTupleAttrSetDescriptor > fAttributeSets
List of AttributeSets linked to this RNTuple.
ROOT::DescriptorId_t fFieldZeroId
Set by the descriptor builder.
std::uint64_t fNEntries
Updated by the descriptor builder when the cluster groups are added.
RClusterGroupDescriptorIterable GetClusterGroupIterable() const
RColumnDescriptorIterable GetColumnIterable() const
bool operator==(const RNTupleDescriptor &other) const
std::uint64_t fOnDiskFooterSize
Like fOnDiskHeaderSize, contains both cluster summaries and page locations.
std::uint16_t fVersionMinor
Set by the descriptor builder when deserialized.
ROOT::DescriptorId_t FindClusterId(ROOT::NTupleSize_t entryIdx) const
std::vector< std::uint64_t > GetFeatureFlags() const
ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
std::unique_ptr< ROOT::RNTupleModel > CreateModel(const RCreateModelOptions &options=RCreateModelOptions()) const
Re-create the C++ model from the stored metadata.
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
std::unordered_map< ROOT::DescriptorId_t, RClusterDescriptor > fClusterDescriptors
Potentially a subset of all the available clusters.
std::size_t GetNClusters() const
ROOT::DescriptorId_t FindPhysicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
RExtraTypeInfoDescriptorIterable GetExtraTypeInfoIterable() const
const RHeaderExtension * GetHeaderExtension() const
Return header extension information; if the descriptor does not have a header extension,...
std::uint64_t fNClusters
Updated by the descriptor builder when the cluster groups are added.
std::uint64_t fOnDiskHeaderXxHash3
Set by the descriptor builder when deserialized.
const RClusterDescriptor & GetClusterDescriptor(ROOT::DescriptorId_t clusterId) const
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
std::string fName
The RNTuple name needs to be unique in a given storage location (file)
std::uint64_t fOnDiskHeaderSize
Set by the descriptor builder when deserialized.
RResult< void > DropClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId)
std::uint16_t fVersionMajor
Set by the descriptor builder when deserialized.
std::vector< ROOT::DescriptorId_t > fSortedClusterGroupIds
References cluster groups sorted by entry range and thus allows for binary search.
std::unordered_map< ROOT::DescriptorId_t, RColumnDescriptor > fColumnDescriptors
ROOT::DescriptorId_t FindLogicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
std::unordered_map< ROOT::DescriptorId_t, RFieldDescriptor > fFieldDescriptors
ROOT::NTupleSize_t GetNElements(ROOT::DescriptorId_t physicalColumnId) const
RResult< void > AddClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId, std::vector< RClusterDescriptor > &clusterDescs)
Methods to load and drop cluster group details (cluster IDs and page locations)
std::uint16_t fVersionPatch
Set by the descriptor builder when deserialized.
std::string fDescription
Free text from the user.
ROOT::Experimental::RNTupleAttrSetDescriptorIterable GetAttrSetIterable() const
RFieldDescriptorIterable GetTopLevelFields() const
std::uint16_t fVersionEpoch
Set by the descriptor builder when deserialized.
std::vector< RExtraTypeInfoDescriptor > fExtraTypeInfoDescriptors
RNTupleDescriptor Clone() const
std::string GetQualifiedFieldName(ROOT::DescriptorId_t fieldId) const
Walks up the parents of the field ID and returns a field name of the form a.b.c.d In case of invalid ...
bool FieldTypeNamesMayNeedFixup() const
ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
RClusterDescriptorIterable GetClusterIterable() const
RNTupleDescriptor CloneSchema() const
Creates a descriptor containing only the schema information about this RNTuple, i....
std::uint64_t fGeneration
The generation of the descriptor.
ROOT::DescriptorId_t FindPrevClusterId(ROOT::DescriptorId_t clusterId) const
std::unique_ptr< RHeaderExtension > fHeaderExtension
Generic information about the physical location of data.
static std::unique_ptr< RNTupleModel > Create()
static std::unique_ptr< RNTupleModel > CreateBare()
Creates a "bare model", i.e. an RNTupleModel with no default entry.
static constexpr std::uint16_t kVersionPatch
Definition RNTuple.hxx:82
static constexpr std::uint16_t kVersionMajor
Definition RNTuple.hxx:80
static constexpr std::uint16_t kVersionEpoch
Definition RNTuple.hxx:79
static constexpr std::uint16_t kVersionMinor
Definition RNTuple.hxx:81
const_iterator begin() const
const_iterator end() const
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:198
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
ROOT::DescriptorId_t CallFindClusterIdOn(const ROOT::RNTupleDescriptor &desc, ROOT::NTupleSize_t entryIdx)
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
ROOT::RResult< std::unique_ptr< ROOT::RFieldBase > > CallFieldBaseCreate(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
void FixupFieldTypeName(ROOT::RFieldDescriptor &fieldDesc)
bool IsCustomEnumFieldDesc(const RNTupleDescriptor &desc, const RFieldDescriptor &fieldDesc)
Tells if the field describes a user-defined enum type.
std::vector< ROOT::Internal::RNTupleClusterBoundaries > GetClusterBoundaries(const RNTupleDescriptor &desc)
Return the cluster boundaries for each cluster in this RNTuple.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
bool IsStdAtomicFieldDesc(const RFieldDescriptor &fieldDesc)
Tells if the field describes a std::atomic<T> type.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr NTupleSize_t kInvalidNTupleIndex
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
Additional information about a page in an in-memory RPageRange.
Information about a single page in the context of a cluster's page range.
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338