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// - RTupleField
11// - RProxiedCollectionField
12// - RMapField
13// - RSetField
14// - RStreamerField
15// - RField<TObject>
16// - RVariantField
17
18#include <ROOT/BitUtils.hxx>
19#include <ROOT/RField.hxx>
20#include <ROOT/RFieldBase.hxx>
21#include <ROOT/RFieldUtils.hxx>
23#include <ROOT/RNTupleUtils.hxx>
24#include <ROOT/RSpan.hxx>
25
26#include <TBaseClass.h>
27#include <TBufferFile.h>
28#include <TClass.h>
29#include <TClassEdit.h>
30#include <TDataMember.h>
31#include <TEnum.h>
32#include <TObject.h>
33#include <TObjArray.h>
34#include <TObjString.h>
35#include <TRealData.h>
36#include <TSchemaRule.h>
37#include <TSchemaRuleSet.h>
38#include <TStreamerElement.h>
39#include <TVirtualObject.h>
41
42#include <algorithm>
43#include <array>
44#include <cstddef> // std::size_t
45#include <cstdint> // std::uint32_t et al.
46#include <cstring> // for memset
47#include <memory>
48#include <mutex>
49#include <string>
50#include <string_view>
51#include <unordered_set>
52#include <utility>
53#include <variant>
54
56
57namespace {
58
59TClass *EnsureValidClass(std::string_view className)
60{
61 auto cl = TClass::GetClass(std::string(className).c_str());
62 if (cl == nullptr) {
63 throw ROOT::RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
64 }
65 return cl;
66}
67
68/// Common checks used both by RClassField and RSoAField
69void EnsureValidUserClass(TClass *cl, const ROOT::RFieldBase &field, std::string_view fieldType)
70{
71 if (cl->GetState() < TClass::kInterpreted) {
72 throw ROOT::RException(R__FAIL(std::string(fieldType) + " " + cl->GetName() +
73 " cannot be constructed from a class that's not at least Interpreted"));
74 }
75 // Avoid accidentally supporting std types through TClass.
76 if (cl->Property() & kIsDefinedInStd) {
77 throw ROOT::RException(R__FAIL(field.GetTypeName() + " is not supported"));
78 }
79 if (field.GetTypeName() == "TObject") {
80 throw ROOT::RException(R__FAIL("TObject is only supported through RField<TObject>"));
81 }
82 if (cl->GetCollectionProxy()) {
83 throw ROOT::RException(R__FAIL(field.GetTypeName() + " has an associated collection proxy; "
84 "use RProxiedCollectionField instead"));
85 }
86 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
87 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
88 if (!cl->CanSplit() && cl->Size() > 1 &&
90 throw ROOT::RException(R__FAIL(field.GetTypeName() + " cannot be stored natively in RNTuple"));
91 }
94 throw ROOT::RException(
95 R__FAIL(field.GetTypeName() + " has streamer mode enforced, not supported as native RNTuple class"));
96 }
97 // Detect custom streamers set on individual members at runtime via
98 // TClass::SetMemberStreamer() or TClass::AdoptMemberStreamer().
99 // CanSplit() only checks for custom streamers set at compile time (fHasCustomStreamerMember),
100 // but runtime streamers are stored in TRealData and must be checked here.
101 if (!cl->GetListOfRealData()) {
102 cl->BuildRealData();
103 }
104 for (auto realMember : ROOT::Detail::TRangeStaticCast<TRealData>(*cl->GetListOfRealData())) {
105 if (realMember->GetStreamer()) {
106 throw ROOT::RException(R__FAIL(std::string(field.GetTypeName()) + " has member " + realMember->GetName() +
107 " with a custom streamer; not supported natively in RNTuple"));
108 }
109 }
110}
111
112TEnum *EnsureValidEnum(std::string_view enumName)
113{
114 auto e = TEnum::GetEnum(std::string(enumName).c_str());
115 if (e == nullptr) {
116 throw ROOT::RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
117 }
118 return e;
119}
120
121void EnsureValidAlignment(std::size_t alignment)
122{
124 throw ROOT::RException(R__FAIL(std::string("invalid alignment: ") + std::to_string(alignment)));
125}
126
127/// Create a comma-separated list of type names from the given fields. Uses either the real type names or the
128/// type aliases (if there are any, otherwise the actual type name). Used to construct template argument lists
129/// for templated types such as std::pair<...>, std::tuple<...>, std::variant<...>.
130std::string GetTypeList(std::span<std::unique_ptr<ROOT::RFieldBase>> itemFields, bool useTypeAliases)
131{
132 std::string result;
133 for (size_t i = 0; i < itemFields.size(); ++i) {
134 if (useTypeAliases && !itemFields[i]->GetTypeAlias().empty()) {
135 result += itemFields[i]->GetTypeAlias();
136 } else {
137 result += itemFields[i]->GetTypeName();
138 }
139 result.push_back(',');
140 }
141 if (result.empty()) {
142 throw ROOT::RException(R__FAIL("invalid empty type list provided as template argument"));
143 }
144 result.pop_back(); // remove trailing comma
145 return result;
146}
147
148std::string BuildSetTypeName(ROOT::RSetField::ESetType setType, const ROOT::RFieldBase &innerField, bool useTypeAlias)
149{
150 std::string typePrefix;
151 switch (setType) {
152 case ROOT::RSetField::ESetType::kSet: typePrefix = "std::set<"; break;
153 case ROOT::RSetField::ESetType::kUnorderedSet: typePrefix = "std::unordered_set<"; break;
154 case ROOT::RSetField::ESetType::kMultiSet: typePrefix = "std::multiset<"; break;
155 case ROOT::RSetField::ESetType::kUnorderedMultiSet: typePrefix = "std::unordered_multiset<"; break;
156 default: R__ASSERT(false);
157 }
158 return typePrefix +
159 ((useTypeAlias && !innerField.GetTypeAlias().empty()) ? innerField.GetTypeAlias()
160 : innerField.GetTypeName()) +
161 ">";
162}
163
164std::string BuildMapTypeName(ROOT::RMapField::EMapType mapType, const ROOT::RFieldBase *innerField, bool useTypeAliases)
165{
166 if (const auto pairField = dynamic_cast<const ROOT::RPairField *>(innerField)) {
167 std::string typePrefix;
168 switch (mapType) {
169 case ROOT::RMapField::EMapType::kMap: typePrefix = "std::map<"; break;
170 case ROOT::RMapField::EMapType::kUnorderedMap: typePrefix = "std::unordered_map<"; break;
171 case ROOT::RMapField::EMapType::kMultiMap: typePrefix = "std::multimap<"; break;
172 case ROOT::RMapField::EMapType::kUnorderedMultiMap: typePrefix = "std::unordered_multimap<"; break;
173 default: R__ASSERT(false);
174 }
175 const auto &items = pairField->GetConstSubfields();
176 std::string type = typePrefix;
177 for (int i : {0, 1}) {
178 if (useTypeAliases && !items[i]->GetTypeAlias().empty()) {
179 type += items[i]->GetTypeAlias();
180 } else {
181 type += items[i]->GetTypeName();
182 }
183 if (i == 0)
184 type.push_back(',');
185 }
186 return type + ">";
187 }
188
189 throw ROOT::RException(R__FAIL("RMapField inner field type must be of RPairField"));
190}
191
192} // anonymous namespace
193
194ROOT::RRuleField::RRuleField(std::string_view fieldName, std::string_view typeName, TClass *classp,
195 ROOT::ENTupleStructure structure)
196 : RFieldBase(fieldName, typeName, structure, false /* isSimple */), fClass(classp)
197{
198}
199
201 ROOT::DescriptorId_t classFieldId) const
202{
203 auto idSourceMember = desc.FindFieldId(memberName, classFieldId);
204 if (idSourceMember != ROOT::kInvalidDescriptorId)
205 return idSourceMember;
206
207 for (const auto &subFieldDesc : desc.GetFieldIterable(classFieldId)) {
208 const auto &subFieldName = subFieldDesc.GetFieldName();
209 if (subFieldName.length() > 2 && subFieldName[0] == ':' && subFieldName[1] == '_') {
210 idSourceMember = LookupMember(desc, memberName, subFieldDesc.GetId());
211 if (idSourceMember != ROOT::kInvalidDescriptorId)
212 return idSourceMember;
213 }
214 }
215
217}
218
219void ROOT::RRuleField::SetStagingClass(const std::string &className, unsigned int classVersion)
220{
221 TClass::GetClass(className.c_str())->GetStreamerInfo(classVersion);
222 if (classVersion != GetTypeVersion() || className != GetTypeName()) {
223 fStagingClass = TClass::GetClass((className + std::string("@@") + std::to_string(classVersion)).c_str());
224 if (!fStagingClass) {
225 // For a rename rule, we may simply ask for the old class name
226 fStagingClass = TClass::GetClass(className.c_str());
227 }
228 } else {
229 fStagingClass = fClass;
230 }
231 R__ASSERT(fStagingClass);
232 R__ASSERT(static_cast<unsigned int>(fStagingClass->GetClassVersion()) == classVersion);
233}
234
235void ROOT::RRuleField::PrepareStagingArea(const std::vector<const TSchemaRule *> &rules,
236 const ROOT::RNTupleDescriptor &desc,
237 const ROOT::RFieldDescriptor &classFieldDesc)
238{
239 std::size_t stagingAreaSize = 0;
240 for (const auto rule : rules) {
241 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
242 auto [itr, isNew] = fStagingItems.emplace(source->GetName(), RStagingItem());
243 if (!isNew) {
244 // This source member has already been processed by another rule (and we only support one type per member)
245 continue;
246 }
247 RStagingItem &stagingItem = itr->second;
248
249 const auto memberFieldId = LookupMember(desc, source->GetName(), classFieldDesc.GetId());
250 if (memberFieldId == kInvalidDescriptorId) {
251 throw RException(R__FAIL(std::string("cannot find on disk rule source member ") + GetTypeName() + "." +
252 source->GetName()));
253 }
254
255 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
256 auto memberField = Create("" /* we don't need a field name */, std::string(memberType)).Unwrap();
257 memberField->SetOnDiskId(memberFieldId);
258 auto fieldZero = std::make_unique<RFieldZero>();
259 Internal::SetAllowFieldSubstitutions(*fieldZero, true);
260 fieldZero->Attach(std::move(memberField));
261 stagingItem.fField = std::move(fieldZero);
262
263 stagingItem.fOffset = fStagingClass->GetDataMemberOffset(source->GetName());
264 // Since we successfully looked up the source member in the RNTuple on-disk metadata, we expect it
265 // to be present in the TClass instance, too.
267 stagingAreaSize = std::max(stagingAreaSize, stagingItem.fOffset + stagingItem.fField->begin()->GetValueSize());
268 }
269 }
270
271 if (stagingAreaSize) {
272 R__ASSERT(static_cast<Int_t>(stagingAreaSize) <= fStagingClass->Size()); // we may have removed rules
273 // We use std::make_unique instead of MakeUninitArray to zero-initialize the staging area.
274 fStagingArea = std::make_unique<unsigned char[]>(stagingAreaSize);
275
276 for (const auto &[_, si] : fStagingItems) {
277 const auto &memberField = *si.fField->cbegin();
278 if (!(memberField.GetTraits() & kTraitTriviallyConstructible)) {
279 CallConstructValueOn(memberField, fStagingArea.get() + si.fOffset);
280 }
281 }
282 }
283}
284
285std::vector<const ROOT::TSchemaRule *> ROOT::RRuleField::FindRules(const ROOT::RFieldDescriptor *fieldDesc) const
286{
288
289 const auto ruleset = fClass->GetSchemaRules();
290 if (!ruleset)
291 return rules;
292
293 if (!fieldDesc) {
294 // If we have no on-disk information for the field, we still process the rules on the current in-memory version
295 // of the class
296 rules = ruleset->FindRules(fClass->GetName(), fClass->GetClassVersion(), fClass->GetCheckSum());
297 } else {
298 // We need to change (back) the name normalization from RNTuple to ROOT Meta
299 std::string normalizedName;
300 TClassEdit::GetNormalizedName(normalizedName, fieldDesc->GetTypeName());
301 // We do have an on-disk field that correspond to the current RClassField instance. Ask for rules matching the
302 // on-disk version of the field.
303 if (fieldDesc->GetTypeChecksum()) {
304 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion(), *fieldDesc->GetTypeChecksum());
305 } else {
306 rules = ruleset->FindRules(normalizedName, fieldDesc->GetTypeVersion());
307 }
308 }
309
310 // Cleanup and sort rules
311 // Check that any given source member uses the same type in all rules
312 std::unordered_map<std::string, std::string> sourceNameAndType;
313 std::size_t nskip = 0; // skip whole-object-rules that were moved to the end of the rules vector
314 for (auto itr = rules.begin(); itr != rules.end() - nskip;) {
315 const auto rule = *itr;
316
317 // Erase unknown rule types
318 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
320 << "ignoring I/O customization rule with unsupported type: " << rule->GetRuleType();
321 itr = rules.erase(itr);
322 continue;
323 }
324
325 bool hasConflictingSourceMembers = false;
326 for (auto source : TRangeDynCast<TSchemaRule::TSources>(rule->GetSource())) {
327 auto memberType = source->GetTypeForDeclaration() + source->GetDimensions();
328 auto [itrSrc, isNew] = sourceNameAndType.emplace(source->GetName(), memberType);
329 if (!isNew && (itrSrc->second != memberType)) {
331 << "ignoring I/O customization rule due to conflicting source member type: " << itrSrc->second << " vs. "
332 << memberType << " for member " << source->GetName();
333 hasConflictingSourceMembers = true;
334 break;
335 }
336 }
337 if (hasConflictingSourceMembers) {
338 itr = rules.erase(itr);
339 continue;
340 }
341
342 // Rules targeting the entire object need to be executed at the end
343 if (rule->GetTarget() == nullptr) {
344 nskip++;
345 if (itr != rules.end() - nskip)
346 std::iter_swap(itr++, rules.end() - nskip);
347 continue;
348 }
349
350 ++itr;
351 }
352
353 return rules;
354}
355
356void ROOT::RRuleField::AddReadCallbacksFromIORule(const TSchemaRule *rule, std::size_t subObjectOffset)
357{
358 auto func = rule->GetReadFunctionPointer();
359 if (func == nullptr) {
360 // Can happen for rename rules
361 return;
362 }
363 fReadCallbacks.emplace_back(
364 [func, subObjectOffset, stagingClass = fStagingClass, stagingArea = fStagingArea.get()](void *target) {
365 TVirtualObject onfileObj{nullptr};
366 onfileObj.fClass = stagingClass;
367 onfileObj.fObject = stagingArea;
368 func(static_cast<char *>(target) + subObjectOffset, &onfileObj);
369 onfileObj.fObject = nullptr; // TVirtualObject does not own the value
370 });
371}
372
373//------------------------------------------------------------------------------
374
375ROOT::RClassField::RClassField(std::string_view fieldName, const RClassField &source)
376 : ROOT::RRuleField(fieldName, source.GetTypeName(), source.fClass, ROOT::ENTupleStructure::kRecord),
377 fSubfieldsInfo(source.fSubfieldsInfo)
378{
379 for (const auto &f : source.GetConstSubfields()) {
380 RFieldBase::Attach(f->Clone(f->GetFieldName()));
381 }
382 fTraits = source.GetTraits();
383}
384
385ROOT::RClassField::RClassField(std::string_view fieldName, std::string_view className)
386 : RClassField(fieldName, EnsureValidClass(className))
387{
388}
389
390ROOT::RClassField::RClassField(std::string_view fieldName, TClass *classp)
391 : ROOT::RRuleField(fieldName, GetRenormalizedTypeName(classp->GetName()), classp, ROOT::ENTupleStructure::kRecord)
392{
393 EnsureValidUserClass(fClass, *this, "RClassField");
394
396 throw ROOT::RException(R__FAIL(GetTypeName() + " is a SoA field and cannot be used through RClassField"));
397 }
398
403
404 std::string renormalizedAlias;
405 if (Internal::NeedsMetaNameAsAlias(classp->GetName(), renormalizedAlias))
406 fTypeAlias = renormalizedAlias;
407
408 int i = 0;
409 const auto *bases = fClass->GetListOfBases();
410 assert(bases);
411 for (auto baseClass : ROOT::Detail::TRangeStaticCast<TBaseClass>(*bases)) {
412 if (baseClass->GetDelta() < 0) {
413 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
414 " virtually inherits from " + baseClass->GetName()));
415 }
416 TClass *c = baseClass->GetClassPointer();
417 auto subField =
418 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
419 fTraits &= subField->GetTraits();
420 Attach(std::move(subField), RSubfieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
421 i++;
422 }
424 // Skip, for instance, unscoped enum constants defined in the class
425 if (dataMember->Property() & kIsStatic)
426 continue;
427 // Skip members explicitly marked as transient by user comment
428 if (!dataMember->IsPersistent()) {
429 // TODO(jblomer): we could do better
431 continue;
432 }
433
434 // NOTE: we use the already-resolved type name for the fields, otherwise TClass::GetClass may fail to resolve
435 // context-dependent types (e.g. typedefs defined in the class itself - which will not be fully qualified in
436 // the string returned by dataMember->GetFullTypeName())
437 std::string typeName{dataMember->GetTrueTypeName()};
438
439 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
440 if (dataMember->Property() & kIsArray) {
441 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim) {
442 typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
443 }
444 }
445
446 auto subField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
447
448 fTraits &= subField->GetTraits();
449 Attach(std::move(subField), RSubfieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
450 }
452}
453
455{
456 if (fStagingArea) {
457 for (const auto &[_, si] : fStagingItems) {
458 if (!(si.fField->GetTraits() & kTraitTriviallyDestructible)) {
459 auto deleter = GetDeleterOf(*si.fField);
460 deleter->operator()(fStagingArea.get() + si.fOffset, true /* dtorOnly */);
461 }
462 }
463 }
464}
465
466void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubfieldInfo info)
467{
468 fSubfieldsInfo.push_back(info);
469 RFieldBase::Attach(std::move(child));
470}
471
472std::unique_ptr<ROOT::RFieldBase> ROOT::RClassField::CloneImpl(std::string_view newName) const
473{
474 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
475}
476
477std::size_t ROOT::RClassField::AppendImpl(const void *from)
478{
479 std::size_t nbytes = 0;
480 for (unsigned i = 0; i < fSubfields.size(); i++) {
481 nbytes += CallAppendOn(*fSubfields[i], static_cast<const unsigned char *>(from) + fSubfieldsInfo[i].fOffset);
482 }
483 return nbytes;
484}
485
487{
488 for (const auto &[_, si] : fStagingItems) {
489 CallReadOn(*si.fField, globalIndex, fStagingArea.get() + si.fOffset);
490 }
491 for (unsigned i = 0; i < fSubfields.size(); i++) {
492 CallReadOn(*fSubfields[i], globalIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
493 }
494}
495
497{
498 for (const auto &[_, si] : fStagingItems) {
499 CallReadOn(*si.fField, localIndex, fStagingArea.get() + si.fOffset);
500 }
501 for (unsigned i = 0; i < fSubfields.size(); i++) {
502 CallReadOn(*fSubfields[i], localIndex, static_cast<unsigned char *>(to) + fSubfieldsInfo[i].fOffset);
503 }
504}
505
507{
508 std::vector<const TSchemaRule *> rules;
509 // On-disk members that are not targeted by an I/O rule; all other sub fields of the in-memory class
510 // will be marked as artificial (added member in a new class version or member set by rule).
511 std::unordered_set<std::string> regularSubfields;
512 // We generally don't support changing the number of base classes, with the exception of changing from/to zero
513 // base classes. The variable stores the number of on-disk base classes.
514 int nOnDiskBaseClasses = 0;
515
516 if (GetOnDiskId() == kInvalidDescriptorId) {
517 // This can happen for added base classes or added members of class type
518 rules = FindRules(nullptr);
519 if (!rules.empty())
520 SetStagingClass(GetTypeName(), GetTypeVersion());
521 } else {
522 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
523 const ROOT::RNTupleDescriptor &desc = descriptorGuard.GetConstRef();
524 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
525
526 if (fieldDesc.GetStructure() == ENTupleStructure::kStreamer) {
527 // Streamer field on disk but meanwhile the type can be represented as a class field; replace this field
528 // by a streamer field to read the data from disk.
529 auto substitute = std::make_unique<RStreamerField>(GetFieldName(), GetTypeName());
530 substitute->SetOnDiskId(GetOnDiskId());
531 return substitute;
532 }
533
534 for (auto linkId : fieldDesc.GetLinkIds()) {
535 const auto &subFieldDesc = desc.GetFieldDescriptor(linkId);
536 regularSubfields.insert(subFieldDesc.GetFieldName());
537 if (!subFieldDesc.GetFieldName().empty() && subFieldDesc.GetFieldName()[0] == ':')
538 nOnDiskBaseClasses++;
539 }
540
541 rules = FindRules(&fieldDesc);
542
543 // 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
544 // (source) type name and version/checksum.
545 if (rules.empty()) {
546 // Otherwise we require compatible type names, after renormalization. GetTypeName() is already renormalized,
547 // but RNTuple data written with ROOT v6.34 might not have renormalized the field type name. Ask the
548 // RNTupleDescriptor, which knows about the spec version, for a fixed up type name.
549 std::string descTypeName = desc.GetTypeNameForComparison(fieldDesc);
550 if (GetTypeName() != descTypeName) {
551 throw RException(R__FAIL("incompatible type name for field " + GetFieldName() + ": " + GetTypeName() +
552 " vs. " + descTypeName));
553 }
554 }
555
556 const bool hasSources = std::any_of(rules.begin(), rules.end(), [](const auto &r) {
557 return r->GetSource() && (r->GetSource()->GetEntries() > 0);
558 });
559
560 // A staging class (conversion streamer info) only exists if there is at least one rule that has an
561 // on disk source member defined.
562 if (hasSources) {
563 // For unversioned classes, the in-memory layout may by chance have the same transient version number
564 // than the recorded (transient, at the time of writing) on-disk version. Therefore, we also need to compare
565 // the checksums to find out if we need a conversion streamer info.
566 std::uint32_t assignedVersionForOnDiskLayout = fieldDesc.GetTypeVersion();
567 R__ASSERT(fieldDesc.GetTypeChecksum());
568 if (fieldDesc.GetTypeVersion() != GetTypeVersion() || *fieldDesc.GetTypeChecksum() != fClass->GetCheckSum() ||
569 fieldDesc.GetTypeName() != GetTypeName()) {
570 auto oldCl = TClass::GetClass(fieldDesc.GetTypeName().c_str());
571 R__ASSERT(oldCl);
572 auto onDiskStreamerInfo = oldCl->FindStreamerInfo(*fieldDesc.GetTypeChecksum());
573 R__ASSERT(onDiskStreamerInfo);
574 assignedVersionForOnDiskLayout = onDiskStreamerInfo->GetClassVersion();
575 }
576 SetStagingClass(fieldDesc.GetTypeName(), assignedVersionForOnDiskLayout);
577 PrepareStagingArea(rules, desc, fieldDesc);
578 for (auto &[_, si] : fStagingItems) {
579 Internal::CallConnectPageSourceOnField(*si.fField, pageSource);
580 si.fField = std::move(static_cast<RFieldZero *>(si.fField.get())->ReleaseSubfields()[0]);
581 }
582 }
583
584 // Remove target member of read rules from the list of regular members of the underlying on-disk field
585 for (const auto rule : rules) {
586 if (!rule->GetTarget())
587 continue;
588
589 for (const auto target : ROOT::Detail::TRangeStaticCast<const TObjString>(*rule->GetTarget())) {
590 regularSubfields.erase(std::string(target->GetString()));
591 }
592 }
593 }
594
595 for (const auto rule : rules) {
596 AddReadCallbacksFromIORule(rule);
597 }
598
599 // Iterate over all sub fields in memory and mark those as missing that are not in the descriptor.
600 int nInMemoryBaseClasses = 0;
601 for (auto &field : fSubfields) {
602 const auto &fieldName = field->GetFieldName();
603 if (regularSubfields.count(fieldName) == 0) {
604 CallSetArtificialOn(*field);
605 }
606 if (!fieldName.empty() && fieldName[0] == ':')
607 nInMemoryBaseClasses++;
608 }
609
610 if (nInMemoryBaseClasses != 0 && nOnDiskBaseClasses != 0 && nInMemoryBaseClasses != nOnDiskBaseClasses) {
611 throw RException(
612 R__FAIL(std::string("incompatible number of base classes for field ") + GetFieldName() + ": " + GetTypeName() +
613 ", " + std::to_string(nInMemoryBaseClasses) +
614 " base classes in memory "
615 " vs. " +
616 std::to_string(nOnDiskBaseClasses) + " base classes on-disk\n" +
618 }
619
620 return nullptr;
621}
622
624{
625 EnsureMatchingOnDiskField(desc, kDiffTypeVersion | kDiffTypeName).ThrowOnError();
626}
627
629{
630 fClass->New(where);
631}
632
634
635void ROOT::RClassField::RClassDeleter::operator()(void *objPtr, bool dtorOnly)
636{
637 fClass->Destructor(objPtr, true /* dtorOnly */);
638 RDeleter::operator()(objPtr, dtorOnly);
639}
640
641std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue &value) const
642{
643 std::vector<RValue> result;
644 auto valuePtr = value.GetPtr<void>();
645 auto charPtr = static_cast<unsigned char *>(valuePtr.get());
646 result.reserve(fSubfields.size());
647 for (unsigned i = 0; i < fSubfields.size(); i++) {
648 result.emplace_back(
649 fSubfields[i]->BindValue(std::shared_ptr<void>(valuePtr, charPtr + fSubfieldsInfo[i].fOffset)));
650 }
651 return result;
652}
653
655{
656 return fClass->GetClassSize();
657}
658
660{
661 const auto align = fClass->GetClassAlignment();
662 EnsureValidAlignment(align);
663 return align;
664}
665
667{
668 return fClass->GetClassVersion();
669}
670
672{
673 return fClass->GetCheckSum();
674}
675
676const std::type_info *ROOT::RClassField::GetPolymorphicTypeInfo() const
677{
678 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
679 if (!polymorphic) {
680 return nullptr;
681 }
682 return fClass->GetTypeInfo();
683}
684
686{
687 visitor.VisitClassField(*this);
688}
689
690//------------------------------------------------------------------------------
691
692ROOT::RSoAField::RSoAField(std::string_view fieldName, const RSoAField &source)
693 : ROOT::RRuleField(fieldName, source.GetTypeName(), source.fClass, ROOT::ENTupleStructure::kCollection),
694 fSoAMemberOffsets(source.fSoAMemberOffsets)
695{
696 fTraits = source.GetTraits();
697 Attach(source.fSubfields[0]->Clone(source.fSubfields[0]->GetFieldName()));
698 fRecordMemberFields = fSubfields[0]->GetMutableSubfields();
700 for (const auto f : fRecordMemberFields)
701 fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
702 fLockSplitFields = std::make_unique<std::mutex>();
703}
704
705ROOT::RSoAField::RSoAField(std::string_view fieldName, std::string_view className)
706 : RSoAField(fieldName, EnsureValidClass(className))
707{
708}
709
711 const RSoAField &nestedSoA, std::size_t offsetInParent,
712 const std::function<RFieldBase *(const std::string &)> &fnRecordFieldFinder)
713{
714 const std::size_t nNestedRecordMemberFields = nestedSoA.fRecordMemberFields.size();
715
716 // The qualified field name of fields in nestedSoA->fRecordMemberFields will have a "<field name>._0."
717 // prefix because these fields are rooted in a collection named after the nested SoA field.
718 //
719 // E.g., in the following example:
720 //
721 // struct SoA_A { struct Record_A {
722 // SoA_B fB; Record_B fB;
723 // }; };
724 //
725 // struct SoA_B { struct Record_B {
726 // ROOT::RVec<float> fX; float fX;
727 // }; };
728 //
729 // The on-disk schema of SoA_A is "collection of Record_A", and the on-disk schema of SoA_B is
730 // "collection of Record_B".
731 // The qualified field name of fX in the subfield hiararchy of SoA_A is <field name>._0.fB.fX.
732 // The qualified field name of fX in the subfield hiararchy of SoA_B is <field name>._0.fX.
733 const auto lenPrefix = nestedSoA.GetFieldName().length() + strlen("._0.");
734
735 for (std::size_t i = 0; i < nNestedRecordMemberFields; ++i) {
736 const auto fieldNameForMatching =
737 nestedSoA.GetFieldName() + "." + nestedSoA.fRecordMemberFields[i]->GetQualifiedFieldName().substr(lenPrefix);
738
739 fRecordMemberFields.emplace_back(fnRecordFieldFinder(fieldNameForMatching));
740 fRecordMemberDeleters.emplace_back(GetDeleterOf(*nestedSoA.fRecordMemberFields[i]));
741 fSoAMemberOffsets.emplace_back(offsetInParent + nestedSoA.fSoAMemberOffsets[i]);
742 }
743}
744
746{
747 // Build a map of all subfields (nested) of the underlying record type. Map the fully qualified name of the
748 // subfields to their field pointer, so that we can later match the subfields of the SoA class to their corresponding
749 // fields in the underlying record type. Note that the members of the SoA class and the underlying record type
750 // can have different ordering. However, the base classes of the SoA class and the underlying record type must match
751 // in order.
752
753 std::vector<RFieldBase *> realRecordMemberFields; // Contains all subfields of the underlying record type
754 // Qualified field name --> index in realRecordMemberFields
755 std::unordered_map<std::string, std::size_t> recordFieldNameToIdx;
756
757 // Count the top-level subfields of the underlying record type for cross-check with the SoA type
758 unsigned int nDirectRecordSubfields = 0;
759 unsigned int nDirectRecordBases = 0;
760
761 for (auto itr = fSubfields[0]->begin(), iEnd = fSubfields[0]->end(); itr != iEnd; ++itr) {
762 if (itr->GetParent() == fSubfields[0].get()) {
763 if (itr->GetFieldName()[0] == ':') {
764 nDirectRecordBases++;
765 } else {
766 nDirectRecordSubfields++;
767 }
768 }
769
770 // Build the qualified field name for matching. We root the qualified field name at the underlying record type.
771 auto qualifiedName = itr->GetFieldName();
772 auto parent = itr->GetParent();
773 while (parent != fSubfields[0].get()) {
774 qualifiedName = parent->GetFieldName() + "." + qualifiedName;
775 parent = parent->GetParent();
776 }
777 recordFieldNameToIdx[qualifiedName] = realRecordMemberFields.size();
778
779 realRecordMemberFields.emplace_back(&(*itr));
780 }
781
782 // Base classes are treated as unrolled nested SoA classes
783 const auto *soaBases = fClass->GetListOfBases();
784 if (soaBases->GetSize() != static_cast<Int_t>(nDirectRecordBases)) {
785 throw RException(R__FAIL(std::string("number of base classes don't match between SoA class ") + GetFieldName() +
786 " and its underlying record type"));
787 }
788 unsigned int baseIdx = 0;
789 for (auto base : ROOT::Detail::TRangeStaticCast<TBaseClass>(*fClass->GetListOfBases())) {
790 if (base->GetDelta() < 0) {
791 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + GetTypeName() +
792 " virtually inherits from " + base->GetName()));
793 }
794 TClass *cl = base->GetClassPointer();
795
796 const auto baseFieldName = std::string(":_") + std::to_string(baseIdx);
797
798 // SoA class `A` is allowed to inherit from a SoA class `B` whose underlying record type is `X` if and only if
799 // the underlying record type of `A` inherits from a type `X`.
800 const auto underlyingBaseTypeName = ROOT::Internal::GetRNTupleSoARecord(cl);
801 auto recordBaseField = realRecordMemberFields[recordFieldNameToIdx[baseFieldName]];
802 if (underlyingBaseTypeName != recordBaseField->GetTypeName()) {
803 throw RException(R__FAIL(std::string("inheritance of SoA class ") + GetFieldName() +
804 " does not match its underlying record type"));
805 }
806
807 std::unique_ptr<RSoAField> soaBaseField;
808 try {
809 soaBaseField = std::make_unique<RSoAField>(baseFieldName, cl->GetName());
810 } catch (const RException &e) {
811 throw RException(R__FAIL(std::string("invalid field type in base class: ") + cl->GetName() + " of SoA field " +
812 GetFieldName() + " (" + e.what() + ")"));
813 }
814
815 GraftNestedMemberFields(*soaBaseField, base->GetDelta(), [&](const std::string &name) {
816 return realRecordMemberFields[recordFieldNameToIdx[name]];
817 });
818
819 for (const RRule &r : soaBaseField->fRules) {
820 fRules.emplace_back(r);
821 fRules.back().fOffset += base->GetDelta();
822 }
823
824 baseIdx++;
825 }
826
827 unsigned int nMembers = 0;
828 for (auto dataMember : ROOT::Detail::TRangeStaticCast<TDataMember>(*fClass->GetListOfDataMembers())) {
829 // NOTE: ReconstructSplitFields() will also traverse the data members and need to apply the same rules for
830 // skipping members
831
832 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
833 continue;
834
835 if (dataMember->Property() & kIsArray) {
836 throw RException(R__FAIL(std::string("unsupported array type in SoA class: ") + dataMember->GetName()));
837 }
838
839 const std::string typeName{dataMember->GetTrueTypeName()};
840 auto dmField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
841
842 auto itr = recordFieldNameToIdx.find(dmField->GetFieldName());
843 if (itr == recordFieldNameToIdx.end()) {
844 throw RException(R__FAIL(std::string("unexpected SoA member: ") + dmField->GetFieldName()));
845 }
846 auto underlyingField = realRecordMemberFields[itr->second];
847 assert(dmField->GetFieldName() == underlyingField->GetFieldName());
848
849 if (auto soaField = dynamic_cast<RSoAField *>(dmField.get())) {
850 if (ROOT::Internal::GetRNTupleSoARecord(soaField->fClass) != underlyingField->GetTypeName()) {
851 throw RException(R__FAIL(std::string("nested SoA field ") + soaField->GetQualifiedFieldName() + " [" +
852 soaField->GetTypeName() + "] does not match underlying type " +
853 underlyingField->GetTypeName()));
854 }
855
856 GraftNestedMemberFields(*soaField, dataMember->GetOffset(), [&](const std::string &name) {
857 return realRecordMemberFields[recordFieldNameToIdx[name]];
858 });
859
860 for (const RRule &r : soaField->fRules) {
861 fRules.emplace_back(r);
862 fRules.back().fOffset += dataMember->GetOffset();
863 }
864 } else if (auto vecField = dynamic_cast<RRVecField *>(dmField.get())) {
865 if (vecField->begin()->GetTypeName() != underlyingField->GetTypeName() ||
866 vecField->begin()->GetTypeAlias() != underlyingField->GetTypeAlias()) {
867 const std::string leftType =
868 vecField->begin()->GetTypeName() +
869 (vecField->begin()->GetTypeAlias().empty() ? "" : " [" + vecField->begin()->GetTypeAlias() + "]");
870 const std::string rightType =
871 underlyingField->GetTypeName() +
872 (underlyingField->GetTypeAlias().empty() ? "" : " [" + underlyingField->GetTypeAlias() + "]");
873 throw RException(R__FAIL(std::string("SoA member type mismatch: ") + vecField->GetFieldName() + " (" +
874 leftType + " vs. " + rightType + ")"));
875 }
876
877 fRecordMemberFields.emplace_back(underlyingField);
878 fRecordMemberDeleters.emplace_back(GetDeleterOf(*underlyingField));
879 fSoAMemberOffsets.emplace_back(dataMember->GetOffset());
880 } else {
881 throw RException(R__FAIL("invalid field type in SoA class: " + dmField->GetTypeName()));
882 }
883
884 nMembers++;
885 }
886 if (nDirectRecordSubfields != nMembers) {
887 throw RException(R__FAIL("missing SoA members"));
888 }
889
890 auto schemaRules = FindRules(nullptr);
891 for (const auto &r : schemaRules)
892 fRules.emplace_back(RRule{r, 0});
893}
894
895ROOT::RSoAField::RSoAField(std::string_view fieldName, TClass *clSoA)
896 : ROOT::RRuleField(fieldName, GetRenormalizedTypeName(clSoA->GetName()), clSoA, ROOT::ENTupleStructure::kCollection)
897{
898 EnsureValidUserClass(fClass, *this, "RSoAField");
899 const auto recordTypeName = ROOT::Internal::GetRNTupleSoARecord(fClass);
900 if (recordTypeName.empty()) {
901 throw ROOT::RException(R__FAIL(std::string("class ") + GetTypeName() +
902 " is not marked with the rntupleSoARecord "
903 "dictionary option; cannot create corresponding RSoAField."));
904 }
905 try {
906 Attach(std::make_unique<ROOT::RClassField>("_0", recordTypeName));
907 } catch (ROOT::RException &e) {
908 throw RException(R__FAIL("invalid record type of SoA field " + GetTypeName() + " [" + e.what() + "]"));
909 }
910 if (fClass->GetClassVersion() <= 0) {
911 throw RException(R__FAIL(std::string("SoA class ") + GetTypeName() + " is transient or unversioned."));
912 }
913 if (static_cast<std::uint32_t>(fClass->GetClassVersion()) != fSubfields[0]->GetTypeVersion()) {
914 throw RException(R__FAIL(std::string("version mismatch between SoA type and underlying record type: ") +
915 std::to_string(fClass->GetClassVersion()) + " vs. " +
916 std::to_string(fSubfields[0]->GetTypeVersion())));
917 }
918
920
921 std::string renormalizedAlias;
922 if (ROOT::Internal::NeedsMetaNameAsAlias(fClass->GetName(), renormalizedAlias))
923 fTypeAlias = renormalizedAlias;
924
926 fLockSplitFields = std::make_unique<std::mutex>();
927}
928
929std::unique_ptr<ROOT::RFieldBase> ROOT::RSoAField::CloneImpl(std::string_view newName) const
930{
931 return std::unique_ptr<RSoAField>(new RSoAField(newName, *this));
932}
933
943
945{
946 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
947}
948
950{
951 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
952}
953
954std::size_t ROOT::RSoAField::AppendImpl(const void *from)
955{
956 const std::size_t nSoAMembers = fSoAMemberOffsets.size();
957
958 std::size_t N = 0; // Set by first SoA member and verified for the rest
959 for (std::size_t i = 0; i < nSoAMembers; ++i) {
960 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
961 auto [beginPtr, sizePtr, _] = ROOT::Internal::GetRVecDataMembers(rvecPtr);
962 assert(*sizePtr >= 0);
963 if (i == 0) {
964 N = *sizePtr;
965 } else {
966 if (static_cast<std::size_t>(*sizePtr) != N) {
967 const auto f = fRecordMemberFields[i];
968 throw RException(R__FAIL("SoA length mismatch for " + f->GetFieldName() + ": " + std::to_string(*sizePtr) +
969 " vs. " + std::to_string(N) + " (expected)"));
970 }
971 }
972 }
973
974 std::size_t nbytes = 0;
975 if (N > 0) {
976 for (std::size_t i = 0; i < nSoAMembers; ++i) {
977 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
978 auto [beginPtr, _, __] = ROOT::Internal::GetRVecDataMembers(rvecPtr);
979 RFieldBase *memberField = fRecordMemberFields[i];
980 if (memberField->IsSimple()) {
981 GetPrincipalColumnOf(*memberField)->AppendV(*beginPtr, N);
982 nbytes += N * GetPrincipalColumnOf(*memberField)->GetElement()->GetPackedSize();
983 } else {
984 for (std::size_t j = 0; j < N; ++j) {
985 nbytes += CallAppendOn(*memberField, *beginPtr + j * memberField->GetValueSize());
986 }
987 }
988 }
989 }
990
991 fNWritten += N;
992 fPrincipalColumn->Append(&fNWritten);
993 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
994}
995
997{
998 // Read collection info for this entry
1000 RNTupleLocalIndex collectionStart;
1001 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &N);
1002
1003 const auto nSoAMembers = fSoAMemberOffsets.size();
1004 for (std::size_t i = 0; i < nSoAMembers; ++i) {
1005 RFieldBase *memberField = fRecordMemberFields[i];
1006 const auto memberSize = memberField->GetValueSize();
1007 void *rvecPtr = static_cast<unsigned char *>(to) + fSoAMemberOffsets[i];
1008 auto begin = ROOT::RRVecField::ResizeRVec(rvecPtr, N, memberSize, memberField, fRecordMemberDeleters[i].get());
1009
1010 if (N == 0)
1011 continue;
1012
1013 if (memberField->IsSimple()) {
1014 GetPrincipalColumnOf(*memberField)->ReadV(collectionStart, N, begin);
1015 } else {
1016 if (memberField->IsArtificial()) {
1017 // Other artificial fields simply don't read at all. This does not work here because then
1018 // the vector elements of trivial types would be left uninitialized (complex types explicitly call
1019 // the constructor on vector resize). Thus we explicitly default-initialize trivial types.
1020 // Note that this causes a subtle difference in behavior: if the added member is default-initialized to
1021 // a non-zero value, this will be forgotten in the SoA layout.
1022 if (memberField->GetTraits() & kTraitTriviallyConstructible) {
1023 std::memset(begin, 0, N * memberSize);
1024 }
1025 } else {
1026 for (std::size_t j = 0; j < N; ++j) {
1027 CallReadOn(*memberField, collectionStart + j, begin + (j * memberSize));
1028 }
1029 }
1030 }
1031 }
1032}
1033
1034std::unique_ptr<ROOT::RFieldBase> ROOT::RSoAField::BeforeConnectPageSource(ROOT::Internal::RPageSource & /*pageSource*/)
1035{
1036 // Most of the heavy lifting is done by the actual subfields that map onto the on-disk AoS schema.
1037 // In the SoA field itself, we only need to take care of the in-memory part of the rule processing.
1038 // One complication is that for non-rename rules, we manually need to recurse into base (SoA) classes and
1039 // nested (SoA) classes because we don't have them in the field tree.
1040
1041 // For now, allow only rename rules and whole-object rules
1042
1043 const bool hasSources = std::any_of(fRules.begin(), fRules.end(), [](const auto &r) {
1044 return r.fRule->GetSource() && (r.fRule->GetSource()->GetEntries() > 0);
1045 });
1046 const bool hasTargets =
1047 std::any_of(fRules.begin(), fRules.end(), [](const auto &r) { return r.fRule->GetTarget(); });
1048
1049 if (hasSources) {
1050 throw RException(R__FAIL("I/O customization rules with sources are currently unsupported for SoA fields (" +
1051 GetTypeName() + ")"));
1052 }
1053
1054 if (hasTargets) {
1055 throw RException(R__FAIL("I/O customization rules with targets are currently unsupported for SoA fields (" +
1056 GetTypeName() + ")"));
1057 }
1058
1059 for (const auto &r : fRules) {
1060 AddReadCallbacksFromIORule(r.fRule, r.fOffset);
1061 }
1062
1063 return nullptr;
1064}
1065
1067{
1068 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1069}
1070
1071void ROOT::RSoAField::ConstructValue(void *where) const
1072{
1073 fClass->New(where);
1074}
1075
1076ROOT::RSoAField::RSoADeleter::RSoADeleter(TClass *cl) : RDeleter(cl->GetClassAlignment()), fSoAClass(cl) {}
1077
1078void ROOT::RSoAField::RSoADeleter::operator()(void *objPtr, bool dtorOnly)
1079{
1080 fSoAClass->Destructor(objPtr, true /* dtorOnly */);
1081 RDeleter::operator()(objPtr, dtorOnly);
1082}
1083
1085{
1086 std::lock_guard<std::mutex> lockGuard(*fLockSplitFields);
1087 if (fSplitFields)
1088 return;
1089
1090 fSplitFields = std::make_unique<std::vector<std::unique_ptr<ROOT::RFieldBase>>>();
1091 fSplitOffsets = std::make_unique<std::vector<std::size_t>>();
1092
1093 unsigned int baseIdx = 0;
1095 TClass *cl = base->GetClassPointer();
1096 auto baseField = RFieldBase::Create(std::string(":_" + std::to_string(baseIdx)), cl->GetName()).Unwrap();
1097 fSplitFields->emplace_back(std::move(baseField));
1098 fSplitOffsets->emplace_back(base->GetDelta());
1099 baseIdx++;
1100 }
1101
1103 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
1104 continue;
1105
1106 const std::string typeName{dataMember->GetTrueTypeName()};
1107 auto dmField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
1108 fSplitFields->emplace_back(std::move(dmField));
1109 fSplitOffsets->emplace_back(dataMember->GetOffset());
1110 }
1111}
1112
1113std::vector<ROOT::RFieldBase::RValue> ROOT::RSoAField::SplitValue(const RValue &value) const
1114{
1116 const auto nSplitFields = fSplitFields->size();
1117
1118 auto valuePtr = value.GetPtr<void>();
1119 auto soaPtr = static_cast<unsigned char *>(valuePtr.get());
1120 std::vector<RValue> values;
1121 values.reserve(nSplitFields);
1122 for (std::size_t i = 0; i < nSplitFields; ++i) {
1123 values.emplace_back((*fSplitFields)[i]->BindValue(std::shared_ptr<void>(valuePtr, soaPtr + (*fSplitOffsets)[i])));
1124 }
1125 return values;
1126}
1127
1129{
1130 return fClass->GetClassSize();
1131}
1132
1134{
1135 return fClass->GetClassVersion();
1136}
1137
1139{
1140 return fClass->GetCheckSum();
1141}
1142
1144{
1145 const auto align = fClass->GetClassAlignment();
1146 EnsureValidAlignment(align);
1147 return align;
1148}
1149
1150const std::type_info *ROOT::RSoAField::GetPolymorphicTypeInfo() const
1151{
1152 // TODO(jblomer): factor out
1153 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
1154 if (!polymorphic) {
1155 return nullptr;
1156 }
1157 return fClass->GetTypeInfo();
1158}
1159
1161{
1162 visitor.VisitSoAField(*this);
1163}
1164
1165//------------------------------------------------------------------------------
1166
1167std::unique_ptr<ROOT::RFieldBase> ROOT::Internal::CreateEmulatedEnumField(std::string_view fieldName,
1168 std::string_view emulatedFromType,
1169 std::string_view underlyingIntType)
1170{
1171 return std::unique_ptr<RFieldBase>(new REnumField(fieldName, emulatedFromType, underlyingIntType));
1172}
1173
1174ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
1175 : REnumField(fieldName, EnsureValidEnum(enumName))
1176{
1177}
1178
1179ROOT::REnumField::REnumField(std::string_view fieldName, TEnum *enump)
1180 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
1181 false /* isSimple */)
1182{
1183 // Avoid accidentally supporting std types through TEnum.
1184 if (enump->Property() & kIsDefinedInStd) {
1185 throw RException(R__FAIL(GetTypeName() + " is not supported"));
1186 }
1187
1188 switch (enump->GetUnderlyingType()) {
1189 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
1190 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
1191 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
1192 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
1193 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
1194 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
1195 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
1196 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
1197 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
1198 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
1199 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
1200 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
1201 }
1202
1204}
1205
1206ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
1207 std::unique_ptr<RFieldBase> intField)
1208 : ROOT::RFieldBase(fieldName, enumName, ROOT::ENTupleStructure::kPlain, false /* isSimple */)
1209{
1210 Attach(std::move(intField));
1212}
1213
1214ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view emulatedFromType,
1215 std::string_view underlyingIntType)
1216 : ROOT::RFieldBase(fieldName, emulatedFromType, ROOT::ENTupleStructure::kPlain, false /* isSimple */)
1217{
1218 auto intField = Create("_0", std::string(underlyingIntType)).Unwrap();
1219 Attach(std::move(intField));
1221}
1222
1223std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
1224{
1225 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
1226 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
1227}
1228
1230{
1231 // TODO(jblomer): allow enum to enum conversion only by rename rule
1232 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1233}
1234
1235std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
1236{
1237 std::vector<RValue> result;
1238 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
1239 return result;
1240}
1241
1243{
1244 visitor.VisitEnumField(*this);
1245}
1246
1247//------------------------------------------------------------------------------
1248
1249ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
1250 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1251{
1252 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1253 if (typeAlias != GetTypeName())
1254 fTypeAlias = typeAlias;
1255
1256 AttachItemFields(std::move(itemFields));
1257
1258 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
1259 auto *c = TClass::GetClass(GetTypeName().c_str());
1260 if (!c) {
1262 fSubfields[0]->GetTypeName(), fSubfields[1]->GetTypeName(), true /* silent */, 0, 0);
1263 if (pairInfo)
1264 c = pairInfo->GetClass();
1265 }
1266 if (!c)
1267 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1268 fSize = c->Size();
1269
1270 auto firstElem = c->GetRealData("first");
1271 if (!firstElem)
1272 throw RException(R__FAIL("first: no such member"));
1273 fOffsets.push_back(firstElem->GetThisOffset());
1274
1275 auto secondElem = c->GetRealData("second");
1276 if (!secondElem)
1277 throw RException(R__FAIL("second: no such member"));
1278 fOffsets.push_back(secondElem->GetThisOffset());
1279}
1280
1281std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
1282{
1283 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
1284 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
1285 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones)));
1286}
1287
1289{
1290 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1291
1292 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1293 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1294
1295 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1296 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1297 if (nOnDiskSubfields != 2) {
1298 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
1299 std::to_string(nOnDiskSubfields) + "\n" +
1300 Internal::GetTypeTraceReport(*this, desc)));
1301 }
1302}
1303
1304//------------------------------------------------------------------------------
1305
1308 bool readFromDisk)
1309{
1310 RIteratorFuncs ifuncs;
1311 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
1312 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
1313 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
1314 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
1315 (ifuncs.fNext != nullptr));
1316 return ifuncs;
1317}
1318
1320 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
1321 false /* isSimple */),
1322 fNWritten(0)
1323{
1324 if (!classp->GetCollectionProxy())
1325 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
1326 if (classp->Property() & kIsDefinedInStd) {
1327 static const std::vector<std::string> supportedStdTypes = {
1328 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
1329 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
1330 bool isSupported = false;
1331 for (const auto &tn : supportedStdTypes) {
1332 if (GetTypeName().rfind(tn, 0) == 0) {
1333 isSupported = true;
1334 break;
1335 }
1336 }
1337 if (!isSupported)
1338 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
1339 }
1340
1341 std::string renormalizedAlias;
1342 if (Internal::NeedsMetaNameAsAlias(classp->GetName(), renormalizedAlias))
1343 fTypeAlias = renormalizedAlias;
1344
1345 fProxy.reset(classp->GetCollectionProxy()->Generate());
1346 fProperties = fProxy->GetProperties();
1347 fCollectionType = fProxy->GetCollectionType();
1348 if (fProxy->HasPointers())
1349 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
1350
1351 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
1352 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1353}
1354
1355ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
1356 : RProxiedCollectionField(fieldName, EnsureValidClass(typeName))
1357{
1358 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
1360 throw RException(R__FAIL("custom associative collection proxies not supported"));
1361
1362 std::unique_ptr<ROOT::RFieldBase> itemField;
1363
1364 if (auto valueClass = fProxy->GetValueClass()) {
1365 // Element type is a class
1366 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
1367 } else {
1368 switch (fProxy->GetType()) {
1369 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
1370 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
1371 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
1372 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
1373 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
1374 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
1375 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
1376 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
1377 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
1378 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
1379 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
1380 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
1381 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
1382 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
1383 }
1384 }
1385
1386 fItemSize = itemField->GetValueSize();
1387 Attach(std::move(itemField));
1388}
1389
1390std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
1391{
1392 auto clone =
1393 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
1394 clone->fItemSize = fItemSize;
1395 clone->Attach(fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1396 return clone;
1397}
1398
1400{
1401 std::size_t nbytes = 0;
1402 unsigned count = 0;
1403 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
1404 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
1405 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1406 nbytes += CallAppendOn(*fSubfields[0], ptr);
1407 count++;
1408 }
1409
1410 fNWritten += count;
1411 fPrincipalColumn->Append(&fNWritten);
1412 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1413}
1414
1416{
1417 ROOT::NTupleSize_t nItems;
1418 RNTupleLocalIndex collectionStart;
1419 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1420
1421 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
1422 void *obj =
1423 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
1424
1425 unsigned i = 0;
1426 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
1427 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
1428 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
1429 }
1430 if (obj != to)
1431 fProxy->Commit(obj);
1432}
1433
1443
1445{
1446 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
1447}
1448
1450{
1451 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
1452}
1453
1455{
1456 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1457}
1458
1460{
1461 fProxy->New(where);
1462}
1463
1464std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
1465{
1466 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
1467 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
1468 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
1469 }
1470 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
1471}
1472
1474 std::shared_ptr<TVirtualCollectionProxy> proxy)
1475 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()), fProxy(std::move(proxy))
1476{
1477}
1478
1480 std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter, size_t itemSize)
1481 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()),
1482 fProxy(std::move(proxy)),
1483 fItemDeleter(std::move(itemDeleter)),
1484 fItemSize(itemSize)
1485{
1486 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1487}
1488
1490{
1491 if (fItemDeleter) {
1492 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), objPtr);
1493 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
1494 fItemDeleter->operator()(ptr, true /* dtorOnly */);
1495 }
1496 }
1497 fProxy->Destructor(objPtr, true /* dtorOnly */);
1498 RDeleter::operator()(objPtr, dtorOnly);
1499}
1500
1501std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
1502{
1503 std::vector<RValue> result;
1504 auto valueRawPtr = value.GetPtr<void>().get();
1505 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), valueRawPtr);
1506 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
1507 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1508 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
1509 }
1510 return result;
1511}
1512
1514{
1515 return fProxy->Sizeof();
1516}
1517
1519{
1520 const auto align = fProxy->GetCollectionClass()->GetClassAlignment();
1521 EnsureValidAlignment(align);
1522 return align;
1523}
1524
1529
1530//------------------------------------------------------------------------------
1531
1532ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
1533 : RProxiedCollectionField(fieldName,
1534 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
1535 fMapType(mapType)
1536{
1537 if (!itemField->GetTypeAlias().empty())
1538 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
1539
1540 auto *itemClass = fProxy->GetValueClass();
1541 fItemSize = itemClass->GetClassSize();
1542
1543 Attach(std::move(itemField), "_0");
1544}
1545
1546std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
1547{
1548 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1549}
1550
1552{
1553 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
1554
1555 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1556
1557 switch (fMapType) {
1558 case EMapType::kMap:
1559 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1560 default:
1561 break;
1562 // no restrictions for multimaps
1563 }
1564}
1565
1566//------------------------------------------------------------------------------
1567
1568ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1569 : ROOT::RProxiedCollectionField(fieldName,
1570 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1571 fSetType(setType)
1572{
1573 if (!itemField->GetTypeAlias().empty())
1574 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1575
1576 fItemSize = itemField->GetValueSize();
1577
1578 Attach(std::move(itemField), "_0");
1579}
1580
1581std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1582{
1583 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1584}
1585
1587{
1588 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1589 "std::unordered_map<"};
1590
1591 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1592
1593 switch (fSetType) {
1594 case ESetType::kSet:
1595 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1596 default:
1597 break;
1598 // no restrictions for multisets
1599 }
1600}
1601
1602//------------------------------------------------------------------------------
1603
1604namespace {
1605
1606/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1607class TBufferRecStreamer : public TBufferFile {
1608public:
1609 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1610
1611private:
1612 RCallbackStreamerInfo fCallbackStreamerInfo;
1613
1614public:
1615 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1616 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(std::move(callbackStreamerInfo))
1617 {
1618 }
1619 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1620};
1621
1622} // anonymous namespace
1623
1624ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1625 : RStreamerField(fieldName, EnsureValidClass(className))
1626{
1627}
1628
1629ROOT::RStreamerField::RStreamerField(std::string_view fieldName, TClass *classp)
1630 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1631 false /* isSimple */),
1632 fClass(classp),
1633 fIndex(0)
1634{
1636 throw RException(R__FAIL("streamer field " + GetFieldName() + " requires at least an emulated class"));
1639
1640 std::string renormalizedAlias;
1641 if (Internal::NeedsMetaNameAsAlias(classp->GetName(), renormalizedAlias))
1642 fTypeAlias = renormalizedAlias;
1643
1645 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1646 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1647 // implicit constructors and destructors.
1652}
1653
1654std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1655{
1656 // To get the correct TClass instance in the clone, we clone using the un-normalized type name
1657 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, fClass->GetName()));
1658}
1659
1660std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1661{
1662 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1663 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1664 fClass->Streamer(const_cast<void *>(from), buffer);
1665
1666 auto nbytes = buffer.Length();
1667 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1668 fIndex += nbytes;
1669 fPrincipalColumn->Append(&fIndex);
1670 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1671}
1672
1674{
1675 RNTupleLocalIndex collectionStart;
1676 ROOT::NTupleSize_t nbytes;
1677 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1678
1679 TBufferFile buffer(TBuffer::kRead, nbytes);
1680 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1681 fClass->Streamer(to, buffer);
1682}
1683
1693
1695{
1696 GenerateColumnsImpl<ROOT::Internal::RColumnIndex, std::byte>();
1697}
1698
1700{
1701 GenerateColumnsImpl<ROOT::Internal::RColumnIndex, std::byte>(desc);
1702}
1703
1705{
1706 source.RegisterStreamerInfos();
1707 return nullptr;
1708}
1709
1711{
1712 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1713}
1714
1716{
1717 fClass->New(where);
1718}
1719
1724
1726{
1727 fClass->Destructor(objPtr, true /* dtorOnly */);
1728 RDeleter::operator()(objPtr, dtorOnly);
1729}
1730
1740
1742{
1743 const auto align = fClass->GetClassAlignment();
1744 EnsureValidAlignment(align);
1745 return align;
1746}
1747
1749{
1750 return fClass->GetClassSize();
1751}
1752
1754{
1755 return fClass->GetClassVersion();
1756}
1757
1759{
1760 return fClass->GetCheckSum();
1761}
1762
1764{
1765 visitor.VisitStreamerField(*this);
1766}
1767
1768//------------------------------------------------------------------------------
1769
1771{
1772 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1773 return dataMember->GetOffset();
1774 }
1775 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1776}
1777
1778ROOT::RField<TObject>::RField(std::string_view fieldName, const RField<TObject> &source)
1779 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1780{
1782 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1783 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1784}
1785
1786ROOT::RField<TObject>::RField(std::string_view fieldName)
1787 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1788{
1789 assert(TObject::Class()->GetClassVersion() == 1);
1790
1792 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1793 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1794}
1795
1796std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1797{
1798 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1799}
1800
1801std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1802{
1803 // Cf. TObject::Streamer()
1804
1805 auto *obj = static_cast<const TObject *>(from);
1806 if (obj->TestBit(TObject::kIsReferenced)) {
1807 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1808 }
1809
1810 std::size_t nbytes = 0;
1811 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1812
1813 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1814 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1815 nbytes += CallAppendOn(*fSubfields[1], &bits);
1816
1817 return nbytes;
1818}
1819
1821{
1822 // Cf. TObject::Streamer()
1823
1824 auto *obj = static_cast<TObject *>(to);
1825 if (obj->TestBit(TObject::kIsReferenced)) {
1826 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1827 }
1828
1829 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1830
1831 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1832 bits |= bitIsOnHeap | TObject::kNotDeleted;
1833 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1834}
1835
1837{
1838 UInt_t uniqueID, bits;
1839 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1840 CallReadOn(*fSubfields[1], globalIndex, &bits);
1841 ReadTObject(to, uniqueID, bits);
1842}
1843
1845{
1846 UInt_t uniqueID, bits;
1847 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1848 CallReadOn(*fSubfields[1], localIndex, &bits);
1849 ReadTObject(to, uniqueID, bits);
1850}
1851
1853{
1854 return TObject::Class()->GetClassVersion();
1855}
1856
1858{
1859 return TObject::Class()->GetCheckSum();
1860}
1861
1863{
1864 new (where) TObject();
1865}
1866
1867std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1868{
1869 std::vector<RValue> result;
1870 // Use GetPtr<TObject> to type-check
1871 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1872 auto charPtr = static_cast<unsigned char *>(ptr.get());
1873 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1874 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1875 return result;
1876}
1877
1879{
1880 return sizeof(TObject);
1881}
1882
1884{
1885 return alignof(TObject);
1886}
1887
1889{
1890 visitor.VisitTObjectField(*this);
1891}
1892
1893//------------------------------------------------------------------------------
1894
1895ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1896 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1897{
1898 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1899 if (typeAlias != GetTypeName())
1900 fTypeAlias = typeAlias;
1901
1902 AttachItemFields(std::move(itemFields));
1903
1904 auto *c = TClass::GetClass(GetTypeName().c_str());
1905 if (!c)
1906 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1907 fSize = c->Size();
1908
1909 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1910 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1911 // following the order of the type list.
1912 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1913 // members, the assertion below will fail.
1914 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1915 std::string memberName("_" + std::to_string(i));
1916 auto member = c->GetRealData(memberName.c_str());
1917 if (!member)
1918 throw RException(R__FAIL(memberName + ": no such member"));
1919 fOffsets.push_back(member->GetThisOffset());
1920 }
1921}
1922
1923std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1924{
1925 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1926 itemClones.reserve(fSubfields.size());
1927 for (const auto &f : fSubfields) {
1928 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1929 }
1930 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones)));
1931}
1932
1934{
1935 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1936
1937 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1938 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1939
1940 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1941 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1942 const auto nSubfields = fSubfields.size();
1943 if (nOnDiskSubfields != nSubfields) {
1944 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1945 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1946 Internal::GetTypeTraceReport(*this, desc)));
1947 }
1948}
1949
1950//------------------------------------------------------------------------------
1951
1952namespace {
1953
1954// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1955constexpr std::size_t GetVariantTagSize()
1956{
1957 // Should be all zeros except for the tag, which is 1
1958 std::variant<char> t;
1959 constexpr auto sizeOfT = sizeof(t);
1960
1961 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1962 return sizeOfT == 2 ? 1 : 4;
1963}
1964
1965template <std::size_t VariantSizeT>
1966struct RVariantTag {
1967 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1968 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1969};
1970
1971} // anonymous namespace
1972
1974 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1975 fMaxItemSize(source.fMaxItemSize),
1976 fMaxAlignment(source.fMaxAlignment),
1977 fTagOffset(source.fTagOffset),
1978 fVariantOffset(source.fVariantOffset),
1979 fNWritten(source.fNWritten.size(), 0)
1980{
1981 for (const auto &f : source.GetConstSubfields())
1982 Attach(f->Clone(f->GetFieldName()));
1983 fTraits = source.fTraits;
1984}
1985
1986ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1987 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1988 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1989{
1990 // The variant needs to initialize its own tag member
1991 fTraits |= kTraitTriviallyDestructible & ~kTraitTriviallyConstructible;
1992
1993 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1994 if (typeAlias != GetTypeName())
1995 fTypeAlias = typeAlias;
1996
1997 auto nFields = itemFields.size();
1998 if (nFields == 0 || nFields > kMaxVariants) {
1999 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
2000 }
2001 fNWritten.resize(nFields, 0);
2002 for (unsigned int i = 0; i < nFields; ++i) {
2003 fMaxItemSize = std::max(fMaxItemSize, itemFields[i]->GetValueSize());
2004 fMaxAlignment = std::max(fMaxAlignment, itemFields[i]->GetAlignment());
2005 fTraits &= itemFields[i]->GetTraits();
2006 Attach(std::move(itemFields[i]), "_" + std::to_string(i));
2007 }
2008
2009 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
2010 // For instance, std::variant<std::optional<int>> on macOS.
2011 auto cl = TClass::GetClass(GetTypeName().c_str());
2012 assert(cl);
2013 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
2014 if (dm)
2015 fVariantOffset = dm->GetOffset();
2016
2017 const auto tagSize = GetVariantTagSize();
2018 const auto padding = tagSize - (fMaxItemSize % tagSize);
2019 fTagOffset = fVariantOffset + fMaxItemSize + ((padding == tagSize) ? 0 : padding);
2020}
2021
2022std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
2023{
2024 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
2025}
2026
2027std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
2028{
2029 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
2030 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
2031 return (tag == TagType_t(-1)) ? 0 : tag + 1;
2032}
2033
2034void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
2035{
2036 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
2037 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
2038 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
2039}
2040
2041std::size_t ROOT::RVariantField::AppendImpl(const void *from)
2042{
2043 auto tag = GetTag(from, fTagOffset);
2044 std::size_t nbytes = 0;
2045 auto index = 0;
2046 if (tag > 0) {
2047 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
2048 index = fNWritten[tag - 1]++;
2049 }
2050 ROOT::Internal::RColumnSwitch varSwitch(index, tag);
2051 fPrincipalColumn->Append(&varSwitch);
2052 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
2053}
2054
2056{
2057 RNTupleLocalIndex variantIndex;
2058 std::uint32_t tag;
2059 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
2060 R__ASSERT(tag < 256);
2061
2062 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
2063 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
2064 // any `std::holds_alternative<T>` check fail later.
2065 if (R__likely(tag > 0)) {
2066 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
2067 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
2068 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
2069 }
2070 SetTag(to, fTagOffset, tag);
2071}
2072
2074{
2075 static RColumnRepresentations representations({{ENTupleColumnType::kSwitch}}, {});
2076 return representations;
2077}
2078
2080{
2081 GenerateColumnsImpl<ROOT::Internal::RColumnSwitch>();
2082}
2083
2085{
2086 GenerateColumnsImpl<ROOT::Internal::RColumnSwitch>(desc);
2087}
2088
2090{
2091 static const std::vector<std::string> prefixes = {"std::variant<"};
2092
2093 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
2094 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
2095
2096 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
2097 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
2098 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
2099 Internal::GetTypeTraceReport(*this, desc)));
2100 }
2101}
2102
2104{
2105 memset(where, 0, GetValueSize());
2106 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
2107 SetTag(where, fTagOffset, 1);
2108}
2109
2111{
2112 auto tag = GetTag(objPtr, fTagOffset);
2113 if (tag > 0) {
2114 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
2115 }
2116 RDeleter::operator()(objPtr, dtorOnly);
2117}
2118
2119std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
2120{
2121 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
2122 itemDeleters.reserve(fSubfields.size());
2123 for (const auto &f : fSubfields) {
2124 itemDeleters.emplace_back(GetDeleterOf(*f));
2125 }
2126 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, GetAlignment(), std::move(itemDeleters));
2127}
2128
2130{
2131 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
2132}
2133
2135{
2136 const auto alignment = GetAlignment();
2137 const auto actualSize = fTagOffset + GetVariantTagSize();
2138 const auto padding = alignment - (actualSize % alignment);
2139 return actualSize + ((padding == alignment) ? 0 : padding);
2140}
2141
2143{
2144 std::fill(fNWritten.begin(), fNWritten.end(), 0);
2145}
interop::TCppScope_t fClass
#define R__likely(expr)
Definition RConfig.hxx:569
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:322
#define R__LOG_WARNING(...)
Definition RLogger.hxx:357
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
@ kFloat_t
Definition TDataType.h:31
@ kULong64_t
Definition TDataType.h:32
@ kInt_t
Definition TDataType.h:30
@ kLong_t
Definition TDataType.h:30
@ kShort_t
Definition TDataType.h:29
@ kBool_t
Definition TDataType.h:32
@ kULong_t
Definition TDataType.h:30
@ kLong64_t
Definition TDataType.h:32
@ kUShort_t
Definition TDataType.h:29
@ kDouble_t
Definition TDataType.h:31
@ kChar_t
Definition TDataType.h:29
@ kUChar_t
Definition TDataType.h:29
@ kUInt_t
Definition TDataType.h:30
@ kClassHasExplicitCtor
@ kClassHasImplicitCtor
@ kClassHasVirtual
@ kClassHasExplicitDtor
@ kClassHasImplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:139
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char mode
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:142
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:112
Abstract base class for classes implementing the visitor design pattern.
virtual void VisitSoAField(const ROOT::RSoAField &field)
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 VisitClassField(const ROOT::RClassField &field)
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 & GetConstRef() const
Abstract interface to read data from an ntuple.
void RegisterStreamerInfos()
Builds the streamer info records from the descriptor's extra type info section.
RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
void operator()(void *objPtr, bool dtorOnly) final
The field for a class with dictionary.
Definition RField.hxx:184
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::vector< RSubfieldInfo > fSubfieldsInfo
Additional information kept for each entry in fSubfields
Definition RField.hxx:205
void Attach(std::unique_ptr< RFieldBase > child, RSubfieldInfo info)
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
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.
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.
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:305
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
REnumField(std::string_view fieldName, TEnum *enump)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Field specific extra type information from the header / extenstion header.
The list of column representations a field can have.
A functor to release the memory acquired by CreateValue() (memory and constructor).
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
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.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
std::vector< const RFieldBase * > GetConstSubfields() const
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
@ kTraitEmulatedField
This field is a user defined type that was missing dictionaries and was reconstructed from the on-dis...
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitTriviallyConstructible
No constructor needs to be called, i.e.
@ kTraitSoACollection
The field represents a collection in SoA layout.
@ kTraitTypeChecksum
The TClass checksum is set and valid.
const std::string & GetFieldName() const
const std::string & GetTypeAlias() const
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
bool IsArtificial() const
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.
bool IsSimple() const
std::uint32_t GetTraits() const
const std::string & GetTypeName() const
RValue BindValue(std::shared_ptr< void > objPtr)
Creates a value from a memory location with an already constructed object.
Metadata stored for every field of an RNTuple.
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
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:58
std::vector< std::unique_ptr< RFieldBase > > ReleaseSubfields()
Moves all subfields into the returned vector.
Definition RField.cxx:64
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:342
RField(std::string_view name)
Definition RField.hxx:345
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
The field for a class representing a collection of elements via TVirtualCollectionProxy.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
RProxiedCollectionField(std::string_view fieldName, TClass *classp)
Constructor used when the value type of the collection is not known in advance, i....
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
std::shared_ptr< TVirtualCollectionProxy > fProxy
The collection proxy is needed by the deleters and thus defined as a shared pointer.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) override
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
Template specializations for ROOT's RVec.
static unsigned char * ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize, const RFieldBase *itemField, RDeleter *itemDeleter)
The field for an untyped record.
void AttachItemFields(ContainerT &&itemFields)
std::vector< std::size_t > fOffsets
Base class for fields that are subject to I/O customization rules. Used by the class field and the So...
Definition RField.hxx:135
std::vector< const TSchemaRule * > FindRules(const ROOT::RFieldDescriptor *fieldDesc) const
Given the on-disk information from the page source, find all the I/O customization rules that apply t...
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:145
ROOT::DescriptorId_t LookupMember(const ROOT::RNTupleDescriptor &desc, std::string_view memberName, ROOT::DescriptorId_t classFieldId) const
Returns the id of member 'name' in the class field given by 'fieldId', or kInvalidDescriptorId if no ...
RRuleField(std::string_view name, std::string_view type, TClass *classp, ROOT::ENTupleStructure structure)
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...
void AddReadCallbacksFromIORule(const TSchemaRule *rule, std::size_t subObjectOffset=0)
Register post-read callback corresponding to a ROOT I/O customization rules.
TClass * fClass
Set by derived class to the TClass instance representing the current in-memory layout.
Definition RField.hxx:148
void SetStagingClass(const std::string &className, unsigned int classVersion)
Sets fStagingClass according to the given name and version.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr< RFieldBase > itemField)
void operator()(void *objPtr, bool dtorOnly) final
The SoA field provides I/O for an in-memory SoA layout linked to an on-disk collection of the underly...
Definition RFieldSoA.hxx:54
std::unique_ptr< std::mutex > fLockSplitFields
protects the fSplitFields member.
Definition RFieldSoA.hxx:95
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
std::vector< RFieldBase * > fRecordMemberFields
Direct access to the member fields of the underlying record.
Definition RFieldSoA.hxx:75
size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::vector< std::unique_ptr< RDeleter > > fRecordMemberDeleters
Definition RFieldSoA.hxx:79
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
void CollectRecordMemberFields()
Called during construction, picks up the (nested) member fields of the underlying record type(s) and ...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ReconstructSplitFields() const
std::vector< std::size_t > fSoAMemberOffsets
The offset of the RVec members in the SoA type in the same order as fRecordMemberFields.
Definition RFieldSoA.hxx:77
RSoAField(std::string_view fieldName, const RSoAField &source)
Used by CloneImpl.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
std::unique_ptr< std::vector< std::size_t > > fSplitOffsets
Definition RFieldSoA.hxx:94
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 ...
size_t GetAlignment() const final
What alignof(T) for this type returns.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
std::unique_ptr< std::vector< std::unique_ptr< ROOT::RFieldBase > > > fSplitFields
For reading and writing, the RVecs of the SoA class do not have a dedicated field.
Definition RFieldSoA.hxx:93
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void GraftNestedMemberFields(const RSoAField &nestedSoA, std::size_t offsetInParent, const std::function< RFieldBase *(const std::string &)> &fnRecordFieldFinder)
For a nested SoA struct (either as a member of as a base class), use their fRecordMemberFields in thi...
void operator()(void *objPtr, bool dtorOnly) final
The field for a class using ROOT standard streaming.
Definition RField.hxx:245
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:257
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &source) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RStreamerField(std::string_view fieldName, TClass *classp)
size_t GetAlignment() const final
What alignof(T) for this type returns.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
size_t GetValueSize() const final
What sizeof(T) for this type returns.
Template specializations for C++ std::tuple.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > itemFields)
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void operator()(void *objPtr, bool dtorOnly) final
Template specializations for C++ std::variant.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
static constexpr std::size_t kMaxVariants
std::vector< ROOT::Internal::RColumnIndex::ValueType > fNWritten
static std::uint8_t GetTag(const void *variantPtr, std::size_t tagOffset)
Extracts the index from an std::variant and transforms it into the 1-based index used for the switch ...
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
size_t fVariantOffset
In the std::variant memory layout, the actual union of types may start at an offset > 0.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
size_t fTagOffset
In the std::variant memory layout, at which byte number is the index stored.
RVariantField(std::string_view name, const RVariantField &source)
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
void CommitClusterImpl() final
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:6679
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2331
EState GetState() const
Definition TClass.h:504
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition TClass.cxx:5533
size_t GetClassAlignment() const
Return the alignment requirement (in bytes) for objects of this class.
Definition TClass.cxx:5844
void BuildRealData(void *pointer=nullptr, Bool_t isTransient=kFALSE)
Build a full list of persistent data members.
Definition TClass.cxx:2043
const std::type_info * GetTypeInfo() const
Definition TClass.h:512
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3833
TList * GetListOfRealData() const
Definition TClass.h:468
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5869
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3699
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:4720
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2923
Int_t GetClassSize() const
Definition TClass.h:439
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2408
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6254
@ kInterpreted
Definition TClass.h:129
@ kEmulated
Definition TClass.h:128
Version_t GetClassVersion() const
Definition TClass.h:434
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2999
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
The TEnum class implements the enum type.
Definition TEnum.h:33
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
TObject * First() const override
Return the first object in the list. Returns 0 when list is empty.
Definition TList.cxx:789
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
Mother of all ROOT objects.
Definition TObject.h:42
@ kIsOnHeap
object is on heap
Definition TObject.h:90
@ kNotDeleted
object has not been deleted
Definition TObject.h:91
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:74
The TRealData class manages the effective list of all data members for a given class.
Definition TRealData.h:30
RAII helper class that ensures that PushProxy() / PopProxy() are called when entering / leaving a C++...
Defines a common interface to inspect/change the contents of an object that represents a collection.
@ kNeedDelete
The collection contains directly or indirectly (via other collection) some pointers that need explici...
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 TVirtualStreamerInfo * GenerateInfoForPair(const std::string &pairclassname, bool silent, size_t hint_pair_offset, size_t hint_pair_size)=0
Generate the TClass and TStreamerInfo for the requested pair.
virtual Int_t GetNumber() const =0
static TVirtualStreamerInfo * Factory()
Static function returning a pointer to a new TVirtualStreamerInfo object.
virtual TClass * GetClass() const =0
const Int_t n
Definition legend1.C:16
TRangeCast< T, false > TRangeStaticCast
TRangeStaticCast is an adapter class that allows the typed iteration through a TCollection.
void SetAllowFieldSubstitutions(RFieldZero &fieldZero, bool val)
Definition RField.cxx:35
std::tuple< unsigned char **, std::int32_t *, std::int32_t * > GetRVecDataMembers(void *rvecPtr)
Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the R...
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
void CallConnectPageSourceOnField(RFieldBase &, ROOT::Internal::RPageSource &)
std::string GetRNTupleSoARecord(const TClass *cl)
Checks if the "rntuple.SoARecord" class attribute is set in the dictionary.
bool NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias, bool isArgInTemplatedUserClass=false)
Checks if the meta normalized name is different from the RNTuple normalized name in a way that would ...
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
std::unique_ptr< RFieldBase > CreateEmulatedEnumField(std::string_view fieldName, std::string_view emulatedFromType, std::string_view underlyingIntType)
ERNTupleSerializationMode GetRNTupleSerializationMode(const TClass *cl)
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
constexpr bool IsValidAlignment(std::size_t align) noexcept
Return true if align is a valid C++ alignment value: strictly positive and a power of two.
Definition BitUtils.hxx:36
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
@ kSTLvector
Definition ESTLType.h:30
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.
std::unique_ptr< RFieldBase > fField
The field used to read the on-disk data.
Definition RField.hxx:141
std::size_t fOffset
offset in fStagingArea
Definition RField.hxx:142