Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldMeta.cxx
Go to the documentation of this file.
1/// \file RFieldMeta.cxx
2/// \ingroup NTuple
3/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
4/// \date 2024-11-19
5
6// This file has concrete RField implementations that depend on ROOT Meta:
7// - RClassField
8// - REnumField
9// - RPairField
10// - RProxiedCollectionField
11// - RMapField
12// - RSetField
13// - RStreamerField
14// - RField<TObject>
15// - RVariantField
16
17#include <ROOT/RField.hxx>
18#include <ROOT/RFieldBase.hxx>
19#include <ROOT/RFieldUtils.hxx>
21#include <ROOT/RNTupleUtils.hxx>
22#include <ROOT/RSpan.hxx>
23
24#include <TBaseClass.h>
25#include <TBufferFile.h>
26#include <TClass.h>
27#include <TClassEdit.h>
28#include <TDataMember.h>
29#include <TEnum.h>
30#include <TObject.h>
31#include <TObjArray.h>
32#include <TObjString.h>
33#include <TRealData.h>
34#include <TSchemaRule.h>
35#include <TSchemaRuleSet.h>
36#include <TStreamerElement.h>
37#include <TVirtualObject.h>
39
40#include <algorithm>
41#include <array>
42#include <cstddef> // std::size_t
43#include <cstdint> // std::uint32_t et al.
44#include <cstring> // for memset
45#include <memory>
46#include <string>
47#include <string_view>
48#include <unordered_set>
49#include <utility>
50#include <variant>
51
53
54namespace {
55
56TClass *EnsureValidClass(std::string_view className)
57{
58 auto cl = TClass::GetClass(std::string(className).c_str());
59 if (cl == nullptr) {
60 throw ROOT::RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
61 }
62 return cl;
63}
64
65TEnum *EnsureValidEnum(std::string_view enumName)
66{
67 auto e = TEnum::GetEnum(std::string(enumName).c_str());
68 if (e == nullptr) {
69 throw ROOT::RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
70 }
71 return e;
72}
73
74} // anonymous namespace
75
77 : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kRecord, false /* isSimple */),
79 fSubfieldsInfo(source.fSubfieldsInfo),
80 fMaxAlignment(source.fMaxAlignment)
81{
82 for (const auto &f : source.GetConstSubfields()) {
83 RFieldBase::Attach(f->Clone(f->GetFieldName()));
84 }
85 fTraits = source.GetTraits();
86}
87
88ROOT::RClassField::RClassField(std::string_view fieldName, std::string_view className)
90{
91}
92
94 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kRecord,
95 false /* isSimple */),
97{
99 throw RException(R__FAIL(std::string("RField: RClassField \"") + classp->GetName() +
100 " cannot be constructed from a class that's not at least Interpreted"));
101 }
102 // Avoid accidentally supporting std types through TClass.
104 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
105 }
106 if (GetTypeName() == "TObject") {
107 throw RException(R__FAIL("TObject is only supported through RField<TObject>"));
108 }
109 if (fClass->GetCollectionProxy()) {
110 throw RException(R__FAIL(std::string(GetTypeName()) + " has an associated collection proxy; "
111 "use RProxiedCollectionField instead"));
112 }
113 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
114 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
115 if (!fClass->CanSplit() && fClass->Size() > 1 &&
118 throw RException(R__FAIL(GetTypeName() + " cannot be stored natively in RNTuple"));
119 }
122 throw RException(R__FAIL(GetTypeName() + " has streamer mode enforced, not supported as native RNTuple class"));
123 }
124
129
130 int i = 0;
131 const auto *bases = fClass->GetListOfBases();
132 assert(bases);
134 if (baseClass->GetDelta() < 0) {
135 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
136 " virtually inherits from " + baseClass->GetName()));
137 }
138 TClass *c = baseClass->GetClassPointer();
139 auto subField =
140 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
141 fTraits &= subField->GetTraits();
142 Attach(std::move(subField), RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
143 i++;
144 }
146 // Skip, for instance, unscoped enum constants defined in the class
147 if (dataMember->Property() & kIsStatic)
148 continue;
149 // Skip members explicitly marked as transient by user comment
150 if (!dataMember->IsPersistent()) {
151 // TODO(jblomer): we could do better
153 continue;
154 }
155
156 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
157 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
158 // the string returned by dataMember->GetFullTypeName())
159 std::string typeName{dataMember->GetTrueTypeName()};
160 // RFieldBase::Create() set subField->fTypeAlias based on the assumption that the user specified typeName, which
161 // already went through one round of type resolution.
162 std::string origTypeName{dataMember->GetFullTypeName()};
163
164 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
165 if (dataMember->Property() & kIsArray) {
166 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
167 const auto addedStr = "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
168 typeName += addedStr;
170 }
171 }
172
173 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
174
176 if (normTypeName == subField->GetTypeName()) {
178 } else {
180 }
181
182 fTraits &= subField->GetTraits();
183 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
184 }
186}
187
189{
190 if (fStagingArea) {
191 for (const auto &[_, si] : fStagingItems) {
192 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
193 auto deleter = GetDeleterOf(*si.fField);
194 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
195 }
196 }
197 }
198}
199
200void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
201{
202 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
203 fSubfieldsInfo.push_back(info);
204 RFieldBase::Attach(std::move(child));
205}
206
207std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
208{
210 const auto ruleset = fClass->GetSchemaRules();
211 if (!ruleset)
212 return rules;
213
214 if (!fieldDesc) {
215 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
216 // of the class
217 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
218 } else {
219 // We need to change (back) the name normalization from RNTuple to ROOT Meta
220 std::string normalizedName;
222 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
223 // on-disk version of the field.
224 if (fieldDesc->GetTypeChecksum()) {
225 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
226 } else {
227 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
228 }
229 }
230
231 // Cleanup and sort rules
232 // Check that any any given source member uses the same type in all rules
233 std::unordered_map<std::string, std::string> sourceNameAndType;
234 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
235 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
236 const auto rule = *itr;
237
238 // Erase unknown rule types
239 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
241 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
242 itr = rules.erase(itr);
243 continue;
244 }
245
246 bool hasConflictingSourceMembers = false;
247 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
248 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
249 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
250 if (!isNew && (itrSrc->second != memberType)) {
252 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
253 << memberType << " for member " << source->GetName();
255 break;
256 }
257 }
259 itr = rules.erase(itr);
260 continue;
261 }
262
263 // Rules targeting the entire object need to be executed at the end
264 if (rule->GetTarget() == nullptr) {
265 nskip++;
266 if (itr != rules.end() - nskip)
267 std::iter_swap(itr++, rules.end() - nskip);
268 continue;
269 }
270
271 ++itr;
272 }
273
274 return rules;
275}
276
277std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
278{
279 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
280}
281
282std::size_t ROOT::RClassField::AppendImpl(const void *from)
283{
284 std::size_t nbytes = 0;
285 for (unsigned i = 0; i < fSubfields.size(); i++) {
286 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
287 }
288 return nbytes;
289}
290
292{
293 for (const auto &[_, si] : fStagingItems) {
294 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
295 }
296 for (unsigned i = 0; i < fSubfields.size(); i++) {
297 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
298 }
299}
300
302{
303 for (const auto &[_, si] : fStagingItems) {
304 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
305 }
306 for (unsigned i = 0; i < fSubfields.size(); i++) {
307 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
308 }
309}
310
313{
316 return idSourceMember;
317
318 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
319 const auto subFieldName = subFieldDesc.GetFieldName();
320 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
321 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
323 return idSourceMember;
324 }
325 }
326
328}
329
330void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
331{
332 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
333 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
334 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
335 if (!fStagingClass) {
336 // For a rename rule, we may simply ask for the old class name
337 fStagingClass = TClass::GetClass(className.c_str());
338 }
339 } else {
340 fStagingClass = fClass;
341 }
342 R__ASSERT(fStagingClass);
343 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
344}
345
346void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
347 const ROOT::RNTupleDescriptor &desc,
349{
350 std::size_t stagingAreaSize = 0;
351 for (const auto rule : rules) {
352 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
353 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
354 if (!isNew) {
355 // This source member has already been processed by another rule (and we only support one type per member)
356 continue;
357 }
358 RStagingItem &stagingItem = itr->second;
359
360 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
362 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
363 source->GetName()));
364 }
366
367 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
368 stagingItem.fField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
369 stagingItem.fField->SetOnDiskId(memberFieldDesc.GetId());
370
371 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
372 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
373 // to be present in the TClass instance, too.
375 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->GetValueSize());
376 }
377 }
378
379 if (stagingAreaSize) {
380 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
381 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
382 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
383
384 for (const auto &[_, si] : fStagingItems) {
385 if (!(si.fField->GetTraits() & kTraitTriviallyConstructible)) {
386 CallConstructValueOn(*si.fField, fStagingArea.get() + si.fOffset);
387 }
388 }
389 }
390}
391
393{
394 auto func = rule->GetReadFunctionPointer();
395 if (func == nullptr) {
396 // Can happen for rename rules
397 return;
398 }
399 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
400 TVirtualObject onfileObj{nullptr};
401 onfileObj.fClass = stagingClass;
402 onfileObj.fObject = stagingArea;
403 func(static_cast<char *>(target), &onfileObj);
404 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
405 });
406}
407
409{
410 std::vector<const TSchemaRule *> rules;
411 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
412 // will be marked as artificial (added member in a new class version or member set by rule).
413 std::unordered_set<std::string> regularSubfields;
414
415 if (GetOnDiskId() == kInvalidDescriptorId) {
416 // This can happen for added base classes or added members of class type
417 rules = FindRules(nullptr);
418 if (!rules.empty())
419 SetStagingClass(GetTypeName(), GetTypeVersion());
420 } else {
421 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
422 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
423 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
424
425 for (auto linkId : fieldDesc.GetLinkIds()) {
426 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
427 regularSubfields.insert(subFieldDesc.GetFieldName());
428 }
429
430 rules = FindRules(&fieldDesc);
431
432 // 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
433 // (source) type name and version/checksum.
434 if (rules.empty()) {
435 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
436 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
437 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
439 if (GetTypeName() != descTypeName) {
440 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
441 " vs. " + descTypeName));
442 }
443 }
444
445 if (!rules.empty()) {
446 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
447 PrepareStagingArea(rules, desc, fieldDesc);
448 for (auto &[_, si] : fStagingItems)
450
451 // Remove target member of read rules from the list of regular members of the underlying on-disk field
452 for (const auto rule : rules) {
453 if (!rule->GetTarget())
454 continue;
455
456 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
457 regularSubfields.erase(std::string(target->GetString()));
458 }
459 }
460 }
461 }
462
463 for (const auto rule : rules) {
464 AddReadCallbacksFromIORule(rule);
465 }
466
467 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
468 for (auto &field : fSubfields) {
469 if (regularSubfields.count(field->GetFieldName()) == 0) {
470 CallSetArtificialOn(*field);
471 }
472 }
473}
474
476{
477 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeVersion | kDiffTypeName);
478}
479
481{
482 fClass->New(where);
483}
484
486{
487 fClass->Destructor(objPtr, true /* dtorOnly */);
488 RDeleter::operator()(objPtr, dtorOnly);
489}
490
491std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
492{
493 std::vector<RValue> result;
494 auto valuePtr = value.GetPtr<void>();
495 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
496 result.reserve(fSubfields.size());
497 for (unsigned i = 0; i < fSubfields.size(); i++) {
498 result.emplace_back(
499 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
500 }
501 return result;
502}
503
505{
506 return fClass->GetClassSize();
507}
508
510{
511 return fClass->GetClassVersion();
512}
513
515{
516 return fClass->GetCheckSum();
517}
518
520{
521 visitor.VisitClassField(*this);
522}
523
524//------------------------------------------------------------------------------
525
526ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
528{
529}
530
532 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
533 false /* isSimple */)
534{
535 // Avoid accidentally supporting std types through TEnum.
536 if (enump->Property() & kIsDefinedInStd) {
537 throw RException(R__FAIL(GetTypeName() + " is not supported"));
538 }
539
540 switch (enump->GetUnderlyingType()) {
541 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
542 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
543 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
544 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
545 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
546 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
547 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
548 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
549 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
550 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
551 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
552 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
553 }
554
556}
557
558ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
559 std::unique_ptr<RFieldBase> intField)
561{
562 Attach(std::move(intField));
564}
565
566std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
567{
568 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
569 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
570}
571
573{
574 // TODO(jblomer): allow enum to enum conversion only by rename rule
575 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName | kDiffTypeVersion);
576}
577
578std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
579{
580 std::vector<RValue> result;
581 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
582 return result;
583}
584
586{
587 visitor.VisitEnumField(*this);
588}
589
590//------------------------------------------------------------------------------
591
592std::string ROOT::RPairField::RPairField::GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
593{
594 return itemFields[0]->GetTypeName() + "," + itemFields[1]->GetTypeName();
595}
596
597ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
598 const std::array<std::size_t, 2> &offsets)
599 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
600{
601 AttachItemFields(std::move(itemFields));
602 fOffsets.push_back(offsets[0]);
603 fOffsets.push_back(offsets[1]);
604}
605
606ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
607 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
608{
609 AttachItemFields(std::move(itemFields));
610
611 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
612 auto *c = TClass::GetClass(GetTypeName().c_str());
613 if (!c)
614 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
615 fSize = c->Size();
616
617 auto firstElem = c->GetRealData("first");
618 if (!firstElem)
619 throw RException(R__FAIL("first: no such member"));
620 fOffsets.push_back(firstElem->GetThisOffset());
621
622 auto secondElem = c->GetRealData("second");
623 if (!secondElem)
624 throw RException(R__FAIL("second: no such member"));
625 fOffsets.push_back(secondElem->GetThisOffset());
626}
627
628std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
629{
630 std::array<std::size_t, 2> offsets = {fOffsets[0], fOffsets[1]};
631 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
632 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
633 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones), offsets));
634}
635
637{
638 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
639
640 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
641 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);
642 EnsureMatchingTypePrefix(fieldDesc, prefixes);
643
644 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
645 if (nOnDiskSubfields != 2) {
646 throw ROOT::RException(
647 R__FAIL("invalid number of on-disk subfields for std::pair " + std::to_string(nOnDiskSubfields)));
648 }
649}
650
651//------------------------------------------------------------------------------
652
655 bool readFromDisk)
656{
658 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
659 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
660 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
661 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
662 (ifuncs.fNext != nullptr));
663 return ifuncs;
664}
665
667 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
668 false /* isSimple */),
669 fNWritten(0)
670{
671 if (!classp->GetCollectionProxy())
672 throw RException(R__FAIL(std::string(GetTypeName()) + " has no associated collection proxy"));
673
674 fProxy.reset(classp->GetCollectionProxy()->Generate());
675 fProperties = fProxy->GetProperties();
676 fCollectionType = fProxy->GetCollectionType();
677 if (fProxy->HasPointers())
678 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
679
680 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
681 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
682}
683
684ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName,
685 std::unique_ptr<RFieldBase> itemField)
687{
688 fItemSize = itemField->GetValueSize();
689 Attach(std::move(itemField));
690}
691
692ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
694{
695 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
697 throw RException(R__FAIL("custom associative collection proxies not supported"));
698
699 std::unique_ptr<ROOT::RFieldBase> itemField;
700
701 if (auto valueClass = fProxy->GetValueClass()) {
702 // Element type is a class
703 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
704 } else {
705 switch (fProxy->GetType()) {
706 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
707 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
708 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
709 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
710 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
711 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
712 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
713 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
714 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
715 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
716 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
717 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
718 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
719 default: throw RException(R__FAIL("unsupported value type"));
720 }
721 }
722
723 fItemSize = itemField->GetValueSize();
724 Attach(std::move(itemField));
725}
726
727std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
728{
729 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
730 return std::unique_ptr<RProxiedCollectionField>(
731 new RProxiedCollectionField(newName, GetTypeName(), std::move(newItemField)));
732}
733
734std::size_t ROOT::RProxiedCollectionField::AppendImpl(const void *from)
735{
736 std::size_t nbytes = 0;
737 unsigned count = 0;
738 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
739 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
740 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
741 nbytes += CallAppendOn(*fSubfields[0], ptr);
742 count++;
743 }
744
745 fNWritten += count;
746 fPrincipalColumn->Append(&fNWritten);
747 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
748}
749
751{
754 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
755
756 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
757 void *obj =
758 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
759
760 unsigned i = 0;
761 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
762 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
763 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
764 }
765 if (obj != to)
766 fProxy->Commit(obj);
767}
768
778
783
788
790{
791 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName);
792}
793
795{
796 fProxy->New(where);
797}
798
799std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
800{
801 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
802 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
803 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
804 }
805 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
806}
807
809{
810 if (fItemDeleter) {
812 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
813 fItemDeleter->operator()(ptr, true /* dtorOnly */);
814 }
815 }
816 fProxy->Destructor(objPtr, true /* dtorOnly */);
817 RDeleter::operator()(objPtr, dtorOnly);
818}
819
820std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
821{
822 std::vector<RValue> result;
823 auto valueRawPtr = value.GetPtr<void>().get();
825 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
826 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
827 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
828 }
829 return result;
830}
831
833{
834 visitor.VisitProxiedCollectionField(*this);
835}
836
837//------------------------------------------------------------------------------
838
839ROOT::RMapField::RMapField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField)
841{
842 if (!dynamic_cast<RPairField *>(itemField.get()))
843 throw RException(R__FAIL("RMapField inner field type must be of RPairField"));
844
845 auto *itemClass = fProxy->GetValueClass();
846 fItemSize = itemClass->GetClassSize();
847
848 Attach(std::move(itemField));
849}
850
851//------------------------------------------------------------------------------
852
853ROOT::RSetField::RSetField(std::string_view fieldName, std::string_view typeName, std::unique_ptr<RFieldBase> itemField)
855{
856}
857
858//------------------------------------------------------------------------------
859
860namespace {
861
862/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
863class TBufferRecStreamer : public TBufferFile {
864public:
865 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
866
867private:
868 RCallbackStreamerInfo fCallbackStreamerInfo;
869
870public:
871 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
872 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
873 {
874 }
875 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
876};
877
878} // anonymous namespace
879
880ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className, std::string_view typeAlias)
882{
884}
885
887 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
888 false /* isSimple */),
889 fClass(classp),
890 fIndex(0)
891{
893 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
894 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
895 // implicit constructors and destructors.
900}
901
902std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
903{
904 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName(), GetTypeAlias()));
905}
906
907std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
908{
909 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
910 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
911 fClass->Streamer(const_cast<void *>(from), buffer);
912
913 auto nbytes = buffer.Length();
914 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
915 fIndex += nbytes;
916 fPrincipalColumn->Append(&fIndex);
917 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
918}
919
921{
924 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
925
927 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
928 fClass->Streamer(to, buffer);
929}
930
940
945
950
955
957{
958 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName | kDiffTypeVersion);
959}
960
962{
963 fClass->New(where);
964}
965
967{
968 fClass->Destructor(objPtr, true /* dtorOnly */);
969 RDeleter::operator()(objPtr, dtorOnly);
970}
971
981
983{
984 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
985}
986
988{
989 return fClass->GetClassSize();
990}
991
993{
994 return fClass->GetClassVersion();
995}
996
998{
999 return fClass->GetCheckSum();
1000}
1001
1003{
1004 visitor.VisitStreamerField(*this);
1005}
1006
1007//------------------------------------------------------------------------------
1008
1010{
1011 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1012 return dataMember->GetOffset();
1013 }
1014 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1015}
1016
1018 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1019{
1021 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1022 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1023}
1024
1026 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1027{
1028 assert(TObject::Class()->GetClassVersion() == 1);
1029
1031 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1032 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1033}
1034
1035std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1036{
1037 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1038}
1039
1040std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1041{
1042 // Cf. TObject::Streamer()
1043
1044 auto *obj = static_cast<const TObject *>(from);
1045 if (obj->TestBit(TObject::kIsReferenced)) {
1046 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1047 }
1048
1049 std::size_t nbytes = 0;
1050 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1051
1052 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1053 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1054 nbytes += CallAppendOn(*fSubfields[1], &bits);
1055
1056 return nbytes;
1057}
1058
1060{
1061 // Cf. TObject::Streamer()
1062
1063 auto *obj = static_cast<TObject *>(to);
1064 if (obj->TestBit(TObject::kIsReferenced)) {
1065 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1066 }
1067
1068 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1069
1070 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1072 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1073}
1074
1076{
1077 UInt_t uniqueID, bits;
1078 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1079 CallReadOn(*fSubfields[1], globalIndex, &bits);
1080 ReadTObject(to, uniqueID, bits);
1081}
1082
1084{
1085 UInt_t uniqueID, bits;
1086 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1087 CallReadOn(*fSubfields[1], localIndex, &bits);
1088 ReadTObject(to, uniqueID, bits);
1089}
1090
1092{
1093 return TObject::Class()->GetClassVersion();
1094}
1095
1097{
1098 return TObject::Class()->GetCheckSum();
1099}
1100
1102{
1103 new (where) TObject();
1104}
1105
1106std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1107{
1108 std::vector<RValue> result;
1109 // Use GetPtr<TObject> to type-check
1110 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1111 auto charPtr = static_cast<unsigned char *>(ptr.get());
1112 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1113 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1114 return result;
1115}
1116
1118{
1119 return sizeof(TObject);
1120}
1121
1123{
1124 return alignof(TObject);
1125}
1126
1128{
1129 visitor.VisitTObjectField(*this);
1130}
1131
1132//------------------------------------------------------------------------------
1133
1134std::string ROOT::RTupleField::RTupleField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1135{
1136 std::string result;
1137 if (itemFields.empty())
1138 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
1139 for (size_t i = 0; i < itemFields.size(); ++i) {
1140 result += itemFields[i]->GetTypeName() + ",";
1141 }
1142 result.pop_back(); // remove trailing comma
1143 return result;
1144}
1145
1146ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields,
1147 const std::vector<std::size_t> &offsets)
1148 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1149{
1150 AttachItemFields(std::move(itemFields));
1151 fOffsets = offsets;
1152}
1153
1154ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1155 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1156{
1157 AttachItemFields(std::move(itemFields));
1158
1159 auto *c = TClass::GetClass(GetTypeName().c_str());
1160 if (!c)
1161 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1162 fSize = c->Size();
1163
1164 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1165 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1166 // following the order of the type list.
1167 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1168 // members, the assertion below will fail.
1169 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1170 std::string memberName("_" + std::to_string(i));
1171 auto member = c->GetRealData(memberName.c_str());
1172 if (!member)
1173 throw RException(R__FAIL(memberName + ": no such member"));
1174 fOffsets.push_back(member->GetThisOffset());
1175 }
1176}
1177
1178std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1179{
1180 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1181 itemClones.reserve(fSubfields.size());
1182 for (const auto &f : fSubfields) {
1183 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1184 }
1185 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones), fOffsets));
1186}
1187
1189{
1190 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1191
1192 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1193 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);
1194 EnsureMatchingTypePrefix(fieldDesc, prefixes);
1195
1196 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1197 const auto nSubfields = fSubfields.size();
1199 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1200 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields)));
1201 }
1202}
1203
1204//------------------------------------------------------------------------------
1205
1206namespace {
1207
1208// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1209constexpr std::size_t GetVariantTagSize()
1210{
1211 // Should be all zeros except for the tag, which is 1
1212 std::variant<char> t;
1213 constexpr auto sizeOfT = sizeof(t);
1214
1215 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1216 return sizeOfT == 2 ? 1 : 4;
1217}
1218
1219template <std::size_t VariantSizeT>
1220struct RVariantTag {
1221 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1222 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1223};
1224
1225} // anonymous namespace
1226
1227std::string ROOT::RVariantField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1228{
1229 std::string result;
1230 for (size_t i = 0; i < itemFields.size(); ++i) {
1231 result += itemFields[i]->GetTypeName() + ",";
1232 }
1233 R__ASSERT(!result.empty()); // there is always at least one variant
1234 result.pop_back(); // remove trailing comma
1235 return result;
1236}
1237
1239 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1240 fMaxItemSize(source.fMaxItemSize),
1241 fMaxAlignment(source.fMaxAlignment),
1242 fTagOffset(source.fTagOffset),
1243 fVariantOffset(source.fVariantOffset),
1244 fNWritten(source.fNWritten.size(), 0)
1245{
1246 for (const auto &f : source.GetConstSubfields())
1247 Attach(f->Clone(f->GetFieldName()));
1248 fTraits = source.fTraits;
1249}
1250
1251ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1252 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields) + ">", ROOT::ENTupleStructure::kVariant,
1253 false /* isSimple */)
1254{
1255 // The variant needs to initialize its own tag member
1257
1258 auto nFields = itemFields.size();
1259 if (nFields == 0 || nFields > kMaxVariants) {
1260 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1261 }
1262 fNWritten.resize(nFields, 0);
1263 for (unsigned int i = 0; i < nFields; ++i) {
1266 fTraits &= itemFields[i]->GetTraits();
1267 Attach(std::move(itemFields[i]));
1268 }
1269
1270 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1271 // For instance, std::variant<std::optional<int>> on macOS.
1272 auto cl = TClass::GetClass(GetTypeName().c_str());
1273 assert(cl);
1274 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1275 if (dm)
1276 fVariantOffset = dm->GetOffset();
1277
1278 const auto tagSize = GetVariantTagSize();
1279 const auto padding = tagSize - (fMaxItemSize % tagSize);
1281}
1282
1283std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1284{
1285 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1286}
1287
1288std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1289{
1290 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1291 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1292 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1293}
1294
1295void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1296{
1297 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1298 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1299 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1300}
1301
1302std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1303{
1304 auto tag = GetTag(from, fTagOffset);
1305 std::size_t nbytes = 0;
1306 auto index = 0;
1307 if (tag > 0) {
1308 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1309 index = fNWritten[tag - 1]++;
1310 }
1312 fPrincipalColumn->Append(&varSwitch);
1313 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1314}
1315
1317{
1319 std::uint32_t tag;
1320 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1321 R__ASSERT(tag < 256);
1322
1323 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1324 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1325 // any `std::holds_alternative<T>` check fail later.
1326 if (R__likely(tag > 0)) {
1327 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1328 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1329 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1330 }
1331 SetTag(to, fTagOffset, tag);
1332}
1333
1339
1344
1349
1351{
1352 static const std::vector<std::string> prefixes = {"std::variant<"};
1353
1354 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1355 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);
1356 EnsureMatchingTypePrefix(fieldDesc, prefixes);
1357
1358 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1359 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName()));
1360 }
1361}
1362
1364{
1365 memset(where, 0, GetValueSize());
1366 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1367 SetTag(where, fTagOffset, 1);
1368}
1369
1371{
1372 auto tag = GetTag(objPtr, fTagOffset);
1373 if (tag > 0) {
1374 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1375 }
1376 RDeleter::operator()(objPtr, dtorOnly);
1377}
1378
1379std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1380{
1381 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1382 itemDeleters.reserve(fSubfields.size());
1383 for (const auto &f : fSubfields) {
1384 itemDeleters.emplace_back(GetDeleterOf(*f));
1385 }
1386 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1387}
1388
1390{
1391 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1392}
1393
1395{
1396 const auto alignment = GetAlignment();
1397 const auto actualSize = fTagOffset + GetVariantTagSize();
1398 const auto padding = alignment - (actualSize % alignment);
1399 return actualSize + ((padding == alignment) ? 0 : padding);
1400}
1401
1403{
1404 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1405}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:595
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
#define R__LOG_WARNING(...)
Definition RLogger.hxx:358
#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
@ kClassHasExplicitDtor
@ kClassHasImplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
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 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
char name[80]
Definition TGX11.cxx:110
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:108
Abstract base class for classes implementing the visitor design pattern.
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:113
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.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:201
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void Attach(std::unique_ptr< RFieldBase > child, RSubFieldInfo info)
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:142
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.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
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.
~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:131
void SetStagingClass(const std::string &className, unsigned int classVersion)
Sets fStagingClass according to the given name and version.
void BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:262
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:79
Field specific extra type information from the header / extenstion header.
The list of column representations a field can have.
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.
static void SetTypeAliasOf(RFieldBase &other, const std::string &alias)
Allow class fields to adjust the type alias of their members.
void Attach(std::unique_ptr< RFieldBase > child)
Add a new subfield to the list of nested fields.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
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
@ 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.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:292
RField(std::string_view name)
Definition RField.hxx:295
RMapField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
The on-storage metadata of an RNTuple.
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
Template specializations for C++ std::pair.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RPairField(std::string_view fieldName, std::array< std::unique_ptr< RFieldBase >, 2 > itemFields, const std::array< std::size_t, 2 > &offsets)
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)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class representing a collection of elements via TVirtualCollectionProxy.
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 final
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
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
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
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const_iterator begin() const
const_iterator end() const
The field for an untyped record.
void AttachItemFields(std::vector< std::unique_ptr< RFieldBase > > itemFields)
Definition RField.cxx:514
std::vector< std::size_t > fOffsets
RSetField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class using ROOT standard streaming.
Definition RField.hxx:208
ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
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 BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
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 > 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
As a rule of thumb, the alignment is equal to the size of the type.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
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, const std::vector< std::size_t > &offsets)
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.
static std::string GetTypeList(const std::vector< std::unique_ptr< RFieldBase > > &itemFields)
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
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.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
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
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2324
EState GetState() const
Definition TClass.h:501
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3797
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5743
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3663
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2902
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2401
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6128
@ kInterpreted
Definition TClass.h:129
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2973
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
Mother of all ROOT objects.
Definition TObject.h:41
@ kIsOnHeap
object is on heap
Definition TObject.h:87
@ kNotDeleted
object has not been deleted
Definition TObject.h:88
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:71
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
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
ERNTupleSerializationMode GetRNTupleSerializationMode(TClass *cl)
std::string GetNormalizedUnresolvedTypeName(const std::string &origName)
Applies all RNTuple type normalization rules except typedef resolution.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
@ 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.