Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RNTupleDescriptor.cxx
Go to the documentation of this file.
1/// \file RNTupleDescriptor.cxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \author Javier Lopez-Gomez <javier.lopez.gomez@cern.ch>
4/// \date 2018-10-04
5
6/*************************************************************************
7 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#include <ROOT/RError.hxx>
15#include <ROOT/RFieldBase.hxx>
16#include <ROOT/RNTuple.hxx>
18#include <ROOT/RNTupleModel.hxx>
19#include <ROOT/RNTupleTypes.hxx>
20#include <ROOT/RNTupleUtils.hxx>
21#include <ROOT/RPage.hxx>
22#include <string_view>
23
24#include <RZip.h>
25#include <TError.h>
26
27#include <algorithm>
28#include <cstdint>
29#include <deque>
30#include <functional>
31#include <iostream>
32#include <set>
33#include <utility>
34
36{
37 static std::string gEmpty;
38 return gEmpty;
39}
40
42{
43 return fFieldId == other.fFieldId && fFieldVersion == other.fFieldVersion && fTypeVersion == other.fTypeVersion &&
44 GetFieldName() == other.GetFieldName() && GetFieldDescription() == other.GetFieldDescription() &&
45 GetTypeName() == other.GetTypeName() && GetTypeAlias() == other.GetTypeAlias() &&
46 fNRepetitions == other.fNRepetitions && fStructure == other.fStructure && fParentId == other.fParentId &&
47 fProjectionSourceId == other.fProjectionSourceId && fLinkIds == other.fLinkIds &&
48 fLogicalColumnIds == other.fLogicalColumnIds && fTypeChecksum == other.fTypeChecksum &&
49 fIsSoACollection == other.fIsSoACollection;
50}
51
53{
54 fFieldId = source.fFieldId;
55 fFieldVersion = source.fFieldVersion;
56 fTypeVersion = source.fTypeVersion;
57 fNRepetitions = source.fNRepetitions;
58 fStructure = source.fStructure;
59 fParentId = source.fParentId;
60 fProjectionSourceId = source.fProjectionSourceId;
61 fLinkIds = source.fLinkIds;
62 fColumnCardinality = source.fColumnCardinality;
63 fLogicalColumnIds = source.fLogicalColumnIds;
64 fTypeChecksum = source.fTypeChecksum;
65 fIsSoACollection = source.fIsSoACollection;
66
67 fFieldName = stringPool.Intern(source.GetFieldName());
68 fFieldDescription = stringPool.Intern(source.GetFieldDescription());
69 fTypeName = stringPool.Intern(source.GetTypeName());
70 fTypeAlias = stringPool.Intern(source.GetTypeAlias());
71}
72
74{
75 RFieldDescriptor clone;
76 clone.fStringPool = std::make_unique<Internal::RStringPool>();
77 clone.InitFrom(*this, *clone.fStringPool);
78 return clone;
79}
80
81std::unique_ptr<ROOT::RFieldBase>
83{
84 if (GetStructure() == ROOT::ENTupleStructure::kStreamer) {
85 auto streamerField = std::make_unique<ROOT::RStreamerField>(GetFieldName(), GetTypeName());
86 streamerField->SetOnDiskId(fFieldId);
87 return streamerField;
88 }
89
90 // The structure may be unknown if the descriptor comes from a deserialized field with an unknown structural role.
91 // For forward compatibility, we allow this case and return an InvalidField.
92 if (GetStructure() == ROOT::ENTupleStructure::kUnknown) {
93 if (options.GetReturnInvalidOnError()) {
94 auto invalidField = std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), "",
96 invalidField->SetOnDiskId(fFieldId);
97 return invalidField;
98 } else {
99 throw RException(R__FAIL("unexpected on-disk field structure value for field \"" + GetFieldName() + "\""));
100 }
101 }
102
103 // Untyped records and collections
104 if (GetTypeName().empty()) {
105 switch (GetStructure()) {
107 std::vector<std::unique_ptr<ROOT::RFieldBase>> memberFields;
108 memberFields.reserve(fLinkIds.size());
109 for (auto id : fLinkIds) {
110 const auto &memberDesc = ntplDesc.GetFieldDescriptor(id);
111 auto field = memberDesc.CreateField(ntplDesc, options);
113 return field;
114 memberFields.emplace_back(std::move(field));
115 }
116 auto recordField = std::make_unique<ROOT::RRecordField>(GetFieldName(), std::move(memberFields));
117 recordField->SetOnDiskId(fFieldId);
118 return recordField;
119 }
121 if (fLinkIds.size() != 1) {
122 throw RException(R__FAIL("unsupported untyped collection for field \"" + GetFieldName() + "\""));
123 }
124 auto itemField = ntplDesc.GetFieldDescriptor(fLinkIds[0]).CreateField(ntplDesc, options);
126 return itemField;
127 auto collectionField = ROOT::RVectorField::CreateUntyped(GetFieldName(), std::move(itemField));
128 collectionField->SetOnDiskId(fFieldId);
129 return collectionField;
130 }
131 default: throw RException(R__FAIL("unsupported untyped field structure for field \"" + GetFieldName() + "\""));
132 }
133 }
134
135 try {
136 const auto &fieldName = GetFieldName();
137 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
138 // NOTE: Unwrap() here may throw an exception, hence the try block.
139 // If options.fReturnInvalidOnError is false we just rethrow it, otherwise we return an InvalidField wrapping the
140 // error.
141 auto field = ROOT::Internal::CallFieldBaseCreate(fieldName, typeName, options, &ntplDesc, fFieldId).Unwrap();
142 field->SetOnDiskId(fFieldId);
143
144 for (auto &subfield : *field) {
145 const auto subfieldId = ntplDesc.FindFieldId(subfield.GetFieldName(), subfield.GetParent()->GetOnDiskId());
146 subfield.SetOnDiskId(subfieldId);
148 auto &invalidField = static_cast<ROOT::RInvalidField &>(subfield);
149 // A subfield being invalid "infects" its entire ancestry.
150 return invalidField.Clone(fieldName);
151 }
152 }
153
154 return field;
155 } catch (const RException &ex) {
156 if (options.GetReturnInvalidOnError())
157 return std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), ex.what(),
159 else
160 throw ex;
161 }
162}
163
164////////////////////////////////////////////////////////////////////////////////
165
167{
168 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
169 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
170 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
171 fValueRange == other.fValueRange;
172}
173
175{
176 RColumnDescriptor clone;
177 clone.fLogicalColumnId = fLogicalColumnId;
178 clone.fPhysicalColumnId = fPhysicalColumnId;
179 clone.fBitsOnStorage = fBitsOnStorage;
180 clone.fType = fType;
181 clone.fFieldId = fFieldId;
182 clone.fIndex = fIndex;
183 clone.fFirstElementIndex = fFirstElementIndex;
184 clone.fRepresentationIndex = fRepresentationIndex;
185 if (fValueRange)
186 clone.fValueRange = std::make_unique<RValueRange>(*fValueRange);
187 return clone;
188}
189
190////////////////////////////////////////////////////////////////////////////////
191
194{
195 if (!fCumulativeNElements) {
196 // Small range, just iterate through fPageInfos
199 for (const auto &pi : fPageInfos) {
200 if (firstInPage + pi.GetNElements() > idxInCluster) {
202 }
203 pageNumber++;
204 firstInPage += pi.GetNElements();
205 }
206 R__ASSERT(false);
207 }
208
209 const auto N = fCumulativeNElements->size();
210 R__ASSERT(N > 0);
211 R__ASSERT(N == fPageInfos.size());
212
213 std::size_t left = 0;
214 std::size_t right = N - 1;
215 std::size_t midpoint = N;
216 while (left <= right) {
217 midpoint = (left + right) / 2;
218 if ((*fCumulativeNElements)[midpoint] <= idxInCluster) {
219 left = midpoint + 1;
220 continue;
221 }
222
223 if ((midpoint == 0) || ((*fCumulativeNElements)[midpoint - 1] <= idxInCluster))
224 break;
225
226 right = midpoint - 1;
227 }
229
230 auto pageInfo = fPageInfos[midpoint];
231 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : (*fCumulativeNElements)[midpoint - 1];
233 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
235}
236
237std::size_t
240 std::size_t pageSize)
241{
242 R__ASSERT(fPhysicalColumnId == columnRange.GetPhysicalColumnId());
243 R__ASSERT(!columnRange.IsSuppressed());
244
245 const auto nElements =
246 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
247 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
248 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
249
251 return 0U;
252 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
253
254 std::vector<RPageInfo> pageInfos;
255 // Synthesize new `RPageInfo`s as needed
256 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
260 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
263 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
264 pageInfo.SetLocator(locator);
265 pageInfos.emplace_back(pageInfo);
266 nRemainingElements -= pageInfo.GetNElements();
267 }
268
269 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
270 std::make_move_iterator(fPageInfos.end()));
271 std::swap(fPageInfos, pageInfos);
273}
274
276{
277 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
278 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
279}
280
282{
283 std::uint64_t nbytes = 0;
284 for (const auto &pr : fPageRanges) {
285 for (const auto &pi : pr.second.GetPageInfos()) {
286 nbytes += pi.GetLocator().GetNBytesOnStorage();
287 }
288 }
289 return nbytes;
290}
291
293{
294 RClusterDescriptor clone;
295 clone.fClusterId = fClusterId;
296 clone.fFirstEntryIndex = fFirstEntryIndex;
297 clone.fNEntries = fNEntries;
298 clone.fColumnRanges = fColumnRanges;
299 for (const auto &d : fPageRanges)
300 clone.fPageRanges.emplace(d.first, d.second.Clone());
301 return clone;
302}
303
304////////////////////////////////////////////////////////////////////////////////
305
307{
308 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
309}
310
312{
314 clone.fContentId = fContentId;
315 clone.fTypeVersion = fTypeVersion;
316 clone.fTypeName = fTypeName;
317 clone.fContent = fContent;
318 return clone;
319}
320
321////////////////////////////////////////////////////////////////////////////////
322
324{
325 // We need to make sure that for cloning fields, the empty string that is not explicitly entered for
326 // default constructed field descriptors is available.
327 fStringPool->Intern("");
328}
329
331{
332 // clang-format off
333 return fName == other.fName &&
334 fDescription == other.fDescription &&
335 fNEntries == other.fNEntries &&
336 fGeneration == other.fGeneration &&
337 fFieldZeroId == other.fFieldZeroId &&
338 fFieldDescriptors == other.fFieldDescriptors &&
339 fColumnDescriptors == other.fColumnDescriptors &&
340 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
341 fClusterDescriptors == other.fClusterDescriptors;
342 // clang-format on
343}
344
345ROOT::NTupleSize_t ROOT::RNTupleDescriptor::GetNElements(ROOT::DescriptorId_t physicalColumnId) const
346{
348 for (const auto &cd : fClusterDescriptors) {
349 if (!cd.second.ContainsColumn(physicalColumnId))
350 continue;
351 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
352 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
353 }
354 return result;
355}
356
357////////////////////////////////////////////////////////////////////////////////
358/// Return the cluster boundaries for each cluster in this RNTuple.
359std::vector<ROOT::Internal::RNTupleClusterBoundaries>
361{
362 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
363 boundaries.reserve(desc.GetNClusters());
364 R__ASSERT(desc.GetNClusters() == desc.GetNActiveClusters());
365 for (const auto &clusterDesc : desc.GetActiveClusterIterable()) {
366 R__ASSERT(clusterDesc.GetNEntries() > 0);
367 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
368 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
369 }
370 return boundaries;
371}
372
375{
376 std::string leafName(fieldName);
377 auto posDot = leafName.find_last_of('.');
378 if (posDot != std::string::npos) {
379 auto parentName = leafName.substr(0, posDot);
380 leafName = leafName.substr(posDot + 1);
381 parentId = FindFieldId(parentName, parentId);
382 }
383 auto itrFieldDesc = fFieldDescriptors.find(parentId);
384 if (itrFieldDesc == fFieldDescriptors.end())
386 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
387 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
388 return linkId;
389 }
391}
392
394{
396 return "";
397
398 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
399 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
400 if (prefix.empty())
401 return fieldDescriptor.GetFieldName();
402 return prefix + "." + fieldDescriptor.GetFieldName();
403}
404
406{
407 R__ASSERT(fVersionEpoch == 1);
408 return fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1;
409}
410
412{
413 std::string typeName = fieldDesc.GetTypeName();
414
415 if (FieldTypeNamesMayNeedFixup()) {
416 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
417 }
418
419 return typeName;
420}
421
423{
424 return FindFieldId(fieldName, GetFieldZeroId());
425}
426
428 std::uint32_t columnIndex,
429 std::uint16_t representationIndex) const
430{
431 auto itr = fFieldDescriptors.find(fieldId);
432 if (itr == fFieldDescriptors.cend())
434 if (columnIndex >= itr->second.GetColumnCardinality())
436 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
437 if (itr->second.GetLogicalColumnIds().size() <= idx)
439 return itr->second.GetLogicalColumnIds()[idx];
440}
441
443 std::uint32_t columnIndex,
444 std::uint16_t representationIndex) const
445{
446 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
449 return GetColumnDescriptor(logicalId).GetPhysicalId();
450}
451
454{
455 if (GetNClusterGroups() == 0)
457
458 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
459
460 std::size_t cgLeft = 0;
461 std::size_t cgRight = GetNClusterGroups() - 1;
462 while (cgLeft <= cgRight) {
463 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
464 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
465 R__ASSERT(!clusterIds.empty());
466
467 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
468 // this may happen if the RNTuple has an empty schema
469 if (!clusterDesc.ContainsColumn(physicalColumnId))
471
472 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
474 // Look into the lower half of cluster groups
476 cgRight = cgMidpoint - 1;
477 continue;
478 }
479
480 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
481 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
482 // Look into the upper half of cluster groups
483 cgLeft = cgMidpoint + 1;
484 continue;
485 }
486
487 // Binary search in the current cluster group; since we already checked the element range boundaries,
488 // the element must be in that cluster group.
489 std::size_t clusterLeft = 0;
490 std::size_t clusterRight = clusterIds.size() - 1;
491 while (clusterLeft <= clusterRight) {
492 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
494 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
495
496 if (columnRange.Contains(index))
497 return clusterId;
498
499 if (columnRange.GetFirstElementIndex() > index) {
502 continue;
503 }
504
505 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
507 continue;
508 }
509 }
510 R__ASSERT(false);
511 }
513}
514
516{
517 if (GetNClusterGroups() == 0)
519
520 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
521
522 std::size_t cgLeft = 0;
523 std::size_t cgRight = GetNClusterGroups() - 1;
524 while (cgLeft <= cgRight) {
525 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
526 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
527
528 if (cgDesc.GetMinEntry() > entryIdx) {
530 cgRight = cgMidpoint - 1;
531 continue;
532 }
533
534 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
535 cgLeft = cgMidpoint + 1;
536 continue;
537 }
538
539 // Binary search in the current cluster group; since we already checked the element range boundaries,
540 // the element must be in that cluster group.
541 const auto &clusterIds = cgDesc.GetClusterIds();
542 R__ASSERT(!clusterIds.empty());
543 std::size_t clusterLeft = 0;
544 std::size_t clusterRight = clusterIds.size() - 1;
545 while (clusterLeft <= clusterRight) {
546 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
547 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
548
549 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
552 continue;
553 }
554
555 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
557 continue;
558 }
559
561 }
562 R__ASSERT(false);
563 }
565}
566
567ROOT::DescriptorId_t ROOT::RNTupleDescriptor::FindNextClusterId(ROOT::DescriptorId_t clusterId) const
568{
569 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
570 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
571 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
572 // binary search code path remains tested.
573 const auto &clusterDesc = GetClusterDescriptor(clusterId);
574 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
575 return FindClusterId(firstEntryInNextCluster);
576}
577
578ROOT::DescriptorId_t ROOT::RNTupleDescriptor::FindPrevClusterId(ROOT::DescriptorId_t clusterId) const
579{
580 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
581 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
582 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
583 // binary search code path remains tested.
584 const auto &clusterDesc = GetClusterDescriptor(clusterId);
585 if (clusterDesc.GetFirstEntryIndex() == 0)
587 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
588}
589
590std::vector<ROOT::DescriptorId_t>
592{
593 std::vector<ROOT::DescriptorId_t> fields;
594 for (const auto fieldId : fFieldIdsOrder) {
595 if (fFieldIdsLookup.count(desc.GetFieldDescriptor(fieldId).GetParentId()) == 0)
596 fields.emplace_back(fieldId);
597 }
598 return fields;
599}
600
606
608 : fNTuple(ntuple)
609{
610 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
611
612 while (!fieldIdQueue.empty()) {
613 auto currFieldId = fieldIdQueue.front();
614 fieldIdQueue.pop_front();
615
616 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
617 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
618
619 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
620 auto fieldId = field.GetId();
621 fieldIdQueue.push_back(fieldId);
622 }
623 }
624}
625
626std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
627{
628 std::vector<std::uint64_t> result;
629 unsigned int base = 0;
630 std::uint64_t flags = 0;
631 for (auto f : fFeatureFlags) {
632 if ((f > 0) && ((f % 64) == 0))
633 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
634 while (f > base + 64) {
635 result.emplace_back(flags);
636 flags = 0;
637 base += 64;
638 }
639 // Note that in the following iterations of the outer loop over fFeatureFlags, we can never have the situation
640 // where base is larger than the feature flag, because they are stored ordered in the std::set.
641 assert(f >= base);
642 f -= base;
643 flags |= std::uint64_t(1) << f;
644 }
645 result.emplace_back(flags);
646 return result;
647}
648
650 std::vector<RClusterDescriptor> &clusterDescs)
651{
653 if (iter == fClusterGroupDescriptors.end())
654 return R__FAIL("invalid attempt to add details of unknown cluster group");
655 if (iter->second.HasClusterDetails())
656 return R__FAIL("invalid attempt to re-populate cluster group details");
657 if (iter->second.GetNClusters() != clusterDescs.size())
658 return R__FAIL("mismatch of number of clusters");
659
660 std::vector<ROOT::DescriptorId_t> clusterIds;
661 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
662 clusterIds.emplace_back(clusterDescs[i].GetId());
663 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
664 if (!success) {
665 return R__FAIL("invalid attempt to re-populate existing cluster");
666 }
667 }
669 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
670 });
672 cgBuilder.AddSortedClusters(clusterIds);
673 iter->second = cgBuilder.MoveDescriptor().Unwrap();
674 return RResult<void>::Success();
675}
676
678{
680 if (iter == fClusterGroupDescriptors.end())
681 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
682 if (!iter->second.HasClusterDetails())
683 return R__FAIL("invalid attempt to drop details of cluster group summary");
684
685 for (auto clusterId : iter->second.GetClusterIds())
687 iter->second = iter->second.CloneSummary();
688 return RResult<void>::Success();
689}
690
691std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
692{
693 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
694 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
695 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
696 for (const auto &colDesc : GetColumnIterable()) {
698 auto fieldId = colDesc.GetFieldId();
699 while (1) {
700 const auto &field = GetFieldDescriptor(fieldId);
701 if (field.GetParentId() == GetFieldZeroId())
702 break;
703 fieldId = field.GetParentId();
704 }
705 invalidFields.insert(fieldId);
706
707 // No need to look for all invalid fields if we're gonna error out anyway
708 if (!options.GetForwardCompatible())
709 break;
710 }
711 }
712
713 if (!options.GetForwardCompatible() && !invalidFields.empty())
715 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
716 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
717
718 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
719 fieldZero->SetOnDiskId(GetFieldZeroId());
720 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
721 : RNTupleModel::Create(std::move(fieldZero));
723 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
724 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
725 for (const auto &topDesc : GetTopLevelFields()) {
726 if (invalidFields.count(topDesc.GetId()) > 0) {
727 // Field contains invalid columns: skip it
728 continue;
729 }
730
731 auto field = topDesc.CreateField(*this, createFieldOpts);
732
733 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
734 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
735 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
736 // in the first place, so we don't need to check for that flag again).
737 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
738 const auto &invalid = static_cast<const RInvalidField &>(*field);
739 const auto cat = invalid.GetCategory();
741 if (mustThrow)
742 throw RException(R__FAIL(invalid.GetError()));
743
744 // Not a hard error: skip the field and go on.
745 continue;
746 }
747
748 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
749 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
750 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
751 });
752 } else {
753 model->AddField(std::move(field));
754 }
755 }
756 model->Freeze();
757 return model;
758}
759
761{
762 RNTupleDescriptor clone;
763 clone.fName = fName;
768 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
769 // and therefore not represent the actual sources's header.
770 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
771
773 shareStringPool = false;
774
775 if (shareStringPool)
776 clone.fStringPool = fStringPool;
777
779 // In case we are copying the schema from a pre-1.0.0.1 RNTuple we need to patch all field type names
780 // to use the proper normalization. In this case, the clone will get a new string pool because we may need
781 // to insert.
782 for (const auto &d : fFieldDescriptors) {
784 fieldDescBuilder.TypeName(ROOT::Internal::GetRenormalizedTypeName(d.second.GetTypeName()));
785 clone.fFieldDescriptors.emplace(d.first, fieldDescBuilder.MoveDescriptor().Unwrap());
786 }
787 } else {
788 for (const auto &d : fFieldDescriptors) {
789 clone.fFieldDescriptors.emplace(
791 }
792 }
793
794 for (const auto &d : fColumnDescriptors)
795 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
796
797 for (const auto &d : fExtraTypeInfoDescriptors)
798 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
800 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
801
802 return clone;
803}
804
806{
807 RNTupleDescriptor clone = CloneSchema(true /* shareStringPool */);
808 clone.fStringPool->Freeze();
809
814
818 clone.fNEntries = fNEntries;
819 clone.fNClusters = fNClusters;
820 clone.fGeneration = fGeneration;
821 for (const auto &d : fClusterGroupDescriptors)
822 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
824 for (const auto &d : fClusterDescriptors)
825 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
826 for (const auto &d : fAttributeSets)
827 clone.fAttributeSets.emplace_back(d.Clone());
828 return clone;
829}
830
831////////////////////////////////////////////////////////////////////////////////
832
834{
835 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
836 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
837}
838
840{
842 clone.fClusterGroupId = fClusterGroupId;
843 clone.fPageListLocator = fPageListLocator;
844 clone.fPageListLength = fPageListLength;
845 clone.fMinEntry = fMinEntry;
846 clone.fEntrySpan = fEntrySpan;
847 clone.fNClusters = fNClusters;
848 return clone;
849}
850
852{
853 RClusterGroupDescriptor clone = CloneSummary();
854 clone.fClusterIds = fClusterIds;
855 return clone;
856}
857
858////////////////////////////////////////////////////////////////////////////////
859
862 std::uint64_t firstElementIndex,
863 std::uint32_t compressionSettings,
865{
866 if (physicalId != pageRange.fPhysicalColumnId)
867 return R__FAIL("column ID mismatch");
868 if (fCluster.fColumnRanges.count(physicalId) > 0)
869 return R__FAIL("column ID conflict");
871 for (const auto &pi : pageRange.fPageInfos) {
872 columnRange.IncrementNElements(pi.GetNElements());
873 }
874 fCluster.fPageRanges[physicalId] = pageRange.Clone();
875 fCluster.fColumnRanges[physicalId] = columnRange;
876 return RResult<void>::Success();
877}
878
881{
882 if (fCluster.fColumnRanges.count(physicalId) > 0)
883 return R__FAIL("column ID conflict");
884
886 columnRange.SetPhysicalColumnId(physicalId);
887 columnRange.SetIsSuppressed(true);
888 fCluster.fColumnRanges[physicalId] = columnRange;
889 return RResult<void>::Success();
890}
891
894{
895 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
896 if (!columnRange.IsSuppressed())
897 continue;
898 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
899
900 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
901 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
902 // We expect only few columns and column representations per field, so we do a linear search
903 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
904 const auto &otherColumnDesc = desc.GetColumnDescriptor(otherColumnLogicalId);
905 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
906 continue;
907 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
908 continue;
909
910 // Found corresponding column of a different column representation
911 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
912 if (otherColumnRange.IsSuppressed())
913 continue;
914
915 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
916 columnRange.SetNElements(otherColumnRange.GetNElements());
917 break;
918 }
919
920 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
921 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
922 std::to_string(columnRange.GetPhysicalColumnId()) +
923 ", cluster ID: " + std::to_string(fCluster.GetId()));
924 }
925 }
926 return RResult<void>::Success();
927}
928
931{
932 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
933 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
934 /// parent field in the hierarchy.
936 const auto &visitField, const auto &enterSubtree) -> void {
938 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
939 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
941 }
942 };
943
944 // Extended columns can only be part of the header extension
945 if (!desc.GetHeaderExtension())
946 return *this;
947
948 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
949 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
950 for (const auto &topLevelField : desc.GetTopLevelFields()) {
952 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
953 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
954 for (const auto &c : desc.GetColumnIterable(fieldId)) {
955 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
956 auto &columnRange = fCluster.fColumnRanges[physicalId];
957
958 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
959 // extension won't have on-disk metadata for the clusters that were already committed before the model
960 // was extended. Therefore, these need to be synthetically initialized upon reading.
961 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
962 columnRange.SetPhysicalColumnId(physicalId);
963 columnRange.SetFirstElementIndex(0);
964 columnRange.SetNElements(0);
965 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
966 }
967 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
968 // number of elements should have been if the column was not deferred; fix those and let
969 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
970 if (c.IsDeferredColumn()) {
971 if (c.GetRepresentationIndex() == 0) {
972 // Note that a deferred column (i.e, whose first element index is > 0) for the 0th representation
973 // index already met the criteria of `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a
974 // principal column reachable from the field zero excluding subfields of collection and variant
975 // fields.
976 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
977 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
978 } else {
979 // Deferred representations which are not the first cannot count on the number of elements being
980 // equal to Entries * nRepetitions because they might have been added in a later cluster. But they
981 // can rely on the first representation having the correct FirstElement/NElements (by definition
982 // the first representation cannot be an "extended" one), therefore they can just copy the value
983 // from it.
984 const auto &field = desc.GetFieldDescriptor(fieldId);
985 const auto firstReprColumnId = field.GetLogicalColumnIds()[c.GetIndex()];
986 const auto &firstReprColumnRange = fCluster.fColumnRanges[firstReprColumnId];
987 columnRange.SetFirstElementIndex(firstReprColumnRange.GetFirstElementIndex());
988 columnRange.SetNElements(firstReprColumnRange.GetNElements());
989 }
990 if (!columnRange.IsSuppressed()) {
991 auto &pageRange = fCluster.fPageRanges[physicalId];
992 pageRange.fPhysicalColumnId = physicalId;
993 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
994 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
995 }
996 } else if (!columnRange.IsSuppressed()) {
997 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
998 }
999 }
1000 },
1002 }
1003 return *this;
1004}
1005
1007{
1008 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
1009 return R__FAIL("unset cluster ID");
1010 if (fCluster.fNEntries == 0)
1011 return R__FAIL("empty cluster");
1012 for (auto &pr : fCluster.fPageRanges) {
1013 if (fCluster.fColumnRanges.count(pr.first) == 0) {
1014 return R__FAIL("missing column range");
1015 }
1016 pr.second.fCumulativeNElements.reset();
1017 const auto nPages = pr.second.fPageInfos.size();
1019 pr.second.fCumulativeNElements = std::make_unique<std::vector<NTupleSize_t>>();
1020 pr.second.fCumulativeNElements->reserve(nPages);
1022 for (const auto &pi : pr.second.fPageInfos) {
1023 sum += pi.GetNElements();
1024 pr.second.fCumulativeNElements->emplace_back(sum);
1025 }
1026 }
1027 }
1029 std::swap(result, fCluster);
1030 return result;
1031}
1032
1033////////////////////////////////////////////////////////////////////////////////
1034
1037{
1039 builder.ClusterGroupId(clusterGroupDesc.GetId())
1040 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1041 .PageListLength(clusterGroupDesc.GetPageListLength())
1042 .MinEntry(clusterGroupDesc.GetMinEntry())
1043 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1044 .NClusters(clusterGroupDesc.GetNClusters());
1045 return builder;
1046}
1047
1049{
1050 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1051 return R__FAIL("unset cluster group ID");
1053 std::swap(result, fClusterGroup);
1054 return result;
1055}
1056
1057////////////////////////////////////////////////////////////////////////////////
1058
1060{
1061 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1062 throw RException(R__FAIL("invalid extra type info content id"));
1064 std::swap(result, fExtraTypeInfo);
1065 return result;
1066}
1067
1068////////////////////////////////////////////////////////////////////////////////
1069
1071{
1072 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1073 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1074 return RResult<void>::Success();
1075}
1076
1078{
1079 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1080 return R__FAIL("unset or unsupported RNTuple epoch version");
1081 }
1082
1083 // Reuse field name validity check
1084 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1085 if (!validName) {
1087 }
1088
1089 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1090 // parent not properly set?
1091 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1092 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1093 }
1094
1095 // Same number of columns in every column representation?
1096 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1097 if (columnCardinality == 0)
1098 continue;
1099
1100 // In AddColumn, we already checked that all but the last representation are complete.
1101 // Check that the last column representation is complete, i.e. has all columns.
1102 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1103 const auto nColumns = logicalColumnIds.size();
1104 // If we have only a single column representation, the following condition is true by construction
1105 if ((nColumns + 1) == columnCardinality)
1106 continue;
1107
1108 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1109 if (lastColumn.GetIndex() + 1 != columnCardinality)
1110 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1111 }
1112
1113 return RResult<void>::Success();
1114}
1115
1117{
1118 EnsureValidDescriptor().ThrowOnError();
1119 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1120 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1121 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1122 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1124 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1125 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1126 });
1127 fDescriptor.fStringPool->Freeze();
1129 std::swap(result, fDescriptor);
1130 return result;
1131}
1132
1134 std::uint16_t versionMinor, std::uint16_t versionPatch)
1135{
1137 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1138 }
1139 fDescriptor.fVersionEpoch = versionEpoch;
1140 fDescriptor.fVersionMajor = versionMajor;
1141 fDescriptor.fVersionMinor = versionMinor;
1142 fDescriptor.fVersionPatch = versionPatch;
1143}
1144
1146{
1147 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1148 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1149 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1150 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1151}
1152
1154{
1155 fDescriptor.fName = std::string(name);
1156 fDescriptor.fDescription = std::string(description);
1157}
1158
1160{
1161 if (flag > 0 && flag % 64 == 0)
1162 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1163 fDescriptor.fFeatureFlags.insert(flag);
1164}
1165
1168{
1169 if (fDesc.fName.empty())
1170 return R__FAIL("attribute set name cannot be empty");
1171 if (fDesc.fAnchorLength == 0)
1172 return R__FAIL("invalid anchor length");
1173 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1174 return R__FAIL("invalid locator type");
1175
1176 return std::move(fDesc);
1177}
1178
1180{
1181 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1182 return R__FAIL("invalid logical column id");
1183 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1184 return R__FAIL("invalid physical column id");
1185 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1186 return R__FAIL("invalid field id, dangling column");
1187
1188 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1189 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1190 // as we have no idea what the valid range is.
1191 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1192 // we try to read the actual data contained in it.
1193 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1194 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1195 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1196 return R__FAIL("invalid column bit width");
1197 }
1198
1200 std::swap(result, fColumn);
1201 return result;
1202}
1203
1211
1213{
1214 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1215 return R__FAIL("invalid field id");
1216 }
1217 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1218 return R__FAIL("invalid field structure");
1219 }
1220 if (fField.IsSoACollection() && (fField.GetStructure() != ROOT::ENTupleStructure::kCollection)) {
1221 return R__FAIL("invalid SoA flag on non-collection field");
1222 }
1223 // FieldZero is usually named "" and would be a false positive here
1224 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1225 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1226 if (!validName) {
1228 }
1229 if (fField.GetFieldName().empty()) {
1230 return R__FAIL("name cannot be empty string \"\"");
1231 }
1232 }
1233
1235 std::swap(result, fField);
1236 return result;
1237}
1238
1240{
1241 RFieldDescriptorBuilder fieldDesc(GetStringPool());
1242 fieldDesc.FieldId(fieldId)
1243 .FieldVersion(field.GetFieldVersion())
1244 .TypeVersion(field.GetTypeVersion())
1245 .FieldName(field.GetFieldName())
1246 .FieldDescription(field.GetDescription())
1247 .TypeName(field.GetTypeName())
1248 .TypeAlias(field.GetTypeAlias())
1249 .Structure(field.GetStructure())
1250 .NRepetitions(field.GetNRepetitions());
1252 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1253 if (field.GetTraits() & ROOT::RFieldBase::kTraitSoACollection) {
1254 assert(field.GetStructure() == ENTupleStructure::kCollection);
1255 fieldDesc.IsSoACollection(true);
1256 }
1257 AddField(fieldDesc.MoveDescriptor().Unwrap());
1258}
1259
1261{
1262 const auto id = fieldDesc.GetId();
1263 if (fDescriptor.fHeaderExtension)
1264 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1265 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1266 fDescriptor.fFieldZeroId = id;
1267 }
1268
1269 fDescriptor.fFieldDescriptors.emplace(id, std::move(fieldDesc));
1270}
1271
1274{
1276 if (!(fieldExists = EnsureFieldExists(fieldId)))
1278 if (!(fieldExists = EnsureFieldExists(linkId)))
1279 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1280
1281 if (linkId == fDescriptor.GetFieldZeroId()) {
1282 return R__FAIL("cannot make FieldZero a child field");
1283 }
1284 // fail if field already has another valid parent
1285 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1287 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1288 }
1289 if (fieldId == linkId) {
1290 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1291 }
1292 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1293 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1294 return RResult<void>::Success();
1295}
1296
1299{
1301 if (!(fieldExists = EnsureFieldExists(sourceId)))
1303 if (!(fieldExists = EnsureFieldExists(targetId)))
1304 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1305
1306 if (targetId == fDescriptor.GetFieldZeroId()) {
1307 return R__FAIL("cannot make FieldZero a projected field");
1308 }
1309 if (sourceId == targetId) {
1310 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1311 }
1312 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1313 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1314 }
1315 // fail if target field already has another valid projection source
1316 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1317 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1318 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1319 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1320 }
1321 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1322 return RResult<void>::Success();
1323}
1324
1326{
1327 const auto fieldId = columnDesc.GetFieldId();
1328 const auto columnIndex = columnDesc.GetIndex();
1329 const auto representationIndex = columnDesc.GetRepresentationIndex();
1330
1331 auto fieldExists = EnsureFieldExists(fieldId);
1332 if (!fieldExists) {
1334 }
1335 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1336
1337 if (columnDesc.IsAliasColumn()) {
1338 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1339 return R__FAIL("alias column type mismatch");
1340 }
1341 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1342 return R__FAIL("column index clash");
1343 }
1344 if (columnIndex > 0) {
1345 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1346 return R__FAIL("out of bounds column index");
1347 }
1348 if (representationIndex > 0) {
1349 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1350 return R__FAIL("out of bounds representation index");
1351 }
1352 if (columnIndex == 0) {
1353 assert(fieldDesc.fColumnCardinality > 0);
1354 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1356 return R__FAIL("incomplete column representations");
1357 }
1358 } else {
1359 if (columnIndex >= fieldDesc.fColumnCardinality)
1360 return R__FAIL("irregular column representations");
1361 }
1362 } else {
1363 // This will set the column cardinality to the number of columns of the first representation
1364 fieldDesc.fColumnCardinality = columnIndex + 1;
1365 }
1366
1367 const auto logicalId = columnDesc.GetLogicalId();
1368 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1369
1370 if (!columnDesc.IsAliasColumn())
1371 fDescriptor.fNPhysicalColumns++;
1372 if (fDescriptor.fHeaderExtension)
1373 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1374 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1375
1376 return RResult<void>::Success();
1377}
1378
1380{
1381 const auto id = clusterGroup.GetId();
1382 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1383 return R__FAIL("cluster group id clash");
1384 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1385 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1386 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1387 return RResult<void>::Success();
1388}
1389
1391{
1392 // We expect the resulting descriptor to be ammended, so use a dedicated string pool
1393 fDescriptor = descriptor.CloneSchema(false /* shareStringPool */);
1394}
1395
1397{
1398 if (!fDescriptor.fHeaderExtension)
1399 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1400}
1401
1403{
1404 if (fDescriptor.GetNLogicalColumns() == 0)
1405 return;
1406 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1407
1408 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1409 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1410 R__ASSERT(c.IsAliasColumn());
1411 R__ASSERT(id == c.GetLogicalId());
1412 fDescriptor.fColumnDescriptors.erase(id);
1413 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1414 if (link == c.fLogicalColumnId) {
1415 link += offset;
1416 break;
1417 }
1418 }
1419 c.fLogicalColumnId += offset;
1420 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1421 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1422 }
1423
1424 // Patch up column ids in the header extension
1425 if (auto &xHeader = fDescriptor.fHeaderExtension) {
1426 for (auto &columnId : xHeader->fExtendedColumnRepresentations) {
1427 if (columnId >= fDescriptor.GetNPhysicalColumns())
1428 columnId += offset;
1429 }
1430 }
1431}
1432
1434{
1435 auto clusterId = clusterDesc.GetId();
1436 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1437 return R__FAIL("cluster id clash");
1438 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1439 return RResult<void>::Success();
1440}
1441
1444{
1445 // Make sure we have no duplicates
1446 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1447 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1448 return R__FAIL("extra type info duplicates");
1449 }
1450 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1451 return RResult<void>::Success();
1452}
1453
1455{
1456 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1458 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1459 *it = std::move(extraTypeInfoDesc);
1460 else
1461 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1462}
1463
1466{
1467 auto &attrSets = fDescriptor.fAttributeSets;
1468 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1469 return desc.GetName() == name;
1470 }) != attrSets.end()) {
1471 return R__FAIL("attribute sets with duplicate names");
1472 }
1473 attrSets.push_back(std::move(attrSetDesc));
1474 return RResult<void>::Success();
1475}
1476
1481
1487
1494
1500
1507
1512
1518
1523
1529
1535
1540
1541ROOT::RNTupleDescriptor::RClusterDescriptorIterable ROOT::RNTupleDescriptor::GetClusterIterable() const
1542{
1543 return GetActiveClusterIterable();
1544}
1545
1550
1555
1560
1562{
1563 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1564 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1565 fName == other.fName;
1566};
1567
1569{
1571 desc.fAnchorLength = fAnchorLength;
1572 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1573 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1574 desc.fAnchorLocator = fAnchorLocator;
1575 desc.fName = fName;
1576 return desc;
1577}
1578
1580{
1581 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1582 return false;
1583 if (fieldDesc.GetTypeName().rfind("std::", 0) == 0)
1584 return false;
1585
1586 auto subFieldId = desc.FindFieldId("_0", fieldDesc.GetId());
1588 return false;
1589
1590 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
1591 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
1592 "std::int64_t", "std::uint64_t"};
1593 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
1594 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
1595}
1596
1598{
1599 if (fieldDesc.GetStructure() != ROOT::ENTupleStructure::kPlain)
1600 return false;
1601 return (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0);
1602}
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:326
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:322
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:130
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
char name[80]
Definition TGX11.cxx:142
#define _(A, B)
Definition cfortran.h:108
RResult< ROOT::Experimental::RNTupleAttrSetDescriptor > MoveDescriptor()
Attempt to make an AttributeSet descriptor.
Used to loop over all the Attribute Sets linked to an RNTuple.
Metadata stored for every Attribute Set linked to an RNTuple.
bool operator==(const RNTupleAttrSetDescriptor &other) const
A helper class for piece-wise construction of an RClusterDescriptor.
RResult< void > MarkSuppressedColumnRange(ROOT::DescriptorId_t physicalId)
Books the given column ID as being suppressed in this cluster.
RResult< void > CommitColumnRange(ROOT::DescriptorId_t physicalId, std::uint64_t firstElementIndex, std::uint32_t compressionSettings, const RClusterDescriptor::RPageRange &pageRange)
RClusterDescriptorBuilder & AddExtendedColumnRanges(const RNTupleDescriptor &desc)
Add column and page ranges for columns created during late model extension missing in this cluster.
RResult< void > CommitSuppressedColumnRanges(const RNTupleDescriptor &desc)
Sets the first element index and number of elements for all the suppressed column ranges.
RResult< RClusterDescriptor > MoveDescriptor()
Move out the full cluster descriptor including page locations.
A helper class for piece-wise construction of an RClusterGroupDescriptor.
RClusterGroupDescriptorBuilder & EntrySpan(std::uint64_t entrySpan)
RClusterGroupDescriptorBuilder & PageListLocator(const RNTupleLocator &pageListLocator)
static RClusterGroupDescriptorBuilder FromSummary(const RClusterGroupDescriptor &clusterGroupDesc)
RClusterGroupDescriptorBuilder & PageListLength(std::uint64_t pageListLength)
RClusterGroupDescriptorBuilder & MinEntry(std::uint64_t minEntry)
RResult< RClusterGroupDescriptor > MoveDescriptor()
RClusterGroupDescriptorBuilder & ClusterGroupId(ROOT::DescriptorId_t clusterGroupId)
RClusterGroupDescriptorBuilder & NClusters(std::uint32_t nClusters)
RResult< RColumnDescriptor > MoveDescriptor()
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 > MoveDescriptor()
Attempt to make a field descriptor.
static RFieldDescriptor CloneDescriptor(const RFieldDescriptor &source, RStringPool &stringPool)
void SetNTuple(std::string_view name, std::string_view description)
void SetSchemaFromExisting(const RNTupleDescriptor &descriptor)
Copies the "schema" part of descriptor into the builder's descriptor.
RResult< void > AddColumn(RColumnDescriptor &&columnDesc)
void AddField(RFieldDescriptor fieldDesc)
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)
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.
Used for string interning of repeated type names, field names, etc.
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.
std::unique_ptr< RValueRange > fValueRange
Optional value range (used e.g. by quantized real fields)
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.
RColumnDescriptor Clone() const
Get a copy of the descriptor.
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Field specific extra type information from the header / extenstion header.
bool operator==(const RExtraTypeInfoDescriptor &other) const
RExtraTypeInfoDescriptor Clone() const
EExtraTypeInfoIds fContentId
Specifies the meaning of the extra information.
std::string fTypeName
The type name the extra information refers to; empty for RNTuple-wide extra information.
std::string fContent
The content format depends on the content ID and may be binary.
std::uint32_t fTypeVersion
Type version the extra type information is bound to.
A field translates read and write calls from/to underlying columns to/from tree values.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ kTraitInvalidField
This field is an instance of RInvalidField and can be safely static_cast to it.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
std::unique_ptr< ROOT::RFieldBase > CreateField(const RNTupleDescriptor &ntplDesc, const ROOT::RCreateFieldOptions &options={}) const
In general, we create a field simply from the C++ type name.
RFieldDescriptor Clone() const
Get a copy of the descriptor.
static const std::string & GetEmptyString()
bool operator==(const RFieldDescriptor &other) const
void InitFrom(const RFieldDescriptor &source, Internal::RStringPool &stringPool)
std::unique_ptr< Internal::RStringPool > fStringPool
Optional string storage for a free-standing field descriptor.
Used in RFieldBase::Check() to record field creation failures.
Definition RField.hxx:96
@ kGeneric
Generic unrecoverable error.
@ kUnknownStructure
The field could not be created because its descriptor had an unknown structural role.
Loop over all the clusters of an RNTuple, ordered by first entry number.
Used to loop over all the cluster groups of an RNTuple in order of entry ranges.
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.
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.
RClusterDescriptorIterable GetActiveClusterIterable() const
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
ROOT::NTupleSize_t R__DEPRECATED(6, 46, "This function is ill-conceived in the descriptor " "as not all cluster descriptors may be present. This interface is no longer publicly exposed.") GetNElements(ROOT ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
std::vector< std::uint64_t > GetFeatureFlags() const
std::unique_ptr< ROOT::RNTupleModel > CreateModel(const RCreateModelOptions &options=RCreateModelOptions()) const
Re-create the C++ model from the stored metadata.
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
std::unordered_map< ROOT::DescriptorId_t, RClusterDescriptor > fClusterDescriptors
Potentially a subset of all the available clusters.
ROOT::DescriptorId_t FindPhysicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
RExtraTypeInfoDescriptorIterable GetExtraTypeInfoIterable() const
std::shared_ptr< Internal::RStringPool > fStringPool
Storage for all the field strings. Shared among descriptor clones.
std::uint64_t fNClusters
Updated by the descriptor builder when the cluster groups are added.
RNTupleDescriptor CloneSchema(bool shareStringPool) const
Creates a descriptor containing only the schema information about this RNTuple, i....
std::uint64_t fOnDiskHeaderXxHash3
Set by the descriptor builder when deserialized.
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
std::string fName
The RNTuple name needs to be unique in a given storage location (file)
std::uint64_t fOnDiskHeaderSize
Set by the descriptor builder when deserialized.
RResult< void > DropClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId)
std::uint16_t fVersionMajor
Set by the descriptor builder when deserialized.
std::vector< ROOT::DescriptorId_t > fSortedClusterGroupIds
References cluster groups sorted by entry range and thus allows for binary search.
std::unordered_map< ROOT::DescriptorId_t, RColumnDescriptor > fColumnDescriptors
ROOT::DescriptorId_t FindLogicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
std::unordered_map< ROOT::DescriptorId_t, RFieldDescriptor > fFieldDescriptors
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
ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindClusterId(ROOT ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindNextClusterId(ROOT ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindPrevClusterId(ROOT 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.
std::uint64_t fGeneration
The generation of the descriptor.
std::unique_ptr< RHeaderExtension > fHeaderExtension
Generic information about the physical location of data.
static std::unique_ptr< RNTupleModel > Create()
static std::unique_ptr< RNTupleModel > CreateBare()
Creates a "bare model", i.e. an RNTupleModel with no default entry.
static constexpr std::uint16_t kVersionPatch
Definition RNTuple.hxx:82
static constexpr std::uint16_t kVersionMajor
Definition RNTuple.hxx:80
static constexpr std::uint16_t kVersionEpoch
Definition RNTuple.hxx:79
static constexpr std::uint16_t kVersionMinor
Definition RNTuple.hxx:81
const_iterator begin() const
const_iterator end() const
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:222
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
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:2335