Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldMeta.cxx
Go to the documentation of this file.
1/// \file RFieldMeta.cxx
2/// \ingroup NTuple
3/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
4/// \date 2024-11-19
5
6// This file has concrete RField implementations that depend on ROOT Meta:
7// - RClassField
8// - REnumField
9// - RPairField
10// - RProxiedCollectionField
11// - RMapField
12// - RSetField
13// - RStreamerField
14// - RField<TObject>
15// - RVariantField
16
17#include <ROOT/RField.hxx>
18#include <ROOT/RFieldBase.hxx>
19#include <ROOT/RFieldUtils.hxx>
21#include <ROOT/RNTupleUtils.hxx>
22#include <ROOT/RSpan.hxx>
23
24#include <TBaseClass.h>
25#include <TBufferFile.h>
26#include <TClass.h>
27#include <TClassEdit.h>
28#include <TDataMember.h>
29#include <TEnum.h>
30#include <TObject.h>
31#include <TObjArray.h>
32#include <TObjString.h>
33#include <TRealData.h>
34#include <TSchemaRule.h>
35#include <TSchemaRuleSet.h>
36#include <TStreamerElement.h>
37#include <TVirtualObject.h>
39
40#include <algorithm>
41#include <array>
42#include <cstddef> // std::size_t
43#include <cstdint> // std::uint32_t et al.
44#include <cstring> // for memset
45#include <memory>
46#include <string>
47#include <string_view>
48#include <unordered_set>
49#include <utility>
50#include <variant>
51
53
54namespace {
55
56TClass *EnsureValidClass(std::string_view className)
57{
58 auto cl = TClass::GetClass(std::string(className).c_str());
59 if (cl == nullptr) {
60 throw ROOT::RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
61 }
62 return cl;
63}
64
65TEnum *EnsureValidEnum(std::string_view enumName)
66{
67 auto e = TEnum::GetEnum(std::string(enumName).c_str());
68 if (e == nullptr) {
69 throw ROOT::RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
70 }
71 return e;
72}
73
75{
76 std::string typePrefix;
77 switch (setType) {
78 case ROOT::RSetField::ESetType::kSet: typePrefix = "std::set<"; break;
79 case ROOT::RSetField::ESetType::kUnorderedSet: typePrefix = "std::unordered_set<"; break;
80 case ROOT::RSetField::ESetType::kMultiSet: typePrefix = "std::multiset<"; break;
81 case ROOT::RSetField::ESetType::kUnorderedMultiSet: typePrefix = "std::unordered_multiset<"; break;
82 default: R__ASSERT(false);
83 }
84 return typePrefix + innerField.GetTypeName() + ">";
85}
86
88{
89 if (const auto pairField = dynamic_cast<const ROOT::RPairField *>(innerField)) {
90 std::string typePrefix;
91 switch (mapType) {
92 case ROOT::RMapField::EMapType::kMap: typePrefix = "std::map<"; break;
93 case ROOT::RMapField::EMapType::kUnorderedMap: typePrefix = "std::unordered_map<"; break;
94 case ROOT::RMapField::EMapType::kMultiMap: typePrefix = "std::multimap<"; break;
95 case ROOT::RMapField::EMapType::kUnorderedMultiMap: typePrefix = "std::unordered_multimap<"; break;
96 default: R__ASSERT(false);
97 }
98 auto subFields = pairField->GetConstSubfields();
99 return typePrefix + subFields[0]->GetTypeName() + "," + subFields[1]->GetTypeName() + ">";
100 }
101
102 throw ROOT::RException(R__FAIL("RMapField inner field type must be of RPairField"));
103}
104
105} // anonymous namespace
106
108 : ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kRecord, false /* isSimple */),
110 fSubfieldsInfo(source.fSubfieldsInfo),
111 fMaxAlignment(source.fMaxAlignment)
112{
113 for (const auto &f : source.GetConstSubfields()) {
114 RFieldBase::Attach(f->Clone(f->GetFieldName()));
115 }
116 fTraits = source.GetTraits();
117}
118
119ROOT::RClassField::RClassField(std::string_view fieldName, std::string_view className)
121{
122}
123
125 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kRecord,
126 false /* isSimple */),
128{
130 throw RException(R__FAIL(std::string("RField: RClassField \"") + classp->GetName() +
131 " cannot be constructed from a class that's not at least Interpreted"));
132 }
133 // Avoid accidentally supporting std types through TClass.
135 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
136 }
137 if (GetTypeName() == "TObject") {
138 throw RException(R__FAIL("TObject is only supported through RField<TObject>"));
139 }
140 if (fClass->GetCollectionProxy()) {
141 throw RException(R__FAIL(std::string(GetTypeName()) + " has an associated collection proxy; "
142 "use RProxiedCollectionField instead"));
143 }
144 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
145 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
146 if (!fClass->CanSplit() && fClass->Size() > 1 &&
149 throw RException(R__FAIL(GetTypeName() + " cannot be stored natively in RNTuple"));
150 }
153 throw RException(R__FAIL(GetTypeName() + " has streamer mode enforced, not supported as native RNTuple class"));
154 }
155
160
161 int i = 0;
162 const auto *bases = fClass->GetListOfBases();
163 assert(bases);
165 if (baseClass->GetDelta() < 0) {
166 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
167 " virtually inherits from " + baseClass->GetName()));
168 }
169 TClass *c = baseClass->GetClassPointer();
170 auto subField =
171 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
172 fTraits &= subField->GetTraits();
173 Attach(std::move(subField), RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
174 i++;
175 }
177 // Skip, for instance, unscoped enum constants defined in the class
178 if (dataMember->Property() & kIsStatic)
179 continue;
180 // Skip members explicitly marked as transient by user comment
181 if (!dataMember->IsPersistent()) {
182 // TODO(jblomer): we could do better
184 continue;
185 }
186
187 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
188 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
189 // the string returned by dataMember->GetFullTypeName())
190 std::string typeName{dataMember->GetTrueTypeName()};
191 // RFieldBase::Create() set subField->fTypeAlias based on the assumption that the user specified typeName, which
192 // already went through one round of type resolution.
193 std::string origTypeName{dataMember->GetFullTypeName()};
194
195 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
196 if (dataMember->Property() & kIsArray) {
197 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
198 const auto addedStr = "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
199 typeName += addedStr;
201 }
202 }
203
204 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
205
207 if (normTypeName == subField->GetTypeName()) {
209 } else {
211 }
212
213 fTraits &= subField->GetTraits();
214 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
215 }
217}
218
220{
221 if (fStagingArea) {
222 for (const auto &[_, si] : fStagingItems) {
223 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
224 auto deleter = GetDeleterOf(*si.fField);
225 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
226 }
227 }
228 }
229}
230
231void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
232{
233 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
234 fSubfieldsInfo.push_back(info);
235 RFieldBase::Attach(std::move(child));
236}
237
238std::vector<const ROOT::TSchemaRule *> ROOT::RClassField::FindRules(const ROOT::RFieldDescriptor *fieldDesc)
239{
241 const auto ruleset = fClass->GetSchemaRules();
242 if (!ruleset)
243 return rules;
244
245 if (!fieldDesc) {
246 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
247 // of the class
248 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
249 } else {
250 // We need to change (back) the name normalization from RNTuple to ROOT Meta
251 std::string normalizedName;
253 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
254 // on-disk version of the field.
255 if (fieldDesc->GetTypeChecksum()) {
256 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
257 } else {
258 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
259 }
260 }
261
262 // Cleanup and sort rules
263 // Check that any any given source member uses the same type in all rules
264 std::unordered_map<std::string, std::string> sourceNameAndType;
265 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
266 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
267 const auto rule = *itr;
268
269 // Erase unknown rule types
270 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
272 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
273 itr = rules.erase(itr);
274 continue;
275 }
276
277 bool hasConflictingSourceMembers = false;
278 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
279 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
280 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
281 if (!isNew && (itrSrc->second != memberType)) {
283 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
284 << memberType << " for member " << source->GetName();
286 break;
287 }
288 }
290 itr = rules.erase(itr);
291 continue;
292 }
293
294 // Rules targeting the entire object need to be executed at the end
295 if (rule->GetTarget() == nullptr) {
296 nskip++;
297 if (itr != rules.end() - nskip)
298 std::iter_swap(itr++, rules.end() - nskip);
299 continue;
300 }
301
302 ++itr;
303 }
304
305 return rules;
306}
307
308std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
309{
310 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
311}
312
313std::size_t ROOT::RClassField::AppendImpl(const void *from)
314{
315 std::size_t nbytes = 0;
316 for (unsigned i = 0; i < fSubfields.size(); i++) {
317 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
318 }
319 return nbytes;
320}
321
323{
324 for (const auto &[_, si] : fStagingItems) {
325 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
326 }
327 for (unsigned i = 0; i < fSubfields.size(); i++) {
328 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
329 }
330}
331
333{
334 for (const auto &[_, si] : fStagingItems) {
335 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
336 }
337 for (unsigned i = 0; i < fSubfields.size(); i++) {
338 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
339 }
340}
341
344{
347 return idSourceMember;
348
349 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
350 const auto subFieldName = subFieldDesc.GetFieldName();
351 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
352 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
354 return idSourceMember;
355 }
356 }
357
359}
360
361void ROOT::RClassField::SetStagingClass(const std::string &className, unsigned int classVersion)
362{
363 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
364 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
365 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
366 if (!fStagingClass) {
367 // For a rename rule, we may simply ask for the old class name
368 fStagingClass = TClass::GetClass(className.c_str());
369 }
370 } else {
371 fStagingClass = fClass;
372 }
373 R__ASSERT(fStagingClass);
374 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
375}
376
377void ROOT::RClassField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
378 const ROOT::RNTupleDescriptor &desc,
380{
381 std::size_t stagingAreaSize = 0;
382 for (const auto rule : rules) {
383 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
384 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
385 if (!isNew) {
386 // This source member has already been processed by another rule (and we only support one type per member)
387 continue;
388 }
389 RStagingItem &stagingItem = itr->second;
390
391 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
393 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
394 source->GetName()));
395 }
396
397 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
398 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
399 memberField->SetOnDiskId(memberFieldId);
400 auto fieldZero = std::make_unique<RFieldZero>();
402 fieldZero->Attach(std::move(memberField));
403 stagingItem.fField = std::move(fieldZero);
404
405 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
406 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
407 // to be present in the TClass instance, too.
409 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
410 }
411 }
412
413 if (stagingAreaSize) {
414 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
415 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
416 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
417
418 for (const auto &[_, si] : fStagingItems) {
419 const auto &memberField = *si.fField->cbegin();
420 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
421 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
422 }
423 }
424 }
425}
426
428{
429 auto func = rule->GetReadFunctionPointer();
430 if (func == nullptr) {
431 // Can happen for rename rules
432 return;
433 }
434 fReadCallbacks.emplace_back([func, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
435 TVirtualObject onfileObj{nullptr};
436 onfileObj.fClass = stagingClass;
437 onfileObj.fObject = stagingArea;
438 func(static_cast<char *>(target), &onfileObj);
439 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
440 });
441}
442
444{
445 std::vector<const TSchemaRule *> rules;
446 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
447 // will be marked as artificial (added member in a new class version or member set by rule).
448 std::unordered_set<std::string> regularSubfields;
449
450 if (GetOnDiskId() == kInvalidDescriptorId) {
451 // This can happen for added base classes or added members of class type
452 rules = FindRules(nullptr);
453 if (!rules.empty())
454 SetStagingClass(GetTypeName(), GetTypeVersion());
455 } else {
456 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
457 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetRef();
458 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
459
460 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
461 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
462 // by a streamer field to read the data from disk.
463 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
464 substitute->SetOnDiskId(GetOnDiskId());
465 return substitute;
466 }
467
468 for (auto linkId : fieldDesc.GetLinkIds()) {
469 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
470 regularSubfields.insert(subFieldDesc.GetFieldName());
471 }
472
473 rules = FindRules(&fieldDesc);
474
475 // 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
476 // (source) type name and version/checksum.
477 if (rules.empty()) {
478 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
479 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
480 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
482 if (GetTypeName() != descTypeName) {
483 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
484 " vs. " + descTypeName));
485 }
486 }
487
488 if (!rules.empty()) {
489 SetStagingClass(fieldDesc.GetTypeName(), fieldDesc.GetTypeVersion());
490 PrepareStagingArea(rules, desc, fieldDesc);
491 for (auto &[_, si] : fStagingItems) {
493 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
494 }
495
496 // Remove target member of read rules from the list of regular members of the underlying on-disk field
497 for (const auto rule : rules) {
498 if (!rule->GetTarget())
499 continue;
500
501 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
502 regularSubfields.erase(std::string(target->GetString()));
503 }
504 }
505 }
506 }
507
508 for (const auto rule : rules) {
509 AddReadCallbacksFromIORule(rule);
510 }
511
512 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
513 for (auto &field : fSubfields) {
514 if (regularSubfields.count(field->GetFieldName()) == 0) {
515 CallSetArtificialOn(*field);
516 }
517 }
518
519 return nullptr;
520}
521
523{
524 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeVersion | kDiffTypeName).ThrowOnError();
525}
526
528{
529 fClass->New(where);
530}
531
533{
534 fClass->Destructor(objPtr, true /* dtorOnly */);
535 RDeleter::operator()(objPtr, dtorOnly);
536}
537
538std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
539{
540 std::vector<RValue> result;
541 auto valuePtr = value.GetPtr<void>();
542 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
543 result.reserve(fSubfields.size());
544 for (unsigned i = 0; i < fSubfields.size(); i++) {
545 result.emplace_back(
546 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
547 }
548 return result;
549}
550
552{
553 return fClass->GetClassSize();
554}
555
557{
558 return fClass->GetClassVersion();
559}
560
562{
563 return fClass->GetCheckSum();
564}
565
567{
568 visitor.VisitClassField(*this);
569}
570
571//------------------------------------------------------------------------------
572
573ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
575{
576}
577
579 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
580 false /* isSimple */)
581{
582 // Avoid accidentally supporting std types through TEnum.
583 if (enump->Property() & kIsDefinedInStd) {
584 throw RException(R__FAIL(GetTypeName() + " is not supported"));
585 }
586
587 switch (enump->GetUnderlyingType()) {
588 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
589 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
590 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
591 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
592 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
593 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
594 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
595 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
596 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
597 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
598 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
599 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
600 }
601
603}
604
605ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
606 std::unique_ptr<RFieldBase> intField)
608{
609 Attach(std::move(intField));
611}
612
613std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
614{
615 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
616 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
617}
618
620{
621 // TODO(jblomer): allow enum to enum conversion only by rename rule
622 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName | kDiffTypeVersion).ThrowOnError();
623}
624
625std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
626{
627 std::vector<RValue> result;
628 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
629 return result;
630}
631
633{
634 visitor.VisitEnumField(*this);
635}
636
637//------------------------------------------------------------------------------
638
639std::string ROOT::RPairField::RPairField::GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
640{
641 return itemFields[0]->GetTypeName() + "," + itemFields[1]->GetTypeName();
642}
643
644ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
645 const std::array<std::size_t, 2> &offsets)
646 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
647{
648 AttachItemFields(std::move(itemFields));
649 fOffsets.push_back(offsets[0]);
650 fOffsets.push_back(offsets[1]);
651}
652
653ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
654 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
655{
656 AttachItemFields(std::move(itemFields));
657
658 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
659 auto *c = TClass::GetClass(GetTypeName().c_str());
660 if (!c)
661 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
662 fSize = c->Size();
663
664 auto firstElem = c->GetRealData("first");
665 if (!firstElem)
666 throw RException(R__FAIL("first: no such member"));
667 fOffsets.push_back(firstElem->GetThisOffset());
668
669 auto secondElem = c->GetRealData("second");
670 if (!secondElem)
671 throw RException(R__FAIL("second: no such member"));
672 fOffsets.push_back(secondElem->GetThisOffset());
673}
674
675std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
676{
677 std::array<std::size_t, 2> offsets = {fOffsets[0], fOffsets[1]};
678 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
679 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
680 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones), offsets));
681}
682
684{
685 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
686
687 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
688 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName).ThrowOnError();
689 EnsureMatchingTypePrefix(fieldDesc, prefixes).ThrowOnError();
690
691 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
692 if (nOnDiskSubfields != 2) {
693 throw ROOT::RException(
694 R__FAIL("invalid number of on-disk subfields for std::pair " + std::to_string(nOnDiskSubfields)));
695 }
696}
697
698//------------------------------------------------------------------------------
699
702 bool readFromDisk)
703{
705 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
706 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
707 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
708 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
709 (ifuncs.fNext != nullptr));
710 return ifuncs;
711}
712
714 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
715 false /* isSimple */),
716 fNWritten(0)
717{
718 if (!classp->GetCollectionProxy())
719 throw RException(R__FAIL(std::string(GetTypeName()) + " has no associated collection proxy"));
720
721 fProxy.reset(classp->GetCollectionProxy()->Generate());
722 fProperties = fProxy->GetProperties();
723 fCollectionType = fProxy->GetCollectionType();
724 if (fProxy->HasPointers())
725 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
726
727 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
728 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
729}
730
731ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
733{
734 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
736 throw RException(R__FAIL("custom associative collection proxies not supported"));
737
738 std::unique_ptr<ROOT::RFieldBase> itemField;
739
740 if (auto valueClass = fProxy->GetValueClass()) {
741 // Element type is a class
742 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
743 } else {
744 switch (fProxy->GetType()) {
745 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
746 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
747 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
748 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
749 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
750 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
751 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
752 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
753 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
754 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
755 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
756 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
757 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
758 default: throw RException(R__FAIL("unsupported value type"));
759 }
760 }
761
762 fItemSize = itemField->GetValueSize();
763 Attach(std::move(itemField));
764}
765
766std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
767{
768 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
769 auto clone =
770 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
771 clone->fItemSize = fItemSize;
772 clone->Attach(std::move(newItemField));
773 return clone;
774}
775
776std::size_t ROOT::RProxiedCollectionField::AppendImpl(const void *from)
777{
778 std::size_t nbytes = 0;
779 unsigned count = 0;
780 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
781 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
782 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
783 nbytes += CallAppendOn(*fSubfields[0], ptr);
784 count++;
785 }
786
787 fNWritten += count;
788 fPrincipalColumn->Append(&fNWritten);
789 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
790}
791
793{
796 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
797
798 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
799 void *obj =
800 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
801
802 unsigned i = 0;
803 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
804 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
805 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
806 }
807 if (obj != to)
808 fProxy->Commit(obj);
809}
810
820
825
830
832{
833 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName).ThrowOnError();
834}
835
837{
838 fProxy->New(where);
839}
840
841std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
842{
843 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
844 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
845 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
846 }
847 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
848}
849
851{
852 if (fItemDeleter) {
854 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
855 fItemDeleter->operator()(ptr, true /* dtorOnly */);
856 }
857 }
858 fProxy->Destructor(objPtr, true /* dtorOnly */);
859 RDeleter::operator()(objPtr, dtorOnly);
860}
861
862std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
863{
864 std::vector<RValue> result;
865 auto valueRawPtr = value.GetPtr<void>().get();
867 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
868 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
869 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
870 }
871 return result;
872}
873
875{
876 visitor.VisitProxiedCollectionField(*this);
877}
878
879//------------------------------------------------------------------------------
880
881ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
883{
884 auto *itemClass = fProxy->GetValueClass();
885 fItemSize = itemClass->GetClassSize();
886
887 Attach(std::move(itemField));
888}
889
890//------------------------------------------------------------------------------
891
892ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
894{
895 fItemSize = itemField->GetValueSize();
896 Attach(std::move(itemField));
897}
898
899//------------------------------------------------------------------------------
900
901namespace {
902
903/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
904class TBufferRecStreamer : public TBufferFile {
905public:
906 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
907
908private:
909 RCallbackStreamerInfo fCallbackStreamerInfo;
910
911public:
912 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
913 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo)
914 {
915 }
916 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
917};
918
919} // anonymous namespace
920
921ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className, std::string_view typeAlias)
923{
925}
926
928 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
929 false /* isSimple */),
930 fClass(classp),
931 fIndex(0)
932{
934 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
935 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
936 // implicit constructors and destructors.
941}
942
943std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
944{
945 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName(), GetTypeAlias()));
946}
947
948std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
949{
950 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
951 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
952 fClass->Streamer(const_cast<void *>(from), buffer);
953
954 auto nbytes = buffer.Length();
955 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
956 fIndex += nbytes;
957 fPrincipalColumn->Append(&fIndex);
958 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
959}
960
962{
965 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
966
968 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
969 fClass->Streamer(to, buffer);
970}
971
981
986
991
993{
994 source.RegisterStreamerInfos();
995 return nullptr;
996}
997
999{
1000 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1001}
1002
1004{
1005 fClass->New(where);
1006}
1007
1009{
1010 fClass->Destructor(objPtr, true /* dtorOnly */);
1011 RDeleter::operator()(objPtr, dtorOnly);
1012}
1013
1015{
1018 .TypeVersion(GetTypeVersion())
1019 .TypeName(GetTypeName())
1021 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
1022}
1023
1025{
1026 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1027}
1028
1030{
1031 return fClass->GetClassSize();
1032}
1033
1035{
1036 return fClass->GetClassVersion();
1037}
1038
1040{
1041 return fClass->GetCheckSum();
1042}
1043
1045{
1046 visitor.VisitStreamerField(*this);
1047}
1048
1049//------------------------------------------------------------------------------
1050
1052{
1053 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1054 return dataMember->GetOffset();
1055 }
1056 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1057}
1058
1060 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1061{
1063 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1064 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1065}
1066
1068 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1069{
1070 assert(TObject::Class()->GetClassVersion() == 1);
1071
1073 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1074 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1075}
1076
1077std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1078{
1079 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1080}
1081
1082std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1083{
1084 // Cf. TObject::Streamer()
1085
1086 auto *obj = static_cast<const TObject *>(from);
1087 if (obj->TestBit(TObject::kIsReferenced)) {
1088 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1089 }
1090
1091 std::size_t nbytes = 0;
1092 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1093
1094 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1095 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1096 nbytes += CallAppendOn(*fSubfields[1], &bits);
1097
1098 return nbytes;
1099}
1100
1102{
1103 // Cf. TObject::Streamer()
1104
1105 auto *obj = static_cast<TObject *>(to);
1106 if (obj->TestBit(TObject::kIsReferenced)) {
1107 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1108 }
1109
1110 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1111
1112 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1114 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1115}
1116
1118{
1119 UInt_t uniqueID, bits;
1120 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1121 CallReadOn(*fSubfields[1], globalIndex, &bits);
1122 ReadTObject(to, uniqueID, bits);
1123}
1124
1126{
1127 UInt_t uniqueID, bits;
1128 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1129 CallReadOn(*fSubfields[1], localIndex, &bits);
1130 ReadTObject(to, uniqueID, bits);
1131}
1132
1134{
1135 return TObject::Class()->GetClassVersion();
1136}
1137
1139{
1140 return TObject::Class()->GetCheckSum();
1141}
1142
1144{
1145 new (where) TObject();
1146}
1147
1148std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1149{
1150 std::vector<RValue> result;
1151 // Use GetPtr<TObject> to type-check
1152 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1153 auto charPtr = static_cast<unsigned char *>(ptr.get());
1154 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1155 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1156 return result;
1157}
1158
1160{
1161 return sizeof(TObject);
1162}
1163
1165{
1166 return alignof(TObject);
1167}
1168
1170{
1171 visitor.VisitTObjectField(*this);
1172}
1173
1174//------------------------------------------------------------------------------
1175
1176std::string ROOT::RTupleField::RTupleField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1177{
1178 std::string result;
1179 if (itemFields.empty())
1180 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
1181 for (size_t i = 0; i < itemFields.size(); ++i) {
1182 result += itemFields[i]->GetTypeName() + ",";
1183 }
1184 result.pop_back(); // remove trailing comma
1185 return result;
1186}
1187
1188ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields,
1189 const std::vector<std::size_t> &offsets)
1190 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1191{
1192 AttachItemFields(std::move(itemFields));
1193 fOffsets = offsets;
1194}
1195
1196ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1197 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
1198{
1199 AttachItemFields(std::move(itemFields));
1200
1201 auto *c = TClass::GetClass(GetTypeName().c_str());
1202 if (!c)
1203 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1204 fSize = c->Size();
1205
1206 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1207 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1208 // following the order of the type list.
1209 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1210 // members, the assertion below will fail.
1211 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1212 std::string memberName("_" + std::to_string(i));
1213 auto member = c->GetRealData(memberName.c_str());
1214 if (!member)
1215 throw RException(R__FAIL(memberName + ": no such member"));
1216 fOffsets.push_back(member->GetThisOffset());
1217 }
1218}
1219
1220std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1221{
1222 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1223 itemClones.reserve(fSubfields.size());
1224 for (const auto &f : fSubfields) {
1225 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1226 }
1227 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones), fOffsets));
1228}
1229
1231{
1232 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1233
1234 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1235 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName).ThrowOnError();
1236 EnsureMatchingTypePrefix(fieldDesc, prefixes).ThrowOnError();
1237
1238 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1239 const auto nSubfields = fSubfields.size();
1241 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1242 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields)));
1243 }
1244}
1245
1246//------------------------------------------------------------------------------
1247
1248namespace {
1249
1250// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1251constexpr std::size_t GetVariantTagSize()
1252{
1253 // Should be all zeros except for the tag, which is 1
1254 std::variant<char> t;
1255 constexpr auto sizeOfT = sizeof(t);
1256
1257 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1258 return sizeOfT == 2 ? 1 : 4;
1259}
1260
1261template <std::size_t VariantSizeT>
1262struct RVariantTag {
1263 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1264 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1265};
1266
1267} // anonymous namespace
1268
1269std::string ROOT::RVariantField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
1270{
1271 std::string result;
1272 for (size_t i = 0; i < itemFields.size(); ++i) {
1273 result += itemFields[i]->GetTypeName() + ",";
1274 }
1275 R__ASSERT(!result.empty()); // there is always at least one variant
1276 result.pop_back(); // remove trailing comma
1277 return result;
1278}
1279
1281 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1282 fMaxItemSize(source.fMaxItemSize),
1283 fMaxAlignment(source.fMaxAlignment),
1284 fTagOffset(source.fTagOffset),
1285 fVariantOffset(source.fVariantOffset),
1286 fNWritten(source.fNWritten.size(), 0)
1287{
1288 for (const auto &f : source.GetConstSubfields())
1289 Attach(f->Clone(f->GetFieldName()));
1290 fTraits = source.fTraits;
1291}
1292
1293ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1294 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields) + ">", ROOT::ENTupleStructure::kVariant,
1295 false /* isSimple */)
1296{
1297 // The variant needs to initialize its own tag member
1299
1300 auto nFields = itemFields.size();
1301 if (nFields == 0 || nFields > kMaxVariants) {
1302 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
1303 }
1304 fNWritten.resize(nFields, 0);
1305 for (unsigned int i = 0; i < nFields; ++i) {
1308 fTraits &= itemFields[i]->GetTraits();
1309 Attach(std::move(itemFields[i]));
1310 }
1311
1312 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
1313 // For instance, std::variant<std::optional<int>> on macOS.
1314 auto cl = TClass::GetClass(GetTypeName().c_str());
1315 assert(cl);
1316 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
1317 if (dm)
1318 fVariantOffset = dm->GetOffset();
1319
1320 const auto tagSize = GetVariantTagSize();
1321 const auto padding = tagSize - (fMaxItemSize % tagSize);
1323}
1324
1325std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
1326{
1327 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
1328}
1329
1330std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
1331{
1332 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1333 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
1334 return (tag == TagType_t(-1)) ? 0 : tag + 1;
1335}
1336
1337void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
1338{
1339 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
1340 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
1341 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
1342}
1343
1344std::size_t ROOT::RVariantField::AppendImpl(const void *from)
1345{
1346 auto tag = GetTag(from, fTagOffset);
1347 std::size_t nbytes = 0;
1348 auto index = 0;
1349 if (tag > 0) {
1350 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
1351 index = fNWritten[tag - 1]++;
1352 }
1354 fPrincipalColumn->Append(&varSwitch);
1355 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
1356}
1357
1359{
1361 std::uint32_t tag;
1362 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
1363 R__ASSERT(tag < 256);
1364
1365 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
1366 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
1367 // any `std::holds_alternative<T>` check fail later.
1368 if (R__likely(tag > 0)) {
1369 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
1370 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
1371 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
1372 }
1373 SetTag(to, fTagOffset, tag);
1374}
1375
1381
1386
1391
1393{
1394 static const std::vector<std::string> prefixes = {"std::variant<"};
1395
1396 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1397 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName).ThrowOnError();
1398 EnsureMatchingTypePrefix(fieldDesc, prefixes).ThrowOnError();
1399
1400 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
1401 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName()));
1402 }
1403}
1404
1406{
1407 memset(where, 0, GetValueSize());
1408 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
1409 SetTag(where, fTagOffset, 1);
1410}
1411
1413{
1414 auto tag = GetTag(objPtr, fTagOffset);
1415 if (tag > 0) {
1416 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
1417 }
1418 RDeleter::operator()(objPtr, dtorOnly);
1419}
1420
1421std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
1422{
1423 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
1424 itemDeleters.reserve(fSubfields.size());
1425 for (const auto &f : fSubfields) {
1426 itemDeleters.emplace_back(GetDeleterOf(*f));
1427 }
1428 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
1429}
1430
1432{
1433 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
1434}
1435
1437{
1438 const auto alignment = GetAlignment();
1439 const auto actualSize = fTagOffset + GetVariantTagSize();
1440 const auto padding = alignment - (actualSize % alignment);
1441 return actualSize + ((padding == alignment) ? 0 : padding);
1442}
1443
1445{
1446 std::fill(fNWritten.begin(), fNWritten.end(), 0);
1447}
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:595
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
#define R__LOG_WARNING(...)
Definition RLogger.hxx:358
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kLong_t
Definition TDataType.h:30
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kUInt_t
Definition TDataType.h:30
@ kClassHasExplicitCtor
@ kClassHasImplicitCtor
@ kClassHasExplicitDtor
@ kClassHasImplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char mode
char name[80]
Definition TGX11.cxx:110
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:108
Abstract base class for classes implementing the visitor design pattern.
Holds the index and the tag of a kSwitch column.
A helper class for piece-wise construction of an RExtraTypeInfoDescriptor.
static std::string SerializeStreamerInfos(const StreamerInfoMap_t &infos)
Abstract interface to read data from an ntuple.
void operator()(void *objPtr, bool dtorOnly) final
The field for a class with dictionary.
Definition RField.hxx:134
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
void AddReadCallbacksFromIORule(const TSchemaRule *rule)
Register post-read callback corresponding to a ROOT I/O customization rules.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:222
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void Attach(std::unique_ptr< RFieldBase > child, RSubFieldInfo info)
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::vector< const TSchemaRule * > FindRules(const ROOT::RFieldDescriptor *fieldDesc)
Given the on-disk information from the page source, find all the I/O customization rules that apply t...
ROOT::DescriptorId_t LookupMember(const ROOT::RNTupleDescriptor &desc, std::string_view memberName, ROOT::DescriptorId_t classFieldId)
Returns the id of member 'name' in the class field given by 'fieldId', or kInvalidDescriptorId if no ...
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
TClass * fClass
Definition RField.hxx:163
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
RClassField(std::string_view fieldName, const RClassField &source)
Used by CloneImpl.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
void PrepareStagingArea(const std::vector< const TSchemaRule * > &rules, const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &classFieldId)
If there are rules with inputs (source members), create the staging area according to the TClass inst...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
~RClassField() override
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:152
void SetStagingClass(const std::string &className, unsigned int classVersion)
Sets fStagingClass according to the given name and version.
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:283
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
REnumField(std::string_view fieldName, TEnum *enump)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
Field specific extra type information from the header / extenstion header.
The list of column representations a field can have.
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
static void SetTypeAliasOf(RFieldBase &other, const std::string &alias)
Allow class fields to adjust the type alias of their members.
void Attach(std::unique_ptr< RFieldBase > child)
Add a new subfield to the list of nested fields.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &typeName, const ROOT::RCreateFieldOptions &options, const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId)
Factory method to resurrect a field from the stored on-disk type information.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
const std::string & GetTypeName() const
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitTriviallyConstructible
No constructor needs to be called, i.e.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
Metadata stored for every field of an RNTuple.
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:59
std::vector< std::unique_ptr< RFieldBase > > ReleaseSubfields()
Moves all subfields into the returned vector.
Definition RField.cxx:47
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:313
RField(std::string_view name)
Definition RField.hxx:316
RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr< RFieldBase > itemField)
The on-storage metadata of an RNTuple.
RFieldDescriptorIterable GetFieldIterable(const RFieldDescriptor &fieldDesc) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
Template specializations for C++ std::pair.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RPairField(std::string_view fieldName, std::array< std::unique_ptr< RFieldBase >, 2 > itemFields, const std::array< std::size_t, 2 > &offsets)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Allows for iterating over the elements of a proxied collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class representing a collection of elements via TVirtualCollectionProxy.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
RProxiedCollectionField(std::string_view fieldName, TClass *classp)
Constructor used when the value type of the collection is not known in advance, i....
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
std::shared_ptr< TVirtualCollectionProxy > fProxy
The collection proxy is needed by the deleters and thus defined as a shared pointer.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RDeleter > GetDeleter() const final
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
const_iterator begin() const
const_iterator end() const
The field for an untyped record.
void AttachItemFields(std::vector< std::unique_ptr< RFieldBase > > itemFields)
Definition RField.cxx:563
std::vector< std::size_t > fOffsets
RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr< RFieldBase > itemField)
void operator()(void *objPtr, bool dtorOnly) final
The field for a class using ROOT standard streaming.
Definition RField.hxx:229
ROOT::RExtraTypeInfoDescriptor GetExtraTypeInfo() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RStreamerField(std::string_view fieldName, TClass *classp)
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Template specializations for C++ std::tuple.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > itemFields, const std::vector< std::size_t > &offsets)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void operator()(void *objPtr, bool dtorOnly) final
Template specializations for C++ std::variant.
static std::string GetTypeList(const std::vector< std::unique_ptr< RFieldBase > > &itemFields)
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
static constexpr std::size_t kMaxVariants
std::vector< ROOT::Internal::RColumnIndex::ValueType > fNWritten
static std::uint8_t GetTag(const void *variantPtr, std::size_t tagOffset)
Extracts the index from an std::variant and transforms it into the 1-based index used for the switch ...
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
size_t fVariantOffset
In the std::variant memory layout, the actual union of types may start at an offset > 0.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
size_t fTagOffset
In the std::variant memory layout, at which byte number is the index stored.
RVariantField(std::string_view name, const RVariantField &source)
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
void CommitClusterImpl() final
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition TBufferFile.h:47
@ kWrite
Definition TBuffer.h:73
@ kRead
Definition TBuffer.h:73
char * Buffer() const
Definition TBuffer.h:96
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2324
EState GetState() const
Definition TClass.h:501
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3797
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5743
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3663
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2902
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2401
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6128
@ kInterpreted
Definition TClass.h:129
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2973
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
The TEnum class implements the enum type.
Definition TEnum.h:33
static TEnum * GetEnum(const std::type_info &ti, ESearchAction sa=kALoadAndInterpLookup)
Definition TEnum.cxx:181
Mother of all ROOT objects.
Definition TObject.h:41
@ kIsOnHeap
object is on heap
Definition TObject.h:87
@ kNotDeleted
object has not been deleted
Definition TObject.h:88
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:71
RAII helper class that ensures that PushProxy() / PopProxy() are called when entering / leaving a C++...
Defines a common interface to inspect/change the contents of an object that represents a collection.
@ kNeedDelete
The collection contains directly or indirectly (via other collection) some pointers that need explici...
Abstract Interface class describing Streamer information for one class.
const Int_t n
Definition legend1.C:16
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:34
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
ERNTupleSerializationMode GetRNTupleSerializationMode(TClass *cl)
std::string GetNormalizedUnresolvedTypeName(const std::string &origName)
Applies all RNTuple type normalization rules except typedef resolution.
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
@ kSTLvector
Definition ESTLType.h:30
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.