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{
569}
570
572{
573 std::string result = GetFieldName();
574 auto parent = GetParent();
575 while (parent && !parent->GetFieldName().empty()) {
576 result = parent->GetFieldName() + "." + result;
577 parent = parent->GetParent();
578 }
579 return result;
580}
581
583ROOT::Experimental::RFieldBase::Create(const std::string &fieldName, const std::string &typeName)
584{
585 auto typeAlias = GetNormalizedTypeName(typeName);
586 auto canonicalType = GetNormalizedTypeName(GetCanonicalTypeName(typeAlias));
587 return R__FORWARD_RESULT(RFieldBase::Create(fieldName, canonicalType, typeAlias));
588}
589
590std::vector<ROOT::Experimental::RFieldBase::RCheckResult>
591ROOT::Experimental::RFieldBase::Check(const std::string &fieldName, const std::string &typeName)
592{
593 auto typeAlias = GetNormalizedTypeName(typeName);
594 auto canonicalType = GetNormalizedTypeName(GetCanonicalTypeName(typeAlias));
595
596 RFieldZero fieldZero;
597 fieldZero.Attach(RFieldBase::Create(fieldName, canonicalType, typeAlias, true /* continueOnError */).Unwrap());
598
599 std::vector<RCheckResult> result;
600 for (const auto &f : fieldZero) {
601 auto invalidField = dynamic_cast<const RInvalidField *>(&f);
602 if (!invalidField)
603 continue;
604
605 result.emplace_back(
606 RCheckResult{invalidField->GetQualifiedFieldName(), invalidField->GetTypeName(), invalidField->GetError()});
607 }
608 return result;
609}
610
612ROOT::Experimental::RFieldBase::Create(const std::string &fieldName, const std::string &canonicalType,
613 const std::string &typeAlias, bool continueOnError)
614{
615 thread_local CreateContext createContext;
616 CreateContextGuard createContextGuard(createContext);
617 if (continueOnError)
618 createContextGuard.SetContinueOnError(true);
619
620 auto fnFail = [&fieldName, &canonicalType](const std::string &errMsg) -> RResult<std::unique_ptr<RFieldBase>> {
621 if (createContext.GetContinueOnError()) {
622 return std::unique_ptr<RFieldBase>(std::make_unique<RInvalidField>(fieldName, canonicalType, errMsg));
623 } else {
624 return R__FAIL(errMsg);
625 }
626 };
627
628 if (canonicalType.empty())
629 return R__FORWARD_RESULT(fnFail("no type name specified for field '" + fieldName + "'"));
630
631 std::unique_ptr<ROOT::Experimental::RFieldBase> result;
632
633 // try-catch block to intercept any exception that may be thrown by Unwrap() so that this
634 // function never throws but returns RResult::Error instead.
635 try {
636 if (auto [arrayBaseType, arraySizes] = ParseArrayType(canonicalType); !arraySizes.empty()) {
637 std::unique_ptr<RFieldBase> arrayField = Create("_0", arrayBaseType).Unwrap();
638 for (int i = arraySizes.size() - 1; i >= 0; --i) {
639 arrayField =
640 std::make_unique<RArrayField>((i == 0) ? fieldName : "_0", std::move(arrayField), arraySizes[i]);
641 }
642 return arrayField;
643 }
644
645 if (canonicalType == "bool") {
646 result = std::make_unique<RField<bool>>(fieldName);
647 } else if (canonicalType == "char") {
648 result = std::make_unique<RField<char>>(fieldName);
649 } else if (canonicalType == "signed char") {
650 result = std::make_unique<RField<signed char>>(fieldName);
651 } else if (canonicalType == "unsigned char") {
652 result = std::make_unique<RField<unsigned char>>(fieldName);
653 } else if (canonicalType == "short") {
654 result = std::make_unique<RField<short>>(fieldName);
655 } else if (canonicalType == "unsigned short") {
656 result = std::make_unique<RField<unsigned short>>(fieldName);
657 } else if (canonicalType == "int") {
658 result = std::make_unique<RField<int>>(fieldName);
659 } else if (canonicalType == "unsigned int") {
660 result = std::make_unique<RField<unsigned int>>(fieldName);
661 } else if (canonicalType == "long") {
662 result = std::make_unique<RField<long>>(fieldName);
663 } else if (canonicalType == "unsigned long") {
664 result = std::make_unique<RField<unsigned long>>(fieldName);
665 } else if (canonicalType == "long long") {
666 result = std::make_unique<RField<long long>>(fieldName);
667 } else if (canonicalType == "unsigned long long") {
668 result = std::make_unique<RField<unsigned long long>>(fieldName);
669 } else if (canonicalType == "std::byte") {
670 result = std::make_unique<RField<std::byte>>(fieldName);
671 } else if (canonicalType == "std::int8_t") {
672 result = std::make_unique<RField<std::int8_t>>(fieldName);
673 } else if (canonicalType == "std::uint8_t") {
674 result = std::make_unique<RField<std::uint8_t>>(fieldName);
675 } else if (canonicalType == "std::int16_t") {
676 result = std::make_unique<RField<std::int16_t>>(fieldName);
677 } else if (canonicalType == "std::uint16_t") {
678 result = std::make_unique<RField<std::uint16_t>>(fieldName);
679 } else if (canonicalType == "std::int32_t") {
680 result = std::make_unique<RField<std::int32_t>>(fieldName);
681 } else if (canonicalType == "std::uint32_t") {
682 result = std::make_unique<RField<std::uint32_t>>(fieldName);
683 } else if (canonicalType == "std::int64_t") {
684 result = std::make_unique<RField<std::int64_t>>(fieldName);
685 } else if (canonicalType == "std::uint64_t") {
686 result = std::make_unique<RField<std::uint64_t>>(fieldName);
687 } else if (canonicalType == "float") {
688 result = std::make_unique<RField<float>>(fieldName);
689 } else if (canonicalType == "double") {
690 result = std::make_unique<RField<double>>(fieldName);
691 } else if (canonicalType == "Double32_t") {
692 result = std::make_unique<RField<double>>(fieldName);
693 static_cast<RField<double> *>(result.get())->SetDouble32();
694 // Prevent the type alias from being reset by returning early
695 return result;
696 } else if (canonicalType == "std::string") {
697 result = std::make_unique<RField<std::string>>(fieldName);
698 } else if (canonicalType == "TObject") {
699 result = std::make_unique<RField<TObject>>(fieldName);
700 } else if (canonicalType == "std::vector<bool>") {
701 result = std::make_unique<RField<std::vector<bool>>>(fieldName);
702 } else if (canonicalType.substr(0, 12) == "std::vector<") {
703 std::string itemTypeName = canonicalType.substr(12, canonicalType.length() - 13);
704 auto itemField = Create("_0", itemTypeName);
705 result = std::make_unique<RVectorField>(fieldName, itemField.Unwrap());
706 } else if (canonicalType.substr(0, 19) == "ROOT::VecOps::RVec<") {
707 std::string itemTypeName = canonicalType.substr(19, canonicalType.length() - 20);
708 auto itemField = Create("_0", itemTypeName);
709 result = std::make_unique<RRVecField>(fieldName, itemField.Unwrap());
710 } else if (canonicalType.substr(0, 11) == "std::array<") {
711 auto arrayDef = TokenizeTypeList(canonicalType.substr(11, canonicalType.length() - 12));
712 if (arrayDef.size() != 2) {
713 return R__FORWARD_RESULT(fnFail("the template list for std::array must have exactly two elements"));
714 }
715 auto arrayLength = std::stoi(arrayDef[1]);
716 auto itemField = Create("_0", arrayDef[0]);
717 result = std::make_unique<RArrayField>(fieldName, itemField.Unwrap(), arrayLength);
718 } else if (canonicalType.substr(0, 13) == "std::variant<") {
719 auto innerTypes = TokenizeTypeList(canonicalType.substr(13, canonicalType.length() - 14));
720 std::vector<std::unique_ptr<RFieldBase>> items;
721 items.reserve(innerTypes.size());
722 for (unsigned int i = 0; i < innerTypes.size(); ++i) {
723 items.emplace_back(Create("_" + std::to_string(i), innerTypes[i]).Unwrap());
724 }
725 result = std::make_unique<RVariantField>(fieldName, std::move(items));
726 } else if (canonicalType.substr(0, 10) == "std::pair<") {
727 auto innerTypes = TokenizeTypeList(canonicalType.substr(10, canonicalType.length() - 11));
728 if (innerTypes.size() != 2) {
729 return R__FORWARD_RESULT(fnFail("the type list for std::pair must have exactly two elements"));
730 }
731 std::array<std::unique_ptr<RFieldBase>, 2> items{Create("_0", innerTypes[0]).Unwrap(),
732 Create("_1", innerTypes[1]).Unwrap()};
733 result = std::make_unique<RPairField>(fieldName, std::move(items));
734 } else if (canonicalType.substr(0, 11) == "std::tuple<") {
735 auto innerTypes = TokenizeTypeList(canonicalType.substr(11, canonicalType.length() - 12));
736 std::vector<std::unique_ptr<RFieldBase>> items;
737 items.reserve(innerTypes.size());
738 for (unsigned int i = 0; i < innerTypes.size(); ++i) {
739 items.emplace_back(Create("_" + std::to_string(i), innerTypes[i]).Unwrap());
740 }
741 result = std::make_unique<RTupleField>(fieldName, std::move(items));
742 } else if (canonicalType.substr(0, 12) == "std::bitset<") {
743 auto size = std::stoull(canonicalType.substr(12, canonicalType.length() - 13));
744 result = std::make_unique<RBitsetField>(fieldName, size);
745 } else if (canonicalType.substr(0, 16) == "std::unique_ptr<") {
746 std::string itemTypeName = canonicalType.substr(16, canonicalType.length() - 17);
747 auto itemField = Create("_0", itemTypeName).Unwrap();
748 auto normalizedInnerTypeName = itemField->GetTypeName();
749 result = std::make_unique<RUniquePtrField>(fieldName, "std::unique_ptr<" + normalizedInnerTypeName + ">",
750 std::move(itemField));
751 } else if (canonicalType.substr(0, 14) == "std::optional<") {
752 std::string itemTypeName = canonicalType.substr(14, canonicalType.length() - 15);
753 auto itemField = Create("_0", itemTypeName).Unwrap();
754 auto normalizedInnerTypeName = itemField->GetTypeName();
755 result = std::make_unique<ROptionalField>(fieldName, "std::optional<" + normalizedInnerTypeName + ">",
756 std::move(itemField));
757 } else if (canonicalType.substr(0, 9) == "std::set<") {
758 std::string itemTypeName = canonicalType.substr(9, canonicalType.length() - 10);
759 auto itemField = Create("_0", itemTypeName).Unwrap();
760 auto normalizedInnerTypeName = itemField->GetTypeName();
761 result =
762 std::make_unique<RSetField>(fieldName, "std::set<" + normalizedInnerTypeName + ">", std::move(itemField));
763 } else if (canonicalType.substr(0, 19) == "std::unordered_set<") {
764 std::string itemTypeName = canonicalType.substr(19, canonicalType.length() - 20);
765 auto itemField = Create("_0", itemTypeName).Unwrap();
766 auto normalizedInnerTypeName = itemField->GetTypeName();
767 result = std::make_unique<RSetField>(fieldName, "std::unordered_set<" + normalizedInnerTypeName + ">",
768 std::move(itemField));
769 } else if (canonicalType.substr(0, 14) == "std::multiset<") {
770 std::string itemTypeName = canonicalType.substr(14, canonicalType.length() - 15);
771 auto itemField = Create("_0", itemTypeName).Unwrap();
772 auto normalizedInnerTypeName = itemField->GetTypeName();
773 result = std::make_unique<RSetField>(fieldName, "std::multiset<" + normalizedInnerTypeName + ">",
774 std::move(itemField));
775 } else if (canonicalType.substr(0, 24) == "std::unordered_multiset<") {
776 std::string itemTypeName = canonicalType.substr(24, canonicalType.length() - 25);
777 auto itemField = Create("_0", itemTypeName).Unwrap();
778 auto normalizedInnerTypeName = itemField->GetTypeName();
779 result = std::make_unique<RSetField>(fieldName, "std::unordered_multiset<" + normalizedInnerTypeName + ">",
780 std::move(itemField));
781 } else if (canonicalType.substr(0, 9) == "std::map<") {
782 auto innerTypes = TokenizeTypeList(canonicalType.substr(9, canonicalType.length() - 10));
783 if (innerTypes.size() != 2) {
784 return R__FORWARD_RESULT(fnFail("the type list for std::map must have exactly two elements"));
785 }
786
787 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
788
789 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
790 // the inner type names are properly normalized.
791 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
792 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
793
794 result = std::make_unique<RMapField>(fieldName, "std::map<" + keyTypeName + "," + valueTypeName + ">",
795 std::move(itemField));
796 } else if (canonicalType.substr(0, 19) == "std::unordered_map<") {
797 auto innerTypes = TokenizeTypeList(canonicalType.substr(19, canonicalType.length() - 20));
798 if (innerTypes.size() != 2)
799 return R__FORWARD_RESULT(fnFail("the type list for std::unordered_map must have exactly two elements"));
800
801 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
802
803 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
804 // the inner type names are properly normalized.
805 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
806 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
807
808 result = std::make_unique<RMapField>(
809 fieldName, "std::unordered_map<" + keyTypeName + "," + valueTypeName + ">", std::move(itemField));
810 } else if (canonicalType.substr(0, 14) == "std::multimap<") {
811 auto innerTypes = TokenizeTypeList(canonicalType.substr(14, canonicalType.length() - 15));
812 if (innerTypes.size() != 2)
813 return R__FORWARD_RESULT(fnFail("the type list for std::multimap must have exactly two elements"));
814
815 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
816
817 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
818 // the inner type names are properly normalized.
819 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
820 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
821
822 result = std::make_unique<RMapField>(fieldName, "std::multimap<" + keyTypeName + "," + valueTypeName + ">",
823 std::move(itemField));
824 } else if (canonicalType.substr(0, 24) == "std::unordered_multimap<") {
825 auto innerTypes = TokenizeTypeList(canonicalType.substr(24, canonicalType.length() - 25));
826 if (innerTypes.size() != 2)
827 return R__FORWARD_RESULT(
828 fnFail("the type list for std::unordered_multimap must have exactly two elements"));
829
830 auto itemField = Create("_0", "std::pair<" + innerTypes[0] + "," + innerTypes[1] + ">").Unwrap();
831
832 // We use the type names of subfields of the newly created item fields to create the map's type name to ensure
833 // the inner type names are properly normalized.
834 auto keyTypeName = itemField->GetSubFields()[0]->GetTypeName();
835 auto valueTypeName = itemField->GetSubFields()[1]->GetTypeName();
836
837 result = std::make_unique<RMapField>(
838 fieldName, "std::unordered_multimap<" + keyTypeName + "," + valueTypeName + ">", std::move(itemField));
839 } else if (canonicalType.substr(0, 12) == "std::atomic<") {
840 std::string itemTypeName = canonicalType.substr(12, canonicalType.length() - 13);
841 auto itemField = Create("_0", itemTypeName).Unwrap();
842 auto normalizedInnerTypeName = itemField->GetTypeName();
843 result = std::make_unique<RAtomicField>(fieldName, "std::atomic<" + normalizedInnerTypeName + ">",
844 std::move(itemField));
845 } else if (canonicalType.substr(0, 25) == "ROOT::RNTupleCardinality<") {
846 auto innerTypes = TokenizeTypeList(canonicalType.substr(25, canonicalType.length() - 26));
847 if (innerTypes.size() != 1)
848 return R__FORWARD_RESULT(fnFail("invalid cardinality template: " + canonicalType));
849 if (innerTypes[0] == "std::uint32_t") {
850 result = std::make_unique<RField<RNTupleCardinality<std::uint32_t>>>(fieldName);
851 } else if (innerTypes[0] == "std::uint64_t") {
852 result = std::make_unique<RField<RNTupleCardinality<std::uint64_t>>>(fieldName);
853 } else {
854 return R__FORWARD_RESULT(fnFail("invalid cardinality template: " + canonicalType));
855 }
856 }
857
858 if (!result) {
859 auto e = TEnum::GetEnum(canonicalType.c_str());
860 if (e != nullptr) {
861 result = std::make_unique<REnumField>(fieldName, canonicalType);
862 }
863 }
864
865 if (!result) {
866 auto cl = TClass::GetClass(canonicalType.c_str());
867 if (cl != nullptr) {
868 createContextGuard.AddClassToStack(canonicalType);
869 if (cl->GetCollectionProxy()) {
870 result = std::make_unique<RProxiedCollectionField>(fieldName, canonicalType);
871 } else {
872 if (GetRNTupleSerializationMode(cl) == ERNTupleSerializationMode::kForceStreamerMode) {
873 result = std::make_unique<RStreamerField>(fieldName, canonicalType);
874 } else {
875 result = std::make_unique<RClassField>(fieldName, canonicalType);
876 }
877 }
878 }
879 }
880 } catch (RException &e) {
881 auto error = e.GetError();
882 if (createContext.GetContinueOnError()) {
883 return std::unique_ptr<RFieldBase>(
884 std::make_unique<RInvalidField>(fieldName, canonicalType, error.GetReport()));
885 } else {
886 return error;
887 }
888 }
889
890 if (result) {
891 if (typeAlias != canonicalType)
892 result->fTypeAlias = typeAlias;
893 return result;
894 }
895 return R__FORWARD_RESULT(fnFail("unknown type: " + canonicalType));
896}
897
900{
901 static RColumnRepresentations representations;
902 return representations;
903}
904
905std::unique_ptr<ROOT::Experimental::RFieldBase> ROOT::Experimental::RFieldBase::Clone(std::string_view newName) const
906{
907 auto clone = CloneImpl(newName);
908 clone->fTypeAlias = fTypeAlias;
909 clone->fOnDiskId = fOnDiskId;
910 clone->fDescription = fDescription;
911 // We can just copy the references because fColumnRepresentatives point into a static structure
912 clone->fColumnRepresentatives = fColumnRepresentatives;
913 return clone;
914}
915
916std::size_t ROOT::Experimental::RFieldBase::AppendImpl(const void * /* from */)
917{
918 R__ASSERT(false && "A non-simple RField must implement its own AppendImpl");
919 return 0;
920}
921
923{
924 R__ASSERT(false);
925}
926
928{
929 ReadGlobalImpl(fPrincipalColumn->GetGlobalIndex(clusterIndex), to);
930}
931
933{
934 const auto valueSize = GetValueSize();
935 std::size_t nRead = 0;
936 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
937 // Value not needed
938 if (!bulkSpec.fMaskReq[i])
939 continue;
940
941 // Value already present
942 if (bulkSpec.fMaskAvail[i])
943 continue;
944
945 Read(bulkSpec.fFirstIndex + i, reinterpret_cast<unsigned char *>(bulkSpec.fValues) + i * valueSize);
946 bulkSpec.fMaskAvail[i] = true;
947 nRead++;
948 }
949 return nRead;
950}
951
953{
954 void *where = operator new(GetValueSize());
955 R__ASSERT(where != nullptr);
956 ConstructValue(where);
957 return where;
958}
959
961{
962 void *obj = CreateObjectRawPtr();
963 return RValue(this, std::shared_ptr<void>(obj, RSharedPtrDeleter(GetDeleter())));
964}
965
966std::vector<ROOT::Experimental::RFieldBase::RValue>
968{
969 return std::vector<RValue>();
970}
971
972void ROOT::Experimental::RFieldBase::Attach(std::unique_ptr<ROOT::Experimental::RFieldBase> child)
973{
974 // Note that during a model update, new fields will be attached to the zero field. The zero field, however,
975 // does not change its inital state because only its sub fields get connected by RPageSink::UpdateSchema.
976 if (fState != EState::kUnconnected)
977 throw RException(R__FAIL("invalid attempt to attach subfield to already connected field"));
978 child->fParent = this;
979 fSubFields.emplace_back(std::move(child));
980}
981
984{
985 std::size_t result = globalIndex;
986 for (auto f = this; f != nullptr; f = f->GetParent()) {
987 auto parent = f->GetParent();
988 if (parent && (parent->GetStructure() == kCollection || parent->GetStructure() == kVariant))
989 return 0U;
990 result *= std::max(f->GetNRepetitions(), std::size_t{1U});
991 }
992 return result;
993}
994
995std::vector<ROOT::Experimental::RFieldBase *> ROOT::Experimental::RFieldBase::GetSubFields()
996{
997 std::vector<RFieldBase *> result;
998 result.reserve(fSubFields.size());
999 for (const auto &f : fSubFields) {
1000 result.emplace_back(f.get());
1001 }
1002 return result;
1003}
1004
1005std::vector<const ROOT::Experimental::RFieldBase *> ROOT::Experimental::RFieldBase::GetSubFields() const
1006{
1007 std::vector<const RFieldBase *> result;
1008 result.reserve(fSubFields.size());
1009 for (const auto &f : fSubFields) {
1010 result.emplace_back(f.get());
1011 }
1012 return result;
1013}
1014
1016{
1017 if (!fAvailableColumns.empty()) {
1018 const auto activeRepresentationIndex = fPrincipalColumn->GetRepresentationIndex();
1019 for (auto &column : fAvailableColumns) {
1020 if (column->GetRepresentationIndex() == activeRepresentationIndex) {
1021 column->Flush();
1022 }
1023 }
1024 }
1025}
1026
1028{
1029 if (!fAvailableColumns.empty()) {
1030 const auto activeRepresentationIndex = fPrincipalColumn->GetRepresentationIndex();
1031 for (auto &column : fAvailableColumns) {
1032 if (column->GetRepresentationIndex() == activeRepresentationIndex) {
1033 column->Flush();
1034 } else {
1035 column->CommitSuppressed();
1036 }
1037 }
1038 }
1039 CommitClusterImpl();
1040}
1041
1042void ROOT::Experimental::RFieldBase::SetDescription(std::string_view description)
1043{
1044 if (fState != EState::kUnconnected)
1045 throw RException(R__FAIL("cannot set field description once field is connected"));
1046 fDescription = std::string(description);
1047}
1048
1050{
1051 if (fState != EState::kUnconnected)
1052 throw RException(R__FAIL("cannot set field ID once field is connected"));
1053 fOnDiskId = id;
1054}
1055
1056/// Write the given value into columns. The value object has to be of the same type as the field.
1057/// Returns the number of uncompressed bytes written.
1058std::size_t ROOT::Experimental::RFieldBase::Append(const void *from)
1059{
1060 if (~fTraits & kTraitMappable)
1061 return AppendImpl(from);
1062
1063 fPrincipalColumn->Append(from);
1064 return fPrincipalColumn->GetElement()->GetPackedSize();
1065}
1066
1068{
1069 return RBulk(this);
1070}
1071
1073{
1074 return RValue(this, objPtr);
1075}
1076
1078{
1079 if (fIsSimple) {
1080 /// For simple types, ignore the mask and memcopy the values into the destination
1081 fPrincipalColumn->ReadV(bulkSpec.fFirstIndex, bulkSpec.fCount, bulkSpec.fValues);
1082 std::fill(bulkSpec.fMaskAvail, bulkSpec.fMaskAvail + bulkSpec.fCount, true);
1083 return RBulkSpec::kAllSet;
1084 }
1085
1086 return ReadBulkImpl(bulkSpec);
1087}
1088
1090{
1091 return fSubFields.empty() ? RSchemaIterator(this, -1) : RSchemaIterator(fSubFields[0].get(), 0);
1092}
1093
1095{
1096 return RSchemaIterator(this, -1);
1097}
1098
1100{
1101 return fSubFields.empty() ? RConstSchemaIterator(this, -1) : RConstSchemaIterator(fSubFields[0].get(), 0);
1102}
1103
1105{
1106 return RConstSchemaIterator(this, -1);
1107}
1108
1110{
1111 return fSubFields.empty() ? RConstSchemaIterator(this, -1) : RConstSchemaIterator(fSubFields[0].get(), 0);
1112}
1113
1115{
1116 return RConstSchemaIterator(this, -1);
1117}
1118
1121{
1122 if (fColumnRepresentatives.empty()) {
1123 return {GetColumnRepresentations().GetSerializationDefault()};
1124 }
1125
1127 result.reserve(fColumnRepresentatives.size());
1128 for (const auto &r : fColumnRepresentatives) {
1129 result.emplace_back(r.get());
1130 }
1131 return result;
1132}
1133
1135 const RColumnRepresentations::Selection_t &representatives)
1136{
1137 if (fState != EState::kUnconnected)
1138 throw RException(R__FAIL("cannot set column representative once field is connected"));
1139 const auto &validTypes = GetColumnRepresentations().GetSerializationTypes();
1140 fColumnRepresentatives.clear();
1141 fColumnRepresentatives.reserve(representatives.size());
1142 for (const auto &r : representatives) {
1143 auto itRepresentative = std::find(validTypes.begin(), validTypes.end(), r);
1144 if (itRepresentative == std::end(validTypes))
1145 throw RException(R__FAIL("invalid column representative"));
1146 fColumnRepresentatives.emplace_back(*itRepresentative);
1147 }
1148}
1149
1152 std::uint16_t representationIndex) const
1153{
1154 static const ColumnRepresentation_t kEmpty;
1155
1156 if (fOnDiskId == kInvalidDescriptorId)
1157 throw RException(R__FAIL("No on-disk field information for `" + GetQualifiedFieldName() + "`"));
1158
1159 ColumnRepresentation_t onDiskTypes;
1160 for (const auto &c : desc.GetColumnIterable(fOnDiskId)) {
1161 if (c.GetRepresentationIndex() == representationIndex)
1162 onDiskTypes.emplace_back(c.GetType());
1163 }
1164 if (onDiskTypes.empty()) {
1165 if (representationIndex == 0) {
1166 throw RException(R__FAIL("No on-disk column information for field `" + GetQualifiedFieldName() + "`"));
1167 }
1168 return kEmpty;
1169 }
1170
1171 for (const auto &t : GetColumnRepresentations().GetDeserializationTypes()) {
1172 if (t == onDiskTypes)
1173 return t;
1174 }
1175
1176 std::string columnTypeNames;
1177 for (const auto &t : onDiskTypes) {
1178 if (!columnTypeNames.empty())
1179 columnTypeNames += ", ";
1180 columnTypeNames += std::string("`") + Internal::RColumnElementBase::GetColumnTypeName(t) + "`";
1181 }
1182 throw RException(R__FAIL("On-disk column types {" + columnTypeNames + "} for field `" + GetQualifiedFieldName() +
1183 "` cannot be matched to its in-memory type `" + GetTypeName() + "` " +
1184 "(representation index: " + std::to_string(representationIndex) + ")"));
1185}
1186
1188{
1189 fReadCallbacks.push_back(func);
1190 fIsSimple = false;
1191 return fReadCallbacks.size() - 1;
1192}
1193
1195{
1196 fReadCallbacks.erase(fReadCallbacks.begin() + idx);
1197 fIsSimple = (fTraits & kTraitMappable) && fReadCallbacks.empty();
1198}
1199
1201{
1202 if ((options.GetCompression() == 0) && HasDefaultColumnRepresentative()) {
1203 ColumnRepresentation_t rep = GetColumnRepresentations().GetSerializationDefault();
1204 for (auto &colType : rep) {
1205 switch (colType) {
1208 case EColumnType::kSplitReal64: colType = EColumnType::kReal64; break;
1209 case EColumnType::kSplitReal32: colType = EColumnType::kReal32; break;
1210 case EColumnType::kSplitInt64: colType = EColumnType::kInt64; break;
1211 case EColumnType::kSplitInt32: colType = EColumnType::kInt32; break;
1212 case EColumnType::kSplitInt16: colType = EColumnType::kInt16; break;
1213 case EColumnType::kSplitUInt64: colType = EColumnType::kUInt64; break;
1214 case EColumnType::kSplitUInt32: colType = EColumnType::kUInt32; break;
1215 case EColumnType::kSplitUInt16: colType = EColumnType::kUInt16; break;
1216 default: break;
1217 }
1218 }
1219 SetColumnRepresentatives({rep});
1220 }
1221
1222 if (fTypeAlias == "Double32_t")
1223 SetColumnRepresentatives({{EColumnType::kSplitReal32}});
1224}
1225
1227{
1228 if (dynamic_cast<ROOT::Experimental::RFieldZero *>(this))
1229 throw RException(R__FAIL("invalid attempt to connect zero field to page sink"));
1230 if (fState != EState::kUnconnected)
1231 throw RException(R__FAIL("invalid attempt to connect an already connected field to a page sink"));
1232
1233 AutoAdjustColumnTypes(pageSink.GetWriteOptions());
1234
1235 GenerateColumns();
1236 for (auto &column : fAvailableColumns) {
1237 // Only the first column of every representation can be a deferred column. In all column representations,
1238 // larger column indexes are data columns of collections (string, streamer) and thus
1239 // they have no elements on late model extension
1240 auto firstElementIndex = (column->GetIndex() == 0) ? EntryToColumnElementIndex(firstEntry) : 0;
1241 column->ConnectPageSink(fOnDiskId, pageSink, firstElementIndex);
1242 }
1243
1244 if (HasExtraTypeInfo()) {
1246 [this](Internal::RPageSink &sink) { sink.UpdateExtraTypeInfo(GetExtraTypeInfo()); });
1247 }
1248
1249 fState = EState::kConnectedToSink;
1250}
1251
1253{
1254 if (dynamic_cast<ROOT::Experimental::RFieldZero *>(this))
1255 throw RException(R__FAIL("invalid attempt to connect zero field to page source"));
1256 if (fState != EState::kUnconnected)
1257 throw RException(R__FAIL("invalid attempt to connect an already connected field to a page source"));
1258
1259 if (!fColumnRepresentatives.empty())
1260 throw RException(R__FAIL("fixed column representative only valid when connecting to a page sink"));
1261 if (!fDescription.empty())
1262 throw RException(R__FAIL("setting description only valid when connecting to a page sink"));
1263
1264 for (auto &f : fSubFields) {
1265 if (f->GetOnDiskId() == kInvalidDescriptorId) {
1266 f->SetOnDiskId(pageSource.GetSharedDescriptorGuard()->FindFieldId(f->GetFieldName(), GetOnDiskId()));
1267 }
1268 f->ConnectPageSource(pageSource);
1269 }
1270
1271 {
1272 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
1273 const RNTupleDescriptor &desc = descriptorGuard.GetRef();
1274 GenerateColumns(desc);
1275 if (fColumnRepresentatives.empty()) {
1276 // If we didn't get columns from the descriptor, ensure that we actually expect a field without columns
1277 for (const auto &t : GetColumnRepresentations().GetDeserializationTypes()) {
1278 if (t.empty()) {
1279 fColumnRepresentatives = {t};
1280 break;
1281 }
1282 }
1283 }
1284 R__ASSERT(!fColumnRepresentatives.empty());
1285 if (fOnDiskId != kInvalidDescriptorId) {
1286 const auto &fieldDesc = desc.GetFieldDescriptor(fOnDiskId);
1287 fOnDiskTypeVersion = fieldDesc.GetTypeVersion();
1288 if (fieldDesc.GetTypeChecksum().has_value())
1289 fOnDiskTypeChecksum = *fieldDesc.GetTypeChecksum();
1290 }
1291 }
1292 for (auto &column : fAvailableColumns)
1293 column->ConnectPageSource(fOnDiskId, pageSource);
1294 OnConnectPageSource();
1295
1296 fState = EState::kConnectedToSource;
1297}
1298
1300{
1301 visitor.VisitField(*this);
1302}
1303
1304//-----------------------------------------------------------------------------
1305
1306std::unique_ptr<ROOT::Experimental::RFieldBase>
1307ROOT::Experimental::RFieldZero::CloneImpl(std::string_view /*newName*/) const
1308{
1309 auto result = std::make_unique<RFieldZero>();
1310 for (auto &f : fSubFields)
1311 result->Attach(f->Clone(f->GetFieldName()));
1312 return result;
1313}
1314
1316{
1317 visitor.VisitFieldZero(*this);
1318}
1319
1320//------------------------------------------------------------------------------
1321
1324{
1325 static RColumnRepresentations representations(
1327 {});
1328 return representations;
1329}
1330
1332{
1333 GenerateColumnsImpl<ClusterSize_t>(desc);
1334}
1335
1337{
1338 visitor.VisitCardinalityField(*this);
1339}
1340
1343{
1344 return dynamic_cast<const RField<RNTupleCardinality<std::uint32_t>> *>(this);
1345}
1346
1349{
1350 return dynamic_cast<const RField<RNTupleCardinality<std::uint64_t>> *>(this);
1351}
1352
1353//------------------------------------------------------------------------------
1354
1356
1359{
1360 static RColumnRepresentations representations({{EColumnType::kChar}}, {{EColumnType::kInt8},
1375 return representations;
1376}
1377
1379{
1380 visitor.VisitCharField(*this);
1381}
1382
1383//------------------------------------------------------------------------------
1384
1386
1389{
1390 static RColumnRepresentations representations({{EColumnType::kByte}}, {});
1391 return representations;
1392}
1393
1394void ROOT::Experimental::RField<std::byte>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1395{
1396 visitor.VisitByteField(*this);
1397}
1398
1399//------------------------------------------------------------------------------
1400
1402
1405{
1406 static RColumnRepresentations representations({{EColumnType::kInt8}}, {{EColumnType::kChar},
1421 return representations;
1422}
1423
1424void ROOT::Experimental::RIntegralField<std::int8_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1425{
1426 visitor.VisitInt8Field(*this);
1427}
1428
1429//------------------------------------------------------------------------------
1430
1432
1435{
1436 static RColumnRepresentations representations({{EColumnType::kUInt8}}, {{EColumnType::kChar},
1451 return representations;
1452}
1453
1454void ROOT::Experimental::RIntegralField<std::uint8_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1455{
1456 visitor.VisitUInt8Field(*this);
1457}
1458
1459//------------------------------------------------------------------------------
1460
1462
1465{
1466 static RColumnRepresentations representations({{EColumnType::kBit}}, {{EColumnType::kChar},
1481 return representations;
1482}
1483
1485{
1486 visitor.VisitBoolField(*this);
1487}
1488
1489//------------------------------------------------------------------------------
1490
1492
1495{
1496 static RColumnRepresentations representations({{EColumnType::kSplitReal32},
1502 return representations;
1503}
1504
1506{
1507 visitor.VisitFloatField(*this);
1508}
1509
1510//------------------------------------------------------------------------------
1511
1513
1516{
1517 static RColumnRepresentations representations({{EColumnType::kSplitReal64},
1524 {});
1525 return representations;
1526}
1527
1529{
1530 visitor.VisitDoubleField(*this);
1531}
1532
1534{
1535 fTypeAlias = "Double32_t";
1536}
1537
1538//------------------------------------------------------------------------------
1539
1541
1544{
1545 static RColumnRepresentations representations({{EColumnType::kSplitInt16}, {EColumnType::kInt16}},
1560 return representations;
1561}
1562
1563void ROOT::Experimental::RIntegralField<std::int16_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1564{
1565 visitor.VisitInt16Field(*this);
1566}
1567
1568//------------------------------------------------------------------------------
1569
1571
1574{
1575 static RColumnRepresentations representations({{EColumnType::kSplitUInt16}, {EColumnType::kUInt16}},
1590 return representations;
1591}
1592
1593void ROOT::Experimental::RIntegralField<std::uint16_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1594{
1595 visitor.VisitUInt16Field(*this);
1596}
1597
1598//------------------------------------------------------------------------------
1599
1601
1604{
1605 static RColumnRepresentations representations({{EColumnType::kSplitInt32}, {EColumnType::kInt32}},
1620 return representations;
1621}
1622
1623void ROOT::Experimental::RIntegralField<std::int32_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1624{
1625 visitor.VisitInt32Field(*this);
1626}
1627
1628//------------------------------------------------------------------------------
1629
1631
1634{
1635 static RColumnRepresentations representations({{EColumnType::kSplitUInt32}, {EColumnType::kUInt32}},
1650 return representations;
1651}
1652
1653void ROOT::Experimental::RIntegralField<std::uint32_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1654{
1655 visitor.VisitUInt32Field(*this);
1656}
1657
1658//------------------------------------------------------------------------------
1659
1661
1664{
1665 static RColumnRepresentations representations({{EColumnType::kSplitUInt64}, {EColumnType::kUInt64}},
1680 return representations;
1681}
1682
1683void ROOT::Experimental::RIntegralField<std::uint64_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1684{
1685 visitor.VisitUInt64Field(*this);
1686}
1687
1688//------------------------------------------------------------------------------
1689
1691
1694{
1695 static RColumnRepresentations representations({{EColumnType::kSplitInt64}, {EColumnType::kInt64}},
1710 return representations;
1711}
1712
1713void ROOT::Experimental::RIntegralField<std::int64_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1714{
1715 visitor.VisitInt64Field(*this);
1716}
1717
1718//------------------------------------------------------------------------------
1719
1722{
1723 static RColumnRepresentations representations({{EColumnType::kSplitIndex64, EColumnType::kChar},
1727 {});
1728 return representations;
1729}
1730
1732{
1733 GenerateColumnsImpl<ClusterSize_t, char>();
1734}
1735
1736void ROOT::Experimental::RField<std::string>::GenerateColumns(const RNTupleDescriptor &desc)
1737{
1738 GenerateColumnsImpl<ClusterSize_t, char>(desc);
1739}
1740
1741std::size_t ROOT::Experimental::RField<std::string>::AppendImpl(const void *from)
1742{
1743 auto typedValue = static_cast<const std::string *>(from);
1744 auto length = typedValue->length();
1745 fAuxiliaryColumn->AppendV(typedValue->data(), length);
1746 fIndex += length;
1747 fPrincipalColumn->Append(&fIndex);
1748 return length + fPrincipalColumn->GetElement()->GetPackedSize();
1749}
1750
1752{
1753 auto typedValue = static_cast<std::string *>(to);
1754 RClusterIndex collectionStart;
1755 ClusterSize_t nChars;
1756 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nChars);
1757 if (nChars == 0) {
1758 typedValue->clear();
1759 } else {
1760 typedValue->resize(nChars);
1761 fAuxiliaryColumn->ReadV(collectionStart, nChars, const_cast<char *>(typedValue->data()));
1762 }
1763}
1764
1765void ROOT::Experimental::RField<std::string>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1766{
1767 visitor.VisitStringField(*this);
1768}
1769
1770//------------------------------------------------------------------------------
1771
1772ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, const RClassField &source)
1773 : ROOT::Experimental::RFieldBase(fieldName, source.GetTypeName(), ENTupleStructure::kRecord, false /* isSimple */),
1774 fClass(source.fClass),
1775 fSubFieldsInfo(source.fSubFieldsInfo),
1776 fMaxAlignment(source.fMaxAlignment)
1777{
1778 for (const auto &f : source.GetSubFields()) {
1779 RFieldBase::Attach(f->Clone(f->GetFieldName()));
1780 }
1781 fTraits = source.GetTraits();
1782}
1783
1784ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::string_view className)
1785 : RClassField(fieldName, className, TClass::GetClass(std::string(className).c_str()))
1786{
1787}
1788
1789ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::string_view className, TClass *classp)
1790 : ROOT::Experimental::RFieldBase(fieldName, className, ENTupleStructure::kRecord, false /* isSimple */),
1791 fClass(classp)
1792{
1793 if (fClass == nullptr) {
1794 throw RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
1795 }
1796 // Avoid accidentally supporting std types through TClass.
1797 if (fClass->Property() & kIsDefinedInStd) {
1798 throw RException(R__FAIL(std::string(className) + " is not supported"));
1799 }
1800 if (className == "TObject") {
1801 throw RException(R__FAIL("TObject is only supported through RField<TObject>"));
1802 }
1803 if (fClass->GetCollectionProxy()) {
1804 throw RException(
1805 R__FAIL(std::string(className) + " has an associated collection proxy; use RProxiedCollectionField instead"));
1806 }
1807 // Classes with, e.g., custom streamers are not supported through this field. Empty classes, however, are.
1808 // Can be overwritten with the "rntuple.streamerMode=true" class attribute
1809 if (!fClass->CanSplit() && fClass->Size() > 1 &&
1810 GetRNTupleSerializationMode(fClass) != ERNTupleSerializationMode::kForceNativeMode) {
1811 throw RException(R__FAIL(std::string(className) + " cannot be stored natively in RNTuple"));
1812 }
1813 if (GetRNTupleSerializationMode(fClass) == ERNTupleSerializationMode::kForceStreamerMode) {
1814 throw RException(
1815 R__FAIL(std::string(className) + " has streamer mode enforced, not supported as native RNTuple class"));
1816 }
1817
1822
1823 int i = 0;
1825 if (baseClass->GetDelta() < 0) {
1826 throw RException(R__FAIL(std::string("virtual inheritance is not supported: ") + std::string(className) +
1827 " virtually inherits from " + baseClass->GetName()));
1828 }
1829 TClass *c = baseClass->GetClassPointer();
1830 auto subField =
1831 RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i), c->GetName()).Unwrap();
1832 fTraits &= subField->GetTraits();
1833 Attach(std::move(subField), RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
1834 i++;
1835 }
1837 // Skip, for instance, unscoped enum constants defined in the class
1838 if (dataMember->Property() & kIsStatic)
1839 continue;
1840 // Skip members explicitly marked as transient by user comment
1841 if (!dataMember->IsPersistent()) {
1842 // TODO(jblomer): we could do better
1844 continue;
1845 }
1846
1847 std::string typeName{GetNormalizedTypeName(dataMember->GetTrueTypeName())};
1848 std::string typeAlias{GetNormalizedTypeName(dataMember->GetFullTypeName())};
1849
1850 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
1851 if (dataMember->Property() & kIsArray) {
1852 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim)
1853 typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
1854 }
1855
1856 std::unique_ptr<RFieldBase> subField;
1857
1858 subField = RFieldBase::Create(dataMember->GetName(), typeName, typeAlias).Unwrap();
1859 fTraits &= subField->GetTraits();
1860 Attach(std::move(subField), RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
1861 }
1863}
1864
1865void ROOT::Experimental::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubFieldInfo info)
1866{
1867 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
1868 fSubFieldsInfo.push_back(info);
1869 RFieldBase::Attach(std::move(child));
1870}
1871
1872void ROOT::Experimental::RClassField::AddReadCallbacksFromIORules(const std::span<const ROOT::TSchemaRule *> rules,
1873 TClass *classp)
1874{
1875 for (const auto rule : rules) {
1876 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
1877 R__LOG_WARNING(NTupleLog()) << "ignoring I/O customization rule with unsupported type";
1878 continue;
1879 }
1880 auto func = rule->GetReadFunctionPointer();
1881 R__ASSERT(func != nullptr);
1882 fReadCallbacks.emplace_back([func, classp](void *target) {
1883 TVirtualObject oldObj{nullptr};
1884 oldObj.fClass = classp;
1885 oldObj.fObject = target;
1886 func(static_cast<char *>(target), &oldObj);
1887 oldObj.fClass = nullptr; // TVirtualObject does not own the value
1888 });
1889 }
1890}
1891
1892std::unique_ptr<ROOT::Experimental::RFieldBase>
1893ROOT::Experimental::RClassField::CloneImpl(std::string_view newName) const
1894{
1895 return std::unique_ptr<RClassField>(new RClassField(newName, *this));
1896}
1897
1899{
1900 std::size_t nbytes = 0;
1901 for (unsigned i = 0; i < fSubFields.size(); i++) {
1902 nbytes += CallAppendOn(*fSubFields[i], static_cast<const unsigned char *>(from) + fSubFieldsInfo[i].fOffset);
1903 }
1904 return nbytes;
1905}
1906
1908{
1909 for (unsigned i = 0; i < fSubFields.size(); i++) {
1910 CallReadOn(*fSubFields[i], globalIndex, static_cast<unsigned char *>(to) + fSubFieldsInfo[i].fOffset);
1911 }
1912}
1913
1915{
1916 for (unsigned i = 0; i < fSubFields.size(); i++) {
1917 CallReadOn(*fSubFields[i], clusterIndex, static_cast<unsigned char *>(to) + fSubFieldsInfo[i].fOffset);
1918 }
1919}
1920
1922{
1923 // Add post-read callbacks for I/O customization rules; only rules that target transient members are allowed for now
1924 // TODO(jalopezg): revise after supporting schema evolution
1925 const auto ruleset = fClass->GetSchemaRules();
1926 if (!ruleset)
1927 return;
1928 auto referencesNonTransientMembers = [klass = fClass](const ROOT::TSchemaRule *rule) {
1929 if (rule->GetTarget() == nullptr)
1930 return false;
1931 for (auto target : ROOT::Detail::TRangeStaticCast<TObjString>(*rule->GetTarget())) {
1932 const auto dataMember = klass->GetDataMember(target->GetString());
1933 if (!dataMember || dataMember->IsPersistent()) {
1934 R__LOG_WARNING(NTupleLog()) << "ignoring I/O customization rule with non-transient member: "
1935 << dataMember->GetName();
1936 return true;
1937 }
1938 }
1939 return false;
1940 };
1941
1942 auto rules = ruleset->FindRules(fClass->GetName(), static_cast<Int_t>(GetOnDiskTypeVersion()),
1943 static_cast<UInt_t>(GetOnDiskTypeChecksum()));
1944 rules.erase(std::remove_if(rules.begin(), rules.end(), referencesNonTransientMembers), rules.end());
1945 AddReadCallbacksFromIORules(rules, fClass);
1946}
1947
1949{
1950 fClass->New(where);
1951}
1952
1954{
1955 fClass->Destructor(objPtr, true /* dtorOnly */);
1956 RDeleter::operator()(objPtr, dtorOnly);
1957}
1958
1959std::vector<ROOT::Experimental::RFieldBase::RValue>
1961{
1962 std::vector<RValue> result;
1963 auto basePtr = value.GetPtr<unsigned char>().get();
1964 result.reserve(fSubFields.size());
1965 for (unsigned i = 0; i < fSubFields.size(); i++) {
1966 result.emplace_back(
1967 fSubFields[i]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + fSubFieldsInfo[i].fOffset)));
1968 }
1969 return result;
1970}
1971
1973{
1974 return fClass->GetClassSize();
1975}
1976
1978{
1979 return fClass->GetClassVersion();
1980}
1981
1983{
1984 return fClass->GetCheckSum();
1985}
1986
1988{
1989 visitor.VisitClassField(*this);
1990}
1991
1992//------------------------------------------------------------------------------
1993
1995{
1996 if (auto dataMember = TObject::Class()->GetDataMember(name)) {
1997 return dataMember->GetOffset();
1998 }
1999 throw RException(R__FAIL('\'' + std::string(name) + '\'' + " is an invalid data member"));
2000}
2001
2002ROOT::Experimental::RField<TObject>::RField(std::string_view fieldName, const RField<TObject> &source)
2003 : ROOT::Experimental::RFieldBase(fieldName, "TObject", ENTupleStructure::kRecord, false /* isSimple */)
2004{
2006 Attach(source.GetSubFields()[0]->Clone("fUniqueID"));
2007 Attach(source.GetSubFields()[1]->Clone("fBits"));
2008}
2009
2011 : ROOT::Experimental::RFieldBase(fieldName, "TObject", ENTupleStructure::kRecord, false /* isSimple */)
2012{
2013 assert(TObject::Class()->GetClassVersion() == 1);
2014
2016 Attach(std::make_unique<RField<UInt_t>>("fUniqueID"));
2017 Attach(std::make_unique<RField<UInt_t>>("fBits"));
2018}
2019
2020std::unique_ptr<ROOT::Experimental::RFieldBase>
2022{
2023 return std::unique_ptr<RField<TObject>>(new RField<TObject>(newName, *this));
2024}
2025
2027{
2028 // Cf. TObject::Streamer()
2029
2030 auto *obj = static_cast<const TObject *>(from);
2031 if (obj->TestBit(TObject::kIsReferenced)) {
2032 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
2033 }
2034
2035 std::size_t nbytes = 0;
2036 nbytes += CallAppendOn(*fSubFields[0], reinterpret_cast<const unsigned char *>(from) + GetOffsetUniqueID());
2037
2038 UInt_t bits = *reinterpret_cast<const UInt_t *>(reinterpret_cast<const unsigned char *>(from) + GetOffsetBits());
2039 bits &= (~TObject::kIsOnHeap & ~TObject::kNotDeleted);
2040 nbytes += CallAppendOn(*fSubFields[1], &bits);
2041
2042 return nbytes;
2043}
2044
2046{
2047 // Cf. TObject::Streamer()
2048
2049 auto *obj = static_cast<TObject *>(to);
2050 if (obj->TestBit(TObject::kIsReferenced)) {
2051 throw RException(R__FAIL("RNTuple I/O on referenced TObject is unsupported"));
2052 }
2053
2054 CallReadOn(*fSubFields[0], globalIndex, static_cast<unsigned char *>(to) + GetOffsetUniqueID());
2055
2056 const UInt_t bitIsOnHeap = obj->TestBit(TObject::kIsOnHeap) ? TObject::kIsOnHeap : 0;
2057 UInt_t bits;
2058 CallReadOn(*fSubFields[1], globalIndex, &bits);
2059 bits |= bitIsOnHeap | TObject::kNotDeleted;
2060 *reinterpret_cast<UInt_t *>(reinterpret_cast<unsigned char *>(to) + GetOffsetBits()) = bits;
2061}
2062
2064{
2065 if (GetTypeVersion() != 1) {
2066 throw RException(R__FAIL("unsupported on-disk version of TObject: " + std::to_string(GetTypeVersion())));
2067 }
2068}
2069
2071{
2072 return TObject::Class()->GetClassVersion();
2073}
2074
2076{
2077 return TObject::Class()->GetCheckSum();
2078}
2079
2081{
2082 new (where) TObject();
2083}
2084
2085std::vector<ROOT::Experimental::RFieldBase::RValue>
2087{
2088 std::vector<RValue> result;
2089 auto basePtr = value.GetPtr<unsigned char>().get();
2090 result.emplace_back(
2091 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + GetOffsetUniqueID())));
2092 result.emplace_back(
2093 fSubFields[1]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + GetOffsetBits())));
2094 return result;
2095}
2096
2098{
2099 return sizeof(TObject);
2100}
2101
2103{
2104 return alignof(TObject);
2105}
2106
2108{
2109 visitor.VisitTObjectField(*this);
2110}
2111
2112//------------------------------------------------------------------------------
2113
2114ROOT::Experimental::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className,
2115 std::string_view typeAlias)
2116 : RStreamerField(fieldName, className, TClass::GetClass(std::string(className).c_str()))
2117{
2118 fTypeAlias = typeAlias;
2119}
2120
2121ROOT::Experimental::RStreamerField::RStreamerField(std::string_view fieldName, std::string_view className,
2122 TClass *classp)
2123 : ROOT::Experimental::RFieldBase(fieldName, className, ENTupleStructure::kStreamer, false /* isSimple */),
2124 fClass(classp),
2125 fIndex(0)
2126{
2127 if (fClass == nullptr) {
2128 throw RException(R__FAIL("RStreamerField: no I/O support for type " + std::string(className)));
2129 }
2130
2136}
2137
2138std::unique_ptr<ROOT::Experimental::RFieldBase>
2140{
2141 return std::unique_ptr<RStreamerField>(new RStreamerField(newName, GetTypeName(), GetTypeAlias()));
2142}
2143
2145{
2146 TBufferRecStreamer buffer(TBuffer::kWrite, GetValueSize(),
2147 [this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
2148 fClass->Streamer(const_cast<void *>(from), buffer);
2149
2150 auto nbytes = buffer.Length();
2151 fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
2152 fIndex += nbytes;
2153 fPrincipalColumn->Append(&fIndex);
2154 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2155}
2156
2158{
2159 RClusterIndex collectionStart;
2160 ClusterSize_t nbytes;
2161 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
2162
2163 TBufferFile buffer(TBuffer::kRead, nbytes);
2164 fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
2165 fClass->Streamer(to, buffer);
2166}
2167
2170{
2175 {});
2176 return representations;
2177}
2178
2180{
2181 GenerateColumnsImpl<ClusterSize_t, std::byte>();
2182}
2183
2185{
2186 GenerateColumnsImpl<ClusterSize_t, std::byte>(desc);
2187}
2188
2190{
2191 fClass->New(where);
2192}
2193
2195{
2196 fClass->Destructor(objPtr, true /* dtorOnly */);
2197 RDeleter::operator()(objPtr, dtorOnly);
2198}
2199
2201{
2202 Internal::RExtraTypeInfoDescriptorBuilder extraTypeInfoBuilder;
2203 extraTypeInfoBuilder.ContentId(EExtraTypeInfoIds::kStreamerInfo)
2204 .TypeVersion(GetTypeVersion())
2205 .TypeName(GetTypeName())
2207 return extraTypeInfoBuilder.MoveDescriptor().Unwrap();
2208}
2209
2211{
2212 return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
2213}
2214
2216{
2217 return fClass->GetClassSize();
2218}
2219
2221{
2222 return fClass->GetClassVersion();
2223}
2224
2226{
2227 return fClass->GetCheckSum();
2228}
2229
2231{
2232 visitor.VisitStreamerField(*this);
2233}
2234
2235//------------------------------------------------------------------------------
2236
2237ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
2238 : REnumField(fieldName, enumName, TEnum::GetEnum(std::string(enumName).c_str()))
2239{
2240}
2241
2242ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName, TEnum *enump)
2243 : ROOT::Experimental::RFieldBase(fieldName, enumName, ENTupleStructure::kLeaf, false /* isSimple */)
2244{
2245 if (enump == nullptr) {
2246 throw RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
2247 }
2248 // Avoid accidentally supporting std types through TEnum.
2249 if (enump->Property() & kIsDefinedInStd) {
2250 throw RException(R__FAIL(std::string(enumName) + " is not supported"));
2251 }
2252
2253 switch (enump->GetUnderlyingType()) {
2254 case kChar_t: Attach(std::make_unique<RField<int8_t>>("_0")); break;
2255 case kUChar_t: Attach(std::make_unique<RField<uint8_t>>("_0")); break;
2256 case kShort_t: Attach(std::make_unique<RField<int16_t>>("_0")); break;
2257 case kUShort_t: Attach(std::make_unique<RField<uint16_t>>("_0")); break;
2258 case kInt_t: Attach(std::make_unique<RField<int32_t>>("_0")); break;
2259 case kUInt_t: Attach(std::make_unique<RField<uint32_t>>("_0")); break;
2260 case kLong_t:
2261 case kLong64_t: Attach(std::make_unique<RField<int64_t>>("_0")); break;
2262 case kULong_t:
2263 case kULong64_t: Attach(std::make_unique<RField<uint64_t>>("_0")); break;
2264 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + std::string(enumName)));
2265 }
2266
2268}
2269
2270ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
2271 std::unique_ptr<RFieldBase> intField)
2272 : ROOT::Experimental::RFieldBase(fieldName, enumName, ENTupleStructure::kLeaf, false /* isSimple */)
2273{
2274 Attach(std::move(intField));
2276}
2277
2278std::unique_ptr<ROOT::Experimental::RFieldBase>
2279ROOT::Experimental::REnumField::CloneImpl(std::string_view newName) const
2280{
2281 auto newIntField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2282 return std::unique_ptr<REnumField>(new REnumField(newName, GetTypeName(), std::move(newIntField)));
2283}
2284
2285std::vector<ROOT::Experimental::RFieldBase::RValue>
2287{
2288 std::vector<RValue> result;
2289 result.emplace_back(fSubFields[0]->BindValue(value.GetPtr<void>()));
2290 return result;
2291}
2292
2294{
2295 visitor.VisitEnumField(*this);
2296}
2297
2298//------------------------------------------------------------------------------
2299
2302 bool readFromDisk)
2303{
2304 RIteratorFuncs ifuncs;
2305 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
2306 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
2307 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
2308 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
2309 (ifuncs.fNext != nullptr));
2310 return ifuncs;
2311}
2312
2314 std::string_view typeName, TClass *classp)
2315 : RFieldBase(fieldName, typeName, ENTupleStructure::kCollection, false /* isSimple */), fNWritten(0)
2316{
2317 if (classp == nullptr)
2318 throw RException(R__FAIL("RField: no I/O support for collection proxy type " + std::string(typeName)));
2319 if (!classp->GetCollectionProxy())
2320 throw RException(R__FAIL(std::string(typeName) + " has no associated collection proxy"));
2321
2322 fProxy.reset(classp->GetCollectionProxy()->Generate());
2323 fProperties = fProxy->GetProperties();
2324 fCollectionType = fProxy->GetCollectionType();
2325 if (fProxy->HasPointers())
2326 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
2327 if (!fProxy->GetCollectionClass()->HasDictionary()) {
2328 throw RException(R__FAIL("dictionary not available for type " +
2329 GetNormalizedTypeName(fProxy->GetCollectionClass()->GetName())));
2330 }
2331
2332 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
2333 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
2334}
2335
2337 std::string_view typeName,
2338 std::unique_ptr<RFieldBase> itemField)
2339 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
2340{
2341 fItemSize = itemField->GetValueSize();
2342 Attach(std::move(itemField));
2343}
2344
2346 std::string_view typeName)
2347 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
2348{
2349 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
2351 throw RException(R__FAIL("custom associative collection proxies not supported"));
2352
2353 std::unique_ptr<ROOT::Experimental::RFieldBase> itemField;
2354
2355 if (auto valueClass = fProxy->GetValueClass()) {
2356 // Element type is a class
2357 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
2358 } else {
2359 switch (fProxy->GetType()) {
2360 case EDataType::kChar_t: itemField = std::make_unique<RField<char>>("_0"); break;
2361 case EDataType::kUChar_t: itemField = std::make_unique<RField<std::uint8_t>>("_0"); break;
2362 case EDataType::kShort_t: itemField = std::make_unique<RField<std::int16_t>>("_0"); break;
2363 case EDataType::kUShort_t: itemField = std::make_unique<RField<std::uint16_t>>("_0"); break;
2364 case EDataType::kInt_t: itemField = std::make_unique<RField<std::int32_t>>("_0"); break;
2365 case EDataType::kUInt_t: itemField = std::make_unique<RField<std::uint32_t>>("_0"); break;
2366 case EDataType::kLong_t:
2367 case EDataType::kLong64_t: itemField = std::make_unique<RField<std::int64_t>>("_0"); break;
2369 case EDataType::kULong64_t: itemField = std::make_unique<RField<std::uint64_t>>("_0"); break;
2370 case EDataType::kFloat_t: itemField = std::make_unique<RField<float>>("_0"); break;
2371 case EDataType::kDouble_t: itemField = std::make_unique<RField<double>>("_0"); break;
2372 case EDataType::kBool_t: itemField = std::make_unique<RField<bool>>("_0"); break;
2373 default: throw RException(R__FAIL("unsupported value type"));
2374 }
2375 }
2376
2377 fItemSize = itemField->GetValueSize();
2378 Attach(std::move(itemField));
2379}
2380
2381std::unique_ptr<ROOT::Experimental::RFieldBase>
2383{
2384 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2385 return std::unique_ptr<RProxiedCollectionField>(
2386 new RProxiedCollectionField(newName, GetTypeName(), std::move(newItemField)));
2387}
2388
2390{
2391 std::size_t nbytes = 0;
2392 unsigned count = 0;
2393 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
2394 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
2395 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
2396 nbytes += CallAppendOn(*fSubFields[0], ptr);
2397 count++;
2398 }
2399
2400 fNWritten += count;
2401 fPrincipalColumn->Append(&fNWritten);
2402 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2403}
2404
2406{
2407 ClusterSize_t nItems;
2408 RClusterIndex collectionStart;
2409 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2410
2411 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
2412 void *obj =
2413 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
2414
2415 unsigned i = 0;
2416 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
2417 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
2418 CallReadOn(*fSubFields[0], collectionStart + (i++), elementPtr);
2419 }
2420 if (obj != to)
2421 fProxy->Commit(obj);
2422}
2423
2426{
2427 static RColumnRepresentations representations(
2429 {});
2430 return representations;
2431}
2432
2434{
2435 GenerateColumnsImpl<ClusterSize_t>();
2436}
2437
2439{
2440 GenerateColumnsImpl<ClusterSize_t>(desc);
2441}
2442
2444{
2445 fProxy->New(where);
2446}
2447
2448std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter>
2450{
2451 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
2452 std::size_t itemSize = fCollectionType == kSTLvector ? fItemSize : 0U;
2453 return std::make_unique<RProxiedCollectionDeleter>(fProxy, GetDeleterOf(*fSubFields[0]), itemSize);
2454 }
2455 return std::make_unique<RProxiedCollectionDeleter>(fProxy);
2456}
2457
2459{
2460 if (fItemDeleter) {
2461 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), objPtr);
2462 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(), fItemSize}) {
2463 fItemDeleter->operator()(ptr, true /* dtorOnly */);
2464 }
2465 }
2466 fProxy->Destructor(objPtr, true /* dtorOnly */);
2467 RDeleter::operator()(objPtr, dtorOnly);
2468}
2469
2470std::vector<ROOT::Experimental::RFieldBase::RValue>
2472{
2473 std::vector<RValue> result;
2474 auto valueRawPtr = value.GetPtr<void>().get();
2475 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), valueRawPtr);
2476 for (auto ptr : RCollectionIterableOnce{valueRawPtr, fIFuncsWrite, fProxy.get(),
2477 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
2478 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr)));
2479 }
2480 return result;
2481}
2482
2484{
2485 visitor.VisitProxiedCollectionField(*this);
2486}
2487
2488//------------------------------------------------------------------------------
2489
2491 : ROOT::Experimental::RFieldBase(name, source.GetTypeName(), ENTupleStructure::kRecord, false /* isSimple */),
2492 fMaxAlignment(source.fMaxAlignment),
2493 fSize(source.fSize),
2494 fOffsets(source.fOffsets)
2495{
2496 for (const auto &f : source.GetSubFields())
2497 Attach(f->Clone(f->GetFieldName()));
2498 fTraits = source.fTraits;
2499}
2500
2501ROOT::Experimental::RRecordField::RRecordField(std::string_view fieldName, std::string_view typeName)
2502 : ROOT::Experimental::RFieldBase(fieldName, typeName, ENTupleStructure::kRecord, false /* isSimple */)
2503{
2504}
2505
2506void ROOT::Experimental::RRecordField::RRecordField::AttachItemFields(
2507 std::vector<std::unique_ptr<RFieldBase>> itemFields)
2508{
2509 fTraits |= kTraitTrivialType;
2510 for (auto &item : itemFields) {
2511 fMaxAlignment = std::max(fMaxAlignment, item->GetAlignment());
2512 fSize += GetItemPadding(fSize, item->GetAlignment()) + item->GetValueSize();
2513 fTraits &= item->GetTraits();
2514 Attach(std::move(item));
2515 }
2516 // Trailing padding: although this is implementation-dependent, most add enough padding to comply with the
2517 // requirements of the type with strictest alignment
2518 fSize += GetItemPadding(fSize, fMaxAlignment);
2519}
2520
2522 std::vector<std::unique_ptr<RFieldBase>> itemFields)
2523 : ROOT::Experimental::RFieldBase(fieldName, "", ENTupleStructure::kRecord, false /* isSimple */)
2524{
2526 fOffsets.reserve(itemFields.size());
2527 for (auto &item : itemFields) {
2528 fSize += GetItemPadding(fSize, item->GetAlignment());
2529 fOffsets.push_back(fSize);
2530 fMaxAlignment = std::max(fMaxAlignment, item->GetAlignment());
2531 fSize += item->GetValueSize();
2532 fTraits &= item->GetTraits();
2533 Attach(std::move(item));
2534 }
2535 // Trailing padding: although this is implementation-dependent, most add enough padding to comply with the
2536 // requirements of the type with strictest alignment
2538}
2539
2540std::size_t ROOT::Experimental::RRecordField::GetItemPadding(std::size_t baseOffset, std::size_t itemAlignment) const
2541{
2542 if (itemAlignment > 1) {
2543 auto remainder = baseOffset % itemAlignment;
2544 if (remainder != 0)
2545 return itemAlignment - remainder;
2546 }
2547 return 0;
2548}
2549
2550std::unique_ptr<ROOT::Experimental::RFieldBase>
2551ROOT::Experimental::RRecordField::CloneImpl(std::string_view newName) const
2552{
2553 return std::unique_ptr<RRecordField>(new RRecordField(newName, *this));
2554}
2555
2557{
2558 std::size_t nbytes = 0;
2559 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2560 nbytes += CallAppendOn(*fSubFields[i], static_cast<const unsigned char *>(from) + fOffsets[i]);
2561 }
2562 return nbytes;
2563}
2564
2566{
2567 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2568 CallReadOn(*fSubFields[i], globalIndex, static_cast<unsigned char *>(to) + fOffsets[i]);
2569 }
2570}
2571
2573{
2574 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2575 CallReadOn(*fSubFields[i], clusterIndex, static_cast<unsigned char *>(to) + fOffsets[i]);
2576 }
2577}
2578
2580{
2581 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2582 CallConstructValueOn(*fSubFields[i], static_cast<unsigned char *>(where) + fOffsets[i]);
2583 }
2584}
2585
2587{
2588 for (unsigned i = 0; i < fItemDeleters.size(); ++i) {
2589 fItemDeleters[i]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fOffsets[i], true /* dtorOnly */);
2590 }
2591 RDeleter::operator()(objPtr, dtorOnly);
2592}
2593
2594std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RRecordField::GetDeleter() const
2595{
2596 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
2597 itemDeleters.reserve(fOffsets.size());
2598 for (const auto &f : fSubFields) {
2599 itemDeleters.emplace_back(GetDeleterOf(*f));
2600 }
2601 return std::make_unique<RRecordDeleter>(std::move(itemDeleters), fOffsets);
2602}
2603
2604std::vector<ROOT::Experimental::RFieldBase::RValue>
2606{
2607 auto basePtr = value.GetPtr<unsigned char>().get();
2608 std::vector<RValue> result;
2609 result.reserve(fSubFields.size());
2610 for (unsigned i = 0; i < fSubFields.size(); ++i) {
2611 result.emplace_back(fSubFields[i]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), basePtr + fOffsets[i])));
2612 }
2613 return result;
2614}
2615
2617{
2618 visitor.VisitRecordField(*this);
2619}
2620
2621//------------------------------------------------------------------------------
2622
2623ROOT::Experimental::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
2624 bool isUntyped)
2625 : ROOT::Experimental::RFieldBase(fieldName, isUntyped ? "" : "std::vector<" + itemField->GetTypeName() + ">",
2626 ENTupleStructure::kCollection, false /* isSimple */),
2627 fItemSize(itemField->GetValueSize()),
2628 fNWritten(0)
2629{
2630 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
2631 fItemDeleter = GetDeleterOf(*itemField);
2632 Attach(std::move(itemField));
2633}
2634
2635ROOT::Experimental::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
2636 : RVectorField(fieldName, std::move(itemField), false)
2637{
2638}
2639
2640std::unique_ptr<ROOT::Experimental::RVectorField>
2641ROOT::Experimental::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
2642{
2643 return std::unique_ptr<ROOT::Experimental::RVectorField>(new RVectorField(fieldName, std::move(itemField), true));
2644}
2645
2646std::unique_ptr<ROOT::Experimental::RFieldBase>
2647ROOT::Experimental::RVectorField::CloneImpl(std::string_view newName) const
2648{
2649 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2650 return std::unique_ptr<ROOT::Experimental::RVectorField>(
2651 new RVectorField(newName, std::move(newItemField), GetTypeName().empty()));
2652}
2653
2655{
2656 auto typedValue = static_cast<const std::vector<char> *>(from);
2657 R__ASSERT((typedValue->size() % fItemSize) == 0);
2658 std::size_t nbytes = 0;
2659 auto count = typedValue->size() / fItemSize;
2660
2661 if (fSubFields[0]->IsSimple() && count) {
2662 GetPrincipalColumnOf(*fSubFields[0])->AppendV(typedValue->data(), count);
2663 nbytes += count * GetPrincipalColumnOf(*fSubFields[0])->GetElement()->GetPackedSize();
2664 } else {
2665 for (unsigned i = 0; i < count; ++i) {
2666 nbytes += CallAppendOn(*fSubFields[0], typedValue->data() + (i * fItemSize));
2667 }
2668 }
2669
2670 fNWritten += count;
2671 fPrincipalColumn->Append(&fNWritten);
2672 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2673}
2674
2676{
2677 auto typedValue = static_cast<std::vector<char> *>(to);
2678
2679 ClusterSize_t nItems;
2680 RClusterIndex collectionStart;
2681 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2682
2683 if (fSubFields[0]->IsSimple()) {
2684 typedValue->resize(nItems * fItemSize);
2685 if (nItems)
2686 GetPrincipalColumnOf(*fSubFields[0])->ReadV(collectionStart, nItems, typedValue->data());
2687 return;
2688 }
2689
2690 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md
2691 const auto oldNItems = typedValue->size() / fItemSize;
2692 const bool canRealloc = oldNItems < nItems;
2693 bool allDeallocated = false;
2694 if (fItemDeleter) {
2695 allDeallocated = canRealloc;
2696 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
2697 fItemDeleter->operator()(typedValue->data() + (i * fItemSize), true /* dtorOnly */);
2698 }
2699 }
2700 typedValue->resize(nItems * fItemSize);
2701 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible)) {
2702 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
2703 CallConstructValueOn(*fSubFields[0], typedValue->data() + (i * fItemSize));
2704 }
2705 }
2706
2707 for (std::size_t i = 0; i < nItems; ++i) {
2708 CallReadOn(*fSubFields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
2709 }
2710}
2711
2714{
2715 static RColumnRepresentations representations(
2717 {});
2718 return representations;
2719}
2720
2722{
2723 GenerateColumnsImpl<ClusterSize_t>();
2724}
2725
2727{
2728 GenerateColumnsImpl<ClusterSize_t>(desc);
2729}
2730
2732{
2733 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
2734 if (fItemDeleter) {
2735 R__ASSERT((vecPtr->size() % fItemSize) == 0);
2736 auto nItems = vecPtr->size() / fItemSize;
2737 for (std::size_t i = 0; i < nItems; ++i) {
2738 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
2739 }
2740 }
2741 std::destroy_at(vecPtr);
2742 RDeleter::operator()(objPtr, dtorOnly);
2743}
2744
2745std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RVectorField::GetDeleter() const
2746{
2747 if (fItemDeleter)
2748 return std::make_unique<RVectorDeleter>(fItemSize, GetDeleterOf(*fSubFields[0]));
2749 return std::make_unique<RVectorDeleter>();
2750}
2751
2752std::vector<ROOT::Experimental::RFieldBase::RValue>
2754{
2755 auto vec = value.GetPtr<std::vector<char>>();
2756 R__ASSERT((vec->size() % fItemSize) == 0);
2757 auto nItems = vec->size() / fItemSize;
2758 std::vector<RValue> result;
2759 result.reserve(nItems);
2760 for (unsigned i = 0; i < nItems; ++i) {
2761 result.emplace_back(
2762 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), vec->data() + (i * fItemSize))));
2763 }
2764 return result;
2765}
2766
2768{
2769 visitor.VisitVectorField(*this);
2770}
2771
2772//------------------------------------------------------------------------------
2773
2774ROOT::Experimental::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
2775 : ROOT::Experimental::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
2776 ENTupleStructure::kCollection, false /* isSimple */),
2777 fItemSize(itemField->GetValueSize()),
2778 fNWritten(0)
2779{
2780 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
2781 fItemDeleter = GetDeleterOf(*itemField);
2782 Attach(std::move(itemField));
2783 fValueSize = EvalRVecValueSize(fSubFields[0]->GetAlignment(), fSubFields[0]->GetValueSize(), GetAlignment());
2784}
2785
2786std::unique_ptr<ROOT::Experimental::RFieldBase>
2787ROOT::Experimental::RRVecField::CloneImpl(std::string_view newName) const
2788{
2789 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
2790 return std::make_unique<RRVecField>(newName, std::move(newItemField));
2791}
2792
2794{
2795 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(from);
2796
2797 std::size_t nbytes = 0;
2798 if (fSubFields[0]->IsSimple() && *sizePtr) {
2799 GetPrincipalColumnOf(*fSubFields[0])->AppendV(*beginPtr, *sizePtr);
2800 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubFields[0])->GetElement()->GetPackedSize();
2801 } else {
2802 auto begin = reinterpret_cast<const char *>(*beginPtr); // for pointer arithmetics
2803 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2804 nbytes += CallAppendOn(*fSubFields[0], begin + i * fItemSize);
2805 }
2806 }
2807
2808 fNWritten += *sizePtr;
2809 fPrincipalColumn->Append(&fNWritten);
2810 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
2811}
2812
2814{
2815 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
2816 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
2817
2818 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(to);
2819
2820 // Read collection info for this entry
2821 ClusterSize_t nItems;
2822 RClusterIndex collectionStart;
2823 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2824 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2825 const std::size_t oldSize = *sizePtr;
2826
2827 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
2828 // on the element construction/destrution.
2829 const bool owns = (*capacityPtr != -1);
2830 const bool needsConstruct = !(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible);
2831 const bool needsDestruct = owns && fItemDeleter;
2832
2833 // Destroy excess elements, if any
2834 if (needsDestruct) {
2835 for (std::size_t i = nItems; i < oldSize; ++i) {
2836 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
2837 }
2838 }
2839
2840 // Resize RVec (capacity and size)
2841 if (std::int32_t(nItems) > *capacityPtr) { // must reallocate
2842 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
2843 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
2844 if (needsDestruct) {
2845 for (std::size_t i = 0u; i < oldSize; ++i) {
2846 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
2847 }
2848 }
2849
2850 // TODO Increment capacity by a factor rather than just enough to fit the elements.
2851 if (owns) {
2852 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
2853 free(*beginPtr);
2854 }
2855 // We trust that malloc returns a buffer with large enough alignment.
2856 // This might not be the case if T in RVec<T> is over-aligned.
2857 *beginPtr = malloc(nItems * fItemSize);
2858 R__ASSERT(*beginPtr != nullptr);
2859 begin = reinterpret_cast<char *>(*beginPtr);
2860 *capacityPtr = nItems;
2861
2862 // Placement new for elements that were already there before the resize
2863 if (needsConstruct) {
2864 for (std::size_t i = 0u; i < oldSize; ++i)
2865 CallConstructValueOn(*fSubFields[0], begin + (i * fItemSize));
2866 }
2867 }
2868 *sizePtr = nItems;
2869
2870 // Placement new for new elements, if any
2871 if (needsConstruct) {
2872 for (std::size_t i = oldSize; i < nItems; ++i)
2873 CallConstructValueOn(*fSubFields[0], begin + (i * fItemSize));
2874 }
2875
2876 if (fSubFields[0]->IsSimple() && nItems) {
2877 GetPrincipalColumnOf(*fSubFields[0])->ReadV(collectionStart, nItems, begin);
2878 return;
2879 }
2880
2881 // Read the new values into the collection elements
2882 for (std::size_t i = 0; i < nItems; ++i) {
2883 CallReadOn(*fSubFields[0], collectionStart + i, begin + (i * fItemSize));
2884 }
2885}
2886
2888{
2889 if (!fSubFields[0]->IsSimple())
2890 return RFieldBase::ReadBulkImpl(bulkSpec);
2891
2892 if (bulkSpec.fAuxData->empty()) {
2893 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
2894 /// The following bytes store the item values, consecutively.
2895 bulkSpec.fAuxData->resize(sizeof(std::size_t));
2896 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fSubFields[0]->GetValueSize();
2897 }
2898 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
2899 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
2900 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(bulkSpec.fValues);
2901
2902 // Get size of the first RVec of the bulk
2903 RClusterIndex firstItemIndex;
2904 RClusterIndex collectionStart;
2905 ClusterSize_t collectionSize;
2906 this->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
2907 *beginPtr = itemValueArray;
2908 *sizePtr = collectionSize;
2909 *capacityPtr = -1;
2910
2911 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
2912 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
2913 // given range. If not, we'll fix up the pointers afterwards.
2914 auto lastOffset = firstItemIndex.GetIndex() + collectionSize;
2915 ClusterSize_t::ValueType nRemainingValues = bulkSpec.fCount - 1;
2916 std::size_t nValues = 1;
2917 std::size_t nItems = collectionSize;
2918 while (nRemainingValues > 0) {
2919 NTupleSize_t nElementsUntilPageEnd;
2920 const auto offsets = fPrincipalColumn->MapV<ClusterSize_t>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
2921 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
2922 for (std::size_t i = 0; i < nBatch; ++i) {
2923 const auto size = offsets[i] - lastOffset;
2924 std::tie(beginPtr, sizePtr, capacityPtr) =
2925 GetRVecDataMembers(reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
2926 *beginPtr = itemValueArray + nItems * itemValueSize;
2927 *sizePtr = size;
2928 *capacityPtr = -1;
2929
2930 nItems += size;
2931 lastOffset = offsets[i];
2932 }
2933 nRemainingValues -= nBatch;
2934 nValues += nBatch;
2935 }
2936
2937 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
2938 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
2939 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
2940 if (delta != 0) {
2941 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
2942 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
2943 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
2944 beginPtrAsUChar += fValueSize;
2945 }
2946 }
2947
2948 GetPrincipalColumnOf(*fSubFields[0])->ReadV(firstItemIndex, nItems, itemValueArray - delta);
2949 return RBulkSpec::kAllSet;
2950}
2951
2954{
2955 static RColumnRepresentations representations(
2957 {});
2958 return representations;
2959}
2960
2962{
2963 GenerateColumnsImpl<ClusterSize_t>();
2964}
2965
2967{
2968 GenerateColumnsImpl<ClusterSize_t>(desc);
2969}
2970
2972{
2973 // initialize data members fBegin, fSize, fCapacity
2974 // currently the inline buffer is left uninitialized
2975 void **beginPtr = new (where)(void *)(nullptr);
2976 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
2977 new (sizePtr + 1) std::int32_t(-1);
2978}
2979
2981{
2982 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(objPtr);
2983
2984 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2985 if (fItemDeleter) {
2986 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2987 fItemDeleter->operator()(begin + i * fItemSize, true /* dtorOnly */);
2988 }
2989 }
2990
2991 DestroyRVecWithChecks(fItemAlignment, beginPtr, begin, capacityPtr);
2992 RDeleter::operator()(objPtr, dtorOnly);
2993}
2994
2995std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RRVecField::GetDeleter() const
2996{
2997 if (fItemDeleter)
2998 return std::make_unique<RRVecDeleter>(fSubFields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubFields[0]));
2999 return std::make_unique<RRVecDeleter>(fSubFields[0]->GetAlignment());
3000}
3001
3002std::vector<ROOT::Experimental::RFieldBase::RValue>
3004{
3005 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetPtr<void>().get());
3006
3007 std::vector<RValue> result;
3008 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
3009 result.reserve(*sizePtr);
3010 for (std::int32_t i = 0; i < *sizePtr; ++i) {
3011 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), begin + i * fItemSize)));
3012 }
3013 return result;
3014}
3015
3017{
3018 return fValueSize;
3019}
3020
3022{
3023 return EvalRVecAlignment(fSubFields[0]->GetAlignment());
3024}
3025
3027{
3028 visitor.VisitRVecField(*this);
3029}
3030
3031//------------------------------------------------------------------------------
3032
3034 : ROOT::Experimental::RFieldBase(name, "std::vector<bool>", ENTupleStructure::kCollection, false /* isSimple */)
3035{
3036 Attach(std::make_unique<RField<bool>>("_0"));
3037}
3038
3039std::size_t ROOT::Experimental::RField<std::vector<bool>>::AppendImpl(const void *from)
3040{
3041 auto typedValue = static_cast<const std::vector<bool> *>(from);
3042 auto count = typedValue->size();
3043 for (unsigned i = 0; i < count; ++i) {
3044 bool bval = (*typedValue)[i];
3045 CallAppendOn(*fSubFields[0], &bval);
3046 }
3047 fNWritten += count;
3048 fPrincipalColumn->Append(&fNWritten);
3049 return count + fPrincipalColumn->GetElement()->GetPackedSize();
3050}
3051
3052void ROOT::Experimental::RField<std::vector<bool>>::ReadGlobalImpl(NTupleSize_t globalIndex, void *to)
3053{
3054 auto typedValue = static_cast<std::vector<bool> *>(to);
3055
3056 ClusterSize_t nItems;
3057 RClusterIndex collectionStart;
3058 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
3059
3060 typedValue->resize(nItems);
3061 for (unsigned i = 0; i < nItems; ++i) {
3062 bool bval;
3063 CallReadOn(*fSubFields[0], collectionStart + i, &bval);
3064 (*typedValue)[i] = bval;
3065 }
3066}
3067
3069ROOT::Experimental::RField<std::vector<bool>>::GetColumnRepresentations() const
3070{
3071 static RColumnRepresentations representations(
3073 {});
3074 return representations;
3075}
3076
3077void ROOT::Experimental::RField<std::vector<bool>>::GenerateColumns()
3078{
3079 GenerateColumnsImpl<ClusterSize_t>();
3080}
3081
3082void ROOT::Experimental::RField<std::vector<bool>>::GenerateColumns(const RNTupleDescriptor &desc)
3083{
3084 GenerateColumnsImpl<ClusterSize_t>(desc);
3085}
3086
3087std::vector<ROOT::Experimental::RFieldBase::RValue>
3088ROOT::Experimental::RField<std::vector<bool>>::SplitValue(const RValue &value) const
3089{
3090 const auto &typedValue = value.GetRef<std::vector<bool>>();
3091 auto count = typedValue.size();
3092 std::vector<RValue> result;
3093 result.reserve(count);
3094 for (unsigned i = 0; i < count; ++i) {
3095 if (typedValue[i])
3096 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
3097 else
3098 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
3099 }
3100 return result;
3101}
3102
3103void ROOT::Experimental::RField<std::vector<bool>>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
3104{
3105 visitor.VisitVectorBoolField(*this);
3106}
3107
3108//------------------------------------------------------------------------------
3109
3110ROOT::Experimental::RArrayField::RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
3111 std::size_t arrayLength)
3112 : ROOT::Experimental::RFieldBase(fieldName,
3113 "std::array<" + itemField->GetTypeName() + "," + std::to_string(arrayLength) + ">",
3114 ENTupleStructure::kLeaf, false /* isSimple */, arrayLength),
3115 fItemSize(itemField->GetValueSize()),
3116 fArrayLength(arrayLength)
3117{
3118 fTraits |= itemField->GetTraits() & ~kTraitMappable;
3119 Attach(std::move(itemField));
3120}
3121
3122std::unique_ptr<ROOT::Experimental::RFieldBase>
3123ROOT::Experimental::RArrayField::CloneImpl(std::string_view newName) const
3124{
3125 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3126 return std::make_unique<RArrayField>(newName, std::move(newItemField), fArrayLength);
3127}
3128
3130{
3131 std::size_t nbytes = 0;
3132 auto arrayPtr = static_cast<const unsigned char *>(from);
3133 for (unsigned i = 0; i < fArrayLength; ++i) {
3134 nbytes += CallAppendOn(*fSubFields[0], arrayPtr + (i * fItemSize));
3135 }
3136 return nbytes;
3137}
3138
3140{
3141 auto arrayPtr = static_cast<unsigned char *>(to);
3142 for (unsigned i = 0; i < fArrayLength; ++i) {
3143 CallReadOn(*fSubFields[0], globalIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
3144 }
3145}
3146
3148{
3149 auto arrayPtr = static_cast<unsigned char *>(to);
3150 for (unsigned i = 0; i < fArrayLength; ++i) {
3151 CallReadOn(*fSubFields[0], RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() * fArrayLength + i),
3152 arrayPtr + (i * fItemSize));
3153 }
3154}
3155
3157{
3158 if (fSubFields[0]->GetTraits() & kTraitTriviallyConstructible)
3159 return;
3160
3161 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
3162 for (unsigned i = 0; i < fArrayLength; ++i) {
3163 CallConstructValueOn(*fSubFields[0], arrayPtr + (i * fItemSize));
3164 }
3165}
3166
3168{
3169 if (fItemDeleter) {
3170 for (unsigned i = 0; i < fArrayLength; ++i) {
3171 fItemDeleter->operator()(reinterpret_cast<unsigned char *>(objPtr) + i * fItemSize, true /* dtorOnly */);
3172 }
3173 }
3174 RDeleter::operator()(objPtr, dtorOnly);
3175}
3176
3177std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RArrayField::GetDeleter() const
3178{
3179 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyDestructible))
3180 return std::make_unique<RArrayDeleter>(fItemSize, fArrayLength, GetDeleterOf(*fSubFields[0]));
3181 return std::make_unique<RDeleter>();
3182}
3183
3184std::vector<ROOT::Experimental::RFieldBase::RValue>
3186{
3187 auto arrayPtr = value.GetPtr<unsigned char>().get();
3188 std::vector<RValue> result;
3189 result.reserve(fArrayLength);
3190 for (unsigned i = 0; i < fArrayLength; ++i) {
3191 result.emplace_back(
3192 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
3193 }
3194 return result;
3195}
3196
3198{
3199 visitor.VisitArrayField(*this);
3200}
3201
3202//------------------------------------------------------------------------------
3203// RArrayAsRVecField
3204
3206 std::unique_ptr<ROOT::Experimental::RFieldBase> itemField,
3207 std::size_t arrayLength)
3208 : ROOT::Experimental::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
3209 ENTupleStructure::kCollection, false /* isSimple */),
3210 fItemSize(itemField->GetValueSize()),
3211 fArrayLength(arrayLength)
3212{
3213 Attach(std::move(itemField));
3214 fValueSize = EvalRVecValueSize(fSubFields[0]->GetAlignment(), fSubFields[0]->GetValueSize(), GetAlignment());
3217}
3218
3219std::unique_ptr<ROOT::Experimental::RFieldBase>
3221{
3222 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3223 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
3224}
3225
3227{
3228 // initialize data members fBegin, fSize, fCapacity
3229 void **beginPtr = new (where)(void *)(nullptr);
3230 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
3231 std::int32_t *capacityPtr = new (sizePtr + 1) std::int32_t(0);
3232
3233 // Create the RVec with the known fixed size, do it once here instead of
3234 // every time the value is read in `Read*Impl` functions
3235 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
3236
3237 // Early return if the RVec has already been allocated.
3238 if (*sizePtr == std::int32_t(fArrayLength))
3239 return;
3240
3241 // Need to allocate the RVec if it is the first time the value is being created.
3242 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
3243 // on the element construction.
3244 const bool owns = (*capacityPtr != -1); // RVec is adopting the memory
3245 const bool needsConstruct = !(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible);
3246 const bool needsDestruct = owns && fItemDeleter;
3247
3248 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
3249 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
3250 if (needsDestruct) {
3251 for (std::int32_t i = 0; i < *sizePtr; ++i) {
3252 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
3253 }
3254 }
3255
3256 // TODO: Isn't the RVec always owning in this case?
3257 if (owns) {
3258 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
3259 free(*beginPtr);
3260 }
3261
3262 *beginPtr = malloc(fArrayLength * fItemSize);
3263 R__ASSERT(*beginPtr != nullptr);
3264 // Re-assign begin pointer after allocation
3265 begin = reinterpret_cast<char *>(*beginPtr);
3266 // Size and capacity are equal since the field data type is std::array
3267 *sizePtr = fArrayLength;
3268 *capacityPtr = fArrayLength;
3269
3270 // Placement new for the array elements
3271 if (needsConstruct) {
3272 for (std::size_t i = 0; i < fArrayLength; ++i)
3273 CallConstructValueOn(*fSubFields[0], begin + (i * fItemSize));
3274 }
3275}
3276
3277std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RArrayAsRVecField::GetDeleter() const
3278{
3279 if (fItemDeleter) {
3280 return std::make_unique<RRVecField::RRVecDeleter>(fSubFields[0]->GetAlignment(), fItemSize,
3281 GetDeleterOf(*fSubFields[0]));
3282 }
3283 return std::make_unique<RRVecField::RRVecDeleter>(fSubFields[0]->GetAlignment());
3284}
3285
3287{
3288
3289 auto [beginPtr, _, __] = GetRVecDataMembers(to);
3290 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
3291
3292 if (fSubFields[0]->IsSimple()) {
3293 GetPrincipalColumnOf(*fSubFields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, rvecBeginPtr);
3294 return;
3295 }
3296
3297 // Read the new values into the collection elements
3298 for (std::size_t i = 0; i < fArrayLength; ++i) {
3299 CallReadOn(*fSubFields[0], globalIndex * fArrayLength + i, rvecBeginPtr + (i * fItemSize));
3300 }
3301}
3302
3304{
3305 auto [beginPtr, _, __] = GetRVecDataMembers(to);
3306 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
3307
3308 const auto &clusterId = clusterIndex.GetClusterId();
3309 const auto &clusterIndexIndex = clusterIndex.GetIndex();
3310
3311 if (fSubFields[0]->IsSimple()) {
3312 GetPrincipalColumnOf(*fSubFields[0])
3313 ->ReadV(RClusterIndex(clusterId, clusterIndexIndex * fArrayLength), fArrayLength, rvecBeginPtr);
3314 return;
3315 }
3316
3317 // Read the new values into the collection elements
3318 for (std::size_t i = 0; i < fArrayLength; ++i) {
3319 CallReadOn(*fSubFields[0], RClusterIndex(clusterId, clusterIndexIndex * fArrayLength + i),
3320 rvecBeginPtr + (i * fItemSize));
3321 }
3322}
3323
3325{
3326 return EvalRVecAlignment(fSubFields[0]->GetAlignment());
3327}
3328
3329std::vector<ROOT::Experimental::RFieldBase::RValue>
3331{
3332 auto arrayPtr = value.GetPtr<unsigned char>().get();
3333 std::vector<ROOT::Experimental::RFieldBase::RValue> result;
3334 result.reserve(fArrayLength);
3335 for (unsigned i = 0; i < fArrayLength; ++i) {
3336 result.emplace_back(
3337 fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
3338 }
3339 return result;
3340}
3341
3343{
3344 visitor.VisitArrayAsRVecField(*this);
3345}
3346
3347// RArrayAsRVecField
3348//------------------------------------------------------------------------------
3349
3350//------------------------------------------------------------------------------
3351
3352ROOT::Experimental::RBitsetField::RBitsetField(std::string_view fieldName, std::size_t N)
3353 : ROOT::Experimental::RFieldBase(fieldName, "std::bitset<" + std::to_string(N) + ">", ENTupleStructure::kLeaf,
3354 false /* isSimple */, N),
3355 fN(N)
3356{
3358}
3359
3362{
3363 static RColumnRepresentations representations({{EColumnType::kBit}}, {});
3364 return representations;
3365}
3366
3368{
3369 GenerateColumnsImpl<bool>();
3370}
3371
3373{
3374 GenerateColumnsImpl<bool>(desc);
3375}
3376
3378{
3379 const auto *asULongArray = static_cast<const Word_t *>(from);
3380 bool elementValue;
3381 std::size_t i = 0;
3382 for (std::size_t word = 0; word < (fN + kBitsPerWord - 1) / kBitsPerWord; ++word) {
3383 for (std::size_t mask = 0; (mask < kBitsPerWord) && (i < fN); ++mask, ++i) {
3384 elementValue = (asULongArray[word] & (static_cast<Word_t>(1) << mask)) != 0;
3385 fPrincipalColumn->Append(&elementValue);
3386 }
3387 }
3388 return fN;
3389}
3390
3392{
3393 auto *asULongArray = static_cast<Word_t *>(to);
3394 bool elementValue;
3395 for (std::size_t i = 0; i < fN; ++i) {
3396 fPrincipalColumn->Read(globalIndex * fN + i, &elementValue);
3397 Word_t mask = static_cast<Word_t>(1) << (i % kBitsPerWord);
3398 Word_t bit = static_cast<Word_t>(elementValue) << (i % kBitsPerWord);
3399 asULongArray[i / kBitsPerWord] = (asULongArray[i / kBitsPerWord] & ~mask) | bit;
3400 }
3401}
3402
3404{
3405 auto *asULongArray = static_cast<Word_t *>(to);
3406 bool elementValue;
3407 for (std::size_t i = 0; i < fN; ++i) {
3408 fPrincipalColumn->Read(RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() * fN) + i,
3409 &elementValue);
3410 Word_t mask = static_cast<Word_t>(1) << (i % kBitsPerWord);
3411 Word_t bit = static_cast<Word_t>(elementValue) << (i % kBitsPerWord);
3412 asULongArray[i / kBitsPerWord] = (asULongArray[i / kBitsPerWord] & ~mask) | bit;
3413 }
3414}
3415
3417{
3418 visitor.VisitBitsetField(*this);
3419}
3420
3421//------------------------------------------------------------------------------
3422
3423std::string ROOT::Experimental::RVariantField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
3424{
3425 std::string result;
3426 for (size_t i = 0; i < itemFields.size(); ++i) {
3427 result += itemFields[i]->GetTypeName() + ",";
3428 }
3429 R__ASSERT(!result.empty()); // there is always at least one variant
3430 result.pop_back(); // remove trailing comma
3431 return result;
3432}
3433
3435 : ROOT::Experimental::RFieldBase(name, source.GetTypeName(), ENTupleStructure::kVariant, false /* isSimple */),
3436 fMaxItemSize(source.fMaxItemSize),
3437 fMaxAlignment(source.fMaxAlignment),
3438 fTagOffset(source.fTagOffset),
3439 fVariantOffset(source.fVariantOffset),
3440 fNWritten(source.fNWritten.size(), 0)
3441{
3442 for (const auto &f : source.GetSubFields())
3443 Attach(f->Clone(f->GetFieldName()));
3444 fTraits = source.fTraits;
3445}
3446
3448 std::vector<std::unique_ptr<RFieldBase>> itemFields)
3449 : ROOT::Experimental::RFieldBase(fieldName, "std::variant<" + GetTypeList(itemFields) + ">",
3450 ENTupleStructure::kVariant, false /* isSimple */)
3451{
3452 // The variant needs to initialize its own tag member
3453 fTraits |= kTraitTriviallyDestructible & ~kTraitTriviallyConstructible;
3454
3455 auto nFields = itemFields.size();
3456 if (nFields == 0 || nFields > kMaxVariants) {
3457 throw RException(R__FAIL("invalid number of variant fields (outside [1.." + std::to_string(kMaxVariants) + ")"));
3458 }
3459 fNWritten.resize(nFields, 0);
3460 for (unsigned int i = 0; i < nFields; ++i) {
3461 fMaxItemSize = std::max(fMaxItemSize, itemFields[i]->GetValueSize());
3462 fMaxAlignment = std::max(fMaxAlignment, itemFields[i]->GetAlignment());
3463 fTraits &= itemFields[i]->GetTraits();
3464 Attach(std::move(itemFields[i]));
3465 }
3466
3467 // With certain template parameters, the union of members of an std::variant starts at an offset > 0.
3468 // For instance, std::variant<std::optional<int>> on macOS.
3469 auto cl = TClass::GetClass(GetTypeName().c_str());
3470 assert(cl);
3471 auto dm = reinterpret_cast<TDataMember *>(cl->GetListOfDataMembers()->First());
3472 if (dm)
3473 fVariantOffset = dm->GetOffset();
3474
3475 const auto tagSize = GetVariantTagSize();
3476 const auto padding = tagSize - (fMaxItemSize % tagSize);
3477 fTagOffset = fVariantOffset + fMaxItemSize + ((padding == tagSize) ? 0 : padding);
3478}
3479
3480std::unique_ptr<ROOT::Experimental::RFieldBase>
3482{
3483 return std::unique_ptr<RVariantField>(new RVariantField(newName, *this));
3484}
3485
3486std::uint8_t ROOT::Experimental::RVariantField::GetTag(const void *variantPtr, std::size_t tagOffset)
3487{
3488 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
3489 auto tag = *reinterpret_cast<const TagType_t *>(reinterpret_cast<const unsigned char *>(variantPtr) + tagOffset);
3490 return (tag == TagType_t(-1)) ? 0 : tag + 1;
3491}
3492
3493void ROOT::Experimental::RVariantField::SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
3494{
3495 using TagType_t = RVariantTag<GetVariantTagSize()>::ValueType_t;
3496 auto tagPtr = reinterpret_cast<TagType_t *>(reinterpret_cast<unsigned char *>(variantPtr) + tagOffset);
3497 *tagPtr = (tag == 0) ? TagType_t(-1) : static_cast<TagType_t>(tag - 1);
3498}
3499
3501{
3502 auto tag = GetTag(from, fTagOffset);
3503 std::size_t nbytes = 0;
3504 auto index = 0;
3505 if (tag > 0) {
3506 nbytes += CallAppendOn(*fSubFields[tag - 1], reinterpret_cast<const unsigned char *>(from) + fVariantOffset);
3507 index = fNWritten[tag - 1]++;
3508 }
3509 RColumnSwitch varSwitch(ClusterSize_t(index), tag);
3510 fPrincipalColumn->Append(&varSwitch);
3511 return nbytes + sizeof(RColumnSwitch);
3512}
3513
3515{
3516 RClusterIndex variantIndex;
3517 std::uint32_t tag;
3518 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
3519 R__ASSERT(tag < 256);
3520
3521 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
3522 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
3523 // any `std::holds_alternative<T>` check fail later.
3524 if (R__likely(tag > 0)) {
3525 void *varPtr = reinterpret_cast<unsigned char *>(to) + fVariantOffset;
3526 CallConstructValueOn(*fSubFields[tag - 1], varPtr);
3527 CallReadOn(*fSubFields[tag - 1], variantIndex, varPtr);
3528 }
3529 SetTag(to, fTagOffset, tag);
3530}
3531
3534{
3535 static RColumnRepresentations representations({{EColumnType::kSwitch}}, {});
3536 return representations;
3537}
3538
3540{
3541 GenerateColumnsImpl<RColumnSwitch>();
3542}
3543
3545{
3546 GenerateColumnsImpl<RColumnSwitch>(desc);
3547}
3548
3550{
3551 memset(where, 0, GetValueSize());
3552 CallConstructValueOn(*fSubFields[0], reinterpret_cast<unsigned char *>(where) + fVariantOffset);
3553 SetTag(where, fTagOffset, 1);
3554}
3555
3557{
3558 auto tag = GetTag(objPtr, fTagOffset);
3559 if (tag > 0) {
3560 fItemDeleters[tag - 1]->operator()(reinterpret_cast<unsigned char *>(objPtr) + fVariantOffset, true /*dtorOnly*/);
3561 }
3562 RDeleter::operator()(objPtr, dtorOnly);
3563}
3564
3565std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RVariantField::GetDeleter() const
3566{
3567 std::vector<std::unique_ptr<RDeleter>> itemDeleters;
3568 itemDeleters.reserve(fSubFields.size());
3569 for (const auto &f : fSubFields) {
3570 itemDeleters.emplace_back(GetDeleterOf(*f));
3571 }
3572 return std::make_unique<RVariantDeleter>(fTagOffset, fVariantOffset, std::move(itemDeleters));
3573}
3574
3576{
3577 return std::max(fMaxAlignment, alignof(RVariantTag<GetVariantTagSize()>::ValueType_t));
3578}
3579
3581{
3582 const auto alignment = GetAlignment();
3583 const auto actualSize = fTagOffset + GetVariantTagSize();
3584 const auto padding = alignment - (actualSize % alignment);
3585 return actualSize + ((padding == alignment) ? 0 : padding);
3586}
3587
3589{
3590 std::fill(fNWritten.begin(), fNWritten.end(), 0);
3591}
3592
3593//------------------------------------------------------------------------------
3594
3595ROOT::Experimental::RSetField::RSetField(std::string_view fieldName, std::string_view typeName,
3596 std::unique_ptr<RFieldBase> itemField)
3597 : ROOT::Experimental::RProxiedCollectionField(fieldName, typeName, std::move(itemField))
3598{
3599}
3600
3601//------------------------------------------------------------------------------
3602
3603ROOT::Experimental::RMapField::RMapField(std::string_view fieldName, std::string_view typeName,
3604 std::unique_ptr<RFieldBase> itemField)
3605 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
3606{
3607 if (!dynamic_cast<RPairField *>(itemField.get()))
3608 throw RException(R__FAIL("RMapField inner field type must be of RPairField"));
3609
3610 auto *itemClass = fProxy->GetValueClass();
3611 fItemSize = itemClass->GetClassSize();
3612
3613 Attach(std::move(itemField));
3614}
3615
3616//------------------------------------------------------------------------------
3617
3618ROOT::Experimental::RNullableField::RNullableField(std::string_view fieldName, std::string_view typeName,
3619 std::unique_ptr<RFieldBase> itemField)
3620 : ROOT::Experimental::RFieldBase(fieldName, typeName, ENTupleStructure::kCollection, false /* isSimple */)
3621{
3622 Attach(std::move(itemField));
3623}
3624
3627{
3628 static RColumnRepresentations representations(
3630 {});
3631 return representations;
3632}
3633
3635{
3636 GenerateColumnsImpl<ClusterSize_t>();
3637}
3638
3640{
3641 GenerateColumnsImpl<ClusterSize_t>(desc);
3642}
3643
3645{
3646 fPrincipalColumn->Append(&fNWritten);
3647 return sizeof(ClusterSize_t);
3648}
3649
3651{
3652 auto nbytesItem = CallAppendOn(*fSubFields[0], from);
3653 fNWritten++;
3654 fPrincipalColumn->Append(&fNWritten);
3655 return sizeof(ClusterSize_t) + nbytesItem;
3656}
3657
3659{
3660 RClusterIndex collectionStart;
3661 ClusterSize_t collectionSize;
3662 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &collectionSize);
3663 return (collectionSize == 0) ? RClusterIndex() : collectionStart;
3664}
3665
3667{
3668 visitor.VisitNullableField(*this);
3669}
3670
3671//------------------------------------------------------------------------------
3672
3673ROOT::Experimental::RUniquePtrField::RUniquePtrField(std::string_view fieldName, std::string_view typeName,
3674 std::unique_ptr<RFieldBase> itemField)
3675 : RNullableField(fieldName, typeName, std::move(itemField)), fItemDeleter(GetDeleterOf(*fSubFields[0]))
3676{
3677}
3678
3679std::unique_ptr<ROOT::Experimental::RFieldBase>
3681{
3682 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3683 return std::make_unique<RUniquePtrField>(newName, GetTypeName(), std::move(newItemField));
3684}
3685
3687{
3688 auto typedValue = static_cast<const std::unique_ptr<char> *>(from);
3689 if (*typedValue) {
3690 return AppendValue(typedValue->get());
3691 } else {
3692 return AppendNull();
3693 }
3694}
3695
3697{
3698 auto ptr = static_cast<std::unique_ptr<char> *>(to);
3699 bool isValidValue = static_cast<bool>(*ptr);
3700
3701 auto itemIndex = GetItemIndex(globalIndex);
3702 bool isValidItem = itemIndex.GetIndex() != kInvalidClusterIndex;
3703
3704 void *valuePtr = nullptr;
3705 if (isValidValue)
3706 valuePtr = ptr->get();
3707
3708 if (isValidValue && !isValidItem) {
3709 ptr->release();
3710 fItemDeleter->operator()(valuePtr, false /* dtorOnly */);
3711 return;
3712 }
3713
3714 if (!isValidItem) // On-disk value missing; nothing else to do
3715 return;
3716
3717 if (!isValidValue) {
3718 valuePtr = CallCreateObjectRawPtrOn(*fSubFields[0]);
3719 ptr->reset(reinterpret_cast<char *>(valuePtr));
3720 }
3721
3722 CallReadOn(*fSubFields[0], itemIndex, valuePtr);
3723}
3724
3726{
3727 auto typedPtr = static_cast<std::unique_ptr<char> *>(objPtr);
3728 if (*typedPtr) {
3729 fItemDeleter->operator()(typedPtr->get(), false /* dtorOnly */);
3730 typedPtr->release();
3731 }
3732 RDeleter::operator()(objPtr, dtorOnly);
3733}
3734
3735std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::RUniquePtrField::GetDeleter() const
3736{
3737 return std::make_unique<RUniquePtrDeleter>(GetDeleterOf(*fSubFields[0]));
3738}
3739
3740std::vector<ROOT::Experimental::RFieldBase::RValue>
3742{
3743 std::vector<RValue> result;
3744 const auto &ptr = value.GetRef<std::unique_ptr<char>>();
3745 if (ptr) {
3746 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), ptr.get())));
3747 }
3748 return result;
3749}
3750
3751//------------------------------------------------------------------------------
3752
3753ROOT::Experimental::ROptionalField::ROptionalField(std::string_view fieldName, std::string_view typeName,
3754 std::unique_ptr<RFieldBase> itemField)
3755 : RNullableField(fieldName, typeName, std::move(itemField)), fItemDeleter(GetDeleterOf(*fSubFields[0]))
3756{
3759}
3760
3761std::pair<void *, bool *> ROOT::Experimental::ROptionalField::GetValueAndEngagementPtrs(void *optionalPtr) const
3762{
3763 void *value = optionalPtr;
3764 bool *engagement =
3765 reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(optionalPtr) + fSubFields[0]->GetValueSize());
3766 return {value, engagement};
3767}
3768
3769std::pair<const void *, const bool *>
3771{
3772 return GetValueAndEngagementPtrs(const_cast<void *>(optionalPtr));
3773}
3774
3775std::unique_ptr<ROOT::Experimental::RFieldBase>
3777{
3778 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3779 return std::make_unique<ROptionalField>(newName, GetTypeName(), std::move(newItemField));
3780}
3781
3783{
3784 const auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(from);
3785 if (*engagementPtr) {
3786 return AppendValue(valuePtr);
3787 } else {
3788 return AppendNull();
3789 }
3790}
3791
3793{
3794 auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(to);
3795 auto itemIndex = GetItemIndex(globalIndex);
3796 if (itemIndex.GetIndex() == kInvalidClusterIndex) {
3797 *engagementPtr = false;
3798 } else {
3799 CallReadOn(*fSubFields[0], itemIndex, valuePtr);
3800 *engagementPtr = true;
3801 }
3802}
3803
3805{
3806 auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(where);
3807 CallConstructValueOn(*fSubFields[0], valuePtr);
3808 *engagementPtr = false;
3809}
3810
3812{
3813 fItemDeleter->operator()(objPtr, true /* dtorOnly */);
3814 RDeleter::operator()(objPtr, dtorOnly);
3815}
3816
3817std::unique_ptr<ROOT::Experimental::RFieldBase::RDeleter> ROOT::Experimental::ROptionalField::GetDeleter() const
3818{
3819 return std::make_unique<ROptionalDeleter>(GetDeleterOf(*fSubFields[0]));
3820}
3821
3822std::vector<ROOT::Experimental::RFieldBase::RValue>
3824{
3825 std::vector<RValue> result;
3826 const auto [valuePtr, engagementPtr] = GetValueAndEngagementPtrs(value.GetPtr<void>().get());
3827 if (*engagementPtr) {
3828 result.emplace_back(fSubFields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), valuePtr)));
3829 }
3830 return result;
3831}
3832
3834{
3835 const auto alignment = GetAlignment();
3836 // real size is the sum of the value size and the engagement boolean
3837 const auto actualSize = fSubFields[0]->GetValueSize() + sizeof(bool);
3838 auto padding = 0;
3839 if (alignment > 1) {
3840 auto remainder = actualSize % alignment;
3841 if (remainder != 0)
3842 padding = alignment - remainder;
3843 }
3844 return actualSize + padding;
3845}
3846
3848{
3849 return fSubFields[0]->GetAlignment();
3850}
3851
3852//------------------------------------------------------------------------------
3853
3854std::string
3855ROOT::Experimental::RPairField::RPairField::GetTypeList(const std::array<std::unique_ptr<RFieldBase>, 2> &itemFields)
3856{
3857 return itemFields[0]->GetTypeName() + "," + itemFields[1]->GetTypeName();
3858}
3859
3861 std::array<std::unique_ptr<RFieldBase>, 2> itemFields,
3862 const std::array<std::size_t, 2> &offsets)
3863 : ROOT::Experimental::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
3864{
3865 AttachItemFields(std::move(itemFields));
3866 fOffsets.push_back(offsets[0]);
3867 fOffsets.push_back(offsets[1]);
3868}
3869
3871 std::array<std::unique_ptr<RFieldBase>, 2> itemFields)
3872 : ROOT::Experimental::RRecordField(fieldName, "std::pair<" + GetTypeList(itemFields) + ">")
3873{
3874 AttachItemFields(std::move(itemFields));
3875
3876 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
3877 auto *c = TClass::GetClass(GetTypeName().c_str());
3878 if (!c)
3879 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
3880 fSize = c->Size();
3881
3882 auto firstElem = c->GetRealData("first");
3883 if (!firstElem)
3884 throw RException(R__FAIL("first: no such member"));
3885 fOffsets.push_back(firstElem->GetThisOffset());
3886
3887 auto secondElem = c->GetRealData("second");
3888 if (!secondElem)
3889 throw RException(R__FAIL("second: no such member"));
3890 fOffsets.push_back(secondElem->GetThisOffset());
3891}
3892
3893//------------------------------------------------------------------------------
3894
3895std::string
3896ROOT::Experimental::RTupleField::RTupleField::GetTypeList(const std::vector<std::unique_ptr<RFieldBase>> &itemFields)
3897{
3898 std::string result;
3899 if (itemFields.empty())
3900 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
3901 for (size_t i = 0; i < itemFields.size(); ++i) {
3902 result += itemFields[i]->GetTypeName() + ",";
3903 }
3904 result.pop_back(); // remove trailing comma
3905 return result;
3906}
3907
3909 std::vector<std::unique_ptr<RFieldBase>> itemFields,
3910 const std::vector<std::size_t> &offsets)
3911 : ROOT::Experimental::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
3912{
3913 AttachItemFields(std::move(itemFields));
3914 fOffsets = offsets;
3915}
3916
3918 std::vector<std::unique_ptr<RFieldBase>> itemFields)
3919 : ROOT::Experimental::RRecordField(fieldName, "std::tuple<" + GetTypeList(itemFields) + ">")
3920{
3921 AttachItemFields(std::move(itemFields));
3922
3923 auto *c = TClass::GetClass(GetTypeName().c_str());
3924 if (!c)
3925 throw RException(R__FAIL("cannot get type information for " + GetTypeName()));
3926 fSize = c->Size();
3927
3928 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
3929 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
3930 // following the order of the type list.
3931 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
3932 // members, the assertion below will fail.
3933 for (unsigned i = 0; i < fSubFields.size(); ++i) {
3934 std::string memberName("_" + std::to_string(i));
3935 auto member = c->GetRealData(memberName.c_str());
3936 if (!member)
3937 throw RException(R__FAIL(memberName + ": no such member"));
3938 fOffsets.push_back(member->GetThisOffset());
3939 }
3940}
3941
3942//------------------------------------------------------------------------------
3943
3944ROOT::Experimental::RAtomicField::RAtomicField(std::string_view fieldName, std::string_view typeName,
3945 std::unique_ptr<RFieldBase> itemField)
3946 : RFieldBase(fieldName, typeName, ENTupleStructure::kLeaf, false /* isSimple */)
3947{
3948 if (itemField->GetTraits() & kTraitTriviallyConstructible)
3950 if (itemField->GetTraits() & kTraitTriviallyDestructible)
3952 Attach(std::move(itemField));
3953}
3954
3955std::unique_ptr<ROOT::Experimental::RFieldBase>
3956ROOT::Experimental::RAtomicField::CloneImpl(std::string_view newName) const
3957{
3958 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetFieldName());
3959 return std::make_unique<RAtomicField>(newName, GetTypeName(), std::move(newItemField));
3960}
3961
3962std::vector<ROOT::Experimental::RFieldBase::RValue>
3964{
3965 std::vector<RValue> result;
3966 result.emplace_back(fSubFields[0]->BindValue(value.GetPtr<void>()));
3967 return result;
3968}
3969
3971{
3972 visitor.VisitAtomicField(*this);
3973}
size_t fValueSize
Cppyy::TCppType_t fClass
dim_t fSize
#define R__likely(expr)
Definition RConfig.hxx:595
#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 * GetColumnTypeName(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:3220
std::size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:3324
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:3277
RArrayAsRVecField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
Constructor of the field.
Definition RField.cxx:3205
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:3226
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3342
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3286
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:3303
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:3330
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3167
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:3185
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:3156
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3129
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3197
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:3147
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:3177
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3123
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3139
RArrayField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
Definition RField.cxx:3110
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3956
RAtomicField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3944
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:3963
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3970
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3377
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3391
RBitsetField(std::string_view fieldName, std::size_t N)
Definition RField.cxx:3352
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:3367
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:3361
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3416
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:3403
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:1323
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:1336
const RField< RNTupleCardinality< std::uint32_t > > * As32Bit() const
Definition RField.cxx:1342
const RField< RNTupleCardinality< std::uint64_t > > * As64Bit() const
Definition RField.cxx:1348
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:1953
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:1972
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:1914
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:1865
void OnConnectPageSource() final
Called by ConnectPageSource() once connected; derived classes may override this as appropriate.
Definition RField.cxx:1921
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:1898
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:1907
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
Definition RField.cxx:1982
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1893
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:1872
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:1948
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:1977
RClassField(std::string_view fieldName, const RClassField &source)
Used by CloneImpl.
Definition RField.cxx:1772
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1987
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:1960
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:2293
REnumField(std::string_view fieldName, std::string_view enumName, TEnum *enump)
Definition RField.cxx:2242
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:2286
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2279
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:1067
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:972
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:1200
void SetColumnRepresentatives(const RColumnRepresentations::Selection_t &representatives)
Fixes a column representative.
Definition RField.cxx:1134
ENTupleStructure fStructure
The role of this field in the data model structure.
std::vector< RFieldBase * > GetSubFields()
Definition RField.cxx:995
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:591
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:1299
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:1015
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:612
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:1226
virtual std::size_t AppendImpl(const void *from)
Operations on values of complex types, e.g.
Definition RField.cxx:916
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:1114
std::string GetQualifiedFieldName() const
Returns the field name and parent field names separated by dots ("grandparent.parent....
Definition RField.cxx:571
std::vector< EColumnType > ColumnRepresentation_t
virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec)
General implementation of bulk read.
Definition RField.cxx:932
std::size_t Append(const void *from)
Write the given value into columns.
Definition RField.cxx:1058
virtual void ReadInClusterImpl(RClusterIndex clusterIndex, void *to)
Definition RField.cxx:927
void RemoveReadCallback(size_t idx)
Definition RField.cxx:1194
void * CreateObjectRawPtr() const
Factory method for the field's type. The caller owns the returned pointer.
Definition RField.cxx:952
void CommitCluster()
Flushes data from active columns to disk and calls CommitClusterImpl.
Definition RField.cxx:1027
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:1151
void ConnectPageSource(Internal::RPageSource &pageSource)
Connects the field and its sub field tree to the given page source.
Definition RField.cxx:1252
RValue CreateValue()
Generates an object of the field type and wraps the created object in a shared pointer and returns it...
Definition RField.cxx:960
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:905
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:1077
RConstSchemaIterator cbegin() const
Definition RField.cxx:1109
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:1187
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:967
void SetOnDiskId(DescriptorId_t id)
Definition RField.cxx:1049
RColumnRepresentations::Selection_t GetColumnRepresentatives() const
Returns the fColumnRepresentative pointee or, if unset, the field's default representative.
Definition RField.cxx:1120
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:1042
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:983
RValue BindValue(std::shared_ptr< void > objPtr)
Creates a value from a memory location with an already constructed object.
Definition RField.cxx:1072
virtual void ReadGlobalImpl(NTupleSize_t globalIndex, void *to)
Definition RField.cxx:922
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:899
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:972
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1315
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1307
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:3603
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:3626
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:3634
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3666
RClusterIndex GetItemIndex(NTupleSize_t globalIndex)
Given the index of the nullable field, returns the corresponding global index of the subfield or,...
Definition RField.cxx:3658
RNullableField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3618
std::size_t AppendValue(const void *from)
Definition RField.cxx:3650
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3811
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3792
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:3804
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:3817
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:3847
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3782
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:3770
ROptionalField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3753
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:3833
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3776
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:3823
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:3860
Allows for iterating over the elements of a proxied collection.
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
Definition RField.cxx:2301
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:2389
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:2483
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2405
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:2313
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:2449
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2425
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2382
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:2433
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:2443
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:2471
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:2980
std::unique_ptr< RDeleter > fItemDeleter
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2793
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2813
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:2971
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:2995
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3026
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:3003
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:3016
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:3021
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:2961
RRVecField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:2774
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2953
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
General implementation of bulk read.
Definition RField.cxx:2887
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2787
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:2586
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:2551
void ReadInClusterImpl(RClusterIndex clusterIndex, void *to) final
Definition RField.cxx:2572
RRecordField(std::string_view name, const RRecordField &source)
Definition RField.cxx:2490
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2565
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:2605
std::size_t GetItemPadding(std::size_t baseOffset, std::size_t itemAlignment) const
Definition RField.cxx:2540
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:2594
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2556
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2616
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:2579
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:3595
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:2194
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:2189
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2157
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2144
std::uint32_t GetTypeChecksum() const final
Return the current TClass reported checksum of this class. Only valid if kTraitTypeChecksum is set.
Definition RField.cxx:2225
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:2179
RStreamerField(std::string_view fieldName, std::string_view className, TClass *classp)
Definition RField.cxx:2121
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:2215
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2230
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
Definition RField.cxx:2220
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:2210
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2139
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2169
RExtraTypeInfoDescriptor GetExtraTypeInfo() const final
Definition RField.cxx:2200
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< RFieldBase > > itemFields, const std::vector< std::size_t > &offsets)
Definition RField.cxx:3908
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3725
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3680
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3696
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:3741
RUniquePtrField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:3673
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:3735
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3686
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:3556
Template specializations for C++ std::variant.
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:3514
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:3580
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:3533
static void SetTag(void *variantPtr, std::size_t tagOffset, std::uint8_t tag)
Definition RField.cxx:3493
static std::string GetTypeList(const std::vector< std::unique_ptr< RFieldBase > > &itemFields)
Definition RField.cxx:3423
std::vector< ClusterSize_t::ValueType > fNWritten
size_t fTagOffset
In the std::variant memory layout, at which byte number is the index stored.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3481
static constexpr std::size_t kMaxVariants
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:3549
static std::uint8_t GetTag(const void *variantPtr, std::size_t tagOffset)
Extracts the index from an std::variant and transforms it into the 1-based index used for the switch ...
Definition RField.cxx:3486
RVariantField(std::string_view name, const RVariantField &source)
Definition RField.cxx:3434
size_t fVariantOffset
In the std::variant memory layout, the actual union of types may start at an offset > 0.
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:3565
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:3500
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:3575
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:3539
void operator()(void *objPtr, bool dtorOnly) final
Definition RField.cxx:2731
Template specializations for C++ std::vector.
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
Definition RField.cxx:2641
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:2753
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2767
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2654
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponsing to the field type ...
Definition RField.cxx:2721
std::unique_ptr< RDeleter > GetDeleter() const final
Definition RField.cxx:2745
RVectorField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, bool isUntyped)
Definition RField.cxx:2623
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2713
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2647
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2675
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition TBufferFile.h:47
void TagStreamerInfo(TVirtualStreamerInfo *info) override
Mark the classindex of the current file as using this TStreamerInfo.
@ kWrite
Definition TBuffer.h:73
@ kRead
Definition TBuffer.h:73
char * Buffer() const
Definition TBuffer.h:96
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
UInt_t GetCheckSum(ECheckSum code=kCurrentCheckSum) const
Call GetCheckSum with validity check.
Definition TClass.cxx:6574
Bool_t CanSplit() const
Return true if the data member of this TClass can be saved separately.
Definition TClass.cxx:2388
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3839
Int_t Size() const
Return size of object of this class.
Definition TClass.cxx:5773
TList * GetListOfBases()
Return list containing the TBaseClass(es) of a class.
Definition TClass.cxx:3705
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2966
Long_t ClassProperty() const
Return the C++ property of this class, eg.
Definition TClass.cxx:2465
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6155
Version_t GetClassVersion() const
Definition TClass.h:420
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:3037
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
Longptr_t GetOffset() const
Get offset from "this".
TDictAttributeMap * GetAttributeMap() const
The TEnum class implements the enum type.
Definition TEnum.h:33
EDataType GetUnderlyingType() const
Get the underlying integer type of the enum: enum E { kOne }; // ==> int enum F: long; // ==> long Re...
Definition TEnum.h:71
Long_t Property() const override
Get property description word. For meaning of bits see EProperty.
Definition TEnum.cxx:139
static TEnum * GetEnum(const std::type_info &ti, ESearchAction sa=kALoadAndInterpLookup)
Definition TEnum.cxx:175
TObject * First() const override
Return the first object in the list. Returns 0 when list is empty.
Definition TList.cxx:657
Mother of all ROOT objects.
Definition TObject.h:41
@ kIsOnHeap
object is on heap
Definition TObject.h:81
@ kNotDeleted
object has not been deleted
Definition TObject.h:82
static TClass * Class()
@ kIsReferenced
if object is referenced by a TRef or TRefArray
Definition TObject.h:65
RAII helper class that ensures that PushProxy() / PopProxy() are called when entering / leaving a C++...
Defines a common interface to inspect/change the contents of an object that represents a collection.
@ kNeedDelete
The collection contains directly or indirectly (via other collection) some pointers that need explici...
virtual Next_t GetFunctionNext(Bool_t read=kTRUE)=0
Return a pointer to a function that can advance an iterator (see Next_t).
virtual DeleteTwoIterators_t GetFunctionDeleteTwoIterators(Bool_t read=kTRUE)=0
virtual TVirtualCollectionProxy * Generate() const =0
Returns a clean object of the actual class that derives from TVirtualCollectionProxy.
virtual CreateIterators_t GetFunctionCreateIterators(Bool_t read=kTRUE)=0
Return a pointer to a function that can create an iterator pair, where each iterator points to the be...
Wrapper around an object and giving indirect access to its content even if the object is not of a cla...
Abstract Interface class describing Streamer information for one class.
virtual Int_t GetNumber() const =0
const Int_t n
Definition legend1.C:16
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
auto MakeAliasedSharedPtr(T *rawPtr)
void CallConnectPageSinkOnField(RFieldBase &, RPageSink &, NTupleSize_t firstEntry=0)
Definition RField.cxx:406
void CallFlushColumnsOnField(RFieldBase &)
Definition RField.cxx:398
void CallConnectPageSourceOnField(RFieldBase &, RPageSource &)
Definition RField.cxx:411
void CallCommitClusterOnField(RFieldBase &)
Definition RField.cxx:402
RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
RClusterSize ClusterSize_t
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr ClusterSize_t kInvalidClusterIndex(std::uint64_t(-1))
ENTupleStructure
The fields in the ntuple model tree can carry different structural information about the type system.
constexpr DescriptorId_t kInvalidDescriptorId
void(off) SmallVectorTemplateBase< T
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
TClass * GetClass(T *)
Definition TClass.h:663
@ kSTLvector
Definition ESTLType.h:30
std::string ResolveTypedef(const char *tname, bool resolveAll=false)
std::string CleanType(const char *typeDesc, int mode=0, const char **tail=nullptr)
Cleanup type description, redundant blanks removed and redundant tail ignored return *tail = pointer ...
Wrap the integer in a struct in order to avoid template specialization clash with std::uint64_t.
void * fValues
The destination area, which has to be a big enough array of valid objects of the correct type.
const bool * fMaskReq
A bool array of size fCount, indicating the required values in the requested range.
bool * fMaskAvail
A bool array of size fCount, indicating the valid values in fValues.
std::size_t fCount
Size of the bulk range.
RClusterIndex fFirstIndex
Start of the bulk range.
std::vector< unsigned char > * fAuxData
Reference to memory owned by the RBulk class.
Used in the return value of the Check() method.