Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldMeta.cxx
Go to the documentation of this file.
1/// \file RFieldMeta.cxx
2/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
3/// \date 2024-11-19
4
5// This file has concrete RField implementations that depend on ROOT Meta:
6// - RClassField
7// - RSoAField
8// - REnumField
9// - RPairField
10// - RProxiedCollectionField
11// - RMapField
12// - RSetField
13// - RStreamerField
14// - RField<TObject>
15// - RVariantField
16
17#include <ROOT/BitUtils.hxx>
18#include <ROOT/RField.hxx>
19#include <ROOT/RFieldBase.hxx>
20#include <ROOT/RFieldUtils.hxx>
22#include <ROOT/RNTupleUtils.hxx>
23#include <ROOT/RSpan.hxx>
24
25#include <TBaseClass.h>
26#include <TBufferFile.h>
27#include <TClass.h>
28#include <TClassEdit.h>
29#include <TDataMember.h>
30#include <TEnum.h>
31#include <TObject.h>
32#include <TObjArray.h>
33#include <TObjString.h>
34#include <TRealData.h>
35#include <TSchemaRule.h>
36#include <TSchemaRuleSet.h>
37#include <TStreamerElement.h>
38#include <TVirtualObject.h>
40
41#include <algorithm>
42#include <array>
43#include <cstddef> // std::size_t
44#include <cstdint> // std::uint32_t et al.
45#include <cstring> // for memset
46#include <memory>
47#include <mutex>
48#include <string>
49#include <string_view>
50#include <unordered_set>
51#include <utility>
52#include <variant>
53
55
56namespace {
57
58TClass *EnsureValidClass(std::string_view className)
59{
60 auto cl = TClass::GetClass(std::string(className).c_str());
61 if (cl == nullptr) {
62 throw ROOT::RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
63 }
64 return cl;
65}
66
67/// Common checks used both by RClassField and RSoAField
68void EnsureValidUserClass(TClass *cl, const ROOT::RFieldBase &field, std::string_view fieldType)
69{
70 if (cl->GetState() < TClass::kInterpreted) {
71 throw ROOT::RException(R__FAIL(std::string(fieldType) + " " + cl->GetName() +
72 " cannot be constructed from a class that's not at least Interpreted"));
73 }
74 // Avoid accidentally supporting std types through TClass.
75 if (cl->Property() & kIsDefinedInStd) {
76 throw ROOT::RException(R__FAIL(field.GetTypeName() + " is not supported"));
77 }
78 if (field.GetTypeName() == "TObject") {
79 throw ROOT::RException(R__FAIL("TObject is only supported through RField<TObject>"));
80 }
81 if (cl->GetCollectionProxy()) {
82 throw ROOT::RException(R__FAIL(field.GetTypeName() + " has an associated collection proxy; "
83 "use RProxiedCollectionField instead"));
84 }
85 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
86 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
87 if (!cl->CanSplit() && cl->Size() > 1 &&
89 throw ROOT::RException(R__FAIL(field.GetTypeName() + " cannot be stored natively in RNTuple"));
90 }
93 throw ROOT::RException(
94 R__FAIL(field.GetTypeName() + " has streamer mode enforced, not supported as native RNTuple class"));
95 }
96 // Detect custom streamers set on individual members at runtime via
97 // TClass::SetMemberStreamer() or TClass::AdoptMemberStreamer().
98 // CanSplit() only checks for custom streamers set at compile time (fHasCustomStreamerMember),
99 // but runtime streamers are stored in TRealData and must be checked here.
100 if (!cl->GetListOfRealData()) {
101 cl->BuildRealData();
102 }
103 for (auto realMember : ROOT::Detail::TRangeStaticCast<TRealData>(*cl->GetListOfRealData())) {
104 if (realMember->GetStreamer()) {
105 throw ROOT::RException(R__FAIL(std::string(field.GetTypeName()) + " has member " + realMember->GetName() +
106 " with a custom streamer; not supported natively in RNTuple"));
107 }
108 }
109}
110
111TEnum *EnsureValidEnum(std::string_view enumName)
112{
113 auto e = TEnum::GetEnum(std::string(enumName).c_str());
114 if (e == nullptr) {
115 throw ROOT::RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
116 }
117 return e;
118}
119
120void EnsureValidAlignment(std::size_t alignment)
121{
123 throw ROOT::RException(R__FAIL(std::string("invalid alignment: ") + std::to_string(alignment)));
124}
125
126/// Create a comma-separated list of type names from the given fields. Uses either the real type names or the
127/// type aliases (if there are any, otherwise the actual type name). Used to construct template argument lists
128/// for templated types such as std::pair<...>, std::tuple<...>, std::variant<...>.
129std::string GetTypeList(std::span<std::unique_ptr<ROOT::RFieldBase>> itemFields, bool useTypeAliases)
130{
131 std::string result;
132 for (size_t i = 0; i < itemFields.size(); ++i) {
133 if (useTypeAliases && !itemFields[i]->GetTypeAlias().empty()) {
134 result += itemFields[i]->GetTypeAlias();
135 } else {
136 result += itemFields[i]->GetTypeName();
137 }
138 result.push_back(',');
139 }
140 if (result.empty()) {
141 throw ROOT::RException(R__FAIL("invalid empty type list provided as template argument"));
142 }
143 result.pop_back(); // remove trailing comma
144 return result;
145}
146
148{
149 std::string typePrefix;
150 switch (setType) {
151 case ROOT::RSetField::ESetType::kSet: typePrefix = "std::set<"; break;
152 case ROOT::RSetField::ESetType::kUnorderedSet: typePrefix = "std::unordered_set<"; break;
153 case ROOT::RSetField::ESetType::kMultiSet: typePrefix = "std::multiset<"; break;
154 case ROOT::RSetField::ESetType::kUnorderedMultiSet: typePrefix = "std::unordered_multiset<"; break;
155 default: R__ASSERT(false);
156 }
157 return typePrefix +
158 ((useTypeAlias && !innerField.GetTypeAlias().empty()) ? innerField.GetTypeAlias()
159 : innerField.GetTypeName()) +
160 ">";
161}
162
164{
165 if (const auto pairField = dynamic_cast<const ROOT::RPairField *>(innerField)) {
166 std::string typePrefix;
167 switch (mapType) {
168 case ROOT::RMapField::EMapType::kMap: typePrefix = "std::map<"; break;
169 case ROOT::RMapField::EMapType::kUnorderedMap: typePrefix = "std::unordered_map<"; break;
170 case ROOT::RMapField::EMapType::kMultiMap: typePrefix = "std::multimap<"; break;
171 case ROOT::RMapField::EMapType::kUnorderedMultiMap: typePrefix = "std::unordered_multimap<"; break;
172 default: R__ASSERT(false);
173 }
174 const auto &items = pairField->GetConstSubfields();
175 std::string type = typePrefix;
176 for (int i : {0, 1}) {
177 if (useTypeAliases && !items[i]->GetTypeAlias().empty()) {
178 type += items[i]->GetTypeAlias();
179 } else {
180 type += items[i]->GetTypeName();
181 }
182 if (i == 0)
183 type.push_back(',');
184 }
185 return type + ">";
186 }
187
188 throw ROOT::RException(R__FAIL("RMapField inner field type must be of RPairField"));
189}
190
191} // anonymous namespace
192
194 : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kRecord, false /* isSimple */),
196 fSubfieldsInfo(source.fSubfieldsInfo)
197{
198 for (const auto &f : source.GetConstSubfields()) {
199 RFieldBase::Attach(f->Clone(f->GetFieldName()));
200 }
201 fTraits = source.GetTraits();
202}
203
204ROOT::RClassField::RClassField(std::string_view fieldName, std::string_view className)
206{
207}
208
210 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kRecord,
211 false /* isSimple */),
213{
214 EnsureValidUserClass(fClass, *this, "RClassField");
215
217 throw ROOT::RException(R__FAIL(GetTypeName() + " is a SoA field and connot be used through RClassField"));
218 }
219
224
225 std::string renormalizedAlias;
228
229 int i = 0;
230 const auto *bases = fClass->GetListOfBases();
231 assert(bases);
233 if (baseClass->GetDelta() < 0) {
234 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
235 " virtually inherits from " + baseClass->GetName()));
236 }
237 TClass *c = baseClass->GetClassPointer();
238 auto subField =
239 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
240 fTraits &= subField->GetTraits();
241 Attach(std::move(subField), RSubfieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
242 i++;
243 }
245 // Skip, for instance, unscoped enum constants defined in the class
246 if (dataMember->Property() & kIsStatic)
247 continue;
248 // Skip members explicitly marked as transient by user comment
249 if (!dataMember->IsPersistent()) {
250 // TODO(jblomer): we could do better
252 continue;
253 }
254
255 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
256 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
257 // the string returned by dataMember->GetFullTypeName())
258 std::string typeName{dataMember->GetTrueTypeName()};
259
260 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
261 if (dataMember->Property() & kIsArray) {
262 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
263 typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
264 }
265 }
266
267 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
268
269 fTraits &= subField->GetTraits();
270 Attach(std::move(subField), RSubfieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
271 }
273}
274
276{
277 if (fStagingArea) {
278 for (const auto &[_, si] : fStagingItems) {
279 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
280 auto deleter = GetDeleterOf(*si.fField);
281 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
282 }
283 }
284 }
285}
286
287void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubfieldInfo info)
288{
289 fSubfieldsInfo.push_back(info);
290 RFieldBase::Attach(std::move(child));
291}
292
293std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
294{
296 const auto ruleset = fClass->GetSchemaRules();
297 if (!ruleset)
298 return rules;
299
300 if (!fieldDesc) {
301 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
302 // of the class
303 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
304 } else {
305 // We need to change (back) the name normalization from RNTuple to ROOT Meta
306 std::string normalizedName;
308 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
309 // on-disk version of the field.
310 if (fieldDesc->GetTypeChecksum()) {
311 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
312 } else {
313 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
314 }
315 }
316
317 // Cleanup and sort rules
318 // Check that any any given source member uses the same type in all rules
319 std::unordered_map<std::string, std::string> sourceNameAndType;
320 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
321 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
322 const auto rule = *itr;
323
324 // Erase unknown rule types
325 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
327 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
328 itr = rules.erase(itr);
329 continue;
330 }
331
332 bool hasConflictingSourceMembers = false;
333 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
334 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
335 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
336 if (!isNew && (itrSrc->second != memberType)) {
338 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
339 << memberType << " for member " << source->GetName();
341 break;
342 }
343 }
345 itr = rules.erase(itr);
346 continue;
347 }
348
349 // Rules targeting the entire object need to be executed at the end
350 if (rule->GetTarget() == nullptr) {
351 nskip++;
352 if (itr != rules.end() - nskip)
353 std::iter_swap(itr++, rules.end() - nskip);
354 continue;
355 }
356
357 ++itr;
358 }
359
360 return rules;
361}
362
363std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
364{
365 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
366}
367
368std::size_t ROOT::RClassField::AppendImpl(const void *from)
369{
370 std::size_t nbytes = 0;
371 for (unsigned i = 0; i < fSubfields.size(); i++) {
372 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
373 }
374 return nbytes;
375}
376
378{
379 for (const auto &[_, si] : fStagingItems) {
380 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
381 }
382 for (unsigned i = 0; i < fSubfields.size(); i++) {
383 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
384 }
385}
386
388{
389 for (const auto &[_, si] : fStagingItems) {
390 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
391 }
392 for (unsigned i = 0; i < fSubfields.size(); i++) {
393 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
394 }
395}
396
399{
402 return idSourceMember;
403
404 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
405 const auto subFieldName = subFieldDesc.GetFieldName();
406 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
407 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
409 return idSourceMember;
410 }
411 }
412
414}
415
416void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
417{
418 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
419 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
420 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
421 if (!fStagingClass) {
422 // For a rename rule, we may simply ask for the old class name
423 fStagingClass = TClass::GetClass(className.c_str());
424 }
425 } else {
426 fStagingClass = fClass;
427 }
428 R__ASSERT(fStagingClass);
429 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
430}
431
432void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
433 const ROOT::RNTupleDescriptor &desc,
435{
436 std::size_t stagingAreaSize = 0;
437 for (const auto rule : rules) {
438 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
439 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
440 if (!isNew) {
441 // This source member has already been processed by another rule (and we only support one type per member)
442 continue;
443 }
444 RStagingItem &stagingItem = itr->second;
445
446 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
448 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
449 source->GetName()));
450 }
451
452 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
453 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
454 memberField->SetOnDiskId(memberFieldId);
455 auto fieldZero = std::make_unique<RFieldZero>();
457 fieldZero->Attach(std::move(memberField));
458 stagingItem.fField = std::move(fieldZero);
459
460 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
461 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
462 // to be present in the TClass instance, too.
464 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
465 }
466 }
467
468 if (stagingAreaSize) {
469 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
470 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
471 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
472
473 for (const auto &[_, si] : fStagingItems) {
474 const auto &memberField = *si.fField->cbegin();
475 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
476 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
477 }
478 }
479 }
480}
481
483{
484 auto func = rule->GetReadFunctionPointer();
485 if (func == nullptr) {
486 // Can happen for rename rules
487 return;
488 }
489 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
490 TVirtualObject onfileObj{nullptr};
491 onfileObj.fClass = stagingClass;
492 onfileObj.fObject = stagingArea;
493 func(static_cast<char *>(target), &onfileObj);
494 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
495 });
496}
497
499{
500 std::vector<const TSchemaRule *> rules;
501 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
502 // will be marked as artificial (added member in a new class version or member set by rule).
503 std::unordered_set<std::string> regularSubfields;
504 // We generally don't support changing the number of base classes, with the exception of changing from/to zero
505 // base classes. The variable stores the number of on-disk base classes.
506 int nOnDiskBaseClasses = 0;
507
508 if (GetOnDiskId() == kInvalidDescriptorId) {
509 // This can happen for added base classes or added members of class type
510 rules = FindRules(nullptr);
511 if (!rules.empty())
512 SetStagingClass(GetTypeName(), GetTypeVersion());
513 } else {
514 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
515 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
516 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
517
518 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
519 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
520 // by a streamer field to read the data from disk.
521 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
522 substitute->SetOnDiskId(GetOnDiskId());
523 return substitute;
524 }
525
526 for (auto linkId : fieldDesc.GetLinkIds()) {
527 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
528 regularSubfields.insert(subFieldDesc.GetFieldName());
529 if (!subFieldDesc.GetFieldName().empty() && subFieldDesc.GetFieldName()[0] == ':')
531 }
532
533 rules = FindRules(&fieldDesc);
534
535 // If we found a rule, we know it is valid to read on-disk data because we found the rule according to the on-disk
536 // (source) type name and version/checksum.
537 if (rules.empty()) {
538 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
539 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
540 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
542 if (GetTypeName() != descTypeName) {
543 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
544 " vs. " + descTypeName));
545 }
546 }
547
548 const bool hasSources = std::any_of(rules.begin(), rules.end(), [](const auto &r) {
549 return r->GetSource() && (r->GetSource()->GetEntries() > 0);
550 });
551
552 // A staging class (conversion streamer info) only exists if there is at least one rule that has an
553 // on disk source member defined.
554 if (hasSources) {
555 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
556 PrepareStagingArea(rules, desc, fieldDesc);
557 for (auto &[_, si] : fStagingItems) {
559 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
560 }
561 }
562
563 // Remove target member of read rules from the list of regular members of the underlying on-disk field
564 for (const auto rule : rules) {
565 if (!rule->GetTarget())
566 continue;
567
568 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
569 regularSubfields.erase(std::string(target->GetString()));
570 }
571 }
572 }
573
574 for (const auto rule : rules) {
575 AddReadCallbacksFromIORule(rule);
576 }
577
578 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
579 int nInMemoryBaseClasses = 0;
580 for (auto &field : fSubfields) {
581 const auto &fieldName = field->GetFieldName();
582 if (regularSubfields.count(fieldName) == 0) {
583 CallSetArtificialOn(*field);
584 }
585 if (!fieldName.empty() && fieldName[0] == ':')
587 }
588
590 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
591 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
592 " base classes in memory "
593 " vs. " +
594 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
595 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
596 }
597
598 return nullptr;
599}
600
602{
603 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
604}
605
607{
608 fClass->New(where);
609}
610
612
614{
615 fClass->Destructor(objPtr, true /* dtorOnly */);
616 RDeleter::operator()(objPtr, dtorOnly);
617}
618
619std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
620{
621 std::vector<RValue> result;
622 auto valuePtr = value.GetPtr<void>();
623 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
624 result.reserve(fSubfields.size());
625 for (unsigned i = 0; i < fSubfields.size(); i++) {
626 result.emplace_back(
627 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
628 }
629 return result;
630}
631
633{
634 return fClass->GetClassSize();
635}
636
638{
639 const auto align = fClass->GetClassAlignment();
641 return align;
642}
643
645{
646 return fClass->GetClassVersion();
647}
648
650{
651 return fClass->GetCheckSum();
652}
653
654const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
655{
657 if (!polymorphic) {
658 return nullptr;
659 }
660 return fClass->GetTypeInfo();
661}
662
664{
665 visitor.VisitClassField(*this);
666}
667
668//------------------------------------------------------------------------------
669
672 fSoAClass(source.fSoAClass),
673 fSoAMemberOffsets(source.fSoAMemberOffsets)
674{
675 fTraits = source.GetTraits();
676 Attach(source.fSubfields[0]->Clone(source.fSubfields[0]->GetFieldName()));
677 fRecordMemberFields = fSubfields[0]->GetMutableSubfields();
679 for (const auto f : fRecordMemberFields)
680 fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
681 fLockSplitFields = std::make_unique<std::mutex>();
682}
683
684ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, std::string_view className)
686{
687}
688
690 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(clSoA->GetName()), ROOT::ENTupleStructure::kCollection,
691 false /* isSimple */),
692 fSoAClass(clSoA)
693{
694 static std::once_flag once;
695 std::call_once(once, []() {
696 R__LOG_WARNING(ROOT::Internal::NTupleLog()) << "The SoA field is experimental and still under development.";
697 });
698
699 EnsureValidUserClass(fSoAClass, *this, "RSoAField");
701 if (recordTypeName.empty()) {
702 throw ROOT::RException(R__FAIL(std::string("class ") + GetTypeName() +
703 " is not marked with the rntupleSoARecord "
704 "dictionary option; cannot create corresponding RSoAField."));
705 }
706 try {
707 Attach(std::make_unique<ROOT::RClassField>("_0", recordTypeName));
708 } catch (ROOT::RException &e) {
709 throw RException(R__FAIL("invalid record type of SoA field " + GetTypeName() + " [" + e.what() + "]"));
710 }
712 if (static_cast<std::uint32_t>(fSoAClass->GetClassVersion()) != fSubfields[0]->GetTypeVersion()) {
713 throw RException(R__FAIL(std::string("version mismatch between SoA type and underlying record type: ") +
714 std::to_string(fSoAClass->GetClassVersion()) + " vs. " +
715 std::to_string(fSubfields[0]->GetTypeVersion())));
716 }
717 fRecordMemberFields = fSubfields[0]->GetMutableSubfields();
718
719 std::unordered_map<std::string, std::size_t> recordFieldNameToIdx;
722 for (std::size_t i = 0; i < fRecordMemberFields.size(); ++i) {
723 const RFieldBase *f = fRecordMemberFields[i];
724 assert(!f->GetFieldName().empty());
725 if (f->GetFieldName()[0] == ':') {
726 throw RException(R__FAIL("SoA fields with inheritance are currently unsupported"));
727 }
728 recordFieldNameToIdx[f->GetFieldName()] = i;
729 fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
730 }
731
732 const auto *bases = fSoAClass->GetListOfBases();
733 assert(bases);
735 if (baseClass->GetDelta() < 0) {
736 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
737 " virtually inherits from " + baseClass->GetName()));
738 }
739 // At a later point, we will support inheritance
740 throw RException(R__FAIL("SoA fields with inheritance are currently unsupported"));
741 }
742
744 unsigned int nMembers = 0;
746 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
747 continue;
748
749 if (dataMember->Property() & kIsArray) {
750 throw RException(R__FAIL(std::string("unsupported array type in SoA class: ") + dataMember->GetName()));
751 }
752
753 const std::string typeName{dataMember->GetTrueTypeName()};
754 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
755 auto vecFieldPtr = dynamic_cast<RRVecField *>(subField.get());
756 if (!vecFieldPtr) {
757 throw RException(R__FAIL("invalid field type in SoA class: " + subField->GetTypeName()));
758 }
759 subField.release();
760 auto vecField = std::unique_ptr<RRVecField>(vecFieldPtr);
761
762 auto itr = recordFieldNameToIdx.find(vecField->GetFieldName());
763 if (itr == recordFieldNameToIdx.end()) {
764 throw RException(R__FAIL(std::string("unexpected SoA member: ") + vecField->GetFieldName()));
765 }
767 if (vecField->begin()->GetTypeName() != memberField->GetTypeName() ||
768 vecField->begin()->GetTypeAlias() != memberField->GetTypeAlias()) {
769 const std::string leftType =
770 vecField->begin()->GetTypeName() +
771 (vecField->begin()->GetTypeAlias().empty() ? "" : " [" + vecField->begin()->GetTypeAlias() + "]");
772 const std::string rightType =
773 memberField->GetTypeName() +
774 (memberField->GetTypeAlias().empty() ? "" : " [" + memberField->GetTypeAlias() + "]");
775 throw RException(R__FAIL(std::string("SoA member type mismatch: ") + vecField->GetFieldName() + " (" +
776 leftType + " vs. " + rightType + ")"));
777 }
778
779 assert(itr->second < fSoAMemberOffsets.size());
780 fSoAMemberOffsets[itr->second] = dataMember->GetOffset();
781 nMembers++;
782 }
783 if (recordFieldNameToIdx.size() != nMembers) {
784 throw RException(R__FAIL("missing SoA members"));
785 }
786
787 std::string renormalizedAlias;
790
792 fLockSplitFields = std::make_unique<std::mutex>();
793}
794
795std::unique_ptr<ROOT::RFieldBase> ROOT::Experimental::RSoAField::CloneImpl(std::string_view newName) const
796{
797 return std::unique_ptr<RSoAField>(new RSoAField(newName, *this));
798}
799
809
814
819
820std::size_t ROOT::Experimental::RSoAField::AppendImpl(const void *from)
821{
822 const std::size_t nSoAMembers = fSoAMemberOffsets.size();
823
824 std::size_t N = 0; // Set by first SoA member and verified for the rest
825 for (std::size_t i = 0; i < nSoAMembers; ++i) {
826 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
828 assert(*sizePtr >= 0);
829 if (i == 0) {
830 N = *sizePtr;
831 } else {
832 if (static_cast<std::size_t>(*sizePtr) != N) {
833 const auto f = fRecordMemberFields[i];
834 throw RException(R__FAIL("SoA length mismatch for " + f->GetFieldName() + ": " + std::to_string(*sizePtr) +
835 " vs. " + std::to_string(N) + " (expected)"));
836 }
837 }
838 }
839
840 std::size_t nbytes = 0;
841 if (N > 0) {
842 for (std::size_t i = 0; i < nSoAMembers; ++i) {
843 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
845 RFieldBase *memberField = fRecordMemberFields[i];
846 if (memberField->IsSimple()) {
847 GetPrincipalColumnOf(*memberField)->AppendV(*beginPtr, N);
848 nbytes += N * GetPrincipalColumnOf(*memberField)->GetElement()->GetPackedSize();
849 } else {
850 for (std::size_t j = 0; j < N; ++j) {
851 nbytes += CallAppendOn(*memberField, *beginPtr + j * memberField->GetValueSize());
852 }
853 }
854 }
855 }
856
857 fNWritten += N;
858 fPrincipalColumn->Append(&fNWritten);
859 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
860}
861
863{
864 // Read collection info for this entry
867 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &N);
868
869 const auto nSoAMembers = fSoAMemberOffsets.size();
870 for (std::size_t i = 0; i < nSoAMembers; ++i) {
871 RFieldBase *memberField = fRecordMemberFields[i];
872 const auto memberSize = memberField->GetValueSize();
873 void *rvecPtr = static_cast<unsigned char *>(to) + fSoAMemberOffsets[i];
874 auto begin = ROOT::RRVecField::ResizeRVec(rvecPtr, N, memberSize, memberField, fRecordMemberDeleters[i].get());
875
876 if (memberField->IsSimple() && N) {
877 GetPrincipalColumnOf(*memberField)->ReadV(collectionStart, N, begin);
878 } else {
879 for (std::size_t j = 0; j < N; ++j) {
880 CallReadOn(*memberField, collectionStart + j, begin + (j * memberSize));
881 }
882 }
883 }
884}
885
887{
888 fSoAClass->New(where);
889}
890
891ROOT::Experimental::RSoAField::RSoADeleter::RSoADeleter(TClass *cl) : RDeleter(cl->GetClassAlignment()), fSoAClass(cl)
892{
893}
894
896{
897 fSoAClass->Destructor(objPtr, true /* dtorOnly */);
898 RDeleter::operator()(objPtr, dtorOnly);
899}
900
901std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(const RValue &value) const
902{
903 const auto nSoAMembers = fSoAMemberOffsets.size();
904
905 {
906 std::lock_guard<std::mutex> lockGuard(*fLockSplitFields);
907 if (!fSplitFields) {
908 fSplitFields = std::make_unique<std::vector<std::unique_ptr<ROOT::RRVecField>>>();
909 fSplitFields->reserve(nSoAMembers);
910 for (std::size_t i = 0; i < nSoAMembers; ++i) {
911 const auto itemField = fRecordMemberFields[i];
912 fSplitFields->emplace_back(std::make_unique<RRVecField>(itemField->GetFieldName(), itemField->Clone("_0")));
913 }
914 }
915 }
916
917 auto valuePtr = value.GetPtr<void>();
918 auto soaPtr = static_cast<unsigned char *>(valuePtr.get());
919 std::vector<RValue> values;
920 values.reserve(nSoAMembers);
921 for (std::size_t i = 0; i < nSoAMembers; ++i) {
922 values.emplace_back(
923 (*fSplitFields)[i]->BindValue(std::shared_ptr<void>(valuePtr, soaPtr + fSoAMemberOffsets[i])));
924 }
925 return values;
926}
927
929{
930 return fSoAClass->GetClassSize();
931}
932
934{
935 return fSoAClass->GetClassVersion();
936}
937
939{
940 return fSoAClass->GetCheckSum();
941}
942
944{
945 const auto align = fSoAClass->GetClassAlignment();
947 return align;
948}
949
951{
952 // TODO(jblomer): factor out
954 if (!polymorphic) {
955 return nullptr;
956 }
957 return fSoAClass->GetTypeInfo();
958}
959
964
965//------------------------------------------------------------------------------
966
967ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
969{
970}
971
973 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
974 false /* isSimple */)
975{
976 // Avoid accidentally supporting std types through TEnum.
977 if (enump->Property() & kIsDefinedInStd) {
978 throw RException(R__FAIL(GetTypeName() + " is not supported"));
979 }
980
981 switch (enump->GetUnderlyingType()) {
982 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
983 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
984 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
985 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
986 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
987 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
988 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
989 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
990 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
991 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
992 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
993 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
994 }
995
997}
998
999ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
1000 std::unique_ptr<RFieldBase> intField)
1002{
1003 Attach(std::move(intField));
1005}
1006
1007std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
1008{
1009 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
1010 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
1011}
1012
1014{
1015 // TODO(jblomer): allow enum to enum conversion only by rename rule
1016 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1017}
1018
1019std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
1020{
1021 std::vector<RValue> result;
1022 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
1023 return result;
1024}
1025
1027{
1028 visitor.VisitEnumField(*this);
1029}
1030
1031//------------------------------------------------------------------------------
1032
1033ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
1034 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1035{
1036 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1037 if (typeAlias != GetTypeName())
1039
1040 AttachItemFields(std::move(itemFields));
1041
1042 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
1043 auto *c = TClass::GetClass(GetTypeName().c_str());
1044 if (!c)
1045 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1046 fSize = c->Size();
1047
1048 auto firstElem = c->GetRealData("first");
1049 if (!firstElem)
1050 throw RException(R__FAIL("first: no such member"));
1051 fOffsets.push_back(firstElem->GetThisOffset());
1052
1053 auto secondElem = c->GetRealData("second");
1054 if (!secondElem)
1055 throw RException(R__FAIL("second: no such member"));
1056 fOffsets.push_back(secondElem->GetThisOffset());
1057}
1058
1059std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
1060{
1061 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
1062 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
1063 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones)));
1064}
1065
1067{
1068 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1069
1070 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1071 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1072
1073 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1074 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1075 if (nOnDiskSubfields != 2) {
1076 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
1077 std::to_string(nOnDiskSubfields) + "\n" +
1078 Internal::GetTypeTraceReport(*this, desc)));
1079 }
1080}
1081
1082//------------------------------------------------------------------------------
1083
1086 bool readFromDisk)
1087{
1089 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
1090 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
1091 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
1092 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
1093 (ifuncs.fNext != nullptr));
1094 return ifuncs;
1095}
1096
1098 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
1099 false /* isSimple */),
1100 fNWritten(0)
1101{
1102 if (!classp->GetCollectionProxy())
1103 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
1104 if (classp->Property() & kIsDefinedInStd) {
1105 static const std::vector<std::string> supportedStdTypes = {
1106 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
1107 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
1108 bool isSupported = false;
1109 for (const auto &tn : supportedStdTypes) {
1110 if (GetTypeName().rfind(tn, 0) == 0) {
1111 isSupported = true;
1112 break;
1113 }
1114 }
1115 if (!isSupported)
1116 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
1117 }
1118
1119 std::string renormalizedAlias;
1122
1123 fProxy.reset(classp->GetCollectionProxy()->Generate());
1124 fProperties = fProxy->GetProperties();
1125 fCollectionType = fProxy->GetCollectionType();
1126 if (fProxy->HasPointers())
1127 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
1128
1129 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
1130 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1131}
1132
1133ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
1135{
1136 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
1138 throw RException(R__FAIL("custom associative collection proxies not supported"));
1139
1140 std::unique_ptr<ROOT::RFieldBase> itemField;
1141
1142 if (auto valueClass = fProxy->GetValueClass()) {
1143 // Element type is a class
1144 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
1145 } else {
1146 switch (fProxy->GetType()) {
1147 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
1148 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
1149 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
1150 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
1151 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
1152 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
1153 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
1154 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
1155 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
1156 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
1157 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
1158 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
1159 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
1160 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
1161 }
1162 }
1163
1164 fItemSize = itemField->GetValueSize();
1165 Attach(std::move(itemField));
1166}
1167
1168std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
1169{
1170 auto clone =
1171 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
1172 clone->fItemSize = fItemSize;
1173 clone->Attach(fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1174 return clone;
1175}
1176
1178{
1179 std::size_t nbytes = 0;
1180 unsigned count = 0;
1181 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
1182 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
1183 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1184 nbytes += CallAppendOn(*fSubfields[0], ptr);
1185 count++;
1186 }
1187
1188 fNWritten += count;
1189 fPrincipalColumn->Append(&fNWritten);
1190 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1191}
1192
1194{
1197 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1198
1199 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
1200 void *obj =
1201 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
1202
1203 unsigned i = 0;
1204 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
1205 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
1206 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
1207 }
1208 if (obj != to)
1209 fProxy->Commit(obj);
1210}
1211
1221
1226
1231
1233{
1234 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1235}
1236
1238{
1239 fProxy->New(where);
1240}
1241
1242std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
1243{
1244 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
1245 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
1246 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
1247 }
1248 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
1249}
1250
1252 std::shared_ptr<TVirtualCollectionProxy> proxy)
1253 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()), fProxy(proxy)
1254{
1255}
1256
1258 std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter, size_t itemSize)
1259 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()),
1260 fProxy(proxy),
1261 fItemDeleter(std::move(itemDeleter)),
1263{
1264 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1265}
1266
1268{
1269 if (fItemDeleter) {
1271 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
1272 fItemDeleter->operator()(ptr, true /* dtorOnly */);
1273 }
1274 }
1275 fProxy->Destructor(objPtr, true /* dtorOnly */);
1276 RDeleter::operator()(objPtr, dtorOnly);
1277}
1278
1279std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
1280{
1281 std::vector<RValue> result;
1282 auto valueRawPtr = value.GetPtr<void>().get();
1285 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1286 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
1287 }
1288 return result;
1289}
1290
1292{
1293 return fProxy->Sizeof();
1294}
1295
1297{
1298 const auto align = fProxy->GetCollectionClass()->GetClassAlignment();
1299 EnsureValidAlignment(align);
1300 return align;
1301}
1302
1304{
1305 visitor.VisitProxiedCollectionField(*this);
1306}
1307
1308//------------------------------------------------------------------------------
1309
1310ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
1312 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
1313 fMapType(mapType)
1314{
1315 if (!itemField->GetTypeAlias().empty())
1316 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
1317
1318 auto *itemClass = fProxy->GetValueClass();
1319 fItemSize = itemClass->GetClassSize();
1320
1321 Attach(std::move(itemField), "_0");
1322}
1323
1324std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
1325{
1326 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1327}
1328
1330{
1331 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
1332
1333 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1334
1335 switch (fMapType) {
1336 case EMapType::kMap:
1337 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1338 default:
1339 break;
1340 // no restrictions for multimaps
1341 }
1342}
1343
1344//------------------------------------------------------------------------------
1345
1346ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1348 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1349 fSetType(setType)
1350{
1351 if (!itemField->GetTypeAlias().empty())
1352 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1353
1354 fItemSize = itemField->GetValueSize();
1355
1356 Attach(std::move(itemField), "_0");
1357}
1358
1359std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1360{
1361 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1362}
1363
1365{
1366 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1367 "std::unordered_map<"};
1368
1369 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1370
1371 switch (fSetType) {
1372 case ESetType::kSet:
1373 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1374 default:
1375 break;
1376 // no restrictions for multisets
1377 }
1378}
1379
1380//------------------------------------------------------------------------------
1381
1382namespace {
1383
1384/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1385class TBufferRecStreamer : public TBufferFile {
1386public:
1387 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1388
1389private:
1390 RCallbackStreamerInfo fCallbackStreamerInfo;
1391
1392public:
1393 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1394 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
1395 {
1396 }
1397 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1398};
1399
1400} // anonymous namespace
1401
1402ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1404{
1405}
1406
1408 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1409 false /* isSimple */),
1410 fClass(classp),
1411 fIndex(0)
1412{
1413 std::string renormalizedAlias;
1416
1418 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1419 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1420 // implicit constructors and destructors.
1425}
1426
1427std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1428{
1429 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1430}
1431
1432std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1433{
1434 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1435 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1436 fClass->Streamer(const_cast<void *>(from), buffer);
1437
1438 auto nbytes = buffer.Length();
1439 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1440 fIndex += nbytes;
1441 fPrincipalColumn->Append(&fIndex);
1442 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1443}
1444
1446{
1449 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1450
1452 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1453 fClass->Streamer(to, buffer);
1454}
1455
1465
1470
1475
1477{
1478 source.RegisterStreamerInfos();
1479 return nullptr;
1480}
1481
1483{
1484 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1485}
1486
1488{
1489 fClass->New(where);
1490}
1491
1496
1498{
1499 fClass->Destructor(objPtr, true /* dtorOnly */);
1500 RDeleter::operator()(objPtr, dtorOnly);
1501}
1502
1512
1514{
1515 const auto align = fClass->GetClassAlignment();
1516 EnsureValidAlignment(align);
1517 return align;
1518}
1519
1521{
1522 return fClass->GetClassSize();
1523}
1524
1526{
1527 return fClass->GetClassVersion();
1528}
1529
1531{
1532 return fClass->GetCheckSum();
1533}
1534
1536{
1537 visitor.VisitStreamerField(*this);
1538}
1539
1540//------------------------------------------------------------------------------
1541
1543{
1544 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1545 return dataMember->GetOffset();
1546 }
1547 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1548}
1549
1551 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1552{
1554 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1555 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1556}
1557
1559 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1560{
1561 assert(TObject::Class()->GetClassVersion() == 1);
1562
1564 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1565 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1566}
1567
1568std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1569{
1570 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1571}
1572
1573std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1574{
1575 // Cf. TObject::Streamer()
1576
1577 auto *obj = static_cast<const TObject *>(from);
1578 if (obj->TestBit(TObject::kIsReferenced)) {
1579 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1580 }
1581
1582 std::size_t nbytes = 0;
1583 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1584
1585 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1586 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1587 nbytes += CallAppendOn(*fSubfields[1], &bits);
1588
1589 return nbytes;
1590}
1591
1593{
1594 // Cf. TObject::Streamer()
1595
1596 auto *obj = static_cast<TObject *>(to);
1597 if (obj->TestBit(TObject::kIsReferenced)) {
1598 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1599 }
1600
1601 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1602
1603 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1605 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1606}
1607
1609{
1610 UInt_t uniqueID, bits;
1611 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1612 CallReadOn(*fSubfields[1], globalIndex, &bits);
1613 ReadTObject(to, uniqueID, bits);
1614}
1615
1617{
1618 UInt_t uniqueID, bits;
1619 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1620 CallReadOn(*fSubfields[1], localIndex, &bits);
1621 ReadTObject(to, uniqueID, bits);
1622}
1623
1625{
1626 return TObject::Class()->GetClassVersion();
1627}
1628
1630{
1631 return TObject::Class()->GetCheckSum();
1632}
1633
1635{
1636 new (where) TObject();
1637}
1638
1639std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1640{
1641 std::vector<RValue> result;
1642 // Use GetPtr<TObject> to type-check
1643 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1644 auto charPtr = static_cast<unsigned char *>(ptr.get());
1645 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1646 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1647 return result;
1648}
1649
1651{
1652 return sizeof(TObject);
1653}
1654
1656{
1657 return alignof(TObject);
1658}
1659
1661{
1662 visitor.VisitTObjectField(*this);
1663}
1664
1665//------------------------------------------------------------------------------
1666
1667ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1668 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1669{
1670 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1671 if (typeAlias != GetTypeName())
1673
1674 AttachItemFields(std::move(itemFields));
1675
1676 auto *c = TClass::GetClass(GetTypeName().c_str());
1677 if (!c)
1678 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1679 fSize = c->Size();
1680
1681 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1682 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1683 // following the order of the type list.
1684 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1685 // members, the assertion below will fail.
1686 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1687 std::string memberName("_" + std::to_string(i));
1688 auto member = c->GetRealData(memberName.c_str());
1689 if (!member)
1690 throw RException(R__FAIL(memberName + ": no such member"));
1691 fOffsets.push_back(member->GetThisOffset());
1692 }
1693}
1694
1695std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1696{
1697 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1698 itemClones.reserve(fSubfields.size());
1699 for (const auto &f : fSubfields) {
1700 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1701 }
1702 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones)));
1703}
1704
1706{
1707 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1708
1709 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1710 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1711
1712 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1713 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1714 const auto nSubfields = fSubfields.size();
1716 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1717 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1718 Internal::GetTypeTraceReport(*this, desc)));
1719 }
1720}
1721
1722//------------------------------------------------------------------------------
1723
1724namespace {
1725
1726// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1727constexpr std::size_t GetVariantTagSize()
1728{
1729 // Should be all zeros except for the tag, which is 1
1730 std::variant<char> t;
1731 constexpr auto sizeOfT = sizeof(t);
1732
1733 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1734 return sizeOfT == 2 ? 1 : 4;
1735}
1736
1737template <std::size_t VariantSizeT>
1738struct RVariantTag {
1739 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1740 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1741};
1742
1743} // anonymous namespace
1744
1746 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1747 fMaxItemSize(source.fMaxItemSize),
1748 fMaxAlignment(source.fMaxAlignment),
1749 fTagOffset(source.fTagOffset),
1750 fVariantOffset(source.fVariantOffset),
1751 fNWritten(source.fNWritten.size(), 0)
1752{
1753 for (const auto &f : source.GetConstSubfields())
1754 Attach(f->Clone(f->GetFieldName()));
1755 fTraits = source.fTraits;
1756}
1757
1758ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1759 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1760 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1761{
1762 // The variant needs to initialize its own tag member
1764
1765 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1766 if (typeAlias != GetTypeName())
1768
1769 auto nFields = itemFields.size();
1770 if (nFields == 0 || nFields > kMaxVariants) {
1771 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1772 }
1773 fNWritten.resize(nFields, 0);
1774 for (unsigned int i = 0; i < nFields; ++i) {
1777 fTraits &= itemFields[i]->GetTraits();
1778 Attach(std::move(itemFields[i]), "_" + std::to_string(i));
1779 }
1780
1781 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1782 // For instance, std::variant<std::optional<int>> on macOS.
1783 auto cl = TClass::GetClass(GetTypeName().c_str());
1784 assert(cl);
1785 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1786 if (dm)
1787 fVariantOffset = dm->GetOffset();
1788
1789 const auto tagSize = GetVariantTagSize();
1790 const auto padding = tagSize - (fMaxItemSize % tagSize);
1792}
1793
1794std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1795{
1796 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1797}
1798
1799std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1800{
1801 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1802 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1803 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1804}
1805
1806void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1807{
1808 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1809 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1810 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1811}
1812
1813std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1814{
1815 auto tag = GetTag(from, fTagOffset);
1816 std::size_t nbytes = 0;
1817 auto index = 0;
1818 if (tag > 0) {
1819 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1820 index = fNWritten[tag - 1]++;
1821 }
1823 fPrincipalColumn->Append(&varSwitch);
1824 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1825}
1826
1828{
1830 std::uint32_t tag;
1831 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1832 R__ASSERT(tag < 256);
1833
1834 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1835 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1836 // any `std::holds_alternative<T>` check fail later.
1837 if (R__likely(tag > 0)) {
1838 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1839 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1840 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1841 }
1842 SetTag(to, fTagOffset, tag);
1843}
1844
1850
1855
1860
1862{
1863 static const std::vector<std::string> prefixes = {"std::variant<"};
1864
1865 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1866 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1867
1868 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1869 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1870 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1871 Internal::GetTypeTraceReport(*this, desc)));
1872 }
1873}
1874
1876{
1877 memset(where, 0, GetValueSize());
1878 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1879 SetTag(where, fTagOffset, 1);
1880}
1881
1883{
1884 auto tag = GetTag(objPtr, fTagOffset);
1885 if (tag > 0) {
1886 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1887 }
1888 RDeleter::operator()(objPtr, dtorOnly);
1889}
1890
1891std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1892{
1893 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1894 itemDeleters.reserve(fSubfields.size());
1895 for (const auto &f : fSubfields) {
1896 itemDeleters.emplace_back(GetDeleterOf(*f));
1897 }
1898 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, GetAlignment(), std::move(itemDeleters));
1899}
1900
1902{
1903 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1904}
1905
1907{
1908 const auto alignment = GetAlignment();
1909 const auto actualSize = fTagOffset + GetVariantTagSize();
1910 const auto padding = alignment - (actualSize % alignment);
1911 return actualSize + ((padding == alignment) ? 0 : padding);
1912}
1913
1915{
1916 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1917}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:587
#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 R__LOG_WARNING(...)
Definition RLogger.hxx:357
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kLong_t
Definition TDataType.h:30
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kUInt_t
Definition TDataType.h:30
@ kClassHasExplicitCtor
@ kClassHasImplicitCtor
@ kClassHasVirtual
@ kClassHasExplicitDtor
@ kClassHasImplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#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 Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
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 r
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 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 child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char mode
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 Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:148
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:108
Abstract base class for classes implementing the visitor design pattern.
void operator()(void *objPtr, bool dtorOnly) final
The SoA field provides I/O for an in-memory SoA layout linked to an on-disk collection of the underly...
Definition RFieldSoA.hxx:55
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::vector< std::size_t > fSoAMemberOffsets
The offset of the RVec members in the SoA type in the order of subfields of the underlying record typ...
Definition RFieldSoA.hxx:69
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::vector< std::unique_ptr< RDeleter > > fRecordMemberDeleters
Definition RFieldSoA.hxx:71
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
size_t GetAlignment() const final
What alignof(T) for this type returns.
std::vector< RFieldBase * > fRecordMemberFields
Direct access to the member fields of the underlying record.
Definition RFieldSoA.hxx:66
RSoAField(std::string_view fieldName, const RSoAField &source)
Used by CloneImpl.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::unique_ptr< std::vector< std::unique_ptr< ROOT::RRVecField > > > fSplitFields
For reading and writing, the RVecs of the SoA class do not have a dedicated field.
Definition RFieldSoA.hxx:78
std::unique_ptr< std::mutex > fLockSplitFields
protects the fSplitFields member.
Definition RFieldSoA.hxx:79
Holds the index and the tag of a kSwitch column.
A helper class for piece-wise construction of an RExtraTypeInfoDescriptor.
static std::string SerializeStreamerInfos(const StreamerInfoMap_t &infos)
Abstract interface to read data from an ntuple.
void operator()(void *objPtr, bool dtorOnly) final
The field for a class with dictionary.
Definition RField.hxx:135
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
void AddReadCallbacksFromIORule(const TSchemaRule *rule)
Register post-read callback corresponding to a ROOT I/O customization rules.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::vector< RSubfieldInfo > fSubfieldsInfo
Additional information kept for each entry in fSubfields
Definition RField.hxx:166
void Attach(std::unique_ptr< RFieldBase > child, RSubfieldInfo info)
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::vector< const TSchemaRule * > FindRules(const ROOT::RFieldDescriptor *fieldDesc)
Given the on-disk information from the page source, find all the I/O customization rules that apply t...
ROOT::DescriptorId_t LookupMember(const ROOT::RNTupleDescriptor &desc, std::string_view memberName, ROOT::DescriptorId_t classFieldId)
Returns the id of member 'name' in the class field given by 'fieldId', or kInvalidDescriptorId if no ...
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
TClass * fClass
Definition RField.hxx:164
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
RClassField(std::string_view fieldName, const RClassField &source)
Used by CloneImpl.
void PrepareStagingArea(const std::vector< const TSchemaRule * > &rules, const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &classFieldId)
If there are rules with inputs (source members), create the staging area according to the TClass inst...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
~RClassField() override
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:153
void SetStagingClass(const std::string &className, unsigned int classVersion)
Sets fStagingClass according to the given name and version.
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:289
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
REnumField(std::string_view fieldName, TEnum *enump)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Field specific extra type information from the header / extenstion header.
The list of column representations a field can have.
A functor to release the memory acquired by CreateValue() (memory and constructor).
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
void Attach(std::unique_ptr< RFieldBase > child, std::string_view expectedChildName="")
Add a new subfield to the list of nested fields.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitTriviallyConstructible
No constructor needs to be called, i.e.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
const std::string & GetTypeName() const
RValue BindValue(std::shared_ptr< void > objPtr)
Creates a value from a memory location with an already constructed object.
Metadata stored for every field of an RNTuple.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:58
std::vector< std::unique_ptr< RFieldBase > > ReleaseSubfields()
Moves all subfields into the returned vector.
Definition RField.cxx:64
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:319
RField(std::string_view name)
Definition RField.hxx:322
RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr< RFieldBase > itemField)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
The on-storage metadata of an RNTuple.
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
Template specializations for C++ std::pair.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RPairField(std::string_view fieldName, std::array< std::unique_ptr< RFieldBase >, 2 > itemFields)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Allows for iterating over the elements of a proxied collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
RProxiedCollectionDeleter(std::shared_ptr< TVirtualCollectionProxy > proxy)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class representing a collection of elements via TVirtualCollectionProxy.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
RProxiedCollectionField(std::string_view fieldName, TClass *classp)
Constructor used when the value type of the collection is not known in advance, i....
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
std::shared_ptr< TVirtualCollectionProxy > fProxy
The collection proxy is needed by the deleters and thus defined as a shared pointer.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) override
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
Template specializations for ROOT's RVec.
static unsigned char * ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize, const RFieldBase *itemField, RDeleter *itemDeleter)
const_iterator begin() const
const_iterator end() const
The field for an untyped record.
void AttachItemFields(ContainerT &&itemFields)
std::vector< std::size_t > fOffsets
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr< RFieldBase > itemField)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class using ROOT standard streaming.
Definition RField.hxx:234
ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t fStreamerInfos
streamer info records seen during writing
Definition RField.hxx:246
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RStreamerField(std::string_view fieldName, TClass *classp)
size_t GetAlignment() const final
What alignof(T) for this type returns.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
size_t GetValueSize() const final
What sizeof(T) for this type returns.
Template specializations for C++ std::tuple.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > itemFields)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void operator()(void *objPtr, bool dtorOnly) final
Template specializations for C++ std::variant.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
static constexpr std::size_t kMaxVariants
std::vector< ROOT::Internal::RColumnIndex::ValueType > fNWritten
static std::uint8_t GetTag(const void *variantPtr, std::size_t tagOffset)
Extracts the index from an std::variant and transforms it into the 1-based index used for the switch ...
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
size_t fVariantOffset
In the std::variant memory layout, the actual union of types may start at an offset > 0.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
size_t fTagOffset
In the std::variant memory layout, at which byte number is the index stored.
RVariantField(std::string_view name, const RVariantField &source)
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
void CommitClusterImpl() final
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition TBufferFile.h:47
@ kWrite
Definition TBuffer.h:73
@ kRead
Definition TBuffer.h:73
char * Buffer() const
Definition TBuffer.h:96
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
UInt_t GetCheckSum(ECheckSum code=kCurrentCheckSum) const
Call GetCheckSum with validity check.
Definition TClass.cxx:6616
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2326
EState GetState() const
Definition TClass.h:504
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition TClass.cxx:5470
size_t GetClassAlignment() const
Return the alignment requirement (in bytes) for objects of this class.
Definition TClass.cxx:5781
void BuildRealData(void *pointer=nullptr, Bool_t isTransient=kFALSE)
Build a full list of persistent data members.
Definition TClass.cxx:2038
const std::type_info * GetTypeInfo() const
Definition TClass.h:512
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3828
TList * GetListOfRealData() const
Definition TClass.h:468
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5806
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3694
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2918
Int_t GetClassSize() const
Definition TClass.h:439
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2403
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6191
@ kInterpreted
Definition TClass.h:129
Version_t GetClassVersion() const
Definition TClass.h:434
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:2994
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
The TEnum class implements the enum type.
Definition TEnum.h:33
static TEnum * GetEnum(const std::type_info &ti, ESearchAction sa=kALoadAndInterpLookup)
Definition TEnum.cxx:181
TObject * First() const override
Return the first object in the list. Returns 0 when list is empty.
Definition TList.cxx:789
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Mother of all ROOT objects.
Definition TObject.h:42
@ kIsOnHeap
object is on heap
Definition TObject.h:90
@ kNotDeleted
object has not been deleted
Definition TObject.h:91
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:74
The TRealData class manages the effective list of all data members for a given class.
Definition TRealData.h:30
RAII helper class that ensures that PushProxy() / PopProxy() are called when entering / leaving a C++...
Defines a common interface to inspect/change the contents of an object that represents a collection.
@ kNeedDelete
The collection contains directly or indirectly (via other collection) some pointers that need explici...
Abstract Interface class describing Streamer information for one class.
const Int_t n
Definition legend1.C:16
TRangeCast< T, false > TRangeStaticCast
TRangeStaticCast is an adapter class that allows the typed iteration through a TCollection.
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:35
std::tuple< unsigned char **, std::int32_t *, std::int32_t * > GetRVecDataMembers(void *rvecPtr)
Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the R...
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
std::string GetRNTupleSoARecord(const TClass *cl)
Checks if the "rntuple.SoARecord" class attribute is set in the dictionary.
bool NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias, bool isArgInTemplatedUserClass=false)
Checks if the meta normalized name is different from the RNTuple normalized name in a way that would ...
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
ERNTupleSerializationMode GetRNTupleSerializationMode(const TClass *cl)
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
constexpr bool IsValidAlignment(std::size_t align) noexcept
Return true if align is a valid C++ alignment value: strictly positive and a power of two.
Definition BitUtils.hxx:36
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
@ kSTLvector
Definition ESTLType.h:30
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.