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