Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RField.cxx
Go to the documentation of this file.
1/// \file RField.cxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-15
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#include <ROOT/RColumn.hxx>
17#include <ROOT/REntry.hxx>
18#include <ROOT/RError.hxx>
19#include <ROOT/RField.hxx>
21#include <ROOT/RLogger.hxx>
23#include <ROOT/RNTupleModel.hxx>
25
26#include <TBaseClass.h>
27#include <TBufferFile.h>
28#include <TClass.h>
29#include <TClassEdit.h>
30#include <TCollection.h>
31#include <TDataMember.h>
32#include <TDictAttributeMap.h>
33#include <TEnum.h>
34#include <TError.h>
35#include <TList.h>
36#include <TObject.h>
37#include <TObjArray.h>
38#include <TObjString.h>
39#include <TRealData.h>
40#include <TSchemaRule.h>
41#include <TSchemaRuleSet.h>
42#include <TVirtualObject.h>
44
45#include <algorithm>
46#include <cctype> // for isspace
47#include <charconv>
48#include <cstdint>
49#include <cstdlib> // for malloc, free
50#include <cstring> // for memset
51#include <exception>
52#include <functional>
53#include <iostream>
54#include <memory>
55#include <new> // hardware_destructive_interference_size
56#include <type_traits>
57#include "ROOT/RNTupleUtil.hxx"
58#include <unordered_map>
59
60namespace {
61
62const std::unordered_map<std::string_view, std::string_view> typeTranslationMap{
63 {"Bool_t", "bool"},
64 {"Float_t", "float"},
65 {"Double_t", "double"},
66 {"string", "std::string"},
67
68 {"byte", "std::byte"},
69 {"Char_t", "char"},
70 {"int8_t", "std::int8_t"},
71 {"UChar_t", "unsigned char"},
72 {"uint8_t", "std::uint8_t"},
73
74 {"Short_t", "short"},
75 {"int16_t", "std::int16_t"},
76 {"UShort_t", "unsigned short"},
77 {"uint16_t", "std::uint16_t"},
78
79 {"Int_t", "int"},
80 {"int32_t", "std::int32_t"},
81 {"UInt_t", "unsigned int"},
82 {"unsigned", "unsigned int"},
83 {"uint32_t", "std::uint32_t"},
84
85 // Long_t and ULong_t follow the platform's size of long and unsigned long: They are 64 bit on 64-bit Linux and
86 // macOS, but 32 bit on 32-bit platforms and Windows (regardless of pointer size).
87 {"Long_t", "long"},
88 {"ULong_t", "unsigned long"},
89
90 {"Long64_t", "long long"},
91 {"int64_t", "std::int64_t"},
92 {"ULong64_t", "unsigned long long"},
93 {"uint64_t", "std::uint64_t"}};
94
95/// Used in CreateField() in order to get the comma-separated list of template types
96/// E.g., gets {"int", "std::variant<double,int>"} from "int,std::variant<double,int>"
97std::vector<std::string> TokenizeTypeList(std::string templateType)
98{
99 std::vector<std::string> result;
100 if (templateType.empty())
101 return result;
102
103 const char *eol = templateType.data() + templateType.length();
104 const char *typeBegin = templateType.data();
105 const char *typeCursor = templateType.data();
106 unsigned int nestingLevel = 0;
107 while (typeCursor != eol) {
108 switch (*typeCursor) {
109 case '<': ++nestingLevel; break;
110 case '>': --nestingLevel; break;
111 case ',':
112 if (nestingLevel == 0) {
113 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
114 typeBegin = typeCursor + 1;
115 }
116 break;
117 }
118 typeCursor++;
119 }
120 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
121 return result;
122}
123
124/// Parse a type name of the form `T[n][m]...` and return the base type `T` and a vector that contains,
125/// in order, the declared size for each dimension, e.g. for `unsigned char[1][2][3]` it returns the tuple
126/// `{"unsigned char", {1, 2, 3}}`. Extra whitespace in `typeName` should be removed before calling this function.
127///
128/// If `typeName` is not an array type, it returns a tuple `{T, {}}`. On error, it returns a default-constructed tuple.
129std::tuple<std::string, std::vector<size_t>> ParseArrayType(std::string_view typeName)
130{
131 std::vector<size_t> sizeVec;
132
133 // Only parse outer array definition, i.e. the right `]` should be at the end of the type name
134 while (typeName.back() == ']') {
135 auto posRBrace = typeName.size() - 1;
136 auto posLBrace = typeName.find_last_of('[', posRBrace);
137 if (posLBrace == std::string_view::npos)
138 return {};
139
140 size_t size;
141 if (std::from_chars(typeName.data() + posLBrace + 1, typeName.data() + posRBrace, size).ec != std::errc{})
142 return {};
143 sizeVec.insert(sizeVec.begin(), size);
144 typeName.remove_suffix(typeName.size() - posLBrace);
145 }
146 return std::make_tuple(std::string{typeName}, sizeVec);
147}
148
149/// Return the canonical name of a type, resolving typedefs to their underlying types if needed. A canonical type has
150/// typedefs stripped out from the type name.
151std::string GetCanonicalTypeName(const std::string &typeName)
152{
153 // The following types are asummed to be canonical names; thus, do not perform `typedef` resolution on those
154 if (typeName == "ROOT::Experimental::ClusterSize_t" || typeName.substr(0, 5) == "std::" ||
155 typeName.substr(0, 39) == "ROOT::Experimental::RNTupleCardinality<")
156 return typeName;
157
158 return TClassEdit::ResolveTypedef(typeName.c_str());
159}
160
161/// Applies type name normalization rules that lead to the final name used to create a RField, e.g. transforms
162/// `const vector<T>` to `std::vector<T>`. Specifically, `const` / `volatile` qualifiers are removed and `std::` is
163/// added to fully qualify known types in the `std` namespace. The same happens to `ROOT::RVec` which is normalized to
164/// `ROOT::VecOps::RVec`.
165std::string GetNormalizedTypeName(const std::string &typeName)
166{
167 std::string normalizedType{TClassEdit::CleanType(typeName.c_str(), /*mode=*/2)};
168
169 if (auto it = typeTranslationMap.find(normalizedType); it != typeTranslationMap.end())
170 normalizedType = it->second;
171
172 if (normalizedType.substr(0, 7) == "vector<")
173 normalizedType = "std::" + normalizedType;
174 if (normalizedType.substr(0, 6) == "array<")
175 normalizedType = "std::" + normalizedType;
176 if (normalizedType.substr(0, 8) == "variant<")
177 normalizedType = "std::" + normalizedType;
178 if (normalizedType.substr(0, 5) == "pair<")
179 normalizedType = "std::" + normalizedType;
180 if (normalizedType.substr(0, 6) == "tuple<")
181 normalizedType = "std::" + normalizedType;
182 if (normalizedType.substr(0, 7) == "bitset<")
183 normalizedType = "std::" + normalizedType;
184 if (normalizedType.substr(0, 11) == "unique_ptr<")
185 normalizedType = "std::" + normalizedType;
186 if (normalizedType.substr(0, 4) == "set<")
187 normalizedType = "std::" + normalizedType;
188 if (normalizedType.substr(0, 14) == "unordered_set<")
189 normalizedType = "std::" + normalizedType;
190 if (normalizedType.substr(0, 9) == "multiset<")
191 normalizedType = "std::" + normalizedType;
192 if (normalizedType.substr(0, 19) == "unordered_multiset<")
193 normalizedType = "std::" + normalizedType;
194 if (normalizedType.substr(0, 4) == "map<")
195 normalizedType = "std::" + normalizedType;
196 if (normalizedType.substr(0, 14) == "unordered_map<")
197 normalizedType = "std::" + normalizedType;
198 if (normalizedType.substr(0, 9) == "multimap<")
199 normalizedType = "std::" + normalizedType;
200 if (normalizedType.substr(0, 19) == "unordered_multimap<")
201 normalizedType = "std::" + normalizedType;
202 if (normalizedType.substr(0, 7) == "atomic<")
203 normalizedType = "std::" + normalizedType;
204
205 if (normalizedType.substr(0, 11) == "ROOT::RVec<")
206 normalizedType = "ROOT::VecOps::RVec<" + normalizedType.substr(11);
207
208 return normalizedType;
209}
210
211/// Used as a thread local context storage for Create(); steers the behavior of the Create() call stack
212class CreateContextGuard;
213class CreateContext {
214 friend class CreateContextGuard;
215 /// All classes that were defined by Create() calls higher up in the stack. Finds cyclic type definitions.
216 std::vector<std::string> fClassesOnStack;
217 /// If set to true, Create() will create an RInvalidField on error instead of throwing an exception.
218 /// This is used in RFieldBase::Check() to identify unsupported sub fields.
219 bool fContinueOnError = false;
220
221public:
222 CreateContext() = default;
223 bool GetContinueOnError() const { return fContinueOnError; }
224};
225
226/// RAII for modifications of CreateContext
227class CreateContextGuard {
228 CreateContext &fCreateContext;
229 std::size_t fNOriginalClassesOnStack;
230 bool fOriginalContinueOnError;
231
232public:
233 CreateContextGuard(CreateContext &ctx)
234 : fCreateContext(ctx),
235 fNOriginalClassesOnStack(ctx.fClassesOnStack.size()),
236 fOriginalContinueOnError(ctx.fContinueOnError)
237 {
238 }
239 ~CreateContextGuard()
240 {
241 fCreateContext.fClassesOnStack.resize(fNOriginalClassesOnStack);
242 fCreateContext.fContinueOnError = fOriginalContinueOnError;
243 }
244
245 void AddClassToStack(const std::string &cl)
246 {
247 if (std::find(fCreateContext.fClassesOnStack.begin(), fCreateContext.fClassesOnStack.end(), cl) !=
248 fCreateContext.fClassesOnStack.end()) {
249 throw ROOT::Experimental::RException(R__FAIL("cyclic class definition: " + cl));
250 }
251 fCreateContext.fClassesOnStack.emplace_back(cl);
252 }
253
254 void SetContinueOnError(bool value) { fCreateContext.fContinueOnError = value; }
255};
256
257/// Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the RVec object.
258/// Returns pointers to fBegin, fSize and fCapacity in a std::tuple.
259std::tuple<void **, std::int32_t *, std::int32_t *> GetRVecDataMembers(void *rvecPtr)
260{
261 void **begin = reinterpret_cast<void **>(rvecPtr);
262 // int32_t fSize is the second data member (after 1 void*)
263 std::int32_t *size = reinterpret_cast<std::int32_t *>(begin + 1);
264 R__ASSERT(*size >= 0);
265 // int32_t fCapacity is the third data member (1 int32_t after fSize)
266 std::int32_t *capacity = size + 1;
267 R__ASSERT(*capacity >= -1);
268 return {begin, size, capacity};
269}
270
271std::tuple<const void *const *, const std::int32_t *, const std::int32_t *> GetRVecDataMembers(const void *rvecPtr)
272{
273 return {GetRVecDataMembers(const_cast<void *>(rvecPtr))};
274}
275
276/// Applies the field IDs from 'from' to 'to', where from and to are expected to be each other's clones.
277/// Used in RClassField and RCollectionClassField cloning. In these classes, we don't clone the subfields
278/// but we recreate them. Therefore, the on-disk IDs need to be fixed up.
279void SyncFieldIDs(const ROOT::Experimental::RFieldBase &from, ROOT::Experimental::RFieldBase &to)
280{
281 auto iFrom = from.cbegin();
282 auto iTo = to.begin();
283 for (; iFrom != from.cend(); ++iFrom, ++iTo) {
284 iTo->SetOnDiskId(iFrom->GetOnDiskId());
285 }
286}
287
288std::size_t EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
289{
290 // the size of an RVec<T> is the size of its 4 data-members + optional padding:
291 //
292 // data members:
293 // - void *fBegin
294 // - int32_t fSize
295 // - int32_t fCapacity
296 // - the char[] inline storage, which is aligned like T
297 //
298 // padding might be present:
299 // - between fCapacity and the char[] buffer aligned like T
300 // - after the char[] buffer
301
302 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
303
304 // mimic the logic of RVecInlineStorageSize, but at runtime
305 const auto inlineStorageSz = [&] {
306#ifdef R__HAS_HARDWARE_INTERFERENCE_SIZE
307 // hardware_destructive_interference_size is a C++17 feature but many compilers do not implement it yet
308 constexpr unsigned cacheLineSize = std::hardware_destructive_interference_size;
309#else
310 constexpr unsigned cacheLineSize = 64u;
311#endif
312 const unsigned elementsPerCacheLine = (cacheLineSize - dataMemberSz) / sizeOfT;
313 constexpr unsigned maxInlineByteSize = 1024;
314 const unsigned nElements =
315 elementsPerCacheLine >= 8 ? elementsPerCacheLine : (sizeOfT * 8 > maxInlineByteSize ? 0 : 8);
316 return nElements * sizeOfT;
317 }();
318
319 // compute padding between first 3 datamembers and inline buffer
320 // (there should be no padding between the first 3 data members)
321 auto paddingMiddle = dataMemberSz % alignOfT;
322 if (paddingMiddle != 0)
323 paddingMiddle = alignOfT - paddingMiddle;
324
325 // padding at the end of the object
326 auto paddingEnd = (dataMemberSz + paddingMiddle + inlineStorageSz) % alignOfRVecT;
327 if (paddingEnd != 0)
328 paddingEnd = alignOfRVecT - paddingEnd;
329
330 return dataMemberSz + inlineStorageSz + paddingMiddle + paddingEnd;
331}
332
333std::size_t EvalRVecAlignment(std::size_t alignOfSubField)
334{
335 // the alignment of an RVec<T> is the largest among the alignments of its data members
336 // (including the inline buffer which has the same alignment as the RVec::value_type)
337 return std::max({alignof(void *), alignof(std::int32_t), alignOfSubField});
338}
339
340void DestroyRVecWithChecks(std::size_t alignOfT, void **beginPtr, char *begin, std::int32_t *capacityPtr)
341{
342 // figure out if we are in the small state, i.e. begin == &inlineBuffer
343 // there might be padding between fCapacity and the inline buffer, so we compute it here
344 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
345 auto paddingMiddle = dataMemberSz % alignOfT;
346 if (paddingMiddle != 0)
347 paddingMiddle = alignOfT - paddingMiddle;
348 const bool isSmall = (reinterpret_cast<void *>(begin) == (beginPtr + dataMemberSz + paddingMiddle));
349
350 const bool owns = (*capacityPtr != -1);
351 if (!isSmall && owns)
352 free(begin);
353}
354
355/// Possible settings for the "rntuple.split" class attribute in the dictionary.
356enum class ERNTupleUnsplitSetting { kForceSplit, kForceUnsplit, kUnset };
357
358ERNTupleUnsplitSetting GetRNTupleUnsplitSetting(TClass *cl)
359{
360 auto am = cl->GetAttributeMap();
361 if (!am || !am->HasKey("rntuple.split"))
362 return ERNTupleUnsplitSetting::kUnset;
363
364 std::string value = am->GetPropertyAsString("rntuple.split");
365 std::transform(value.begin(), value.end(), value.begin(), ::toupper);
366 if (value == "TRUE") {
367 return ERNTupleUnsplitSetting::kForceSplit;
368 } else if (value == "FALSE") {
369 return ERNTupleUnsplitSetting::kForceUnsplit;
370 } else {
372 << "invalid setting for 'rntuple.split' class attribute: " << am->GetPropertyAsString("rntuple.split");
373 return ERNTupleUnsplitSetting::kUnset;
374 }
375}
376
377// Depending on the compiler, the variant tag is stored either in a trailing char or in a trailing unsigned int
378constexpr std::size_t GetVariantTagSize()
379{
380 // Should be all zeros except for the tag, which is 1
381 std::variant<char> t;
382 constexpr auto sizeOfT = sizeof(t);
383
384 static_assert(sizeOfT == 2 || sizeOfT == 8, "unsupported std::variant layout");
385 return sizeOfT == 2 ? 1 : 4;
386}
387
388template <std::size_t VariantSizeT>
389struct RVariantTag {
390 using ValueType_t = typename std::conditional_t<VariantSizeT == 1, std::uint8_t,
391 typename std::conditional_t<VariantSizeT == 4, std::uint32_t, void>>;
392};
393
394/// Used in RUnsplitField::AppendImpl() in order to record the encountered streamer info records
395class TBufferRecStreamer : public TBufferFile {
396public:
397 using RCallbackStreamerInfo = std::function<void(TVirtualStreamerInfo *)>;
398
399private:
400 RCallbackStreamerInfo fCallbackStreamerInfo;
401
402public:
403 TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsiz, RCallbackStreamerInfo callbackStreamerInfo)
404 : TBufferFile(mode, bufsiz), fCallbackStreamerInfo(callbackStreamerInfo)
405 {
406 }
407 void TagStreamerInfo(TVirtualStreamerInfo *info) final { fCallbackStreamerInfo(info); }
408};
409
410} // anonymous namespace
411
413{
414 field.FlushColumns();
415}
417{
418 field.CommitCluster();
419}
421 NTupleSize_t firstEntry)
422{
423 field.ConnectPageSink(sink, firstEntry);
424}
426{
427 field.ConnectPageSource(source);
428}
430//------------------------------------------------------------------------------
431
433{
434 // A single representations with an empty set of columns
437}
438
440 const Selection_t &serializationTypes, const Selection_t &deserializationExtraTypes)
441 : fSerializationTypes(serializationTypes), fDeserializationTypes(serializationTypes)
442{
443 fDeserializationTypes.insert(fDeserializationTypes.end(), deserializationExtraTypes.begin(),
444 deserializationExtraTypes.end());
445}
446
447//------------------------------------------------------------------------------
448
450{
451 // Set fObjPtr to an aliased shared_ptr of the input raw pointer. Note that
452 // fObjPtr will be non-empty but have use count zero.
454}
455
456//------------------------------------------------------------------------------
457
459 : fField(other.fField),
460 fValueSize(other.fValueSize),
461 fCapacity(other.fCapacity),
462 fSize(other.fSize),
463 fIsAdopted(other.fIsAdopted),
464 fNValidValues(other.fNValidValues),
465 fFirstIndex(other.fFirstIndex)
466{
467 std::swap(fDeleter, other.fDeleter);
468 std::swap(fValues, other.fValues);
469 std::swap(fMaskAvail, other.fMaskAvail);
470}
471
473{
474 std::swap(fField, other.fField);
475 std::swap(fDeleter, other.fDeleter);
476 std::swap(fValues, other.fValues);
477 std::swap(fValueSize, other.fValueSize);
478 std::swap(fCapacity, other.fCapacity);
479 std::swap(fSize, other.fSize);
480 std::swap(fIsAdopted, other.fIsAdopted);
481 std::swap(fMaskAvail, other.fMaskAvail);
482 std::swap(fNValidValues, other.fNValidValues);
483 std::swap(fFirstIndex, other.fFirstIndex);
484 return *this;
485}
486
488{
489 if (fValues)
490 ReleaseValues();
491}
492
494{
495 if (fIsAdopted)
496 return;
497
498 if (fField->GetTraits() & RFieldBase::kTraitTriviallyDestructible) {
499 free(fValues);
500 return;
501 }
502
503 for (std::size_t i = 0; i < fCapacity; ++i) {
504 fDeleter->operator()(GetValuePtrAt(i), true /* dtorOnly */);
505 }
506 free(fValues);
507}
508
510{
511 if (fCapacity < size) {
512 if (fIsAdopted) {
513 throw RException(R__FAIL("invalid attempt to bulk read beyond the adopted buffer"));
514 }
515 ReleaseValues();
516 fValues = malloc(size * fValueSize);
517
518 if (!(fField->GetTraits() & RFieldBase::kTraitTriviallyConstructible)) {
519 for (std::size_t i = 0; i < size; ++i) {
520 fField->ConstructValue(GetValuePtrAt(i));
521 }
522 }
523
524 fMaskAvail = std::make_unique<bool[]>(size);
525 fCapacity = size;
526 }
527
528 std::fill(fMaskAvail.get(), fMaskAvail.get() + size, false);
529 fNValidValues = 0;
530
531 fFirstIndex = firstIndex;
532 fSize = size;
533}
534
536{
537 fNValidValues = 0;
538 for (std::size_t i = 0; i < fSize; ++i)
539 fNValidValues += static_cast<std::size_t>(fMaskAvail[i]);
540}
541
542void ROOT::Experimental::RFieldBase::RBulk::AdoptBuffer(void *buf, std::size_t capacity)
543{
544 ReleaseValues();
545 fValues = buf;
546 fCapacity = capacity;
547 fSize = capacity;
548
549 fMaskAvail = std::make_unique<bool[]>(capacity);
550
551 fFirstIndex = RClusterIndex();
552
553 fIsAdopted = true;
554}
555
556//------------------------------------------------------------------------------
557
559{
560 R__LOG_WARNING(NTupleLog()) << "possibly leaking object from RField<T>::CreateObject<void>";
561}
562
563template <>
564std::unique_ptr<void, typename ROOT::Experimental::RFieldBase::RCreateObjectDeleter<void>::deleter>
565ROOT::Experimental::RFieldBase::CreateObject<void>() const
566{
568 return std::unique_ptr<void, RCreateObjectDeleter<void>::deleter>(CreateObjectRawPtr(), gDeleter);
569}
570
571//------------------------------------------------------------------------------
572
573ROOT::Experimental::RFieldBase::RFieldBase(std::string_view name, std::string_view type, ENTupleStructure structure,
574 bool isSimple, std::size_t nRepetitions)
575 : fName(name),
576 fType(type),
577 fStructure(structure),
578 fNRepetitions(nRepetitions),
579 fIsSimple(isSimple),
580 fParent(nullptr),
581 fPrincipalColumn(nullptr),
582 fTraits(isSimple ? kTraitMappable : 0)
583{
584}
585
587{
588 std::string result = GetFieldName();
589 auto parent = GetParent();
590 while (parent && !parent->GetFieldName().empty()) {
591 result = parent->GetFieldName() + "." + result;
592 parent = parent->GetParent();
593 }
594 return result;
595}
596
598ROOT::Experimental::RFieldBase::Create(const std::string &fieldName, const std::string &typeName)
599{
600 auto typeAlias = GetNormalizedTypeName(typeName);
601 auto canonicalType = GetNormalizedTypeName(GetCanonicalTypeName(typeAlias));
602 return R__FORWARD_RESULT(RFieldBase::Create(fieldName, canonicalType, typeAlias));
603}
604
605std::vector<ROOT::Experimental::RFieldBase::RCheckResult>
606ROOT::Experimental::RFieldBase::Check(const std::string &fieldName, const std::string &typeName)
607{
608 auto typeAlias = GetNormalizedTypeName(typeName);
609 auto canonicalType = GetNormalizedTypeName(GetCanonicalTypeName(typeAlias));
610
611 RFieldZero fieldZero;
612 fieldZero.Attach(RFieldBase::Create(fieldName, canonicalType, typeAlias, true /* continueOnError */).Unwrap());
613
614 std::vector<RCheckResult> result;
615 for (const auto &f : fieldZero) {
616 auto invalidField = dynamic_cast<const RInvalidField *>(&f);
617 if (!invalidField)
618 continue;
619
620 result.emplace_back(
621 RCheckResult{invalidField->GetQualifiedFieldName(), invalidField->GetTypeName(), invalidField->GetError()});
622 }
623 return result;
624}
625
627ROOT::Experimental::RFieldBase::Create(const std::string &fieldName, const std::string &canonicalType,
628 const std::string &typeAlias, bool continueOnError)
629{
630 thread_local CreateContext createContext;
631 CreateContextGuard createContextGuard(createContext);
632 if (continueOnError)
633 createContextGuard.SetContinueOnError(true);
634
635 auto fnFail = [&fieldName, &canonicalType](const std::string &errMsg) -> RResult<std::unique_ptr<RFieldBase>> {
636 if (createContext.GetContinueOnError()) {
637 return std::unique_ptr<RFieldBase>(std::make_unique<RInvalidField>(fieldName, canonicalType, errMsg));
638 } else {
639 return R__FAIL(errMsg);
640 }
641 };
642
643 if (canonicalType.empty())
644 return R__FORWARD_RESULT(fnFail("no type name specified for field '" + fieldName + "'"));
645
646 if (auto [arrayBaseType, arraySizes] = ParseArrayType(canonicalType); !arraySizes.empty()) {
647 std::unique_ptr<RFieldBase> arrayField = Create("_0", arrayBaseType).Unwrap();
648 for (int i = arraySizes.size() - 1; i >= 0; --i) {
649 arrayField = std::make_unique<RArrayField>((i == 0) ? fieldName : "_0", std::move(arrayField), arraySizes[i]);
650 }
651 return arrayField;
652 }
653
654 std::unique_ptr<ROOT::Experimental::RFieldBase> result;
655
656 if (canonicalType == "ROOT::Experimental::ClusterSize_t") {
657 result = std::make_unique<RField<ClusterSize_t>>(fieldName);
658 } else if (canonicalType == "bool") {
659 result = std::make_unique<RField<bool>>(fieldName);
660 } else if (canonicalType == "char") {
661 result = std::make_unique<RField<char>>(fieldName);
662 } else if (canonicalType == "signed char") {
663 result = std::make_unique<RField<signed char>>(fieldName);
664 } else if (canonicalType == "unsigned char") {
665 result = std::make_unique<RField<unsigned char>>(fieldName);
666 } else if (canonicalType == "short") {
667 result = std::make_unique<RField<short>>(fieldName);
668 } else if (canonicalType == "unsigned short") {
669 result = std::make_unique<RField<unsigned short>>(fieldName);
670 } else if (canonicalType == "int") {
671 result = std::make_unique<RField<int>>(fieldName);
672 } else if (canonicalType == "unsigned int") {
673 result = std::make_unique<RField<unsigned int>>(fieldName);
674 } else if (canonicalType == "long") {
675 result = std::make_unique<RField<long>>(fieldName);
676 } else if (canonicalType == "unsigned long") {
677 result = std::make_unique<RField<unsigned long>>(fieldName);
678 } else if (canonicalType == "long long") {
679 result = std::make_unique<RField<long long>>(fieldName);
680 } else if (canonicalType == "unsigned long long") {
681 result = std::make_unique<RField<unsigned long long>>(fieldName);
682 } else if (canonicalType == "std::byte") {
683 result = std::make_unique<RField<std::byte>>(fieldName);
684 } else if (canonicalType == "std::int8_t") {
685 result = std::make_unique<RField<std::int8_t>>(fieldName);
686 } else if (canonicalType == "std::uint8_t") {
687 result = std::make_unique<RField<std::uint8_t>>(fieldName);
688 } else if (canonicalType == "std::int16_t") {
689 result = std::make_unique<RField<std::int16_t>>(fieldName);
690 } else if (canonicalType == "std::uint16_t") {
691 result = std::make_unique<RField<std::uint16_t>>(fieldName);
692 } else if (canonicalType == "std::int32_t") {
693 result = std::make_unique<RField<std::int32_t>>(fieldName);
694 } else if (canonicalType == "std::uint32_t") {
695 result = std::make_unique<RField<std::uint32_t>>(fieldName);
696 } else if (canonicalType == "std::int64_t") {
697 result = std::make_unique<RField<std::int64_t>>(fieldName);
698 } else if (canonicalType == "std::uint64_t") {
699 result = std::make_unique<RField<std::uint64_t>>(fieldName);
700 } else if (canonicalType == "float") {
701 result = std::make_unique<RField<float>>(fieldName);
702 } else if (canonicalType == "double") {
703 result = std::make_unique<RField<double>>(fieldName);
704 } else if (canonicalType == "Double32_t") {
705 result = std::make_unique<RField<double>>(fieldName);
706 static_cast<RField<double> *>(result.get())->SetDouble32();
707 // Prevent the type alias from being reset by returning early
708 return result;
709 } else if (canonicalType == "std::string") {
710 result = std::make_unique<RField<std::string>>(fieldName);
711 } else if (canonicalType == "TObject") {
712 result = std::make_unique<RField<TObject>>(fieldName);
713 } else if (canonicalType == "std::vector<bool>") {
714 result = std::make_unique<RField<std::vector<bool>>>(fieldName);
715 } else if (canonicalType.substr(0, 12) == "std::vector<") {
716 std::string itemTypeName = canonicalType.substr(12, canonicalType.length() - 13);
717 auto itemField = Create("_0", itemTypeName);
718 result = std::make_unique<RVectorField>(fieldName, itemField.Unwrap());
719 } else if (canonicalType.substr(0, 19) == "ROOT::VecOps::RVec<") {
720 std::string itemTypeName = canonicalType.substr(19, canonicalType.length() - 20);
721 auto itemField = Create("_0", itemTypeName);
722 result = std::make_unique<RRVecField>(fieldName, itemField.Unwrap());
723 } else if (canonicalType.substr(0, 11) == "std::array<") {
724 auto arrayDef = TokenizeTypeList(canonicalType.substr(11, canonicalType.length() - 12));
725 if (arrayDef.size() != 2) {
726 return R__FORWARD_RESULT(fnFail("the template list for std::array must have exactly two elements"));
727 }
728 auto arrayLength = std::stoi(arrayDef[1]);
729 auto itemField = Create("_0", arrayDef[0]);
730 result = std::make_unique<RArrayField>(fieldName, itemField.Unwrap(), arrayLength);
731 } else if (canonicalType.substr(0, 13) == "std::variant<") {
732 auto innerTypes = TokenizeTypeList(canonicalType.substr(13, canonicalType.length() - 14));
733 std::vector<RFieldBase *> items;
734 items.reserve(innerTypes.size());
735 for (unsigned int i = 0; i < innerTypes.size(); ++i) {
736 items.emplace_back(Create("_" + std::to_string(i), innerTypes[i]).Unwrap().release());
737 }
738 result = std::make_unique<RVariantField>(fieldName, items);
739 } else if (canonicalType.substr(0, 10) == "std::pair<") {
740 auto innerTypes = TokenizeTypeList(canonicalType.substr(10, canonicalType.length() - 11));
741 if (innerTypes.size() != 2) {
742 return R__FORWARD_RESULT(fnFail("the type list for std::pair must have exactly two elements"));
743 }
744 std::array<std::unique_ptr<RFieldBase>, 2> items{Create("_0", innerTypes[0]).Unwrap(),
745 Create("_1", innerTypes[1]).Unwrap()};
746 result = std::make_unique<RPairField>(fieldName, items);
747 } else if (canonicalType.substr(0, 11) == "std::tuple<") {
748 auto innerTypes = TokenizeTypeList(canonicalType.substr(11, canonicalType.length() - 12));
749 std::vector<std::unique_ptr<RFieldBase>> items;
750 items.reserve(innerTypes.size());
751 for (unsigned int i = 0; i < innerTypes.size(); ++i) {
752 items.emplace_back(Create("_" + std::to_string(i), innerTypes[i]).Unwrap());
753 }
754 result = std::make_unique<RTupleField>(fieldName, items);
755 } else if (canonicalType.substr(0, 12) == "std::bitset<") {
756 auto size = std::stoull(canonicalType.substr(12, canonicalType.length() - 13));
757 result = std::make_unique<RBitsetField>(fieldName, size);
758 } else if (canonicalType.substr(0, 16) == "std::unique_ptr<") {
759 std::string itemTypeName = canonicalType.substr(16, canonicalType.length() - 17);
760 auto itemField = Create("_0", itemTypeName).Unwrap();
761 auto normalizedInnerTypeName = itemField->GetTypeName();
762 result = std::make_unique<RUniquePtrField>(fieldName, "std::unique_ptr<" + normalizedInnerTypeName + ">",
763 std::move(itemField));
764 } else if (canonicalType.substr(0, 14) == "std::optional<") {
765 std::string itemTypeName = canonicalType.substr(14, canonicalType.length() - 15);
766 auto itemField = Create("_0", itemTypeName).Unwrap();
767 auto normalizedInnerTypeName = itemField->GetTypeName();
768 result = std::make_unique<ROptionalField>(fieldName, "std::optional<" + normalizedInnerTypeName + ">",
769 std::move(itemField));
770 } else if (canonicalType.substr(0, 9) == "std::set<") {
771 std::string itemTypeName = canonicalType.substr(9, canonicalType.length() - 10);
772 auto itemField = Create("_0", itemTypeName).Unwrap();
773 auto normalizedInnerTypeName = itemField->GetTypeName();
774 result =
775 std::make_unique<RSetField>(fieldName, "std::set<" + normalizedInnerTypeName + ">", std::move(itemField));
776 } else if (canonicalType.substr(0, 19) == "std::unordered_set<") {
777 std::string itemTypeName = canonicalType.substr(19, canonicalType.length() - 20);
778 auto itemField = Create("_0", itemTypeName).Unwrap();
779 auto normalizedInnerTypeName = itemField->GetTypeName();
780 result = std::make_unique<RSetField>(fieldName, "std::unordered_set<" + normalizedInnerTypeName + ">",
781 std::move(itemField));
782 } else if (canonicalType.substr(0, 14) == "std::multiset<") {
783 std::string itemTypeName = canonicalType.substr(14, canonicalType.length() - 15);
784 auto itemField = Create("_0", itemTypeName).Unwrap();
785 auto normalizedInnerTypeName = itemField->GetTypeName();
786 result =
787 std::make_unique<RSetField>(fieldName, "std::multiset<" + normalizedInnerTypeName + ">", std::move(itemField));
788 } else if (canonicalType.substr(0, 24) == "std::unordered_multiset<") {
789 std::string itemTypeName = canonicalType.substr(24, canonicalType.length() - 25);
790 auto itemField = Create("_0", itemTypeName).Unwrap();
791 auto normalizedInnerTypeName = itemField->GetTypeName();
792 result = std::make_unique<RSetField>(fieldName, "std::unordered_multiset<" + normalizedInnerTypeName + ">",
793 std::move(itemField));
794 } else if (canonicalType.substr(0, 9) == "std::map<") {
795 auto innerTypes = TokenizeTypeList(canonicalType.substr(9, canonicalType.length() - 10));
796 if (innerTypes.size() != 2) {
797 return R__FORWARD_RESULT(fnFail("the type list for std::map must have exactly two elements"));
798 }
799
800 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
801
802 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
803 // the inner type names are properly normalized.
804 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
805 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
806
807 result = std::make_unique<RMapField>(fieldName, "std::map<" + keyTypeName + "," + valueTypeName + ">",
808 std::move(itemField));
809 } else if (canonicalType.substr(0, 19) == "std::unordered_map<") {
810 auto innerTypes = TokenizeTypeList(canonicalType.substr(19, canonicalType.length() - 20));
811 if (innerTypes.size() != 2)
812 return R__FORWARD_RESULT(fnFail("the type list for std::unordered_map must have exactly two elements"));
813
814 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
815
816 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
817 // the inner type names are properly normalized.
818 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
819 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
820
821 result = std::make_unique<RMapField>(fieldName, "std::unordered_map<" + keyTypeName + "," + valueTypeName + ">",
822 std::move(itemField));
823 } else if (canonicalType.substr(0, 14) == "std::multimap<") {
824 auto innerTypes = TokenizeTypeList(canonicalType.substr(14, canonicalType.length() - 15));
825 if (innerTypes.size() != 2)
826 return R__FORWARD_RESULT(fnFail("the type list for std::multimap must have exactly two elements"));
827
828 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
829
830 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
831 // the inner type names are properly normalized.
832 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
833 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
834
835 result = std::make_unique<RMapField>(fieldName, "std::multimap<" + keyTypeName + "," + valueTypeName + ">",
836 std::move(itemField));
837 } else if (canonicalType.substr(0, 24) == "std::unordered_multimap<") {
838 auto innerTypes = TokenizeTypeList(canonicalType.substr(24, canonicalType.length() - 25));
839 if (innerTypes.size() != 2)
840 return R__FORWARD_RESULT(fnFail("the type list for std::unordered_multimap must have exactly two elements"));
841
842 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
843
844 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
845 // the inner type names are properly normalized.
846 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
847 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
848
849 result = std::make_unique<RMapField>(
850 fieldName, "std::unordered_multimap<" + keyTypeName + "," + valueTypeName + ">", std::move(itemField));
851 } else if (canonicalType.substr(0, 12) == "std::atomic<") {
852 std::string itemTypeName = canonicalType.substr(12, canonicalType.length() - 13);
853 auto itemField = Create("_0", itemTypeName).Unwrap();
854 auto normalizedInnerTypeName = itemField->GetTypeName();
855 result = std::make_unique<RAtomicField>(fieldName, "std::atomic<" + normalizedInnerTypeName + ">",
856 std::move(itemField));
857 } else if (canonicalType.substr(0, 39) == "ROOT::Experimental::RNTupleCardinality<") {
858 auto innerTypes = TokenizeTypeList(canonicalType.substr(39, canonicalType.length() - 40));
859 if (innerTypes.size() != 1)
860 return R__FORWARD_RESULT(fnFail("invalid cardinality template: " + canonicalType));
861 if (innerTypes[0] == "std::uint32_t") {
862 result = std::make_unique<RField<RNTupleCardinality<std::uint32_t>>>(fieldName);
863 } else if (innerTypes[0] == "std::uint64_t") {
864 result = std::make_unique<RField<RNTupleCardinality<std::uint64_t>>>(fieldName);
865 } else {
866 return R__FORWARD_RESULT(fnFail("invalid cardinality template: " + canonicalType));
867 }
868 }
869
870 try {
871 if (!result) {
872 auto e = TEnum::GetEnum(canonicalType.c_str());
873 if (e != nullptr) {
874 result = std::make_unique<REnumField>(fieldName, canonicalType);
875 }
876 }
877
878 if (!result) {
879 auto cl = TClass::GetClass(canonicalType.c_str());
880 if (cl != nullptr) {
881 createContextGuard.AddClassToStack(canonicalType);
882 if (cl->GetCollectionProxy()) {
883 result = std::make_unique<RProxiedCollectionField>(fieldName, canonicalType);
884 } else {
885 if (GetRNTupleUnsplitSetting(cl) == ERNTupleUnsplitSetting::kForceUnsplit) {
886 result = std::make_unique<RUnsplitField>(fieldName, canonicalType);
887 } else {
888 result = std::make_unique<RClassField>(fieldName, canonicalType);
889 }
890 }
891 }
892 }
893 } catch (RException &e) {
894 auto error = e.GetError();
895 if (createContext.GetContinueOnError()) {
896 std::unique_ptr<RFieldBase>(std::make_unique<RInvalidField>(fieldName, canonicalType, error.GetReport()));
897 } else {
898 return error;
899 }
900 }
901
902 if (result) {
903 if (typeAlias != canonicalType)
904 result->fTypeAlias = typeAlias;
905 return result;
906 }
907 return R__FORWARD_RESULT(fnFail("unknown type: " + canonicalType));
908}
909
911{
912 if (fieldName.empty()) {
913 return R__FAIL("name cannot be empty string \"\"");
914 } else if (fieldName.find('.') != std::string::npos) {
915 return R__FAIL("name '" + std::string(fieldName) + "' cannot contain dot characters '.'");
916 }
917 return RResult<void>::Success();
918}
919
922{
923 static RColumnRepresentations representations;
924 return representations;
925}
926
927std::unique_ptr<ROOT::Experimental::RFieldBase> ROOT::Experimental::RFieldBase::Clone(std::string_view newName) const
928{
929 auto clone = CloneImpl(newName);
930 clone->fTypeAlias = fTypeAlias;
931 clone->fOnDiskId = fOnDiskId;
932 clone->fDescription = fDescription;
933 // We can just copy the references because fColumnRepresentatives point into a static structure
934 clone->fColumnRepresentatives = fColumnRepresentatives;
935 return clone;
936}
937
938std::size_t ROOT::Experimental::RFieldBase::AppendImpl(const void * /* from */)
939{
940 R__ASSERT(false && "A non-simple RField must implement its own AppendImpl");
941 return 0;
942}
943
945{
946 R__ASSERT(false);
947}
948
950{
951 ReadGlobalImpl(fPrincipalColumn->GetGlobalIndex(clusterIndex), to);
952}
953
955{
956 const auto valueSize = GetValueSize();
957 std::size_t nRead = 0;
958 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
959 // Value not needed
960 if (!bulkSpec.fMaskReq[i])
961 continue;
962
963 // Value already present
964 if (bulkSpec.fMaskAvail[i])
965 continue;
966
967 Read(bulkSpec.fFirstIndex + i, reinterpret_cast<unsigned char *>(bulkSpec.fValues) + i * valueSize);
968 bulkSpec.fMaskAvail[i] = true;
969 nRead++;
970 }
971 return nRead;
972}
973
975{
976 void *where = operator new(GetValueSize());
977 R__ASSERT(where != nullptr);
978 ConstructValue(where);
979 return where;
980}
981
983{
984 void *obj = CreateObjectRawPtr();
985 return RValue(this, std::shared_ptr<void>(obj, RSharedPtrDeleter(GetDeleter())));
986}
987
988std::vector<ROOT::Experimental::RFieldBase::RValue>
990{
991 return std::vector<RValue>();
992}
993
994void ROOT::Experimental::RFieldBase::Attach(std::unique_ptr<ROOT::Experimental::RFieldBase> child)
995{
996 // Note that during a model update, new fields will be attached to the zero field. The zero field, however,
997 // does not change its inital state because only its sub fields get connected by RPageSink::UpdateSchema.
998 if (fState != EState::kUnconnected)
999 throw RException(R__FAIL("invalid attempt to attach subfield to already connected field"));
1000 child->fParent = this;
1001 fSubFields.emplace_back(std::move(child));
1002}
1003
1006{
1007 std::size_t result = globalIndex;
1008 for (auto f = this; f != nullptr; f = f->GetParent()) {
1009 auto parent = f->GetParent();
1010 if (parent && (parent->GetStructure() == kCollection || parent->GetStructure() == kVariant))
1011 return 0U;
1012 result *= std::max(f->GetNRepetitions(), std::size_t{1U});
1013 }
1014 return result;
1015}
1016
1017std::vector<ROOT::Experimental::RFieldBase *> ROOT::Experimental::RFieldBase::GetSubFields()
1018{
1019 std::vector<RFieldBase *> result;
1020 result.reserve(fSubFields.size());
1021 for (const auto &f : fSubFields) {
1022 result.emplace_back(f.get());
1023 }
1024 return result;
1025}
1026
1027std::vector<const ROOT::Experimental::RFieldBase *> ROOT::Experimental::RFieldBase::GetSubFields() const
1028{
1029 std::vector<const RFieldBase *> result;
1030 result.reserve(fSubFields.size());
1031 for (const auto &f : fSubFields) {
1032 result.emplace_back(f.get());
1033 }
1034 return result;
1035}
1036
1038{
1039 if (!fAvailableColumns.empty()) {
1040 const auto activeRepresentationIndex = fPrincipalColumn->GetRepresentationIndex();
1041 for (auto &column : fAvailableColumns) {
1042 if (column->GetRepresentationIndex() == activeRepresentationIndex) {
1043 column->Flush();
1044 }
1045 }
1046 }
1047}
1048
1050{
1051 if (!fAvailableColumns.empty()) {
1052 const auto activeRepresentationIndex = fPrincipalColumn->GetRepresentationIndex();
1053 for (auto &column : fAvailableColumns) {
1054 if (column->GetRepresentationIndex() == activeRepresentationIndex) {
1055 column->Flush();
1056 } else {
1057 column->CommitSuppressed();
1058 }
1059 }
1060 }
1061 CommitClusterImpl();
1062}
1063
1064void ROOT::Experimental::RFieldBase::SetDescription(std::string_view description)
1065{
1066 if (fState != EState::kUnconnected)
1067 throw RException(R__FAIL("cannot set field description once field is connected"));
1068 fDescription = std::string(description);
1069}
1070
1072{
1073 if (fState != EState::kUnconnected)
1074 throw RException(R__FAIL("cannot set field ID once field is connected"));
1075 fOnDiskId = id;
1076}
1077
1078/// Write the given value into columns. The value object has to be of the same type as the field.
1079/// Returns the number of uncompressed bytes written.
1080std::size_t ROOT::Experimental::RFieldBase::Append(const void *from)
1081{
1082 if (~fTraits & kTraitMappable)
1083 return AppendImpl(from);
1084
1085 fPrincipalColumn->Append(from);
1086 return fPrincipalColumn->GetElement()->GetPackedSize();
1087}
1088
1090{
1091 return RBulk(this);
1092}
1093
1095{
1096 return RValue(this, objPtr);
1097}
1098
1100{
1101 if (fIsSimple) {
1102 /// For simple types, ignore the mask and memcopy the values into the destination
1103 fPrincipalColumn->ReadV(bulkSpec.fFirstIndex, bulkSpec.fCount, bulkSpec.fValues);
1104 std::fill(bulkSpec.fMaskAvail, bulkSpec.fMaskAvail + bulkSpec.fCount, true);
1105 return RBulkSpec::kAllSet;
1106 }
1107
1108 return ReadBulkImpl(bulkSpec);
1109}
1110
1112{
1113 return fSubFields.empty() ? RSchemaIterator(this, -1) : RSchemaIterator(fSubFields[0].get(), 0);
1114}
1115
1117{
1118 return RSchemaIterator(this, -1);
1119}
1120
1122{
1123 return fSubFields.empty() ? RConstSchemaIterator(this, -1) : RConstSchemaIterator(fSubFields[0].get(), 0);
1124}
1125
1127{
1128 return RConstSchemaIterator(this, -1);
1129}
1130
1133{
1134 if (fColumnRepresentatives.empty()) {
1135 return {GetColumnRepresentations().GetSerializationDefault()};
1136 }
1137
1139 result.reserve(fColumnRepresentatives.size());
1140 for (const auto &r : fColumnRepresentatives) {
1141 result.emplace_back(r.get());
1142 }
1143 return result;
1144}
1145
1147 const RColumnRepresentations::Selection_t &representatives)
1148{
1149 if (fState != EState::kUnconnected)
1150 throw RException(R__FAIL("cannot set column representative once field is connected"));
1151 const auto &validTypes = GetColumnRepresentations().GetSerializationTypes();
1152 fColumnRepresentatives.clear();
1153 fColumnRepresentatives.reserve(representatives.size());
1154 for (const auto &r : representatives) {
1155 auto itRepresentative = std::find(validTypes.begin(), validTypes.end(), r);
1156 if (itRepresentative == std::end(validTypes))
1157 throw RException(R__FAIL("invalid column representative"));
1158 fColumnRepresentatives.emplace_back(*itRepresentative);
1159 }
1160}
1161
1164 std::uint16_t representationIndex) const
1165{
1166 static const ColumnRepresentation_t kEmpty;
1167
1168 if (fOnDiskId == kInvalidDescriptorId)
1169 throw RException(R__FAIL("No on-disk field information for `" + GetQualifiedFieldName() + "`"));
1170
1171 ColumnRepresentation_t onDiskTypes;
1172 for (const auto &c : desc.GetColumnIterable(fOnDiskId)) {
1173 if (c.GetRepresentationIndex() == representationIndex)
1174 onDiskTypes.emplace_back(c.GetType());
1175 }
1176 if (onDiskTypes.empty()) {
1177 if (representationIndex == 0) {
1178 throw RException(R__FAIL("No on-disk column information for field `" + GetQualifiedFieldName() + "`"));
1179 }
1180 return kEmpty;
1181 }
1182
1183 for (const auto &t : GetColumnRepresentations().GetDeserializationTypes()) {
1184 if (t == onDiskTypes)
1185 return t;
1186 }
1187
1188 std::string columnTypeNames;
1189 for (const auto &t : onDiskTypes) {
1190 if (!columnTypeNames.empty())
1191 columnTypeNames += ", ";
1192 columnTypeNames += std::string("`") + Internal::RColumnElementBase::GetTypeName(t) + "`";
1193 }
1194 throw RException(R__FAIL("On-disk column types {" + columnTypeNames + "} for field `" + GetQualifiedFieldName() +
1195 "` cannot be matched to its in-memory type `" + GetTypeName() + "` " +
1196 "(representation index: " + std::to_string(representationIndex) + ")"));
1197}
1198
1200{
1201 fReadCallbacks.push_back(func);
1202 fIsSimple = false;
1203 return fReadCallbacks.size() - 1;
1204}
1205
1207{
1208 fReadCallbacks.erase(fReadCallbacks.begin() + idx);
1209 fIsSimple = (fTraits & kTraitMappable) && fReadCallbacks.empty();
1210}
1211
1213{
1214 if ((options.GetCompression() == 0) && HasDefaultColumnRepresentative()) {
1215 ColumnRepresentation_t rep = GetColumnRepresentations().GetSerializationDefault();
1216 for (auto &colType : rep) {
1217 switch (colType) {
1220 case EColumnType::kSplitReal64: colType = EColumnType::kReal64; break;
1221 case EColumnType::kSplitReal32: colType = EColumnType::kReal32; break;
1222 case EColumnType::kSplitInt64: colType = EColumnType::kInt64; break;
1223 case EColumnType::kSplitInt32: colType = EColumnType::kInt32; break;
1224 case EColumnType::kSplitInt16: colType = EColumnType::kInt16; break;
1225 default: break;
1226 }
1227 }
1228 SetColumnRepresentatives({rep});
1229 }
1230
1231 if (fTypeAlias == "Double32_t")
1232 SetColumnRepresentatives({{EColumnType::kSplitReal32}});
1233}
1234
1236{
1237 if (dynamic_cast<ROOT::Experimental::RFieldZero *>(this))
1238 throw RException(R__FAIL("invalid attempt to connect zero field to page sink"));
1239 if (fState != EState::kUnconnected)
1240 throw RException(R__FAIL("invalid attempt to connect an already connected field to a page sink"));
1241
1242 AutoAdjustColumnTypes(pageSink.GetWriteOptions());
1243
1244 GenerateColumns();
1245 for (auto &column : fAvailableColumns) {
1246 // Only the first column of every representation can be a deferred column. In all column representations,
1247 // larger column indexes are data columns of collections (string, unsplit) and thus
1248 // they have no elements on late model extension
1249 auto firstElementIndex = (column->GetIndex() == 0) ? EntryToColumnElementIndex(firstEntry) : 0;
1250 column->ConnectPageSink(fOnDiskId, pageSink, firstElementIndex);
1251 }
1252
1253 if (HasExtraTypeInfo()) {
1255 [this](Internal::RPageSink &sink) { sink.UpdateExtraTypeInfo(GetExtraTypeInfo()); });
1256 }
1257
1258 fState = EState::kConnectedToSink;
1259}
1260
1262{
1263 if (dynamic_cast<ROOT::Experimental::RFieldZero *>(this))
1264 throw RException(R__FAIL("invalid attempt to connect zero field to page source"));
1265 if (fState != EState::kUnconnected)
1266 throw RException(R__FAIL("invalid attempt to connect an already connected field to a page source"));
1267
1268 if (!fColumnRepresentatives.empty())
1269 throw RException(R__FAIL("fixed column representative only valid when connecting to a page sink"));
1270 if (!fDescription.empty())
1271 throw RException(R__FAIL("setting description only valid when connecting to a page sink"));
1272
1273 for (auto &f : fSubFields) {
1274 if (f->GetOnDiskId() == kInvalidDescriptorId) {
1275 f->SetOnDiskId(pageSource.GetSharedDescriptorGuard()->FindFieldId(f->GetFieldName(), GetOnDiskId()));
1276 }
1277 f->ConnectPageSource(pageSource);
1278 }
1279
1280 {
1281 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
1282 const RNTupleDescriptor &desc = descriptorGuard.GetRef();
1283 GenerateColumns(desc);
1284 if (fColumnRepresentatives.empty()) {
1285 // If we didn't get columns from the descriptor, ensure that we actually expect a field without columns
1286 for (const auto &t : GetColumnRepresentations().GetDeserializationTypes()) {
1287 if (t.empty()) {
1288 fColumnRepresentatives = {t};
1289 break;
1290 }
1291 }
1292 }
1293 R__ASSERT(!fColumnRepresentatives.empty());
1294 if (fOnDiskId != kInvalidDescriptorId) {
1295 const auto &fieldDesc = desc.GetFieldDescriptor(fOnDiskId);
1296 fOnDiskTypeVersion = fieldDesc.GetTypeVersion();
1297 if (fieldDesc.GetTypeChecksum().has_value())
1298 fOnDiskTypeChecksum = *fieldDesc.GetTypeChecksum();
1299 }
1300 }
1301 for (auto &column : fAvailableColumns)
1302 column->ConnectPageSource(fOnDiskId, pageSource);
1303 OnConnectPageSource();
1304
1305 fState = EState::kConnectedToSource;
1306}
1307
1309{
1310 visitor.VisitField(*this);
1311}
1312
1313//-----------------------------------------------------------------------------
1314
1315std::unique_ptr<ROOT::Experimental::RFieldBase>
1316ROOT::Experimental::RFieldZero::CloneImpl(std::string_view /*newName*/) const
1317{
1318 auto result = std::make_unique<RFieldZero>();
1319 for (auto &f : fSubFields)
1320 result->Attach(f->Clone(f->GetFieldName()));
1321 return result;
1322}
1323
1325{
1326 visitor.VisitFieldZero(*this);
1327}
1328
1329//------------------------------------------------------------------------------
1330
1333{
1334 static RColumnRepresentations representations(
1336 {});
1337 return representations;
1338}
1339
1341{
1342 visitor.VisitClusterSizeField(*this);
1343}
1344
1345//------------------------------------------------------------------------------
1346
1349{
1350 static RColumnRepresentations representations(
1352 {});
1353 return representations;
1354}
1355
1357{
1358 GenerateColumnsImpl<ClusterSize_t>(desc);
1359}
1360
1362{
1363 visitor.VisitCardinalityField(*this);
1364}
1365
1368{
1369 return dynamic_cast<const RField<RNTupleCardinality<std::uint32_t>> *>(this);
1370}
1371
1374{
1375 return dynamic_cast<const RField<RNTupleCardinality<std::uint64_t>> *>(this);
1376}
1377
1378//------------------------------------------------------------------------------
1379
1381
1384{
1385 static RColumnRepresentations representations({{EColumnType::kChar}}, {});
1386 return representations;
1387}
1388
1390{
1391 visitor.VisitCharField(*this);
1392}
1393
1394//------------------------------------------------------------------------------
1395
1397
1400{
1401 static RColumnRepresentations representations({{EColumnType::kByte}}, {});
1402 return representations;
1403}
1404
1405void ROOT::Experimental::RField<std::byte>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1406{
1407 visitor.VisitByteField(*this);
1408}
1409
1410//------------------------------------------------------------------------------
1411
1413
1416{
1417 static RColumnRepresentations representations({{EColumnType::kInt8}}, {{EColumnType::kUInt8}});
1418 return representations;
1419}
1420
1421void ROOT::Experimental::RIntegralField<std::int8_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1422{
1423 visitor.VisitInt8Field(*this);
1424}
1425
1426//------------------------------------------------------------------------------
1427
1429
1432{
1433 static RColumnRepresentations representations({{EColumnType::kUInt8}}, {{EColumnType::kInt8}});
1434 return representations;
1435}
1436
1437void ROOT::Experimental::RIntegralField<std::uint8_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1438{
1439 visitor.VisitUInt8Field(*this);
1440}
1441
1442//------------------------------------------------------------------------------
1443
1445
1448{
1449 static RColumnRepresentations representations({{EColumnType::kBit}}, {{EColumnType::kChar},
1464 return representations;
1465}
1466
1468{
1469 visitor.VisitBoolField(*this);
1470}
1471
1472//------------------------------------------------------------------------------
1473
1475
1478{
1479 static RColumnRepresentations representations({{EColumnType::kSplitReal32},
1484 {});
1485 return representations;
1486}
1487
1489{
1490 const auto r = GetColumnRepresentatives();
1491 const auto n = r.size();
1492 fAvailableColumns.reserve(n);
1493 for (std::uint16_t i = 0; i < n; ++i) {
1494 auto &column = fAvailableColumns.emplace_back(Internal::RColumn::Create<float>(r[i][0], 0, i));
1495 if (r[i][0] == EColumnType::kReal32Trunc) {
1496 column->SetBitsOnStorage(fBitWidth);
1497 } else if (r[i][0] == EColumnType::kReal32Quant) {
1498 column->SetBitsOnStorage(fBitWidth);
1499 column->SetValueRange(fValueMin, fValueMax);
1500 }
1501 }
1502 fPrincipalColumn = fAvailableColumns[0].get();
1503}
1504
1506{
1507 std::uint16_t representationIndex = 0;
1508 do {
1509 const auto &onDiskTypes = EnsureCompatibleColumnTypes(desc, representationIndex);
1510 if (onDiskTypes.empty())
1511 break;
1512
1513 auto &column =
1514 fAvailableColumns.emplace_back(Internal::RColumn::Create<float>(onDiskTypes[0], 0, representationIndex));
1515 if (onDiskTypes[0] == EColumnType::kReal32Trunc) {
1516 const auto &fdesc = desc.GetFieldDescriptor(GetOnDiskId());
1517 const auto &coldesc = desc.GetColumnDescriptor(fdesc.GetLogicalColumnIds()[0]);
1518 column->SetBitsOnStorage(coldesc.GetBitsOnStorage());
1519 } else if (onDiskTypes[0] == EColumnType::kReal32Quant) {
1520 const auto &fdesc = desc.GetFieldDescriptor(GetOnDiskId());
1521 const auto &coldesc = desc.GetColumnDescriptor(fdesc.GetLogicalColumnIds()[0]);
1522 assert(coldesc.GetValueRange().has_value());
1523 const auto [valMin, valMax] = *coldesc.GetValueRange();
1524 column->SetBitsOnStorage(coldesc.GetBitsOnStorage());
1525 column->SetValueRange(valMin, valMax);
1526 }
1527 fColumnRepresentatives.emplace_back(onDiskTypes);
1528 if (representationIndex > 0) {
1529 fAvailableColumns[0]->MergeTeams(*fAvailableColumns[representationIndex]);
1530 }
1531
1532 representationIndex++;
1533 } while (true);
1534 fPrincipalColumn = fAvailableColumns[0].get();
1535}
1536
1538{
1539 visitor.VisitFloatField(*this);
1540}
1541
1543{
1544 SetColumnRepresentatives({{EColumnType::kReal16}});
1545}
1546
1548{
1550 if (nBits < minBits || nBits > maxBits) {
1551 throw RException(R__FAIL("SetTruncated() argument nBits = " + std::to_string(nBits) + " is out of valid range [" +
1552 std::to_string(minBits) + ", " + std::to_string(maxBits) + "])"));
1553 }
1554 SetColumnRepresentatives({{EColumnType::kReal32Trunc}});
1555 fBitWidth = nBits;
1556}
1557
1558void ROOT::Experimental::RField<float>::SetQuantized(float minValue, float maxValue, std::size_t nBits)
1559{
1561 if (nBits < minBits || nBits > maxBits) {
1562 throw RException(R__FAIL("SetQuantized() argument nBits = " + std::to_string(nBits) + " is out of valid range [" +
1563 std::to_string(minBits) + ", " + std::to_string(maxBits) + "])"));
1564 }
1565 SetColumnRepresentatives({{EColumnType::kReal32Quant}});
1566 fBitWidth = nBits;
1567 fValueMin = minValue;
1568 fValueMax = maxValue;
1569}
1570
1571//------------------------------------------------------------------------------
1572
1574
1577{
1578 static RColumnRepresentations representations({{EColumnType::kSplitReal64},
1583 {});
1584 return representations;
1585}
1586
1588{
1589 visitor.VisitDoubleField(*this);
1590}
1591
1593{
1594 fTypeAlias = "Double32_t";
1595}
1596
1597//------------------------------------------------------------------------------
1598
1600
1603{
1604 static RColumnRepresentations representations({{EColumnType::kSplitInt16}, {EColumnType::kInt16}},
1606 return representations;
1607}
1608
1609void ROOT::Experimental::RIntegralField<std::int16_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1610{
1611 visitor.VisitInt16Field(*this);
1612}
1613
1614//------------------------------------------------------------------------------
1615
1617
1620{
1621 static RColumnRepresentations representations({{EColumnType::kSplitUInt16}, {EColumnType::kUInt16}},
1623 return representations;
1624}
1625
1626void ROOT::Experimental::RIntegralField<std::uint16_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1627{
1628 visitor.VisitUInt16Field(*this);
1629}
1630
1631//------------------------------------------------------------------------------
1632
1634
1637{
1638 static RColumnRepresentations representations({{EColumnType::kSplitInt32}, {EColumnType::kInt32}},
1640 return representations;
1641}
1642
1643void ROOT::Experimental::RIntegralField<std::int32_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1644{
1645 visitor.VisitInt32Field(*this);
1646}
1647
1648//------------------------------------------------------------------------------
1649
1651
1654{
1655 static RColumnRepresentations representations({{EColumnType::kSplitUInt32}, {EColumnType::kUInt32}},
1657 return representations;
1658}
1659
1660void ROOT::Experimental::RIntegralField<std::uint32_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1661{
1662 visitor.VisitUInt32Field(*this);
1663}
1664
1665//------------------------------------------------------------------------------
1666
1668
1671{
1672 static RColumnRepresentations representations({{EColumnType::kSplitUInt64}, {EColumnType::kUInt64}},
1674 return representations;
1675}
1676
1677void ROOT::Experimental::RIntegralField<std::uint64_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1678{
1679 visitor.VisitUInt64Field(*this);
1680}
1681
1682//------------------------------------------------------------------------------
1683
1685
1688{
1689 static RColumnRepresentations representations({{EColumnType::kSplitInt64}, {EColumnType::kInt64}},
1696 return representations;
1697}
1698
1699void ROOT::Experimental::RIntegralField<std::int64_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1700{
1701 visitor.VisitInt64Field(*this);
1702}
1703
1704//------------------------------------------------------------------------------
1705
1708{
1709 static RColumnRepresentations representations({{EColumnType::kSplitIndex64, EColumnType::kChar},
1713 {});
1714 return representations;
1715}
1716
1718{
1719 GenerateColumnsImpl<ClusterSize_t, char>();
1720}
1721
1722void ROOT::Experimental::RField<std::string>::GenerateColumns(const RNTupleDescriptor &desc)
1723{
1724 GenerateColumnsImpl<ClusterSize_t, char>(desc);
1725}
1726
1727std::size_t ROOT::Experimental::RField<std::string>::AppendImpl(const void *from)
1728{
1729 auto typedValue = static_cast<const std::string *>(from);
1730 auto length = typedValue->length();
1731 fAuxiliaryColumn->AppendV(typedValue->data(), length);
1732 fIndex += length;
1733 fPrincipalColumn->Append(&fIndex);
1734 return length + fPrincipalColumn->GetElement()->GetPackedSize();
1735}
1736
1738{
1739 auto typedValue = static_cast<std::string *>(to);
1740 RClusterIndex collectionStart;
1741 ClusterSize_t nChars;
1742 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nChars);
1743 if (nChars == 0) {
1744 typedValue->clear();
1745 } else {
1746 typedValue->resize(nChars);
1747 fAuxiliaryColumn->ReadV(collectionStart, nChars, const_cast<char *>(typedValue->data()));
1748 }
1749}
1750
1751void ROOT::Experimental::RField<std::string>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1752{
1753 visitor.VisitStringField(*this);
1754}
1755
1756//------------------------------------------------------------------------------
1757
1758ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::string_view className)
1759 : RClassField(fieldName, className, TClass::GetClass(std::string(className).c_str()))
1760{
1761}
1762
1763ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::string_view className, TClass *classp)
1764 : ROOT::Experimental::RFieldBase(fieldName, className, ENTupleStructure::kRecord, false /* isSimple */),
1765 fClass(classp)
1766{
1767 if (fClass == nullptr) {
1768 throw RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
1769 }
1770 // Avoid accidentally supporting std types through TClass.
1771 if (fClass->Property() & kIsDefinedInStd) {
1772 throw RException(R__FAIL(std::string(className) + " is not supported"));
1773 }
1774 if (className == "TObject") {
1775 throw RException(R__FAIL("TObject is only supported through RField<TObject>"));
1776 }
1777 if (fClass->GetCollectionProxy()) {
1778 throw RException(
1779 R__FAIL(std::string(className) + " has an associated collection proxy; use RProxiedCollectionField instead"));
1780 }
1781 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
1782 // Can be overwritten with the "rntuple.split=true" class attribute
1783 if (!fClass->CanSplit() && fClass->Size() > 1 &&
1784 GetRNTupleUnsplitSetting(fClass) != ERNTupleUnsplitSetting::kForceSplit) {
1785 throw RException(R__FAIL(std::string(className) + " cannot be split"));
1786 }
1787 if (GetRNTupleUnsplitSetting(fClass) == ERNTupleUnsplitSetting::kForceUnsplit) {
1788 throw RException(
1789 R__FAIL(std::string(className) + " has unsplit mode enforced, not supported as native RNTuple class"));
1790 }
1791
1796
1797 int i = 0;
1799 if (baseClass->GetDelta() < 0) {
1800 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + std::string(className) +
1801 " virtually inherits from " + baseClass->GetName()));
1802 }
1803 TClass *c = baseClass->GetClassPointer();
1804 auto subField =
1805 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
1806 fTraits &= subField->GetTraits();
1807 Attach(std::move(subField), RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
1808 i++;
1809 }
1811 // Skip, for instance, unscoped enum constants defined in the class
1812 if (dataMember->Property() & kIsStatic)
1813 continue;
1814 // Skip members explicitly marked as transient by user comment
1815 if (!dataMember->IsPersistent()) {
1816 // TODO(jblomer): we could do better
1818 continue;
1819 }
1820
1821 std::string typeName{GetNormalizedTypeName(dataMember->GetTrueTypeName())};
1822 std::string typeAlias{GetNormalizedTypeName(dataMember->GetFullTypeName())};
1823
1824 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
1825 if (dataMember->Property() & kIsArray) {
1826 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim)
1827 typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
1828 }
1829
1830 std::unique_ptr<RFieldBase> subField;
1831
1832 subField = RFieldBase::Create(dataMember->GetName(), typeName, typeAlias).Unwrap();
1833 fTraits &= subField->GetTraits();
1834 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
1835 }
1837}
1838
1839void ROOT::Experimental::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
1840{
1841 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
1842 fSubFieldsInfo.push_back(info);
1843 RFieldBase::Attach(std::move(child));
1844}
1845
1846void ROOT::Experimental::RClassField::AddReadCallbacksFromIORules(const std::span<const ROOT::TSchemaRule *> rules,
1847 TClass *classp)
1848{
1849 for (const auto rule : rules) {
1850 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
1851 R__LOG_WARNING(NTupleLog()) << "ignoring I/O customization rule with unsupported type";
1852 continue;
1853 }
1854 auto func = rule->GetReadFunctionPointer();
1855 R__ASSERT(func != nullptr);
1856 fReadCallbacks.emplace_back([func, classp](void *target) {
1857 TVirtualObject oldObj{nullptr};
1858 oldObj.fClass = classp;
1859 oldObj.fObject = target;
1860 func(static_cast<char *>(target), &oldObj);
1861 oldObj.fClass = nullptr; // TVirtualObject does not own the value
1862 });
1863 }
1864}
1865
1866std::unique_ptr<ROOT::Experimental::RFieldBase>
1867ROOT::Experimental::RClassField::CloneImpl(std::string_view newName) const
1868{
1869 auto result = std::unique_ptr<RClassField>(new RClassField(newName, GetTypeName(), fClass));
1870 SyncFieldIDs(*this, *result);
1871 return result;
1872}
1873
1875{
1876 std::size_t nbytes = 0;
1877 for (unsigned i = 0; i < fSubFields.size(); i++) {
1878 nbytes += CallAppendOn(*fSubFields[i], static_cast<const unsigned char *>(from) + fSubFieldsInfo[i].fOffset);
1879 }
1880 return nbytes;
1881}
1882
1884{
1885 for (unsigned i = 0; i < fSubFields.size(); i++) {
1886 CallReadOn(*fSubFields[i], globalIndex, static_cast<unsigned char *>(to) + fSubFieldsInfo[i].fOffset);
1887 }
1888}
1889
1891{
1892 for (unsigned i = 0; i < fSubFields.size(); i++) {
1893 CallReadOn(*fSubFields[i], clusterIndex, static_cast<unsigned char *>(to) + fSubFieldsInfo[i].fOffset);
1894 }
1895}
1896
1898{
1899 // Add post-read callbacks for I/O customization rules; only rules that target transient members are allowed for now
1900 // TODO(jalopezg): revise after supporting schema evolution
1901 const auto ruleset = fClass->GetSchemaRules();
1902 if (!ruleset)
1903 return;
1904 auto referencesNonTransientMembers = [klass = fClass](const ROOT::TSchemaRule *rule) {
1905 if (rule->GetTarget() == nullptr)
1906 return false;
1907 for (auto target : ROOT::Detail::TRangeStaticCast<TObjString>(*rule->GetTarget())) {
1908 const auto dataMember = klass->GetDataMember(target->GetString());
1909 if (!dataMember || dataMember->IsPersistent()) {
1910 R__LOG_WARNING(NTupleLog()) << "ignoring I/O customization rule with non-transient member: "
1911 << dataMember->GetName();
1912 return true;
1913 }
1914 }
1915 return false;
1916 };
1917
1918 auto rules = ruleset->FindRules(fClass->GetName(), static_cast<Int_t>(GetOnDiskTypeVersion()),
1919 static_cast<UInt_t>(GetOnDiskTypeChecksum()));
1920 rules.erase(std::remove_if(rules.begin(), rules.end(), referencesNonTransientMembers), rules.end());
1921 AddReadCallbacksFromIORules(rules, fClass);
1922}
1923
1925{
1926 fClass->New(where);
1927}
1928
1930{
1931 fClass->Destructor(objPtr, true /* dtorOnly */);
1932 RDeleter::operator()(objPtr, dtorOnly);
1933}
1934
1935std::vector<ROOT::Experimental::RFieldBase::RValue>
1937{
1938 std::vector<RValue> result;
1939 auto basePtr = value.GetPtr<unsigned char>().get();
1940 result.reserve(fSubFields.size());
1941 for (unsigned i = 0; i < fSubFields.size(); i++) {
1942 result.emplace_back(
1943 fSubFields[i]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + fSubFieldsInfo[i].fOffset)));
1944 }
1945 return result;
1946}
1947
1949{
1950 return fClass->GetClassSize();
1951}
1952
1954{
1955 return fClass->GetClassVersion();
1956}
1957
1959{
1960 return fClass->GetCheckSum();
1961}
1962
1964{
1965 visitor.VisitClassField(*this);
1966}
1967
1968//------------------------------------------------------------------------------
1969
1971{
1972 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1973 return dataMember->GetOffset();
1974 }
1975 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
1976}
1977
1979 : ROOT::Experimental::RFieldBase(fieldName, "TObject", ENTupleStructure::kRecord, false /* isSimple */)
1980{
1981 assert(TObject::Class()->GetClassVersion() == 1);
1982
1984 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
1985 Attach(std::make_unique<RField<UInt_t>>("fBits"));
1986}
1987
1988std::unique_ptr<ROOT::Experimental::RFieldBase>
1990{
1991 auto result = std::make_unique<RField<TObject>>(newName);
1992 SyncFieldIDs(*this, *result);
1993 return result;
1994}
1995
1997{
1998 // Cf. TObject::Streamer()
1999
2000 auto *obj = static_cast<const TObject *>(from);
2001 if (obj->TestBit(TObject::kIsReferenced)) {
2002 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
2003 }
2004
2005 std::size_t nbytes = 0;
2006 nbytes += CallAppendOn(*fSubFields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
2007
2008 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
2009 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
2010 nbytes += CallAppendOn(*fSubFields[1], &bits);
2011
2012 return nbytes;
2013}
2014
2016{
2017 // Cf. TObject::Streamer()
2018
2019 auto *obj = static_cast<TObject *>(to);
2020 if (obj->TestBit(TObject::kIsReferenced)) {
2021 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
2022 }
2023
2024 CallReadOn(*fSubFields[0], globalIndex, static_cast<unsigned char *>(to) + GetOffsetUniqueID());
2025
2026 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
2027 UInt_t bits;
2028 CallReadOn(*fSubFields[1], globalIndex, &bits);
2029 bits |= bitIsOnHeap | TObject::kNotDeleted;
2030 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
2031}
2032
2034{
2035 if (GetTypeVersion() != 1) {
2036 throw RException(R__FAIL("unsupported on-disk version of TObject: " + std::to_string(GetTypeVersion())));
2037 }
2038}
2039
2041{
2042 return TObject::Class()->GetClassVersion();
2043}
2044
2046{
2047 return TObject::Class()->GetCheckSum();
2048}
2049
2051{
2052 new (where) TObject();
2053}
2054
2055std::vector<ROOT::Experimental::RFieldBase::RValue>
2057{
2058 std::vector<RValue> result;
2059 auto basePtr = value.GetPtr<unsigned char>().get();
2060 result.emplace_back(
2061 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + GetOffsetUniqueID())));
2062 result.emplace_back(
2063 fSubFields[1]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + GetOffsetBits())));
2064 return result;
2065}
2066
2068{
2069 return sizeof(TObject);
2070}
2071
2073{
2074 return alignof(TObject);
2075}
2076
2078{
2079 visitor.VisitTObjectField(*this);
2080}
2081
2082//------------------------------------------------------------------------------
2083
2084ROOT::Experimental::RUnsplitField::RUnsplitField(std::string_view fieldName, std::string_view className,
2085 std::string_view typeAlias)
2086 : RUnsplitField(fieldName, className, TClass::GetClass(std::string(className).c_str()))
2087{
2088 fTypeAlias = typeAlias;
2089}
2090
2091ROOT::Experimental::RUnsplitField::RUnsplitField(std::string_view fieldName, std::string_view className, TClass *classp)
2092 : ROOT::Experimental::RFieldBase(fieldName, className, ENTupleStructure::kUnsplit, false /* isSimple */),
2093 fClass(classp),
2094 fIndex(0)
2095{
2096 if (fClass == nullptr) {
2097 throw RException(R__FAIL("RUnsplitField: no I/O support for type " + std::string(className)));
2098 }
2099
2105}
2106
2107std::unique_ptr<ROOT::Experimental::RFieldBase>
2109{
2110 return std::unique_ptr<RUnsplitField>(new RUnsplitField(newName, GetTypeName(), GetTypeAlias()));
2111}
2112
2114{
2115 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
2116 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
2117 fClass->Streamer(const_cast<void *>(from), buffer);
2118
2119 auto nbytes = buffer.Length();
2120 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
2121 fIndex += nbytes;
2122 fPrincipalColumn->Append(&fIndex);
2123 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2124}
2125
2127{
2128 RClusterIndex collectionStart;
2129 ClusterSize_t nbytes;
2130 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
2131
2132 TBufferFile buffer(TBuffer::kRead, nbytes);
2133 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
2134 fClass->Streamer(to, buffer);
2135}
2136
2139{
2144 {});
2145 return representations;
2146}
2147
2149{
2150 GenerateColumnsImpl<ClusterSize_t, std::byte>();
2151}
2152
2154{
2155 GenerateColumnsImpl<ClusterSize_t, std::byte>(desc);
2156}
2157
2159{
2160 fClass->New(where);
2161}
2162
2164{
2165 fClass->Destructor(objPtr, true /* dtorOnly */);
2166 RDeleter::operator()(objPtr, dtorOnly);
2167}
2168
2170{
2171 Internal::RExtraTypeInfoDescriptorBuilder extraTypeInfoBuilder;
2172 extraTypeInfoBuilder.ContentId(EExtraTypeInfoIds::kStreamerInfo)
2173 .TypeVersionFrom(GetTypeVersion())
2174 .TypeVersionTo(GetTypeVersion())
2175 .TypeName(GetTypeName())
2177 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
2178}
2179
2181{
2182 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
2183}
2184
2186{
2187 return fClass->GetClassSize();
2188}
2189
2191{
2192 return fClass->GetClassVersion();
2193}
2194
2196{
2197 return fClass->GetCheckSum();
2198}
2199
2201{
2202 visitor.VisitUnsplitField(*this);
2203}
2204
2205//------------------------------------------------------------------------------
2206
2207ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
2208 : REnumField(fieldName, enumName, TEnum::GetEnum(std::string(enumName).c_str()))
2209{
2210}
2211
2212ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName, TEnum *enump)
2213 : ROOT::Experimental::RFieldBase(fieldName, enumName, ENTupleStructure::kLeaf, false /* isSimple */)
2214{
2215 if (enump == nullptr) {
2216 throw RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
2217 }
2218 // Avoid accidentally supporting std types through TEnum.
2219 if (enump->Property() & kIsDefinedInStd) {
2220 throw RException(R__FAIL(std::string(enumName) + " is not supported"));
2221 }
2222
2223 switch (enump->GetUnderlyingType()) {
2224 case kChar_t: Attach(std::make_unique<RField<int8_t>>("_0")); break;
2225 case kUChar_t: Attach(std::make_unique<RField<uint8_t>>("_0")); break;
2226 case kShort_t: Attach(std::make_unique<RField<int16_t>>("_0")); break;
2227 case kUShort_t: Attach(std::make_unique<RField<uint16_t>>("_0")); break;
2228 case kInt_t: Attach(std::make_unique<RField<int32_t>>("_0")); break;
2229 case kUInt_t: Attach(std::make_unique<RField<uint32_t>>("_0")); break;
2230 case kLong_t:
2231 case kLong64_t: Attach(std::make_unique<RField<int64_t>>("_0")); break;
2232 case kULong_t:
2233 case kULong64_t: Attach(std::make_unique<RField<uint64_t>>("_0")); break;
2234 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + std::string(enumName)));
2235 }
2236
2238}
2239
2240ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
2241 std::unique_ptr<RFieldBase> intField)
2242 : ROOT::Experimental::RFieldBase(fieldName, enumName, ENTupleStructure::kLeaf, false /* isSimple */)
2243{
2244 Attach(std::move(intField));
2246}
2247
2248std::unique_ptr<ROOT::Experimental::RFieldBase>
2249ROOT::Experimental::REnumField::CloneImpl(std::string_view newName) const
2250{
2251 auto newIntField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2252 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
2253}
2254
2255std::vector<ROOT::Experimental::RFieldBase::RValue>
2257{
2258 std::vector<RValue> result;
2259 result.emplace_back(fSubFields[0]->BindValue(value.GetPtr<void>()));
2260 return result;
2261}
2262
2264{
2265 visitor.VisitEnumField(*this);
2266}
2267
2268//------------------------------------------------------------------------------
2269
2272 bool readFromDisk)
2273{
2274 RIteratorFuncs ifuncs;
2275 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
2276 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
2277 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
2278 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
2279 (ifuncs.fNext != nullptr));
2280 return ifuncs;
2281}
2282
2284 std::string_view typeName, TClass *classp)
2285 : RFieldBase(fieldName, typeName, ENTupleStructure::kCollection, false /* isSimple */), fNWritten(0)
2286{
2287 if (classp == nullptr)
2288 throw RException(R__FAIL("RField: no I/O support for collection proxy type " + std::string(typeName)));
2289 if (!classp->GetCollectionProxy())
2290 throw RException(R__FAIL(std::string(typeName) + " has no associated collection proxy"));
2291
2292 fProxy.reset(classp->GetCollectionProxy()->Generate());
2293 fProperties = fProxy->GetProperties();
2294 fCollectionType = fProxy->GetCollectionType();
2295 if (fProxy->HasPointers())
2296 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
2297 if (!fProxy->GetCollectionClass()->HasDictionary()) {
2298 throw RException(R__FAIL("dictionary not available for type " +
2299 GetNormalizedTypeName(fProxy->GetCollectionClass()->GetName())));
2300 }
2301
2302 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
2303 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
2304}
2305
2307 std::string_view typeName,
2308 std::unique_ptr<RFieldBase> itemField)
2309 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
2310{
2311 fItemSize = itemField->GetValueSize();
2312 Attach(std::move(itemField));
2313}
2314
2316 std::string_view typeName)
2317 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
2318{
2319 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
2321 throw RException(R__FAIL("custom associative collection proxies not supported"));
2322
2323 std::unique_ptr<ROOT::Experimental::RFieldBase> itemField;
2324
2325 if (auto valueClass = fProxy->GetValueClass()) {
2326 // Element type is a class
2327 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
2328 } else {
2329 switch (fProxy->GetType()) {
2330 case EDataType::kChar_t: itemField = std::make_unique<RField<char>>("_0"); break;
2331 case EDataType::kUChar_t: itemField = std::make_unique<RField<std::uint8_t>>("_0"); break;
2332 case EDataType::kShort_t: itemField = std::make_unique<RField<std::int16_t>>("_0"); break;
2333 case EDataType::kUShort_t: itemField = std::make_unique<RField<std::uint16_t>>("_0"); break;
2334 case EDataType::kInt_t: itemField = std::make_unique<RField<std::int32_t>>("_0"); break;
2335 case EDataType::kUInt_t: itemField = std::make_unique<RField<std::uint32_t>>("_0"); break;
2336 case EDataType::kLong_t:
2337 case EDataType::kLong64_t: itemField = std::make_unique<RField<std::int64_t>>("_0"); break;
2339 case EDataType::kULong64_t: itemField = std::make_unique<RField<std::uint64_t>>("_0"); break;
2340 case EDataType::kFloat_t: itemField = std::make_unique<RField<float>>("_0"); break;
2341 case EDataType::kDouble_t: itemField = std::make_unique<RField<double>>("_0"); break;
2342 case EDataType::kBool_t: itemField = std::make_unique<RField<bool>>("_0"); break;
2343 default: throw RException(R__FAIL("unsupported value type"));
2344 }
2345 }
2346
2347 fItemSize = itemField->GetValueSize();
2348 Attach(std::move(itemField));
2349}
2350
2351std::unique_ptr<ROOT::Experimental::RFieldBase>
2353{
2354 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2355 return std::unique_ptr<RProxiedCollectionField>(
2356 new RProxiedCollectionField(newName, GetTypeName(), std::move(newItemField)));
2357}
2358
2360{
2361 std::size_t nbytes = 0;
2362 unsigned count = 0;
2363 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
2364 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
2365 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
2366 nbytes += CallAppendOn(*fSubFields[0], ptr);
2367 count++;
2368 }
2369
2370 fNWritten += count;
2371 fPrincipalColumn->Append(&fNWritten);
2372 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2373}
2374
2376{
2377 ClusterSize_t nItems;
2378 RClusterIndex collectionStart;
2379 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2380
2381 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
2382 void *obj =
2383 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
2384
2385 unsigned i = 0;
2386 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
2387 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
2388 CallReadOn(*fSubFields[0], collectionStart + (i++), elementPtr);
2389 }
2390 if (obj != to)
2391 fProxy->Commit(obj);
2392}
2393
2396{
2397 static RColumnRepresentations representations(
2399 {});
2400 return representations;
2401}
2402
2404{
2405 GenerateColumnsImpl<ClusterSize_t>();
2406}
2407
2409{
2410 GenerateColumnsImpl<ClusterSize_t>(desc);
2411}
2412
2414{
2415 fProxy->New(where);
2416}
2417
2418std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter>
2420{
2421 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
2422 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
2423 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubFields[0]), itemSize);
2424 }
2425 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
2426}
2427
2429{
2430 if (fItemDeleter) {
2431 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), objPtr);
2432 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
2433 fItemDeleter->operator()(ptr, true /* dtorOnly */);
2434 }
2435 }
2436 fProxy->Destructor(objPtr, true /* dtorOnly */);
2437 RDeleter::operator()(objPtr, dtorOnly);
2438}
2439
2440std::vector<ROOT::Experimental::RFieldBase::RValue>
2442{
2443 std::vector<RValue> result;
2444 auto valueRawPtr = value.GetPtr<void>().get();
2445 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), valueRawPtr);
2446 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
2447 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
2448 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
2449 }
2450 return result;
2451}
2452
2454{
2455 visitor.VisitProxiedCollectionField(*this);
2456}
2457
2458//------------------------------------------------------------------------------
2459
2461 std::vector<std::unique_ptr<RFieldBase>> &&itemFields,
2462 const std::vector<std::size_t> &offsets, std::string_view typeName)
2463 : ROOT::Experimental::RFieldBase(fieldName, typeName, ENTupleStructure::kRecord, false /* isSimple */),
2464 fOffsets(offsets)
2465{
2467 for (auto &item : itemFields) {
2468 fMaxAlignment = std::max(fMaxAlignment, item->GetAlignment());
2469 fSize += GetItemPadding(fSize, item->GetAlignment()) + item->GetValueSize();
2470 fTraits &= item->GetTraits();
2471 Attach(std::move(item));
2472 }
2473}
2474
2476 std::vector<std::unique_ptr<RFieldBase>> &&itemFields)
2477 : ROOT::Experimental::RFieldBase(fieldName, "", ENTupleStructure::kRecord, false /* isSimple */)
2478{
2480 fOffsets.reserve(itemFields.size());
2481 for (auto &item : itemFields) {
2482 fSize += GetItemPadding(fSize, item->GetAlignment());
2483 fOffsets.push_back(fSize);
2484 fMaxAlignment = std::max(fMaxAlignment, item->GetAlignment());
2485 fSize += item->GetValueSize();
2486 fTraits &= item->GetTraits();
2487 Attach(std::move(item));
2488 }
2489 // Trailing padding: although this is implementation-dependent, most add enough padding to comply with the
2490 // requirements of the type with strictest alignment
2492}
2493
2495 std::vector<std::unique_ptr<RFieldBase>> &itemFields)
2496 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields))
2497{
2498}
2499
2500std::size_t ROOT::Experimental::RRecordField::GetItemPadding(std::size_t baseOffset, std::size_t itemAlignment) const
2501{
2502 if (itemAlignment > 1) {
2503 auto remainder = baseOffset % itemAlignment;
2504 if (remainder != 0)
2505 return itemAlignment - remainder;
2506 }
2507 return 0;
2508}
2509
2510std::unique_ptr<ROOT::Experimental::RFieldBase>
2511ROOT::Experimental::RRecordField::CloneImpl(std::string_view newName) const
2512{
2513 std::vector<std::unique_ptr<RFieldBase>> cloneItems;
2514 cloneItems.reserve(fSubFields.size());
2515 for (auto &item : fSubFields)
2516 cloneItems.emplace_back(item->Clone(item->GetFieldName()));
2517 return std::unique_ptr<RRecordField>(new RRecordField(newName, std::move(cloneItems), fOffsets, GetTypeName()));
2518}
2519
2521{
2522 std::size_t nbytes = 0;
2523 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2524 nbytes += CallAppendOn(*fSubFields[i], static_cast<const unsigned char *>(from) + fOffsets[i]);
2525 }
2526 return nbytes;
2527}
2528
2530{
2531 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2532 CallReadOn(*fSubFields[i], globalIndex, static_cast<unsigned char *>(to) + fOffsets[i]);
2533 }
2534}
2535
2537{
2538 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2539 CallReadOn(*fSubFields[i], clusterIndex, static_cast<unsigned char *>(to) + fOffsets[i]);
2540 }
2541}
2542
2544{
2545 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2546 CallConstructValueOn(*fSubFields[i], static_cast<unsigned char *>(where) + fOffsets[i]);
2547 }
2548}
2549
2551{
2552 for (unsigned i = 0; i < fItemDeleters.size(); ++i) {
2553 fItemDeleters[i]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fOffsets[i], true /* dtorOnly */);
2554 }
2555 RDeleter::operator()(objPtr, dtorOnly);
2556}
2557
2558std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RRecordField::GetDeleter() const
2559{
2560 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
2561 itemDeleters.reserve(fOffsets.size());
2562 for (const auto &f : fSubFields) {
2563 itemDeleters.emplace_back(GetDeleterOf(*f));
2564 }
2565 return std::make_unique<RRecordDeleter>(itemDeleters, fOffsets);
2566}
2567
2568std::vector<ROOT::Experimental::RFieldBase::RValue>
2570{
2571 auto basePtr = value.GetPtr<unsigned char>().get();
2572 std::vector<RValue> result;
2573 result.reserve(fSubFields.size());
2574 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2575 result.emplace_back(fSubFields[i]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + fOffsets[i])));
2576 }
2577 return result;
2578}
2579
2581{
2582 visitor.VisitRecordField(*this);
2583}
2584
2585//------------------------------------------------------------------------------
2586
2587ROOT::Experimental::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
2588 : ROOT::Experimental::RFieldBase(fieldName, "std::vector<" + itemField->GetTypeName() + ">",
2589 ENTupleStructure::kCollection, false /* isSimple */),
2590 fItemSize(itemField->GetValueSize()),
2591 fNWritten(0)
2592{
2593 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
2594 fItemDeleter = GetDeleterOf(*itemField);
2595 Attach(std::move(itemField));
2596}
2597
2598std::unique_ptr<ROOT::Experimental::RFieldBase>
2599ROOT::Experimental::RVectorField::CloneImpl(std::string_view newName) const
2600{
2601 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2602 return std::make_unique<RVectorField>(newName, std::move(newItemField));
2603}
2604
2606{
2607 auto typedValue = static_cast<const std::vector<char> *>(from);
2608 R__ASSERT((typedValue->size() % fItemSize) == 0);
2609 std::size_t nbytes = 0;
2610 auto count = typedValue->size() / fItemSize;
2611
2612 if (fSubFields[0]->IsSimple() && count) {
2613 GetPrincipalColumnOf(*fSubFields[0])->AppendV(typedValue->data(), count);
2614 nbytes += count * GetPrincipalColumnOf(*fSubFields[0])->GetElement()->GetPackedSize();
2615 } else {
2616 for (unsigned i = 0; i < count; ++i) {
2617 nbytes += CallAppendOn(*fSubFields[0], typedValue->data() + (i * fItemSize));
2618 }
2619 }
2620
2621 fNWritten += count;
2622 fPrincipalColumn->Append(&fNWritten);
2623 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2624}
2625
2627{
2628 auto typedValue = static_cast<std::vector<char> *>(to);
2629
2630 ClusterSize_t nItems;
2631 RClusterIndex collectionStart;
2632 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2633
2634 if (fSubFields[0]->IsSimple()) {
2635 typedValue->resize(nItems * fItemSize);
2636 if (nItems)
2637 GetPrincipalColumnOf(*fSubFields[0])->ReadV(collectionStart, nItems, typedValue->data());
2638 return;
2639 }
2640
2641 // See "semantics of reading non-trivial objects" in RNTuple's architecture.md
2642 const auto oldNItems = typedValue->size() / fItemSize;
2643 const bool canRealloc = oldNItems < nItems;
2644 bool allDeallocated = false;
2645 if (fItemDeleter) {
2646 allDeallocated = canRealloc;
2647 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
2648 fItemDeleter->operator()(typedValue->data() + (i * fItemSize), true /* dtorOnly */);
2649 }
2650 }
2651 typedValue->resize(nItems * fItemSize);
2652 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible)) {
2653 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
2654 CallConstructValueOn(*fSubFields[0], typedValue->data() + (i * fItemSize));
2655 }
2656 }
2657
2658 for (std::size_t i = 0; i < nItems; ++i) {
2659 CallReadOn(*fSubFields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
2660 }
2661}
2662
2665{
2666 static RColumnRepresentations representations(
2668 {});
2669 return representations;
2670}
2671
2673{
2674 GenerateColumnsImpl<ClusterSize_t>();
2675}
2676
2678{
2679 GenerateColumnsImpl<ClusterSize_t>(desc);
2680}
2681
2683{
2684 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
2685 if (fItemDeleter) {
2686 R__ASSERT((vecPtr->size() % fItemSize) == 0);
2687 auto nItems = vecPtr->size() / fItemSize;
2688 for (std::size_t i = 0; i < nItems; ++i) {
2689 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
2690 }
2691 }
2692 std::destroy_at(vecPtr);
2693 RDeleter::operator()(objPtr, dtorOnly);
2694}
2695
2696std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RVectorField::GetDeleter() const
2697{
2698 if (fItemDeleter)
2699 return std::make_unique<RVectorDeleter>(fItemSize, GetDeleterOf(*fSubFields[0]));
2700 return std::make_unique<RVectorDeleter>();
2701}
2702
2703std::vector<ROOT::Experimental::RFieldBase::RValue>
2705{
2706 auto vec = value.GetPtr<std::vector<char>>();
2707 R__ASSERT((vec->size() % fItemSize) == 0);
2708 auto nItems = vec->size() / fItemSize;
2709 std::vector<RValue> result;
2710 result.reserve(nItems);
2711 for (unsigned i = 0; i < nItems; ++i) {
2712 result.emplace_back(
2713 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), vec->data() + (i * fItemSize))));
2714 }
2715 return result;
2716}
2717
2719{
2720 visitor.VisitVectorField(*this);
2721}
2722
2723//------------------------------------------------------------------------------
2724
2725ROOT::Experimental::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
2726 : ROOT::Experimental::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
2727 ENTupleStructure::kCollection, false /* isSimple */),
2728 fItemSize(itemField->GetValueSize()),
2729 fNWritten(0)
2730{
2731 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
2732 fItemDeleter = GetDeleterOf(*itemField);
2733 Attach(std::move(itemField));
2734 fValueSize = EvalRVecValueSize(fSubFields[0]->GetAlignment(), fSubFields[0]->GetValueSize(), GetAlignment());
2735}
2736
2737std::unique_ptr<ROOT::Experimental::RFieldBase>
2738ROOT::Experimental::RRVecField::CloneImpl(std::string_view newName) const
2739{
2740 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2741 return std::make_unique<RRVecField>(newName, std::move(newItemField));
2742}
2743
2745{
2746 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(from);
2747
2748 std::size_t nbytes = 0;
2749 if (fSubFields[0]->IsSimple() && *sizePtr) {
2750 GetPrincipalColumnOf(*fSubFields[0])->AppendV(*beginPtr, *sizePtr);
2751 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubFields[0])->GetElement()->GetPackedSize();
2752 } else {
2753 auto begin = reinterpret_cast<const char *>(*beginPtr); // for pointer arithmetics
2754 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2755 nbytes += CallAppendOn(*fSubFields[0], begin + i * fItemSize);
2756 }
2757 }
2758
2759 fNWritten += *sizePtr;
2760 fPrincipalColumn->Append(&fNWritten);
2761 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2762}
2763
2765{
2766 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
2767 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
2768
2769 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(to);
2770
2771 // Read collection info for this entry
2772 ClusterSize_t nItems;
2773 RClusterIndex collectionStart;
2774 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2775 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2776 const std::size_t oldSize = *sizePtr;
2777
2778 // See "semantics of reading non-trivial objects" in RNTuple's architecture.md for details
2779 // on the element construction/destrution.
2780 const bool owns = (*capacityPtr != -1);
2781 const bool needsConstruct = !(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible);
2782 const bool needsDestruct = owns && fItemDeleter;
2783
2784 // Destroy excess elements, if any
2785 if (needsDestruct) {
2786 for (std::size_t i = nItems; i < oldSize; ++i) {
2787 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
2788 }
2789 }
2790
2791 // Resize RVec (capacity and size)
2792 if (std::int32_t(nItems) > *capacityPtr) { // must reallocate
2793 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
2794 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
2795 if (needsDestruct) {
2796 for (std::size_t i = 0u; i < oldSize; ++i) {
2797 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
2798 }
2799 }
2800
2801 // TODO Increment capacity by a factor rather than just enough to fit the elements.
2802 if (owns) {
2803 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
2804 free(*beginPtr);
2805 }
2806 // We trust that malloc returns a buffer with large enough alignment.
2807 // This might not be the case if T in RVec<T> is over-aligned.
2808 *beginPtr = malloc(nItems * fItemSize);
2809 R__ASSERT(*beginPtr != nullptr);
2810 begin = reinterpret_cast<char *>(*beginPtr);
2811 *capacityPtr = nItems;
2812
2813 // Placement new for elements that were already there before the resize
2814 if (needsConstruct) {
2815 for (std::size_t i = 0u; i < oldSize; ++i)
2816 CallConstructValueOn(*fSubFields[0], begin + (i * fItemSize));
2817 }
2818 }
2819 *sizePtr = nItems;
2820
2821 // Placement new for new elements, if any
2822 if (needsConstruct) {
2823 for (std::size_t i = oldSize; i < nItems; ++i)
2824 CallConstructValueOn(*fSubFields[0], begin + (i * fItemSize));
2825 }
2826
2827 if (fSubFields[0]->IsSimple() && nItems) {
2828 GetPrincipalColumnOf(*fSubFields[0])->ReadV(collectionStart, nItems, begin);
2829 return;
2830 }
2831
2832 // Read the new values into the collection elements
2833 for (std::size_t i = 0; i < nItems; ++i) {
2834 CallReadOn(*fSubFields[0], collectionStart + i, begin + (i * fItemSize));
2835 }
2836}
2837
2839{
2840 if (!fSubFields[0]->IsSimple())
2841 return RFieldBase::ReadBulkImpl(bulkSpec);
2842
2843 if (bulkSpec.fAuxData->empty()) {
2844 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
2845 /// The following bytes store the item values, consecutively.
2846 bulkSpec.fAuxData->resize(sizeof(std::size_t));
2847 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fSubFields[0]->GetValueSize();
2848 }
2849 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
2850 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
2851 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(bulkSpec.fValues);
2852
2853 // Get size of the first RVec of the bulk
2854 RClusterIndex firstItemIndex;
2855 RClusterIndex collectionStart;
2856 ClusterSize_t collectionSize;
2857 this->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
2858 *beginPtr = itemValueArray;
2859 *sizePtr = collectionSize;
2860 *capacityPtr = -1;
2861
2862 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
2863 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
2864 // given range. If not, we'll fix up the pointers afterwards.
2865 auto lastOffset = firstItemIndex.GetIndex() + collectionSize;
2866 ClusterSize_t::ValueType nRemainingValues = bulkSpec.fCount - 1;
2867 std::size_t nValues = 1;
2868 std::size_t nItems = collectionSize;
2869 while (nRemainingValues > 0) {
2870 NTupleSize_t nElementsUntilPageEnd;
2871 const auto offsets = fPrincipalColumn->MapV<ClusterSize_t>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
2872 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
2873 for (std::size_t i = 0; i < nBatch; ++i) {
2874 const auto size = offsets[i] - lastOffset;
2875 std::tie(beginPtr, sizePtr, capacityPtr) =
2876 GetRVecDataMembers(reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
2877 *beginPtr = itemValueArray + nItems * itemValueSize;
2878 *sizePtr = size;
2879 *capacityPtr = -1;
2880
2881 nItems += size;
2882 lastOffset = offsets[i];
2883 }
2884 nRemainingValues -= nBatch;
2885 nValues += nBatch;
2886 }
2887
2888 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
2889 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
2890 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
2891 if (delta != 0) {
2892 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
2893 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
2894 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
2895 beginPtrAsUChar += fValueSize;
2896 }
2897 }
2898
2899 GetPrincipalColumnOf(*fSubFields[0])->ReadV(firstItemIndex, nItems, itemValueArray - delta);
2900 return RBulkSpec::kAllSet;
2901}
2902
2905{
2906 static RColumnRepresentations representations(
2908 {});
2909 return representations;
2910}
2911
2913{
2914 GenerateColumnsImpl<ClusterSize_t>();
2915}
2916
2918{
2919 GenerateColumnsImpl<ClusterSize_t>(desc);
2920}
2921
2923{
2924 // initialize data members fBegin, fSize, fCapacity
2925 // currently the inline buffer is left uninitialized
2926 void **beginPtr = new (where)(void *)(nullptr);
2927 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
2928 new (sizePtr + 1) std::int32_t(-1);
2929}
2930
2932{
2933 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(objPtr);
2934
2935 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2936 if (fItemDeleter) {
2937 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2938 fItemDeleter->operator()(begin + i * fItemSize, true /* dtorOnly */);
2939 }
2940 }
2941
2942 DestroyRVecWithChecks(fItemAlignment, beginPtr, begin, capacityPtr);
2943 RDeleter::operator()(objPtr, dtorOnly);
2944}
2945
2946std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RRVecField::GetDeleter() const
2947{
2948 if (fItemDeleter)
2949 return std::make_unique<RRVecDeleter>(fSubFields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubFields[0]));
2950 return std::make_unique<RRVecDeleter>(fSubFields[0]->GetAlignment());
2951}
2952
2953std::vector<ROOT::Experimental::RFieldBase::RValue>
2955{
2956 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetPtr<void>().get());
2957
2958 std::vector<RValue> result;
2959 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2960 result.reserve(*sizePtr);
2961 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2962 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), begin + i * fItemSize)));
2963 }
2964 return result;
2965}
2966
2968{
2969 return fValueSize;
2970}
2971
2973{
2974 return EvalRVecAlignment(fSubFields[0]->GetAlignment());
2975}
2976
2978{
2979 visitor.VisitRVecField(*this);
2980}
2981
2982//------------------------------------------------------------------------------
2983
2985 : ROOT::Experimental::RFieldBase(name, "std::vector<bool>", ENTupleStructure::kCollection, false /* isSimple */)
2986{
2987 Attach(std::make_unique<RField<bool>>("_0"));
2988}
2989
2990std::size_t ROOT::Experimental::RField<std::vector<bool>>::AppendImpl(const void *from)
2991{
2992 auto typedValue = static_cast<const std::vector<bool> *>(from);
2993 auto count = typedValue->size();
2994 for (unsigned i = 0; i < count; ++i) {
2995 bool bval = (*typedValue)[i];
2996 CallAppendOn(*fSubFields[0], &bval);
2997 }
2998 fNWritten += count;
2999 fPrincipalColumn->Append(&fNWritten);
3000 return count + fPrincipalColumn->GetElement()->GetPackedSize();
3001}
3002
3003void ROOT::Experimental::RField<std::vector<bool>>::ReadGlobalImpl(NTupleSize_t globalIndex, void *to)
3004{
3005 auto typedValue = static_cast<std::vector<bool> *>(to);
3006
3007 ClusterSize_t nItems;
3008 RClusterIndex collectionStart;
3009 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
3010
3011 typedValue->resize(nItems);
3012 for (unsigned i = 0; i < nItems; ++i) {
3013 bool bval;
3014 CallReadOn(*fSubFields[0], collectionStart + i, &bval);
3015 (*typedValue)[i] = bval;
3016 }
3017}
3018
3020ROOT::Experimental::RField<std::vector<bool>>::GetColumnRepresentations() const
3021{
3022 static RColumnRepresentations representations(
3024 {});
3025 return representations;
3026}
3027
3028void ROOT::Experimental::RField<std::vector<bool>>::GenerateColumns()
3029{
3030 GenerateColumnsImpl<ClusterSize_t>();
3031}
3032
3033void ROOT::Experimental::RField<std::vector<bool>>::GenerateColumns(const RNTupleDescriptor &desc)
3034{
3035 GenerateColumnsImpl<ClusterSize_t>(desc);
3036}
3037
3038std::vector<ROOT::Experimental::RFieldBase::RValue>
3039ROOT::Experimental::RField<std::vector<bool>>::SplitValue(const RValue &value) const
3040{
3041 const auto &typedValue = value.GetRef<std::vector<bool>>();
3042 auto count = typedValue.size();
3043 std::vector<RValue> result;
3044 result.reserve(count);
3045 for (unsigned i = 0; i < count; ++i) {
3046 if (typedValue[i])
3047 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
3048 else
3049 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
3050 }
3051 return result;
3052}
3053
3054void ROOT::Experimental::RField<std::vector<bool>>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
3055{
3056 visitor.VisitVectorBoolField(*this);
3057}
3058
3059//------------------------------------------------------------------------------
3060
3061ROOT::Experimental::RArrayField::RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
3062 std::size_t arrayLength)
3063 : ROOT::Experimental::RFieldBase(fieldName,
3064 "std::array<" + itemField->GetTypeName() + "," + std::to_string(arrayLength) + ">",
3065 ENTupleStructure::kLeaf, false /* isSimple */, arrayLength),
3066 fItemSize(itemField->GetValueSize()),
3067 fArrayLength(arrayLength)
3068{
3069 fTraits |= itemField->GetTraits() & ~kTraitMappable;
3070 Attach(std::move(itemField));
3071}
3072
3073std::unique_ptr<ROOT::Experimental::RFieldBase>
3074ROOT::Experimental::RArrayField::CloneImpl(std::string_view newName) const
3075{
3076 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3077 return std::make_unique<RArrayField>(newName, std::move(newItemField), fArrayLength);
3078}
3079
3081{
3082 std::size_t nbytes = 0;
3083 auto arrayPtr = static_cast<const unsigned char *>(from);
3084 for (unsigned i = 0; i < fArrayLength; ++i) {
3085 nbytes += CallAppendOn(*fSubFields[0], arrayPtr + (i * fItemSize));
3086 }
3087 return nbytes;
3088}
3089
3091{
3092 auto arrayPtr = static_cast<unsigned char *>(to);
3093 for (unsigned i = 0; i < fArrayLength; ++i) {
3094 CallReadOn(*fSubFields[0], globalIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
3095 }
3096}
3097
3099{
3100 auto arrayPtr = static_cast<unsigned char *>(to);
3101 for (unsigned i = 0; i < fArrayLength; ++i) {
3102 CallReadOn(*fSubFields[0], RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() * fArrayLength + i),
3103 arrayPtr + (i * fItemSize));
3104 }
3105}
3106
3108{
3109 if (fSubFields[0]->GetTraits() & kTraitTriviallyConstructible)
3110 return;
3111
3112 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
3113 for (unsigned i = 0; i < fArrayLength; ++i) {
3114 CallConstructValueOn(*fSubFields[0], arrayPtr + (i * fItemSize));
3115 }
3116}
3117
3119{
3120 if (fItemDeleter) {
3121 for (unsigned i = 0; i < fArrayLength; ++i) {
3122 fItemDeleter->operator()(reinterpret_cast<unsigned char *>(objPtr) + i * fItemSize, true /* dtorOnly */);
3123 }
3124 }
3125 RDeleter::operator()(objPtr, dtorOnly);
3126}
3127
3128std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RArrayField::GetDeleter() const
3129{
3130 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyDestructible))
3131 return std::make_unique<RArrayDeleter>(fItemSize, fArrayLength, GetDeleterOf(*fSubFields[0]));
3132 return std::make_unique<RDeleter>();
3133}
3134
3135std::vector<ROOT::Experimental::RFieldBase::RValue>
3137{
3138 auto arrayPtr = value.GetPtr<unsigned char>().get();
3139 std::vector<RValue> result;
3140 result.reserve(fArrayLength);
3141 for (unsigned i = 0; i < fArrayLength; ++i) {
3142 result.emplace_back(
3143 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
3144 }
3145 return result;
3146}
3147
3149{
3150 visitor.VisitArrayField(*this);
3151}
3152
3153//------------------------------------------------------------------------------
3154// RArrayAsRVecField
3155
3157 std::unique_ptr<ROOT::Experimental::RFieldBase> itemField,
3158 std::size_t arrayLength)
3159 : ROOT::Experimental::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
3160 ENTupleStructure::kCollection, false /* isSimple */),
3161 fItemSize(itemField->GetValueSize()),
3162 fArrayLength(arrayLength)
3163{
3164 Attach(std::move(itemField));
3165 fValueSize = EvalRVecValueSize(fSubFields[0]->GetAlignment(), fSubFields[0]->GetValueSize(), GetAlignment());
3168}
3169
3170std::unique_ptr<ROOT::Experimental::RFieldBase>
3172{
3173 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3174 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
3175}
3176
3178{
3179 // initialize data members fBegin, fSize, fCapacity
3180 void **beginPtr = new (where)(void *)(nullptr);
3181 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
3182 std::int32_t *capacityPtr = new (sizePtr + 1) std::int32_t(0);
3183
3184 // Create the RVec with the known fixed size, do it once here instead of
3185 // every time the value is read in `Read*Impl` functions
3186 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
3187
3188 // Early return if the RVec has already been allocated.
3189 if (*sizePtr == std::int32_t(fArrayLength))
3190 return;
3191
3192 // Need to allocate the RVec if it is the first time the value is being created.
3193 // See "semantics of reading non-trivial objects" in RNTuple's architecture.md for details
3194 // on the element construction.
3195 const bool owns = (*capacityPtr != -1); // RVec is adopting the memory
3196 const bool needsConstruct = !(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible);
3197 const bool needsDestruct = owns && fItemDeleter;
3198
3199 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
3200 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
3201 if (needsDestruct) {
3202 for (std::int32_t i = 0; i < *sizePtr; ++i) {
3203 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
3204 }
3205 }
3206
3207 // TODO: Isn't the RVec always owning in this case?
3208 if (owns) {
3209 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
3210 free(*beginPtr);
3211 }
3212
3213 *beginPtr = malloc(fArrayLength * fItemSize);
3214 R__ASSERT(*beginPtr != nullptr);
3215 // Re-assign begin pointer after allocation
3216 begin = reinterpret_cast<char *>(*beginPtr);
3217 // Size and capacity are equal since the field data type is std::array
3218 *sizePtr = fArrayLength;
3219 *capacityPtr = fArrayLength;
3220
3221 // Placement new for the array elements
3222 if (needsConstruct) {
3223 for (std::size_t i = 0; i < fArrayLength; ++i)
3224 CallConstructValueOn(*fSubFields[0], begin + (i * fItemSize));
3225 }
3226}
3227
3228std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RArrayAsRVecField::GetDeleter() const
3229{
3230 if (fItemDeleter) {
3231 return std::make_unique<RRVecField::RRVecDeleter>(fSubFields[0]->GetAlignment(), fItemSize,
3232 GetDeleterOf(*fSubFields[0]));
3233 }
3234 return std::make_unique<RRVecField::RRVecDeleter>(fSubFields[0]->GetAlignment());
3235}
3236
3238{
3239
3240 auto [beginPtr, _, __] = GetRVecDataMembers(to);
3241 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
3242
3243 if (fSubFields[0]->IsSimple()) {
3244 GetPrincipalColumnOf(*fSubFields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, rvecBeginPtr);
3245 return;
3246 }
3247
3248 // Read the new values into the collection elements
3249 for (std::size_t i = 0; i < fArrayLength; ++i) {
3250 CallReadOn(*fSubFields[0], globalIndex * fArrayLength + i, rvecBeginPtr + (i * fItemSize));
3251 }
3252}
3253
3255{
3256 auto [beginPtr, _, __] = GetRVecDataMembers(to);
3257 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
3258
3259 const auto &clusterId = clusterIndex.GetClusterId();
3260 const auto &clusterIndexIndex = clusterIndex.GetIndex();
3261
3262 if (fSubFields[0]->IsSimple()) {
3263 GetPrincipalColumnOf(*fSubFields[0])
3264 ->ReadV(RClusterIndex(clusterId, clusterIndexIndex * fArrayLength), fArrayLength, rvecBeginPtr);
3265 return;
3266 }
3267
3268 // Read the new values into the collection elements
3269 for (std::size_t i = 0; i < fArrayLength; ++i) {
3270 CallReadOn(*fSubFields[0], RClusterIndex(clusterId, clusterIndexIndex * fArrayLength + i),
3271 rvecBeginPtr + (i * fItemSize));
3272 }
3273}
3274
3276{
3277 return EvalRVecAlignment(fSubFields[0]->GetAlignment());
3278}
3279
3280std::vector<ROOT::Experimental::RFieldBase::RValue>
3282{
3283 auto arrayPtr = value.GetPtr<unsigned char>().get();
3284 std::vector<ROOT::Experimental::RFieldBase::RValue> result;
3285 result.reserve(fArrayLength);
3286 for (unsigned i = 0; i < fArrayLength; ++i) {
3287 result.emplace_back(
3288 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
3289 }
3290 return result;
3291}
3292
3294{
3295 visitor.VisitArrayAsRVecField(*this);
3296}
3297
3298// RArrayAsRVecField
3299//------------------------------------------------------------------------------
3300
3301//------------------------------------------------------------------------------
3302
3303ROOT::Experimental::RBitsetField::RBitsetField(std::string_view fieldName, std::size_t N)
3304 : ROOT::Experimental::RFieldBase(fieldName, "std::bitset<" + std::to_string(N) + ">", ENTupleStructure::kLeaf,
3305 false /* isSimple */, N),
3306 fN(N)
3307{
3309}
3310
3313{
3314 static RColumnRepresentations representations({{EColumnType::kBit}}, {});
3315 return representations;
3316}
3317
3319{
3320 GenerateColumnsImpl<bool>();
3321}
3322
3324{
3325 GenerateColumnsImpl<bool>(desc);
3326}
3327
3329{
3330 const auto *asULongArray = static_cast<const Word_t *>(from);
3331 bool elementValue;
3332 std::size_t i = 0;
3333 for (std::size_t word = 0; word < (fN + kBitsPerWord - 1) / kBitsPerWord; ++word) {
3334 for (std::size_t mask = 0; (mask < kBitsPerWord) && (i < fN); ++mask, ++i) {
3335 elementValue = (asULongArray[word] & (static_cast<Word_t>(1) << mask)) != 0;
3336 fPrincipalColumn->Append(&elementValue);
3337 }
3338 }
3339 return fN;
3340}
3341
3343{
3344 auto *asULongArray = static_cast<Word_t *>(to);
3345 bool elementValue;
3346 for (std::size_t i = 0; i < fN; ++i) {
3347 fPrincipalColumn->Read(globalIndex * fN + i, &elementValue);
3348 Word_t mask = static_cast<Word_t>(1) << (i % kBitsPerWord);
3349 Word_t bit = static_cast<Word_t>(elementValue) << (i % kBitsPerWord);
3350 asULongArray[i / kBitsPerWord] = (asULongArray[i / kBitsPerWord] & ~mask) | bit;
3351 }
3352}
3353
3355{
3356 visitor.VisitBitsetField(*this);
3357}
3358
3359//------------------------------------------------------------------------------
3360
3361std::string ROOT::Experimental::RVariantField::GetTypeList(const std::vector<RFieldBase *> &itemFields)
3362{
3363 std::string result;
3364 for (size_t i = 0; i < itemFields.size(); ++i) {
3365 result += itemFields[i]->GetTypeName() + ",";
3366 }
3367 R__ASSERT(!result.empty()); // there is always at least one variant
3368 result.pop_back(); // remove trailing comma
3369 return result;
3370}
3371
3373 const std::vector<RFieldBase *> &itemFields)
3374 : ROOT::Experimental::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields) + ">",
3375 ENTupleStructure::kVariant, false /* isSimple */)
3376{
3377 // The variant needs to initialize its own tag member
3378 fTraits |= kTraitTriviallyDestructible & ~kTraitTriviallyConstructible;
3379
3380 auto nFields = itemFields.size();
3381 if (nFields == 0 || nFields > kMaxVariants) {
3382 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
3383 }
3384 fNWritten.resize(nFields, 0);
3385 for (unsigned int i = 0; i < nFields; ++i) {
3386 fMaxItemSize = std::max(fMaxItemSize, itemFields[i]->GetValueSize());
3387 fMaxAlignment = std::max(fMaxAlignment, itemFields[i]->GetAlignment());
3388 fTraits &= itemFields[i]->GetTraits();
3389 Attach(std::unique_ptr<RFieldBase>(itemFields[i]));
3390 }
3391
3392 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
3393 // For instance, std::variant<std::optional<int>> on macOS.
3394 auto cl = TClass::GetClass(GetTypeName().c_str());
3395 assert(cl);
3396 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
3397 if (dm)
3398 fVariantOffset = dm->GetOffset();
3399
3400 const auto tagSize = GetVariantTagSize();
3401 const auto padding = tagSize - (fMaxItemSize % tagSize);
3402 fTagOffset = fVariantOffset + fMaxItemSize + ((padding == tagSize) ? 0 : padding);
3403}
3404
3405std::unique_ptr<ROOT::Experimental::RFieldBase>
3407{
3408 auto nFields = fSubFields.size();
3409 std::vector<RFieldBase *> itemFields;
3410 itemFields.reserve(nFields);
3411 for (unsigned i = 0; i < nFields; ++i) {
3412 // TODO(jblomer): use unique_ptr in RVariantField constructor
3413 itemFields.emplace_back(fSubFields[i]->Clone(fSubFields[i]->GetFieldName()).release());
3414 }
3415 return std::make_unique<RVariantField>(newName, itemFields);
3416}
3417
3418std::uint8_t ROOT::Experimental::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
3419{
3420 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
3421 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
3422 return (tag == TagType_t(-1)) ? 0 : tag + 1;
3423}
3424
3425void ROOT::Experimental::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
3426{
3427 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
3428 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
3429 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
3430}
3431
3433{
3434 auto tag = GetTag(from, fTagOffset);
3435 std::size_t nbytes = 0;
3436 auto index = 0;
3437 if (tag > 0) {
3438 nbytes += CallAppendOn(*fSubFields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
3439 index = fNWritten[tag - 1]++;
3440 }
3441 RColumnSwitch varSwitch(ClusterSize_t(index), tag);
3442 fPrincipalColumn->Append(&varSwitch);
3443 return nbytes + sizeof(RColumnSwitch);
3444}
3445
3447{
3448 RClusterIndex variantIndex;
3449 std::uint32_t tag;
3450 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
3451 R__ASSERT(tag < 256);
3452
3453 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
3454 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
3455 // any `std::holds_alternative<T>` check fail later.
3456 if (R__likely(tag > 0)) {
3457 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
3458 CallConstructValueOn(*fSubFields[tag - 1], varPtr);
3459 CallReadOn(*fSubFields[tag - 1], variantIndex, varPtr);
3460 }
3461 SetTag(to, fTagOffset, tag);
3462}
3463
3466{
3467 static RColumnRepresentations representations({{EColumnType::kSwitch}}, {});
3468 return representations;
3469}
3470
3472{
3473 GenerateColumnsImpl<RColumnSwitch>();
3474}
3475
3477{
3478 GenerateColumnsImpl<RColumnSwitch>(desc);
3479}
3480
3482{
3483 memset(where, 0, GetValueSize());
3484 CallConstructValueOn(*fSubFields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
3485 SetTag(where, fTagOffset, 1);
3486}
3487
3489{
3490 auto tag = GetTag(objPtr, fTagOffset);
3491 if (tag > 0) {
3492 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
3493 }
3494 RDeleter::operator()(objPtr, dtorOnly);
3495}
3496
3497std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RVariantField::GetDeleter() const
3498{
3499 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
3500 itemDeleters.reserve(fSubFields.size());
3501 for (const auto &f : fSubFields) {
3502 itemDeleters.emplace_back(GetDeleterOf(*f));
3503 }
3504 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, itemDeleters);
3505}
3506
3508{
3509 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
3510}
3511
3513{
3514 const auto alignment = GetAlignment();
3515 const auto actualSize = fTagOffset + GetVariantTagSize();
3516 const auto padding = alignment - (actualSize % alignment);
3517 return actualSize + ((padding == alignment) ? 0 : padding);
3518}
3519
3521{
3522 std::fill(fNWritten.begin(), fNWritten.end(), 0);
3523}
3524
3525//------------------------------------------------------------------------------
3526
3527ROOT::Experimental::RSetField::RSetField(std::string_view fieldName, std::string_view typeName,
3528 std::unique_ptr<RFieldBase> itemField)
3529 : ROOT::Experimental::RProxiedCollectionField(fieldName, typeName, std::move(itemField))
3530{
3531}
3532
3533std::unique_ptr<ROOT::Experimental::RFieldBase> ROOT::Experimental::RSetField::CloneImpl(std::string_view newName) const
3534{
3535 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3536 return std::make_unique<RSetField>(newName, GetTypeName(), std::move(newItemField));
3537}
3538
3539//------------------------------------------------------------------------------
3540
3541ROOT::Experimental::RMapField::RMapField(std::string_view fieldName, std::string_view typeName,
3542 std::unique_ptr<RFieldBase> itemField)
3543 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
3544{
3545 if (!dynamic_cast<RPairField *>(itemField.get()))
3546 throw RException(R__FAIL("RMapField inner field type must be of RPairField"));
3547
3548 fItemClass = fProxy->GetValueClass();
3550
3551 Attach(std::move(itemField));
3552}
3553
3555{
3556 std::size_t nbytes = 0;
3557 unsigned count = 0;
3558 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
3559 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(), 0U}) {
3560 nbytes += CallAppendOn(*fSubFields[0], ptr);
3561 count++;
3562 }
3563 fNWritten += count;
3564 fPrincipalColumn->Append(&fNWritten);
3565 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
3566}
3567
3569{
3570 ClusterSize_t nItems;
3571 RClusterIndex collectionStart;
3572 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
3573
3574 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
3575 void *obj =
3576 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
3577
3578 unsigned i = 0;
3579 for (auto ptr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(), fItemSize}) {
3580 CallReadOn(*fSubFields[0], collectionStart + i, ptr);
3581 i++;
3582 }
3583
3584 if (obj != to)
3585 fProxy->Commit(obj);
3586}
3587
3588std::vector<ROOT::Experimental::RFieldBase::RValue> ROOT::Experimental::RMapField::SplitValue(const RValue &value) const
3589{
3590 std::vector<RValue> result;
3591 auto valueRawPtr = value.GetPtr<void>().get();
3592 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), valueRawPtr);
3593 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(), 0U}) {
3594 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
3595 }
3596 return result;
3597}
3598
3599std::unique_ptr<ROOT::Experimental::RFieldBase> ROOT::Experimental::RMapField::CloneImpl(std::string_view newName) const
3600{
3601 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3602 return std::unique_ptr<RMapField>(new RMapField(newName, GetTypeName(), std::move(newItemField)));
3603}
3604
3605//------------------------------------------------------------------------------
3606
3607ROOT::Experimental::RNullableField::RNullableField(std::string_view fieldName, std::string_view typeName,
3608 std::unique_ptr<RFieldBase> itemField)
3609 : ROOT::Experimental::RFieldBase(fieldName, typeName, ENTupleStructure::kCollection, false /* isSimple */)
3610{
3611 Attach(std::move(itemField));
3612}
3613
3616{
3617 static RColumnRepresentations representations(
3619 {});
3620 return representations;
3621}
3622
3624{
3625 GenerateColumnsImpl<ClusterSize_t>();
3626}
3627
3629{
3630 GenerateColumnsImpl<ClusterSize_t>(desc);
3631}
3632
3634{
3635 fPrincipalColumn->Append(&fNWritten);
3636 return sizeof(ClusterSize_t);
3637}
3638
3640{
3641 auto nbytesItem = CallAppendOn(*fSubFields[0], from);
3642 fNWritten++;
3643 fPrincipalColumn->Append(&fNWritten);
3644 return sizeof(ClusterSize_t) + nbytesItem;
3645}
3646
3648{
3649 RClusterIndex collectionStart;
3650 ClusterSize_t collectionSize;
3651 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &collectionSize);
3652 return (collectionSize == 0) ? RClusterIndex() : collectionStart;
3653}
3654
3656{
3657 visitor.VisitNullableField(*this);
3658}
3659
3660//------------------------------------------------------------------------------
3661
3662ROOT::Experimental::RUniquePtrField::RUniquePtrField(std::string_view fieldName, std::string_view typeName,
3663 std::unique_ptr<RFieldBase> itemField)
3664 : RNullableField(fieldName, typeName, std::move(itemField)), fItemDeleter(GetDeleterOf(*fSubFields[0]))
3665{
3666}
3667
3668std::unique_ptr<ROOT::Experimental::RFieldBase>
3670{
3671 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3672 return std::make_unique<RUniquePtrField>(newName, GetTypeName(), std::move(newItemField));
3673}
3674
3676{
3677 auto typedValue = static_cast<const std::unique_ptr<char> *>(from);
3678 if (*typedValue) {
3679 return AppendValue(typedValue->get());
3680 } else {
3681 return AppendNull();
3682 }
3683}
3684
3686{
3687 auto ptr = static_cast<std::unique_ptr<char> *>(to);
3688 bool isValidValue = static_cast<bool>(*ptr);
3689
3690 auto itemIndex = GetItemIndex(globalIndex);
3691 bool isValidItem = itemIndex.GetIndex() != kInvalidClusterIndex;
3692
3693 void *valuePtr = nullptr;
3694 if (isValidValue)
3695 valuePtr = ptr->get();
3696
3697 if (isValidValue && !isValidItem) {
3698 ptr->release();
3699 fItemDeleter->operator()(valuePtr, false /* dtorOnly */);
3700 return;
3701 }
3702
3703 if (!isValidItem) // On-disk value missing; nothing else to do
3704 return;
3705
3706 if (!isValidValue) {
3707 valuePtr = malloc(fSubFields[0]->GetValueSize());
3708 CallConstructValueOn(*fSubFields[0], valuePtr);
3709 ptr->reset(reinterpret_cast<char *>(valuePtr));
3710 }
3711
3712 CallReadOn(*fSubFields[0], itemIndex, valuePtr);
3713}
3714
3716{
3717 auto typedPtr = static_cast<std::unique_ptr<char> *>(objPtr);
3718 if (*typedPtr) {
3719 fItemDeleter->operator()(typedPtr->get(), false /* dtorOnly */);
3720 typedPtr->release();
3721 }
3722 RDeleter::operator()(objPtr, dtorOnly);
3723}
3724
3725std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RUniquePtrField::GetDeleter() const
3726{
3727 return std::make_unique<RUniquePtrDeleter>(GetDeleterOf(*fSubFields[0]));
3728}
3729
3730std::vector<ROOT::Experimental::RFieldBase::RValue>
3732{
3733 std::vector<RValue> result;
3734 const auto &ptr = value.GetRef<std::unique_ptr<char>>();
3735 if (ptr) {
3736 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr.get())));
3737 }
3738 return result;
3739}
3740
3741//------------------------------------------------------------------------------
3742
3743ROOT::Experimental::ROptionalField::ROptionalField(std::string_view fieldName, std::string_view typeName,
3744 std::unique_ptr<RFieldBase> itemField)
3745 : RNullableField(fieldName, typeName, std::move(itemField)), fItemDeleter(GetDeleterOf(*fSubFields[0]))
3746{
3749}
3750
3751std::pair<void *, bool *> ROOT::Experimental::ROptionalField::GetValueAndEngagementPtrs(void *optionalPtr) const
3752{
3753 void *value = optionalPtr;
3754 bool *engagement =
3755 reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(optionalPtr) + fSubFields[0]->GetValueSize());
3756 return {value, engagement};
3757}
3758
3759std::pair<const void *, const bool *>
3761{
3762 return GetValueAndEngagementPtrs(const_cast<void *>(optionalPtr));
3763}
3764
3765std::unique_ptr<ROOT::Experimental::RFieldBase>
3767{
3768 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3769 return std::make_unique<ROptionalField>(newName, GetTypeName(), std::move(newItemField));
3770}
3771
3773{
3774 const auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(from);
3775 if (*engagementPtr) {
3776 return AppendValue(valuePtr);
3777 } else {
3778 return AppendNull();
3779 }
3780}
3781
3783{
3784 auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(to);
3785 auto itemIndex = GetItemIndex(globalIndex);
3786 if (itemIndex.GetIndex() == kInvalidClusterIndex) {
3787 *engagementPtr = false;
3788 } else {
3789 CallReadOn(*fSubFields[0], itemIndex, valuePtr);
3790 *engagementPtr = true;
3791 }
3792}
3793
3795{
3796 auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(where);
3797 CallConstructValueOn(*fSubFields[0], valuePtr);
3798 *engagementPtr = false;
3799}
3800
3802{
3803 fItemDeleter->operator()(objPtr, true /* dtorOnly */);
3804 RDeleter::operator()(objPtr, dtorOnly);
3805}
3806
3807std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::ROptionalField::GetDeleter() const
3808{
3809 return std::make_unique<ROptionalDeleter>(GetDeleterOf(*fSubFields[0]));
3810}
3811
3812std::vector<ROOT::Experimental::RFieldBase::RValue>
3814{
3815 std::vector<RValue> result;
3816 const auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(value.GetPtr<void>().get());
3817 if (*engagementPtr) {
3818 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), valuePtr)));
3819 }
3820 return result;
3821}
3822
3824{
3825 const auto alignment = GetAlignment();
3826 // real size is the sum of the value size and the engagement boolean
3827 const auto actualSize = fSubFields[0]->GetValueSize() + sizeof(bool);
3828 auto padding = 0;
3829 if (alignment > 1) {
3830 auto remainder = actualSize % alignment;
3831 if (remainder != 0)
3832 padding = alignment - remainder;
3833 }
3834 return actualSize + padding;
3835}
3836
3838{
3839 return fSubFields[0]->GetAlignment();
3840}
3841
3842//------------------------------------------------------------------------------
3843
3844std::string
3845ROOT::Experimental::RPairField::RPairField::GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
3846{
3847 return itemFields[0]->GetTypeName() + "," + itemFields[1]->GetTypeName();
3848}
3849
3851 std::array<std::unique_ptr<RFieldBase>, 2> &&itemFields,
3852 const std::array<std::size_t, 2> &offsets)
3853 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), offsets,
3854 "std::pair<" + GetTypeList(itemFields) + ">")
3855{
3856}
3857
3859 std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
3860 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), {},
3861 "std::pair<" + GetTypeList(itemFields) + ">")
3862{
3863 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
3864 fClass = TClass::GetClass(GetTypeName().c_str());
3865 if (!fClass)
3866 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
3867 fSize = fClass->Size();
3868
3869 auto firstElem = fClass->GetRealData("first");
3870 if (!firstElem)
3871 throw RException(R__FAIL("first: no such member"));
3872 fOffsets[0] = firstElem->GetThisOffset();
3873
3874 auto secondElem = fClass->GetRealData("second");
3875 if (!secondElem)
3876 throw RException(R__FAIL("second: no such member"));
3877 fOffsets[1] = secondElem->GetThisOffset();
3878}
3879
3880std::unique_ptr<ROOT::Experimental::RFieldBase>
3881ROOT::Experimental::RPairField::CloneImpl(std::string_view newName) const
3882{
3883 std::array<std::unique_ptr<RFieldBase>, 2> items{fSubFields[0]->Clone(fSubFields[0]->GetFieldName()),
3884 fSubFields[1]->Clone(fSubFields[1]->GetFieldName())};
3885
3886 std::unique_ptr<RPairField> result(new RPairField(newName, std::move(items), {fOffsets[0], fOffsets[1]}));
3887 result->fClass = fClass;
3888 return result;
3889}
3890
3892{
3893 fClass->New(where);
3894}
3895
3897{
3898 fClass->Destructor(objPtr, true /* dtorOnly */);
3899 RDeleter::operator()(objPtr, dtorOnly);
3900}
3901
3902//------------------------------------------------------------------------------
3903
3904std::string
3905ROOT::Experimental::RTupleField::RTupleField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
3906{
3907 std::string result;
3908 if (itemFields.empty())
3909 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
3910 for (size_t i = 0; i < itemFields.size(); ++i) {
3911 result += itemFields[i]->GetTypeName() + ",";
3912 }
3913 result.pop_back(); // remove trailing comma
3914 return result;
3915}
3916
3918 std::vector<std::unique_ptr<RFieldBase>> &&itemFields,
3919 const std::vector<std::size_t> &offsets)
3920 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), offsets,
3921 "std::tuple<" + GetTypeList(itemFields) + ">")
3922{
3923}
3924
3926 std::vector<std::unique_ptr<RFieldBase>> &itemFields)
3927 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), {},
3928 "std::tuple<" + GetTypeList(itemFields) + ">")
3929{
3930 fClass = TClass::GetClass(GetTypeName().c_str());
3931 if (!fClass)
3932 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
3933 fSize = fClass->Size();
3934
3935 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
3936 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
3937 // following the order of the type list.
3938 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
3939 // members, the assertion below will fail.
3940 for (unsigned i = 0; i < fSubFields.size(); ++i) {
3941 std::string memberName("_" + std::to_string(i));
3942 auto member = fClass->GetRealData(memberName.c_str());
3943 if (!member)
3944 throw RException(R__FAIL(memberName + ": no such member"));
3945 fOffsets.push_back(member->GetThisOffset());
3946 }
3947}
3948
3949std::unique_ptr<ROOT::Experimental::RFieldBase>
3950ROOT::Experimental::RTupleField::CloneImpl(std::string_view newName) const
3951{
3952 std::vector<std::unique_ptr<RFieldBase>> items;
3953 items.reserve(fSubFields.size());
3954 for (const auto &item : fSubFields)
3955 items.push_back(item->Clone(item->GetFieldName()));
3956
3957 std::unique_ptr<RTupleField> result(new RTupleField(newName, std::move(items), fOffsets));
3958 result->fClass = fClass;
3959 return result;
3960}
3961
3963{
3964 fClass->New(where);
3965}
3966
3968{
3969 fClass->Destructor(objPtr, true /* dtorOnly */);
3970 RDeleter::operator()(objPtr, dtorOnly);
3971}
3972
3973//------------------------------------------------------------------------------
3974
3976 std::shared_ptr<RNTupleCollectionWriter> collectionWriter,
3977 std::unique_ptr<RFieldZero> collectionParent)
3978 : RFieldBase(name, "", ENTupleStructure::kCollection, false /* isSimple */), fCollectionWriter(collectionWriter)
3979{
3980 const std::size_t N = collectionParent->fSubFields.size();
3981 for (std::size_t i = 0; i < N; ++i) {
3982 Attach(std::move(collectionParent->fSubFields[i]));
3983 }
3984}
3985
3988{
3989 static RColumnRepresentations representations(
3991 {});
3992 return representations;
3993}
3994
3996{
3997 GenerateColumnsImpl<ClusterSize_t>();
3998}
3999
4001{
4002 GenerateColumnsImpl<ClusterSize_t>(desc);
4003}
4004
4005std::unique_ptr<ROOT::Experimental::RFieldBase>
4007{
4008 auto parent = std::make_unique<RFieldZero>();
4009 for (auto &f : fSubFields) {
4010 parent->Attach(f->Clone(f->GetFieldName()));
4011 }
4012 return std::make_unique<RCollectionField>(newName, fCollectionWriter, std::move(parent));
4013}
4014
4016{
4017 // RCollectionFields are almost simple, but they return the bytes written by their subfields as accumulated by the
4018 // RNTupleCollectionWriter.
4019 std::size_t bytesWritten = fCollectionWriter->fBytesWritten;
4020 fCollectionWriter->fBytesWritten = 0;
4021
4022 fPrincipalColumn->Append(from);
4023 return bytesWritten + fPrincipalColumn->GetElement()->GetPackedSize();
4024}
4025
4027{
4028 R__ASSERT(false && "should never read an RCollectionField");
4029}
4030
4032{
4033 *fCollectionWriter->GetOffsetPtr() = 0;
4034}
4035
4036//------------------------------------------------------------------------------
4037
4038ROOT::Experimental::RAtomicField::RAtomicField(std::string_view fieldName, std::string_view typeName,
4039 std::unique_ptr<RFieldBase> itemField)
4040 : RFieldBase(fieldName, typeName, ENTupleStructure::kLeaf, false /* isSimple */)
4041{
4042 if (itemField->GetTraits() & kTraitTriviallyConstructible)
4044 if (itemField->GetTraits() & kTraitTriviallyDestructible)
4046 Attach(std::move(itemField));
4047}
4048
4049std::unique_ptr<ROOT::Experimental::RFieldBase>
4050ROOT::Experimental::RAtomicField::CloneImpl(std::string_view newName) const
4051{
4052 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
4053 return std::make_unique<RAtomicField>(newName, GetTypeName(), std::move(newItemField));
4054}
4055
4056std::vector<ROOT::Experimental::RFieldBase::RValue>
4058{
4059 std::vector<RValue> result;
4060 result.emplace_back(fSubFields[0]->BindValue(value.GetPtr<void>()));
4061 return result;
4062}
4063
4065{
4066 visitor.VisitAtomicField(*this);
4067}
size_t fValueSize
Cppyy::TCppType_t fClass
dim_t fSize
#define R__likely(expr)
Definition RConfig.hxx:587
#define R__FORWARD_RESULT(res)
Short-hand to return an RResult<T> value from a subroutine to the calling stack frame.
Definition RError.hxx:292
#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:290
#define R__LOG_WARNING(...)
Definition RLogger.hxx:363
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
int Int_t
Definition RtypesCore.h:45
@ 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
@ kClassHasExplicitDtor
@ kIsArray
Definition TDictionary.h:79
@ kIsStatic
Definition TDictionary.h:80
@ kIsDefinedInStd
Definition TDictionary.h:98
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
constexpr Int_t kUnset
Definition TError.h:43
#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 mask
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 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 length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize id
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:110
TCanvas * alignment()
Definition alignment.C:1
#define _(A, B)
Definition cfortran.h:108
#define free
Definition civetweb.c:1539
#define malloc
Definition civetweb.c:1536
Abstract base class for classes implementing the visitor design pattern.
virtual void VisitUnsplitField(const RUnsplitField &field)
virtual void VisitProxiedCollectionField(const RProxiedCollectionField &field)
virtual void VisitBoolField(const RField< bool > &field)
virtual void VisitBitsetField(const RBitsetField &field)
virtual void VisitNullableField(const RNullableField &field)
virtual void VisitFieldZero(const RFieldZero &field)
virtual void VisitRVecField(const RRVecField &field)
virtual void VisitCardinalityField(const RCardinalityField &field)
virtual void VisitTObjectField(const RField< TObject > &field)
virtual void VisitEnumField(const REnumField &field)
virtual void VisitArrayAsRVecField(const RArrayAsRVecField &field)
virtual void VisitDoubleField(const RField< double > &field)
virtual void VisitField(const RFieldBase &field)=0
virtual void VisitCharField(const RField< char > &field)
virtual void VisitArrayField(const RArrayField &field)
virtual void VisitClassField(const RClassField &field)
virtual void VisitRecordField(const RRecordField &field)
virtual void VisitVectorField(const RVectorField &field)
virtual void VisitFloatField(const RField< float > &field)
virtual void VisitAtomicField(const RAtomicField &field)
static std::string GetTypeName(EColumnType type)
static std::pair< std::uint16_t, std::uint16_t > GetValidBitRange(EColumnType type)
Most types have a fixed on-disk bit width.
A helper class for piece-wise construction of an RExtraTypeInfoDescriptor.
RExtraTypeInfoDescriptorBuilder & Content(const std::string &content)
RExtraTypeInfoDescriptorBuilder & TypeVersionTo(std::uint32_t typeVersionTo)
RExtraTypeInfoDescriptorBuilder & TypeVersionFrom(std::uint32_t typeVersionFrom)
RExtraTypeInfoDescriptorBuilder & TypeName(const std::string &typeName)
RExtraTypeInfoDescriptorBuilder & ContentId(EExtraTypeInfoIds contentId)
static std::string SerializeStreamerInfos(const StreamerInfoMap_t &infos)
Abstract interface to write data into an ntuple.
virtual void UpdateExtraTypeInfo(const RExtraTypeInfoDescriptor &extraTypeInfo)=0
Adds an extra type information record to schema.
const RNTupleWriteOptions & GetWriteOptions() const
Returns the sink's write options.
void RegisterOnCommitDatasetCallback(Callback_t callback)
The registered callback is executed at the beginning of CommitDataset();.
Abstract interface to read data from an ntuple.
const RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
The size of a value of this field, i.e. an RVec.
Definition RField.cxx:3171
std::size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:3275
std::size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
std::size_t fValueSize
The length of the arrays in this field.
std::unique_ptr< RDeleter > GetDeleter() const final
Returns an RRVecField::RRVecDeleter.
Definition RField.cxx:3228
RArrayAsRVecField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
Constructor of the field.
Definition RField.cxx:3156
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:3177
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3293
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3237
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:3254
std::vector< RFieldBase::RValue > SplitValue(const RFieldBase::RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:3281
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3118
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:3136
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3080
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3148
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:3098
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:3128
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3074
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3090
void ConstructValue(void *where) const override
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:3107
RArrayField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
Definition RField.cxx:3061
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:4050
RAtomicField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:4038
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:4057
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:4064
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3328
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3342
RBitsetField(std::string_view fieldName, std::size_t N)
Definition RField.cxx:3303
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:3318
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:3312
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3354
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:1348
const RField< RNTupleCardinality< std::uint32_t > > * As32Bit() const
Definition RField.cxx:1367
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.hxx:316
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1361
const RField< RNTupleCardinality< std::uint64_t > > * As64Bit() const
Definition RField.cxx:1373
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:1929
The field for a class with dictionary.
Definition RField.hxx:98
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:1890
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:109
void Attach(std::unique_ptr< RFieldBase > child, RSubFieldInfo info)
Definition RField.cxx:1839
void OnConnectPageSource() final
Called by ConnectPageSource() once connected; derived classes may override this as appropriate.
Definition RField.cxx:1897
size_t GetValueSize() const override
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:1948
void ConstructValue(void *where) const override
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:1924
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:1874
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:1883
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
Definition RField.cxx:1958
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1867
void AddReadCallbacksFromIORules(const std::span< const TSchemaRule * > rules, TClass *classp=nullptr)
Register post-read callbacks corresponding to a list of ROOT I/O customization rules.
Definition RField.cxx:1846
void AcceptVisitor(Detail::RFieldVisitor &visitor) const override
Definition RField.cxx:1963
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:151
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
Definition RField.cxx:1953
RClassField(std::string_view fieldName, std::string_view className, TClass *classp)
Definition RField.cxx:1763
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:1936
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
DescriptorId_t GetClusterId() const
ClusterSize_t::ValueType GetIndex() const
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:4015
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:3995
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:4006
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:4026
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:3987
Holds the index and the tag of a kSwitch column.
The field for an unscoped or scoped enum with dictionary.
Definition RField.hxx:211
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2263
REnumField(std::string_view fieldName, std::string_view enumName, TEnum *enump)
Definition RField.cxx:2212
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:2256
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2249
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Field specific extra type information from the header / extenstion header.
Similar to RValue but manages an array of consecutive values.
std::unique_ptr< bool[]> fMaskAvail
Masks invalid values in the array.
void AdoptBuffer(void *buf, std::size_t capacity)
Definition RField.cxx:542
RBulk & operator=(const RBulk &)=delete
void Reset(RClusterIndex firstIndex, std::size_t size)
Sets a new range for the bulk.
Definition RField.cxx:509
void * fValues
Cached deleter of fField.
std::unique_ptr< RFieldBase::RDeleter > fDeleter
Some fields have multiple possible column representations, e.g.
std::vector< ColumnRepresentation_t > Selection_t
A list of column representations.
Selection_t fDeserializationTypes
The union of the serialization types and the deserialization extra types.
Iterates over the sub tree of fields in depth-first search order.
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.
RBulk CreateBulk()
The returned bulk is initially empty; RBulk::ReadBulk will construct the array of values.
Definition RField.cxx:1089
static constexpr int kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
virtual void GenerateColumns()
Implementations in derived classes should create the backing columns corresponsing to the field type ...
void Attach(std::unique_ptr< RFieldBase > child)
Add a new subfield to the list of nested fields.
Definition RField.cxx:994
void AutoAdjustColumnTypes(const RNTupleWriteOptions &options)
When connecting a field to a page sink, the field's default column representation is subject to adjus...
Definition RField.cxx:1212
void SetColumnRepresentatives(const RColumnRepresentations::Selection_t &representatives)
Fixes a column representative.
Definition RField.cxx:1146
ENTupleStructure fStructure
The role of this field in the data model structure.
std::vector< RFieldBase * > GetSubFields()
Definition RField.cxx:1017
static std::vector< RCheckResult > Check(const std::string &fieldName, const std::string &typeName)
Checks if the given type is supported by RNTuple.
Definition RField.cxx:606
static constexpr int kTraitMappable
A field of a fundamental type that can be directly mapped via RField<T>::Map(), i....
std::function< void(void *)> ReadCallback_t
static constexpr int kTraitTriviallyConstructible
No constructor needs to be called, i.e.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
const std::string & GetTypeName() const
virtual void AcceptVisitor(Detail::RFieldVisitor &visitor) const
Definition RField.cxx:1308
std::size_t fNRepetitions
For fixed sized arrays, the array length.
friend class ROOT::Experimental::RCollectionField
RFieldBase * fParent
Sub fields point to their mother field.
void FlushColumns()
Flushes data from active columns.
Definition RField.cxx:1037
int fTraits
Properties of the type that allow for optimizations of collections of that type.
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
void ConnectPageSink(Internal::RPageSink &pageSink, NTupleSize_t firstEntry=0)
Fields and their columns live in the void until connected to a physical page storage.
Definition RField.cxx:1235
virtual std::size_t AppendImpl(const void *from)
Operations on values of complex types, e.g.
Definition RField.cxx:938
static constexpr int kTraitTypeChecksum
The TClass checksum is set and valid.
bool fIsSimple
A field qualifies as simple if it is both mappable and has no post-read callback.
RConstSchemaIterator cend() const
Definition RField.cxx:1126
std::string GetQualifiedFieldName() const
Returns the field name and parent field names separated by dots ("grandparent.parent....
Definition RField.cxx:586
std::vector< EColumnType > ColumnRepresentation_t
virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec)
General implementation of bulk read.
Definition RField.cxx:954
std::size_t Append(const void *from)
Write the given value into columns.
Definition RField.cxx:1080
virtual void ReadInClusterImpl(RClusterIndex clusterIndex, void *to)
Definition RField.cxx:949
void RemoveReadCallback(size_t idx)
Definition RField.cxx:1206
void * CreateObjectRawPtr() const
Factory method for the field's type. The caller owns the returned pointer.
Definition RField.cxx:974
void CommitCluster()
Flushes data from active columns to disk and calls CommitClusterImpl.
Definition RField.cxx:1049
const ColumnRepresentation_t & EnsureCompatibleColumnTypes(const RNTupleDescriptor &desc, std::uint16_t representationIndex) const
Returns the on-disk column types found in the provided descriptor for fOnDiskId and the given represe...
Definition RField.cxx:1163
void ConnectPageSource(Internal::RPageSource &pageSource)
Connects the field and its sub field tree to the given page source.
Definition RField.cxx:1261
RValue CreateValue()
Generates an object of the field type and wraps the created object in a shared pointer and returns it...
Definition RField.cxx:982
std::unique_ptr< RFieldBase > Clone(std::string_view newName) const
Copies the field and its sub fields using a possibly new name and a new, unconnected set of columns.
Definition RField.cxx:927
std::size_t ReadBulk(const RBulkSpec &bulkSpec)
Returns the number of newly available values, that is the number of bools in bulkSpec....
Definition RField.cxx:1099
RConstSchemaIterator cbegin() const
Definition RField.cxx:1121
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &canonicalType, const std::string &typeAlias, bool fContinueOnError=false)
Factory method to resurrect a field from the stored on-disk type information.
Definition RField.cxx:627
size_t AddReadCallback(ReadCallback_t func)
Set a user-defined function to be called after reading a value, giving a chance to inspect and/or mod...
Definition RField.cxx:1199
virtual std::vector< RValue > SplitValue(const RValue &value) const
Creates the list of direct child values given a value for this field.
Definition RField.cxx:989
void SetOnDiskId(DescriptorId_t id)
Definition RField.cxx:1071
RColumnRepresentations::Selection_t GetColumnRepresentatives() const
Returns the fColumnRepresentative pointee or, if unset, the field's default representative.
Definition RField.cxx:1132
std::string fName
The field name relative to its parent field.
std::vector< std::unique_ptr< RFieldBase > > fSubFields
Collections and classes own sub fields.
std::string fType
The C++ type captured by this field.
Internal::RColumn * fPrincipalColumn
All fields that have columns have a distinct main column.
static constexpr int kTraitTrivialType
Shorthand for types that are both trivially constructible and destructible.
void SetDescription(std::string_view description)
Definition RField.cxx:1064
static RResult< void > EnsureValidFieldName(std::string_view fieldName)
Check whether a given string is a valid field name.
Definition RField.cxx:910
NTupleSize_t EntryToColumnElementIndex(NTupleSize_t globalIndex) const
Translate an entry index to a column element index of the principal column and viceversa.
Definition RField.cxx:1005
RValue BindValue(std::shared_ptr< void > objPtr)
Creates a value from a memory location with an already constructed object.
Definition RField.cxx:1094
virtual void ReadGlobalImpl(NTupleSize_t globalIndex, void *to)
Definition RField.cxx:944
RFieldBase(std::string_view name, std::string_view type, ENTupleStructure structure, bool isSimple, std::size_t nRepetitions=0)
The constructor creates the underlying column objects and connects them to either a sink or a source.
Definition RField.cxx:573
virtual const RColumnRepresentations & GetColumnRepresentations() const
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:921
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:59
void Attach(std::unique_ptr< RFieldBase > child)
Add a new subfield to the list of nested fields.
Definition RField.cxx:994
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1324
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1316
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:239
RField(std::string_view name)
Definition RField.hxx:253
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.hxx:241
Used in RFieldBase::Check() to record field creation failures.
Definition RField.hxx:75
Template specializations for C++ std::[unordered_][multi]map.
RMapField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3541
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3554
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3568
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3599
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:3588
The on-storage meta-data of an ntuple.
RColumnDescriptorIterable GetColumnIterable() const
DescriptorId_t FindFieldId(std::string_view fieldName, DescriptorId_t parentId) const
const RColumnDescriptor & GetColumnDescriptor(DescriptorId_t columnId) const
const RFieldDescriptor & GetFieldDescriptor(DescriptorId_t fieldId) const
Common user-tunable settings for storing ntuples.
Template specializations for C++ std::optional and std::unique_ptr.
const RFieldBase::RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:3615
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:3623
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3655
RClusterIndex GetItemIndex(NTupleSize_t globalIndex)
Given the index of the nullable field, returns the corresponding global index of the subfield or,...
Definition RField.cxx:3647
RNullableField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3607
std::size_t AppendValue(const void *from)
Definition RField.cxx:3639
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3801
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3782
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:3794
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:3807
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:3837
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3772
std::pair< const void *, const bool * > GetValueAndEngagementPtrs(const void *optionalPtr) const
Given a pointer to an std::optional<T> in optionalPtr, extract a pointer to the value T* and a pointe...
Definition RField.cxx:3760
ROptionalField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3743
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:3823
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3766
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:3813
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3896
Template specializations for C++ std::pair.
void ConstructValue(void *where) const override
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:3891
RPairField(std::string_view fieldName, std::array< std::unique_ptr< RFieldBase >, 2 > &&itemFields, const std::array< std::size_t, 2 > &offsets)
Definition RField.cxx:3850
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3881
Allows for iterating over the elements of a proxied collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
Definition RField.cxx:2271
The field for a class representing a collection of elements via TVirtualCollectionProxy.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2352
void ConstructValue(void *where) const override
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:2413
std::size_t AppendImpl(const void *from) override
Operations on values of complex types, e.g.
Definition RField.cxx:2359
std::shared_ptr< TVirtualCollectionProxy > fProxy
The collection proxy is needed by the deleters and thus defined as a shared pointer.
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
void AcceptVisitor(Detail::RFieldVisitor &visitor) const override
Definition RField.cxx:2453
std::unique_ptr< RDeleter > GetDeleter() const override
Definition RField.cxx:2419
RProxiedCollectionField(std::string_view fieldName, std::string_view typeName, TClass *classp)
Constructor used when the value type of the collection is not known in advance, i....
Definition RField.cxx:2283
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2395
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:2403
std::vector< RValue > SplitValue(const RValue &value) const override
Creates the list of direct child values given a value for this field.
Definition RField.cxx:2441
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) override
Definition RField.cxx:2375
size_t GetValueSize() const override
The number of bytes taken by a value of the appropriate type.
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:2931
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2738
std::unique_ptr< RDeleter > fItemDeleter
std::unique_ptr< RDeleter > GetDeleter() const override
Definition RField.cxx:2946
void ConstructValue(void *where) const override
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:2922
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2977
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:2954
size_t GetAlignment() const override
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:2972
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:2912
size_t GetValueSize() const override
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:2967
RRVecField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:2725
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2904
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
General implementation of bulk read.
Definition RField.cxx:2838
std::size_t AppendImpl(const void *from) override
Operations on values of complex types, e.g.
Definition RField.cxx:2744
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) override
Definition RField.cxx:2764
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:2550
The field for an untyped record.
RRecordField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > &&itemFields, const std::vector< std::size_t > &offsets, std::string_view typeName="")
Definition RField.cxx:2460
std::vector< std::size_t > fOffsets
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:2536
std::unique_ptr< RDeleter > GetDeleter() const override
Definition RField.cxx:2558
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2529
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given a value for this field.
Definition RField.cxx:2569
std::size_t GetItemPadding(std::size_t baseOffset, std::size_t itemAlignment) const
Definition RField.cxx:2500
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2520
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2580
void ConstructValue(void *where) const override
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:2543
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2511
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:194
RSetField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3527
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3533
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3967
Template specializations for C++ std::tuple.
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > &&itemFields, const std::vector< std::size_t > &offsets)
Definition RField.cxx:3917
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3950
void ConstructValue(void *where) const override
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
Definition RField.cxx:3962
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3715
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition