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