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