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 cannot 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 const RSoAField &nestedSoA, std::size_t offsetInParent,
691 std::function<RFieldBase *(const std::string &)> fnRecordFieldFinder)
692{
693 const std::size_t nNestedRecordMemberFields = nestedSoA.fRecordMemberFields.size();
694
695 // The qualified field name of fields in nestedSoA->fRecordMemberFields will have a "<field name>._0."
696 // prefix because these fields are rooted in a collection named after the nested SoA field.
697 //
698 // E.g., in the following example:
699 //
700 // struct SoA_A { struct Record_A {
701 // SoA_B fB; Record_B fB;
702 // }; };
703 //
704 // struct SoA_B { struct Record_B {
705 // ROOT::RVec<float> fX; float fX;
706 // }; };
707 //
708 // The on-disk schema of SoA_A is "collection of Record_A", and the on-disk schema of SoA_B is
709 // "collection of Record_B".
710 // The qualified field name of fX in the subfield hiararchy of SoA_A is <field name>._0.fB.fX.
711 // The qualified field name of fX in the subfield hiararchy of SoA_B is <field name>._0.fX.
712 const auto lenPrefix = nestedSoA.GetFieldName().length() + strlen("._0.");
713
714 for (std::size_t i = 0; i < nNestedRecordMemberFields; ++i) {
715 const auto fieldNameForMatching =
716 nestedSoA.GetFieldName() + "." + nestedSoA.fRecordMemberFields[i]->GetQualifiedFieldName().substr(lenPrefix);
717
718 fRecordMemberFields.emplace_back(fnRecordFieldFinder(fieldNameForMatching));
719 fRecordMemberDeleters.emplace_back(GetDeleterOf(*nestedSoA.fRecordMemberFields[i]));
720 fSoAMemberOffsets.emplace_back(offsetInParent + nestedSoA.fSoAMemberOffsets[i]);
721 }
722}
723
725{
726 // Build a map of all subfields (nested) of the underlying record type. Map the fully qualified name of the
727 // subfields to their field pointer, so that we can later match the subfields of the SoA class to their corresponding
728 // fields in the underlying record type. Note that the members of the SoA class and the underlying record type
729 // can have different ordering. However, the base classes of the SoA class and the underlying record type must match
730 // in order.
731
732 std::vector<RFieldBase *> realRecordMemberFields; // Contains all subfields of the underlying record type
733 // Qualified field name --> index in realRecordMemberFields
734 std::unordered_map<std::string, std::size_t> recordFieldNameToIdx;
735
736 // Count the top-level subfields of the underlying record type for cross-check with the SoA type
737 unsigned int nDirectRecordSubfields = 0;
738 unsigned int nDirectRecordBases = 0;
739
740 for (auto itr = fSubfields[0]->begin(), iEnd = fSubfields[0]->end(); itr != iEnd; ++itr) {
741 if (itr->GetParent() == fSubfields[0].get()) {
742 if (itr->GetFieldName()[0] == ':') {
744 } else {
746 }
747 }
748
749 // Build the qualified field name for matching. We root the qualified field name at the underlying record type.
750 auto qualifiedName = itr->GetFieldName();
751 auto parent = itr->GetParent();
752 while (parent != fSubfields[0].get()) {
753 qualifiedName = parent->GetFieldName() + "." + qualifiedName;
754 parent = parent->GetParent();
755 }
757
758 realRecordMemberFields.emplace_back(&(*itr));
759 }
760
761 // Base classes are treated as unrolled nested SoA classes
762 const auto *soaBases = fSoAClass->GetListOfBases();
763 if (soaBases->GetSize() != static_cast<Int_t>(nDirectRecordBases)) {
764 throw RException(R__FAIL(std::string("number of base classes don't match between SoA class ") + GetFieldName() +
765 " and its underlying record type"));
766 }
767 unsigned int baseIdx = 0;
768 for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(*fSoAClass->GetListOfBases())) {
769 if (base->GetDelta() < 0) {
770 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
771 " virtually inherits from " + base->GetName()));
772 }
773 TClass *cl = base->GetClassPointer();
774
775 const auto baseFieldName = std::string(":_") + std::to_string(baseIdx);
776
777 // SoA class `A` is allowed to inherit from a SoA class `B` whose underlying record type is `X` if and only if
778 // the underlying record type of `A` inherits from a type `X`.
781 if (underlyingBaseTypeName != recordBaseField->GetTypeName()) {
782 throw RException(R__FAIL(std::string("inheritance of SoA class ") + GetFieldName() +
783 " does not match its underlying record type"));
784 }
785
786 std::unique_ptr<RSoAField> soaBaseField;
787 try {
788 soaBaseField = std::make_unique<RSoAField>(baseFieldName, cl->GetName());
789 } catch (const RException &e) {
790 throw RException(R__FAIL(std::string("invalid field type in base class: ") + cl->GetName() + " of SoA field " +
791 GetFieldName() + " (" + e.what() + ")"));
792 }
793
794 GraftNestedMemberFields(*soaBaseField, base->GetDelta(), [&](const std::string &name) {
795 return realRecordMemberFields[recordFieldNameToIdx[name]];
796 });
797
798 baseIdx++;
799 }
800
801 unsigned int nMembers = 0;
802 for (auto dataMember : ROOT::Detail::TRangeStaticCast<TDataMember>(*fSoAClass->GetListOfDataMembers())) {
803 // NOTE: ReconstructSplitFields() will also traverse the data members and need to apply the same rules for
804 // skipping members
805
806 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
807 continue;
808
809 if (dataMember->Property() & kIsArray) {
810 throw RException(R__FAIL(std::string("unsupported array type in SoA class: ") + dataMember->GetName()));
811 }
812
813 const std::string typeName{dataMember->GetTrueTypeName()};
814 auto dmField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
815
816 auto itr = recordFieldNameToIdx.find(dmField->GetFieldName());
817 if (itr == recordFieldNameToIdx.end()) {
818 throw RException(R__FAIL(std::string("unexpected SoA member: ") + dmField->GetFieldName()));
819 }
821 assert(dmField->GetFieldName() == underlyingField->GetFieldName());
822
823 if (auto soaField = dynamic_cast<RSoAField *>(dmField.get())) {
824 if (ROOT::Internal::GetRNTupleSoARecord(soaField->fSoAClass) != underlyingField->GetTypeName()) {
825 throw RException(R__FAIL(std::string("nested SoA field ") + soaField->GetQualifiedFieldName() + " [" +
826 soaField->GetTypeName() + "] does not match underlying type " +
827 underlyingField->GetTypeName()));
828 }
829
830 GraftNestedMemberFields(*soaField, dataMember->GetOffset(), [&](const std::string &name) {
831 return realRecordMemberFields[recordFieldNameToIdx[name]];
832 });
833 } else if (auto vecField = dynamic_cast<RRVecField *>(dmField.get())) {
834 if (vecField->begin()->GetTypeName() != underlyingField->GetTypeName() ||
835 vecField->begin()->GetTypeAlias() != underlyingField->GetTypeAlias()) {
836 const std::string leftType =
837 vecField->begin()->GetTypeName() +
838 (vecField->begin()->GetTypeAlias().empty() ? "" : " [" + vecField->begin()->GetTypeAlias() + "]");
839 const std::string rightType =
840 underlyingField->GetTypeName() +
841 (underlyingField->GetTypeAlias().empty() ? "" : " [" + underlyingField->GetTypeAlias() + "]");
842 throw RException(R__FAIL(std::string("SoA member type mismatch: ") + vecField->GetFieldName() + " (" +
843 leftType + " vs. " + rightType + ")"));
844 }
845
846 fRecordMemberFields.emplace_back(underlyingField);
847 fRecordMemberDeleters.emplace_back(GetDeleterOf(*underlyingField));
848 fSoAMemberOffsets.emplace_back(dataMember->GetOffset());
849 } else {
850 throw RException(R__FAIL("invalid field type in SoA class: " + dmField->GetTypeName()));
851 }
852
853 nMembers++;
854 }
856 throw RException(R__FAIL("missing SoA members"));
857 }
858}
859
861 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(clSoA->GetName()), ROOT::ENTupleStructure::kCollection,
862 false /* isSimple */),
863 fSoAClass(clSoA)
864{
865 static std::once_flag once;
866 std::call_once(once, []() {
867 R__LOG_WARNING(ROOT::Internal::NTupleLog()) << "The SoA field is experimental and still under development.";
868 });
869
870 EnsureValidUserClass(fSoAClass, *this, "RSoAField");
872 if (recordTypeName.empty()) {
873 throw ROOT::RException(R__FAIL(std::string("class ") + GetTypeName() +
874 " is not marked with the rntupleSoARecord "
875 "dictionary option; cannot create corresponding RSoAField."));
876 }
877 try {
878 Attach(std::make_unique<ROOT::RClassField>("_0", recordTypeName));
879 } catch (ROOT::RException &e) {
880 throw RException(R__FAIL("invalid record type of SoA field " + GetTypeName() + " [" + e.what() + "]"));
881 }
883 if (static_cast<std::uint32_t>(fSoAClass->GetClassVersion()) != fSubfields[0]->GetTypeVersion()) {
884 throw RException(R__FAIL(std::string("version mismatch between SoA type and underlying record type: ") +
885 std::to_string(fSoAClass->GetClassVersion()) + " vs. " +
886 std::to_string(fSubfields[0]->GetTypeVersion())));
887 }
888
890
891 std::string renormalizedAlias;
894
896 fLockSplitFields = std::make_unique<std::mutex>();
897}
898
899std::unique_ptr<ROOT::RFieldBase> ROOT::Experimental::RSoAField::CloneImpl(std::string_view newName) const
900{
901 return std::unique_ptr<RSoAField>(new RSoAField(newName, *this));
902}
903
913
918
923
924std::size_t ROOT::Experimental::RSoAField::AppendImpl(const void *from)
925{
926 const std::size_t nSoAMembers = fSoAMemberOffsets.size();
927
928 std::size_t N = 0; // Set by first SoA member and verified for the rest
929 for (std::size_t i = 0; i < nSoAMembers; ++i) {
930 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
932 assert(*sizePtr >= 0);
933 if (i == 0) {
934 N = *sizePtr;
935 } else {
936 if (static_cast<std::size_t>(*sizePtr) != N) {
937 const auto f = fRecordMemberFields[i];
938 throw RException(R__FAIL("SoA length mismatch for " + f->GetFieldName() + ": " + std::to_string(*sizePtr) +
939 " vs. " + std::to_string(N) + " (expected)"));
940 }
941 }
942 }
943
944 std::size_t nbytes = 0;
945 if (N > 0) {
946 for (std::size_t i = 0; i < nSoAMembers; ++i) {
947 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
949 RFieldBase *memberField = fRecordMemberFields[i];
950 if (memberField->IsSimple()) {
951 GetPrincipalColumnOf(*memberField)->AppendV(*beginPtr, N);
952 nbytes += N * GetPrincipalColumnOf(*memberField)->GetElement()->GetPackedSize();
953 } else {
954 for (std::size_t j = 0; j < N; ++j) {
955 nbytes += CallAppendOn(*memberField, *beginPtr + j * memberField->GetValueSize());
956 }
957 }
958 }
959 }
960
961 fNWritten += N;
962 fPrincipalColumn->Append(&fNWritten);
963 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
964}
965
967{
968 // Read collection info for this entry
971 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &N);
972
973 const auto nSoAMembers = fSoAMemberOffsets.size();
974 for (std::size_t i = 0; i < nSoAMembers; ++i) {
975 RFieldBase *memberField = fRecordMemberFields[i];
976 const auto memberSize = memberField->GetValueSize();
977 void *rvecPtr = static_cast<unsigned char *>(to) + fSoAMemberOffsets[i];
978 auto begin = ROOT::RRVecField::ResizeRVec(rvecPtr, N, memberSize, memberField, fRecordMemberDeleters[i].get());
979
980 if (memberField->IsSimple() && N) {
981 GetPrincipalColumnOf(*memberField)->ReadV(collectionStart, N, begin);
982 } else {
983 for (std::size_t j = 0; j < N; ++j) {
984 CallReadOn(*memberField, collectionStart + j, begin + (j * memberSize));
985 }
986 }
987 }
988}
989
991{
992 fSoAClass->New(where);
993}
994
995ROOT::Experimental::RSoAField::RSoADeleter::RSoADeleter(TClass *cl) : RDeleter(cl->GetClassAlignment()), fSoAClass(cl)
996{
997}
998
1000{
1001 fSoAClass->Destructor(objPtr, true /* dtorOnly */);
1002 RDeleter::operator()(objPtr, dtorOnly);
1003}
1004
1006{
1007 std::lock_guard<std::mutex> lockGuard(*fLockSplitFields);
1008 if (fSplitFields)
1009 return;
1010
1011 fSplitFields = std::make_unique<std::vector<std::unique_ptr<ROOT::RFieldBase>>>();
1012 fSplitOffsets = std::make_unique<std::vector<std::size_t>>();
1013
1014 unsigned int baseIdx = 0;
1016 TClass *cl = base->GetClassPointer();
1017 auto baseField = RFieldBase::Create(std::string(":_" + std::to_string(baseIdx)), cl->GetName()).Unwrap();
1018 fSplitFields->emplace_back(std::move(baseField));
1019 fSplitOffsets->emplace_back(base->GetDelta());
1020 baseIdx++;
1021 }
1022
1024 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
1025 continue;
1026
1027 const std::string typeName{dataMember->GetTrueTypeName()};
1028 auto dmField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
1029 fSplitFields->emplace_back(std::move(dmField));
1030 fSplitOffsets->emplace_back(dataMember->GetOffset());
1031 }
1032}
1033
1034std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(const RValue &value) const
1035{
1037 const auto nSplitFields = fSplitFields->size();
1038
1039 auto valuePtr = value.GetPtr<void>();
1040 auto soaPtr = static_cast<unsigned char *>(valuePtr.get());
1041 std::vector<RValue> values;
1042 values.reserve(nSplitFields);
1043 for (std::size_t i = 0; i < nSplitFields; ++i) {
1044 values.emplace_back((*fSplitFields)[i]->BindValue(std::shared_ptr<void>(valuePtr, soaPtr + (*fSplitOffsets)[i])));
1045 }
1046 return values;
1047}
1048
1050{
1051 return fSoAClass->GetClassSize();
1052}
1053
1055{
1056 return fSoAClass->GetClassVersion();
1057}
1058
1060{
1061 return fSoAClass->GetCheckSum();
1062}
1063
1065{
1066 const auto align = fSoAClass->GetClassAlignment();
1067 EnsureValidAlignment(align);
1068 return align;
1069}
1070
1072{
1073 // TODO(jblomer): factor out
1075 if (!polymorphic) {
1076 return nullptr;
1077 }
1078 return fSoAClass->GetTypeInfo();
1079}
1080
1085
1086//------------------------------------------------------------------------------
1087
1088ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
1090{
1091}
1092
1094 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
1095 false /* isSimple */)
1096{
1097 // Avoid accidentally supporting std types through TEnum.
1098 if (enump->Property() & kIsDefinedInStd) {
1099 throw RException(R__FAIL(GetTypeName() + " is not supported"));
1100 }
1101
1102 switch (enump->GetUnderlyingType()) {
1103 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
1104 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
1105 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
1106 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
1107 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
1108 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
1109 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
1110 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
1111 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
1112 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
1113 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
1114 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
1115 }
1116
1118}
1119
1120ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
1121 std::unique_ptr<RFieldBase> intField)
1123{
1124 Attach(std::move(intField));
1126}
1127
1128std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
1129{
1130 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
1131 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
1132}
1133
1135{
1136 // TODO(jblomer): allow enum to enum conversion only by rename rule
1137 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1138}
1139
1140std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
1141{
1142 std::vector<RValue> result;
1143 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
1144 return result;
1145}
1146
1148{
1149 visitor.VisitEnumField(*this);
1150}
1151
1152//------------------------------------------------------------------------------
1153
1154ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
1155 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1156{
1157 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1158 if (typeAlias != GetTypeName())
1160
1161 AttachItemFields(std::move(itemFields));
1162
1163 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
1164 auto *c = TClass::GetClass(GetTypeName().c_str());
1165 if (!c)
1166 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1167 fSize = c->Size();
1168
1169 auto firstElem = c->GetRealData("first");
1170 if (!firstElem)
1171 throw RException(R__FAIL("first: no such member"));
1172 fOffsets.push_back(firstElem->GetThisOffset());
1173
1174 auto secondElem = c->GetRealData("second");
1175 if (!secondElem)
1176 throw RException(R__FAIL("second: no such member"));
1177 fOffsets.push_back(secondElem->GetThisOffset());
1178}
1179
1180std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
1181{
1182 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
1183 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
1184 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones)));
1185}
1186
1188{
1189 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1190
1191 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1192 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1193
1194 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1195 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1196 if (nOnDiskSubfields != 2) {
1197 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
1198 std::to_string(nOnDiskSubfields) + "\n" +
1199 Internal::GetTypeTraceReport(*this, desc)));
1200 }
1201}
1202
1203//------------------------------------------------------------------------------
1204
1207 bool readFromDisk)
1208{
1210 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
1211 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
1212 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
1213 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
1214 (ifuncs.fNext != nullptr));
1215 return ifuncs;
1216}
1217
1219 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
1220 false /* isSimple */),
1221 fNWritten(0)
1222{
1223 if (!classp->GetCollectionProxy())
1224 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
1225 if (classp->Property() & kIsDefinedInStd) {
1226 static const std::vector<std::string> supportedStdTypes = {
1227 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
1228 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
1229 bool isSupported = false;
1230 for (const auto &tn : supportedStdTypes) {
1231 if (GetTypeName().rfind(tn, 0) == 0) {
1232 isSupported = true;
1233 break;
1234 }
1235 }
1236 if (!isSupported)
1237 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
1238 }
1239
1240 std::string renormalizedAlias;
1243
1244 fProxy.reset(classp->GetCollectionProxy()->Generate());
1245 fProperties = fProxy->GetProperties();
1246 fCollectionType = fProxy->GetCollectionType();
1247 if (fProxy->HasPointers())
1248 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
1249
1250 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
1251 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1252}
1253
1254ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
1256{
1257 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
1259 throw RException(R__FAIL("custom associative collection proxies not supported"));
1260
1261 std::unique_ptr<ROOT::RFieldBase> itemField;
1262
1263 if (auto valueClass = fProxy->GetValueClass()) {
1264 // Element type is a class
1265 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
1266 } else {
1267 switch (fProxy->GetType()) {
1268 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
1269 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
1270 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
1271 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
1272 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
1273 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
1274 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
1275 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
1276 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
1277 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
1278 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
1279 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
1280 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
1281 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
1282 }
1283 }
1284
1285 fItemSize = itemField->GetValueSize();
1286 Attach(std::move(itemField));
1287}
1288
1289std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
1290{
1291 auto clone =
1292 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
1293 clone->fItemSize = fItemSize;
1294 clone->Attach(fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1295 return clone;
1296}
1297
1299{
1300 std::size_t nbytes = 0;
1301 unsigned count = 0;
1302 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
1303 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
1304 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1305 nbytes += CallAppendOn(*fSubfields[0], ptr);
1306 count++;
1307 }
1308
1309 fNWritten += count;
1310 fPrincipalColumn->Append(&fNWritten);
1311 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1312}
1313
1315{
1318 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1319
1320 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
1321 void *obj =
1322 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
1323
1324 unsigned i = 0;
1325 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
1326 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
1327 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
1328 }
1329 if (obj != to)
1330 fProxy->Commit(obj);
1331}
1332
1342
1347
1352
1354{
1355 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1356}
1357
1359{
1360 fProxy->New(where);
1361}
1362
1363std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
1364{
1365 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
1366 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
1367 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
1368 }
1369 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
1370}
1371
1373 std::shared_ptr<TVirtualCollectionProxy> proxy)
1374 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()), fProxy(std::move(proxy))
1375{
1376}
1377
1379 std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter, size_t itemSize)
1380 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()),
1381 fProxy(std::move(proxy)),
1382 fItemDeleter(std::move(itemDeleter)),
1384{
1385 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1386}
1387
1389{
1390 if (fItemDeleter) {
1392 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
1393 fItemDeleter->operator()(ptr, true /* dtorOnly */);
1394 }
1395 }
1396 fProxy->Destructor(objPtr, true /* dtorOnly */);
1397 RDeleter::operator()(objPtr, dtorOnly);
1398}
1399
1400std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
1401{
1402 std::vector<RValue> result;
1403 auto valueRawPtr = value.GetPtr<void>().get();
1406 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1407 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
1408 }
1409 return result;
1410}
1411
1413{
1414 return fProxy->Sizeof();
1415}
1416
1418{
1419 const auto align = fProxy->GetCollectionClass()->GetClassAlignment();
1420 EnsureValidAlignment(align);
1421 return align;
1422}
1423
1425{
1426 visitor.VisitProxiedCollectionField(*this);
1427}
1428
1429//------------------------------------------------------------------------------
1430
1431ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
1433 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
1434 fMapType(mapType)
1435{
1436 if (!itemField->GetTypeAlias().empty())
1437 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
1438
1439 auto *itemClass = fProxy->GetValueClass();
1440 fItemSize = itemClass->GetClassSize();
1441
1442 Attach(std::move(itemField), "_0");
1443}
1444
1445std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
1446{
1447 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1448}
1449
1451{
1452 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
1453
1454 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1455
1456 switch (fMapType) {
1457 case EMapType::kMap:
1458 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1459 default:
1460 break;
1461 // no restrictions for multimaps
1462 }
1463}
1464
1465//------------------------------------------------------------------------------
1466
1467ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1469 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1470 fSetType(setType)
1471{
1472 if (!itemField->GetTypeAlias().empty())
1473 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1474
1475 fItemSize = itemField->GetValueSize();
1476
1477 Attach(std::move(itemField), "_0");
1478}
1479
1480std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1481{
1482 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1483}
1484
1486{
1487 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1488 "std::unordered_map<"};
1489
1490 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1491
1492 switch (fSetType) {
1493 case ESetType::kSet:
1494 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1495 default:
1496 break;
1497 // no restrictions for multisets
1498 }
1499}
1500
1501//------------------------------------------------------------------------------
1502
1503namespace {
1504
1505/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1506class TBufferRecStreamer : public TBufferFile {
1507public:
1508 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1509
1510private:
1511 RCallbackStreamerInfo fCallbackStreamerInfo;
1512
1513public:
1514 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1515 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(std::move(callbackStreamerInfo))
1516 {
1517 }
1518 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1519};
1520
1521} // anonymous namespace
1522
1523ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1525{
1526}
1527
1529 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1530 false /* isSimple */),
1531 fClass(classp),
1532 fIndex(0)
1533{
1534 std::string renormalizedAlias;
1537
1539 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1540 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1541 // implicit constructors and destructors.
1546}
1547
1548std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1549{
1550 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1551}
1552
1553std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1554{
1555 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1556 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1557 fClass->Streamer(const_cast<void *>(from), buffer);
1558
1559 auto nbytes = buffer.Length();
1560 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1561 fIndex += nbytes;
1562 fPrincipalColumn->Append(&fIndex);
1563 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1564}
1565
1567{
1570 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1571
1573 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1574 fClass->Streamer(to, buffer);
1575}
1576
1586
1591
1596
1598{
1599 source.RegisterStreamerInfos();
1600 return nullptr;
1601}
1602
1604{
1605 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1606}
1607
1609{
1610 fClass->New(where);
1611}
1612
1617
1619{
1620 fClass->Destructor(objPtr, true /* dtorOnly */);
1621 RDeleter::operator()(objPtr, dtorOnly);
1622}
1623
1633
1635{
1636 const auto align = fClass->GetClassAlignment();
1637 EnsureValidAlignment(align);
1638 return align;
1639}
1640
1642{
1643 return fClass->GetClassSize();
1644}
1645
1647{
1648 return fClass->GetClassVersion();
1649}
1650
1652{
1653 return fClass->GetCheckSum();
1654}
1655
1657{
1658 visitor.VisitStreamerField(*this);
1659}
1660
1661//------------------------------------------------------------------------------
1662
1664{
1665 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1666 return dataMember->GetOffset();
1667 }
1668 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1669}
1670
1672 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1673{
1675 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1676 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1677}
1678
1680 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1681{
1682 assert(TObject::Class()->GetClassVersion() == 1);
1683
1685 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1686 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1687}
1688
1689std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1690{
1691 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1692}
1693
1694std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1695{
1696 // Cf. TObject::Streamer()
1697
1698 auto *obj = static_cast<const TObject *>(from);
1699 if (obj->TestBit(TObject::kIsReferenced)) {
1700 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1701 }
1702
1703 std::size_t nbytes = 0;
1704 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1705
1706 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1707 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1708 nbytes += CallAppendOn(*fSubfields[1], &bits);
1709
1710 return nbytes;
1711}
1712
1714{
1715 // Cf. TObject::Streamer()
1716
1717 auto *obj = static_cast<TObject *>(to);
1718 if (obj->TestBit(TObject::kIsReferenced)) {
1719 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1720 }
1721
1722 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1723
1724 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1726 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1727}
1728
1730{
1731 UInt_t uniqueID, bits;
1732 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1733 CallReadOn(*fSubfields[1], globalIndex, &bits);
1734 ReadTObject(to, uniqueID, bits);
1735}
1736
1738{
1739 UInt_t uniqueID, bits;
1740 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1741 CallReadOn(*fSubfields[1], localIndex, &bits);
1742 ReadTObject(to, uniqueID, bits);
1743}
1744
1746{
1747 return TObject::Class()->GetClassVersion();
1748}
1749
1751{
1752 return TObject::Class()->GetCheckSum();
1753}
1754
1756{
1757 new (where) TObject();
1758}
1759
1760std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1761{
1762 std::vector<RValue> result;
1763 // Use GetPtr<TObject> to type-check
1764 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1765 auto charPtr = static_cast<unsigned char *>(ptr.get());
1766 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1767 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1768 return result;
1769}
1770
1772{
1773 return sizeof(TObject);
1774}
1775
1777{
1778 return alignof(TObject);
1779}
1780
1782{
1783 visitor.VisitTObjectField(*this);
1784}
1785
1786//------------------------------------------------------------------------------
1787
1788ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1789 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1790{
1791 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1792 if (typeAlias != GetTypeName())
1794
1795 AttachItemFields(std::move(itemFields));
1796
1797 auto *c = TClass::GetClass(GetTypeName().c_str());
1798 if (!c)
1799 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1800 fSize = c->Size();
1801
1802 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1803 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1804 // following the order of the type list.
1805 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1806 // members, the assertion below will fail.
1807 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1808 std::string memberName("_" + std::to_string(i));
1809 auto member = c->GetRealData(memberName.c_str());
1810 if (!member)
1811 throw RException(R__FAIL(memberName + ": no such member"));
1812 fOffsets.push_back(member->GetThisOffset());
1813 }
1814}
1815
1816std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1817{
1818 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1819 itemClones.reserve(fSubfields.size());
1820 for (const auto &f : fSubfields) {
1821 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1822 }
1823 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones)));
1824}
1825
1827{
1828 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1829
1830 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1831 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1832
1833 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1834 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1835 const auto nSubfields = fSubfields.size();
1837 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1838 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1839 Internal::GetTypeTraceReport(*this, desc)));
1840 }
1841}
1842
1843//------------------------------------------------------------------------------
1844
1845namespace {
1846
1847// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1848constexpr std::size_t GetVariantTagSize()
1849{
1850 // Should be all zeros except for the tag, which is 1
1851 std::variant<char> t;
1852 constexpr auto sizeOfT = sizeof(t);
1853
1854 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1855 return sizeOfT == 2 ? 1 : 4;
1856}
1857
1858template <std::size_t VariantSizeT>
1859struct RVariantTag {
1860 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1861 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1862};
1863
1864} // anonymous namespace
1865
1867 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1868 fMaxItemSize(source.fMaxItemSize),
1869 fMaxAlignment(source.fMaxAlignment),
1870 fTagOffset(source.fTagOffset),
1871 fVariantOffset(source.fVariantOffset),
1872 fNWritten(source.fNWritten.size(), 0)
1873{
1874 for (const auto &f : source.GetConstSubfields())
1875 Attach(f->Clone(f->GetFieldName()));
1876 fTraits = source.fTraits;
1877}
1878
1879ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1880 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1881 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1882{
1883 // The variant needs to initialize its own tag member
1885
1886 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1887 if (typeAlias != GetTypeName())
1889
1890 auto nFields = itemFields.size();
1891 if (nFields == 0 || nFields > kMaxVariants) {
1892 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1893 }
1894 fNWritten.resize(nFields, 0);
1895 for (unsigned int i = 0; i < nFields; ++i) {
1898 fTraits &= itemFields[i]->GetTraits();
1899 Attach(std::move(itemFields[i]), "_" + std::to_string(i));
1900 }
1901
1902 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1903 // For instance, std::variant<std::optional<int>> on macOS.
1904 auto cl = TClass::GetClass(GetTypeName().c_str());
1905 assert(cl);
1906 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1907 if (dm)
1908 fVariantOffset = dm->GetOffset();
1909
1910 const auto tagSize = GetVariantTagSize();
1911 const auto padding = tagSize - (fMaxItemSize % tagSize);
1913}
1914
1915std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1916{
1917 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1918}
1919
1920std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1921{
1922 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1923 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1924 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1925}
1926
1927void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1928{
1929 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1930 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1931 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1932}
1933
1934std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1935{
1936 auto tag = GetTag(from, fTagOffset);
1937 std::size_t nbytes = 0;
1938 auto index = 0;
1939 if (tag > 0) {
1940 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1941 index = fNWritten[tag - 1]++;
1942 }
1944 fPrincipalColumn->Append(&varSwitch);
1945 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1946}
1947
1949{
1951 std::uint32_t tag;
1952 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1953 R__ASSERT(tag < 256);
1954
1955 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1956 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1957 // any `std::holds_alternative<T>` check fail later.
1958 if (R__likely(tag > 0)) {
1959 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1960 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1961 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1962 }
1963 SetTag(to, fTagOffset, tag);
1964}
1965
1971
1976
1981
1983{
1984 static const std::vector<std::string> prefixes = {"std::variant<"};
1985
1986 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1987 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1988
1989 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1990 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1991 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1992 Internal::GetTypeTraceReport(*this, desc)));
1993 }
1994}
1995
1997{
1998 memset(where, 0, GetValueSize());
1999 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
2000 SetTag(where, fTagOffset, 1);
2001}
2002
2004{
2005 auto tag = GetTag(objPtr, fTagOffset);
2006 if (tag > 0) {
2007 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
2008 }
2009 RDeleter::operator()(objPtr, dtorOnly);
2010}
2011
2012std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
2013{
2014 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
2015 itemDeleters.reserve(fSubfields.size());
2016 for (const auto &f : fSubfields) {
2017 itemDeleters.emplace_back(GetDeleterOf(*f));
2018 }
2019 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, GetAlignment(), std::move(itemDeleters));
2020}
2021
2023{
2024 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
2025}
2026
2028{
2029 const auto alignment = GetAlignment();
2030 const auto actualSize = fTagOffset + GetVariantTagSize();
2031 const auto padding = alignment - (actualSize % alignment);
2032 return actualSize + ((padding == alignment) ? 0 : padding);
2033}
2034
2036{
2037 std::fill(fNWritten.begin(), fNWritten.end(), 0);
2038}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:593
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:322
#define 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:56
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
void GraftNestedMemberFields(const RSoAField &nestedSoA, std::size_t offsetInParent, std::function< RFieldBase *(const std::string &)> fnRecordFieldFinder)
For a nested SoA struct (either as a member of as a base class), use their fRecordMemberFields in thi...
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:75
void CollectRecordMemberFields()
Called during construction, picks up the (nested) member fields of the underlying record type(s) and ...
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::unique_ptr< std::vector< std::size_t > > fSplitOffsets
Definition RFieldSoA.hxx:86
std::vector< RFieldBase * > fRecordMemberFields
Direct access to the member fields of the underlying record.
Definition RFieldSoA.hxx:71
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.
std::unique_ptr< std::vector< std::unique_ptr< ROOT::RFieldBase > > > fSplitFields
For reading and writing, the RVecs of the SoA class do not have a dedicated field.
Definition RFieldSoA.hxx:85
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::mutex > fLockSplitFields
protects the fSplitFields member.
Definition RFieldSoA.hxx:87
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.