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{
400 auto idSourceMember = desc.FindFieldId(memberName, classFieldId);
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,
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();
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.
541 std::string descTypeName = desc.GetTypeNameForComparison(fieldDesc);
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 // For unversioned classes, the in-memory layout may by chance have the same transient version number
556 // than the recorded (transient, at the time of writing) on-disk version. Therefore, we also need to compare
557 // the checksums to find out if we need a conversion streamer info.
558 std::uint32_t assignedVersionForOnDiskLayout = fieldDesc.GetTypeVersion();
559 R__ASSERT(fieldDesc.GetTypeChecksum());
560 if (fieldDesc.GetTypeVersion() != GetTypeVersion() || *fieldDesc.GetTypeChecksum() != fClass->GetCheckSum() ||
561 fieldDesc.GetTypeName() != GetTypeName()) {
562 auto oldCl = TClass::GetClass(fieldDesc.GetTypeName().c_str());
564 auto onDiskStreamerInfo = oldCl->FindStreamerInfo(*fieldDesc.GetTypeChecksum());
567 }
568 SetStagingClass(fieldDesc.GetTypeName(), assignedVersionForOnDiskLayout);
569 PrepareStagingArea(rules, desc, fieldDesc);
570 for (auto &[_, si] : fStagingItems) {
572 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
573 }
574 }
575
576 // Remove target member of read rules from the list of regular members of the underlying on-disk field
577 for (const auto rule : rules) {
578 if (!rule->GetTarget())
579 continue;
580
581 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
582 regularSubfields.erase(std::string(target->GetString()));
583 }
584 }
585 }
586
587 for (const auto rule : rules) {
588 AddReadCallbacksFromIORule(rule);
589 }
590
591 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
592 int nInMemoryBaseClasses = 0;
593 for (auto &field : fSubfields) {
594 const auto &fieldName = field->GetFieldName();
595 if (regularSubfields.count(fieldName) == 0) {
596 CallSetArtificialOn(*field);
597 }
598 if (!fieldName.empty() && fieldName[0] == ':')
600 }
601
603 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
604 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
605 " base classes in memory "
606 " vs. " +
607 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
608 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
609 }
610
611 return nullptr;
612}
613
615{
616 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
617}
618
620{
621 fClass->New(where);
622}
623
625
627{
628 fClass->Destructor(objPtr, true /* dtorOnly */);
629 RDeleter::operator()(objPtr, dtorOnly);
630}
631
632std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
633{
634 std::vector<RValue> result;
635 auto valuePtr = value.GetPtr<void>();
636 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
637 result.reserve(fSubfields.size());
638 for (unsigned i = 0; i < fSubfields.size(); i++) {
639 result.emplace_back(
640 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
641 }
642 return result;
643}
644
646{
647 return fClass->GetClassSize();
648}
649
651{
652 const auto align = fClass->GetClassAlignment();
654 return align;
655}
656
658{
659 return fClass->GetClassVersion();
660}
661
663{
664 return fClass->GetCheckSum();
665}
666
667const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
668{
670 if (!polymorphic) {
671 return nullptr;
672 }
673 return fClass->GetTypeInfo();
674}
675
677{
678 visitor.VisitClassField(*this);
679}
680
681//------------------------------------------------------------------------------
682
685 fSoAClass(source.fSoAClass),
686 fSoAMemberOffsets(source.fSoAMemberOffsets)
687{
688 fTraits = source.GetTraits();
689 Attach(source.fSubfields[0]->Clone(source.fSubfields[0]->GetFieldName()));
690 fRecordMemberFields = fSubfields[0]->GetMutableSubfields();
692 for (const auto f : fRecordMemberFields)
693 fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
694 fLockSplitFields = std::make_unique<std::mutex>();
695}
696
697ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, std::string_view className)
699{
700}
701
703 const RSoAField &nestedSoA, std::size_t offsetInParent,
704 const std::function<RFieldBase *(const std::string &)> &fnRecordFieldFinder)
705{
706 const std::size_t nNestedRecordMemberFields = nestedSoA.fRecordMemberFields.size();
707
708 // The qualified field name of fields in nestedSoA->fRecordMemberFields will have a "<field name>._0."
709 // prefix because these fields are rooted in a collection named after the nested SoA field.
710 //
711 // E.g., in the following example:
712 //
713 // struct SoA_A { struct Record_A {
714 // SoA_B fB; Record_B fB;
715 // }; };
716 //
717 // struct SoA_B { struct Record_B {
718 // ROOT::RVec<float> fX; float fX;
719 // }; };
720 //
721 // The on-disk schema of SoA_A is "collection of Record_A", and the on-disk schema of SoA_B is
722 // "collection of Record_B".
723 // The qualified field name of fX in the subfield hiararchy of SoA_A is <field name>._0.fB.fX.
724 // The qualified field name of fX in the subfield hiararchy of SoA_B is <field name>._0.fX.
725 const auto lenPrefix = nestedSoA.GetFieldName().length() + strlen("._0.");
726
727 for (std::size_t i = 0; i < nNestedRecordMemberFields; ++i) {
728 const auto fieldNameForMatching =
729 nestedSoA.GetFieldName() + "." + nestedSoA.fRecordMemberFields[i]->GetQualifiedFieldName().substr(lenPrefix);
730
731 fRecordMemberFields.emplace_back(fnRecordFieldFinder(fieldNameForMatching));
732 fRecordMemberDeleters.emplace_back(GetDeleterOf(*nestedSoA.fRecordMemberFields[i]));
733 fSoAMemberOffsets.emplace_back(offsetInParent + nestedSoA.fSoAMemberOffsets[i]);
734 }
735}
736
738{
739 // Build a map of all subfields (nested) of the underlying record type. Map the fully qualified name of the
740 // subfields to their field pointer, so that we can later match the subfields of the SoA class to their corresponding
741 // fields in the underlying record type. Note that the members of the SoA class and the underlying record type
742 // can have different ordering. However, the base classes of the SoA class and the underlying record type must match
743 // in order.
744
745 std::vector<RFieldBase *> realRecordMemberFields; // Contains all subfields of the underlying record type
746 // Qualified field name --> index in realRecordMemberFields
747 std::unordered_map<std::string, std::size_t> recordFieldNameToIdx;
748
749 // Count the top-level subfields of the underlying record type for cross-check with the SoA type
750 unsigned int nDirectRecordSubfields = 0;
751 unsigned int nDirectRecordBases = 0;
752
753 for (auto itr = fSubfields[0]->begin(), iEnd = fSubfields[0]->end(); itr != iEnd; ++itr) {
754 if (itr->GetParent() == fSubfields[0].get()) {
755 if (itr->GetFieldName()[0] == ':') {
757 } else {
759 }
760 }
761
762 // Build the qualified field name for matching. We root the qualified field name at the underlying record type.
763 auto qualifiedName = itr->GetFieldName();
764 auto parent = itr->GetParent();
765 while (parent != fSubfields[0].get()) {
766 qualifiedName = parent->GetFieldName() + "." + qualifiedName;
767 parent = parent->GetParent();
768 }
770
771 realRecordMemberFields.emplace_back(&(*itr));
772 }
773
774 // Base classes are treated as unrolled nested SoA classes
775 const auto *soaBases = fSoAClass->GetListOfBases();
776 if (soaBases->GetSize() != static_cast<Int_t>(nDirectRecordBases)) {
777 throw RException(R__FAIL(std::string("number of base classes don't match between SoA class ") + GetFieldName() +
778 " and its underlying record type"));
779 }
780 unsigned int baseIdx = 0;
781 for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(*fSoAClass->GetListOfBases())) {
782 if (base->GetDelta() < 0) {
783 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
784 " virtually inherits from " + base->GetName()));
785 }
786 TClass *cl = base->GetClassPointer();
787
788 const auto baseFieldName = std::string(":_") + std::to_string(baseIdx);
789
790 // SoA class `A` is allowed to inherit from a SoA class `B` whose underlying record type is `X` if and only if
791 // the underlying record type of `A` inherits from a type `X`.
794 if (underlyingBaseTypeName != recordBaseField->GetTypeName()) {
795 throw RException(R__FAIL(std::string("inheritance of SoA class ") + GetFieldName() +
796 " does not match its underlying record type"));
797 }
798
799 std::unique_ptr<RSoAField> soaBaseField;
800 try {
801 soaBaseField = std::make_unique<RSoAField>(baseFieldName, cl->GetName());
802 } catch (const RException &e) {
803 throw RException(R__FAIL(std::string("invalid field type in base class: ") + cl->GetName() + " of SoA field " +
804 GetFieldName() + " (" + e.what() + ")"));
805 }
806
807 GraftNestedMemberFields(*soaBaseField, base->GetDelta(), [&](const std::string &name) {
808 return realRecordMemberFields[recordFieldNameToIdx[name]];
809 });
810
811 baseIdx++;
812 }
813
814 unsigned int nMembers = 0;
815 for (auto dataMember : ROOT::Detail::TRangeStaticCast<TDataMember>(*fSoAClass->GetListOfDataMembers())) {
816 // NOTE: ReconstructSplitFields() will also traverse the data members and need to apply the same rules for
817 // skipping members
818
819 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
820 continue;
821
822 if (dataMember->Property() & kIsArray) {
823 throw RException(R__FAIL(std::string("unsupported array type in SoA class: ") + dataMember->GetName()));
824 }
825
826 const std::string typeName{dataMember->GetTrueTypeName()};
827 auto dmField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
828
829 auto itr = recordFieldNameToIdx.find(dmField->GetFieldName());
830 if (itr == recordFieldNameToIdx.end()) {
831 throw RException(R__FAIL(std::string("unexpected SoA member: ") + dmField->GetFieldName()));
832 }
834 assert(dmField->GetFieldName() == underlyingField->GetFieldName());
835
836 if (auto soaField = dynamic_cast<RSoAField *>(dmField.get())) {
837 if (ROOT::Internal::GetRNTupleSoARecord(soaField->fSoAClass) != underlyingField->GetTypeName()) {
838 throw RException(R__FAIL(std::string("nested SoA field ") + soaField->GetQualifiedFieldName() + " [" +
839 soaField->GetTypeName() + "] does not match underlying type " +
840 underlyingField->GetTypeName()));
841 }
842
843 GraftNestedMemberFields(*soaField, dataMember->GetOffset(), [&](const std::string &name) {
844 return realRecordMemberFields[recordFieldNameToIdx[name]];
845 });
846 } else if (auto vecField = dynamic_cast<RRVecField *>(dmField.get())) {
847 if (vecField->begin()->GetTypeName() != underlyingField->GetTypeName() ||
848 vecField->begin()->GetTypeAlias() != underlyingField->GetTypeAlias()) {
849 const std::string leftType =
850 vecField->begin()->GetTypeName() +
851 (vecField->begin()->GetTypeAlias().empty() ? "" : " [" + vecField->begin()->GetTypeAlias() + "]");
852 const std::string rightType =
853 underlyingField->GetTypeName() +
854 (underlyingField->GetTypeAlias().empty() ? "" : " [" + underlyingField->GetTypeAlias() + "]");
855 throw RException(R__FAIL(std::string("SoA member type mismatch: ") + vecField->GetFieldName() + " (" +
856 leftType + " vs. " + rightType + ")"));
857 }
858
859 fRecordMemberFields.emplace_back(underlyingField);
860 fRecordMemberDeleters.emplace_back(GetDeleterOf(*underlyingField));
861 fSoAMemberOffsets.emplace_back(dataMember->GetOffset());
862 } else {
863 throw RException(R__FAIL("invalid field type in SoA class: " + dmField->GetTypeName()));
864 }
865
866 nMembers++;
867 }
869 throw RException(R__FAIL("missing SoA members"));
870 }
871}
872
874 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(clSoA->GetName()), ROOT::ENTupleStructure::kCollection,
875 false /* isSimple */),
876 fSoAClass(clSoA)
877{
878 static std::once_flag once;
879 std::call_once(once, []() {
880 R__LOG_WARNING(ROOT::Internal::NTupleLog()) << "The SoA field is experimental and still under development.";
881 });
882
883 EnsureValidUserClass(fSoAClass, *this, "RSoAField");
885 if (recordTypeName.empty()) {
886 throw ROOT::RException(R__FAIL(std::string("class ") + GetTypeName() +
887 " is not marked with the rntupleSoARecord "
888 "dictionary option; cannot create corresponding RSoAField."));
889 }
890 try {
891 Attach(std::make_unique<ROOT::RClassField>("_0", recordTypeName));
892 } catch (ROOT::RException &e) {
893 throw RException(R__FAIL("invalid record type of SoA field " + GetTypeName() + " [" + e.what() + "]"));
894 }
896 if (static_cast<std::uint32_t>(fSoAClass->GetClassVersion()) != fSubfields[0]->GetTypeVersion()) {
897 throw RException(R__FAIL(std::string("version mismatch between SoA type and underlying record type: ") +
898 std::to_string(fSoAClass->GetClassVersion()) + " vs. " +
899 std::to_string(fSubfields[0]->GetTypeVersion())));
900 }
901
903
904 std::string renormalizedAlias;
907
909 fLockSplitFields = std::make_unique<std::mutex>();
910}
911
912std::unique_ptr<ROOT::RFieldBase> ROOT::Experimental::RSoAField::CloneImpl(std::string_view newName) const
913{
914 return std::unique_ptr<RSoAField>(new RSoAField(newName, *this));
915}
916
926
931
936
937std::size_t ROOT::Experimental::RSoAField::AppendImpl(const void *from)
938{
939 const std::size_t nSoAMembers = fSoAMemberOffsets.size();
940
941 std::size_t N = 0; // Set by first SoA member and verified for the rest
942 for (std::size_t i = 0; i < nSoAMembers; ++i) {
943 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
945 assert(*sizePtr >= 0);
946 if (i == 0) {
947 N = *sizePtr;
948 } else {
949 if (static_cast<std::size_t>(*sizePtr) != N) {
950 const auto f = fRecordMemberFields[i];
951 throw RException(R__FAIL("SoA length mismatch for " + f->GetFieldName() + ": " + std::to_string(*sizePtr) +
952 " vs. " + std::to_string(N) + " (expected)"));
953 }
954 }
955 }
956
957 std::size_t nbytes = 0;
958 if (N > 0) {
959 for (std::size_t i = 0; i < nSoAMembers; ++i) {
960 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
962 RFieldBase *memberField = fRecordMemberFields[i];
963 if (memberField->IsSimple()) {
964 GetPrincipalColumnOf(*memberField)->AppendV(*beginPtr, N);
965 nbytes += N * GetPrincipalColumnOf(*memberField)->GetElement()->GetPackedSize();
966 } else {
967 for (std::size_t j = 0; j < N; ++j) {
968 nbytes += CallAppendOn(*memberField, *beginPtr + j * memberField->GetValueSize());
969 }
970 }
971 }
972 }
973
974 fNWritten += N;
975 fPrincipalColumn->Append(&fNWritten);
976 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
977}
978
980{
981 // Read collection info for this entry
984 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &N);
985
986 const auto nSoAMembers = fSoAMemberOffsets.size();
987 for (std::size_t i = 0; i < nSoAMembers; ++i) {
988 RFieldBase *memberField = fRecordMemberFields[i];
989 const auto memberSize = memberField->GetValueSize();
990 void *rvecPtr = static_cast<unsigned char *>(to) + fSoAMemberOffsets[i];
991 auto begin = ROOT::RRVecField::ResizeRVec(rvecPtr, N, memberSize, memberField, fRecordMemberDeleters[i].get());
992
993 if (N == 0)
994 continue;
995
996 if (memberField->IsSimple()) {
997 GetPrincipalColumnOf(*memberField)->ReadV(collectionStart, N, begin);
998 } else {
999 if (memberField->IsArtificial()) {
1000 // Other artificial fields simply don't read at all. This does not work here because then
1001 // the vector elements of trivial types would be left uninitialized (complex types explicitly call
1002 // the constructor on vector resize). Thus we explicitly default-initialize trivial types.
1003 // Note that this causes a subtle difference in behavior: if the added member is default-initialized to
1004 // a non-zero value, this will be forgotten in the SoA layout.
1005 if (memberField->GetTraits() & kTraitTriviallyConstructible) {
1006 std::memset(begin, 0, N * memberSize);
1007 }
1008 } else {
1009 for (std::size_t j = 0; j < N; ++j) {
1010 CallReadOn(*memberField, collectionStart + j, begin + (j * memberSize));
1011 }
1012 }
1013 }
1014 }
1015}
1016
1018{
1019 EnsureMatchingOnDiskField(desc, kDiffTypeVersion).ThrowOnError();
1020}
1021
1023{
1024 fSoAClass->New(where);
1025}
1026
1028{
1029}
1030
1032{
1033 fSoAClass->Destructor(objPtr, true /* dtorOnly */);
1034 RDeleter::operator()(objPtr, dtorOnly);
1035}
1036
1038{
1039 std::lock_guard<std::mutex> lockGuard(*fLockSplitFields);
1040 if (fSplitFields)
1041 return;
1042
1043 fSplitFields = std::make_unique<std::vector<std::unique_ptr<ROOT::RFieldBase>>>();
1044 fSplitOffsets = std::make_unique<std::vector<std::size_t>>();
1045
1046 unsigned int baseIdx = 0;
1048 TClass *cl = base->GetClassPointer();
1049 auto baseField = RFieldBase::Create(std::string(":_" + std::to_string(baseIdx)), cl->GetName()).Unwrap();
1050 fSplitFields->emplace_back(std::move(baseField));
1051 fSplitOffsets->emplace_back(base->GetDelta());
1052 baseIdx++;
1053 }
1054
1056 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
1057 continue;
1058
1059 const std::string typeName{dataMember->GetTrueTypeName()};
1060 auto dmField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
1061 fSplitFields->emplace_back(std::move(dmField));
1062 fSplitOffsets->emplace_back(dataMember->GetOffset());
1063 }
1064}
1065
1066std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(const RValue &value) const
1067{
1069 const auto nSplitFields = fSplitFields->size();
1070
1071 auto valuePtr = value.GetPtr<void>();
1072 auto soaPtr = static_cast<unsigned char *>(valuePtr.get());
1073 std::vector<RValue> values;
1074 values.reserve(nSplitFields);
1075 for (std::size_t i = 0; i < nSplitFields; ++i) {
1076 values.emplace_back((*fSplitFields)[i]->BindValue(std::shared_ptr<void>(valuePtr, soaPtr + (*fSplitOffsets)[i])));
1077 }
1078 return values;
1079}
1080
1082{
1083 return fSoAClass->GetClassSize();
1084}
1085
1087{
1088 return fSoAClass->GetClassVersion();
1089}
1090
1092{
1093 return fSoAClass->GetCheckSum();
1094}
1095
1097{
1098 const auto align = fSoAClass->GetClassAlignment();
1099 EnsureValidAlignment(align);
1100 return align;
1101}
1102
1104{
1105 // TODO(jblomer): factor out
1107 if (!polymorphic) {
1108 return nullptr;
1109 }
1110 return fSoAClass->GetTypeInfo();
1111}
1112
1117
1118//------------------------------------------------------------------------------
1119
1120std::unique_ptr<ROOT::RFieldBase> ROOT::Internal::CreateEmulatedEnumField(std::string_view fieldName,
1121 std::string_view emulatedFromType,
1122 std::string_view underlyingIntType)
1123{
1124 return std::unique_ptr<RFieldBase>(new REnumField(fieldName, emulatedFromType, underlyingIntType));
1125}
1126
1127ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
1129{
1130}
1131
1133 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
1134 false /* isSimple */)
1135{
1136 // Avoid accidentally supporting std types through TEnum.
1137 if (enump->Property() & kIsDefinedInStd) {
1138 throw RException(R__FAIL(GetTypeName() + " is not supported"));
1139 }
1140
1141 switch (enump->GetUnderlyingType()) {
1142 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
1143 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
1144 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
1145 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
1146 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
1147 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
1148 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
1149 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
1150 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
1151 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
1152 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
1153 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
1154 }
1155
1157}
1158
1159ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
1160 std::unique_ptr<RFieldBase> intField)
1162{
1163 Attach(std::move(intField));
1165}
1166
1168 std::string_view underlyingIntType)
1170{
1171 auto intField = Create("_0", std::string(underlyingIntType)).Unwrap();
1172 Attach(std::move(intField));
1174}
1175
1176std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
1177{
1178 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
1179 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
1180}
1181
1183{
1184 // TODO(jblomer): allow enum to enum conversion only by rename rule
1185 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1186}
1187
1188std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
1189{
1190 std::vector<RValue> result;
1191 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
1192 return result;
1193}
1194
1196{
1197 visitor.VisitEnumField(*this);
1198}
1199
1200//------------------------------------------------------------------------------
1201
1202ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
1203 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1204{
1205 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1206 if (typeAlias != GetTypeName())
1208
1209 AttachItemFields(std::move(itemFields));
1210
1211 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
1212 auto *c = TClass::GetClass(GetTypeName().c_str());
1213 if (!c)
1214 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1215 fSize = c->Size();
1216
1217 auto firstElem = c->GetRealData("first");
1218 if (!firstElem)
1219 throw RException(R__FAIL("first: no such member"));
1220 fOffsets.push_back(firstElem->GetThisOffset());
1221
1222 auto secondElem = c->GetRealData("second");
1223 if (!secondElem)
1224 throw RException(R__FAIL("second: no such member"));
1225 fOffsets.push_back(secondElem->GetThisOffset());
1226}
1227
1228std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
1229{
1230 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
1231 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
1232 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones)));
1233}
1234
1236{
1237 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1238
1239 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1240 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1241
1242 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1243 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1244 if (nOnDiskSubfields != 2) {
1245 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
1246 std::to_string(nOnDiskSubfields) + "\n" +
1248 }
1249}
1250
1251//------------------------------------------------------------------------------
1252
1255 bool readFromDisk)
1256{
1258 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
1259 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
1260 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
1261 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
1262 (ifuncs.fNext != nullptr));
1263 return ifuncs;
1264}
1265
1267 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
1268 false /* isSimple */),
1269 fNWritten(0)
1270{
1271 if (!classp->GetCollectionProxy())
1272 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
1273 if (classp->Property() & kIsDefinedInStd) {
1274 static const std::vector<std::string> supportedStdTypes = {
1275 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
1276 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
1277 bool isSupported = false;
1278 for (const auto &tn : supportedStdTypes) {
1279 if (GetTypeName().rfind(tn, 0) == 0) {
1280 isSupported = true;
1281 break;
1282 }
1283 }
1284 if (!isSupported)
1285 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
1286 }
1287
1288 std::string renormalizedAlias;
1291
1292 fProxy.reset(classp->GetCollectionProxy()->Generate());
1293 fProperties = fProxy->GetProperties();
1294 fCollectionType = fProxy->GetCollectionType();
1295 if (fProxy->HasPointers())
1296 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
1297
1298 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
1299 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1300}
1301
1302ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
1304{
1305 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
1307 throw RException(R__FAIL("custom associative collection proxies not supported"));
1308
1309 std::unique_ptr<ROOT::RFieldBase> itemField;
1310
1311 if (auto valueClass = fProxy->GetValueClass()) {
1312 // Element type is a class
1313 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
1314 } else {
1315 switch (fProxy->GetType()) {
1316 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
1317 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
1318 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
1319 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
1320 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
1321 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
1322 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
1323 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
1324 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
1325 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
1326 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
1327 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
1328 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
1329 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
1330 }
1331 }
1332
1333 fItemSize = itemField->GetValueSize();
1334 Attach(std::move(itemField));
1335}
1336
1337std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
1338{
1339 auto clone =
1340 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
1341 clone->fItemSize = fItemSize;
1342 clone->Attach(fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1343 return clone;
1344}
1345
1347{
1348 std::size_t nbytes = 0;
1349 unsigned count = 0;
1350 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
1351 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
1352 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1353 nbytes += CallAppendOn(*fSubfields[0], ptr);
1354 count++;
1355 }
1356
1357 fNWritten += count;
1358 fPrincipalColumn->Append(&fNWritten);
1359 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1360}
1361
1363{
1366 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1367
1368 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
1369 void *obj =
1370 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
1371
1372 unsigned i = 0;
1373 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
1374 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
1375 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
1376 }
1377 if (obj != to)
1378 fProxy->Commit(obj);
1379}
1380
1390
1395
1400
1402{
1403 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1404}
1405
1407{
1408 fProxy->New(where);
1409}
1410
1411std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
1412{
1413 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
1414 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
1415 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
1416 }
1417 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
1418}
1419
1421 std::shared_ptr<TVirtualCollectionProxy> proxy)
1422 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()), fProxy(std::move(proxy))
1423{
1424}
1425
1427 std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter, size_t itemSize)
1428 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()),
1429 fProxy(std::move(proxy)),
1430 fItemDeleter(std::move(itemDeleter)),
1432{
1433 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1434}
1435
1437{
1438 if (fItemDeleter) {
1440 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
1441 fItemDeleter->operator()(ptr, true /* dtorOnly */);
1442 }
1443 }
1444 fProxy->Destructor(objPtr, true /* dtorOnly */);
1445 RDeleter::operator()(objPtr, dtorOnly);
1446}
1447
1448std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
1449{
1450 std::vector<RValue> result;
1451 auto valueRawPtr = value.GetPtr<void>().get();
1454 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1455 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
1456 }
1457 return result;
1458}
1459
1461{
1462 return fProxy->Sizeof();
1463}
1464
1466{
1467 const auto align = fProxy->GetCollectionClass()->GetClassAlignment();
1468 EnsureValidAlignment(align);
1469 return align;
1470}
1471
1473{
1474 visitor.VisitProxiedCollectionField(*this);
1475}
1476
1477//------------------------------------------------------------------------------
1478
1479ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
1481 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
1482 fMapType(mapType)
1483{
1484 if (!itemField->GetTypeAlias().empty())
1485 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
1486
1487 auto *itemClass = fProxy->GetValueClass();
1488 fItemSize = itemClass->GetClassSize();
1489
1490 Attach(std::move(itemField), "_0");
1491}
1492
1493std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
1494{
1495 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1496}
1497
1499{
1500 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
1501
1502 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1503
1504 switch (fMapType) {
1505 case EMapType::kMap:
1506 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1507 default:
1508 break;
1509 // no restrictions for multimaps
1510 }
1511}
1512
1513//------------------------------------------------------------------------------
1514
1515ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1517 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1518 fSetType(setType)
1519{
1520 if (!itemField->GetTypeAlias().empty())
1521 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1522
1523 fItemSize = itemField->GetValueSize();
1524
1525 Attach(std::move(itemField), "_0");
1526}
1527
1528std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1529{
1530 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1531}
1532
1534{
1535 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1536 "std::unordered_map<"};
1537
1538 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1539
1540 switch (fSetType) {
1541 case ESetType::kSet:
1542 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1543 default:
1544 break;
1545 // no restrictions for multisets
1546 }
1547}
1548
1549//------------------------------------------------------------------------------
1550
1551namespace {
1552
1553/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1554class TBufferRecStreamer : public TBufferFile {
1555public:
1556 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1557
1558private:
1559 RCallbackStreamerInfo fCallbackStreamerInfo;
1560
1561public:
1562 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1563 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(std::move(callbackStreamerInfo))
1564 {
1565 }
1566 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1567};
1568
1569} // anonymous namespace
1570
1571ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1573{
1574}
1575
1577 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1578 false /* isSimple */),
1579 fClass(classp),
1580 fIndex(0)
1581{
1582 std::string renormalizedAlias;
1585
1587 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1588 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1589 // implicit constructors and destructors.
1594}
1595
1596std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1597{
1598 // To get the correct TClass instance in the clone, we clone using the un-normalized type name
1599 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, fClass->GetName()));
1600}
1601
1602std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1603{
1604 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1605 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1606 fClass->Streamer(const_cast<void *>(from), buffer);
1607
1608 auto nbytes = buffer.Length();
1609 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1610 fIndex += nbytes;
1611 fPrincipalColumn->Append(&fIndex);
1612 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1613}
1614
1616{
1619 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1620
1622 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1623 fClass->Streamer(to, buffer);
1624}
1625
1635
1640
1645
1647{
1648 source.RegisterStreamerInfos();
1649 return nullptr;
1650}
1651
1653{
1654 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1655}
1656
1658{
1659 fClass->New(where);
1660}
1661
1666
1668{
1669 fClass->Destructor(objPtr, true /* dtorOnly */);
1670 RDeleter::operator()(objPtr, dtorOnly);
1671}
1672
1682
1684{
1685 const auto align = fClass->GetClassAlignment();
1686 EnsureValidAlignment(align);
1687 return align;
1688}
1689
1691{
1692 return fClass->GetClassSize();
1693}
1694
1696{
1697 return fClass->GetClassVersion();
1698}
1699
1701{
1702 return fClass->GetCheckSum();
1703}
1704
1706{
1707 visitor.VisitStreamerField(*this);
1708}
1709
1710//------------------------------------------------------------------------------
1711
1713{
1714 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1715 return dataMember->GetOffset();
1716 }
1717 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1718}
1719
1721 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1722{
1724 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1725 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1726}
1727
1729 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1730{
1731 assert(TObject::Class()->GetClassVersion() == 1);
1732
1734 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1735 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1736}
1737
1738std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1739{
1740 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1741}
1742
1743std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1744{
1745 // Cf. TObject::Streamer()
1746
1747 auto *obj = static_cast<const TObject *>(from);
1748 if (obj->TestBit(TObject::kIsReferenced)) {
1749 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1750 }
1751
1752 std::size_t nbytes = 0;
1753 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1754
1755 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1756 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1757 nbytes += CallAppendOn(*fSubfields[1], &bits);
1758
1759 return nbytes;
1760}
1761
1763{
1764 // Cf. TObject::Streamer()
1765
1766 auto *obj = static_cast<TObject *>(to);
1767 if (obj->TestBit(TObject::kIsReferenced)) {
1768 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1769 }
1770
1771 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1772
1773 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1775 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1776}
1777
1779{
1780 UInt_t uniqueID, bits;
1781 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1782 CallReadOn(*fSubfields[1], globalIndex, &bits);
1783 ReadTObject(to, uniqueID, bits);
1784}
1785
1787{
1788 UInt_t uniqueID, bits;
1789 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1790 CallReadOn(*fSubfields[1], localIndex, &bits);
1791 ReadTObject(to, uniqueID, bits);
1792}
1793
1795{
1796 return TObject::Class()->GetClassVersion();
1797}
1798
1800{
1801 return TObject::Class()->GetCheckSum();
1802}
1803
1805{
1806 new (where) TObject();
1807}
1808
1809std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1810{
1811 std::vector<RValue> result;
1812 // Use GetPtr<TObject> to type-check
1813 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1814 auto charPtr = static_cast<unsigned char *>(ptr.get());
1815 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1816 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1817 return result;
1818}
1819
1821{
1822 return sizeof(TObject);
1823}
1824
1826{
1827 return alignof(TObject);
1828}
1829
1831{
1832 visitor.VisitTObjectField(*this);
1833}
1834
1835//------------------------------------------------------------------------------
1836
1837ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1838 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1839{
1840 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1841 if (typeAlias != GetTypeName())
1843
1844 AttachItemFields(std::move(itemFields));
1845
1846 auto *c = TClass::GetClass(GetTypeName().c_str());
1847 if (!c)
1848 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1849 fSize = c->Size();
1850
1851 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1852 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1853 // following the order of the type list.
1854 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1855 // members, the assertion below will fail.
1856 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1857 std::string memberName("_" + std::to_string(i));
1858 auto member = c->GetRealData(memberName.c_str());
1859 if (!member)
1860 throw RException(R__FAIL(memberName + ": no such member"));
1861 fOffsets.push_back(member->GetThisOffset());
1862 }
1863}
1864
1865std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1866{
1867 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1868 itemClones.reserve(fSubfields.size());
1869 for (const auto &f : fSubfields) {
1870 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1871 }
1872 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones)));
1873}
1874
1876{
1877 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1878
1879 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1880 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1881
1882 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1883 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1884 const auto nSubfields = fSubfields.size();
1886 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1887 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1889 }
1890}
1891
1892//------------------------------------------------------------------------------
1893
1894namespace {
1895
1896// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1897constexpr std::size_t GetVariantTagSize()
1898{
1899 // Should be all zeros except for the tag, which is 1
1900 std::variant<char> t;
1901 constexpr auto sizeOfT = sizeof(t);
1902
1903 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1904 return sizeOfT == 2 ? 1 : 4;
1905}
1906
1907template <std::size_t VariantSizeT>
1908struct RVariantTag {
1909 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1910 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1911};
1912
1913} // anonymous namespace
1914
1916 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1917 fMaxItemSize(source.fMaxItemSize),
1918 fMaxAlignment(source.fMaxAlignment),
1919 fTagOffset(source.fTagOffset),
1920 fVariantOffset(source.fVariantOffset),
1921 fNWritten(source.fNWritten.size(), 0)
1922{
1923 for (const auto &f : source.GetConstSubfields())
1924 Attach(f->Clone(f->GetFieldName()));
1925 fTraits = source.fTraits;
1926}
1927
1928ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1929 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1930 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1931{
1932 // The variant needs to initialize its own tag member
1934
1935 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1936 if (typeAlias != GetTypeName())
1938
1939 auto nFields = itemFields.size();
1940 if (nFields == 0 || nFields > kMaxVariants) {
1941 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1942 }
1943 fNWritten.resize(nFields, 0);
1944 for (unsigned int i = 0; i < nFields; ++i) {
1947 fTraits &= itemFields[i]->GetTraits();
1948 Attach(std::move(itemFields[i]), "_" + std::to_string(i));
1949 }
1950
1951 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1952 // For instance, std::variant<std::optional<int>> on macOS.
1953 auto cl = TClass::GetClass(GetTypeName().c_str());
1954 assert(cl);
1955 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1956 if (dm)
1957 fVariantOffset = dm->GetOffset();
1958
1959 const auto tagSize = GetVariantTagSize();
1960 const auto padding = tagSize - (fMaxItemSize % tagSize);
1962}
1963
1964std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1965{
1966 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1967}
1968
1969std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1970{
1971 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1972 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1973 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1974}
1975
1976void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1977{
1978 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1979 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1980 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1981}
1982
1983std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1984{
1985 auto tag = GetTag(from, fTagOffset);
1986 std::size_t nbytes = 0;
1987 auto index = 0;
1988 if (tag > 0) {
1989 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1990 index = fNWritten[tag - 1]++;
1991 }
1993 fPrincipalColumn->Append(&varSwitch);
1994 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1995}
1996
1998{
2000 std::uint32_t tag;
2001 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
2002 R__ASSERT(tag < 256);
2003
2004 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
2005 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
2006 // any `std::holds_alternative<T>` check fail later.
2007 if (R__likely(tag > 0)) {
2008 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
2009 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
2010 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
2011 }
2012 SetTag(to, fTagOffset, tag);
2013}
2014
2020
2025
2030
2032{
2033 static const std::vector<std::string> prefixes = {"std::variant<"};
2034
2035 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
2036 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
2037
2038 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
2039 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
2040 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
2042 }
2043}
2044
2046{
2047 memset(where, 0, GetValueSize());
2048 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
2049 SetTag(where, fTagOffset, 1);
2050}
2051
2053{
2054 auto tag = GetTag(objPtr, fTagOffset);
2055 if (tag > 0) {
2056 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
2057 }
2058 RDeleter::operator()(objPtr, dtorOnly);
2059}
2060
2061std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
2062{
2063 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
2064 itemDeleters.reserve(fSubfields.size());
2065 for (const auto &f : fSubfields) {
2066 itemDeleters.emplace_back(GetDeleterOf(*f));
2067 }
2068 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, GetAlignment(), std::move(itemDeleters));
2069}
2070
2072{
2073 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
2074}
2075
2077{
2078 const auto alignment = GetAlignment();
2079 const auto actualSize = fTagOffset + GetVariantTagSize();
2080 const auto padding = alignment - (actualSize % alignment);
2081 return actualSize + ((padding == alignment) ? 0 : padding);
2082}
2083
2085{
2086 std::fill(fNWritten.begin(), fNWritten.end(), 0);
2087}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:569
#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:130
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h 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:142
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 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 GraftNestedMemberFields(const RSoAField &nestedSoA, std::size_t offsetInParent, const 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 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 ...
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< 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:294
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.
@ kTraitEmulatedField
This field is a user defined type that was missing dictionaries and was reconstructed from the on-dis...
@ 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 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.
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:331
RField(std::string_view name)
Definition RField.hxx:334
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.
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:6679
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2331
EState GetState() const
Definition TClass.h:504
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition TClass.cxx:5533
size_t GetClassAlignment() const
Return the alignment requirement (in bytes) for objects of this class.
Definition TClass.cxx:5844
void BuildRealData(void *pointer=nullptr, Bool_t isTransient=kFALSE)
Build a full list of persistent data members.
Definition TClass.cxx:2043
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:3833
TList * GetListOfRealData() const
Definition TClass.h:468
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5869
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3699
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2923
Int_t GetClassSize() const
Definition TClass.h:439
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2408
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6254
@ 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:2999
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 ...
std::unique_ptr< RFieldBase > CreateEmulatedEnumField(std::string_view fieldName, std::string_view emulatedFromType, std::string_view underlyingIntType)
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.