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/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 <mutex>
47#include <string>
48#include <string_view>
49#include <unordered_set>
50#include <utility>
51#include <variant>
52
54
55namespace {
56
57TClass *EnsureValidClass(std::string_view className)
58{
59 auto cl = TClass::GetClass(std::string(className).c_str());
60 if (cl == nullptr) {
61 throw ROOT::RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
62 }
63 return cl;
64}
65
66/// Common checks used both by RClassField and RSoAField
67void EnsureValidUserClass(TClass *cl, const ROOT::RFieldBase &field, std::string_view fieldType)
68{
69 if (cl->GetState() < TClass::kInterpreted) {
70 throw ROOT::RException(R__FAIL(std::string(fieldType) + " " + cl->GetName() +
71 " cannot be constructed from a class that's not at least Interpreted"));
72 }
73 // Avoid accidentally supporting std types through TClass.
74 if (cl->Property() & kIsDefinedInStd) {
75 throw ROOT::RException(R__FAIL(field.GetTypeName() + " is not supported"));
76 }
77 if (field.GetTypeName() == "TObject") {
78 throw ROOT::RException(R__FAIL("TObject is only supported through RField<TObject>"));
79 }
80 if (cl->GetCollectionProxy()) {
81 throw ROOT::RException(R__FAIL(field.GetTypeName() + " has an associated collection proxy; "
82 "use RProxiedCollectionField instead"));
83 }
84 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
85 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
86 if (!cl->CanSplit() && cl->Size() > 1 &&
88 throw ROOT::RException(R__FAIL(field.GetTypeName() + " cannot be stored natively in RNTuple"));
89 }
92 throw ROOT::RException(
93 R__FAIL(field.GetTypeName() + " has streamer mode enforced, not supported as native RNTuple class"));
94 }
95 // Detect custom streamers set on individual members at runtime via
96 // TClass::SetMemberStreamer() or TClass::AdoptMemberStreamer().
97 // CanSplit() only checks for custom streamers set at compile time (fHasCustomStreamerMember),
98 // but runtime streamers are stored in TRealData and must be checked here.
99 if (!cl->GetListOfRealData()) {
100 cl->BuildRealData();
101 }
102 for (auto realMember : ROOT::Detail::TRangeStaticCast<TRealData>(*cl->GetListOfRealData())) {
103 if (realMember->GetStreamer()) {
104 throw ROOT::RException(R__FAIL(std::string(field.GetTypeName()) + " has member " + realMember->GetName() +
105 " with a custom streamer; not supported natively in RNTuple"));
106 }
107 }
108}
109
110TEnum *EnsureValidEnum(std::string_view enumName)
111{
112 auto e = TEnum::GetEnum(std::string(enumName).c_str());
113 if (e == nullptr) {
114 throw ROOT::RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
115 }
116 return e;
117}
118
119/// Create a comma-separated list of type names from the given fields. Uses either the real type names or the
120/// type aliases (if there are any, otherwise the actual type name). Used to construct template argument lists
121/// for templated types such as std::pair<...>, std::tuple<...>, std::variant<...>.
122std::string GetTypeList(std::span<std::unique_ptr<ROOT::RFieldBase>> itemFields, bool useTypeAliases)
123{
124 std::string result;
125 for (size_t i = 0; i < itemFields.size(); ++i) {
126 if (useTypeAliases && !itemFields[i]->GetTypeAlias().empty()) {
127 result += itemFields[i]->GetTypeAlias();
128 } else {
129 result += itemFields[i]->GetTypeName();
130 }
131 result.push_back(',');
132 }
133 if (result.empty()) {
134 throw ROOT::RException(R__FAIL("invalid empty type list provided as template argument"));
135 }
136 result.pop_back(); // remove trailing comma
137 return result;
138}
139
141{
142 std::string typePrefix;
143 switch (setType) {
144 case ROOT::RSetField::ESetType::kSet: typePrefix = "std::set<"; break;
145 case ROOT::RSetField::ESetType::kUnorderedSet: typePrefix = "std::unordered_set<"; break;
146 case ROOT::RSetField::ESetType::kMultiSet: typePrefix = "std::multiset<"; break;
147 case ROOT::RSetField::ESetType::kUnorderedMultiSet: typePrefix = "std::unordered_multiset<"; break;
148 default: R__ASSERT(false);
149 }
150 return typePrefix +
151 ((useTypeAlias && !innerField.GetTypeAlias().empty()) ? innerField.GetTypeAlias()
152 : innerField.GetTypeName()) +
153 ">";
154}
155
157{
158 if (const auto pairField = dynamic_cast<const ROOT::RPairField *>(innerField)) {
159 std::string typePrefix;
160 switch (mapType) {
161 case ROOT::RMapField::EMapType::kMap: typePrefix = "std::map<"; break;
162 case ROOT::RMapField::EMapType::kUnorderedMap: typePrefix = "std::unordered_map<"; break;
163 case ROOT::RMapField::EMapType::kMultiMap: typePrefix = "std::multimap<"; break;
164 case ROOT::RMapField::EMapType::kUnorderedMultiMap: typePrefix = "std::unordered_multimap<"; break;
165 default: R__ASSERT(false);
166 }
167 const auto &items = pairField->GetConstSubfields();
168 std::string type = typePrefix;
169 for (int i : {0, 1}) {
170 if (useTypeAliases && !items[i]->GetTypeAlias().empty()) {
171 type += items[i]->GetTypeAlias();
172 } else {
173 type += items[i]->GetTypeName();
174 }
175 if (i == 0)
176 type.push_back(',');
177 }
178 return type + ">";
179 }
180
181 throw ROOT::RException(R__FAIL("RMapField inner field type must be of RPairField"));
182}
183
184} // anonymous namespace
185
187 : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kRecord, false /* isSimple */),
189 fSubfieldsInfo(source.fSubfieldsInfo),
190 fMaxAlignment(source.fMaxAlignment)
191{
192 for (const auto &f : source.GetConstSubfields()) {
193 RFieldBase::Attach(f->Clone(f->GetFieldName()));
194 }
195 fTraits = source.GetTraits();
196}
197
198ROOT::RClassField::RClassField(std::string_view fieldName, std::string_view className)
200{
201}
202
204 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kRecord,
205 false /* isSimple */),
207{
208 EnsureValidUserClass(fClass, *this, "RClassField");
209
211 throw ROOT::RException(R__FAIL(GetTypeName() + " is a SoA field and connot be used through RClassField"));
212 }
213
218
219 std::string renormalizedAlias;
222
223 int i = 0;
224 const auto *bases = fClass->GetListOfBases();
225 assert(bases);
227 if (baseClass->GetDelta() < 0) {
228 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
229 " virtually inherits from " + baseClass->GetName()));
230 }
231 TClass *c = baseClass->GetClassPointer();
232 auto subField =
233 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
234 fTraits &= subField->GetTraits();
235 Attach(std::move(subField), RSubfieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
236 i++;
237 }
239 // Skip, for instance, unscoped enum constants defined in the class
240 if (dataMember->Property() & kIsStatic)
241 continue;
242 // Skip members explicitly marked as transient by user comment
243 if (!dataMember->IsPersistent()) {
244 // TODO(jblomer): we could do better
246 continue;
247 }
248
249 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
250 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
251 // the string returned by dataMember->GetFullTypeName())
252 std::string typeName{dataMember->GetTrueTypeName()};
253
254 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
255 if (dataMember->Property() & kIsArray) {
256 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
257 typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
258 }
259 }
260
261 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
262
263 fTraits &= subField->GetTraits();
264 Attach(std::move(subField), RSubfieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
265 }
267}
268
270{
271 if (fStagingArea) {
272 for (const auto &[_, si] : fStagingItems) {
273 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
274 auto deleter = GetDeleterOf(*si.fField);
275 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
276 }
277 }
278 }
279}
280
281void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubfieldInfo info)
282{
283 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
284 fSubfieldsInfo.push_back(info);
285 RFieldBase::Attach(std::move(child));
286}
287
288std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
289{
291 const auto ruleset = fClass->GetSchemaRules();
292 if (!ruleset)
293 return rules;
294
295 if (!fieldDesc) {
296 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
297 // of the class
298 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
299 } else {
300 // We need to change (back) the name normalization from RNTuple to ROOT Meta
301 std::string normalizedName;
303 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
304 // on-disk version of the field.
305 if (fieldDesc->GetTypeChecksum()) {
306 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
307 } else {
308 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
309 }
310 }
311
312 // Cleanup and sort rules
313 // Check that any any given source member uses the same type in all rules
314 std::unordered_map<std::string, std::string> sourceNameAndType;
315 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
316 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
317 const auto rule = *itr;
318
319 // Erase unknown rule types
320 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
322 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
323 itr = rules.erase(itr);
324 continue;
325 }
326
327 bool hasConflictingSourceMembers = false;
328 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
329 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
330 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
331 if (!isNew && (itrSrc->second != memberType)) {
333 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
334 << memberType << " for member " << source->GetName();
336 break;
337 }
338 }
340 itr = rules.erase(itr);
341 continue;
342 }
343
344 // Rules targeting the entire object need to be executed at the end
345 if (rule->GetTarget() == nullptr) {
346 nskip++;
347 if (itr != rules.end() - nskip)
348 std::iter_swap(itr++, rules.end() - nskip);
349 continue;
350 }
351
352 ++itr;
353 }
354
355 return rules;
356}
357
358std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
359{
360 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
361}
362
363std::size_t ROOT::RClassField::AppendImpl(const void *from)
364{
365 std::size_t nbytes = 0;
366 for (unsigned i = 0; i < fSubfields.size(); i++) {
367 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
368 }
369 return nbytes;
370}
371
373{
374 for (const auto &[_, si] : fStagingItems) {
375 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
376 }
377 for (unsigned i = 0; i < fSubfields.size(); i++) {
378 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
379 }
380}
381
383{
384 for (const auto &[_, si] : fStagingItems) {
385 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
386 }
387 for (unsigned i = 0; i < fSubfields.size(); i++) {
388 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
389 }
390}
391
394{
397 return idSourceMember;
398
399 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
400 const auto subFieldName = subFieldDesc.GetFieldName();
401 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
402 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
404 return idSourceMember;
405 }
406 }
407
409}
410
411void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
412{
413 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
414 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
415 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
416 if (!fStagingClass) {
417 // For a rename rule, we may simply ask for the old class name
418 fStagingClass = TClass::GetClass(className.c_str());
419 }
420 } else {
421 fStagingClass = fClass;
422 }
423 R__ASSERT(fStagingClass);
424 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
425}
426
427void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
428 const ROOT::RNTupleDescriptor &desc,
430{
431 std::size_t stagingAreaSize = 0;
432 for (const auto rule : rules) {
433 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
434 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
435 if (!isNew) {
436 // This source member has already been processed by another rule (and we only support one type per member)
437 continue;
438 }
439 RStagingItem &stagingItem = itr->second;
440
441 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
443 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
444 source->GetName()));
445 }
446
447 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
448 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
449 memberField->SetOnDiskId(memberFieldId);
450 auto fieldZero = std::make_unique<RFieldZero>();
452 fieldZero->Attach(std::move(memberField));
453 stagingItem.fField = std::move(fieldZero);
454
455 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
456 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
457 // to be present in the TClass instance, too.
459 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
460 }
461 }
462
463 if (stagingAreaSize) {
464 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
465 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
466 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
467
468 for (const auto &[_, si] : fStagingItems) {
469 const auto &memberField = *si.fField->cbegin();
470 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
471 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
472 }
473 }
474 }
475}
476
478{
479 auto func = rule->GetReadFunctionPointer();
480 if (func == nullptr) {
481 // Can happen for rename rules
482 return;
483 }
484 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
485 TVirtualObject onfileObj{nullptr};
486 onfileObj.fClass = stagingClass;
487 onfileObj.fObject = stagingArea;
488 func(static_cast<char *>(target), &onfileObj);
489 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
490 });
491}
492
494{
495 std::vector<const TSchemaRule *> rules;
496 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
497 // will be marked as artificial (added member in a new class version or member set by rule).
498 std::unordered_set<std::string> regularSubfields;
499 // We generally don't support changing the number of base classes, with the exception of changing from/to zero
500 // base classes. The variable stores the number of on-disk base classes.
501 int nOnDiskBaseClasses = 0;
502
503 if (GetOnDiskId() == kInvalidDescriptorId) {
504 // This can happen for added base classes or added members of class type
505 rules = FindRules(nullptr);
506 if (!rules.empty())
507 SetStagingClass(GetTypeName(), GetTypeVersion());
508 } else {
509 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
510 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
511 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
512
513 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
514 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
515 // by a streamer field to read the data from disk.
516 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
517 substitute->SetOnDiskId(GetOnDiskId());
518 return substitute;
519 }
520
521 for (auto linkId : fieldDesc.GetLinkIds()) {
522 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
523 regularSubfields.insert(subFieldDesc.GetFieldName());
524 if (!subFieldDesc.GetFieldName().empty() && subFieldDesc.GetFieldName()[0] == ':')
526 }
527
528 rules = FindRules(&fieldDesc);
529
530 // 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
531 // (source) type name and version/checksum.
532 if (rules.empty()) {
533 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
534 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
535 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
537 if (GetTypeName() != descTypeName) {
538 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
539 " vs. " + descTypeName));
540 }
541 }
542
543 const bool hasSources = std::any_of(rules.begin(), rules.end(), [](const auto &r) {
544 return r->GetSource() && (r->GetSource()->GetEntries() > 0);
545 });
546
547 // A staging class (conversion streamer info) only exists if there is at least one rule that has an
548 // on disk source member defined.
549 if (hasSources) {
550 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
551 PrepareStagingArea(rules, desc, fieldDesc);
552 for (auto &[_, si] : fStagingItems) {
554 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
555 }
556 }
557
558 // Remove target member of read rules from the list of regular members of the underlying on-disk field
559 for (const auto rule : rules) {
560 if (!rule->GetTarget())
561 continue;
562
563 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
564 regularSubfields.erase(std::string(target->GetString()));
565 }
566 }
567 }
568
569 for (const auto rule : rules) {
570 AddReadCallbacksFromIORule(rule);
571 }
572
573 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
574 int nInMemoryBaseClasses = 0;
575 for (auto &field : fSubfields) {
576 const auto &fieldName = field->GetFieldName();
577 if (regularSubfields.count(fieldName) == 0) {
578 CallSetArtificialOn(*field);
579 }
580 if (!fieldName.empty() && fieldName[0] == ':')
582 }
583
585 throw RException(R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " +
586 GetTypeName() + ", " + std::to_string(nInMemoryBaseClasses) +
587 " base classes in memory "
588 " vs. " +
589 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
590 Internal::GetTypeTraceReport(*this, pageSource.GetSharedDescriptorGuard().GetRef())));
591 }
592
593 return nullptr;
594}
595
597{
598 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
599}
600
602{
603 fClass->New(where);
604}
605
607{
608 fClass->Destructor(objPtr, true /* dtorOnly */);
609 RDeleter::operator()(objPtr, dtorOnly);
610}
611
612std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
613{
614 std::vector<RValue> result;
615 auto valuePtr = value.GetPtr<void>();
616 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
617 result.reserve(fSubfields.size());
618 for (unsigned i = 0; i < fSubfields.size(); i++) {
619 result.emplace_back(
620 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
621 }
622 return result;
623}
624
626{
627 return fClass->GetClassSize();
628}
629
631{
632 return fClass->GetClassVersion();
633}
634
636{
637 return fClass->GetCheckSum();
638}
639
640const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
641{
642 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
643 if (!polymorphic) {
644 return nullptr;
645 }
646 return fClass->GetTypeInfo();
647}
648
650{
651 visitor.VisitClassField(*this);
652}
653
654//------------------------------------------------------------------------------
655
657 : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kCollection, false /* isSimple */),
658 fSoAClass(source.fSoAClass),
659 fSoAMemberOffsets(source.fSoAMemberOffsets),
660 fMaxAlignment(source.fMaxAlignment)
661{
662 fTraits = source.GetTraits();
663 Attach(source.fSubfields[0]->Clone(source.fSubfields[0]->GetFieldName()));
664 fRecordMemberFields = fSubfields[0]->GetMutableSubfields();
666 for (const auto f : fRecordMemberFields)
667 fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
668 fLockSplitFields = std::make_unique<std::mutex>();
669}
670
671ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, std::string_view className)
673{
674}
675
677 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(clSoA->GetName()), ROOT::ENTupleStructure::kCollection,
678 false /* isSimple */),
679 fSoAClass(clSoA)
680{
681 static std::once_flag once;
682 std::call_once(once, []() {
683 R__LOG_WARNING(ROOT::Internal::NTupleLog()) << "The SoA field is experimental and still under development.";
684 });
685
686 EnsureValidUserClass(fSoAClass, *this, "RSoAField");
688 if (recordTypeName.empty()) {
689 throw ROOT::RException(R__FAIL(std::string("class ") + GetTypeName() +
690 " is not marked with the rntupleSoARecord "
691 "dictionary option; cannot create corresponding RSoAField."));
692 }
693 try {
694 Attach(std::make_unique<ROOT::RClassField>("_0", recordTypeName));
695 } catch (ROOT::RException &e) {
696 throw RException(R__FAIL("invalid record type of SoA field " + GetTypeName() + " [" + e.what() + "]"));
697 }
699 if (static_cast<std::uint32_t>(fSoAClass->GetClassVersion()) != fSubfields[0]->GetTypeVersion()) {
700 throw RException(R__FAIL(std::string("version mismatch between SoA type and underlying record type: ") +
701 std::to_string(fSoAClass->GetClassVersion()) + " vs. " +
702 std::to_string(fSubfields[0]->GetTypeVersion())));
703 }
704 fRecordMemberFields = fSubfields[0]->GetMutableSubfields();
705
706 std::unordered_map<std::string, std::size_t> recordFieldNameToIdx;
709 for (std::size_t i = 0; i < fRecordMemberFields.size(); ++i) {
710 const RFieldBase *f = fRecordMemberFields[i];
711 assert(!f->GetFieldName().empty());
712 if (f->GetFieldName()[0] == ':') {
713 throw RException(R__FAIL("SoA fields with inheritance are currently unsupported"));
714 }
715 recordFieldNameToIdx[f->GetFieldName()] = i;
716 fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
717 }
718
719 const auto *bases = fSoAClass->GetListOfBases();
720 assert(bases);
722 if (baseClass->GetDelta() < 0) {
723 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
724 " virtually inherits from " + baseClass->GetName()));
725 }
726 // At a later point, we will support inheritance
727 throw RException(R__FAIL("SoA fields with inheritance are currently unsupported"));
728 }
729
731 unsigned int nMembers = 0;
733 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
734 continue;
735
736 if (dataMember->Property() & kIsArray) {
737 throw RException(R__FAIL(std::string("unsupported array type in SoA class: ") + dataMember->GetName()));
738 }
739
740 const std::string typeName{dataMember->GetTrueTypeName()};
741 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
742 auto vecFieldPtr = dynamic_cast<RRVecField *>(subField.get());
743 if (!vecFieldPtr) {
744 throw RException(R__FAIL("invalid field type in SoA class: " + subField->GetTypeName()));
745 }
746 subField.release();
747 auto vecField = std::unique_ptr<RRVecField>(vecFieldPtr);
748
749 auto itr = recordFieldNameToIdx.find(vecField->GetFieldName());
750 if (itr == recordFieldNameToIdx.end()) {
751 throw RException(R__FAIL(std::string("unexpected SoA member: ") + vecField->GetFieldName()));
752 }
754 if (vecField->begin()->GetTypeName() != memberField->GetTypeName() ||
755 vecField->begin()->GetTypeAlias() != memberField->GetTypeAlias()) {
756 const std::string leftType =
757 vecField->begin()->GetTypeName() +
758 (vecField->begin()->GetTypeAlias().empty() ? "" : " [" + vecField->begin()->GetTypeAlias() + "]");
759 const std::string rightType =
760 memberField->GetTypeName() +
761 (memberField->GetTypeAlias().empty() ? "" : " [" + memberField->GetTypeAlias() + "]");
762 throw RException(R__FAIL(std::string("SoA member type mismatch: ") + vecField->GetFieldName() + " (" +
763 leftType + " vs. " + rightType + ")"));
764 }
765
766 fMaxAlignment = std::max(fMaxAlignment, vecField->GetAlignment());
767
768 assert(itr->second < fSoAMemberOffsets.size());
769 fSoAMemberOffsets[itr->second] = dataMember->GetOffset();
770 nMembers++;
771 }
772 if (recordFieldNameToIdx.size() != nMembers) {
773 throw RException(R__FAIL("missing SoA members"));
774 }
775
776 std::string renormalizedAlias;
779
781 fLockSplitFields = std::make_unique<std::mutex>();
782}
783
784std::unique_ptr<ROOT::RFieldBase> ROOT::Experimental::RSoAField::CloneImpl(std::string_view newName) const
785{
786 return std::unique_ptr<RSoAField>(new RSoAField(newName, *this));
787}
788
798
803
808
809std::size_t ROOT::Experimental::RSoAField::AppendImpl(const void *from)
810{
811 const std::size_t nSoAMembers = fSoAMemberOffsets.size();
812
813 std::size_t N = 0; // Set by first SoA member and verified for the rest
814 for (std::size_t i = 0; i < nSoAMembers; ++i) {
815 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
817 assert(*sizePtr >= 0);
818 if (i == 0) {
819 N = *sizePtr;
820 } else {
821 if (static_cast<std::size_t>(*sizePtr) != N) {
822 const auto f = fRecordMemberFields[i];
823 throw RException(R__FAIL("SoA length mismatch for " + f->GetFieldName() + ": " + std::to_string(*sizePtr) +
824 " vs. " + std::to_string(N) + " (expected)"));
825 }
826 }
827 }
828
829 std::size_t nbytes = 0;
830 if (N > 0) {
831 for (std::size_t i = 0; i < nSoAMembers; ++i) {
832 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
834 RFieldBase *memberField = fRecordMemberFields[i];
835 if (memberField->IsSimple()) {
836 GetPrincipalColumnOf(*memberField)->AppendV(*beginPtr, N);
837 nbytes += N * GetPrincipalColumnOf(*memberField)->GetElement()->GetPackedSize();
838 } else {
839 for (std::size_t j = 0; j < N; ++j) {
840 nbytes += CallAppendOn(*memberField, *beginPtr + j * memberField->GetValueSize());
841 }
842 }
843 }
844 }
845
846 fNWritten += N;
847 fPrincipalColumn->Append(&fNWritten);
848 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
849}
850
852{
853 // Read collection info for this entry
856 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &N);
857
858 const auto nSoAMembers = fSoAMemberOffsets.size();
859 for (std::size_t i = 0; i < nSoAMembers; ++i) {
860 RFieldBase *memberField = fRecordMemberFields[i];
861 const auto memberSize = memberField->GetValueSize();
862 void *rvecPtr = static_cast<unsigned char *>(to) + fSoAMemberOffsets[i];
863 auto begin = ROOT::RRVecField::ResizeRVec(rvecPtr, N, memberSize, memberField, fRecordMemberDeleters[i].get());
864
865 if (memberField->IsSimple() && N) {
866 GetPrincipalColumnOf(*memberField)->ReadV(collectionStart, N, begin);
867 } else {
868 for (std::size_t j = 0; j < N; ++j) {
869 CallReadOn(*memberField, collectionStart + j, begin + (j * memberSize));
870 }
871 }
872 }
873}
874
876{
877 fSoAClass->New(where);
878}
879
881{
882 fSoAClass->Destructor(objPtr, true /* dtorOnly */);
883 RDeleter::operator()(objPtr, dtorOnly);
884}
885
886std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(const RValue &value) const
887{
888 const auto nSoAMembers = fSoAMemberOffsets.size();
889
890 {
891 std::lock_guard<std::mutex> lockGuard(*fLockSplitFields);
892 if (!fSplitFields) {
893 fSplitFields = std::make_unique<std::vector<std::unique_ptr<ROOT::RRVecField>>>();
894 fSplitFields->reserve(nSoAMembers);
895 for (std::size_t i = 0; i < nSoAMembers; ++i) {
896 const auto itemField = fRecordMemberFields[i];
897 fSplitFields->emplace_back(std::make_unique<RRVecField>(itemField->GetFieldName(), itemField->Clone("_0")));
898 }
899 }
900 }
901
902 auto valuePtr = value.GetPtr<void>();
903 auto soaPtr = static_cast<unsigned char *>(valuePtr.get());
904 std::vector<RValue> values;
905 values.reserve(nSoAMembers);
906 for (std::size_t i = 0; i < nSoAMembers; ++i) {
907 values.emplace_back(
908 (*fSplitFields)[i]->BindValue(std::shared_ptr<void>(valuePtr, soaPtr + fSoAMemberOffsets[i])));
909 }
910 return values;
911}
912
914{
915 return fSoAClass->GetClassSize();
916}
917
919{
920 return fSoAClass->GetClassVersion();
921}
922
924{
925 return fSoAClass->GetCheckSum();
926}
927
929{
930 // TODO(jblomer): factor out
931 bool polymorphic = fSoAClass->ClassProperty() & kClassHasVirtual;
932 if (!polymorphic) {
933 return nullptr;
934 }
935 return fSoAClass->GetTypeInfo();
936}
937
942
943//------------------------------------------------------------------------------
944
945ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
947{
948}
949
951 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
952 false /* isSimple */)
953{
954 // Avoid accidentally supporting std types through TEnum.
955 if (enump->Property() & kIsDefinedInStd) {
956 throw RException(R__FAIL(GetTypeName() + " is not supported"));
957 }
958
959 switch (enump->GetUnderlyingType()) {
960 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
961 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
962 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
963 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
964 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
965 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
966 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
967 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
968 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
969 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
970 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
971 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
972 }
973
975}
976
977ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
978 std::unique_ptr<RFieldBase> intField)
980{
981 Attach(std::move(intField));
983}
984
985std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
986{
987 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
988 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
989}
990
992{
993 // TODO(jblomer): allow enum to enum conversion only by rename rule
994 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
995}
996
997std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
998{
999 std::vector<RValue> result;
1000 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
1001 return result;
1002}
1003
1005{
1006 visitor.VisitEnumField(*this);
1007}
1008
1009//------------------------------------------------------------------------------
1010
1011ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
1012 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1013{
1014 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1015 if (typeAlias != GetTypeName())
1017
1018 AttachItemFields(std::move(itemFields));
1019
1020 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
1021 auto *c = TClass::GetClass(GetTypeName().c_str());
1022 if (!c)
1023 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1024 fSize = c->Size();
1025
1026 auto firstElem = c->GetRealData("first");
1027 if (!firstElem)
1028 throw RException(R__FAIL("first: no such member"));
1029 fOffsets.push_back(firstElem->GetThisOffset());
1030
1031 auto secondElem = c->GetRealData("second");
1032 if (!secondElem)
1033 throw RException(R__FAIL("second: no such member"));
1034 fOffsets.push_back(secondElem->GetThisOffset());
1035}
1036
1037std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
1038{
1039 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
1040 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
1041 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones)));
1042}
1043
1045{
1046 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1047
1048 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1049 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1050
1051 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1052 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1053 if (nOnDiskSubfields != 2) {
1054 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
1055 std::to_string(nOnDiskSubfields) + "\n" +
1056 Internal::GetTypeTraceReport(*this, desc)));
1057 }
1058}
1059
1060//------------------------------------------------------------------------------
1061
1064 bool readFromDisk)
1065{
1067 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
1068 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
1069 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
1070 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
1071 (ifuncs.fNext != nullptr));
1072 return ifuncs;
1073}
1074
1076 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
1077 false /* isSimple */),
1078 fNWritten(0)
1079{
1080 if (!classp->GetCollectionProxy())
1081 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
1082 if (classp->Property() & kIsDefinedInStd) {
1083 static const std::vector<std::string> supportedStdTypes = {
1084 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
1085 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
1086 bool isSupported = false;
1087 for (const auto &tn : supportedStdTypes) {
1088 if (GetTypeName().rfind(tn, 0) == 0) {
1089 isSupported = true;
1090 break;
1091 }
1092 }
1093 if (!isSupported)
1094 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
1095 }
1096
1097 std::string renormalizedAlias;
1100
1101 fProxy.reset(classp->GetCollectionProxy()->Generate());
1102 fProperties = fProxy->GetProperties();
1103 fCollectionType = fProxy->GetCollectionType();
1104 if (fProxy->HasPointers())
1105 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
1106
1107 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
1108 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1109}
1110
1111ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
1113{
1114 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
1116 throw RException(R__FAIL("custom associative collection proxies not supported"));
1117
1118 std::unique_ptr<ROOT::RFieldBase> itemField;
1119
1120 if (auto valueClass = fProxy->GetValueClass()) {
1121 // Element type is a class
1122 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
1123 } else {
1124 switch (fProxy->GetType()) {
1125 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
1126 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
1127 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
1128 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
1129 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
1130 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
1131 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
1132 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
1133 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
1134 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
1135 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
1136 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
1137 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
1138 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
1139 }
1140 }
1141
1142 fItemSize = itemField->GetValueSize();
1143 Attach(std::move(itemField));
1144}
1145
1146std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
1147{
1148 auto clone =
1149 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
1150 clone->fItemSize = fItemSize;
1151 clone->Attach(fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1152 return clone;
1153}
1154
1156{
1157 std::size_t nbytes = 0;
1158 unsigned count = 0;
1159 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
1160 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
1161 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1162 nbytes += CallAppendOn(*fSubfields[0], ptr);
1163 count++;
1164 }
1165
1166 fNWritten += count;
1167 fPrincipalColumn->Append(&fNWritten);
1168 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1169}
1170
1172{
1175 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1176
1177 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
1178 void *obj =
1179 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
1180
1181 unsigned i = 0;
1182 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
1183 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
1184 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
1185 }
1186 if (obj != to)
1187 fProxy->Commit(obj);
1188}
1189
1199
1204
1209
1211{
1212 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1213}
1214
1216{
1217 fProxy->New(where);
1218}
1219
1220std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
1221{
1222 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
1223 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
1224 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
1225 }
1226 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
1227}
1228
1230{
1231 if (fItemDeleter) {
1233 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
1234 fItemDeleter->operator()(ptr, true /* dtorOnly */);
1235 }
1236 }
1237 fProxy->Destructor(objPtr, true /* dtorOnly */);
1238 RDeleter::operator()(objPtr, dtorOnly);
1239}
1240
1241std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
1242{
1243 std::vector<RValue> result;
1244 auto valueRawPtr = value.GetPtr<void>().get();
1246 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
1247 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1248 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
1249 }
1250 return result;
1251}
1252
1254{
1255 visitor.VisitProxiedCollectionField(*this);
1256}
1257
1258//------------------------------------------------------------------------------
1259
1260ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
1262 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
1263 fMapType(mapType)
1264{
1265 if (!itemField->GetTypeAlias().empty())
1266 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
1267
1268 auto *itemClass = fProxy->GetValueClass();
1269 fItemSize = itemClass->GetClassSize();
1270
1271 Attach(std::move(itemField), "_0");
1272}
1273
1274std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
1275{
1276 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1277}
1278
1280{
1281 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
1282
1283 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1284
1285 switch (fMapType) {
1286 case EMapType::kMap:
1287 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1288 default:
1289 break;
1290 // no restrictions for multimaps
1291 }
1292}
1293
1294//------------------------------------------------------------------------------
1295
1296ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1298 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1299 fSetType(setType)
1300{
1301 if (!itemField->GetTypeAlias().empty())
1302 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1303
1304 fItemSize = itemField->GetValueSize();
1305
1306 Attach(std::move(itemField), "_0");
1307}
1308
1309std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1310{
1311 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1312}
1313
1315{
1316 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1317 "std::unordered_map<"};
1318
1319 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1320
1321 switch (fSetType) {
1322 case ESetType::kSet:
1323 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1324 default:
1325 break;
1326 // no restrictions for multisets
1327 }
1328}
1329
1330//------------------------------------------------------------------------------
1331
1332namespace {
1333
1334/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1335class TBufferRecStreamer : public TBufferFile {
1336public:
1337 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1338
1339private:
1340 RCallbackStreamerInfo fCallbackStreamerInfo;
1341
1342public:
1343 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1344 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
1345 {
1346 }
1347 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1348};
1349
1350} // anonymous namespace
1351
1352ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1354{
1355}
1356
1358 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1359 false /* isSimple */),
1360 fClass(classp),
1361 fIndex(0)
1362{
1363 std::string renormalizedAlias;
1366
1368 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1369 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1370 // implicit constructors and destructors.
1375}
1376
1377std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1378{
1379 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName()));
1380}
1381
1382std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1383{
1384 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1385 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1386 fClass->Streamer(const_cast<void *>(from), buffer);
1387
1388 auto nbytes = buffer.Length();
1389 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1390 fIndex += nbytes;
1391 fPrincipalColumn->Append(&fIndex);
1392 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1393}
1394
1396{
1399 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1400
1402 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1403 fClass->Streamer(to, buffer);
1404}
1405
1415
1420
1425
1427{
1428 source.RegisterStreamerInfos();
1429 return nullptr;
1430}
1431
1433{
1434 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1435}
1436
1438{
1439 fClass->New(where);
1440}
1441
1443{
1444 fClass->Destructor(objPtr, true /* dtorOnly */);
1445 RDeleter::operator()(objPtr, dtorOnly);
1446}
1447
1449{
1452 .TypeVersion(GetTypeVersion())
1453 .TypeName(GetTypeName())
1455 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
1456}
1457
1459{
1460 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1461}
1462
1464{
1465 return fClass->GetClassSize();
1466}
1467
1469{
1470 return fClass->GetClassVersion();
1471}
1472
1474{
1475 return fClass->GetCheckSum();
1476}
1477
1479{
1480 visitor.VisitStreamerField(*this);
1481}
1482
1483//------------------------------------------------------------------------------
1484
1486{
1487 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1488 return dataMember->GetOffset();
1489 }
1490 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1491}
1492
1494 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1495{
1497 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1498 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1499}
1500
1502 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1503{
1504 assert(TObject::Class()->GetClassVersion() == 1);
1505
1507 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1508 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1509}
1510
1511std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1512{
1513 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1514}
1515
1516std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1517{
1518 // Cf. TObject::Streamer()
1519
1520 auto *obj = static_cast<const TObject *>(from);
1521 if (obj->TestBit(TObject::kIsReferenced)) {
1522 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1523 }
1524
1525 std::size_t nbytes = 0;
1526 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1527
1528 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1529 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1530 nbytes += CallAppendOn(*fSubfields[1], &bits);
1531
1532 return nbytes;
1533}
1534
1536{
1537 // Cf. TObject::Streamer()
1538
1539 auto *obj = static_cast<TObject *>(to);
1540 if (obj->TestBit(TObject::kIsReferenced)) {
1541 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1542 }
1543
1544 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1545
1546 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1548 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1549}
1550
1552{
1553 UInt_t uniqueID, bits;
1554 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1555 CallReadOn(*fSubfields[1], globalIndex, &bits);
1556 ReadTObject(to, uniqueID, bits);
1557}
1558
1560{
1561 UInt_t uniqueID, bits;
1562 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1563 CallReadOn(*fSubfields[1], localIndex, &bits);
1564 ReadTObject(to, uniqueID, bits);
1565}
1566
1568{
1569 return TObject::Class()->GetClassVersion();
1570}
1571
1573{
1574 return TObject::Class()->GetCheckSum();
1575}
1576
1578{
1579 new (where) TObject();
1580}
1581
1582std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1583{
1584 std::vector<RValue> result;
1585 // Use GetPtr<TObject> to type-check
1586 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1587 auto charPtr = static_cast<unsigned char *>(ptr.get());
1588 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1589 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1590 return result;
1591}
1592
1594{
1595 return sizeof(TObject);
1596}
1597
1599{
1600 return alignof(TObject);
1601}
1602
1604{
1605 visitor.VisitTObjectField(*this);
1606}
1607
1608//------------------------------------------------------------------------------
1609
1610ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1611 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1612{
1613 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1614 if (typeAlias != GetTypeName())
1616
1617 AttachItemFields(std::move(itemFields));
1618
1619 auto *c = TClass::GetClass(GetTypeName().c_str());
1620 if (!c)
1621 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1622 fSize = c->Size();
1623
1624 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1625 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1626 // following the order of the type list.
1627 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1628 // members, the assertion below will fail.
1629 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1630 std::string memberName("_" + std::to_string(i));
1631 auto member = c->GetRealData(memberName.c_str());
1632 if (!member)
1633 throw RException(R__FAIL(memberName + ": no such member"));
1634 fOffsets.push_back(member->GetThisOffset());
1635 }
1636}
1637
1638std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1639{
1640 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1641 itemClones.reserve(fSubfields.size());
1642 for (const auto &f : fSubfields) {
1643 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1644 }
1645 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones)));
1646}
1647
1649{
1650 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1651
1652 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1653 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1654
1655 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1656 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1657 const auto nSubfields = fSubfields.size();
1659 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1660 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1661 Internal::GetTypeTraceReport(*this, desc)));
1662 }
1663}
1664
1665//------------------------------------------------------------------------------
1666
1667namespace {
1668
1669// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1670constexpr std::size_t GetVariantTagSize()
1671{
1672 // Should be all zeros except for the tag, which is 1
1673 std::variant<char> t;
1674 constexpr auto sizeOfT = sizeof(t);
1675
1676 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1677 return sizeOfT == 2 ? 1 : 4;
1678}
1679
1680template <std::size_t VariantSizeT>
1681struct RVariantTag {
1682 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1683 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1684};
1685
1686} // anonymous namespace
1687
1689 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1690 fMaxItemSize(source.fMaxItemSize),
1691 fMaxAlignment(source.fMaxAlignment),
1692 fTagOffset(source.fTagOffset),
1693 fVariantOffset(source.fVariantOffset),
1694 fNWritten(source.fNWritten.size(), 0)
1695{
1696 for (const auto &f : source.GetConstSubfields())
1697 Attach(f->Clone(f->GetFieldName()));
1698 fTraits = source.fTraits;
1699}
1700
1701ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1702 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1703 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1704{
1705 // The variant needs to initialize its own tag member
1707
1708 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1709 if (typeAlias != GetTypeName())
1711
1712 auto nFields = itemFields.size();
1713 if (nFields == 0 || nFields > kMaxVariants) {
1714 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1715 }
1716 fNWritten.resize(nFields, 0);
1717 for (unsigned int i = 0; i < nFields; ++i) {
1720 fTraits &= itemFields[i]->GetTraits();
1721 Attach(std::move(itemFields[i]), "_" + std::to_string(i));
1722 }
1723
1724 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1725 // For instance, std::variant<std::optional<int>> on macOS.
1726 auto cl = TClass::GetClass(GetTypeName().c_str());
1727 assert(cl);
1728 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1729 if (dm)
1730 fVariantOffset = dm->GetOffset();
1731
1732 const auto tagSize = GetVariantTagSize();
1733 const auto padding = tagSize - (fMaxItemSize % tagSize);
1735}
1736
1737std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1738{
1739 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1740}
1741
1742std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1743{
1744 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1745 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1746 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1747}
1748
1749void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1750{
1751 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1752 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1753 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1754}
1755
1756std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1757{
1758 auto tag = GetTag(from, fTagOffset);
1759 std::size_t nbytes = 0;
1760 auto index = 0;
1761 if (tag > 0) {
1762 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1763 index = fNWritten[tag - 1]++;
1764 }
1766 fPrincipalColumn->Append(&varSwitch);
1767 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1768}
1769
1771{
1773 std::uint32_t tag;
1774 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1775 R__ASSERT(tag < 256);
1776
1777 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1778 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1779 // any `std::holds_alternative<T>` check fail later.
1780 if (R__likely(tag > 0)) {
1781 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1782 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1783 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1784 }
1785 SetTag(to, fTagOffset, tag);
1786}
1787
1793
1798
1803
1805{
1806 static const std::vector<std::string> prefixes = {"std::variant<"};
1807
1808 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1809 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1810
1811 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1812 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1813 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
1814 Internal::GetTypeTraceReport(*this, desc)));
1815 }
1816}
1817
1819{
1820 memset(where, 0, GetValueSize());
1821 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1822 SetTag(where, fTagOffset, 1);
1823}
1824
1826{
1827 auto tag = GetTag(objPtr, fTagOffset);
1828 if (tag > 0) {
1829 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1830 }
1831 RDeleter::operator()(objPtr, dtorOnly);
1832}
1833
1834std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1835{
1836 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1837 itemDeleters.reserve(fSubfields.size());
1838 for (const auto &f : fSubfields) {
1839 itemDeleters.emplace_back(GetDeleterOf(*f));
1840 }
1841 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1842}
1843
1845{
1846 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1847}
1848
1850{
1851 const auto alignment = GetAlignment();
1852 const auto actualSize = fTagOffset + GetVariantTagSize();
1853 const auto padding = alignment - (actualSize % alignment);
1854 return actualSize + ((padding == alignment) ? 0 : padding);
1855}
1856
1858{
1859 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1860}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:587
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:299
#define R__LOG_WARNING(...)
Definition RLogger.hxx:357
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kLong_t
Definition TDataType.h:30
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kUInt_t
Definition TDataType.h:30
@ kClassHasExplicitCtor
@ kClassHasImplicitCtor
@ kClassHasVirtual
@ kClassHasExplicitDtor
@ kClassHasImplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char mode
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:148
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:108
Abstract base class for classes implementing the visitor design pattern.
void operator()(void *objPtr, bool dtorOnly) final
The SoA field provides I/O for an in-memory SoA layout linked to an on-disk collection of the underly...
Definition RFieldSoA.hxx:55
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::vector< std::size_t > fSoAMemberOffsets
The offset of the RVec members in the SoA type in the order of subfields of the underlying record typ...
Definition RFieldSoA.hxx:69
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
std::vector< std::unique_ptr< RDeleter > > fRecordMemberDeleters
Definition RFieldSoA.hxx:71
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
std::vector< RFieldBase * > fRecordMemberFields
Direct access to the member fields of the underlying record.
Definition RFieldSoA.hxx:66
RSoAField(std::string_view fieldName, const RSoAField &source)
Used by CloneImpl.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::unique_ptr< std::mutex > fLockSplitFields
protects the fSplitFields member.
Definition RFieldSoA.hxx:80
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
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.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
void Attach(std::unique_ptr< RFieldBase > child, RSubfieldInfo info)
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:223
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.
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.
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:290
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.
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
void Attach(std::unique_ptr< RFieldBase > child, std::string_view expectedChildName="")
Add a new subfield to the list of nested fields.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitTriviallyConstructible
No constructor needs to be called, i.e.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
const std::string & GetTypeName() const
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:320
RField(std::string_view name)
Definition RField.hxx:323
RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr< RFieldBase > itemField)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
The on-storage metadata of an RNTuple.
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
Template specializations for C++ std::pair.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RPairField(std::string_view fieldName, std::array< std::unique_ptr< RFieldBase >, 2 > itemFields)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Allows for iterating over the elements of a proxied collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
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 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::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:235
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 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
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)
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.
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:2326
EState GetState() const
Definition TClass.h:504
void BuildRealData(void *pointer=nullptr, Bool_t isTransient=kFALSE)
Build a full list of persistent data members.
Definition TClass.cxx:2038
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3828
TList * GetListOfRealData() const
Definition TClass.h:468
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5806
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3694
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2918
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2403
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6191
@ kInterpreted
Definition TClass.h:129
Version_t GetClassVersion() const
Definition TClass.h:434
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2994
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
The TEnum class implements the enum type.
Definition TEnum.h:33
static TEnum * GetEnum(const std::type_info &ti, ESearchAction sa=kALoadAndInterpLookup)
Definition TEnum.cxx:181
TObject * First() const override
Return the first object in the list. Returns 0 when list is empty.
Definition TList.cxx:789
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Mother of all ROOT objects.
Definition TObject.h:42
@ kIsOnHeap
object is on heap
Definition TObject.h:90
@ kNotDeleted
object has not been deleted
Definition TObject.h:91
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:74
The TRealData class manages the effective list of all data members for a given class.
Definition TRealData.h:30
RAII helper class that ensures that PushProxy() / PopProxy() are called when entering / leaving a C++...
Defines a common interface to inspect/change the contents of an object that represents a collection.
@ kNeedDelete
The collection contains directly or indirectly (via other collection) some pointers that need explici...
Abstract Interface class describing Streamer information for one class.
const Int_t n
Definition legend1.C:16
TRangeCast< T, false > TRangeStaticCast
TRangeStaticCast is an adapter class that allows the typed iteration through a TCollection.
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:35
std::tuple< unsigned char **, std::int32_t *, std::int32_t * > GetRVecDataMembers(void *rvecPtr)
Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the R...
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
std::string GetRNTupleSoARecord(const TClass *cl)
Checks if the "rntuple.SoARecord" class attribute is set in the dictionary.
bool NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias, bool isArgInTemplatedUserClass=false)
Checks if the meta normalized name is different from the RNTuple normalized name in a way that would ...
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
ERNTupleSerializationMode GetRNTupleSerializationMode(const TClass *cl)
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
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.