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::Experimental::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::Experimental::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::Experimental::RSoAField::RSoAField(std::string_view fieldName, TClass *clSoA)
896 : ROOT::RRuleField(fieldName, GetRenormalizedTypeName(clSoA->GetName()), clSoA, ROOT::ENTupleStructure::kCollection)
897{
898 static std::once_flag once;
899 std::call_once(once, []() {
900 R__LOG_WARNING(ROOT::Internal::NTupleLog()) << "The SoA field is experimental and still under development.";
901 });
902
903 EnsureValidUserClass(fClass, *this, "RSoAField");
904 const auto recordTypeName = ROOT::Internal::GetRNTupleSoARecord(fClass);
905 if (recordTypeName.empty()) {
906 throw ROOT::RException(R__FAIL(std::string("class ") + GetTypeName() +
907 " is not marked with the rntupleSoARecord "
908 "dictionary option; cannot create corresponding RSoAField."));
909 }
910 try {
911 Attach(std::make_unique<ROOT::RClassField>("_0", recordTypeName));
912 } catch (ROOT::RException &e) {
913 throw RException(R__FAIL("invalid record type of SoA field " + GetTypeName() + " [" + e.what() + "]"));
914 }
915 if (fClass->GetClassVersion() <= 0) {
916 throw RException(R__FAIL(std::string("SoA class ") + GetTypeName() + " is transient or unversioned."));
917 }
918 if (static_cast<std::uint32_t>(fClass->GetClassVersion()) != fSubfields[0]->GetTypeVersion()) {
919 throw RException(R__FAIL(std::string("version mismatch between SoA type and underlying record type: ") +
920 std::to_string(fClass->GetClassVersion()) + " vs. " +
921 std::to_string(fSubfields[0]->GetTypeVersion())));
922 }
923
925
926 std::string renormalizedAlias;
927 if (ROOT::Internal::NeedsMetaNameAsAlias(fClass->GetName(), renormalizedAlias))
928 fTypeAlias = renormalizedAlias;
929
931 fLockSplitFields = std::make_unique<std::mutex>();
932}
933
934std::unique_ptr<ROOT::RFieldBase> ROOT::Experimental::RSoAField::CloneImpl(std::string_view newName) const
935{
936 return std::unique_ptr<RSoAField>(new RSoAField(newName, *this));
937}
938
948
950{
951 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
952}
953
955{
956 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
957}
958
959std::size_t ROOT::Experimental::RSoAField::AppendImpl(const void *from)
960{
961 const std::size_t nSoAMembers = fSoAMemberOffsets.size();
962
963 std::size_t N = 0; // Set by first SoA member and verified for the rest
964 for (std::size_t i = 0; i < nSoAMembers; ++i) {
965 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
966 auto [beginPtr, sizePtr, _] = ROOT::Internal::GetRVecDataMembers(rvecPtr);
967 assert(*sizePtr >= 0);
968 if (i == 0) {
969 N = *sizePtr;
970 } else {
971 if (static_cast<std::size_t>(*sizePtr) != N) {
972 const auto f = fRecordMemberFields[i];
973 throw RException(R__FAIL("SoA length mismatch for " + f->GetFieldName() + ": " + std::to_string(*sizePtr) +
974 " vs. " + std::to_string(N) + " (expected)"));
975 }
976 }
977 }
978
979 std::size_t nbytes = 0;
980 if (N > 0) {
981 for (std::size_t i = 0; i < nSoAMembers; ++i) {
982 const void *rvecPtr = static_cast<const unsigned char *>(from) + fSoAMemberOffsets[i];
983 auto [beginPtr, _, __] = ROOT::Internal::GetRVecDataMembers(rvecPtr);
984 RFieldBase *memberField = fRecordMemberFields[i];
985 if (memberField->IsSimple()) {
986 GetPrincipalColumnOf(*memberField)->AppendV(*beginPtr, N);
987 nbytes += N * GetPrincipalColumnOf(*memberField)->GetElement()->GetPackedSize();
988 } else {
989 for (std::size_t j = 0; j < N; ++j) {
990 nbytes += CallAppendOn(*memberField, *beginPtr + j * memberField->GetValueSize());
991 }
992 }
993 }
994 }
995
996 fNWritten += N;
997 fPrincipalColumn->Append(&fNWritten);
998 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
999}
1000
1002{
1003 // Read collection info for this entry
1005 RNTupleLocalIndex collectionStart;
1006 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &N);
1007
1008 const auto nSoAMembers = fSoAMemberOffsets.size();
1009 for (std::size_t i = 0; i < nSoAMembers; ++i) {
1010 RFieldBase *memberField = fRecordMemberFields[i];
1011 const auto memberSize = memberField->GetValueSize();
1012 void *rvecPtr = static_cast<unsigned char *>(to) + fSoAMemberOffsets[i];
1013 auto begin = ROOT::RRVecField::ResizeRVec(rvecPtr, N, memberSize, memberField, fRecordMemberDeleters[i].get());
1014
1015 if (N == 0)
1016 continue;
1017
1018 if (memberField->IsSimple()) {
1019 GetPrincipalColumnOf(*memberField)->ReadV(collectionStart, N, begin);
1020 } else {
1021 if (memberField->IsArtificial()) {
1022 // Other artificial fields simply don't read at all. This does not work here because then
1023 // the vector elements of trivial types would be left uninitialized (complex types explicitly call
1024 // the constructor on vector resize). Thus we explicitly default-initialize trivial types.
1025 // Note that this causes a subtle difference in behavior: if the added member is default-initialized to
1026 // a non-zero value, this will be forgotten in the SoA layout.
1027 if (memberField->GetTraits() & kTraitTriviallyConstructible) {
1028 std::memset(begin, 0, N * memberSize);
1029 }
1030 } else {
1031 for (std::size_t j = 0; j < N; ++j) {
1032 CallReadOn(*memberField, collectionStart + j, begin + (j * memberSize));
1033 }
1034 }
1035 }
1036 }
1037}
1038
1039std::unique_ptr<ROOT::RFieldBase>
1041{
1042 // Most of the heavy lifting is done by the actual subfields that map onto the on-disk AoS schema.
1043 // In the SoA field itself, we only need to take care of the in-memory part of the rule processing.
1044 // One complication is that for non-rename rules, we manually need to recurse into base (SoA) classes and
1045 // nested (SoA) classes because we don't have them in the field tree.
1046
1047 // For now, allow only rename rules and whole-object rules
1048
1049 const bool hasSources = std::any_of(fRules.begin(), fRules.end(), [](const auto &r) {
1050 return r.fRule->GetSource() && (r.fRule->GetSource()->GetEntries() > 0);
1051 });
1052 const bool hasTargets =
1053 std::any_of(fRules.begin(), fRules.end(), [](const auto &r) { return r.fRule->GetTarget(); });
1054
1055 if (hasSources) {
1056 throw RException(R__FAIL("I/O customization rules with sources are currently unsupported for SoA fields (" +
1057 GetTypeName() + ")"));
1058 }
1059
1060 if (hasTargets) {
1061 throw RException(R__FAIL("I/O customization rules with targets are currently unsupported for SoA fields (" +
1062 GetTypeName() + ")"));
1063 }
1064
1065 for (const auto &r : fRules) {
1066 AddReadCallbacksFromIORule(r.fRule, r.fOffset);
1067 }
1068
1069 return nullptr;
1070}
1071
1073{
1074 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1075}
1076
1078{
1079 fClass->New(where);
1080}
1081
1083{
1084}
1085
1087{
1088 fSoAClass->Destructor(objPtr, true /* dtorOnly */);
1089 RDeleter::operator()(objPtr, dtorOnly);
1090}
1091
1093{
1094 std::lock_guard<std::mutex> lockGuard(*fLockSplitFields);
1095 if (fSplitFields)
1096 return;
1097
1098 fSplitFields = std::make_unique<std::vector<std::unique_ptr<ROOT::RFieldBase>>>();
1099 fSplitOffsets = std::make_unique<std::vector<std::size_t>>();
1100
1101 unsigned int baseIdx = 0;
1103 TClass *cl = base->GetClassPointer();
1104 auto baseField = RFieldBase::Create(std::string(":_" + std::to_string(baseIdx)), cl->GetName()).Unwrap();
1105 fSplitFields->emplace_back(std::move(baseField));
1106 fSplitOffsets->emplace_back(base->GetDelta());
1107 baseIdx++;
1108 }
1109
1111 if ((dataMember->Property() & kIsStatic) || !dataMember->IsPersistent())
1112 continue;
1113
1114 const std::string typeName{dataMember->GetTrueTypeName()};
1115 auto dmField = RFieldBase::Create(dataMember->GetName(), typeName).Unwrap();
1116 fSplitFields->emplace_back(std::move(dmField));
1117 fSplitOffsets->emplace_back(dataMember->GetOffset());
1118 }
1119}
1120
1121std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(const RValue &value) const
1122{
1124 const auto nSplitFields = fSplitFields->size();
1125
1126 auto valuePtr = value.GetPtr<void>();
1127 auto soaPtr = static_cast<unsigned char *>(valuePtr.get());
1128 std::vector<RValue> values;
1129 values.reserve(nSplitFields);
1130 for (std::size_t i = 0; i < nSplitFields; ++i) {
1131 values.emplace_back((*fSplitFields)[i]->BindValue(std::shared_ptr<void>(valuePtr, soaPtr + (*fSplitOffsets)[i])));
1132 }
1133 return values;
1134}
1135
1137{
1138 return fClass->GetClassSize();
1139}
1140
1142{
1143 return fClass->GetClassVersion();
1144}
1145
1147{
1148 return fClass->GetCheckSum();
1149}
1150
1152{
1153 const auto align = fClass->GetClassAlignment();
1154 EnsureValidAlignment(align);
1155 return align;
1156}
1157
1159{
1160 // TODO(jblomer): factor out
1161 bool polymorphic = fClass->ClassProperty() & kClassHasVirtual;
1162 if (!polymorphic) {
1163 return nullptr;
1164 }
1165 return fClass->GetTypeInfo();
1166}
1167
1172
1173//------------------------------------------------------------------------------
1174
1175std::unique_ptr<ROOT::RFieldBase> ROOT::Internal::CreateEmulatedEnumField(std::string_view fieldName,
1176 std::string_view emulatedFromType,
1177 std::string_view underlyingIntType)
1178{
1179 return std::unique_ptr<RFieldBase>(new REnumField(fieldName, emulatedFromType, underlyingIntType));
1180}
1181
1182ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
1183 : REnumField(fieldName, EnsureValidEnum(enumName))
1184{
1185}
1186
1187ROOT::REnumField::REnumField(std::string_view fieldName, TEnum *enump)
1188 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(enump->GetQualifiedName()), ROOT::ENTupleStructure::kPlain,
1189 false /* isSimple */)
1190{
1191 // Avoid accidentally supporting std types through TEnum.
1192 if (enump->Property() & kIsDefinedInStd) {
1193 throw RException(R__FAIL(GetTypeName() + " is not supported"));
1194 }
1195
1196 switch (enump->GetUnderlyingType()) {
1197 case kBool_t: Attach(std::make_unique<RField<Bool_t>>("_0")); break;
1198 case kChar_t: Attach(std::make_unique<RField<Char_t>>("_0")); break;
1199 case kUChar_t: Attach(std::make_unique<RField<UChar_t>>("_0")); break;
1200 case kShort_t: Attach(std::make_unique<RField<Short_t>>("_0")); break;
1201 case kUShort_t: Attach(std::make_unique<RField<UShort_t>>("_0")); break;
1202 case kInt_t: Attach(std::make_unique<RField<Int_t>>("_0")); break;
1203 case kUInt_t: Attach(std::make_unique<RField<UInt_t>>("_0")); break;
1204 case kLong_t: Attach(std::make_unique<RField<Long_t>>("_0")); break;
1205 case kLong64_t: Attach(std::make_unique<RField<Long64_t>>("_0")); break;
1206 case kULong_t: Attach(std::make_unique<RField<ULong_t>>("_0")); break;
1207 case kULong64_t: Attach(std::make_unique<RField<ULong64_t>>("_0")); break;
1208 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + GetTypeName()));
1209 }
1210
1212}
1213
1214ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
1215 std::unique_ptr<RFieldBase> intField)
1216 : ROOT::RFieldBase(fieldName, enumName, ROOT::ENTupleStructure::kPlain, false /* isSimple */)
1217{
1218 Attach(std::move(intField));
1220}
1221
1222ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view emulatedFromType,
1223 std::string_view underlyingIntType)
1224 : ROOT::RFieldBase(fieldName, emulatedFromType, ROOT::ENTupleStructure::kPlain, false /* isSimple */)
1225{
1226 auto intField = Create("_0", std::string(underlyingIntType)).Unwrap();
1227 Attach(std::move(intField));
1229}
1230
1231std::unique_ptr<ROOT::RFieldBase> ROOT::REnumField::CloneImpl(std::string_view newName) const
1232{
1233 auto newIntField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
1234 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
1235}
1236
1238{
1239 // TODO(jblomer): allow enum to enum conversion only by rename rule
1240 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1241}
1242
1243std::vector<ROOT::RFieldBase::RValue> ROOT::REnumField::SplitValue(const RValue &value) const
1244{
1245 std::vector<RValue> result;
1246 result.emplace_back(fSubfields[0]->BindValue(value.GetPtr<void>()));
1247 return result;
1248}
1249
1251{
1252 visitor.VisitEnumField(*this);
1253}
1254
1255//------------------------------------------------------------------------------
1256
1257ROOT::RPairField::RPairField(std::string_view fieldName, std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
1258 : ROOT::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1259{
1260 const std::string typeAlias = "std::pair<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1261 if (typeAlias != GetTypeName())
1262 fTypeAlias = typeAlias;
1263
1264 AttachItemFields(std::move(itemFields));
1265
1266 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
1267 auto *c = TClass::GetClass(GetTypeName().c_str());
1268 if (!c) {
1270 fSubfields[0]->GetTypeName(), fSubfields[1]->GetTypeName(), true /* silent */, 0, 0);
1271 if (pairInfo)
1272 c = pairInfo->GetClass();
1273 }
1274 if (!c)
1275 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1276 fSize = c->Size();
1277
1278 auto firstElem = c->GetRealData("first");
1279 if (!firstElem)
1280 throw RException(R__FAIL("first: no such member"));
1281 fOffsets.push_back(firstElem->GetThisOffset());
1282
1283 auto secondElem = c->GetRealData("second");
1284 if (!secondElem)
1285 throw RException(R__FAIL("second: no such member"));
1286 fOffsets.push_back(secondElem->GetThisOffset());
1287}
1288
1289std::unique_ptr<ROOT::RFieldBase> ROOT::RPairField::CloneImpl(std::string_view newName) const
1290{
1291 std::array<std::unique_ptr<RFieldBase>, 2> itemClones = {fSubfields[0]->Clone(fSubfields[0]->GetFieldName()),
1292 fSubfields[1]->Clone(fSubfields[1]->GetFieldName())};
1293 return std::unique_ptr<RPairField>(new RPairField(newName, std::move(itemClones)));
1294}
1295
1297{
1298 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1299
1300 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1301 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1302
1303 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1304 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1305 if (nOnDiskSubfields != 2) {
1306 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::pair " +
1307 std::to_string(nOnDiskSubfields) + "\n" +
1308 Internal::GetTypeTraceReport(*this, desc)));
1309 }
1310}
1311
1312//------------------------------------------------------------------------------
1313
1316 bool readFromDisk)
1317{
1318 RIteratorFuncs ifuncs;
1319 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
1320 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
1321 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
1322 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
1323 (ifuncs.fNext != nullptr));
1324 return ifuncs;
1325}
1326
1328 : RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kCollection,
1329 false /* isSimple */),
1330 fNWritten(0)
1331{
1332 if (!classp->GetCollectionProxy())
1333 throw RException(R__FAIL(std::string(classp->GetName()) + " has no associated collection proxy"));
1334 if (classp->Property() & kIsDefinedInStd) {
1335 static const std::vector<std::string> supportedStdTypes = {
1336 "std::set<", "std::unordered_set<", "std::multiset<", "std::unordered_multiset<",
1337 "std::map<", "std::unordered_map<", "std::multimap<", "std::unordered_multimap<"};
1338 bool isSupported = false;
1339 for (const auto &tn : supportedStdTypes) {
1340 if (GetTypeName().rfind(tn, 0) == 0) {
1341 isSupported = true;
1342 break;
1343 }
1344 }
1345 if (!isSupported)
1346 throw RException(R__FAIL(std::string(GetTypeName()) + " is not supported"));
1347 }
1348
1349 std::string renormalizedAlias;
1350 if (Internal::NeedsMetaNameAsAlias(classp->GetName(), renormalizedAlias))
1351 fTypeAlias = renormalizedAlias;
1352
1353 fProxy.reset(classp->GetCollectionProxy()->Generate());
1354 fProperties = fProxy->GetProperties();
1355 fCollectionType = fProxy->GetCollectionType();
1356 if (fProxy->HasPointers())
1357 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
1358
1359 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
1360 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1361}
1362
1363ROOT::RProxiedCollectionField::RProxiedCollectionField(std::string_view fieldName, std::string_view typeName)
1364 : RProxiedCollectionField(fieldName, EnsureValidClass(typeName))
1365{
1366 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
1368 throw RException(R__FAIL("custom associative collection proxies not supported"));
1369
1370 std::unique_ptr<ROOT::RFieldBase> itemField;
1371
1372 if (auto valueClass = fProxy->GetValueClass()) {
1373 // Element type is a class
1374 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
1375 } else {
1376 switch (fProxy->GetType()) {
1377 case EDataType::kChar_t: itemField = std::make_unique<RField<Char_t>>("_0"); break;
1378 case EDataType::kUChar_t: itemField = std::make_unique<RField<UChar_t>>("_0"); break;
1379 case EDataType::kShort_t: itemField = std::make_unique<RField<Short_t>>("_0"); break;
1380 case EDataType::kUShort_t: itemField = std::make_unique<RField<UShort_t>>("_0"); break;
1381 case EDataType::kInt_t: itemField = std::make_unique<RField<Int_t>>("_0"); break;
1382 case EDataType::kUInt_t: itemField = std::make_unique<RField<UInt_t>>("_0"); break;
1383 case EDataType::kLong_t: itemField = std::make_unique<RField<Long_t>>("_0"); break;
1384 case EDataType::kLong64_t: itemField = std::make_unique<RField<Long64_t>>("_0"); break;
1385 case EDataType::kULong_t: itemField = std::make_unique<RField<ULong_t>>("_0"); break;
1386 case EDataType::kULong64_t: itemField = std::make_unique<RField<ULong64_t>>("_0"); break;
1387 case EDataType::kFloat_t: itemField = std::make_unique<RField<Float_t>>("_0"); break;
1388 case EDataType::kDouble_t: itemField = std::make_unique<RField<Double_t>>("_0"); break;
1389 case EDataType::kBool_t: itemField = std::make_unique<RField<Bool_t>>("_0"); break;
1390 default: throw RException(R__FAIL("unsupported value type: " + std::to_string(fProxy->GetType())));
1391 }
1392 }
1393
1394 fItemSize = itemField->GetValueSize();
1395 Attach(std::move(itemField));
1396}
1397
1398std::unique_ptr<ROOT::RFieldBase> ROOT::RProxiedCollectionField::CloneImpl(std::string_view newName) const
1399{
1400 auto clone =
1401 std::unique_ptr<RProxiedCollectionField>(new RProxiedCollectionField(newName, fProxy->GetCollectionClass()));
1402 clone->fItemSize = fItemSize;
1403 clone->Attach(fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1404 return clone;
1405}
1406
1408{
1409 std::size_t nbytes = 0;
1410 unsigned count = 0;
1411 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
1412 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
1413 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1414 nbytes += CallAppendOn(*fSubfields[0], ptr);
1415 count++;
1416 }
1417
1418 fNWritten += count;
1419 fPrincipalColumn->Append(&fNWritten);
1420 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1421}
1422
1424{
1425 ROOT::NTupleSize_t nItems;
1426 RNTupleLocalIndex collectionStart;
1427 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1428
1429 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
1430 void *obj =
1431 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
1432
1433 unsigned i = 0;
1434 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
1435 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
1436 CallReadOn(*fSubfields[0], collectionStart + (i++), elementPtr);
1437 }
1438 if (obj != to)
1439 fProxy->Commit(obj);
1440}
1441
1451
1453{
1454 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
1455}
1456
1458{
1459 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
1460}
1461
1463{
1464 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1465}
1466
1468{
1469 fProxy->New(where);
1470}
1471
1472std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RProxiedCollectionField::GetDeleter() const
1473{
1474 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
1475 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
1476 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubfields[0]), itemSize);
1477 }
1478 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
1479}
1480
1482 std::shared_ptr<TVirtualCollectionProxy> proxy)
1483 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()), fProxy(std::move(proxy))
1484{
1485}
1486
1488 std::shared_ptr<TVirtualCollectionProxy> proxy, std::unique_ptr<RDeleter> itemDeleter, size_t itemSize)
1489 : RDeleter(proxy->GetCollectionClass()->GetClassAlignment()),
1490 fProxy(std::move(proxy)),
1491 fItemDeleter(std::move(itemDeleter)),
1492 fItemSize(itemSize)
1493{
1494 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1495}
1496
1498{
1499 if (fItemDeleter) {
1500 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), objPtr);
1501 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
1502 fItemDeleter->operator()(ptr, true /* dtorOnly */);
1503 }
1504 }
1505 fProxy->Destructor(objPtr, true /* dtorOnly */);
1506 RDeleter::operator()(objPtr, dtorOnly);
1507}
1508
1509std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(const RValue &value) const
1510{
1511 std::vector<RValue> result;
1512 auto valueRawPtr = value.GetPtr<void>().get();
1513 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), valueRawPtr);
1514 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
1515 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1516 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
1517 }
1518 return result;
1519}
1520
1522{
1523 return fProxy->Sizeof();
1524}
1525
1527{
1528 const auto align = fProxy->GetCollectionClass()->GetClassAlignment();
1529 EnsureValidAlignment(align);
1530 return align;
1531}
1532
1537
1538//------------------------------------------------------------------------------
1539
1540ROOT::RMapField::RMapField(std::string_view fieldName, EMapType mapType, std::unique_ptr<RFieldBase> itemField)
1541 : RProxiedCollectionField(fieldName,
1542 EnsureValidClass(BuildMapTypeName(mapType, itemField.get(), false /* useTypeAliases */))),
1543 fMapType(mapType)
1544{
1545 if (!itemField->GetTypeAlias().empty())
1546 fTypeAlias = BuildMapTypeName(mapType, itemField.get(), true /* useTypeAliases */);
1547
1548 auto *itemClass = fProxy->GetValueClass();
1549 fItemSize = itemClass->GetClassSize();
1550
1551 Attach(std::move(itemField), "_0");
1552}
1553
1554std::unique_ptr<ROOT::RFieldBase> ROOT::RMapField::CloneImpl(std::string_view newName) const
1555{
1556 return std::make_unique<RMapField>(newName, fMapType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1557}
1558
1560{
1561 static const std::vector<std::string> prefixesRegular = {"std::map<", "std::unordered_map<"};
1562
1563 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1564
1565 switch (fMapType) {
1566 case EMapType::kMap:
1567 case EMapType::kUnorderedMap: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1568 default:
1569 break;
1570 // no restrictions for multimaps
1571 }
1572}
1573
1574//------------------------------------------------------------------------------
1575
1576ROOT::RSetField::RSetField(std::string_view fieldName, ESetType setType, std::unique_ptr<RFieldBase> itemField)
1577 : ROOT::RProxiedCollectionField(fieldName,
1578 EnsureValidClass(BuildSetTypeName(setType, *itemField, false /* useTypeAlias */))),
1579 fSetType(setType)
1580{
1581 if (!itemField->GetTypeAlias().empty())
1582 fTypeAlias = BuildSetTypeName(setType, *itemField, true /* useTypeAlias */);
1583
1584 fItemSize = itemField->GetValueSize();
1585
1586 Attach(std::move(itemField), "_0");
1587}
1588
1589std::unique_ptr<ROOT::RFieldBase> ROOT::RSetField::CloneImpl(std::string_view newName) const
1590{
1591 return std::make_unique<RSetField>(newName, fSetType, fSubfields[0]->Clone(fSubfields[0]->GetFieldName()));
1592}
1593
1595{
1596 static const std::vector<std::string> prefixesRegular = {"std::set<", "std::unordered_set<", "std::map<",
1597 "std::unordered_map<"};
1598
1599 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
1600
1601 switch (fSetType) {
1602 case ESetType::kSet:
1603 case ESetType::kUnorderedSet: EnsureMatchingTypePrefix(desc, prefixesRegular).ThrowOnError(); break;
1604 default:
1605 break;
1606 // no restrictions for multisets
1607 }
1608}
1609
1610//------------------------------------------------------------------------------
1611
1612namespace {
1613
1614/// Used in RStreamerField::AppendImpl() in order to record the encountered streamer info records
1615class TBufferRecStreamer : public TBufferFile {
1616public:
1617 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
1618
1619private:
1620 RCallbackStreamerInfo fCallbackStreamerInfo;
1621
1622public:
1623 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo)
1624 : TBufferFile(mode, bufsize), fCallbackStreamerInfo(std::move(callbackStreamerInfo))
1625 {
1626 }
1627 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
1628};
1629
1630} // anonymous namespace
1631
1632ROOT::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className)
1633 : RStreamerField(fieldName, EnsureValidClass(className))
1634{
1635}
1636
1637ROOT::RStreamerField::RStreamerField(std::string_view fieldName, TClass *classp)
1638 : ROOT::RFieldBase(fieldName, GetRenormalizedTypeName(classp->GetName()), ROOT::ENTupleStructure::kStreamer,
1639 false /* isSimple */),
1640 fClass(classp),
1641 fIndex(0)
1642{
1644 throw RException(R__FAIL("streamer field " + GetFieldName() + " requires at least an emulated class"));
1647
1648 std::string renormalizedAlias;
1649 if (Internal::NeedsMetaNameAsAlias(classp->GetName(), renormalizedAlias))
1650 fTypeAlias = renormalizedAlias;
1651
1653 // For RClassField, we only check for explicit constructors and destructors and then recursively combine traits from
1654 // all member subfields. For RStreamerField, we treat the class as a black box and additionally need to check for
1655 // implicit constructors and destructors.
1660}
1661
1662std::unique_ptr<ROOT::RFieldBase> ROOT::RStreamerField::CloneImpl(std::string_view newName) const
1663{
1664 // To get the correct TClass instance in the clone, we clone using the un-normalized type name
1665 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, fClass->GetName()));
1666}
1667
1668std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
1669{
1670 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
1671 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
1672 fClass->Streamer(const_cast<void *>(from), buffer);
1673
1674 auto nbytes = buffer.Length();
1675 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
1676 fIndex += nbytes;
1677 fPrincipalColumn->Append(&fIndex);
1678 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1679}
1680
1682{
1683 RNTupleLocalIndex collectionStart;
1684 ROOT::NTupleSize_t nbytes;
1685 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
1686
1687 TBufferFile buffer(TBuffer::kRead, nbytes);
1688 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
1689 fClass->Streamer(to, buffer);
1690}
1691
1701
1703{
1704 GenerateColumnsImpl<ROOT::Internal::RColumnIndex, std::byte>();
1705}
1706
1708{
1709 GenerateColumnsImpl<ROOT::Internal::RColumnIndex, std::byte>(desc);
1710}
1711
1713{
1714 source.RegisterStreamerInfos();
1715 return nullptr;
1716}
1717
1719{
1720 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffTypeVersion).ThrowOnError();
1721}
1722
1724{
1725 fClass->New(where);
1726}
1727
1732
1734{
1735 fClass->Destructor(objPtr, true /* dtorOnly */);
1736 RDeleter::operator()(objPtr, dtorOnly);
1737}
1738
1748
1750{
1751 const auto align = fClass->GetClassAlignment();
1752 EnsureValidAlignment(align);
1753 return align;
1754}
1755
1757{
1758 return fClass->GetClassSize();
1759}
1760
1762{
1763 return fClass->GetClassVersion();
1764}
1765
1767{
1768 return fClass->GetCheckSum();
1769}
1770
1772{
1773 visitor.VisitStreamerField(*this);
1774}
1775
1776//------------------------------------------------------------------------------
1777
1779{
1780 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1781 return dataMember->GetOffset();
1782 }
1783 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1784}
1785
1786ROOT::RField<TObject>::RField(std::string_view fieldName, const RField<TObject> &source)
1787 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1788{
1790 Attach(source.GetConstSubfields()[0]->Clone("fUniqueID"));
1791 Attach(source.GetConstSubfields()[1]->Clone("fBits"));
1792}
1793
1794ROOT::RField<TObject>::RField(std::string_view fieldName)
1795 : ROOT::RFieldBase(fieldName, "TObject", ROOT::ENTupleStructure::kRecord, false /* isSimple */)
1796{
1797 assert(TObject::Class()->GetClassVersion() == 1);
1798
1800 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1801 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1802}
1803
1804std::unique_ptr<ROOT::RFieldBase> ROOT::RField<TObject>::CloneImpl(std::string_view newName) const
1805{
1806 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
1807}
1808
1809std::size_t ROOT::RField<TObject>::AppendImpl(const void *from)
1810{
1811 // Cf. TObject::Streamer()
1812
1813 auto *obj = static_cast<const TObject *>(from);
1814 if (obj->TestBit(TObject::kIsReferenced)) {
1815 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1816 }
1817
1818 std::size_t nbytes = 0;
1819 nbytes += CallAppendOn(*fSubfields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
1820
1821 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
1822 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
1823 nbytes += CallAppendOn(*fSubfields[1], &bits);
1824
1825 return nbytes;
1826}
1827
1829{
1830 // Cf. TObject::Streamer()
1831
1832 auto *obj = static_cast<TObject *>(to);
1833 if (obj->TestBit(TObject::kIsReferenced)) {
1834 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
1835 }
1836
1837 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetUniqueID()) = uniqueID;
1838
1839 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
1840 bits |= bitIsOnHeap | TObject::kNotDeleted;
1841 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
1842}
1843
1845{
1846 UInt_t uniqueID, bits;
1847 CallReadOn(*fSubfields[0], globalIndex, &uniqueID);
1848 CallReadOn(*fSubfields[1], globalIndex, &bits);
1849 ReadTObject(to, uniqueID, bits);
1850}
1851
1853{
1854 UInt_t uniqueID, bits;
1855 CallReadOn(*fSubfields[0], localIndex, &uniqueID);
1856 CallReadOn(*fSubfields[1], localIndex, &bits);
1857 ReadTObject(to, uniqueID, bits);
1858}
1859
1861{
1862 return TObject::Class()->GetClassVersion();
1863}
1864
1866{
1867 return TObject::Class()->GetCheckSum();
1868}
1869
1871{
1872 new (where) TObject();
1873}
1874
1875std::vector<ROOT::RFieldBase::RValue> ROOT::RField<TObject>::SplitValue(const RValue &value) const
1876{
1877 std::vector<RValue> result;
1878 // Use GetPtr<TObject> to type-check
1879 std::shared_ptr<void> ptr = value.GetPtr<TObject>();
1880 auto charPtr = static_cast<unsigned char *>(ptr.get());
1881 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetUniqueID())));
1882 result.emplace_back(fSubfields[1]->BindValue(std::shared_ptr<void>(ptr, charPtr + GetOffsetBits())));
1883 return result;
1884}
1885
1887{
1888 return sizeof(TObject);
1889}
1890
1892{
1893 return alignof(TObject);
1894}
1895
1897{
1898 visitor.VisitTObjectField(*this);
1899}
1900
1901//------------------------------------------------------------------------------
1902
1903ROOT::RTupleField::RTupleField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1904 : ROOT::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">")
1905{
1906 const std::string typeAlias = "std::tuple<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
1907 if (typeAlias != GetTypeName())
1908 fTypeAlias = typeAlias;
1909
1910 AttachItemFields(std::move(itemFields));
1911
1912 auto *c = TClass::GetClass(GetTypeName().c_str());
1913 if (!c)
1914 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
1915 fSize = c->Size();
1916
1917 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
1918 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
1919 // following the order of the type list.
1920 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
1921 // members, the assertion below will fail.
1922 for (unsigned i = 0; i < fSubfields.size(); ++i) {
1923 std::string memberName("_" + std::to_string(i));
1924 auto member = c->GetRealData(memberName.c_str());
1925 if (!member)
1926 throw RException(R__FAIL(memberName + ": no such member"));
1927 fOffsets.push_back(member->GetThisOffset());
1928 }
1929}
1930
1931std::unique_ptr<ROOT::RFieldBase> ROOT::RTupleField::CloneImpl(std::string_view newName) const
1932{
1933 std::vector<std::unique_ptr<RFieldBase>> itemClones;
1934 itemClones.reserve(fSubfields.size());
1935 for (const auto &f : fSubfields) {
1936 itemClones.emplace_back(f->Clone(f->GetFieldName()));
1937 }
1938 return std::unique_ptr<RTupleField>(new RTupleField(newName, std::move(itemClones)));
1939}
1940
1942{
1943 static const std::vector<std::string> prefixes = {"std::pair<", "std::tuple<"};
1944
1945 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
1946 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
1947
1948 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1949 const auto nOnDiskSubfields = fieldDesc.GetLinkIds().size();
1950 const auto nSubfields = fSubfields.size();
1951 if (nOnDiskSubfields != nSubfields) {
1952 throw ROOT::RException(R__FAIL("invalid number of on-disk subfields for std::tuple " +
1953 std::to_string(nOnDiskSubfields) + " vs. " + std::to_string(nSubfields) + "\n" +
1954 Internal::GetTypeTraceReport(*this, desc)));
1955 }
1956}
1957
1958//------------------------------------------------------------------------------
1959
1960namespace {
1961
1962// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
1963constexpr std::size_t GetVariantTagSize()
1964{
1965 // Should be all zeros except for the tag, which is 1
1966 std::variant<char> t;
1967 constexpr auto sizeOfT = sizeof(t);
1968
1969 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
1970 return sizeOfT == 2 ? 1 : 4;
1971}
1972
1973template <std::size_t VariantSizeT>
1974struct RVariantTag {
1975 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
1976 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
1977};
1978
1979} // anonymous namespace
1980
1982 : ROOT::RFieldBase(name, source.GetTypeName(), ROOT::ENTupleStructure::kVariant, false /* isSimple */),
1983 fMaxItemSize(source.fMaxItemSize),
1984 fMaxAlignment(source.fMaxAlignment),
1985 fTagOffset(source.fTagOffset),
1986 fVariantOffset(source.fVariantOffset),
1987 fNWritten(source.fNWritten.size(), 0)
1988{
1989 for (const auto &f : source.GetConstSubfields())
1990 Attach(f->Clone(f->GetFieldName()));
1991 fTraits = source.fTraits;
1992}
1993
1994ROOT::RVariantField::RVariantField(std::string_view fieldName, std::vector<std::unique_ptr<RFieldBase>> itemFields)
1995 : ROOT::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields, false /* useTypeAliases */) + ">",
1996 ROOT::ENTupleStructure::kVariant, false /* isSimple */)
1997{
1998 // The variant needs to initialize its own tag member
1999 fTraits |= kTraitTriviallyDestructible & ~kTraitTriviallyConstructible;
2000
2001 const std::string typeAlias = "std::variant<" + GetTypeList(itemFields, true /* useTypeAliases */) + ">";
2002 if (typeAlias != GetTypeName())
2003 fTypeAlias = typeAlias;
2004
2005 auto nFields = itemFields.size();
2006 if (nFields == 0 || nFields > kMaxVariants) {
2007 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
2008 }
2009 fNWritten.resize(nFields, 0);
2010 for (unsigned int i = 0; i < nFields; ++i) {
2011 fMaxItemSize = std::max(fMaxItemSize, itemFields[i]->GetValueSize());
2012 fMaxAlignment = std::max(fMaxAlignment, itemFields[i]->GetAlignment());
2013 fTraits &= itemFields[i]->GetTraits();
2014 Attach(std::move(itemFields[i]), "_" + std::to_string(i));
2015 }
2016
2017 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
2018 // For instance, std::variant<std::optional<int>> on macOS.
2019 auto cl = TClass::GetClass(GetTypeName().c_str());
2020 assert(cl);
2021 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
2022 if (dm)
2023 fVariantOffset = dm->GetOffset();
2024
2025 const auto tagSize = GetVariantTagSize();
2026 const auto padding = tagSize - (fMaxItemSize % tagSize);
2027 fTagOffset = fVariantOffset + fMaxItemSize + ((padding == tagSize) ? 0 : padding);
2028}
2029
2030std::unique_ptr<ROOT::RFieldBase> ROOT::RVariantField::CloneImpl(std::string_view newName) const
2031{
2032 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
2033}
2034
2035std::uint8_t ROOT::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
2036{
2037 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
2038 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
2039 return (tag == TagType_t(-1)) ? 0 : tag + 1;
2040}
2041
2042void ROOT::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
2043{
2044 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
2045 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
2046 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
2047}
2048
2049std::size_t ROOT::RVariantField::AppendImpl(const void *from)
2050{
2051 auto tag = GetTag(from, fTagOffset);
2052 std::size_t nbytes = 0;
2053 auto index = 0;
2054 if (tag > 0) {
2055 nbytes += CallAppendOn(*fSubfields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
2056 index = fNWritten[tag - 1]++;
2057 }
2058 ROOT::Internal::RColumnSwitch varSwitch(index, tag);
2059 fPrincipalColumn->Append(&varSwitch);
2060 return nbytes + sizeof(ROOT::Internal::RColumnSwitch);
2061}
2062
2064{
2065 RNTupleLocalIndex variantIndex;
2066 std::uint32_t tag;
2067 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
2068 R__ASSERT(tag < 256);
2069
2070 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
2071 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
2072 // any `std::holds_alternative<T>` check fail later.
2073 if (R__likely(tag > 0)) {
2074 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
2075 CallConstructValueOn(*fSubfields[tag - 1], varPtr);
2076 CallReadOn(*fSubfields[tag - 1], variantIndex, varPtr);
2077 }
2078 SetTag(to, fTagOffset, tag);
2079}
2080
2082{
2083 static RColumnRepresentations representations({{ENTupleColumnType::kSwitch}}, {});
2084 return representations;
2085}
2086
2088{
2089 GenerateColumnsImpl<ROOT::Internal::RColumnSwitch>();
2090}
2091
2093{
2094 GenerateColumnsImpl<ROOT::Internal::RColumnSwitch>(desc);
2095}
2096
2098{
2099 static const std::vector<std::string> prefixes = {"std::variant<"};
2100
2101 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
2102 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
2103
2104 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
2105 if (fSubfields.size() != fieldDesc.GetLinkIds().size()) {
2106 throw RException(R__FAIL("number of variants on-disk do not match for " + GetQualifiedFieldName() + "\n" +
2107 Internal::GetTypeTraceReport(*this, desc)));
2108 }
2109}
2110
2112{
2113 memset(where, 0, GetValueSize());
2114 CallConstructValueOn(*fSubfields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
2115 SetTag(where, fTagOffset, 1);
2116}
2117
2119{
2120 auto tag = GetTag(objPtr, fTagOffset);
2121 if (tag > 0) {
2122 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
2123 }
2124 RDeleter::operator()(objPtr, dtorOnly);
2125}
2126
2127std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVariantField::GetDeleter() const
2128{
2129 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
2130 itemDeleters.reserve(fSubfields.size());
2131 for (const auto &f : fSubfields) {
2132 itemDeleters.emplace_back(GetDeleterOf(*f));
2133 }
2134 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, GetAlignment(), std::move(itemDeleters));
2135}
2136
2138{
2139 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
2140}
2141
2143{
2144 const auto alignment = GetAlignment();
2145 const auto actualSize = fTagOffset + GetVariantTagSize();
2146 const auto padding = alignment - (actualSize % alignment);
2147 return actualSize + ((padding == alignment) ? 0 : padding);
2148}
2149
2151{
2152 std::fill(fNWritten.begin(), fNWritten.end(), 0);
2153}
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 VisitProxiedCollectionField(const ROOT::RProxiedCollectionField &field)
virtual void VisitTObjectField(const ROOT::RField< TObject > &field)
virtual void VisitStreamerField(const ROOT::RStreamerField &field)
virtual void VisitEnumField(const ROOT::REnumField &field)
virtual void VisitSoAField(const ROOT::Experimental::RSoAField &field)
virtual void VisitClassField(const ROOT::RClassField &field)
void operator()(void *objPtr, bool dtorOnly) final
The SoA field provides I/O for an in-memory SoA layout linked to an on-disk collection of the underly...
Definition RFieldSoA.hxx:56
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::vector< std::size_t > fSoAMemberOffsets
The offset of the RVec members in the SoA type in the same order as fRecordMemberFields.
Definition RFieldSoA.hxx:79
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
const std::type_info * GetPolymorphicTypeInfo() const
For polymorphic classes (that declare or inherit at least one virtual method), return the expected dy...
size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::vector< std::unique_ptr< RDeleter > > fRecordMemberDeleters
Definition RFieldSoA.hxx:81
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 CollectRecordMemberFields()
Called during construction, picks up the (nested) member fields of the underlying record type(s) and ...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
size_t GetAlignment() const final
What alignof(T) for this type returns.
std::unique_ptr< std::vector< std::size_t > > fSplitOffsets
Definition RFieldSoA.hxx:96
std::vector< RFieldBase * > fRecordMemberFields
Direct access to the member fields of the underlying record.
Definition RFieldSoA.hxx:77
RSoAField(std::string_view fieldName, const RSoAField &source)
Used by CloneImpl.
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
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:95
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
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< std::mutex > fLockSplitFields
protects the fSplitFields member.
Definition RFieldSoA.hxx:97
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 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