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
47
69
70std::unique_ptr<ROOT::RFieldBase>
72{
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.
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);
101 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField)
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);
114 if (itemField->GetTraits() & ROOT::RFieldBase::kTraitInvalidField)
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);
136 if (subfield.GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
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
153////////////////////////////////////////////////////////////////////////////////
154
156{
158 fBitsOnStorage == other.fBitsOnStorage && fType == other.fType && fFieldId == other.fFieldId &&
160 fValueRange == other.fValueRange;
161}
162
177
178////////////////////////////////////////////////////////////////////////////////
179
182{
184 // Small range, just iterate through fPageInfos
185 NTupleSize_t pageNumber = 0;
186 NTupleSize_t firstInPage = 0;
187 for (const auto &pi : fPageInfos) {
188 if (firstInPage + pi.GetNElements() > idxInCluster) {
189 return RPageInfoExtended{pi, firstInPage, pageNumber};
190 }
191 pageNumber++;
192 firstInPage += pi.GetNElements();
193 }
194 R__ASSERT(false);
195 }
196
197 const auto N = fCumulativeNElements->size();
198 R__ASSERT(N > 0);
199 R__ASSERT(N == fPageInfos.size());
200
201 std::size_t left = 0;
202 std::size_t right = N - 1;
203 std::size_t midpoint = N;
204 while (left <= right) {
205 midpoint = (left + right) / 2;
206 if ((*fCumulativeNElements)[midpoint] <= idxInCluster) {
207 left = midpoint + 1;
208 continue;
209 }
210
211 if ((midpoint == 0) || ((*fCumulativeNElements)[midpoint - 1] <= idxInCluster))
212 break;
213
214 right = midpoint - 1;
215 }
216 R__ASSERT(midpoint < N);
217
218 auto pageInfo = fPageInfos[midpoint];
219 decltype(idxInCluster) firstInPage = (midpoint == 0) ? 0 : (*fCumulativeNElements)[midpoint - 1];
220 R__ASSERT(firstInPage <= idxInCluster);
221 R__ASSERT((firstInPage + pageInfo.GetNElements()) > idxInCluster);
222 return RPageInfoExtended{pageInfo, firstInPage, midpoint};
223}
224
225std::size_t
228 std::size_t pageSize)
229{
231 R__ASSERT(!columnRange.IsSuppressed());
232
233 const auto nElements =
234 std::accumulate(fPageInfos.begin(), fPageInfos.end(), 0U,
235 [](std::size_t n, const auto &pageInfo) { return n + pageInfo.GetNElements(); });
236 const auto nElementsRequired = static_cast<std::uint64_t>(columnRange.GetNElements());
237
238 if (nElementsRequired == nElements)
239 return 0U;
240 R__ASSERT((nElementsRequired > nElements) && "invalid attempt to shrink RPageRange");
241
242 std::vector<RPageInfo> pageInfos;
243 // Synthesize new `RPageInfo`s as needed
244 const std::uint64_t nElementsPerPage = pageSize / element.GetSize();
245 R__ASSERT(nElementsPerPage > 0);
246 for (auto nRemainingElements = nElementsRequired - nElements; nRemainingElements > 0;) {
247 RPageInfo pageInfo;
248 pageInfo.SetNElements(std::min(nElementsPerPage, nRemainingElements));
249 RNTupleLocator locator;
251 locator.SetNBytesOnStorage(element.GetPackedSize(pageInfo.GetNElements()));
252 pageInfo.SetLocator(locator);
253 pageInfos.emplace_back(pageInfo);
254 nRemainingElements -= pageInfo.GetNElements();
255 }
256
257 pageInfos.insert(pageInfos.end(), std::make_move_iterator(fPageInfos.begin()),
258 std::make_move_iterator(fPageInfos.end()));
259 std::swap(fPageInfos, pageInfos);
260 return nElementsRequired - nElements;
261}
262
264{
265 return fClusterId == other.fClusterId && fFirstEntryIndex == other.fFirstEntryIndex &&
266 fNEntries == other.fNEntries && fColumnRanges == other.fColumnRanges && fPageRanges == other.fPageRanges;
267}
268
270{
271 std::uint64_t nbytes = 0;
272 for (const auto &pr : fPageRanges) {
273 for (const auto &pi : pr.second.GetPageInfos()) {
274 nbytes += pi.GetLocator().GetNBytesOnStorage();
275 }
276 }
277 return nbytes;
278}
279
281{
282 RClusterDescriptor clone;
283 clone.fClusterId = fClusterId;
285 clone.fNEntries = fNEntries;
287 for (const auto &d : fPageRanges)
288 clone.fPageRanges.emplace(d.first, d.second.Clone());
289 return clone;
290}
291
292////////////////////////////////////////////////////////////////////////////////
293
295{
296 return fContentId == other.fContentId && fTypeName == other.fTypeName && fTypeVersion == other.fTypeVersion;
297}
298
308
309////////////////////////////////////////////////////////////////////////////////
310
315
317{
318 // clang-format off
319 return fName == other.fName &&
320 fDescription == other.fDescription &&
321 fNEntries == other.fNEntries &&
322 fGeneration == other.fGeneration &&
323 fFieldZeroId == other.fFieldZeroId &&
328 // clang-format on
329}
330
332{
333 ROOT::NTupleSize_t result = 0;
334 for (const auto &cd : fClusterDescriptors) {
335 if (!cd.second.ContainsColumn(physicalColumnId))
336 continue;
337 auto columnRange = cd.second.GetColumnRange(physicalColumnId);
338 result = std::max(result, columnRange.GetFirstElementIndex() + columnRange.GetNElements());
339 }
340 return result;
341}
342
343////////////////////////////////////////////////////////////////////////////////
344/// Return the cluster boundaries for each cluster in this RNTuple.
345std::vector<ROOT::Internal::RNTupleClusterBoundaries>
347{
348 std::vector<Internal::RNTupleClusterBoundaries> boundaries;
349 boundaries.reserve(desc.GetNClusters());
350 auto clusterId = desc.FindClusterId(0, 0);
351 while (clusterId != ROOT::kInvalidDescriptorId) {
352 const auto &clusterDesc = desc.GetClusterDescriptor(clusterId);
353 R__ASSERT(clusterDesc.GetNEntries() > 0);
354 boundaries.emplace_back(ROOT::Internal::RNTupleClusterBoundaries{
355 clusterDesc.GetFirstEntryIndex(), clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries()});
356 clusterId = desc.FindNextClusterId(clusterId);
357 }
358 return boundaries;
359}
360
362ROOT::RNTupleDescriptor::FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
363{
364 std::string leafName(fieldName);
365 auto posDot = leafName.find_last_of('.');
366 if (posDot != std::string::npos) {
367 auto parentName = leafName.substr(0, posDot);
368 leafName = leafName.substr(posDot + 1);
369 parentId = FindFieldId(parentName, parentId);
370 }
371 auto itrFieldDesc = fFieldDescriptors.find(parentId);
372 if (itrFieldDesc == fFieldDescriptors.end())
374 for (const auto linkId : itrFieldDesc->second.GetLinkIds()) {
375 if (fFieldDescriptors.at(linkId).GetFieldName() == leafName)
376 return linkId;
377 }
379}
380
382{
383 if (fieldId == ROOT::kInvalidDescriptorId)
384 return "";
385
386 const auto &fieldDescriptor = fFieldDescriptors.at(fieldId);
387 auto prefix = GetQualifiedFieldName(fieldDescriptor.GetParentId());
388 if (prefix.empty())
389 return fieldDescriptor.GetFieldName();
390 return prefix + "." + fieldDescriptor.GetFieldName();
391}
392
394{
395 std::string typeName = fieldDesc.GetTypeName();
396
397 // ROOT v6.34, with spec versions before 1.0.0.1, did not properly renormalize the type name.
399 if (fVersionMajor == 0 && fVersionMinor == 0 && fVersionPatch < 1) {
400 typeName = ROOT::Internal::GetRenormalizedTypeName(typeName);
401 }
402
403 return typeName;
404}
405
407{
408 return FindFieldId(fieldName, GetFieldZeroId());
409}
410
412 std::uint32_t columnIndex,
413 std::uint16_t representationIndex) const
414{
415 auto itr = fFieldDescriptors.find(fieldId);
416 if (itr == fFieldDescriptors.cend())
418 if (columnIndex >= itr->second.GetColumnCardinality())
420 const auto idx = representationIndex * itr->second.GetColumnCardinality() + columnIndex;
421 if (itr->second.GetLogicalColumnIds().size() <= idx)
423 return itr->second.GetLogicalColumnIds()[idx];
424}
425
427 std::uint32_t columnIndex,
428 std::uint16_t representationIndex) const
429{
430 auto logicalId = FindLogicalColumnId(fieldId, columnIndex, representationIndex);
431 if (logicalId == ROOT::kInvalidDescriptorId)
433 return GetColumnDescriptor(logicalId).GetPhysicalId();
434}
435
438{
439 if (GetNClusterGroups() == 0)
441
442 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
443
444 std::size_t cgLeft = 0;
445 std::size_t cgRight = GetNClusterGroups() - 1;
446 while (cgLeft <= cgRight) {
447 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
448 const auto &clusterIds = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]).GetClusterIds();
449 R__ASSERT(!clusterIds.empty());
450
451 const auto &clusterDesc = GetClusterDescriptor(clusterIds.front());
452 // this may happen if the RNTuple has an empty schema
453 if (!clusterDesc.ContainsColumn(physicalColumnId))
455
456 const auto firstElementInGroup = clusterDesc.GetColumnRange(physicalColumnId).GetFirstElementIndex();
457 if (firstElementInGroup > index) {
458 // Look into the lower half of cluster groups
459 R__ASSERT(cgMidpoint > 0);
460 cgRight = cgMidpoint - 1;
461 continue;
462 }
463
464 const auto &lastColumnRange = GetClusterDescriptor(clusterIds.back()).GetColumnRange(physicalColumnId);
465 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <= index) {
466 // Look into the upper half of cluster groups
467 cgLeft = cgMidpoint + 1;
468 continue;
469 }
470
471 // Binary search in the current cluster group; since we already checked the element range boundaries,
472 // the element must be in that cluster group.
473 std::size_t clusterLeft = 0;
474 std::size_t clusterRight = clusterIds.size() - 1;
475 while (clusterLeft <= clusterRight) {
476 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
477 const auto clusterId = clusterIds[clusterMidpoint];
478 const auto &columnRange = GetClusterDescriptor(clusterId).GetColumnRange(physicalColumnId);
479
480 if (columnRange.Contains(index))
481 return clusterId;
482
483 if (columnRange.GetFirstElementIndex() > index) {
484 R__ASSERT(clusterMidpoint > 0);
485 clusterRight = clusterMidpoint - 1;
486 continue;
487 }
488
489 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <= index) {
490 clusterLeft = clusterMidpoint + 1;
491 continue;
492 }
493 }
494 R__ASSERT(false);
495 }
497}
498
500{
501 if (GetNClusterGroups() == 0)
503
504 // Binary search in the cluster group list, followed by a binary search in the clusters of that cluster group
505
506 std::size_t cgLeft = 0;
507 std::size_t cgRight = GetNClusterGroups() - 1;
508 while (cgLeft <= cgRight) {
509 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
510 const auto &cgDesc = GetClusterGroupDescriptor(fSortedClusterGroupIds[cgMidpoint]);
511
512 if (cgDesc.GetMinEntry() > entryIdx) {
513 R__ASSERT(cgMidpoint > 0);
514 cgRight = cgMidpoint - 1;
515 continue;
516 }
517
518 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
519 cgLeft = cgMidpoint + 1;
520 continue;
521 }
522
523 // Binary search in the current cluster group; since we already checked the element range boundaries,
524 // the element must be in that cluster group.
525 const auto &clusterIds = cgDesc.GetClusterIds();
526 R__ASSERT(!clusterIds.empty());
527 std::size_t clusterLeft = 0;
528 std::size_t clusterRight = clusterIds.size() - 1;
529 while (clusterLeft <= clusterRight) {
530 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
531 const auto &clusterDesc = GetClusterDescriptor(clusterIds[clusterMidpoint]);
532
533 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
534 R__ASSERT(clusterMidpoint > 0);
535 clusterRight = clusterMidpoint - 1;
536 continue;
537 }
538
539 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
540 clusterLeft = clusterMidpoint + 1;
541 continue;
542 }
543
544 return clusterIds[clusterMidpoint];
545 }
546 R__ASSERT(false);
547 }
549}
550
552{
553 // TODO(jblomer): we may want to shortcut the common case and check if clusterId + 1 contains
554 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
555 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
556 // binary search code path remains tested.
557 const auto &clusterDesc = GetClusterDescriptor(clusterId);
558 const auto firstEntryInNextCluster = clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries();
559 return FindClusterId(firstEntryInNextCluster);
560}
561
563{
564 // TODO(jblomer): we may want to shortcut the common case and check if clusterId - 1 contains
565 // firstEntryInNextCluster. This shortcut would currently always trigger. We do not want, however, to depend
566 // on the linearity of the descriptor IDs, so we should only enable the shortcut if we can ensure that the
567 // binary search code path remains tested.
568 const auto &clusterDesc = GetClusterDescriptor(clusterId);
569 if (clusterDesc.GetFirstEntryIndex() == 0)
571 return FindClusterId(clusterDesc.GetFirstEntryIndex() - 1);
572}
573
574std::vector<ROOT::DescriptorId_t>
576{
577 std::vector<ROOT::DescriptorId_t> fields;
578 for (const auto fieldId : fFieldIdsOrder) {
579 if (fFieldIdsLookup.count(desc.GetFieldDescriptor(fieldId).GetParentId()) == 0)
580 fields.emplace_back(fieldId);
581 }
582 return fields;
583}
584
586 const RFieldDescriptor &field)
587 : fNTuple(ntuple), fColumns(field.GetLogicalColumnIds())
588{
589}
590
592 : fNTuple(ntuple)
593{
594 std::deque<ROOT::DescriptorId_t> fieldIdQueue{ntuple.GetFieldZeroId()};
595
596 while (!fieldIdQueue.empty()) {
597 auto currFieldId = fieldIdQueue.front();
598 fieldIdQueue.pop_front();
599
600 const auto &columns = ntuple.GetFieldDescriptor(currFieldId).GetLogicalColumnIds();
601 fColumns.insert(fColumns.end(), columns.begin(), columns.end());
602
603 for (const auto &field : ntuple.GetFieldIterable(currFieldId)) {
604 auto fieldId = field.GetId();
605 fieldIdQueue.push_back(fieldId);
606 }
607 }
608}
609
610std::vector<std::uint64_t> ROOT::RNTupleDescriptor::GetFeatureFlags() const
611{
612 std::vector<std::uint64_t> result;
613 unsigned int base = 0;
614 std::uint64_t flags = 0;
615 for (auto f : fFeatureFlags) {
616 if ((f > 0) && ((f % 64) == 0))
617 throw RException(R__FAIL("invalid feature flag: " + std::to_string(f)));
618 while (f > base + 64) {
619 result.emplace_back(flags);
620 flags = 0;
621 base += 64;
622 }
623 f -= base;
624 flags |= std::uint64_t(1) << f;
625 }
626 result.emplace_back(flags);
627 return result;
628}
629
631 std::vector<RClusterDescriptor> &clusterDescs)
632{
633 auto iter = fClusterGroupDescriptors.find(clusterGroupId);
634 if (iter == fClusterGroupDescriptors.end())
635 return R__FAIL("invalid attempt to add details of unknown cluster group");
636 if (iter->second.HasClusterDetails())
637 return R__FAIL("invalid attempt to re-populate cluster group details");
638 if (iter->second.GetNClusters() != clusterDescs.size())
639 return R__FAIL("mismatch of number of clusters");
640
641 std::vector<ROOT::DescriptorId_t> clusterIds;
642 for (unsigned i = 0; i < clusterDescs.size(); ++i) {
643 clusterIds.emplace_back(clusterDescs[i].GetId());
644 auto [_, success] = fClusterDescriptors.emplace(clusterIds.back(), std::move(clusterDescs[i]));
645 if (!success) {
646 return R__FAIL("invalid attempt to re-populate existing cluster");
647 }
648 }
649 std::sort(clusterIds.begin(), clusterIds.end(), [this](ROOT::DescriptorId_t a, ROOT::DescriptorId_t b) {
650 return fClusterDescriptors[a].GetFirstEntryIndex() < fClusterDescriptors[b].GetFirstEntryIndex();
651 });
652 auto cgBuilder = Internal::RClusterGroupDescriptorBuilder::FromSummary(iter->second);
653 cgBuilder.AddSortedClusters(clusterIds);
654 iter->second = cgBuilder.MoveDescriptor().Unwrap();
655 return RResult<void>::Success();
656}
657
659{
660 auto iter = fClusterGroupDescriptors.find(clusterGroupId);
661 if (iter == fClusterGroupDescriptors.end())
662 return R__FAIL("invalid attempt to drop cluster details of unknown cluster group");
663 if (!iter->second.HasClusterDetails())
664 return R__FAIL("invalid attempt to drop details of cluster group summary");
665
666 for (auto clusterId : iter->second.GetClusterIds())
667 fClusterDescriptors.erase(clusterId);
668 iter->second = iter->second.CloneSummary();
669 return RResult<void>::Success();
670}
671
672std::unique_ptr<ROOT::RNTupleModel> ROOT::RNTupleDescriptor::CreateModel(const RCreateModelOptions &options) const
673{
674 // Collect all top-level fields that have invalid columns (recursively): by default if we find any we throw an
675 // exception; if we are in ForwardCompatible mode, we proceed but skip of all those top-level fields.
676 std::unordered_set<ROOT::DescriptorId_t> invalidFields;
677 for (const auto &colDesc : GetColumnIterable()) {
678 if (colDesc.GetType() == ROOT::ENTupleColumnType::kUnknown) {
679 auto fieldId = colDesc.GetFieldId();
680 while (1) {
681 const auto &field = GetFieldDescriptor(fieldId);
682 if (field.GetParentId() == GetFieldZeroId())
683 break;
684 fieldId = field.GetParentId();
685 }
686 invalidFields.insert(fieldId);
687
688 // No need to look for all invalid fields if we're gonna error out anyway
689 if (!options.GetForwardCompatible())
690 break;
691 }
692 }
693
694 if (!options.GetForwardCompatible() && !invalidFields.empty())
696 "cannot create Model: descriptor contains unknown column types. Use 'SetForwardCompatible(true)' on the "
697 "RCreateModelOptions to create a partial model containing only the fields made up by known columns."));
698
699 auto fieldZero = std::make_unique<ROOT::RFieldZero>();
700 fieldZero->SetOnDiskId(GetFieldZeroId());
701 auto model = options.GetCreateBare() ? RNTupleModel::CreateBare(std::move(fieldZero))
702 : RNTupleModel::Create(std::move(fieldZero));
703 ROOT::RCreateFieldOptions createFieldOpts;
704 createFieldOpts.SetReturnInvalidOnError(options.GetForwardCompatible());
705 createFieldOpts.SetEmulateUnknownTypes(options.GetEmulateUnknownTypes());
706 for (const auto &topDesc : GetTopLevelFields()) {
707 if (invalidFields.count(topDesc.GetId()) > 0) {
708 // Field contains invalid columns: skip it
709 continue;
710 }
711
712 auto field = topDesc.CreateField(*this, createFieldOpts);
713
714 // If we got an InvalidField here, figure out if it's a hard error or if the field must simply be skipped.
715 // The only case where it's not a hard error is if the field has an unknown structure, as that case is
716 // covered by the ForwardCompatible flag (note that if the flag is off we would not get here
717 // in the first place, so we don't need to check for that flag again).
718 if (field->GetTraits() & ROOT::RFieldBase::kTraitInvalidField) {
719 const auto &invalid = static_cast<const RInvalidField &>(*field);
720 const auto cat = invalid.GetCategory();
721 bool mustThrow = cat != RInvalidField::ECategory::kUnknownStructure;
722 if (mustThrow)
723 throw invalid.GetError();
724
725 // Not a hard error: skip the field and go on.
726 continue;
727 }
728
729 if (options.GetReconstructProjections() && topDesc.IsProjectedField()) {
730 model->AddProjectedField(std::move(field), [this](const std::string &targetName) -> std::string {
731 return GetQualifiedFieldName(GetFieldDescriptor(FindFieldId(targetName)).GetProjectionSourceId());
732 });
733 } else {
734 model->AddField(std::move(field));
735 }
736 }
737 model->Freeze();
738 return model;
739}
740
742{
743 RNTupleDescriptor clone;
744 clone.fName = fName;
749 // OnDiskHeaderSize, OnDiskHeaderXxHash3 not copied because they may come from a merged header + extension header
750 // and therefore not represent the actual sources's header.
751 // OnDiskFooterSize not copied because it contains information beyond the schema, for example the clustering.
752
753 for (const auto &d : fFieldDescriptors)
754 clone.fFieldDescriptors.emplace(d.first, d.second.Clone());
755 for (const auto &d : fColumnDescriptors)
756 clone.fColumnDescriptors.emplace(d.first, d.second.Clone());
757
758 for (const auto &d : fExtraTypeInfoDescriptors)
759 clone.fExtraTypeInfoDescriptors.emplace_back(d.Clone());
761 clone.fHeaderExtension = std::make_unique<RHeaderExtension>(*fHeaderExtension);
762
763 return clone;
764}
765
767{
769
774
778 clone.fNEntries = fNEntries;
779 clone.fNClusters = fNClusters;
780 clone.fGeneration = fGeneration;
781 for (const auto &d : fClusterGroupDescriptors)
782 clone.fClusterGroupDescriptors.emplace(d.first, d.second.Clone());
784 for (const auto &d : fClusterDescriptors)
785 clone.fClusterDescriptors.emplace(d.first, d.second.Clone());
786 for (const auto &d : fAttributeSets)
787 clone.fAttributeSets.emplace_back(d.Clone());
788 return clone;
789}
790
791////////////////////////////////////////////////////////////////////////////////
792
794{
795 return fClusterGroupId == other.fClusterGroupId && fClusterIds == other.fClusterIds &&
796 fMinEntry == other.fMinEntry && fEntrySpan == other.fEntrySpan && fNClusters == other.fNClusters;
797}
798
810
817
818////////////////////////////////////////////////////////////////////////////////
819
822 std::uint64_t firstElementIndex,
823 std::uint32_t compressionSettings,
824 const RClusterDescriptor::RPageRange &pageRange)
825{
826 if (physicalId != pageRange.fPhysicalColumnId)
827 return R__FAIL("column ID mismatch");
828 if (fCluster.fColumnRanges.count(physicalId) > 0)
829 return R__FAIL("column ID conflict");
830 RClusterDescriptor::RColumnRange columnRange{physicalId, firstElementIndex, 0, compressionSettings};
831 for (const auto &pi : pageRange.fPageInfos) {
832 columnRange.IncrementNElements(pi.GetNElements());
833 }
834 fCluster.fPageRanges[physicalId] = pageRange.Clone();
835 fCluster.fColumnRanges[physicalId] = columnRange;
836 return RResult<void>::Success();
837}
838
841{
842 if (fCluster.fColumnRanges.count(physicalId) > 0)
843 return R__FAIL("column ID conflict");
844
846 columnRange.SetPhysicalColumnId(physicalId);
847 columnRange.SetIsSuppressed(true);
848 fCluster.fColumnRanges[physicalId] = columnRange;
849 return RResult<void>::Success();
850}
851
854{
855 for (auto &[_, columnRange] : fCluster.fColumnRanges) {
856 if (!columnRange.IsSuppressed())
857 continue;
858 R__ASSERT(columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex);
859
860 const auto &columnDesc = desc.GetColumnDescriptor(columnRange.GetPhysicalColumnId());
861 const auto &fieldDesc = desc.GetFieldDescriptor(columnDesc.GetFieldId());
862 // We expect only few columns and column representations per field, so we do a linear search
863 for (const auto otherColumnLogicalId : fieldDesc.GetLogicalColumnIds()) {
864 const auto &otherColumnDesc = desc.GetColumnDescriptor(otherColumnLogicalId);
865 if (otherColumnDesc.GetRepresentationIndex() == columnDesc.GetRepresentationIndex())
866 continue;
867 if (otherColumnDesc.GetIndex() != columnDesc.GetIndex())
868 continue;
869
870 // Found corresponding column of a different column representation
871 const auto &otherColumnRange = fCluster.GetColumnRange(otherColumnDesc.GetPhysicalId());
872 if (otherColumnRange.IsSuppressed())
873 continue;
874
875 columnRange.SetFirstElementIndex(otherColumnRange.GetFirstElementIndex());
876 columnRange.SetNElements(otherColumnRange.GetNElements());
877 break;
878 }
879
880 if (columnRange.GetFirstElementIndex() == ROOT::kInvalidNTupleIndex) {
881 return R__FAIL(std::string("cannot find non-suppressed column for column ID ") +
882 std::to_string(columnRange.GetPhysicalColumnId()) +
883 ", cluster ID: " + std::to_string(fCluster.GetId()));
884 }
885 }
886 return RResult<void>::Success();
887}
888
891{
892 /// Carries out a depth-first traversal of a field subtree rooted at `rootFieldId`. For each field, `visitField` is
893 /// called passing the field ID and the number of overall repetitions, taking into account the repetitions of each
894 /// parent field in the hierarchy.
895 auto fnTraverseSubtree = [&](ROOT::DescriptorId_t rootFieldId, std::uint64_t nRepetitionsAtThisLevel,
896 const auto &visitField, const auto &enterSubtree) -> void {
897 visitField(rootFieldId, nRepetitionsAtThisLevel);
898 for (const auto &f : desc.GetFieldIterable(rootFieldId)) {
899 const std::uint64_t nRepetitions = std::max(f.GetNRepetitions(), std::uint64_t{1U}) * nRepetitionsAtThisLevel;
900 enterSubtree(f.GetId(), nRepetitions, visitField, enterSubtree);
901 }
902 };
903
904 // Extended columns can only be part of the header extension
905 if (!desc.GetHeaderExtension())
906 return *this;
907
908 // Ensure that all columns in the header extension have their associated `R(Column|Page)Range`
909 // Extended columns can be attached both to fields of the regular header and to fields of the extension header
910 for (const auto &topLevelField : desc.GetTopLevelFields()) {
911 fnTraverseSubtree(
912 topLevelField.GetId(), std::max(topLevelField.GetNRepetitions(), std::uint64_t{1U}),
913 [&](ROOT::DescriptorId_t fieldId, std::uint64_t nRepetitions) {
914 for (const auto &c : desc.GetColumnIterable(fieldId)) {
915 const ROOT::DescriptorId_t physicalId = c.GetPhysicalId();
916 auto &columnRange = fCluster.fColumnRanges[physicalId];
917
918 // Initialize a RColumnRange for `physicalId` if it was not there. Columns that were created during model
919 // extension won't have on-disk metadata for the clusters that were already committed before the model
920 // was extended. Therefore, these need to be synthetically initialized upon reading.
921 if (columnRange.GetPhysicalColumnId() == ROOT::kInvalidDescriptorId) {
922 columnRange.SetPhysicalColumnId(physicalId);
923 columnRange.SetFirstElementIndex(0);
924 columnRange.SetNElements(0);
925 columnRange.SetIsSuppressed(c.IsSuppressedDeferredColumn());
926 }
927 // Fixup the RColumnRange and RPageRange in deferred columns. We know what the first element index and
928 // number of elements should have been if the column was not deferred; fix those and let
929 // `ExtendToFitColumnRange()` synthesize RPageInfos accordingly.
930 // Note that a deferred column (i.e, whose first element index is > 0) already met the criteria of
931 // `ROOT::RFieldBase::EntryToColumnElementIndex()`, i.e. it is a principal column reachable from the
932 // field zero excluding subfields of collection and variant fields.
933 if (c.IsDeferredColumn()) {
934 columnRange.SetFirstElementIndex(fCluster.GetFirstEntryIndex() * nRepetitions);
935 columnRange.SetNElements(fCluster.GetNEntries() * nRepetitions);
936 if (!columnRange.IsSuppressed()) {
937 auto &pageRange = fCluster.fPageRanges[physicalId];
938 pageRange.fPhysicalColumnId = physicalId;
939 const auto element = ROOT::Internal::RColumnElementBase::Generate<void>(c.GetType());
940 pageRange.ExtendToFitColumnRange(columnRange, *element, ROOT::Internal::RPage::kPageZeroSize);
941 }
942 } else if (!columnRange.IsSuppressed()) {
943 fCluster.fPageRanges[physicalId].fPhysicalColumnId = physicalId;
944 }
945 }
946 },
947 fnTraverseSubtree);
948 }
949 return *this;
950}
951
953{
954 if (fCluster.fClusterId == ROOT::kInvalidDescriptorId)
955 return R__FAIL("unset cluster ID");
956 if (fCluster.fNEntries == 0)
957 return R__FAIL("empty cluster");
958 for (auto &pr : fCluster.fPageRanges) {
959 if (fCluster.fColumnRanges.count(pr.first) == 0) {
960 return R__FAIL("missing column range");
961 }
962 pr.second.fCumulativeNElements.reset();
963 const auto nPages = pr.second.fPageInfos.size();
965 pr.second.fCumulativeNElements = std::make_unique<std::vector<NTupleSize_t>>();
966 pr.second.fCumulativeNElements->reserve(nPages);
968 for (const auto &pi : pr.second.fPageInfos) {
969 sum += pi.GetNElements();
970 pr.second.fCumulativeNElements->emplace_back(sum);
971 }
972 }
973 }
974 RClusterDescriptor result;
975 std::swap(result, fCluster);
976 return result;
977}
978
979////////////////////////////////////////////////////////////////////////////////
980
983{
985 builder.ClusterGroupId(clusterGroupDesc.GetId())
986 .PageListLocator(clusterGroupDesc.GetPageListLocator())
987 .PageListLength(clusterGroupDesc.GetPageListLength())
988 .MinEntry(clusterGroupDesc.GetMinEntry())
989 .EntrySpan(clusterGroupDesc.GetEntrySpan())
990 .NClusters(clusterGroupDesc.GetNClusters());
991 return builder;
992}
993
995{
996 if (fClusterGroup.fClusterGroupId == ROOT::kInvalidDescriptorId)
997 return R__FAIL("unset cluster group ID");
999 std::swap(result, fClusterGroup);
1000 return result;
1001}
1002
1003////////////////////////////////////////////////////////////////////////////////
1004
1006{
1008 throw RException(R__FAIL("invalid extra type info content id"));
1010 std::swap(result, fExtraTypeInfo);
1011 return result;
1012}
1013
1014////////////////////////////////////////////////////////////////////////////////
1015
1017{
1018 if (fDescriptor.fFieldDescriptors.count(fieldId) == 0)
1019 return R__FAIL("field with id '" + std::to_string(fieldId) + "' doesn't exist");
1020 return RResult<void>::Success();
1021}
1022
1024{
1025 if (fDescriptor.fVersionEpoch != RNTuple::kVersionEpoch) {
1026 return R__FAIL("unset or unsupported RNTuple epoch version");
1027 }
1028
1029 // Reuse field name validity check
1030 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fDescriptor.GetName(), "Field");
1031 if (!validName) {
1032 return R__FORWARD_ERROR(validName);
1033 }
1034
1035 for (const auto &[fieldId, fieldDesc] : fDescriptor.fFieldDescriptors) {
1036 // parent not properly set?
1037 if (fieldId != fDescriptor.GetFieldZeroId() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1038 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has an invalid parent id");
1039 }
1040
1041 // Same number of columns in every column representation?
1042 const auto columnCardinality = fieldDesc.GetColumnCardinality();
1043 if (columnCardinality == 0)
1044 continue;
1045
1046 // In AddColumn, we already checked that all but the last representation are complete.
1047 // Check that the last column representation is complete, i.e. has all columns.
1048 const auto &logicalColumnIds = fieldDesc.GetLogicalColumnIds();
1049 const auto nColumns = logicalColumnIds.size();
1050 // If we have only a single column representation, the following condition is true by construction
1051 if ((nColumns + 1) == columnCardinality)
1052 continue;
1053
1054 const auto &lastColumn = fDescriptor.GetColumnDescriptor(logicalColumnIds.back());
1055 if (lastColumn.GetIndex() + 1 != columnCardinality)
1056 return R__FAIL("field with id '" + std::to_string(fieldId) + "' has incomplete column representations");
1057 }
1058
1059 return RResult<void>::Success();
1060}
1061
1063{
1064 EnsureValidDescriptor().ThrowOnError();
1065 fDescriptor.fSortedClusterGroupIds.reserve(fDescriptor.fClusterGroupDescriptors.size());
1066 for (const auto &[id, _] : fDescriptor.fClusterGroupDescriptors)
1067 fDescriptor.fSortedClusterGroupIds.emplace_back(id);
1068 std::sort(fDescriptor.fSortedClusterGroupIds.begin(), fDescriptor.fSortedClusterGroupIds.end(),
1070 return fDescriptor.fClusterGroupDescriptors[a].GetMinEntry() <
1071 fDescriptor.fClusterGroupDescriptors[b].GetMinEntry();
1072 });
1073 RNTupleDescriptor result;
1074 std::swap(result, fDescriptor);
1075 return result;
1076}
1077
1078void ROOT::Internal::RNTupleDescriptorBuilder::SetVersion(std::uint16_t versionEpoch, std::uint16_t versionMajor,
1079 std::uint16_t versionMinor, std::uint16_t versionPatch)
1080{
1081 if (versionEpoch != RNTuple::kVersionEpoch) {
1082 throw RException(R__FAIL("unsupported RNTuple epoch version: " + std::to_string(versionEpoch)));
1083 }
1084 fDescriptor.fVersionEpoch = versionEpoch;
1085 fDescriptor.fVersionMajor = versionMajor;
1086 fDescriptor.fVersionMinor = versionMinor;
1087 fDescriptor.fVersionPatch = versionPatch;
1088}
1089
1097
1099 const std::string_view description)
1100{
1101 fDescriptor.fName = std::string(name);
1102 fDescriptor.fDescription = std::string(description);
1103}
1104
1106{
1107 if (flag > 0 && flag % 64 == 0)
1108 throw RException(R__FAIL("invalid feature flag: " + std::to_string(flag)));
1109 fDescriptor.fFeatureFlags.insert(flag);
1110}
1111
1114{
1115 if (fDesc.fName.empty())
1116 return R__FAIL("attribute set name cannot be empty");
1117 if (fDesc.fAnchorLength == 0)
1118 return R__FAIL("invalid anchor length");
1119 if (fDesc.fAnchorLocator.GetType() == RNTupleLocator::kTypeUnknown)
1120 return R__FAIL("invalid locator type");
1121
1122 return std::move(fDesc);
1123}
1124
1126{
1127 if (fColumn.GetLogicalId() == ROOT::kInvalidDescriptorId)
1128 return R__FAIL("invalid logical column id");
1129 if (fColumn.GetPhysicalId() == ROOT::kInvalidDescriptorId)
1130 return R__FAIL("invalid physical column id");
1131 if (fColumn.GetFieldId() == ROOT::kInvalidDescriptorId)
1132 return R__FAIL("invalid field id, dangling column");
1133
1134 // NOTE: if the column type is unknown we don't want to fail, as we might be reading an RNTuple
1135 // created with a future version of ROOT. In this case we just skip the valid bit range check,
1136 // as we have no idea what the valid range is.
1137 // In general, reading the metadata of an unknown column is fine, it becomes an error only when
1138 // we try to read the actual data contained in it.
1139 if (fColumn.GetType() != ENTupleColumnType::kUnknown) {
1140 const auto [minBits, maxBits] = ROOT::Internal::RColumnElementBase::GetValidBitRange(fColumn.GetType());
1141 if (fColumn.GetBitsOnStorage() < minBits || fColumn.GetBitsOnStorage() > maxBits)
1142 return R__FAIL("invalid column bit width");
1143 }
1144
1145 return fColumn.Clone();
1146}
1147
1150{
1151 RFieldDescriptorBuilder fieldDesc;
1152 fieldDesc.FieldVersion(field.GetFieldVersion())
1153 .TypeVersion(field.GetTypeVersion())
1154 .FieldName(field.GetFieldName())
1156 .TypeName(field.GetTypeName())
1157 .TypeAlias(field.GetTypeAlias())
1158 .Structure(field.GetStructure())
1159 .NRepetitions(field.GetNRepetitions());
1161 fieldDesc.TypeChecksum(field.GetTypeChecksum());
1164 fieldDesc.IsSoACollection(true);
1165 }
1166 return fieldDesc;
1167}
1168
1170{
1171 if (fField.GetId() == ROOT::kInvalidDescriptorId) {
1172 return R__FAIL("invalid field id");
1173 }
1174 if (fField.GetStructure() == ROOT::ENTupleStructure::kInvalid) {
1175 return R__FAIL("invalid field structure");
1176 }
1177 if (fField.IsSoACollection() && (fField.GetStructure() != ROOT::ENTupleStructure::kCollection)) {
1178 return R__FAIL("invalid SoA flag on non-collection field");
1179 }
1180 // FieldZero is usually named "" and would be a false positive here
1181 if (fField.GetParentId() != ROOT::kInvalidDescriptorId) {
1182 auto validName = ROOT::Internal::EnsureValidNameForRNTuple(fField.GetFieldName(), "Field");
1183 if (!validName) {
1184 return R__FORWARD_ERROR(validName);
1185 }
1186 if (fField.GetFieldName().empty()) {
1187 return R__FAIL("name cannot be empty string \"\"");
1188 }
1189 }
1190 return fField.Clone();
1191}
1192
1194{
1195 fDescriptor.fFieldDescriptors.emplace(fieldDesc.GetId(), fieldDesc.Clone());
1196 if (fDescriptor.fHeaderExtension)
1197 fDescriptor.fHeaderExtension->MarkExtendedField(fieldDesc);
1198 if (fieldDesc.GetFieldName().empty() && fieldDesc.GetParentId() == ROOT::kInvalidDescriptorId) {
1199 fDescriptor.fFieldZeroId = fieldDesc.GetId();
1200 }
1201}
1202
1205{
1206 auto fieldExists = RResult<void>::Success();
1207 if (!(fieldExists = EnsureFieldExists(fieldId)))
1208 return R__FORWARD_ERROR(fieldExists);
1209 if (!(fieldExists = EnsureFieldExists(linkId)))
1210 return R__FAIL("child field with id '" + std::to_string(linkId) + "' doesn't exist in NTuple");
1211
1212 if (linkId == fDescriptor.GetFieldZeroId()) {
1213 return R__FAIL("cannot make FieldZero a child field");
1214 }
1215 // fail if field already has another valid parent
1216 auto parentId = fDescriptor.fFieldDescriptors.at(linkId).GetParentId();
1217 if ((parentId != ROOT::kInvalidDescriptorId) && (parentId != fieldId)) {
1218 return R__FAIL("field '" + std::to_string(linkId) + "' already has a parent ('" + std::to_string(parentId) + ")");
1219 }
1220 if (fieldId == linkId) {
1221 return R__FAIL("cannot make field '" + std::to_string(fieldId) + "' a child of itself");
1222 }
1223 fDescriptor.fFieldDescriptors.at(linkId).fParentId = fieldId;
1224 fDescriptor.fFieldDescriptors.at(fieldId).fLinkIds.push_back(linkId);
1225 return RResult<void>::Success();
1226}
1227
1229 ROOT::DescriptorId_t targetId)
1230{
1231 auto fieldExists = RResult<void>::Success();
1232 if (!(fieldExists = EnsureFieldExists(sourceId)))
1233 return R__FORWARD_ERROR(fieldExists);
1234 if (!(fieldExists = EnsureFieldExists(targetId)))
1235 return R__FAIL("projected field with id '" + std::to_string(targetId) + "' doesn't exist in NTuple");
1236
1237 if (targetId == fDescriptor.GetFieldZeroId()) {
1238 return R__FAIL("cannot make FieldZero a projected field");
1239 }
1240 if (sourceId == targetId) {
1241 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of itself");
1242 }
1243 if (fDescriptor.fFieldDescriptors.at(sourceId).IsProjectedField()) {
1244 return R__FAIL("cannot make field '" + std::to_string(targetId) + "' a projection of an already projected field");
1245 }
1246 // fail if target field already has another valid projection source
1247 auto &targetDesc = fDescriptor.fFieldDescriptors.at(targetId);
1248 if (targetDesc.IsProjectedField() && targetDesc.GetProjectionSourceId() != sourceId) {
1249 return R__FAIL("field '" + std::to_string(targetId) + "' has already a projection source ('" +
1250 std::to_string(targetDesc.GetProjectionSourceId()) + ")");
1251 }
1252 fDescriptor.fFieldDescriptors.at(targetId).fProjectionSourceId = sourceId;
1253 return RResult<void>::Success();
1254}
1255
1257{
1258 const auto fieldId = columnDesc.GetFieldId();
1259 const auto columnIndex = columnDesc.GetIndex();
1260 const auto representationIndex = columnDesc.GetRepresentationIndex();
1261
1262 auto fieldExists = EnsureFieldExists(fieldId);
1263 if (!fieldExists) {
1264 return R__FORWARD_ERROR(fieldExists);
1265 }
1266 auto &fieldDesc = fDescriptor.fFieldDescriptors.find(fieldId)->second;
1267
1268 if (columnDesc.IsAliasColumn()) {
1269 if (columnDesc.GetType() != fDescriptor.GetColumnDescriptor(columnDesc.GetPhysicalId()).GetType())
1270 return R__FAIL("alias column type mismatch");
1271 }
1272 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex, representationIndex) != ROOT::kInvalidDescriptorId) {
1273 return R__FAIL("column index clash");
1274 }
1275 if (columnIndex > 0) {
1276 if (fDescriptor.FindLogicalColumnId(fieldId, columnIndex - 1, representationIndex) == ROOT::kInvalidDescriptorId)
1277 return R__FAIL("out of bounds column index");
1278 }
1279 if (representationIndex > 0) {
1280 if (fDescriptor.FindLogicalColumnId(fieldId, 0, representationIndex - 1) == ROOT::kInvalidDescriptorId) {
1281 return R__FAIL("out of bounds representation index");
1282 }
1283 if (columnIndex == 0) {
1284 assert(fieldDesc.fColumnCardinality > 0);
1285 if (fDescriptor.FindLogicalColumnId(fieldId, fieldDesc.fColumnCardinality - 1, representationIndex - 1) ==
1287 return R__FAIL("incomplete column representations");
1288 }
1289 } else {
1290 if (columnIndex >= fieldDesc.fColumnCardinality)
1291 return R__FAIL("irregular column representations");
1292 }
1293 } else {
1294 // This will set the column cardinality to the number of columns of the first representation
1295 fieldDesc.fColumnCardinality = columnIndex + 1;
1296 }
1297
1298 const auto logicalId = columnDesc.GetLogicalId();
1299 fieldDesc.fLogicalColumnIds.emplace_back(logicalId);
1300
1301 if (!columnDesc.IsAliasColumn())
1302 fDescriptor.fNPhysicalColumns++;
1303 fDescriptor.fColumnDescriptors.emplace(logicalId, std::move(columnDesc));
1304 if (fDescriptor.fHeaderExtension)
1305 fDescriptor.fHeaderExtension->MarkExtendedColumn(columnDesc);
1306
1307 return RResult<void>::Success();
1308}
1309
1311{
1312 const auto id = clusterGroup.GetId();
1313 if (fDescriptor.fClusterGroupDescriptors.count(id) > 0)
1314 return R__FAIL("cluster group id clash");
1315 fDescriptor.fNEntries = std::max(fDescriptor.fNEntries, clusterGroup.GetMinEntry() + clusterGroup.GetEntrySpan());
1316 fDescriptor.fNClusters += clusterGroup.GetNClusters();
1317 fDescriptor.fClusterGroupDescriptors.emplace(id, std::move(clusterGroup));
1318 return RResult<void>::Success();
1319}
1320
1325
1327{
1328 if (!fDescriptor.fHeaderExtension)
1329 fDescriptor.fHeaderExtension = std::make_unique<RNTupleDescriptor::RHeaderExtension>();
1330}
1331
1333{
1334 if (fDescriptor.GetNLogicalColumns() == 0)
1335 return;
1336 R__ASSERT(fDescriptor.GetNPhysicalColumns() > 0);
1337
1338 for (ROOT::DescriptorId_t id = fDescriptor.GetNLogicalColumns() - 1; id >= fDescriptor.GetNPhysicalColumns(); --id) {
1339 auto c = fDescriptor.fColumnDescriptors[id].Clone();
1340 R__ASSERT(c.IsAliasColumn());
1341 R__ASSERT(id == c.GetLogicalId());
1342 fDescriptor.fColumnDescriptors.erase(id);
1343 for (auto &link : fDescriptor.fFieldDescriptors[c.fFieldId].fLogicalColumnIds) {
1344 if (link == c.fLogicalColumnId) {
1345 link += offset;
1346 break;
1347 }
1348 }
1349 c.fLogicalColumnId += offset;
1350 R__ASSERT(fDescriptor.fColumnDescriptors.count(c.fLogicalColumnId) == 0);
1351 fDescriptor.fColumnDescriptors.emplace(c.fLogicalColumnId, std::move(c));
1352 }
1353}
1354
1356{
1357 auto clusterId = clusterDesc.GetId();
1358 if (fDescriptor.fClusterDescriptors.count(clusterId) > 0)
1359 return R__FAIL("cluster id clash");
1360 fDescriptor.fClusterDescriptors.emplace(clusterId, std::move(clusterDesc));
1361 return RResult<void>::Success();
1362}
1363
1366{
1367 // Make sure we have no duplicates
1368 if (std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1369 extraTypeInfoDesc) != fDescriptor.fExtraTypeInfoDescriptors.end()) {
1370 return R__FAIL("extra type info duplicates");
1371 }
1372 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1373 return RResult<void>::Success();
1374}
1375
1377{
1378 auto it = std::find(fDescriptor.fExtraTypeInfoDescriptors.begin(), fDescriptor.fExtraTypeInfoDescriptors.end(),
1379 extraTypeInfoDesc);
1380 if (it != fDescriptor.fExtraTypeInfoDescriptors.end())
1381 *it = std::move(extraTypeInfoDesc);
1382 else
1383 fDescriptor.fExtraTypeInfoDescriptors.emplace_back(std::move(extraTypeInfoDesc));
1384}
1385
1388{
1389 auto &attrSets = fDescriptor.fAttributeSets;
1390 if (std::find_if(attrSets.begin(), attrSets.end(), [&name = attrSetDesc.GetName()](const auto &desc) {
1391 return desc.GetName() == name;
1392 }) != attrSets.end()) {
1393 return R__FAIL("attribute sets with duplicate names");
1394 }
1395 attrSets.push_back(std::move(attrSetDesc));
1396 return RResult<void>::Success();
1397}
1398
1403
1406{
1407 return RFieldDescriptorIterable(*this, fieldDesc);
1408}
1409
1411 const RFieldDescriptor &fieldDesc,
1412 const std::function<bool(ROOT::DescriptorId_t, ROOT::DescriptorId_t)> &comparator) const
1413{
1414 return RFieldDescriptorIterable(*this, fieldDesc, comparator);
1415}
1416
1422
1429
1434
1440
1445
1448{
1449 return RColumnDescriptorIterable(*this, fieldDesc);
1450}
1451
1457
1462
1467
1472
1477
1484
1495
1497{
1499 return false;
1500 if (fieldDesc.GetTypeName().rfind("std::", 0) == 0)
1501 return false;
1502
1503 auto subFieldId = desc.FindFieldId("_0", fieldDesc.GetId());
1504 if (subFieldId == kInvalidDescriptorId)
1505 return false;
1506
1507 static const std::string gIntTypeNames[] = {"bool", "char", "std::int8_t", "std::uint8_t",
1508 "std::int16_t", "std::uint16_t", "std::int32_t", "std::uint32_t",
1509 "std::int64_t", "std::uint64_t"};
1510 return std::find(std::begin(gIntTypeNames), std::end(gIntTypeNames),
1511 desc.GetFieldDescriptor(subFieldId).GetTypeName()) != std::end(gIntTypeNames);
1512}
1513
1515{
1517 return false;
1518 return (fieldDesc.GetTypeName().rfind("std::atomic<", 0) == 0);
1519}
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking).
Definition RError.hxx:303
#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:299
#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
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
#define N
XFontStruct * id
Definition TGX11.cxx:147
char name[80]
Definition TGX11.cxx:148
#define _(A, B)
Definition cfortran.h:108
RResult< ROOT::Experimental::RNTupleAttrSetDescriptor > MoveDescriptor()
Attempt to make an AttributeSet descriptor.
Used to loop over all the Attribute Sets linked to an RNTuple.
Metadata stored for every Attribute Set linked to an RNTuple.
bool operator==(const RNTupleAttrSetDescriptor &other) const
std::uint32_t fAnchorLength
uncompressed size of the linked anchor
A helper class for piece-wise construction of an RClusterDescriptor.
RResult< void > MarkSuppressedColumnRange(ROOT::DescriptorId_t physicalId)
Books the given column ID as being suppressed in this cluster.
RResult< void > CommitColumnRange(ROOT::DescriptorId_t physicalId, std::uint64_t firstElementIndex, std::uint32_t compressionSettings, const RClusterDescriptor::RPageRange &pageRange)
RClusterDescriptorBuilder & AddExtendedColumnRanges(const RNTupleDescriptor &desc)
Add column and page ranges for columns created during late model extension missing in this cluster.
RResult< void > CommitSuppressedColumnRanges(const RNTupleDescriptor &desc)
Sets the first element index and number of elements for all the suppressed column ranges.
RResult< RClusterDescriptor > MoveDescriptor()
Move out the full cluster descriptor including page locations.
A helper class for piece-wise construction of an RClusterGroupDescriptor.
RClusterGroupDescriptorBuilder & EntrySpan(std::uint64_t entrySpan)
RClusterGroupDescriptorBuilder & PageListLocator(const RNTupleLocator &pageListLocator)
static RClusterGroupDescriptorBuilder FromSummary(const RClusterGroupDescriptor &clusterGroupDesc)
RClusterGroupDescriptorBuilder & PageListLength(std::uint64_t pageListLength)
RClusterGroupDescriptorBuilder & MinEntry(std::uint64_t minEntry)
RResult< RClusterGroupDescriptor > MoveDescriptor()
RClusterGroupDescriptorBuilder & ClusterGroupId(ROOT::DescriptorId_t clusterGroupId)
RClusterGroupDescriptorBuilder & NClusters(std::uint32_t nClusters)
RResult< RColumnDescriptor > MakeDescriptor() const
Attempt to make a column descriptor.
A column element encapsulates the translation between basic C++ types and their column representation...
static std::pair< std::uint16_t, std::uint16_t > GetValidBitRange(ROOT::ENTupleColumnType type)
Most types have a fixed on-disk bit width.
std::size_t GetPackedSize(std::size_t nElements=1U) const
RResult< RExtraTypeInfoDescriptor > MoveDescriptor()
A helper class for piece-wise construction of an RFieldDescriptor.
RFieldDescriptorBuilder & NRepetitions(std::uint64_t nRepetitions)
RFieldDescriptorBuilder & Structure(const ROOT::ENTupleStructure &structure)
RFieldDescriptorBuilder()=default
Make an empty dangling field descriptor.
RFieldDescriptorBuilder & TypeAlias(const std::string &typeAlias)
RFieldDescriptorBuilder & TypeVersion(std::uint32_t typeVersion)
RFieldDescriptorBuilder & IsSoACollection(bool val)
RFieldDescriptorBuilder & TypeChecksum(const std::optional< std::uint32_t > typeChecksum)
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.
RFieldDescriptorBuilder & FieldDescription(const std::string &fieldDescription)
RFieldDescriptorBuilder & FieldVersion(std::uint32_t fieldVersion)
RFieldDescriptorBuilder & FieldName(const std::string &fieldName)
RFieldDescriptorBuilder & TypeName(const std::string &typeName)
void SetNTuple(const std::string_view name, const std::string_view description)
void SetSchemaFromExisting(const RNTupleDescriptor &descriptor)
Copies the "schema" part of descriptor into the builder's descriptor.
RResult< void > AddColumn(RColumnDescriptor &&columnDesc)
RResult< void > AddAttributeSet(Experimental::RNTupleAttrSetDescriptor &&attrSetDesc)
RResult< void > AddFieldProjection(ROOT::DescriptorId_t sourceId, ROOT::DescriptorId_t targetId)
void ReplaceExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
RResult< void > AddExtraTypeInfo(RExtraTypeInfoDescriptor &&extraTypeInfoDesc)
void ShiftAliasColumns(std::uint32_t offset)
Shift column IDs of alias columns by offset.
void SetVersion(std::uint16_t versionEpoch, std::uint16_t versionMajor, std::uint16_t versionMinor, std::uint16_t versionPatch)
void BeginHeaderExtension()
Mark the beginning of the header extension; any fields and columns added after a call to this functio...
RResult< void > AddCluster(RClusterDescriptor &&clusterDesc)
RResult< void > EnsureValidDescriptor() const
Checks whether invariants hold:
RResult< void > AddFieldLink(ROOT::DescriptorId_t fieldId, ROOT::DescriptorId_t linkId)
void AddField(const RFieldDescriptor &fieldDesc)
RResult< void > AddClusterGroup(RClusterGroupDescriptor &&clusterGroup)
RResult< void > EnsureFieldExists(ROOT::DescriptorId_t fieldId) const
void SetFeature(unsigned int flag)
Sets the flag-th bit of the feature flag to 1.
A helper class for serializing and deserialization of the RNTuple binary format.
The window of element indexes of a particular column in a particular cluster.
void SetPhysicalColumnId(ROOT::DescriptorId_t id)
ROOT::DescriptorId_t GetPhysicalColumnId() const
void IncrementNElements(ROOT::NTupleSize_t by)
Records the partition of data into pages for a particular column in a particular cluster.
std::unique_ptr< std::vector< ROOT::NTupleSize_t > > fCumulativeNElements
Has the same length than fPageInfos and stores the sum of the number of elements of all the pages up ...
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.
ROOT::DescriptorId_t GetId() const
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 GetPageListLength() const
RNTupleLocator GetPageListLocator() const
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: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.
ROOT::ENTupleStructure GetStructure() const
@ 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.
const std::string & GetFieldName() const
const std::string & GetTypeAlias() const
const std::string & GetDescription() const
Get the field's description.
virtual std::uint32_t GetFieldVersion() const
Indicates an evolution of the mapping scheme from C++ type to columns.
virtual std::uint32_t GetTypeChecksum() const
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
std::uint32_t GetTraits() const
std::size_t GetNRepetitions() const
const std::string & GetTypeName() const
std::unique_ptr< RFieldBase > Clone(std::string_view newName) const
Copies the field and its subfields using a possibly new name and a new, unconnected set of columns.
virtual std::uint32_t GetTypeVersion() const
Indicates an evolution of the C++ type itself.
Metadata stored for every field of an RNTuple.
const std::string & GetTypeAlias() const
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.
ROOT::DescriptorId_t GetId() const
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.
ROOT::ENTupleStructure GetStructure() const
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.
ROOT::DescriptorId_t GetParentId() const
std::string fTypeAlias
A typedef or using directive that resolved to the type name during field creation.
ROOT::ENTupleStructure fStructure
The structural information carried by this field in the data model tree.
std::vector< ROOT::DescriptorId_t > fLinkIds
The pointers in the other direction from parent to children.
std::string fFieldName
The leaf name, not including parent fields.
bool fIsSoACollection
Indicates if this is a collection that should be represented in memory by a SoA layout.
std::uint32_t fTypeVersion
The version of the C++ type itself.
std::string fTypeName
The C++ type that was used when writing the field.
const std::string & GetFieldName() const
std::vector< ROOT::DescriptorId_t > fLogicalColumnIds
The ordered list of columns attached to this field: first by representation index then by column inde...
const std::string & GetTypeName() const
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:96
@ kGeneric
Generic unrecoverable error.
Definition RField.hxx:100
@ kUnknownStructure
The field could not be created because its descriptor had an unknown structural role.
Definition RField.hxx:106
ECategory GetCategory() const
Definition RField.hxx:128
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.
const RNTupleDescriptor & fNTuple
The associated RNTuple for this range.
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::unordered_set< ROOT::DescriptorId_t > fFieldIdsLookup
All field IDs of late model extensions for efficient lookup.
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,...
std::vector< ROOT::DescriptorId_t > fFieldIdsOrder
All field IDs of late model extensions, in the order of field addition.
The on-storage metadata of an RNTuple.
const RClusterGroupDescriptor & GetClusterGroupDescriptor(ROOT::DescriptorId_t clusterGroupId) const
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::size_t GetNClusterGroups() 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.
void SetType(ELocatorType type)
void SetNBytesOnStorage(std::uint64_t nBytesOnStorage)
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
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:197
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
const Int_t n
Definition legend1.C:16
Double_t ex[n]
Definition legend1.C:17
ROOT::DescriptorId_t CallFindClusterIdOn(const ROOT::RNTupleDescriptor &desc, ROOT::NTupleSize_t entryIdx)
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
ROOT::RResult< std::unique_ptr< ROOT::RFieldBase > > CallFieldBaseCreate(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
bool IsCustomEnumFieldDesc(const RNTupleDescriptor &desc, const RFieldDescriptor &fieldDesc)
Tells if the field describes a user-defined enum type.
std::vector< ROOT::Internal::RNTupleClusterBoundaries > GetClusterBoundaries(const RNTupleDescriptor &desc)
Return the cluster boundaries for each cluster in this RNTuple.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
bool IsStdAtomicFieldDesc(const RFieldDescriptor &fieldDesc)
Tells if the field describes a std::atomic<T> type.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr NTupleSize_t kInvalidNTupleIndex
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
Additional information about a page in an in-memory RPageRange.
Information about a single page in the context of a cluster's page range.
void SetLocator(const RNTupleLocator &locator)
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338