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>
28
29#include <algorithm>
30#include <cstdint>
31#include <deque>
32#include <functional>
33#include <iostream>
34#include <set>
35#include <utility>
36
38
40{
41 return fFieldId == other.fFieldId && fFieldVersion == other.fFieldVersion && fTypeVersion == other.fTypeVersion &&
42 fFieldName == other.fFieldName && fFieldDescription == other.fFieldDescription &&
43 fTypeName == other.fTypeName && fTypeAlias == other.fTypeAlias && fNRepetitions == other.fNRepetitions &&
44 fStructure == other.fStructure && fParentId == other.fParentId &&
45 fProjectionSourceId == other.fProjectionSourceId && fLinkIds == other.fLinkIds &&
46 fLogicalColumnIds == other.fLogicalColumnIds && other.fTypeChecksum == other.fTypeChecksum;
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 return clone;
68}
69
70std::unique_ptr<ROOT::RFieldBase>
72{
73 if (GetStructure() == ROOT::ENTupleStructure::kStreamer) {
74 auto streamerField = std::make_unique<ROOT::RStreamerField>(GetFieldName(), GetTypeName());
75 streamerField->SetOnDiskId(fFieldId);
76 return streamerField;
77 }
78
79 // The structure may be unknown if the descriptor comes from a deserialized field with an unknown structural role.
80 // For forward compatibility, we allow this case and return an InvalidField.
81 if (GetStructure() == ROOT::ENTupleStructure::kUnknown) {
82 if (options.GetReturnInvalidOnError()) {
83 auto invalidField = std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), "",
85 invalidField->SetOnDiskId(fFieldId);
86 return invalidField;
87 } else {
88 throw RException(R__FAIL("unexpected on-disk field structure value for field \"" + GetFieldName() + "\""));
89 }
90 }
91
92 // Untyped records and collections
93 if (GetTypeName().empty()) {
94 switch (GetStructure()) {
96 std::vector<std::unique_ptr<ROOT::RFieldBase>> memberFields;
97 memberFields.reserve(fLinkIds.size());
98 for (auto id : fLinkIds) {
99 const auto &memberDesc = ntplDesc.GetFieldDescriptor(id);
100 auto field = memberDesc.CreateField(ntplDesc, options);
102 return field;
103 memberFields.emplace_back(std::move(field));
104 }
105 auto recordField = std::make_unique<ROOT::RRecordField>(GetFieldName(), std::move(memberFields));
106 recordField->SetOnDiskId(fFieldId);
107 return recordField;
108 }
110 if (fLinkIds.size() != 1) {
111 throw RException(R__FAIL("unsupported untyped collection for field \"" + GetFieldName() + "\""));
112 }
113 auto itemField = ntplDesc.GetFieldDescriptor(fLinkIds[0]).CreateField(ntplDesc, options);
115 return itemField;
116 auto collectionField = ROOT::RVectorField::CreateUntyped(GetFieldName(), std::move(itemField));
117 collectionField->SetOnDiskId(fFieldId);
118 return collectionField;
119 }
120 default: throw RException(R__FAIL("unsupported untyped field structure for field \"" + GetFieldName() + "\""));
121 }
122 }
123
124 try {
125 const auto &fieldName = GetFieldName();
126 const auto &typeName = GetTypeAlias().empty() ? GetTypeName() : GetTypeAlias();
127 // NOTE: Unwrap() here may throw an exception, hence the try block.
128 // If options.fReturnInvalidOnError is false we just rethrow it, otherwise we return an InvalidField wrapping the
129 // error.
130 auto field = ROOT::Internal::CallFieldBaseCreate(fieldName, typeName, options, &ntplDesc, fFieldId).Unwrap();
131 field->SetOnDiskId(fFieldId);
132
133 for (auto &subfield : *field) {
134 const auto subfieldId = ntplDesc.FindFieldId(subfield.GetFieldName(), subfield.GetParent()->GetOnDiskId());
135 subfield.SetOnDiskId(subfieldId);
137 auto &invalidField = static_cast<ROOT::RInvalidField &>(subfield);
138 // A subfield being invalid "infects" its entire ancestry.
139 return invalidField.Clone(fieldName);
140 }
141 }
142
143 return field;
144 } catch (const RException &ex) {
145 if (options.GetReturnInvalidOnError())
146 return std::make_unique<ROOT::RInvalidField>(GetFieldName(), GetTypeName(), ex.GetError().GetReport(),
148 else
149 throw ex;
150 }
151}
152
154{
156 return false;
157
158 // Skip untyped structs
159 if (fTypeName.empty())
160 return false;
161
162 if (fStructure == ROOT::ENTupleStructure::kRecord) {
163 if (fTypeName.compare(0, 10, "std::pair<") == 0)
164 return false;
165 if (fTypeName.compare(0, 11, "std::tuple<") == 0)
166 return false;
167 }
168
169 return true;
170}
171
173{
174 if (fStructure != ROOT::ENTupleStructure::kPlain)
175 return false;
176 if (fTypeName.rfind("std::", 0) == 0)
177 return false;
178
179 auto subFieldId = desc.FindFieldId("_0", fFieldId);
181 return false;
182
183 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
184 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
185 "std::int64_t", "std::uint64_t"};
186 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
187 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
188}
189
191{
192 if (fStructure != ROOT::ENTupleStructure::kPlain)
193 return false;
194 return (fTypeName.rfind("std::atomic<", 0) == 0);
195}
196
197////////////////////////////////////////////////////////////////////////////////
198
200{
201 return fLogicalColumnId == other.fLogicalColumnId && fPhysicalColumnId == other.fPhysicalColumnId &&
202 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
203 fIndex == other.fIndex && fRepresentationIndex == other.fRepresentationIndex &&
204 fValueRange == other.fValueRange;
205}
206
208{
209 RColumnDescriptor clone;
210 clone.fLogicalColumnId = fLogicalColumnId;
211 clone.fPhysicalColumnId = fPhysicalColumnId;
212 clone.fBitsOnStorage = fBitsOnStorage;
213 clone.fType = fType;
214 clone.fFieldId = fFieldId;
215 clone.fIndex = fIndex;
216 clone.fFirstElementIndex = fFirstElementIndex;
217 clone.fRepresentationIndex = fRepresentationIndex;
218 clone.fValueRange = fValueRange;
219 return clone;
220}
221
222////////////////////////////////////////////////////////////////////////////////
223
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
342{
343 // clang-format off
344 return fName == other.fName &&
345 fDescription == other.fDescription &&
346 fNEntries == other.fNEntries &&
347 fGeneration == other.fGeneration &&
348 fFieldZeroId == other.fFieldZeroId &&
349 fFieldDescriptors == other.fFieldDescriptors &&
350 fColumnDescriptors == other.fColumnDescriptors &&
351 fClusterGroupDescriptors == other.fClusterGroupDescriptors &&
352 fClusterDescriptors == other.fClusterDescriptors;
353 // clang-format on
354}
355
357{
359 for (const auto &cd : fClusterDescriptors) {
360 if (!cd.second.ContainsColumn(physicalColumnId))
361 continue;
362 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
363 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
364 }
365 return result;
366}
367
368////////////////////////////////////////////////////////////////////////////////
369/// Return the cluster boundaries for each cluster in this RNTuple.
370std::vector<ROOT::Internal::RNTupleClusterBoundaries>
372{
373 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
374 boundaries.reserve(desc.GetNClusters());
375 auto clusterId = desc.FindClusterId(0, 0);
377 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
378 R__ASSERT(clusterDesc.GetNEntries() > 0);
379 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
380 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
382 }
383 return boundaries;
384}
385
388{
389 std::string leafName(fieldName);
390 auto posDot = leafName.find_last_of('.');
391 if (posDot != std::string::npos) {
392 auto parentName = leafName.substr(0, posDot);
393 leafName = leafName.substr(posDot + 1);
394 parentId = FindFieldId(parentName, parentId);
395 }
396 auto itrFieldDesc = fFieldDescriptors.find(parentId);
397 if (itrFieldDesc == fFieldDescriptors.end())
399 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
400 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
401 return linkId;
402 }
404}
405
407{
409 return "";
410
411 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
412 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
413 if (prefix.empty())
414 return fieldDescriptor.GetFieldName();
415 return prefix + "." + fieldDescriptor.GetFieldName();
416}
417
419{
420 std::string typeName = fieldDesc.GetTypeName();
421
422 // ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
423 R__ASSERT(fVersionEpoch == 1);
424 if (fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1) {
425 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
426 }
427
428 return typeName;
429}
430
432{
433 return FindFieldId(fieldName, GetFieldZeroId());
434}
435
437 std::uint32_t columnIndex,
438 std::uint16_t representationIndex) const
439{
440 auto itr = fFieldDescriptors.find(fieldId);
441 if (itr == fFieldDescriptors.cend())
443 if (columnIndex >= itr->second.GetColumnCardinality())
445 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
446 if (itr->second.GetLogicalColumnIds().size() <= idx)
448 return itr->second.GetLogicalColumnIds()[idx];
449}
450
452 std::uint32_t columnIndex,
453 std::uint16_t representationIndex) const
454{
455 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
458 return GetColumnDescriptor(logicalId).GetPhysicalId();
459}
460
463{
464 if (GetNClusterGroups() == 0)
466
467 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
468
469 std::size_t cgLeft = 0;
470 std::size_t cgRight = GetNClusterGroups() - 1;
471 while (cgLeft <= cgRight) {
472 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
473 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
474 R__ASSERT(!clusterIds.empty());
475
476 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
477 // this may happen if the RNTuple has an empty schema
478 if (!clusterDesc.ContainsColumn(physicalColumnId))
480
481 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
483 // Look into the lower half of cluster groups
485 cgRight = cgMidpoint - 1;
486 continue;
487 }
488
489 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
490 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
491 // Look into the upper half of cluster groups
492 cgLeft = cgMidpoint + 1;
493 continue;
494 }
495
496 // Binary search in the current cluster group; since we already checked the element range boundaries,
497 // the element must be in that cluster group.
498 std::size_t clusterLeft = 0;
499 std::size_t clusterRight = clusterIds.size() - 1;
500 while (clusterLeft <= clusterRight) {
501 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
503 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
504
505 if (columnRange.Contains(index))
506 return clusterId;
507
508 if (columnRange.GetFirstElementIndex() > index) {
511 continue;
512 }
513
514 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
516 continue;
517 }
518 }
519 R__ASSERT(false);
520 }
522}
523
525{
526 if (GetNClusterGroups() == 0)
528
529 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
530
531 std::size_t cgLeft = 0;
532 std::size_t cgRight = GetNClusterGroups() - 1;
533 while (cgLeft <= cgRight) {
534 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
535 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
536
537 if (cgDesc.GetMinEntry() > entryIdx) {
539 cgRight = cgMidpoint - 1;
540 continue;
541 }
542
543 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
544 cgLeft = cgMidpoint + 1;
545 continue;
546 }
547
548 // Binary search in the current cluster group; since we already checked the element range boundaries,
549 // the element must be in that cluster group.
550 const auto &clusterIds = cgDesc.GetClusterIds();
551 R__ASSERT(!clusterIds.empty());
552 std::size_t clusterLeft = 0;
553 std::size_t clusterRight = clusterIds.size() - 1;
554 while (clusterLeft <= clusterRight) {
555 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
556 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
557
558 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
561 continue;
562 }
563
564 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
566 continue;
567 }
568
570 }
571 R__ASSERT(false);
572 }
574}
575
577{
578 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
579 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
580 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
581 // binary search code path remains tested.
582 const auto &clusterDesc = GetClusterDescriptor(clusterId);
583 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
584 return FindClusterId(firstEntryInNextCluster);
585}
586
588{
589 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
590 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
591 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
592 // binary search code path remains tested.
593 const auto &clusterDesc = GetClusterDescriptor(clusterId);
594 if (clusterDesc.GetFirstEntryIndex() == 0)
596 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
597}
598
599std::vector<ROOT::DescriptorId_t>
601{
602 auto fieldZeroId = desc.GetFieldZeroId();
603
604 std::vector<ROOT::DescriptorId_t> fields;
605 for (const auto fieldId : fFieldIdsOrder) {
606 if (desc.GetFieldDescriptor(fieldId).GetParentId() == fieldZeroId)
607 fields.emplace_back(fieldId);
608 }
609 return fields;
610}
611
617
619 : fNTuple(ntuple)
620{
621 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
622
623 while (!fieldIdQueue.empty()) {
624 auto currFieldId = fieldIdQueue.front();
625 fieldIdQueue.pop_front();
626
627 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
628 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
629
630 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
631 auto fieldId = field.GetId();
632 fieldIdQueue.push_back(fieldId);
633 }
634 }
635}
636
637std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
638{
639 std::vector<std::uint64_t> result;
640 unsigned int base = 0;
641 std::uint64_t flags = 0;
642 for (auto f : fFeatureFlags) {
643 if ((f > 0) && ((f % 64) == 0))
644 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
645 while (f > base + 64) {
646 result.emplace_back(flags);
647 flags = 0;
648 base += 64;
649 }
650 f -= base;
651 flags |= 1 << f;
652 }
653 result.emplace_back(flags);
654 return result;
655}
656
658 std::vector<RClusterDescriptor> &clusterDescs)
659{
661 if (iter == fClusterGroupDescriptors.end())
662 return R__FAIL("invalid attempt to add details of unknown cluster group");
663 if (iter->second.HasClusterDetails())
664 return R__FAIL("invalid attempt to re-populate cluster group details");
665 if (iter->second.GetNClusters() != clusterDescs.size())
666 return R__FAIL("mismatch of number of clusters");
667
668 std::vector<ROOT::DescriptorId_t> clusterIds;
669 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
670 clusterIds.emplace_back(clusterDescs[i].GetId());
671 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
672 if (!success) {
673 return R__FAIL("invalid attempt to re-populate existing cluster");
674 }
675 }
677 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
678 });
680 cgBuilder.AddSortedClusters(clusterIds);
681 iter->second = cgBuilder.MoveDescriptor().Unwrap();
682 return RResult<void>::Success();
683}
684
686{
688 if (iter == fClusterGroupDescriptors.end())
689 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
690 if (!iter->second.HasClusterDetails())
691 return R__FAIL("invalid attempt to drop details of cluster group summary");
692
693 for (auto clusterId : iter->second.GetClusterIds())
695 iter->second = iter->second.CloneSummary();
696 return RResult<void>::Success();
697}
698
699std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
700{
701 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
702 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
703 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
704 for (const auto &colDesc : GetColumnIterable()) {
706 auto fieldId = colDesc.GetFieldId();
707 while (1) {
708 const auto &field = GetFieldDescriptor(fieldId);
709 if (field.GetParentId() == GetFieldZeroId())
710 break;
711 fieldId = field.GetParentId();
712 }
713 invalidFields.insert(fieldId);
714
715 // No need to look for all invalid fields if we're gonna error out anyway
716 if (!options.GetForwardCompatible())
717 break;
718 }
719 }
720
721 if (!options.GetForwardCompatible() && !invalidFields.empty())
723 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
724 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
725
726 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
727 fieldZero->SetOnDiskId(GetFieldZeroId());
728 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
729 : RNTupleModel::Create(std::move(fieldZero));
731 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
732 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
733 for (const auto &topDesc : GetTopLevelFields()) {
734 if (invalidFields.count(topDesc.GetId()) > 0) {
735 // Field contains invalid columns: skip it
736 continue;
737 }
738
739 auto field = topDesc.CreateField(*this, createFieldOpts);
740
741 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
742 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
743 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
744 // in the first place, so we don't need to check for that flag again).
745 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
746 const auto &invalid = static_cast<const RInvalidField &>(*field);
747 const auto cat = invalid.GetCategory();
749 if (mustThrow)
750 throw invalid.GetError();
751
752 // Not a hard error: skip the field and go on.
753 continue;
754 }
755
756 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
757 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
758 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
759 });
760 } else {
761 model->AddField(std::move(field));
762 }
763 }
764 model->Freeze();
765 return model;
766}
767
769{
770 RNTupleDescriptor clone;
771 clone.fName = fName;
776 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
777 // and therefore not represent the actual sources's header.
778 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
779
780 for (const auto &d : fFieldDescriptors)
781 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
782 for (const auto &d : fColumnDescriptors)
783 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
784
785 for (const auto &d : fExtraTypeInfoDescriptors)
786 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
788 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
789
790 return clone;
791}
792
794{
796
801
805 clone.fNEntries = fNEntries;
806 clone.fNClusters = fNClusters;
807 clone.fGeneration = fGeneration;
808 for (const auto &d : fClusterGroupDescriptors)
809 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
811 for (const auto &d : fClusterDescriptors)
812 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
813 for (const auto &d : fAttributeSets)
814 clone.fAttributeSets.emplace_back(d.Clone());
815 return clone;
816}
817
818////////////////////////////////////////////////////////////////////////////////
819
821{
822 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
823 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
824}
825
827{
829 clone.fClusterGroupId = fClusterGroupId;
830 clone.fPageListLocator = fPageListLocator;
831 clone.fPageListLength = fPageListLength;
832 clone.fMinEntry = fMinEntry;
833 clone.fEntrySpan = fEntrySpan;
834 clone.fNClusters = fNClusters;
835 return clone;
836}
837
839{
840 RClusterGroupDescriptor clone = CloneSummary();
841 clone.fClusterIds = fClusterIds;
842 return clone;
843}
844
845////////////////////////////////////////////////////////////////////////////////
846
849 std::uint64_t firstElementIndex,
850 std::uint32_t compressionSettings,
852{
853 if (physicalId != pageRange.fPhysicalColumnId)
854 return R__FAIL("column ID mismatch");
855 if (fCluster.fColumnRanges.count(physicalId) > 0)
856 return R__FAIL("column ID conflict");
858 for (const auto &pi : pageRange.fPageInfos) {
859 columnRange.IncrementNElements(pi.GetNElements());
860 }
861 fCluster.fPageRanges[physicalId] = pageRange.Clone();
862 fCluster.fColumnRanges[physicalId] = columnRange;
863 return RResult<void>::Success();
864}
865
868{
869 if (fCluster.fColumnRanges.count(physicalId) > 0)
870 return R__FAIL("column ID conflict");
871
873 columnRange.SetPhysicalColumnId(physicalId);
874 columnRange.SetIsSuppressed(true);
875 fCluster.fColumnRanges[physicalId] = columnRange;
876 return RResult<void>::Success();
877}
878
881{
882 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
883 if (!columnRange.IsSuppressed())
884 continue;
885 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
886
887 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
888 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
889 // We expect only few columns and column representations per field, so we do a linear search
890 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
892 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
893 continue;
894 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
895 continue;
896
897 // Found corresponding column of a different column representation
898 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
899 if (otherColumnRange.IsSuppressed())
900 continue;
901
902 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
903 columnRange.SetNElements(otherColumnRange.GetNElements());
904 break;
905 }
906
907 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
908 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
909 std::to_string(columnRange.GetPhysicalColumnId()) +
910 ", cluster ID: " + std::to_string(fCluster.GetId()));
911 }
912 }
913 return RResult<void>::Success();
914}
915
918{
919 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
920 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
921 /// parent field in the hierarchy.
923 const auto &visitField, const auto &enterSubtree) -> void {
925 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
926 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
928 }
929 };
930
931 // Extended columns can only be part of the header extension
932 if (!desc.GetHeaderExtension())
933 return *this;
934
935 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
936 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
937 for (const auto &topLevelField : desc.GetTopLevelFields()) {
939 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
940 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
941 for (const auto &c : desc.GetColumnIterable(fieldId)) {
942 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
943 auto &columnRange = fCluster.fColumnRanges[physicalId];
944
945 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
946 // extension won't have on-disk metadata for the clusters that were already committed before the model
947 // was extended. Therefore, these need to be synthetically initialized upon reading.
948 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
949 columnRange.SetPhysicalColumnId(physicalId);
950 columnRange.SetFirstElementIndex(0);
951 columnRange.SetNElements(0);
952 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
953 }
954 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
955 // number of elements should have been if the column was not deferred; fix those and let
956 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
957 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
958 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
959 // field zero excluding subfields of collection and variant fields.
960 if (c.IsDeferredColumn()) {
961 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
962 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
963 if (!columnRange.IsSuppressed()) {
964 auto &pageRange = fCluster.fPageRanges[physicalId];
965 pageRange.fPhysicalColumnId = physicalId;
966 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
967 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
968 }
969 } else if (!columnRange.IsSuppressed()) {
970 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
971 }
972 }
973 },
975 }
976 return *this;
977}
978
980{
981 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
982 return R__FAIL("unset cluster ID");
983 if (fCluster.fNEntries == 0)
984 return R__FAIL("empty cluster");
985 for (auto &pr : fCluster.fPageRanges) {
986 if (fCluster.fColumnRanges.count(pr.first) == 0) {
987 return R__FAIL("missing column range");
988 }
989 pr.second.fCumulativeNElements.clear();
990 pr.second.fCumulativeNElements.reserve(pr.second.fPageInfos.size());
992 for (const auto &pi : pr.second.fPageInfos) {
993 sum += pi.GetNElements();
994 pr.second.fCumulativeNElements.emplace_back(sum);
995 }
996 }
998 std::swap(result, fCluster);
999 return result;
1000}
1001
1002////////////////////////////////////////////////////////////////////////////////
1003
1006{
1008 builder.ClusterGroupId(clusterGroupDesc.GetId())
1009 .PageListLocator(clusterGroupDesc.GetPageListLocator())
1010 .PageListLength(clusterGroupDesc.GetPageListLength())
1011 .MinEntry(clusterGroupDesc.GetMinEntry())
1012 .EntrySpan(clusterGroupDesc.GetEntrySpan())
1013 .NClusters(clusterGroupDesc.GetNClusters());
1014 return builder;
1015}
1016
1018{
1019 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
1020 return R__FAIL("unset cluster group ID");
1022 std::swap(result, fClusterGroup);
1023 return result;
1024}
1025
1026////////////////////////////////////////////////////////////////////////////////
1027
1029{
1030 if (fExtraTypeInfo.fContentId == EExtraTypeInfoIds::kInvalid)
1031 throw RException(R__FAIL("invalid extra type info content id"));
1033 std::swap(result, fExtraTypeInfo);
1034 return result;
1035}
1036
1037////////////////////////////////////////////////////////////////////////////////
1038
1040{
1041 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1042 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1043 return RResult<void>::Success();
1044}
1045
1047{
1048 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1049 return R__FAIL("unset or unsupported RNTuple epoch version");
1050 }
1051
1052 // Reuse field name validity check
1053 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1054 if (!validName) {
1056 }
1057
1058 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1059 // parent not properly set?
1060 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1061 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1062 }
1063
1064 // Same number of columns in every column representation?
1065 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1066 if (columnCardinality == 0)
1067 continue;
1068
1069 // In AddColumn, we already checked that all but the last representation are complete.
1070 // Check that the last column representation is complete, i.e. has all columns.
1071 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1072 const auto nColumns = logicalColumnIds.size();
1073 // If we have only a single column representation, the following condition is true by construction
1074 if ((nColumns + 1) == columnCardinality)
1075 continue;
1076
1077 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1078 if (lastColumn.GetIndex() + 1 != columnCardinality)
1079 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1080 }
1081
1082 return RResult<void>::Success();
1083}
1084
1086{
1087 EnsureValidDescriptor().ThrowOnError();
1088 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1089 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1090 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1091 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1093 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1094 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1095 });
1097 std::swap(result, fDescriptor);
1098 return result;
1099}
1100
1102 std::uint16_t versionMinor, std::uint16_t versionPatch)
1103{
1105 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1106 }
1107 fDescriptor.fVersionEpoch = versionEpoch;
1108 fDescriptor.fVersionMajor = versionMajor;
1109 fDescriptor.fVersionMinor = versionMinor;
1110 fDescriptor.fVersionPatch = versionPatch;
1111}
1112
1114{
1115 fDescriptor.fVersionEpoch = RNTuple::kVersionEpoch;
1116 fDescriptor.fVersionMajor = RNTuple::kVersionMajor;
1117 fDescriptor.fVersionMinor = RNTuple::kVersionMinor;
1118 fDescriptor.fVersionPatch = RNTuple::kVersionPatch;
1119}
1120
1122 const std::string_view description)
1123{
1124 fDescriptor.fName = std::string(name);
1125 fDescriptor.fDescription = std::string(description);
1126}
1127
1129{
1130 if (flag % 64 == 0)
1131 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1132 fDescriptor.fFeatureFlags.insert(flag);
1133}
1134
1137{
1138 if (fDesc.fName.empty())
1139 return R__FAIL("attribute set name cannot be empty");
1140 if (fDesc.fAnchorLength == 0)
1141 return R__FAIL("invalid anchor length");
1142 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1143 return R__FAIL("invalid locator type");
1144
1145 return std::move(fDesc);
1146}
1147
1149{
1150 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1151 return R__FAIL("invalid logical column id");
1152 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1153 return R__FAIL("invalid physical column id");
1154 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1155 return R__FAIL("invalid field id, dangling column");
1156
1157 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1158 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1159 // as we have no idea what the valid range is.
1160 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1161 // we try to read the actual data contained in it.
1162 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1163 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1164 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1165 return R__FAIL("invalid column bit width");
1166 }
1167
1168 return fColumn.Clone();
1169}
1170
1173{
1175 fieldDesc.FieldVersion(field.GetFieldVersion())
1176 .TypeVersion(field.GetTypeVersion())
1177 .FieldName(field.GetFieldName())
1178 .FieldDescription(field.GetDescription())
1179 .TypeName(field.GetTypeName())
1180 .TypeAlias(field.GetTypeAlias())
1181 .Structure(field.GetStructure())
1182 .NRepetitions(field.GetNRepetitions());
1184 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1185 return fieldDesc;
1186}
1187
1189{
1190 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1191 return R__FAIL("invalid field id");
1192 }
1193 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1194 return R__FAIL("invalid field structure");
1195 }
1196 // FieldZero is usually named "" and would be a false positive here
1197 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1198 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1199 if (!validName) {
1201 }
1202 if (fField.GetFieldName().empty()) {
1203 return R__FAIL("name cannot be empty string \"\"");
1204 }
1205 }
1206 return fField.Clone();
1207}
1208
1210{
1211 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1212 if (fDescriptor.fHeaderExtension)
1213 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1214 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1215 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1216 }
1217}
1218
1221{
1223 if (!(fieldExists = EnsureFieldExists(fieldId)))
1225 if (!(fieldExists = EnsureFieldExists(linkId)))
1226 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1227
1228 if (linkId == fDescriptor.GetFieldZeroId()) {
1229 return R__FAIL("cannot make FieldZero a child field");
1230 }
1231 // fail if field already has another valid parent
1232 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1234 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1235 }
1236 if (fieldId == linkId) {
1237 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1238 }
1239 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1240 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1241 return RResult<void>::Success();
1242}
1243
1246{
1248 if (!(fieldExists = EnsureFieldExists(sourceId)))
1250 if (!(fieldExists = EnsureFieldExists(targetId)))
1251 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1252
1253 if (targetId == fDescriptor.GetFieldZeroId()) {
1254 return R__FAIL("cannot make FieldZero a projected field");
1255 }
1256 if (sourceId == targetId) {
1257 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1258 }
1259 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1260 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1261 }
1262 // fail if target field already has another valid projection source
1263 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1264 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1265 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1266 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1267 }
1268 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1269 return RResult<void>::Success();
1270}
1271
1273{
1274 const auto fieldId = columnDesc.GetFieldId();
1275 const auto columnIndex = columnDesc.GetIndex();
1276 const auto representationIndex = columnDesc.GetRepresentationIndex();
1277
1278 auto fieldExists = EnsureFieldExists(fieldId);
1279 if (!fieldExists) {
1281 }
1282 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1283
1284 if (columnDesc.IsAliasColumn()) {
1285 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1286 return R__FAIL("alias column type mismatch");
1287 }
1288 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1289 return R__FAIL("column index clash");
1290 }
1291 if (columnIndex > 0) {
1292 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1293 return R__FAIL("out of bounds column index");
1294 }
1295 if (representationIndex > 0) {
1296 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1297 return R__FAIL("out of bounds representation index");
1298 }
1299 if (columnIndex == 0) {
1300 assert(fieldDesc.fColumnCardinality > 0);
1301 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1303 return R__FAIL("incomplete column representations");
1304 }
1305 } else {
1306 if (columnIndex >= fieldDesc.fColumnCardinality)
1307 return R__FAIL("irregular column representations");
1308 }
1309 } else {
1310 // This will set the column cardinality to the number of columns of the first representation
1311 fieldDesc.fColumnCardinality = columnIndex + 1;
1312 }
1313
1314 const auto logicalId = columnDesc.GetLogicalId();
1315 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1316
1317 if (!columnDesc.IsAliasColumn())
1318 fDescriptor.fNPhysicalColumns++;
1319 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1320 if (fDescriptor.fHeaderExtension)
1321 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1322
1323 return RResult<void>::Success();
1324}
1325
1327{
1328 const auto id = clusterGroup.GetId();
1329 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1330 return R__FAIL("cluster group id clash");
1331 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1332 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1333 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1334 return RResult<void>::Success();
1335}
1336
1341
1343{
1344 if (!fDescriptor.fHeaderExtension)
1345 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1346}
1347
1349{
1350 if (fDescriptor.GetNLogicalColumns() == 0)
1351 return;
1352 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1353
1354 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1355 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1356 R__ASSERT(c.IsAliasColumn());
1357 R__ASSERT(id == c.GetLogicalId());
1358 fDescriptor.fColumnDescriptors.erase(id);
1359 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1360 if (link == c.fLogicalColumnId) {
1361 link += offset;
1362 break;
1363 }
1364 }
1365 c.fLogicalColumnId += offset;
1366 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1367 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1368 }
1369}
1370
1372{
1373 auto clusterId = clusterDesc.GetId();
1374 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1375 return R__FAIL("cluster id clash");
1376 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1377 return RResult<void>::Success();
1378}
1379
1382{
1383 // Make sure we have no duplicates
1384 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1385 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1386 return R__FAIL("extra type info duplicates");
1387 }
1388 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1389 return RResult<void>::Success();
1390}
1391
1393{
1394 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1396 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1397 *it = std::move(extraTypeInfoDesc);
1398 else
1399 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1400}
1401
1404{
1405 auto &attrSets = fDescriptor.fAttributeSets;
1406 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1407 return desc.GetName() == name;
1408 }) != attrSets.end()) {
1409 return R__FAIL("attribute sets with duplicate names");
1410 }
1411 attrSets.push_back(std::move(attrSetDesc));
1412 return RResult<void>::Success();
1413}
1414
1416{
1418 const auto &desc = GetDescriptor();
1419
1420 std::function<void(const RFieldDescriptor &)> fnWalkFieldTree;
1422 if (fieldDesc.IsCustomClass()) {
1423 // Add streamer info for this class to streamerInfoMap
1424 auto cl = TClass::GetClass(fieldDesc.GetTypeName().c_str());
1425 if (!cl) {
1426 throw RException(R__FAIL(std::string("cannot get TClass for ") + fieldDesc.GetTypeName()));
1427 }
1428 auto streamerInfo = cl->GetStreamerInfo(fieldDesc.GetTypeVersion());
1429 if (!streamerInfo) {
1430 throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + fieldDesc.GetTypeName()));
1431 }
1433 }
1434
1435 // Recursively traverse sub fields
1436 for (const auto &subFieldDesc : desc.GetFieldIterable(fieldDesc)) {
1438 }
1439 };
1440
1441 fnWalkFieldTree(desc.GetFieldZero());
1442
1443 // Add the streamer info records from streamer fields: because of runtime polymorphism we may need to add additional
1444 // types not covered by the type names stored in the field headers
1445 for (const auto &extraTypeInfo : desc.GetExtraTypeInfoIterable()) {
1446 if (extraTypeInfo.GetContentId() != EExtraTypeInfoIds::kStreamerInfo)
1447 continue;
1448 // Ideally, we would avoid deserializing the streamer info records of the streamer fields that we just serialized.
1449 // However, this happens only once at the end of writing and only when streamer fields are used, so the
1450 // preference here is for code simplicity.
1452 }
1453
1454 return streamerInfoMap;
1455}
1456
1461
1467
1474
1480
1487
1492
1498
1503
1509
1515
1520
1525
1530
1535
1537{
1538 return fAnchorLength == other.fAnchorLength && fSchemaVersionMajor == other.fSchemaVersionMajor &&
1539 fSchemaVersionMinor == other.fSchemaVersionMinor && fAnchorLocator == other.fAnchorLocator &&
1540 fName == other.fName;
1541};
1542
1544{
1546 desc.fAnchorLength = fAnchorLength;
1547 desc.fSchemaVersionMajor = fSchemaVersionMajor;
1548 desc.fSchemaVersionMinor = fSchemaVersionMinor;
1549 desc.fAnchorLocator = fAnchorLocator;
1550 desc.fName = fName;
1551 return desc;
1552}
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
Definition RError.hxx:304
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
char name[80]
Definition TGX11.cxx:110
#define _(A, B)
Definition cfortran.h:108
RResult< ROOT::Experimental::RNTupleAttrSetDescriptor > MoveDescriptor()
Attempt to make an AttributeSet descriptor.
Used to loop over all the Attribute Sets linked to an RNTuple.
Metadata stored for every Attribute Set linked to an RNTuple.
bool operator==(const RNTupleAttrSetDescriptor &other) const
std::uint32_t fAnchorLength
uncompressed size of the linked anchor
A helper class for piece-wise construction of an RClusterDescriptor.
RResult< void > MarkSuppressedColumnRange(ROOT::DescriptorId_t physicalId)
Books the given column ID as being suppressed in this cluster.
RResult< void > CommitColumnRange(ROOT::DescriptorId_t physicalId, std::uint64_t firstElementIndex, std::uint32_t compressionSettings, const RClusterDescriptor::RPageRange &pageRange)
RClusterDescriptorBuilder & AddExtendedColumnRanges(const RNTupleDescriptor &desc)
Add column and page ranges for columns created during late model extension missing in this cluster.
RResult< void > CommitSuppressedColumnRanges(const RNTupleDescriptor &desc)
Sets the first element index and number of elements for all the suppressed column ranges.
RResult< RClusterDescriptor > MoveDescriptor()
Move out the full cluster descriptor including page locations.
A helper class for piece-wise construction of an RClusterGroupDescriptor.
RClusterGroupDescriptorBuilder & EntrySpan(std::uint64_t entrySpan)
RClusterGroupDescriptorBuilder & PageListLocator(const RNTupleLocator &pageListLocator)
static RClusterGroupDescriptorBuilder FromSummary(const RClusterGroupDescriptor &clusterGroupDesc)
RClusterGroupDescriptorBuilder & PageListLength(std::uint64_t pageListLength)
RClusterGroupDescriptorBuilder & MinEntry(std::uint64_t minEntry)
RResult< RClusterGroupDescriptor > MoveDescriptor()
RClusterGroupDescriptorBuilder & ClusterGroupId(ROOT::DescriptorId_t clusterGroupId)
RClusterGroupDescriptorBuilder & NClusters(std::uint32_t nClusters)
RResult< RColumnDescriptor > MakeDescriptor() const
Attempt to make a column descriptor.
A column element encapsulates the translation between basic C++ types and their column representation...
static std::pair< std::uint16_t, std::uint16_t > GetValidBitRange(ROOT::ENTupleColumnType type)
Most types have a fixed on-disk bit width.
RResult< RExtraTypeInfoDescriptor > MoveDescriptor()
A helper class for piece-wise construction of an RFieldDescriptor.
RResult< RFieldDescriptor > MakeDescriptor() const
Attempt to make a field descriptor.
static RFieldDescriptorBuilder FromField(const ROOT::RFieldBase &field)
Make a new RFieldDescriptorBuilder based off a live RNTuple field.
void SetNTuple(const std::string_view name, const std::string_view description)
void SetSchemaFromExisting(const RNTupleDescriptor &descriptor)
Copies the "schema" part of descriptor into the builder's descriptor.
RResult< void > AddColumn(RColumnDescriptor &&columnDesc)
RResult< void > AddAttributeSet(Experimental::RNTupleAttrSetDescriptor &&attrSetDesc)
RResult< void > AddFieldProjection(ROOT::DescriptorId_t sourceId, ROOT::DescriptorId_t targetId)
void ReplaceExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
RResult< void > AddExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
void ShiftAliasColumns(std::uint32_t offset)
Shift column IDs of alias columns by offset
void SetVersion(std::uint16_t versionEpoch, std::uint16_t versionMajor, std::uint16_t versionMinor, std::uint16_t versionPatch)
void BeginHeaderExtension()
Mark the beginning of the header extension; any fields and columns added after a call to this functio...
RResult< void > AddCluster(RClusterDescriptor &&clusterDesc)
RResult< void > EnsureValidDescriptor() const
Checks whether invariants hold:
RResult< void > AddFieldLink(ROOT::DescriptorId_t fieldId, ROOT::DescriptorId_t linkId)
void AddField(const RFieldDescriptor &fieldDesc)
ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t BuildStreamerInfos() const
Get the streamer info records for custom classes. Currently requires the corresponding dictionaries t...
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.
std::map< Int_t, TVirtualStreamerInfo * > StreamerInfoMap_t
static RResult< StreamerInfoMap_t > DeserializeStreamerInfos(const std::string &extraTypeInfoContent)
The window of element indexes of a particular column in a particular cluster.
Records the partition of data into pages for a particular column in a particular cluster.
RPageInfoExtended Find(ROOT::NTupleSize_t idxInCluster) const
Find the page in the RPageRange that contains the given element. The element must exist.
std::size_t ExtendToFitColumnRange(const RColumnRange &columnRange, const ROOT::Internal::RColumnElementBase &element, std::size_t pageSize)
Extend this RPageRange to fit the given RColumnRange.
Metadata for RNTuple clusters.
ROOT::NTupleSize_t fFirstEntryIndex
Clusters can be swapped by adjusting the entry offsets of the cluster and all ranges.
std::unordered_map< ROOT::DescriptorId_t, RColumnRange > fColumnRanges
ROOT::DescriptorId_t fClusterId
RClusterDescriptor Clone() const
bool operator==(const RClusterDescriptor &other) const
RColumnRangeIterable GetColumnRangeIterable() const
Returns an iterator over pairs { columnId, columnRange }. The iteration order is unspecified.
std::unordered_map< ROOT::DescriptorId_t, RPageRange > fPageRanges
std::uint64_t GetNBytesOnStorage() const
Clusters are bundled in cluster groups.
RNTupleLocator fPageListLocator
The page list that corresponds to the cluster group.
RClusterGroupDescriptor Clone() const
std::vector< ROOT::DescriptorId_t > fClusterIds
The cluster IDs can be empty if the corresponding page list is not loaded.
std::uint64_t fMinEntry
The minimum first entry number of the clusters in the cluster group.
std::uint32_t fNClusters
Number of clusters is always known even if the cluster IDs are not (yet) populated.
std::uint64_t fPageListLength
Uncompressed size of the page list.
std::uint64_t fEntrySpan
Number of entries that are (partially for sharded clusters) covered by this cluster group.
bool operator==(const RClusterGroupDescriptor &other) const
RClusterGroupDescriptor CloneSummary() const
Creates a clone without the cluster IDs.
Metadata stored for every column of an RNTuple.
ROOT::DescriptorId_t fPhysicalColumnId
Usually identical to the logical column ID, except for alias columns where it references the shadowed...
bool operator==(const RColumnDescriptor &other) const
ROOT::DescriptorId_t fLogicalColumnId
The actual column identifier, which is the link to the corresponding field.
ROOT::DescriptorId_t fFieldId
Every column belongs to one and only one field.
std::int64_t fFirstElementIndex
The absolute value specifies the index for the first stored element for this column.
std::uint32_t fIndex
A field can be serialized into several columns, which are numbered from zero to $n$.
std::uint16_t fBitsOnStorage
The size in bits of elements of this column.
std::uint16_t fRepresentationIndex
A field may use multiple column representations, which are numbered from zero to $m$.
ROOT::ENTupleColumnType fType
The on-disk column type.
std::optional< RValueRange > fValueRange
Optional value range (used e.g. by quantized real fields)
RColumnDescriptor Clone() const
Get a copy of the descriptor.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
bool operator==(const RExtraTypeInfoDescriptor &other) const
RExtraTypeInfoDescriptor Clone() const
EExtraTypeInfoIds fContentId
Specifies the meaning of the extra information.
std::string fTypeName
The type name the extra information refers to; empty for RNTuple-wide extra information.
std::string fContent
The content format depends on the content ID and may be binary.
std::uint32_t fTypeVersion
Type version the extra type information is bound to.
A field translates read and write calls from/to underlying columns to/from tree values.
@ kTraitInvalidField
This field is an instance of RInvalidField and can be safely static_cast to it.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
std::unique_ptr< ROOT::RFieldBase > CreateField(const RNTupleDescriptor &ntplDesc, const ROOT::RCreateFieldOptions &options={}) const
In general, we create a field simply from the C++ type name.
std::uint32_t fFieldVersion
The version of the C++-type-to-column translation mechanics.
ROOT::DescriptorId_t fFieldId
RFieldDescriptor Clone() const
Get a copy of the descriptor.
std::uint64_t fNRepetitions
The number of elements per entry for fixed-size arrays.
std::uint32_t fColumnCardinality
The number of columns in the column representations of the field.
ROOT::DescriptorId_t fProjectionSourceId
For projected fields, the source field ID.
bool IsCustomEnum(const RNTupleDescriptor &desc) const
Tells if the field describes a user-defined enum type.
bool operator==(const RFieldDescriptor &other) const
std::string fFieldDescription
Free text set by the user.
ROOT::DescriptorId_t fParentId
Establishes sub field relationships, such as classes and collections.
bool IsCustomClass() const
Tells if the field describes a user-defined class rather than a fundamental type, a collection,...
std::string fTypeAlias
A typedef or using directive that resolved to the type name during field creation.
ROOT::ENTupleStructure fStructure
The structural information carried by this field in the data model tree.
std::vector< ROOT::DescriptorId_t > fLinkIds
The pointers in the other direction from parent to children.
std::string fFieldName
The leaf name, not including parent fields.
std::uint32_t fTypeVersion
The version of the C++ type itself.
std::string fTypeName
The C++ type that was used when writing the field.
std::vector< ROOT::DescriptorId_t > fLogicalColumnIds
The ordered list of columns attached to this field: first by representation index then by column inde...
std::optional< std::uint32_t > fTypeChecksum
For custom classes, we store the ROOT TClass reported checksum to facilitate the use of I/O rules tha...
Used in RFieldBase::Check() to record field creation failures.
Definition RField.hxx:93
@ kGeneric
Generic unrecoverable error.
@ kUnknownStructure
The field could not be created because its descriptor had an unknown structural role.
Used to loop over all the clusters of an RNTuple (in unspecified order)
Used to loop over all the cluster groups of an RNTuple (in unspecified order)
Used to loop over a field's associated columns.
std::vector< ROOT::DescriptorId_t > fColumns
The descriptor ids of the columns ordered by field, representation, and column index.
RColumnDescriptorIterable(const RNTupleDescriptor &ntuple, const RFieldDescriptor &fieldDesc)
Used to loop over all the extra type info record of an RNTuple (in unspecified order)
Used to loop over a field's child fields.
std::vector< ROOT::DescriptorId_t > GetTopLevelFields(const RNTupleDescriptor &desc) const
Return a vector containing the IDs of the top-level fields defined in the extension header,...
The on-storage metadata of an RNTuple.
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
ROOT::DescriptorId_t FindNextClusterId(ROOT::DescriptorId_t clusterId) const
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
std::set< unsigned int > fFeatureFlags
std::unordered_map< ROOT::DescriptorId_t, RClusterGroupDescriptor > fClusterGroupDescriptors
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::uint64_t fNPhysicalColumns
Updated by the descriptor builder when columns are added.
std::vector< Experimental::RNTupleAttrSetDescriptor > fAttributeSets
List of AttributeSets linked to this RNTuple.
ROOT::DescriptorId_t fFieldZeroId
Set by the descriptor builder.
std::uint64_t fNEntries
Updated by the descriptor builder when the cluster groups are added.
RClusterGroupDescriptorIterable GetClusterGroupIterable() const
RColumnDescriptorIterable GetColumnIterable() const
bool operator==(const RNTupleDescriptor &other) const
std::uint64_t fOnDiskFooterSize
Like fOnDiskHeaderSize, contains both cluster summaries and page locations.
std::uint16_t fVersionMinor
Set by the descriptor builder when deserialized.
ROOT::DescriptorId_t FindClusterId(ROOT::NTupleSize_t entryIdx) const
std::vector< std::uint64_t > GetFeatureFlags() const
ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
std::unique_ptr< ROOT::RNTupleModel > CreateModel(const RCreateModelOptions &options=RCreateModelOptions()) const
Re-create the C++ model from the stored metadata.
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
std::unordered_map< ROOT::DescriptorId_t, RClusterDescriptor > fClusterDescriptors
Potentially a subset of all the available clusters.
std::size_t GetNClusters() const
ROOT::DescriptorId_t FindPhysicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
RExtraTypeInfoDescriptorIterable GetExtraTypeInfoIterable() const
const RHeaderExtension * GetHeaderExtension() const
Return header extension information; if the descriptor does not have a header extension,...
std::uint64_t fNClusters
Updated by the descriptor builder when the cluster groups are added.
std::uint64_t fOnDiskHeaderXxHash3
Set by the descriptor builder when deserialized.
const RClusterDescriptor & GetClusterDescriptor(ROOT::DescriptorId_t clusterId) const
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
std::string fName
The RNTuple name needs to be unique in a given storage location (file)
std::uint64_t fOnDiskHeaderSize
Set by the descriptor builder when deserialized.
RResult< void > DropClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId)
std::uint16_t fVersionMajor
Set by the descriptor builder when deserialized.
std::vector< ROOT::DescriptorId_t > fSortedClusterGroupIds
References cluster groups sorted by entry range and thus allows for binary search.
std::unordered_map< ROOT::DescriptorId_t, RColumnDescriptor > fColumnDescriptors
ROOT::DescriptorId_t FindLogicalColumnId(ROOT::DescriptorId_t fieldId, std::uint32_t columnIndex, std::uint16_t representationIndex) const
std::unordered_map< ROOT::DescriptorId_t, RFieldDescriptor > fFieldDescriptors
ROOT::NTupleSize_t GetNElements(ROOT::DescriptorId_t physicalColumnId) const
RResult< void > AddClusterGroupDetails(ROOT::DescriptorId_t clusterGroupId, std::vector< RClusterDescriptor > &clusterDescs)
Methods to load and drop cluster group details (cluster IDs and page locations)
std::uint16_t fVersionPatch
Set by the descriptor builder when deserialized.
std::string fDescription
Free text from the user.
ROOT::Experimental::RNTupleAttrSetDescriptorIterable GetAttrSetIterable() const
RFieldDescriptorIterable GetTopLevelFields() const
std::uint16_t fVersionEpoch
Set by the descriptor builder when deserialized.
std::vector< RExtraTypeInfoDescriptor > fExtraTypeInfoDescriptors
RNTupleDescriptor Clone() const
std::string GetQualifiedFieldName(ROOT::DescriptorId_t fieldId) const
Walks up the parents of the field ID and returns a field name of the form a.b.c.d In case of invalid ...
RClusterDescriptorIterable GetClusterIterable() const
RNTupleDescriptor CloneSchema() const
Creates a descriptor containing only the schema information about this RNTuple, i....
std::uint64_t fGeneration
The generation of the descriptor.
ROOT::DescriptorId_t FindPrevClusterId(ROOT::DescriptorId_t clusterId) const
std::unique_ptr< RHeaderExtension > fHeaderExtension
Generic information about the physical location of data.
static std::unique_ptr< RNTupleModel > Create()
static std::unique_ptr< RNTupleModel > CreateBare()
Creates a "bare model", i.e. an RNTupleModel with no default entry.
static constexpr std::uint16_t kVersionPatch
Definition RNTuple.hxx:81
static constexpr std::uint16_t kVersionMajor
Definition RNTuple.hxx:79
static constexpr std::uint16_t kVersionEpoch
Definition RNTuple.hxx:78
static constexpr std::uint16_t kVersionMinor
Definition RNTuple.hxx:80
const_iterator begin() const
const_iterator end() const
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:198
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2973
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)
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.
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:2339