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>
23#include <ROOT/RNTuple.hxx>
24#include <ROOT/RNTupleModel.hxx>
25
26#include <TBaseClass.h>
27#include <TClass.h>
28#include <TClassEdit.h>
29#include <TCollection.h>
30#include <TDataMember.h>
31#include <TEnum.h>
32#include <TError.h>
33#include <TList.h>
34#include <TObjArray.h>
35#include <TObjString.h>
36#include <TRealData.h>
37#include <TSchemaRule.h>
38#include <TSchemaRuleSet.h>
39#include <TVirtualObject.h>
40
41#include <algorithm>
42#include <cctype> // for isspace
43#include <charconv>
44#include <cstdint>
45#include <cstdlib> // for malloc, free
46#include <cstring> // for memset
47#include <exception>
48#include <iostream>
49#include <memory>
50#include <new> // hardware_destructive_interference_size
51#include <type_traits>
52#include <unordered_map>
53
54namespace {
55
56const std::unordered_map<std::string_view, std::string_view> typeTranslationMap{
57 {"Bool_t", "bool"},
58 {"Float_t", "float"},
59 {"Double_t", "double"},
60 {"string", "std::string"},
61
62 {"Char_t", "char"},
63 {"int8_t", "std::int8_t"},
64 {"signed char", "char"},
65 {"UChar_t", "std::uint8_t"},
66 {"unsigned char", "std::uint8_t"},
67 {"uint8_t", "std::uint8_t"},
68
69 {"Short_t", "std::int16_t"},
70 {"int16_t", "std::int16_t"},
71 {"short", "std::int16_t"},
72 {"UShort_t", "std::uint16_t"},
73 {"unsigned short", "std::uint16_t"},
74 {"uint16_t", "std::uint16_t"},
75
76 {"Int_t", "std::int32_t"},
77 {"int32_t", "std::int32_t"},
78 {"int", "std::int32_t"},
79 {"UInt_t", "std::uint32_t"},
80 {"unsigned", "std::uint32_t"},
81 {"unsigned int", "std::uint32_t"},
82 {"uint32_t", "std::uint32_t"},
83
84 {"Long_t", "std::int64_t"},
85 {"Long64_t", "std::int64_t"},
86 {"int64_t", "std::int64_t"},
87 {"long", "std::int64_t"},
88 {"ULong64_t", "std::uint64_t"},
89 {"unsigned long", "std::uint64_t"},
90 {"uint64_t", "std::uint64_t"}
91};
92
93/// Used in CreateField() in order to get the comma-separated list of template types
94/// E.g., gets {"int", "std::variant<double,int>"} from "int,std::variant<double,int>"
95std::vector<std::string> TokenizeTypeList(std::string templateType) {
96 std::vector<std::string> result;
97 if (templateType.empty())
98 return result;
99
100 const char *eol = templateType.data() + templateType.length();
101 const char *typeBegin = templateType.data();
102 const char *typeCursor = templateType.data();
103 unsigned int nestingLevel = 0;
104 while (typeCursor != eol) {
105 switch (*typeCursor) {
106 case '<':
107 ++nestingLevel;
108 break;
109 case '>':
110 --nestingLevel;
111 break;
112 case ',':
113 if (nestingLevel == 0) {
114 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
115 typeBegin = typeCursor + 1;
116 }
117 break;
118 }
119 typeCursor++;
120 }
121 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
122 return result;
123}
124
125/// Parse a type name of the form `T[n][m]...` and return the base type `T` and a vector that contains,
126/// in order, the declared size for each dimension, e.g. for `unsigned char[1][2][3]` it returns the tuple
127/// `{"unsigned char", {1, 2, 3}}`. Extra whitespace in `typeName` should be removed before calling this function.
128///
129/// If `typeName` is not an array type, it returns a tuple `{T, {}}`. On error, it returns a default-constructed tuple.
130std::tuple<std::string, std::vector<size_t>> ParseArrayType(std::string_view typeName)
131{
132 std::vector<size_t> sizeVec;
133
134 // Only parse outer array definition, i.e. the right `]` should be at the end of the type name
135 while (typeName.back() == ']') {
136 auto posRBrace = typeName.size() - 1;
137 auto posLBrace = typeName.find_last_of('[', posRBrace);
138 if (posLBrace == std::string_view::npos)
139 return {};
140
141 size_t size;
142 if (std::from_chars(typeName.data() + posLBrace + 1, typeName.data() + posRBrace, size).ec != std::errc{})
143 return {};
144 sizeVec.insert(sizeVec.begin(), size);
145 typeName.remove_suffix(typeName.size() - posLBrace);
146 }
147 return std::make_tuple(std::string{typeName}, sizeVec);
148}
149
150/// Return the canonical name of a type, resolving typedefs to their underlying types if needed. A canonical type has
151/// typedefs stripped out from the type name.
152std::string GetCanonicalTypeName(const std::string &typeName)
153{
154 // The following types are asummed to be canonical names; thus, do not perform `typedef` resolution on those
155 if (typeName == "ROOT::Experimental::ClusterSize_t" || typeName.substr(0, 5) == "std::" ||
156 typeName.substr(0, 39) == "ROOT::Experimental::RNTupleCardinality<")
157 return typeName;
158
159 return TClassEdit::ResolveTypedef(typeName.c_str());
160}
161
162/// Applies type name normalization rules that lead to the final name used to create a RField, e.g. transforms
163/// `unsigned int` to `std::uint32_t` or `const vector<T>` to `std::vector<T>`. Specifically, `const` / `volatile`
164/// qualifiers are removed, integral types such as `unsigned int` or `long` are translated to fixed-length integer types
165/// (e.g. `std::uint32_t`), and `std::` is added to fully qualify known types in the `std` namespace.
166std::string GetNormalizedTypeName(const std::string &typeName)
167{
168 std::string normalizedType{TClassEdit::CleanType(typeName.c_str(), /*mode=*/2)};
169
170 if (auto it = typeTranslationMap.find(normalizedType); it != typeTranslationMap.end())
171 normalizedType = it->second;
172
173 if (normalizedType.substr(0, 7) == "vector<")
174 normalizedType = "std::" + normalizedType;
175 if (normalizedType.substr(0, 6) == "array<")
176 normalizedType = "std::" + normalizedType;
177 if (normalizedType.substr(0, 8) == "variant<")
178 normalizedType = "std::" + normalizedType;
179 if (normalizedType.substr(0, 5) == "pair<")
180 normalizedType = "std::" + normalizedType;
181 if (normalizedType.substr(0, 6) == "tuple<")
182 normalizedType = "std::" + normalizedType;
183 if (normalizedType.substr(0, 7) == "bitset<")
184 normalizedType = "std::" + normalizedType;
185 if (normalizedType.substr(0, 11) == "unique_ptr<")
186 normalizedType = "std::" + normalizedType;
187 if (normalizedType.substr(0, 4) == "set<")
188 normalizedType = "std::" + normalizedType;
189 if (normalizedType.substr(0, 14) == "unordered_set<")
190 normalizedType = "std::" + normalizedType;
191 if (normalizedType.substr(0, 4) == "map<")
192 normalizedType = "std::" + normalizedType;
193 if (normalizedType.substr(0, 14) == "unordered_map<")
194 normalizedType = "std::" + normalizedType;
195 if (normalizedType.substr(0, 7) == "atomic<")
196 normalizedType = "std::" + normalizedType;
197 if (normalizedType == "byte")
198 normalizedType = "std::byte";
199
200 return normalizedType;
201}
202
203/// Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the RVec object.
204/// Returns pointers to fBegin, fSize and fCapacity in a std::tuple.
205std::tuple<void **, std::int32_t *, std::int32_t *> GetRVecDataMembers(void *rvecPtr)
206{
207 void **begin = reinterpret_cast<void **>(rvecPtr);
208 // int32_t fSize is the second data member (after 1 void*)
209 std::int32_t *size = reinterpret_cast<std::int32_t *>(begin + 1);
210 R__ASSERT(*size >= 0);
211 // int32_t fCapacity is the third data member (1 int32_t after fSize)
212 std::int32_t *capacity = size + 1;
213 R__ASSERT(*capacity >= -1);
214 return {begin, size, capacity};
215}
216
217std::tuple<const void *const *, const std::int32_t *, const std::int32_t *> GetRVecDataMembers(const void *rvecPtr)
218{
219 return {GetRVecDataMembers(const_cast<void *>(rvecPtr))};
220}
221
222/// Applies the field IDs from 'from' to 'to', where from and to are expected to be each other's clones.
223/// Used in RClassField and RCollectionClassField cloning. In these classes, we don't clone the subfields
224/// but we recreate them. Therefore, the on-disk IDs need to be fixed up.
226{
227 auto iFrom = from.cbegin();
228 auto iTo = to.begin();
229 for (; iFrom != from.cend(); ++iFrom, ++iTo) {
230 iTo->SetOnDiskId(iFrom->GetOnDiskId());
231 }
232}
233
234} // anonymous namespace
235
236//------------------------------------------------------------------------------
237
239{
240 // A single representations with an empty set of columns
243}
244
246 const TypesList_t &serializationTypes, const TypesList_t &deserializationExtraTypes)
247 : fSerializationTypes(serializationTypes), fDeserializationTypes(serializationTypes)
248{
250 deserializationExtraTypes.begin(), deserializationExtraTypes.end());
251}
252
253//------------------------------------------------------------------------------
254
256 : fField(other.fField),
257 fValueSize(other.fValueSize),
258 fCapacity(other.fCapacity),
259 fSize(other.fSize),
260 fNValidValues(other.fNValidValues),
261 fFirstIndex(other.fFirstIndex)
262{
263 std::swap(fValues, other.fValues);
264 std::swap(fMaskAvail, other.fMaskAvail);
265}
266
268{
269 std::swap(fField, other.fField);
270 std::swap(fValues, other.fValues);
271 std::swap(fValueSize, other.fValueSize);
272 std::swap(fCapacity, other.fCapacity);
273 std::swap(fSize, other.fSize);
274 std::swap(fMaskAvail, other.fMaskAvail);
275 std::swap(fNValidValues, other.fNValidValues);
276 std::swap(fFirstIndex, other.fFirstIndex);
277 return *this;
278}
279
281{
282 if (fValues)
283 ReleaseValues();
284}
285
287{
288 if (fField->GetTraits() & RFieldBase::kTraitTriviallyDestructible) {
289 free(fValues);
290 return;
291 }
292
293 for (std::size_t i = 0; i < fCapacity; ++i) {
294 fField->DestroyValue(GetValuePtrAt(i), true /* dtorOnly */);
295 }
296 free(fValues);
297}
298
300{
301 if (fCapacity < size) {
302 ReleaseValues();
303 fValues = malloc(size * fValueSize);
304
305 if (!(fField->GetTraits() & RFieldBase::kTraitTriviallyConstructible)) {
306 for (std::size_t i = 0; i < size; ++i) {
307 fField->GenerateValue(GetValuePtrAt(i));
308 }
309 }
310
311 fMaskAvail = std::make_unique<bool[]>(size);
312 fCapacity = size;
313 }
314
315 std::fill(fMaskAvail.get(), fMaskAvail.get() + size, false);
316 fNValidValues = 0;
317
318 fFirstIndex = firstIndex;
319 fSize = size;
320}
321
323{
324 fNValidValues = 0;
325 for (std::size_t i = 0; i < fSize; ++i)
326 fNValidValues += static_cast<std::size_t>(fMaskAvail[i]);
327}
328
329//------------------------------------------------------------------------------
330
332 ENTupleStructure structure, bool isSimple, std::size_t nRepetitions)
333 : fName(name), fType(type), fStructure(structure), fNRepetitions(nRepetitions), fIsSimple(isSimple),
334 fParent(nullptr), fPrincipalColumn(nullptr), fTraits(isSimple ? kTraitMappable : 0)
335{
336}
337
339{
340}
341
343{
344 std::string result = GetName();
345 RFieldBase *parent = GetParent();
346 while (parent && !parent->GetName().empty()) {
347 result = parent->GetName() + "." + result;
348 parent = parent->GetParent();
349 }
350 return result;
351}
352
354ROOT::Experimental::Detail::RFieldBase::Create(const std::string &fieldName, const std::string &typeName)
355{
356 auto typeAlias = GetNormalizedTypeName(typeName);
357 auto canonicalType = GetNormalizedTypeName(GetCanonicalTypeName(typeAlias));
358 return R__FORWARD_RESULT(RFieldBase::Create(fieldName, canonicalType, typeAlias));
359}
360
362ROOT::Experimental::Detail::RFieldBase::Create(const std::string &fieldName, const std::string &canonicalType,
363 const std::string &typeAlias)
364{
365 if (canonicalType.empty())
366 return R__FAIL("no type name specified for Field " + fieldName);
367
368 if (auto [arrayBaseType, arraySize] = ParseArrayType(canonicalType); !arraySize.empty()) {
369 // TODO(jalopezg): support multi-dimensional row-major (C order) arrays in RArrayField
370 if (arraySize.size() > 1)
371 return R__FAIL("multi-dimensional array type not supported " + canonicalType);
372 auto itemField = Create("_0", arrayBaseType).Unwrap();
373 return {std::make_unique<RArrayField>(fieldName, std::move(itemField), arraySize[0])};
374 }
375
376 std::unique_ptr<ROOT::Experimental::Detail::RFieldBase> result;
377
378 if (canonicalType == "ROOT::Experimental::ClusterSize_t") {
379 result = std::make_unique<RField<ClusterSize_t>>(fieldName);
380 } else if (canonicalType == "bool") {
381 result = std::make_unique<RField<bool>>(fieldName);
382 } else if (canonicalType == "char") {
383 result = std::make_unique<RField<char>>(fieldName);
384 } else if (canonicalType == "std::byte") {
385 result = std::make_unique<RField<std::byte>>(fieldName);
386 } else if (canonicalType == "std::int8_t") {
387 result = std::make_unique<RField<std::int8_t>>(fieldName);
388 } else if (canonicalType == "std::uint8_t") {
389 result = std::make_unique<RField<std::uint8_t>>(fieldName);
390 } else if (canonicalType == "std::int16_t") {
391 result = std::make_unique<RField<std::int16_t>>(fieldName);
392 } else if (canonicalType == "std::uint16_t") {
393 result = std::make_unique<RField<std::uint16_t>>(fieldName);
394 } else if (canonicalType == "std::int32_t") {
395 result = std::make_unique<RField<std::int32_t>>(fieldName);
396 } else if (canonicalType == "std::uint32_t") {
397 result = std::make_unique<RField<std::uint32_t>>(fieldName);
398 } else if (canonicalType == "std::int64_t") {
399 result = std::make_unique<RField<std::int64_t>>(fieldName);
400 } else if (canonicalType == "std::uint64_t") {
401 result = std::make_unique<RField<std::uint64_t>>(fieldName);
402 } else if (canonicalType == "float") {
403 result = std::make_unique<RField<float>>(fieldName);
404 } else if (canonicalType == "double") {
405 result = std::make_unique<RField<double>>(fieldName);
406 } else if (canonicalType == "Double32_t") {
407 result = std::make_unique<RField<double>>(fieldName);
408 static_cast<RField<double> *>(result.get())->SetDouble32();
409 // Prevent the type alias from being reset by returning early
410 return result;
411 } else if (canonicalType == "std::string") {
412 result = std::make_unique<RField<std::string>>(fieldName);
413 } else if (canonicalType == "std::vector<bool>") {
414 result = std::make_unique<RField<std::vector<bool>>>(fieldName);
415 } else if (canonicalType.substr(0, 12) == "std::vector<") {
416 std::string itemTypeName = canonicalType.substr(12, canonicalType.length() - 13);
417 auto itemField = Create("_0", itemTypeName);
418 result = std::make_unique<RVectorField>(fieldName, itemField.Unwrap());
419 } else if (canonicalType.substr(0, 19) == "ROOT::VecOps::RVec<") {
420 std::string itemTypeName = canonicalType.substr(19, canonicalType.length() - 20);
421 auto itemField = Create("_0", itemTypeName);
422 result = std::make_unique<RRVecField>(fieldName, itemField.Unwrap());
423 } else if (canonicalType.substr(0, 11) == "std::array<") {
424 auto arrayDef = TokenizeTypeList(canonicalType.substr(11, canonicalType.length() - 12));
425 R__ASSERT(arrayDef.size() == 2);
426 auto arrayLength = std::stoi(arrayDef[1]);
427 auto itemField = Create("_0", arrayDef[0]);
428 result = std::make_unique<RArrayField>(fieldName, itemField.Unwrap(), arrayLength);
429 } else if (canonicalType.substr(0, 13) == "std::variant<") {
430 auto innerTypes = TokenizeTypeList(canonicalType.substr(13, canonicalType.length() - 14));
431 std::vector<RFieldBase *> items;
432 for (unsigned int i = 0; i < innerTypes.size(); ++i) {
433 items.emplace_back(Create("_" + std::to_string(i), innerTypes[i]).Unwrap().release());
434 }
435 result = std::make_unique<RVariantField>(fieldName, items);
436 } else if (canonicalType.substr(0, 10) == "std::pair<") {
437 auto innerTypes = TokenizeTypeList(canonicalType.substr(10, canonicalType.length() - 11));
438 if (innerTypes.size() != 2)
439 return R__FAIL("the type list for std::pair must have exactly two elements");
440 std::array<std::unique_ptr<RFieldBase>, 2> items{Create("_0", innerTypes[0]).Unwrap(),
441 Create("_1", innerTypes[1]).Unwrap()};
442 result = std::make_unique<RPairField>(fieldName, items);
443 } else if (canonicalType.substr(0, 11) == "std::tuple<") {
444 auto innerTypes = TokenizeTypeList(canonicalType.substr(11, canonicalType.length() - 12));
445 std::vector<std::unique_ptr<RFieldBase>> items;
446 for (unsigned int i = 0; i < innerTypes.size(); ++i) {
447 items.emplace_back(Create("_" + std::to_string(i), innerTypes[i]).Unwrap());
448 }
449 result = std::make_unique<RTupleField>(fieldName, items);
450 } else if (canonicalType.substr(0, 12) == "std::bitset<") {
451 auto size = std::stoull(canonicalType.substr(12, canonicalType.length() - 13));
452 result = std::make_unique<RBitsetField>(fieldName, size);
453 } else if (canonicalType.substr(0, 16) == "std::unique_ptr<") {
454 std::string itemTypeName = canonicalType.substr(16, canonicalType.length() - 17);
455 auto itemField = Create("_0", itemTypeName).Unwrap();
456 auto normalizedInnerTypeName = itemField->GetType();
457 result = std::make_unique<RUniquePtrField>(fieldName, "std::unique_ptr<" + normalizedInnerTypeName + ">",
458 std::move(itemField));
459 } else if (canonicalType.substr(0, 9) == "std::set<") {
460 std::string itemTypeName = canonicalType.substr(9, canonicalType.length() - 10);
461 auto itemField = Create("_0", itemTypeName).Unwrap();
462 auto normalizedInnerTypeName = itemField->GetType();
463 result =
464 std::make_unique<RSetField>(fieldName, "std::set<" + normalizedInnerTypeName + ">", std::move(itemField));
465 } else if (canonicalType.substr(0, 19) == "std::unordered_set<") {
466 std::string itemTypeName = canonicalType.substr(19, canonicalType.length() - 20);
467 auto itemField = Create("_0", itemTypeName).Unwrap();
468 auto normalizedInnerTypeName = itemField->GetType();
469 result = std::make_unique<RSetField>(fieldName, "std::unordered_set<" + normalizedInnerTypeName + ">",
470 std::move(itemField));
471 } else if (canonicalType.substr(0, 9) == "std::map<") {
472 auto innerTypes = TokenizeTypeList(canonicalType.substr(9, canonicalType.length() - 10));
473 if (innerTypes.size() != 2)
474 return R__FAIL("the type list for std::map must have exactly two elements");
475
476 auto normalizedKeyTypeName = GetNormalizedTypeName(innerTypes[0]);
477 auto normalizedValueTypeName = GetNormalizedTypeName(innerTypes[1]);
478
479 auto itemField =
480 Create("_0", "std::pair<" + normalizedKeyTypeName + "," + normalizedValueTypeName + ">").Unwrap();
481 result = std::make_unique<RMapField>(
482 fieldName, "std::map<" + normalizedKeyTypeName + "," + normalizedValueTypeName + ">", std::move(itemField));
483 } else if (canonicalType.substr(0, 19) == "std::unordered_map<") {
484 auto innerTypes = TokenizeTypeList(canonicalType.substr(19, canonicalType.length() - 20));
485 if (innerTypes.size() != 2)
486 return R__FAIL("the type list for std::unordered_map must have exactly two elements");
487
488 auto normalizedKeyTypeName = GetNormalizedTypeName(innerTypes[0]);
489 auto normalizedValueTypeName = GetNormalizedTypeName(innerTypes[1]);
490
491 auto itemField =
492 Create("_0", "std::pair<" + normalizedKeyTypeName + "," + normalizedValueTypeName + ">").Unwrap();
493 result = std::make_unique<RMapField>(
494 fieldName, "std::unordered_map<" + normalizedKeyTypeName + "," + normalizedValueTypeName + ">",
495 std::move(itemField));
496 } else if (canonicalType.substr(0, 12) == "std::atomic<") {
497 std::string itemTypeName = canonicalType.substr(12, canonicalType.length() - 13);
498 auto itemField = Create("_0", itemTypeName).Unwrap();
499 auto normalizedInnerTypeName = itemField->GetType();
500 result = std::make_unique<RAtomicField>(fieldName, "std::atomic<" + normalizedInnerTypeName + ">",
501 std::move(itemField));
502 } else if (canonicalType == ":Collection:") {
503 // TODO: create an RCollectionField?
504 result = std::make_unique<RField<ClusterSize_t>>(fieldName);
505 } else if (canonicalType.substr(0, 39) == "ROOT::Experimental::RNTupleCardinality<") {
506 auto innerTypes = TokenizeTypeList(canonicalType.substr(39, canonicalType.length() - 40));
507 if (innerTypes.size() != 1)
508 return R__FAIL(std::string("Field ") + fieldName + " has invalid cardinality template: " + canonicalType);
509 if (innerTypes[0] == "std::uint32_t") {
510 result = std::make_unique<RField<RNTupleCardinality<std::uint32_t>>>(fieldName);
511 } else if (innerTypes[0] == "std::uint64_t") {
512 result = std::make_unique<RField<RNTupleCardinality<std::uint64_t>>>(fieldName);
513 } else {
514 return R__FAIL(std::string("Field ") + fieldName + " has invalid cardinality template: " + canonicalType);
515 }
516 }
517
518 if (!result) {
519 auto e = TEnum::GetEnum(canonicalType.c_str());
520 if (e != nullptr) {
521 result = std::make_unique<REnumField>(fieldName, canonicalType);
522 }
523 }
524
525 if (!result) {
526 auto cl = TClass::GetClass(canonicalType.c_str());
527 if (cl != nullptr) {
528 if (cl->GetCollectionProxy())
529 result = std::make_unique<RProxiedCollectionField>(fieldName, canonicalType);
530 else
531 result = std::make_unique<RClassField>(fieldName, canonicalType);
532 }
533 }
534
535 if (result) {
536 if (typeAlias != canonicalType)
537 result->fTypeAlias = typeAlias;
538 return result;
539 }
540 return R__FAIL(std::string("Field ") + fieldName + " has unknown type " + canonicalType);
541}
542
545{
546 if (fieldName.empty()) {
547 return R__FAIL("name cannot be empty string \"\"");
548 } else if (fieldName.find('.') != std::string::npos) {
549 return R__FAIL("name '" + std::string(fieldName) + "' cannot contain dot characters '.'");
550 }
551 return RResult<void>::Success();
552}
553
556{
557 static RColumnRepresentations representations;
558 return representations;
559}
560
561std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
563{
564 auto clone = CloneImpl(newName);
565 clone->fTypeAlias = fTypeAlias;
566 clone->fOnDiskId = fOnDiskId;
567 clone->fDescription = fDescription;
568 // We can just copy the pointer because fColumnRepresentative points into a static structure
569 clone->fColumnRepresentative = fColumnRepresentative;
570 return clone;
571}
572
573std::size_t ROOT::Experimental::Detail::RFieldBase::AppendImpl(const void * /* from */)
574{
575 R__ASSERT(false && "A non-simple RField must implement its own AppendImpl");
576 return 0;
577}
578
580{
581 R__ASSERT(false);
582}
583
585{
586 const auto valueSize = GetValueSize();
587 std::size_t nRead = 0;
588 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
589 // Value not needed
590 if (!bulkSpec.fMaskReq[i])
591 continue;
592
593 // Value already present
594 if (bulkSpec.fMaskAvail[i])
595 continue;
596
597 Read(bulkSpec.fFirstIndex + i, reinterpret_cast<unsigned char *>(bulkSpec.fValues) + i * valueSize);
598 bulkSpec.fMaskAvail[i] = true;
599 nRead++;
600 }
601 return nRead;
602}
603
605{
606 void *where = malloc(GetValueSize());
607 R__ASSERT(where != nullptr);
608 GenerateValue(where);
609 return RValue(this, where, true /* isOwning */);
610}
611
612void ROOT::Experimental::Detail::RFieldBase::DestroyValue(void *objPtr, bool dtorOnly) const
613{
614 if (!dtorOnly)
615 free(objPtr);
616}
617
618std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
620{
621 return std::vector<RValue>();
622}
623
625 std::unique_ptr<ROOT::Experimental::Detail::RFieldBase> child)
626{
627 // Note that during a model update, new fields will be attached to the zero field. The zero field, however,
628 // does not change its inital state because only its sub fields get connected by RPageSink::UpdateSchema.
629 if (fState != EState::kUnconnected)
630 throw RException(R__FAIL("invalid attempt to attach subfield to already connected field"));
631 child->fParent = this;
632 fSubFields.emplace_back(std::move(child));
633}
634
637{
638 std::size_t result = globalIndex;
639 for (auto f = this; f != nullptr; f = f->GetParent()) {
640 auto parent = f->GetParent();
641 if (parent && (parent->GetStructure() == kCollection || parent->GetStructure() == kVariant))
642 return 0U;
643 result *= std::max(f->GetNRepetitions(), std::size_t{1U});
644 }
645 return result;
646}
647
648std::vector<ROOT::Experimental::Detail::RFieldBase *> ROOT::Experimental::Detail::RFieldBase::GetSubFields() const
649{
650 std::vector<RFieldBase *> result;
651 result.reserve(fSubFields.size());
652 for (const auto &f : fSubFields) {
653 result.emplace_back(f.get());
654 }
655 return result;
656}
657
659{
660 for (auto& column : fColumns) {
661 column->Flush();
662 }
663 CommitClusterImpl();
664}
665
667{
668 if (fState != EState::kUnconnected)
669 throw RException(R__FAIL("cannot set field description once field is connected"));
670 fDescription = std::string(description);
671}
672
674{
675 if (fState != EState::kUnconnected)
676 throw RException(R__FAIL("cannot set field ID once field is connected"));
677 fOnDiskId = id;
678}
679
682{
683 if (fColumnRepresentative)
684 return *fColumnRepresentative;
685 return GetColumnRepresentations().GetSerializationDefault();
686}
687
689{
690 if (fState != EState::kUnconnected)
691 throw RException(R__FAIL("cannot set column representative once field is connected"));
692 const auto &validTypes = GetColumnRepresentations().GetSerializationTypes();
693 auto itRepresentative = std::find(validTypes.begin(), validTypes.end(), representative);
694 if (itRepresentative == std::end(validTypes))
695 throw RException(R__FAIL("invalid column representative"));
696 fColumnRepresentative = &(*itRepresentative);
697}
698
701{
702 if (fOnDiskId == kInvalidDescriptorId)
703 throw RException(R__FAIL("No on-disk column information for field `" + GetQualifiedFieldName() + "`"));
704
705 ColumnRepresentation_t onDiskTypes;
706 for (const auto &c : desc.GetColumnIterable(fOnDiskId)) {
707 onDiskTypes.emplace_back(c.GetModel().GetType());
708 }
709 for (const auto &t : GetColumnRepresentations().GetDeserializationTypes()) {
710 if (t == onDiskTypes)
711 return t;
712 }
713
714 std::string columnTypeNames;
715 for (const auto &t : onDiskTypes) {
716 if (!columnTypeNames.empty())
717 columnTypeNames += ", ";
718 columnTypeNames += RColumnElementBase::GetTypeName(t);
719 }
720 throw RException(R__FAIL("On-disk column types `" + columnTypeNames + "` for field `" + GetQualifiedFieldName() +
721 "` cannot be matched."));
722}
723
725{
726 fReadCallbacks.push_back(func);
727 fIsSimple = false;
728 return fReadCallbacks.size() - 1;
729}
730
732{
733 fReadCallbacks.erase(fReadCallbacks.begin() + idx);
734 fIsSimple = (fTraits & kTraitMappable) && fReadCallbacks.empty();
735}
736
738{
739 if ((options.GetCompression() == 0) && HasDefaultColumnRepresentative()) {
740 ColumnRepresentation_t rep = GetColumnRepresentative();
741 for (auto &colType : rep) {
742 switch (colType) {
745 case EColumnType::kSplitReal64: colType = EColumnType::kReal64; break;
746 case EColumnType::kSplitReal32: colType = EColumnType::kReal32; break;
747 case EColumnType::kSplitInt64: colType = EColumnType::kInt64; break;
748 case EColumnType::kSplitInt32: colType = EColumnType::kInt32; break;
749 case EColumnType::kSplitInt16: colType = EColumnType::kInt16; break;
750 default: break;
751 }
752 }
753 SetColumnRepresentative(rep);
754 }
755
756 if (options.GetHasSmallClusters()) {
757 ColumnRepresentation_t rep = GetColumnRepresentative();
758 for (auto &colType : rep) {
759 switch (colType) {
761 case EColumnType::kIndex64: colType = EColumnType::kIndex32; break;
762 default: break;
763 }
764 }
765 SetColumnRepresentative(rep);
766 }
767
768 if (fTypeAlias == "Double32_t")
769 SetColumnRepresentative({EColumnType::kSplitReal32});
770}
771
773{
774 if (dynamic_cast<ROOT::Experimental::RFieldZero *>(this))
775 throw RException(R__FAIL("invalid attempt to connect zero field to page sink"));
776 if (fState != EState::kUnconnected)
777 throw RException(R__FAIL("invalid attempt to connect an already connected field to a page sink"));
778
779 AutoAdjustColumnTypes(pageSink.GetWriteOptions());
780
781 GenerateColumnsImpl();
782 if (!fColumns.empty())
783 fPrincipalColumn = fColumns[0].get();
784 for (auto &column : fColumns) {
785 auto firstElementIndex = (column.get() == fPrincipalColumn) ? EntryToColumnElementIndex(firstEntry) : 0;
786 column->Connect(fOnDiskId, &pageSink, firstElementIndex);
787 }
788
789 fState = EState::kConnectedToSink;
790}
791
792
794{
795 if (dynamic_cast<ROOT::Experimental::RFieldZero *>(this))
796 throw RException(R__FAIL("invalid attempt to connect zero field to page source"));
797 if (fState != EState::kUnconnected)
798 throw RException(R__FAIL("invalid attempt to connect an already connected field to a page source"));
799
800 if (fColumnRepresentative)
801 throw RException(R__FAIL("fixed column representative only valid when connecting to a page sink"));
802 if (!fDescription.empty())
803 throw RException(R__FAIL("setting description only valid when connecting to a page sink"));
804
805 {
806 const auto descriptorGuard = pageSource.GetSharedDescriptorGuard();
807 const RNTupleDescriptor &desc = descriptorGuard.GetRef();
808 GenerateColumnsImpl(desc);
809 ColumnRepresentation_t onDiskColumnTypes;
810 for (const auto &c : fColumns) {
811 onDiskColumnTypes.emplace_back(c->GetModel().GetType());
812 }
813 for (const auto &t : GetColumnRepresentations().GetDeserializationTypes()) {
814 if (t == onDiskColumnTypes)
815 fColumnRepresentative = &t;
816 }
817 R__ASSERT(fColumnRepresentative);
818 if (fOnDiskId != kInvalidDescriptorId)
819 fOnDiskTypeVersion = desc.GetFieldDescriptor(fOnDiskId).GetTypeVersion();
820 }
821 if (!fColumns.empty())
822 fPrincipalColumn = fColumns[0].get();
823 for (auto& column : fColumns)
824 column->Connect(fOnDiskId, &pageSource);
825 OnConnectPageSource();
826
827 fState = EState::kConnectedToSource;
828}
829
830
832{
833 visitor.VisitField(*this);
834}
835
836//-----------------------------------------------------------------------------
837
838std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
839ROOT::Experimental::RFieldZero::CloneImpl(std::string_view /*newName*/) const
840{
841 auto result = std::make_unique<RFieldZero>();
842 for (auto &f : fSubFields)
843 result->Attach(f->Clone(f->GetName()));
844 return result;
845}
846
847
849{
850 visitor.VisitFieldZero(*this);
851}
852
853
854//------------------------------------------------------------------------------
855
858{
859 static RColumnRepresentations representations(
861 {});
862 return representations;
863}
864
866{
867 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
868}
869
871{
872 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
873 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
874}
875
877{
878 visitor.VisitClusterSizeField(*this);
879}
880
881//------------------------------------------------------------------------------
882
885{
886 static RColumnRepresentations representations(
888 {});
889 return representations;
890}
891
893{
894 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
895 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
896}
897
899{
900 visitor.VisitCardinalityField(*this);
901}
902
905{
906 return dynamic_cast<const RField<RNTupleCardinality<std::uint32_t>> *>(this);
907}
908
911{
912 return dynamic_cast<const RField<RNTupleCardinality<std::uint64_t>> *>(this);
913}
914
915//------------------------------------------------------------------------------
916
919{
920 static RColumnRepresentations representations({{EColumnType::kChar}}, {{}});
921 return representations;
922}
923
925{
926 fColumns.emplace_back(Detail::RColumn::Create<char>(RColumnModel(GetColumnRepresentative()[0]), 0));
927}
928
930{
931 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
932 fColumns.emplace_back(Detail::RColumn::Create<char>(RColumnModel(onDiskTypes[0]), 0));
933}
934
936{
937 visitor.VisitCharField(*this);
938}
939
940//------------------------------------------------------------------------------
941
944{
945 static RColumnRepresentations representations({{EColumnType::kByte}}, {{}});
946 return representations;
947}
948
950{
951 fColumns.emplace_back(Detail::RColumn::Create<char>(RColumnModel(GetColumnRepresentative()[0]), 0));
952}
953
954void ROOT::Experimental::RField<std::byte>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
955{
956 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
957 fColumns.emplace_back(Detail::RColumn::Create<char>(RColumnModel(onDiskTypes[0]), 0));
958}
959
960void ROOT::Experimental::RField<std::byte>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
961{
962 visitor.VisitByteField(*this);
963}
964
965//------------------------------------------------------------------------------
966
969{
970 static RColumnRepresentations representations({{EColumnType::kInt8}}, {{EColumnType::kUInt8}});
971 return representations;
972}
973
975{
976 fColumns.emplace_back(Detail::RColumn::Create<std::int8_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
977}
978
979void ROOT::Experimental::RField<std::int8_t>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
980{
981 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
982 fColumns.emplace_back(Detail::RColumn::Create<std::int8_t>(RColumnModel(onDiskTypes[0]), 0));
983}
984
985void ROOT::Experimental::RField<std::int8_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
986{
987 visitor.VisitInt8Field(*this);
988}
989
990//------------------------------------------------------------------------------
991
994{
995 static RColumnRepresentations representations({{EColumnType::kUInt8}}, {{EColumnType::kInt8}});
996 return representations;
997}
998
1000{
1001 fColumns.emplace_back(Detail::RColumn::Create<std::uint8_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1002}
1003
1004void ROOT::Experimental::RField<std::uint8_t>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
1005{
1006 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1007 fColumns.emplace_back(Detail::RColumn::Create<std::uint8_t>(RColumnModel(onDiskTypes[0]), 0));
1008}
1009
1010void ROOT::Experimental::RField<std::uint8_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1011{
1012 visitor.VisitUInt8Field(*this);
1013}
1014
1015//------------------------------------------------------------------------------
1016
1019{
1020 static RColumnRepresentations representations({{EColumnType::kBit}}, {});
1021 return representations;
1022}
1023
1025{
1026 fColumns.emplace_back(Detail::RColumn::Create<bool>(RColumnModel(GetColumnRepresentative()[0]), 0));
1027}
1028
1030{
1031 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1032 fColumns.emplace_back(Detail::RColumn::Create<bool>(RColumnModel(onDiskTypes[0]), 0));
1033}
1034
1036{
1037 visitor.VisitBoolField(*this);
1038}
1039
1040//------------------------------------------------------------------------------
1041
1044{
1045 static RColumnRepresentations representations(
1047 return representations;
1048}
1049
1051{
1052 fColumns.emplace_back(Detail::RColumn::Create<float>(RColumnModel(GetColumnRepresentative()[0]), 0));
1053}
1054
1056{
1057 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1058 fColumns.emplace_back(Detail::RColumn::Create<float>(RColumnModel(onDiskTypes[0]), 0));
1059}
1060
1062{
1063 visitor.VisitFloatField(*this);
1064}
1065
1067{
1068 SetColumnRepresentative({EColumnType::kReal16});
1069}
1070
1071//------------------------------------------------------------------------------
1072
1075{
1076 static RColumnRepresentations representations(
1078 return representations;
1079}
1080
1082{
1083 fColumns.emplace_back(Detail::RColumn::Create<double>(RColumnModel(GetColumnRepresentative()[0]), 0));
1084}
1085
1087{
1088 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1089 fColumns.emplace_back(Detail::RColumn::Create<double>(RColumnModel(onDiskTypes[0]), 0));
1090}
1091
1093{
1094 visitor.VisitDoubleField(*this);
1095}
1096
1098{
1099 fTypeAlias = "Double32_t";
1100}
1101
1102//------------------------------------------------------------------------------
1103
1106{
1107 static RColumnRepresentations representations({{EColumnType::kSplitInt16}, {EColumnType::kInt16}},
1109 return representations;
1110}
1111
1113{
1114 fColumns.emplace_back(Detail::RColumn::Create<std::int16_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1115}
1116
1117void ROOT::Experimental::RField<std::int16_t>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
1118{
1119 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1120 fColumns.emplace_back(Detail::RColumn::Create<std::int16_t>(RColumnModel(onDiskTypes[0]), 0));
1121}
1122
1123void ROOT::Experimental::RField<std::int16_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1124{
1125 visitor.VisitInt16Field(*this);
1126}
1127
1128//------------------------------------------------------------------------------
1129
1132{
1133 static RColumnRepresentations representations({{EColumnType::kSplitUInt16}, {EColumnType::kUInt16}},
1135 return representations;
1136}
1137
1139{
1140 fColumns.emplace_back(Detail::RColumn::Create<std::uint16_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1141}
1142
1144{
1145 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1146 fColumns.emplace_back(Detail::RColumn::Create<std::uint16_t>(RColumnModel(onDiskTypes[0]), 0));
1147}
1148
1149void ROOT::Experimental::RField<std::uint16_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1150{
1151 visitor.VisitUInt16Field(*this);
1152}
1153
1154//------------------------------------------------------------------------------
1155
1158{
1159 static RColumnRepresentations representations({{EColumnType::kSplitInt32}, {EColumnType::kInt32}},
1161 return representations;
1162}
1163
1165{
1166 fColumns.emplace_back(Detail::RColumn::Create<std::int32_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1167}
1168
1169void ROOT::Experimental::RField<std::int32_t>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
1170{
1171 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1172 fColumns.emplace_back(Detail::RColumn::Create<std::int32_t>(RColumnModel(onDiskTypes[0]), 0));
1173}
1174
1175void ROOT::Experimental::RField<std::int32_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1176{
1177 visitor.VisitIntField(*this);
1178}
1179
1180//------------------------------------------------------------------------------
1181
1184{
1185 static RColumnRepresentations representations({{EColumnType::kSplitUInt32}, {EColumnType::kUInt32}},
1187 return representations;
1188}
1189
1191{
1192 fColumns.emplace_back(Detail::RColumn::Create<std::uint32_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1193}
1194
1196{
1197 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1198 fColumns.emplace_back(Detail::RColumn::Create<std::uint32_t>(RColumnModel(onDiskTypes[0]), 0));
1199}
1200
1201void ROOT::Experimental::RField<std::uint32_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1202{
1203 visitor.VisitUInt32Field(*this);
1204}
1205
1206//------------------------------------------------------------------------------
1207
1210{
1211 static RColumnRepresentations representations({{EColumnType::kSplitUInt64}, {EColumnType::kUInt64}},
1213 return representations;
1214}
1215
1217{
1218 fColumns.emplace_back(Detail::RColumn::Create<std::uint64_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1219}
1220
1222{
1223 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1224 fColumns.emplace_back(Detail::RColumn::Create<std::uint64_t>(RColumnModel(onDiskTypes[0]), 0));
1225}
1226
1227void ROOT::Experimental::RField<std::uint64_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1228{
1229 visitor.VisitUInt64Field(*this);
1230}
1231
1232//------------------------------------------------------------------------------
1233
1236{
1237 static RColumnRepresentations representations({{EColumnType::kSplitInt64}, {EColumnType::kInt64}},
1244 return representations;
1245}
1246
1248{
1249 fColumns.emplace_back(Detail::RColumn::Create<std::int64_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1250}
1251
1252void ROOT::Experimental::RField<std::int64_t>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
1253{
1254 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1255 fColumns.emplace_back(Detail::RColumn::Create<std::int64_t>(RColumnModel(onDiskTypes[0]), 0));
1256}
1257
1258void ROOT::Experimental::RField<std::int64_t>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1259{
1260 visitor.VisitInt64Field(*this);
1261}
1262
1263//------------------------------------------------------------------------------
1264
1267{
1268 static RColumnRepresentations representations({{EColumnType::kSplitIndex64, EColumnType::kChar},
1272 {});
1273 return representations;
1274}
1275
1277{
1278 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1279 fColumns.emplace_back(Detail::RColumn::Create<char>(RColumnModel(GetColumnRepresentative()[1]), 1));
1280}
1281
1282void ROOT::Experimental::RField<std::string>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
1283{
1284 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1285 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
1286 fColumns.emplace_back(Detail::RColumn::Create<char>(RColumnModel(onDiskTypes[1]), 1));
1287}
1288
1289void ROOT::Experimental::RField<std::string>::DestroyValue(void *objPtr, bool dtorOnly) const
1290{
1291 std::destroy_at(static_cast<std::string *>(objPtr));
1292 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
1293}
1294
1295std::size_t ROOT::Experimental::RField<std::string>::AppendImpl(const void *from)
1296{
1297 auto typedValue = static_cast<const std::string *>(from);
1298 auto length = typedValue->length();
1299 fColumns[1]->AppendV(typedValue->data(), length);
1300 fIndex += length;
1301 fColumns[0]->Append(&fIndex);
1302 return length + fColumns[0]->GetElement()->GetPackedSize();
1303}
1304
1306{
1307 auto typedValue = static_cast<std::string *>(to);
1308 RClusterIndex collectionStart;
1309 ClusterSize_t nChars;
1310 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nChars);
1311 if (nChars == 0) {
1312 typedValue->clear();
1313 } else {
1314 typedValue->resize(nChars);
1315 fColumns[1]->ReadV(collectionStart, nChars, const_cast<char *>(typedValue->data()));
1316 }
1317}
1318
1319void ROOT::Experimental::RField<std::string>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
1320{
1321 visitor.VisitStringField(*this);
1322}
1323
1324//------------------------------------------------------------------------------
1325
1326
1327ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::string_view className)
1328 : RClassField(fieldName, className, TClass::GetClass(std::string(className).c_str()))
1329{
1330}
1331
1332ROOT::Experimental::RClassField::RClassField(std::string_view fieldName, std::string_view className, TClass *classp)
1333 : ROOT::Experimental::Detail::RFieldBase(fieldName, className, ENTupleStructure::kRecord, false /* isSimple */)
1334 , fClass(classp)
1335{
1336 if (fClass == nullptr) {
1337 throw RException(R__FAIL("RField: no I/O support for type " + std::string(className)));
1338 }
1339 // Avoid accidentally supporting std types through TClass.
1340 if (fClass->Property() & kIsDefinedInStd) {
1341 throw RException(R__FAIL(std::string(className) + " is not supported"));
1342 }
1343 if (fClass->GetCollectionProxy()) {
1344 throw RException(
1345 R__FAIL(std::string(className) + " has an associated collection proxy; use RProxiedCollectionField instead"));
1346 }
1347
1352
1353 int i = 0;
1355 TClass *c = baseClass->GetClassPointer();
1356 auto subField = Detail::RFieldBase::Create(std::string(kPrefixInherited) + "_" + std::to_string(i),
1357 c->GetName()).Unwrap();
1358 fTraits &= subField->GetTraits();
1359 Attach(std::move(subField),
1360 RSubFieldInfo{kBaseClass, static_cast<std::size_t>(baseClass->GetDelta())});
1361 i++;
1362 }
1364 // Skip, for instance, unscoped enum constants defined in the class
1365 if (dataMember->Property() & kIsStatic)
1366 continue;
1367 // Skip members explicitly marked as transient by user comment
1368 if (!dataMember->IsPersistent()) {
1369 // TODO(jblomer): we could do better
1371 continue;
1372 }
1373
1374 std::string typeName{GetNormalizedTypeName(dataMember->GetTrueTypeName())};
1375 std::string typeAlias{GetNormalizedTypeName(dataMember->GetFullTypeName())};
1376 // For C-style arrays, complete the type name with the size for each dimension, e.g. `int[4][2]`
1377 if (dataMember->Property() & kIsArray) {
1378 for (int dim = 0, n = dataMember->GetArrayDim(); dim < n; ++dim)
1379 typeName += "[" + std::to_string(dataMember->GetMaxIndex(dim)) + "]";
1380 }
1381 auto subField = Detail::RFieldBase::Create(dataMember->GetName(), typeName, typeAlias).Unwrap();
1382 fTraits &= subField->GetTraits();
1383 Attach(std::move(subField),
1384 RSubFieldInfo{kDataMember, static_cast<std::size_t>(dataMember->GetOffset())});
1385 }
1386}
1387
1388void ROOT::Experimental::RClassField::Attach(std::unique_ptr<Detail::RFieldBase> child, RSubFieldInfo info)
1389{
1390 fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
1391 fSubFieldsInfo.push_back(info);
1392 RFieldBase::Attach(std::move(child));
1393}
1394
1395void ROOT::Experimental::RClassField::AddReadCallbacksFromIORules(const std::span<const ROOT::TSchemaRule *> rules,
1396 TClass *classp)
1397{
1398 for (const auto rule : rules) {
1399 if (rule->GetRuleType() != ROOT::TSchemaRule::kReadRule) {
1400 R__LOG_WARNING(NTupleLog()) << "ignoring I/O customization rule with unsupported type";
1401 continue;
1402 }
1403 auto func = rule->GetReadFunctionPointer();
1404 R__ASSERT(func != nullptr);
1405 fReadCallbacks.emplace_back([func, classp](void *target) {
1406 TVirtualObject oldObj{nullptr};
1407 oldObj.fClass = classp;
1408 oldObj.fObject = target;
1409 func(static_cast<char *>(target), &oldObj);
1410 oldObj.fClass = nullptr; // TVirtualObject does not own the value
1411 });
1412 }
1413}
1414
1415std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
1416ROOT::Experimental::RClassField::CloneImpl(std::string_view newName) const
1417{
1418 auto result = std::unique_ptr<RClassField>(new RClassField(newName, GetType(), fClass));
1419 SyncFieldIDs(*this, *result);
1420 return result;
1421}
1422
1424{
1425 std::size_t nbytes = 0;
1426 for (unsigned i = 0; i < fSubFields.size(); i++) {
1427 nbytes += CallAppendOn(*fSubFields[i], static_cast<const unsigned char *>(from) + fSubFieldsInfo[i].fOffset);
1428 }
1429 return nbytes;
1430}
1431
1433{
1434 for (unsigned i = 0; i < fSubFields.size(); i++) {
1435 CallReadOn(*fSubFields[i], globalIndex, static_cast<unsigned char *>(to) + fSubFieldsInfo[i].fOffset);
1436 }
1437}
1438
1440{
1441 for (unsigned i = 0; i < fSubFields.size(); i++) {
1442 CallReadOn(*fSubFields[i], clusterIndex, static_cast<unsigned char *>(to) + fSubFieldsInfo[i].fOffset);
1443 }
1444}
1445
1447{
1448 // Add post-read callbacks for I/O customization rules; only rules that target transient members are allowed for now
1449 // TODO(jalopezg): revise after supporting schema evolution
1450 const auto ruleset = fClass->GetSchemaRules();
1451 if (!ruleset)
1452 return;
1453 auto referencesNonTransientMembers = [klass = fClass](const ROOT::TSchemaRule *rule) {
1454 if (rule->GetTarget() == nullptr)
1455 return false;
1456 for (auto target : ROOT::Detail::TRangeStaticCast<TObjString>(*rule->GetTarget())) {
1457 const auto dataMember = klass->GetDataMember(target->GetString());
1458 if (!dataMember || dataMember->IsPersistent()) {
1459 R__LOG_WARNING(NTupleLog()) << "ignoring I/O customization rule with non-transient member: "
1460 << dataMember->GetName();
1461 return true;
1462 }
1463 }
1464 return false;
1465 };
1466
1467 auto rules = ruleset->FindRules(fClass->GetName(), static_cast<Int_t>(GetOnDiskTypeVersion()));
1468 rules.erase(std::remove_if(rules.begin(), rules.end(), referencesNonTransientMembers), rules.end());
1469 AddReadCallbacksFromIORules(rules, fClass);
1470}
1471
1473{
1474 fClass->New(where);
1475}
1476
1477void ROOT::Experimental::RClassField::DestroyValue(void *objPtr, bool dtorOnly) const
1478{
1479 fClass->Destructor(objPtr, true /* dtorOnly */);
1480 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
1481}
1482
1483std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
1485{
1486 std::vector<RValue> result;
1487 for (unsigned i = 0; i < fSubFields.size(); i++) {
1488 result.emplace_back(fSubFields[i]->BindValue(value.Get<unsigned char>() + fSubFieldsInfo[i].fOffset));
1489 }
1490 return result;
1491}
1492
1493
1495{
1496 return fClass->GetClassSize();
1497}
1498
1500{
1501 return fClass->GetClassVersion();
1502}
1503
1505{
1506 visitor.VisitClassField(*this);
1507}
1508
1509//------------------------------------------------------------------------------
1510
1511ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
1512 : REnumField(fieldName, enumName, TEnum::GetEnum(std::string(enumName).c_str()))
1513{
1514}
1515
1516ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName, TEnum *enump)
1517 : ROOT::Experimental::Detail::RFieldBase(fieldName, enumName, ENTupleStructure::kLeaf, false /* isSimple */)
1518{
1519 if (enump == nullptr) {
1520 throw RException(R__FAIL("RField: no I/O support for enum type " + std::string(enumName)));
1521 }
1522 // Avoid accidentally supporting std types through TEnum.
1523 if (enump->Property() & kIsDefinedInStd) {
1524 throw RException(R__FAIL(std::string(enumName) + " is not supported"));
1525 }
1526
1527 switch (enump->GetUnderlyingType()) {
1528 case kChar_t: Attach(std::make_unique<RField<int8_t>>("_0")); break;
1529 case kUChar_t: Attach(std::make_unique<RField<uint8_t>>("_0")); break;
1530 case kShort_t: Attach(std::make_unique<RField<int16_t>>("_0")); break;
1531 case kUShort_t: Attach(std::make_unique<RField<uint16_t>>("_0")); break;
1532 case kInt_t: Attach(std::make_unique<RField<int32_t>>("_0")); break;
1533 case kUInt_t: Attach(std::make_unique<RField<uint32_t>>("_0")); break;
1534 case kLong_t:
1535 case kLong64_t: Attach(std::make_unique<RField<int64_t>>("_0")); break;
1536 case kULong_t:
1537 case kULong64_t: Attach(std::make_unique<RField<uint64_t>>("_0")); break;
1538 default: throw RException(R__FAIL("Unsupported underlying integral type for enum type " + std::string(enumName)));
1539 }
1540
1542}
1543
1544ROOT::Experimental::REnumField::REnumField(std::string_view fieldName, std::string_view enumName,
1545 std::unique_ptr<RFieldBase> intField)
1546 : ROOT::Experimental::Detail::RFieldBase(fieldName, enumName, ENTupleStructure::kLeaf, false /* isSimple */)
1547{
1548 Attach(std::move(intField));
1550}
1551
1552std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
1553ROOT::Experimental::REnumField::CloneImpl(std::string_view newName) const
1554{
1555 auto newIntField = fSubFields[0]->Clone(fSubFields[0]->GetName());
1556 return std::unique_ptr<REnumField>(new REnumField(newName, GetType(), std::move(newIntField)));
1557}
1558
1559std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
1561{
1562 std::vector<RValue> result;
1563 result.emplace_back(fSubFields[0]->BindValue(value.GetRawPtr()));
1564 return result;
1565}
1566
1568{
1569 visitor.VisitEnumField(*this);
1570}
1571
1572//------------------------------------------------------------------------------
1573
1576 bool readFromDisk)
1577{
1578 RIteratorFuncs ifuncs;
1579 ifuncs.fCreateIterators = proxy->GetFunctionCreateIterators(readFromDisk);
1580 ifuncs.fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(readFromDisk);
1581 ifuncs.fNext = proxy->GetFunctionNext(readFromDisk);
1582 R__ASSERT((ifuncs.fCreateIterators != nullptr) && (ifuncs.fDeleteTwoIterators != nullptr) &&
1583 (ifuncs.fNext != nullptr));
1584 return ifuncs;
1585}
1586
1588 std::string_view typeName, TClass *classp)
1589 : Detail::RFieldBase(fieldName, typeName, ENTupleStructure::kCollection, false /* isSimple */), fNWritten(0)
1590{
1591 if (classp == nullptr)
1592 throw RException(R__FAIL("RField: no I/O support for collection proxy type " + std::string(typeName)));
1593 if (!classp->GetCollectionProxy())
1594 throw RException(R__FAIL(std::string(typeName) + " has no associated collection proxy"));
1595
1596 fProxy.reset(classp->GetCollectionProxy()->Generate());
1597 fProperties = fProxy->GetProperties();
1598 fCollectionType = fProxy->GetCollectionType();
1599 if (fProxy->HasPointers())
1600 throw RException(R__FAIL("collection proxies whose value type is a pointer are not supported"));
1601 if (!fProxy->GetCollectionClass()->HasDictionary()) {
1602 throw RException(R__FAIL("dictionary not available for type " +
1603 GetNormalizedTypeName(fProxy->GetCollectionClass()->GetName())));
1604 }
1605
1606 fIFuncsRead = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), true /* readFromDisk */);
1607 fIFuncsWrite = RCollectionIterableOnce::GetIteratorFuncs(fProxy.get(), false /* readFromDisk */);
1608}
1609
1611 std::string_view typeName,
1612 std::unique_ptr<Detail::RFieldBase> itemField)
1613 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
1614{
1615 fItemSize = itemField->GetValueSize();
1616 Attach(std::move(itemField));
1617}
1618
1620 std::string_view typeName)
1621 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
1622{
1623 // NOTE (fdegeus): std::map is supported, custom associative might be supported in the future if the need arises.
1625 throw RException(R__FAIL("custom associative collection proxies not supported"));
1626
1627 std::unique_ptr<ROOT::Experimental::Detail::RFieldBase> itemField;
1628
1629 if (auto valueClass = fProxy->GetValueClass()) {
1630 // Element type is a class
1631 itemField = RFieldBase::Create("_0", valueClass->GetName()).Unwrap();
1632 } else {
1633 switch (fProxy->GetType()) {
1634 case EDataType::kChar_t: itemField = std::make_unique<RField<char>>("_0"); break;
1635 case EDataType::kUChar_t: itemField = std::make_unique<RField<std::uint8_t>>("_0"); break;
1636 case EDataType::kShort_t: itemField = std::make_unique<RField<std::int16_t>>("_0"); break;
1637 case EDataType::kUShort_t: itemField = std::make_unique<RField<std::uint16_t>>("_0"); break;
1638 case EDataType::kInt_t: itemField = std::make_unique<RField<std::int32_t>>("_0"); break;
1639 case EDataType::kUInt_t: itemField = std::make_unique<RField<std::uint32_t>>("_0"); break;
1640 case EDataType::kLong_t:
1642 itemField = std::make_unique<RField<std::int64_t>>("_0");
1643 break;
1646 itemField = std::make_unique<RField<std::uint64_t>>("_0");
1647 break;
1648 case EDataType::kFloat_t: itemField = std::make_unique<RField<float>>("_0"); break;
1649 case EDataType::kDouble_t: itemField = std::make_unique<RField<double>>("_0"); break;
1650 case EDataType::kBool_t: itemField = std::make_unique<RField<bool>>("_0"); break;
1651 default:
1652 throw RException(R__FAIL("unsupported value type"));
1653 }
1654 }
1655
1656 fItemSize = itemField->GetValueSize();
1657 Attach(std::move(itemField));
1658}
1659
1660std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
1662{
1663 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
1664 return std::unique_ptr<RProxiedCollectionField>(
1665 new RProxiedCollectionField(newName, GetType(), std::move(newItemField)));
1666}
1667
1669{
1670 std::size_t nbytes = 0;
1671 unsigned count = 0;
1672 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
1673 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(),
1674 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1675 nbytes += CallAppendOn(*fSubFields[0], ptr);
1676 count++;
1677 }
1678
1679 fNWritten += count;
1680 fColumns[0]->Append(&fNWritten);
1681 return nbytes + fColumns[0]->GetElement()->GetPackedSize();
1682}
1683
1685{
1686 ClusterSize_t nItems;
1687 RClusterIndex collectionStart;
1688 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1689
1690 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
1691 void *obj =
1692 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
1693
1694 unsigned i = 0;
1695 for (auto elementPtr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(),
1696 (fCollectionType == kSTLvector || obj != to ? fItemSize : 0U)}) {
1697 CallReadOn(*fSubFields[0], collectionStart + (i++), elementPtr);
1698 }
1699 if (obj != to)
1700 fProxy->Commit(obj);
1701}
1702
1705{
1706 static RColumnRepresentations representations(
1708 {});
1709 return representations;
1710}
1711
1713{
1714 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1715}
1716
1718{
1719 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1720 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
1721}
1722
1724{
1725 fProxy->New(where);
1726}
1727
1729{
1730 if (fProperties & TVirtualCollectionProxy::kNeedDelete) {
1731 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), objPtr);
1732 for (auto ptr : RCollectionIterableOnce{objPtr, fIFuncsWrite, fProxy.get(),
1733 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1734 CallDestroyValueOn(*fSubFields[0], ptr, true /* dtorOnly */);
1735 }
1736 }
1737 fProxy->Destructor(objPtr, true /* dtorOnly */);
1738 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
1739}
1740
1741std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
1743{
1744 std::vector<RValue> result;
1745 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), value.GetRawPtr());
1746 for (auto ptr : RCollectionIterableOnce{value.GetRawPtr(), fIFuncsWrite, fProxy.get(),
1747 (fCollectionType == kSTLvector ? fItemSize : 0U)}) {
1748 result.emplace_back(fSubFields[0]->BindValue(ptr));
1749 }
1750 return result;
1751}
1752
1754{
1755 visitor.VisitProxiedCollectionField(*this);
1756}
1757
1758//------------------------------------------------------------------------------
1759
1761 std::vector<std::unique_ptr<Detail::RFieldBase>> &&itemFields,
1762 const std::vector<std::size_t> &offsets, std::string_view typeName)
1763 : ROOT::Experimental::Detail::RFieldBase(fieldName, typeName, ENTupleStructure::kRecord, false /* isSimple */),
1764 fOffsets(offsets)
1765{
1767 for (auto &item : itemFields) {
1768 fMaxAlignment = std::max(fMaxAlignment, item->GetAlignment());
1769 fSize += GetItemPadding(fSize, item->GetAlignment()) + item->GetValueSize();
1770 fTraits &= item->GetTraits();
1771 Attach(std::move(item));
1772 }
1773}
1774
1776 std::vector<std::unique_ptr<Detail::RFieldBase>> &&itemFields)
1777 : ROOT::Experimental::Detail::RFieldBase(fieldName, "", ENTupleStructure::kRecord, false /* isSimple */)
1778{
1780 for (auto &item : itemFields) {
1781 fSize += GetItemPadding(fSize, item->GetAlignment());
1782 fOffsets.push_back(fSize);
1783 fMaxAlignment = std::max(fMaxAlignment, item->GetAlignment());
1784 fSize += item->GetValueSize();
1785 fTraits &= item->GetTraits();
1786 Attach(std::move(item));
1787 }
1788 // Trailing padding: although this is implementation-dependent, most add enough padding to comply with the
1789 // requirements of the type with strictest alignment
1791}
1792
1794 std::vector<std::unique_ptr<Detail::RFieldBase>> &itemFields)
1795 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields))
1796{
1797}
1798
1799std::size_t ROOT::Experimental::RRecordField::GetItemPadding(std::size_t baseOffset, std::size_t itemAlignment) const
1800{
1801 if (itemAlignment > 1) {
1802 auto remainder = baseOffset % itemAlignment;
1803 if (remainder != 0)
1804 return itemAlignment - remainder;
1805 }
1806 return 0;
1807}
1808
1809std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
1810ROOT::Experimental::RRecordField::CloneImpl(std::string_view newName) const
1811{
1812 std::vector<std::unique_ptr<Detail::RFieldBase>> cloneItems;
1813 cloneItems.reserve(fSubFields.size());
1814 for (auto &item : fSubFields)
1815 cloneItems.emplace_back(item->Clone(item->GetName()));
1816 return std::unique_ptr<RRecordField>(new RRecordField(newName, std::move(cloneItems), fOffsets, GetType()));
1817}
1818
1820{
1821 std::size_t nbytes = 0;
1822 for (unsigned i = 0; i < fSubFields.size(); ++i) {
1823 nbytes += CallAppendOn(*fSubFields[i], static_cast<const unsigned char *>(from) + fOffsets[i]);
1824 }
1825 return nbytes;
1826}
1827
1829{
1830 for (unsigned i = 0; i < fSubFields.size(); ++i) {
1831 CallReadOn(*fSubFields[i], globalIndex, static_cast<unsigned char *>(to) + fOffsets[i]);
1832 }
1833}
1834
1836{
1837 for (unsigned i = 0; i < fSubFields.size(); ++i) {
1838 CallReadOn(*fSubFields[i], clusterIndex, static_cast<unsigned char *>(to) + fOffsets[i]);
1839 }
1840}
1841
1843{
1844 for (unsigned i = 0; i < fSubFields.size(); ++i) {
1845 CallGenerateValueOn(*fSubFields[i], static_cast<unsigned char *>(where) + fOffsets[i]);
1846 }
1847}
1848
1849void ROOT::Experimental::RRecordField::DestroyValue(void *objPtr, bool dtorOnly) const
1850{
1851 for (unsigned i = 0; i < fSubFields.size(); ++i) {
1852 CallDestroyValueOn(*fSubFields[i], static_cast<unsigned char *>(objPtr) + fOffsets[i], true /* dtorOnly */);
1853 }
1854 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
1855}
1856
1857std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
1859{
1860 std::vector<RValue> result;
1861 for (unsigned i = 0; i < fSubFields.size(); ++i) {
1862 result.emplace_back(fSubFields[i]->BindValue(value.Get<unsigned char>() + fOffsets[i]));
1863 }
1864 return result;
1865}
1866
1867
1869{
1870 visitor.VisitRecordField(*this);
1871}
1872
1873//------------------------------------------------------------------------------
1874
1875
1877 std::string_view fieldName, std::unique_ptr<Detail::RFieldBase> itemField)
1878 : ROOT::Experimental::Detail::RFieldBase(
1879 fieldName, "std::vector<" + itemField->GetType() + ">", ENTupleStructure::kCollection, false /* isSimple */)
1880 , fItemSize(itemField->GetValueSize()), fNWritten(0)
1881{
1882 Attach(std::move(itemField));
1883}
1884
1885std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
1886ROOT::Experimental::RVectorField::CloneImpl(std::string_view newName) const
1887{
1888 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
1889 return std::make_unique<RVectorField>(newName, std::move(newItemField));
1890}
1891
1893{
1894 auto typedValue = static_cast<const std::vector<char> *>(from);
1895 R__ASSERT((typedValue->size() % fItemSize) == 0);
1896 std::size_t nbytes = 0;
1897 auto count = typedValue->size() / fItemSize;
1898
1899 if (fSubFields[0]->IsSimple() && count) {
1900 GetPrincipalColumnOf(*fSubFields[0])->AppendV(typedValue->data(), count);
1901 nbytes += count * GetPrincipalColumnOf(*fSubFields[0])->GetElement()->GetPackedSize();
1902 } else {
1903 for (unsigned i = 0; i < count; ++i) {
1904 nbytes += CallAppendOn(*fSubFields[0], typedValue->data() + (i * fItemSize));
1905 }
1906 }
1907
1908 fNWritten += count;
1909 fColumns[0]->Append(&fNWritten);
1910 return nbytes + fColumns[0]->GetElement()->GetPackedSize();
1911}
1912
1914{
1915 auto typedValue = static_cast<std::vector<char> *>(to);
1916
1917 ClusterSize_t nItems;
1918 RClusterIndex collectionStart;
1919 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
1920
1921 if (fSubFields[0]->IsSimple()) {
1922 typedValue->resize(nItems * fItemSize);
1923 if (nItems)
1924 GetPrincipalColumnOf(*fSubFields[0])->ReadV(collectionStart, nItems, typedValue->data());
1925 return;
1926 }
1927
1928 // See "semantics of reading non-trivial objects" in RNTuple's architecture.md
1929 const auto oldNItems = typedValue->size() / fItemSize;
1930 const bool canRealloc = oldNItems < nItems;
1931 bool allDeallocated = false;
1932 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyDestructible)) {
1933 allDeallocated = canRealloc;
1934 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
1935 CallDestroyValueOn(*fSubFields[0], typedValue->data() + (i * fItemSize), true /* dtorOnly */);
1936 }
1937 }
1938 typedValue->resize(nItems * fItemSize);
1939 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible)) {
1940 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
1941 CallGenerateValueOn(*fSubFields[0], typedValue->data() + (i * fItemSize));
1942 }
1943 }
1944
1945 for (std::size_t i = 0; i < nItems; ++i) {
1946 CallReadOn(*fSubFields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
1947 }
1948}
1949
1952{
1953 static RColumnRepresentations representations(
1955 {});
1956 return representations;
1957}
1958
1960{
1961 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
1962}
1963
1965{
1966 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
1967 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
1968}
1969
1970void ROOT::Experimental::RVectorField::DestroyValue(void *objPtr, bool dtorOnly) const
1971{
1972 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
1973 R__ASSERT((vecPtr->size() % fItemSize) == 0);
1974 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyDestructible)) {
1975 auto nItems = vecPtr->size() / fItemSize;
1976 for (unsigned i = 0; i < nItems; ++i) {
1977 CallDestroyValueOn(*fSubFields[0], vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
1978 }
1979 }
1980 std::destroy_at(vecPtr);
1981 if (!dtorOnly)
1982 free(vecPtr);
1983}
1984
1985std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
1987{
1988 auto vec = value.Get<std::vector<char>>();
1989 R__ASSERT((vec->size() % fItemSize) == 0);
1990 auto nItems = vec->size() / fItemSize;
1991 std::vector<RValue> result;
1992 for (unsigned i = 0; i < nItems; ++i) {
1993 result.emplace_back(fSubFields[0]->BindValue(vec->data() + (i * fItemSize)));
1994 }
1995 return result;
1996}
1997
1999{
2000 visitor.VisitVectorField(*this);
2001}
2002
2003
2004//------------------------------------------------------------------------------
2005
2006ROOT::Experimental::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<Detail::RFieldBase> itemField)
2007 : ROOT::Experimental::Detail::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetType() + ">",
2008 ENTupleStructure::kCollection, false /* isSimple */),
2009 fItemSize(itemField->GetValueSize()), fNWritten(0)
2010{
2011 Attach(std::move(itemField));
2012 fValueSize = EvalValueSize(); // requires fSubFields to be populated
2013}
2014
2015std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
2016ROOT::Experimental::RRVecField::CloneImpl(std::string_view newName) const
2017{
2018 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
2019 return std::make_unique<RRVecField>(newName, std::move(newItemField));
2020}
2021
2023{
2024 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(from);
2025
2026 std::size_t nbytes = 0;
2027 if (fSubFields[0]->IsSimple() && *sizePtr) {
2028 GetPrincipalColumnOf(*fSubFields[0])->AppendV(*beginPtr, *sizePtr);
2029 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubFields[0])->GetElement()->GetPackedSize();
2030 } else {
2031 auto begin = reinterpret_cast<const char *>(*beginPtr); // for pointer arithmetics
2032 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2033 nbytes += CallAppendOn(*fSubFields[0], begin + i * fItemSize);
2034 }
2035 }
2036
2037 fNWritten += *sizePtr;
2038 fColumns[0]->Append(&fNWritten);
2039 return nbytes + fColumns[0]->GetElement()->GetPackedSize();
2040}
2041
2043{
2044 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
2045 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
2046
2047 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(to);
2048
2049 // Read collection info for this entry
2050 ClusterSize_t nItems;
2051 RClusterIndex collectionStart;
2052 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2053 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2054 const std::size_t oldSize = *sizePtr;
2055
2056 // See "semantics of reading non-trivial objects" in RNTuple's architecture.md for details
2057 // on the element construction/destrution.
2058 const bool owns = (*capacityPtr != -1);
2059 const bool needsConstruct = !(fSubFields[0]->GetTraits() & kTraitTriviallyConstructible);
2060 const bool needsDestruct = owns && !(fSubFields[0]->GetTraits() & kTraitTriviallyDestructible);
2061
2062 // Destroy excess elements, if any
2063 if (needsDestruct) {
2064 for (std::size_t i = nItems; i < oldSize; ++i) {
2065 CallDestroyValueOn(*fSubFields[0], begin + (i * fItemSize), true /* dtorOnly */);
2066 }
2067 }
2068
2069 // Resize RVec (capacity and size)
2070 if (std::int32_t(nItems) > *capacityPtr) { // must reallocate
2071 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
2072 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
2073 if (needsDestruct) {
2074 for (std::size_t i = 0u; i < oldSize; ++i) {
2075 CallDestroyValueOn(*fSubFields[0], begin + (i * fItemSize), true /* dtorOnly */);
2076 }
2077 }
2078
2079 // TODO Increment capacity by a factor rather than just enough to fit the elements.
2080 if (owns) {
2081 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
2082 free(*beginPtr);
2083 }
2084 // We trust that malloc returns a buffer with large enough alignment.
2085 // This might not be the case if T in RVec<T> is over-aligned.
2086 *beginPtr = malloc(nItems * fItemSize);
2087 R__ASSERT(*beginPtr != nullptr);
2088 begin = reinterpret_cast<char *>(*beginPtr);
2089 *capacityPtr = nItems;
2090
2091 // Placement new for elements that were already there before the resize
2092 if (needsConstruct) {
2093 for (std::size_t i = 0u; i < oldSize; ++i)
2094 CallGenerateValueOn(*fSubFields[0], begin + (i * fItemSize));
2095 }
2096 }
2097 *sizePtr = nItems;
2098
2099 // Placement new for new elements, if any
2100 if (needsConstruct) {
2101 for (std::size_t i = oldSize; i < nItems; ++i)
2102 CallGenerateValueOn(*fSubFields[0], begin + (i * fItemSize));
2103 }
2104
2105 if (fSubFields[0]->IsSimple() && nItems) {
2106 GetPrincipalColumnOf(*fSubFields[0])->ReadV(collectionStart, nItems, begin);
2107 return;
2108 }
2109
2110 // Read the new values into the collection elements
2111 for (std::size_t i = 0; i < nItems; ++i) {
2112 CallReadOn(*fSubFields[0], collectionStart + i, begin + (i * fItemSize));
2113 }
2114}
2115
2117{
2118 if (!fSubFields[0]->IsSimple())
2119 return RFieldBase::ReadBulkImpl(bulkSpec);
2120
2121 if (bulkSpec.fAuxData->empty()) {
2122 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
2123 /// The following bytes store the item values, consecutively.
2124 bulkSpec.fAuxData->resize(sizeof(std::size_t));
2125 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fSubFields[0]->GetValueSize();
2126 }
2127 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
2128 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
2129 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(bulkSpec.fValues);
2130
2131 // Get size of the first RVec of the bulk
2132 RClusterIndex firstItemIndex;
2133 RClusterIndex collectionStart;
2134 ClusterSize_t collectionSize;
2135 this->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
2136 *beginPtr = itemValueArray;
2137 *sizePtr = collectionSize;
2138
2139 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
2140 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
2141 // given range. If not, we'll fix up the pointers afterwards.
2142 auto lastOffset = firstItemIndex.GetIndex() + collectionSize;
2143 ClusterSize_t::ValueType nRemainingValues = bulkSpec.fCount - 1;
2144 std::size_t nValues = 1;
2145 std::size_t nItems = collectionSize;
2146 while (nRemainingValues > 0) {
2147 NTupleSize_t nElementsUntilPageEnd;
2148 const auto offsets = fPrincipalColumn->MapV<ClusterSize_t>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
2149 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
2150 for (std::size_t i = 0; i < nBatch; ++i) {
2151 const auto size = offsets[i] - lastOffset;
2152 std::tie(beginPtr, sizePtr, _) = GetRVecDataMembers(
2153 reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
2154 *beginPtr = itemValueArray + nItems * itemValueSize;
2155 *sizePtr = size;
2156
2157 nItems += size;
2158 lastOffset = offsets[i];
2159 }
2160 nRemainingValues -= nBatch;
2161 nValues += nBatch;
2162 }
2163
2164 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
2165 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
2166 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
2167 if (delta != 0) {
2168 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
2169 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
2170 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
2171 beginPtrAsUChar += fValueSize;
2172 }
2173 }
2174
2175 GetPrincipalColumnOf(*fSubFields[0])->ReadV(firstItemIndex, nItems, itemValueArray - delta);
2176 return RBulkSpec::kAllSet;
2177}
2178
2181{
2182 static RColumnRepresentations representations(
2184 {});
2185 return representations;
2186}
2187
2189{
2190 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
2191}
2192
2194{
2195 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
2196 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
2197}
2198
2200{
2201 // initialize data members fBegin, fSize, fCapacity
2202 // currently the inline buffer is left uninitialized
2203 void **beginPtr = new (where)(void *)(nullptr);
2204 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
2205 new (sizePtr + 1) std::int32_t(-1);
2206}
2207
2208void ROOT::Experimental::RRVecField::DestroyValue(void *objPtr, bool dtorOnly) const
2209{
2210 auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers(objPtr);
2211
2212 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2213 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyDestructible)) {
2214 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2215 CallDestroyValueOn(*fSubFields[0], begin + i * fItemSize, true /* dtorOnly */);
2216 }
2217 }
2218
2219 // figure out if we are in the small state, i.e. begin == &inlineBuffer
2220 // there might be padding between fCapacity and the inline buffer, so we compute it here
2221 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
2222 const auto alignOfT = fSubFields[0]->GetAlignment();
2223 auto paddingMiddle = dataMemberSz % alignOfT;
2224 if (paddingMiddle != 0)
2225 paddingMiddle = alignOfT - paddingMiddle;
2226 const bool isSmall = (reinterpret_cast<void *>(begin) == (beginPtr + dataMemberSz + paddingMiddle));
2227
2228 const bool owns = (*capacityPtr != -1);
2229 if (!isSmall && owns)
2230 free(begin);
2231
2232 if (!dtorOnly)
2233 free(beginPtr);
2234}
2235
2236std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
2238{
2239 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetRawPtr());
2240
2241 std::vector<RValue> result;
2242 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
2243 result.reserve(*sizePtr);
2244 for (std::int32_t i = 0; i < *sizePtr; ++i) {
2245 result.emplace_back(fSubFields[0]->BindValue(begin + i * fItemSize));
2246 }
2247 return result;
2248}
2249
2251{
2252 // the size of an RVec<T> is the size of its 4 data-members + optional padding:
2253 //
2254 // data members:
2255 // - void *fBegin
2256 // - int32_t fSize
2257 // - int32_t fCapacity
2258 // - the char[] inline storage, which is aligned like T
2259 //
2260 // padding might be present:
2261 // - between fCapacity and the char[] buffer aligned like T
2262 // - after the char[] buffer
2263
2264 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
2265 const auto alignOfT = fSubFields[0]->GetAlignment();
2266 const auto sizeOfT = fSubFields[0]->GetValueSize();
2267
2268 // mimic the logic of RVecInlineStorageSize, but at runtime
2269 const auto inlineStorageSz = [&] {
2270#ifdef R__HAS_HARDWARE_INTERFERENCE_SIZE
2271 // hardware_destructive_interference_size is a C++17 feature but many compilers do not implement it yet
2272 constexpr unsigned cacheLineSize = std::hardware_destructive_interference_size;
2273#else
2274 constexpr unsigned cacheLineSize = 64u;
2275#endif
2276 const unsigned elementsPerCacheLine = (cacheLineSize - dataMemberSz) / sizeOfT;
2277 constexpr unsigned maxInlineByteSize = 1024;
2278 const unsigned nElements =
2279 elementsPerCacheLine >= 8 ? elementsPerCacheLine : (sizeOfT * 8 > maxInlineByteSize ? 0 : 8);
2280 return nElements * sizeOfT;
2281 }();
2282
2283 // compute padding between first 3 datamembers and inline buffer
2284 // (there should be no padding between the first 3 data members)
2285 auto paddingMiddle = dataMemberSz % alignOfT;
2286 if (paddingMiddle != 0)
2287 paddingMiddle = alignOfT - paddingMiddle;
2288
2289 // padding at the end of the object
2290 const auto alignOfRVecT = GetAlignment();
2291 auto paddingEnd = (dataMemberSz + paddingMiddle + inlineStorageSz) % alignOfRVecT;
2292 if (paddingEnd != 0)
2293 paddingEnd = alignOfRVecT - paddingEnd;
2294
2295 return dataMemberSz + inlineStorageSz + paddingMiddle + paddingEnd;
2296}
2297
2299{
2300 return fValueSize;
2301}
2302
2304{
2305 // the alignment of an RVec<T> is the largest among the alignments of its data members
2306 // (including the inline buffer which has the same alignment as the RVec::value_type)
2307 return std::max({alignof(void *), alignof(std::int32_t), fSubFields[0]->GetAlignment()});
2308}
2309
2311{
2312 visitor.VisitRVecField(*this);
2313}
2314
2315//------------------------------------------------------------------------------
2316
2318 : ROOT::Experimental::Detail::RFieldBase(name, "std::vector<bool>", ENTupleStructure::kCollection,
2319 false /* isSimple */)
2320{
2321 Attach(std::make_unique<RField<bool>>("_0"));
2322}
2323
2324std::size_t ROOT::Experimental::RField<std::vector<bool>>::AppendImpl(const void *from)
2325{
2326 auto typedValue = static_cast<const std::vector<bool> *>(from);
2327 auto count = typedValue->size();
2328 for (unsigned i = 0; i < count; ++i) {
2329 bool bval = (*typedValue)[i];
2330 CallAppendOn(*fSubFields[0], &bval);
2331 }
2332 fNWritten += count;
2333 fColumns[0]->Append(&fNWritten);
2334 return count + fColumns[0]->GetElement()->GetPackedSize();
2335}
2336
2337void ROOT::Experimental::RField<std::vector<bool>>::ReadGlobalImpl(NTupleSize_t globalIndex, void *to)
2338{
2339 auto typedValue = static_cast<std::vector<bool> *>(to);
2340
2341 ClusterSize_t nItems;
2342 RClusterIndex collectionStart;
2343 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2344
2345 typedValue->resize(nItems);
2346 for (unsigned i = 0; i < nItems; ++i) {
2347 bool bval;
2348 CallReadOn(*fSubFields[0], collectionStart + i, &bval);
2349 (*typedValue)[i] = bval;
2350 }
2351}
2352
2354ROOT::Experimental::RField<std::vector<bool>>::GetColumnRepresentations() const
2355{
2356 static RColumnRepresentations representations(
2358 {});
2359 return representations;
2360}
2361
2362void ROOT::Experimental::RField<std::vector<bool>>::GenerateColumnsImpl()
2363{
2364 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
2365}
2366
2367void ROOT::Experimental::RField<std::vector<bool>>::GenerateColumnsImpl(const RNTupleDescriptor &desc)
2368{
2369 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
2370 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
2371}
2372
2373std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
2374ROOT::Experimental::RField<std::vector<bool>>::SplitValue(const RValue &value) const
2375{
2376 const static bool trueValue = true;
2377 const static bool falseValue = false;
2378
2379 auto typedValue = value.Get<std::vector<bool>>();
2380 auto count = typedValue->size();
2381 std::vector<RValue> result;
2382 for (unsigned i = 0; i < count; ++i) {
2383 if ((*typedValue)[i])
2384 result.emplace_back(fSubFields[0]->BindValue(const_cast<bool *>(&trueValue)));
2385 else
2386 result.emplace_back(fSubFields[0]->BindValue(const_cast<bool *>(&falseValue)));
2387 }
2388 return result;
2389}
2390
2391void ROOT::Experimental::RField<std::vector<bool>>::DestroyValue(void *objPtr, bool dtorOnly) const
2392{
2393 std::destroy_at(static_cast<std::vector<bool> *>(objPtr));
2394 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
2395}
2396
2397void ROOT::Experimental::RField<std::vector<bool>>::AcceptVisitor(Detail::RFieldVisitor &visitor) const
2398{
2399 visitor.VisitVectorBoolField(*this);
2400}
2401
2402
2403//------------------------------------------------------------------------------
2404
2405
2407 std::string_view fieldName, std::unique_ptr<Detail::RFieldBase> itemField, std::size_t arrayLength)
2408 : ROOT::Experimental::Detail::RFieldBase(
2409 fieldName, "std::array<" + itemField->GetType() + "," + std::to_string(arrayLength) + ">",
2410 ENTupleStructure::kLeaf, false /* isSimple */, arrayLength)
2411 , fItemSize(itemField->GetValueSize()), fArrayLength(arrayLength)
2412{
2413 fTraits |= itemField->GetTraits() & ~kTraitMappable;
2414 Attach(std::move(itemField));
2415}
2416
2417std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
2418ROOT::Experimental::RArrayField::CloneImpl(std::string_view newName) const
2419{
2420 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
2421 return std::make_unique<RArrayField>(newName, std::move(newItemField), fArrayLength);
2422}
2423
2425{
2426 std::size_t nbytes = 0;
2427 auto arrayPtr = static_cast<const unsigned char *>(from);
2428 for (unsigned i = 0; i < fArrayLength; ++i) {
2429 nbytes += CallAppendOn(*fSubFields[0], arrayPtr + (i * fItemSize));
2430 }
2431 return nbytes;
2432}
2433
2435{
2436 auto arrayPtr = static_cast<unsigned char *>(to);
2437 for (unsigned i = 0; i < fArrayLength; ++i) {
2438 CallReadOn(*fSubFields[0], globalIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
2439 }
2440}
2441
2443{
2444 auto arrayPtr = static_cast<unsigned char *>(to);
2445 for (unsigned i = 0; i < fArrayLength; ++i) {
2446 CallReadOn(*fSubFields[0], RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() * fArrayLength + i),
2447 arrayPtr + (i * fItemSize));
2448 }
2449}
2450
2452{
2453 if (fSubFields[0]->GetTraits() & kTraitTriviallyConstructible)
2454 return;
2455
2456 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
2457 for (unsigned i = 0; i < fArrayLength; ++i) {
2458 CallGenerateValueOn(*fSubFields[0], arrayPtr + (i * fItemSize));
2459 }
2460}
2461
2462void ROOT::Experimental::RArrayField::DestroyValue(void *objPtr, bool dtorOnly) const
2463{
2464 auto arrayPtr = static_cast<unsigned char *>(objPtr);
2465 if (!(fSubFields[0]->GetTraits() & kTraitTriviallyDestructible)) {
2466 for (unsigned i = 0; i < fArrayLength; ++i) {
2467 CallDestroyValueOn(*fSubFields[0], arrayPtr + (i * fItemSize), true /* dtorOnly */);
2468 }
2469 }
2470 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
2471}
2472
2473std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
2475{
2476 auto arrayPtr = value.Get<unsigned char>();
2477 std::vector<RValue> result;
2478 for (unsigned i = 0; i < fArrayLength; ++i) {
2479 result.emplace_back(fSubFields[0]->BindValue(arrayPtr + (i * fItemSize)));
2480 }
2481 return result;
2482}
2483
2485{
2486 visitor.VisitArrayField(*this);
2487}
2488
2489//------------------------------------------------------------------------------
2490
2491ROOT::Experimental::RBitsetField::RBitsetField(std::string_view fieldName, std::size_t N)
2492 : ROOT::Experimental::Detail::RFieldBase(fieldName, "std::bitset<" + std::to_string(N) + ">",
2493 ENTupleStructure::kLeaf, false /* isSimple */, N),
2494 fN(N)
2495{
2497}
2498
2501{
2502 static RColumnRepresentations representations({{EColumnType::kBit}}, {});
2503 return representations;
2504}
2505
2507{
2508 fColumns.emplace_back(Detail::RColumn::Create<bool>(RColumnModel(GetColumnRepresentative()[0]), 0));
2509}
2510
2512{
2513 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
2514 fColumns.emplace_back(Detail::RColumn::Create<bool>(RColumnModel(onDiskTypes[0]), 0));
2515}
2516
2518{
2519 const auto *asULongArray = static_cast<const Word_t *>(from);
2520 bool elementValue;
2521 std::size_t i = 0;
2522 for (std::size_t word = 0; word < (fN + kBitsPerWord - 1) / kBitsPerWord; ++word) {
2523 for (std::size_t mask = 0; (mask < kBitsPerWord) && (i < fN); ++mask, ++i) {
2524 elementValue = (asULongArray[word] & (static_cast<Word_t>(1) << mask)) != 0;
2525 fColumns[0]->Append(&elementValue);
2526 }
2527 }
2528 return fN;
2529}
2530
2532{
2533 auto *asULongArray = static_cast<Word_t *>(to);
2534 bool elementValue;
2535 for (std::size_t i = 0; i < fN; ++i) {
2536 fColumns[0]->Read(globalIndex * fN + i, &elementValue);
2537 Word_t mask = static_cast<Word_t>(1) << (i % kBitsPerWord);
2538 Word_t bit = static_cast<Word_t>(elementValue) << (i % kBitsPerWord);
2539 asULongArray[i / kBitsPerWord] = (asULongArray[i / kBitsPerWord] & ~mask) | bit;
2540 }
2541}
2542
2544{
2545 visitor.VisitBitsetField(*this);
2546}
2547
2548//------------------------------------------------------------------------------
2549
2550std::string ROOT::Experimental::RVariantField::GetTypeList(const std::vector<Detail::RFieldBase *> &itemFields)
2551{
2552 std::string result;
2553 for (size_t i = 0; i < itemFields.size(); ++i) {
2554 result += itemFields[i]->GetType() + ",";
2555 }
2556 R__ASSERT(!result.empty()); // there is always at least one variant
2557 result.pop_back(); // remove trailing comma
2558 return result;
2559}
2560
2562 std::string_view fieldName, const std::vector<Detail::RFieldBase *> &itemFields)
2563 : ROOT::Experimental::Detail::RFieldBase(fieldName,
2564 "std::variant<" + GetTypeList(itemFields) + ">", ENTupleStructure::kVariant, false /* isSimple */)
2565{
2566 // The variant needs to initialize its own tag member
2567 fTraits |= kTraitTriviallyDestructible & ~kTraitTriviallyConstructible;
2568
2569 auto nFields = itemFields.size();
2570 R__ASSERT(nFields > 0);
2571 fNWritten.resize(nFields, 0);
2572 for (unsigned int i = 0; i < nFields; ++i) {
2573 fMaxItemSize = std::max(fMaxItemSize, itemFields[i]->GetValueSize());
2574 fMaxAlignment = std::max(fMaxAlignment, itemFields[i]->GetAlignment());
2575 fTraits &= itemFields[i]->GetTraits();
2576 Attach(std::unique_ptr<Detail::RFieldBase>(itemFields[i]));
2577 }
2579}
2580
2581std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
2583{
2584 auto nFields = fSubFields.size();
2585 std::vector<Detail::RFieldBase *> itemFields;
2586 for (unsigned i = 0; i < nFields; ++i) {
2587 // TODO(jblomer): use unique_ptr in RVariantField constructor
2588 itemFields.emplace_back(fSubFields[i]->Clone(fSubFields[i]->GetName()).release());
2589 }
2590 return std::make_unique<RVariantField>(newName, itemFields);
2591}
2592
2593std::uint32_t ROOT::Experimental::RVariantField::GetTag(const void *variantPtr) const
2594{
2595 auto index = *(reinterpret_cast<const char *>(variantPtr) + fTagOffset);
2596 return (index < 0) ? 0 : index + 1;
2597}
2598
2599void ROOT::Experimental::RVariantField::SetTag(void *variantPtr, std::uint32_t tag) const
2600{
2601 auto index = reinterpret_cast<char *>(variantPtr) + fTagOffset;
2602 *index = static_cast<char>(tag - 1);
2603}
2604
2606{
2607 auto tag = GetTag(from);
2608 std::size_t nbytes = 0;
2609 auto index = 0;
2610 if (tag > 0) {
2611 nbytes += CallAppendOn(*fSubFields[tag - 1], from);
2612 index = fNWritten[tag - 1]++;
2613 }
2614 RColumnSwitch varSwitch(ClusterSize_t(index), tag);
2615 fColumns[0]->Append(&varSwitch);
2616 return nbytes + sizeof(RColumnSwitch);
2617}
2618
2620{
2621 RClusterIndex variantIndex;
2622 std::uint32_t tag;
2623 fPrincipalColumn->GetSwitchInfo(globalIndex, &variantIndex, &tag);
2624
2625 // If `tag` equals 0, the variant is in the invalid state, i.e, it does not hold any of the valid alternatives in
2626 // the type list. This happens, e.g., if the field was late added; in this case, keep the invalid tag, which makes
2627 // any `std::holds_alternative<T>` check fail later.
2628 if (R__likely(tag > 0)) {
2629 CallGenerateValueOn(*fSubFields[tag - 1], to);
2630 CallReadOn(*fSubFields[tag - 1], variantIndex, to);
2631 }
2632 SetTag(to, tag);
2633}
2634
2637{
2638 static RColumnRepresentations representations({{EColumnType::kSwitch}}, {{}});
2639 return representations;
2640}
2641
2643{
2644 fColumns.emplace_back(Detail::RColumn::Create<RColumnSwitch>(RColumnModel(GetColumnRepresentative()[0]), 0));
2645}
2646
2648{
2649 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
2650 fColumns.emplace_back(Detail::RColumn::Create<RColumnSwitch>(RColumnModel(onDiskTypes[0]), 0));
2651}
2652
2654{
2655 memset(where, 0, GetValueSize());
2656 CallGenerateValueOn(*fSubFields[0], where);
2657 SetTag(where, 1);
2658}
2659
2660void ROOT::Experimental::RVariantField::DestroyValue(void *objPtr, bool dtorOnly) const
2661{
2662 auto tag = GetTag(objPtr);
2663 if (tag > 0) {
2664 CallDestroyValueOn(*fSubFields[tag - 1], objPtr, true /* dtorOnly */);
2665 }
2666 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
2667}
2668
2670{
2671 return fMaxItemSize + fMaxAlignment; // TODO: fix for more than 255 items
2672}
2673
2675{
2676 std::fill(fNWritten.begin(), fNWritten.end(), 0);
2677}
2678
2679//------------------------------------------------------------------------------
2680
2681ROOT::Experimental::RSetField::RSetField(std::string_view fieldName, std::string_view typeName,
2682 std::unique_ptr<Detail::RFieldBase> itemField)
2683 : ROOT::Experimental::RProxiedCollectionField(fieldName, typeName, std::move(itemField))
2684{
2685}
2686
2687std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
2688ROOT::Experimental::RSetField::CloneImpl(std::string_view newName) const
2689{
2690 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
2691 return std::make_unique<RSetField>(newName, GetType(), std::move(newItemField));
2692}
2693
2694//------------------------------------------------------------------------------
2695
2696ROOT::Experimental::RMapField::RMapField(std::string_view fieldName, std::string_view typeName,
2697 std::unique_ptr<Detail::RFieldBase> itemField)
2698 : RProxiedCollectionField(fieldName, typeName, TClass::GetClass(std::string(typeName).c_str()))
2699{
2700 if (!dynamic_cast<RPairField *>(itemField.get()))
2701 throw RException(R__FAIL("RMapField inner field type must be of RPairField"));
2702
2703 fItemClass = fProxy->GetValueClass();
2705
2706 Attach(std::move(itemField));
2707}
2708
2710{
2711 std::size_t nbytes = 0;
2712 unsigned count = 0;
2713 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), const_cast<void *>(from));
2714 for (auto ptr : RCollectionIterableOnce{const_cast<void *>(from), fIFuncsWrite, fProxy.get(), 0U}) {
2715 nbytes += CallAppendOn(*fSubFields[0], ptr);
2716 count++;
2717 }
2718 fNWritten += count;
2719 fColumns[0]->Append(&fNWritten);
2720 return nbytes + fColumns[0]->GetElement()->GetPackedSize();
2721}
2722
2724{
2725 ClusterSize_t nItems;
2726 RClusterIndex collectionStart;
2727 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
2728
2729 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), to);
2730 void *obj =
2731 fProxy->Allocate(static_cast<std::uint32_t>(nItems), (fProperties & TVirtualCollectionProxy::kNeedDelete));
2732
2733 unsigned i = 0;
2734 for (auto ptr : RCollectionIterableOnce{obj, fIFuncsRead, fProxy.get(), fItemSize}) {
2735 CallReadOn(*fSubFields[0], collectionStart + i, ptr);
2736 i++;
2737 }
2738
2739 if (obj != to)
2740 fProxy->Commit(obj);
2741}
2742
2743std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
2745{
2746 std::vector<RValue> result;
2747 TVirtualCollectionProxy::TPushPop RAII(fProxy.get(), value.GetRawPtr());
2748 for (auto ptr : RCollectionIterableOnce{value.GetRawPtr(), fIFuncsWrite, fProxy.get(), 0U}) {
2749 result.emplace_back(fSubFields[0]->BindValue(ptr));
2750 }
2751 return result;
2752}
2753
2754std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
2755ROOT::Experimental::RMapField::CloneImpl(std::string_view newName) const
2756{
2757 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
2758 return std::unique_ptr<RMapField>(new RMapField(newName, GetType(), std::move(newItemField)));
2759}
2760
2761//------------------------------------------------------------------------------
2762
2763ROOT::Experimental::RNullableField::RNullableField(std::string_view fieldName, std::string_view typeName,
2764 std::unique_ptr<Detail::RFieldBase> itemField)
2765 : ROOT::Experimental::Detail::RFieldBase(fieldName, typeName, ENTupleStructure::kCollection, false /* isSimple */)
2766{
2767 Attach(std::move(itemField));
2768}
2769
2772{
2773 static RColumnRepresentations representations(
2775 {EColumnType::kBit}}, {});
2776 return representations;
2777}
2778
2780{
2781 if (HasDefaultColumnRepresentative()) {
2782 if (fSubFields[0]->GetValueSize() < 4) {
2783 SetColumnRepresentative({EColumnType::kBit});
2784 }
2785 }
2786 if (IsDense()) {
2787 fDefaultItemValue = std::make_unique<RValue>(fSubFields[0]->GenerateValue());
2788 fColumns.emplace_back(Detail::RColumn::Create<bool>(RColumnModel(EColumnType::kBit), 0));
2789 } else {
2790 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
2791 }
2792}
2793
2795{
2796 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
2797 if (onDiskTypes[0] == EColumnType::kBit) {
2798 fColumns.emplace_back(Detail::RColumn::Create<bool>(RColumnModel(EColumnType::kBit), 0));
2799 } else {
2800 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
2801 }
2802}
2803
2805{
2806 if (IsDense()) {
2807 bool mask = false;
2808 fPrincipalColumn->Append(&mask);
2809 return 1 + CallAppendOn(*fSubFields[0], fDefaultItemValue->GetRawPtr());
2810 } else {
2811 fPrincipalColumn->Append(&fNWritten);
2812 return sizeof(ClusterSize_t);
2813 }
2814}
2815
2817{
2818 auto nbytesItem = CallAppendOn(*fSubFields[0], from);
2819 if (IsDense()) {
2820 bool mask = true;
2821 fPrincipalColumn->Append(&mask);
2822 return 1 + nbytesItem;
2823 } else {
2824 fNWritten++;
2825 fPrincipalColumn->Append(&fNWritten);
2826 return sizeof(ClusterSize_t) + nbytesItem;
2827 }
2828}
2829
2831{
2832 RClusterIndex nullIndex;
2833 if (IsDense()) {
2834 const bool isValidItem = *fPrincipalColumn->Map<bool>(globalIndex);
2835 return isValidItem ? fPrincipalColumn->GetClusterIndex(globalIndex) : nullIndex;
2836 } else {
2837 RClusterIndex collectionStart;
2838 ClusterSize_t collectionSize;
2839 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &collectionSize);
2840 return (collectionSize == 0) ? nullIndex : collectionStart;
2841 }
2842}
2843
2845{
2846 visitor.VisitNullableField(*this);
2847}
2848
2849//------------------------------------------------------------------------------
2850
2851ROOT::Experimental::RUniquePtrField::RUniquePtrField(std::string_view fieldName, std::string_view typeName,
2852 std::unique_ptr<Detail::RFieldBase> itemField)
2853 : RNullableField(fieldName, typeName, std::move(itemField))
2854{
2855}
2856
2857std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
2859{
2860 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
2861 return std::make_unique<RUniquePtrField>(newName, GetType(), std::move(newItemField));
2862}
2863
2865{
2866 auto typedValue = static_cast<const std::unique_ptr<char> *>(from);
2867 if (*typedValue) {
2868 return AppendValue(typedValue->get());
2869 } else {
2870 return AppendNull();
2871 }
2872}
2873
2875{
2876 auto ptr = static_cast<std::unique_ptr<char> *>(to);
2877 bool isValidValue = static_cast<bool>(*ptr);
2878
2879 auto itemIndex = GetItemIndex(globalIndex);
2880 bool isValidItem = itemIndex.GetIndex() != kInvalidClusterIndex;
2881
2882 void *valuePtr = nullptr;
2883 if (isValidValue)
2884 valuePtr = ptr->get();
2885
2886 if (isValidValue && !isValidItem) {
2887 ptr->release();
2888 CallDestroyValueOn(*fSubFields[0], valuePtr, false /* dtorOnly */);
2889 return;
2890 }
2891
2892 if (!isValidItem) // On-disk value missing; nothing else to do
2893 return;
2894
2895 if (!isValidValue) {
2896 valuePtr = malloc(fSubFields[0]->GetValueSize());
2897 CallGenerateValueOn(*fSubFields[0], valuePtr);
2898 ptr->reset(reinterpret_cast<char *>(valuePtr));
2899 }
2900
2901 CallReadOn(*fSubFields[0], itemIndex, valuePtr);
2902}
2903
2904void ROOT::Experimental::RUniquePtrField::DestroyValue(void *objPtr, bool dtorOnly) const
2905{
2906 auto typedPtr = static_cast<std::unique_ptr<char> *>(objPtr);
2907 if (*typedPtr) {
2908 CallDestroyValueOn(*fSubFields[0], typedPtr->get(), false /* dtorOnly */);
2909 typedPtr->release();
2910 }
2911 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
2912}
2913
2914std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
2916{
2917 std::vector<RValue> result;
2918 auto ptr = value.Get<std::unique_ptr<char>>();
2919 if (*ptr) {
2920 result.emplace_back(fSubFields[0]->BindValue(ptr->get()));
2921 }
2922 return result;
2923}
2924
2925//------------------------------------------------------------------------------
2926
2927std::string ROOT::Experimental::RPairField::RPairField::GetTypeList(
2928 const std::array<std::unique_ptr<Detail::RFieldBase>, 2> &itemFields)
2929{
2930 return itemFields[0]->GetType() + "," + itemFields[1]->GetType();
2931}
2932
2934 std::array<std::unique_ptr<Detail::RFieldBase>, 2> &&itemFields,
2935 const std::array<std::size_t, 2> &offsets)
2936 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), offsets,
2937 "std::pair<" + GetTypeList(itemFields) + ">")
2938{
2939}
2940
2942 std::array<std::unique_ptr<Detail::RFieldBase>, 2> &itemFields)
2943 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), {},
2944 "std::pair<" + GetTypeList(itemFields) + ">")
2945{
2946 // ISO C++ does not guarantee any specific layout for `std::pair`; query TClass for the member offsets
2947 fClass = TClass::GetClass(GetType().c_str());
2948 if (!fClass)
2949 throw RException(R__FAIL("cannot get type information for " + GetType()));
2950 fSize = fClass->Size();
2951 fOffsets[0] = fClass->GetDataMember("first")->GetOffset();
2952 fOffsets[1] = fClass->GetDataMember("second")->GetOffset();
2953}
2954
2955std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
2956ROOT::Experimental::RPairField::CloneImpl(std::string_view newName) const
2957{
2958 std::array<std::unique_ptr<Detail::RFieldBase>, 2> items{fSubFields[0]->Clone(fSubFields[0]->GetName()),
2959 fSubFields[1]->Clone(fSubFields[1]->GetName())};
2960
2961 std::unique_ptr<RPairField> result(new RPairField(newName, std::move(items), {fOffsets[0], fOffsets[1]}));
2962 result->fClass = fClass;
2963 return result;
2964}
2965
2967{
2968 fClass->New(where);
2969}
2970
2971void ROOT::Experimental::RPairField::DestroyValue(void *objPtr, bool dtorOnly) const
2972{
2973 fClass->Destructor(objPtr, true /* dtorOnly */);
2974 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
2975}
2976
2977//------------------------------------------------------------------------------
2978
2979std::string ROOT::Experimental::RTupleField::RTupleField::GetTypeList(
2980 const std::vector<std::unique_ptr<Detail::RFieldBase>> &itemFields)
2981{
2982 std::string result;
2983 if (itemFields.empty())
2984 throw RException(R__FAIL("the type list for std::tuple must have at least one element"));
2985 for (size_t i = 0; i < itemFields.size(); ++i) {
2986 result += itemFields[i]->GetType() + ",";
2987 }
2988 result.pop_back(); // remove trailing comma
2989 return result;
2990}
2991
2993 std::vector<std::unique_ptr<Detail::RFieldBase>> &&itemFields,
2994 const std::vector<std::size_t> &offsets)
2995 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), offsets,
2996 "std::tuple<" + GetTypeList(itemFields) + ">")
2997{
2998}
2999
3001 std::vector<std::unique_ptr<Detail::RFieldBase>> &itemFields)
3002 : ROOT::Experimental::RRecordField(fieldName, std::move(itemFields), {},
3003 "std::tuple<" + GetTypeList(itemFields) + ">")
3004{
3005 fClass = TClass::GetClass(GetType().c_str());
3006 if (!fClass)
3007 throw RException(R__FAIL("cannot get type information for " + GetType()));
3008 fSize = fClass->Size();
3009
3010 // ISO C++ does not guarantee neither specific layout nor member names for `std::tuple`. However, most
3011 // implementations including libstdc++ (gcc), libc++ (llvm), and MSVC name members as `_0`, `_1`, ..., `_N-1`,
3012 // following the order of the type list.
3013 // Use TClass to get their offsets; in case a particular `std::tuple` implementation does not define such
3014 // members, the assertion below will fail.
3015 for (unsigned i = 0; i < fSubFields.size(); ++i) {
3016 std::string memberName("_" + std::to_string(i));
3017 auto member = fClass->GetRealData(memberName.c_str());
3018 if (!member)
3019 throw RException(R__FAIL(memberName + ": no such member"));
3020 fOffsets.push_back(member->GetThisOffset());
3021 }
3022}
3023
3024std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
3025ROOT::Experimental::RTupleField::CloneImpl(std::string_view newName) const
3026{
3027 std::vector<std::unique_ptr<Detail::RFieldBase>> items;
3028 items.reserve(fSubFields.size());
3029 for (const auto &item : fSubFields)
3030 items.push_back(item->Clone(item->GetName()));
3031
3032 std::unique_ptr<RTupleField> result(new RTupleField(newName, std::move(items), fOffsets));
3033 result->fClass = fClass;
3034 return result;
3035}
3036
3038{
3039 fClass->New(where);
3040}
3041
3042void ROOT::Experimental::RTupleField::DestroyValue(void *objPtr, bool dtorOnly) const
3043{
3044 fClass->Destructor(objPtr, true /* dtorOnly */);
3045 Detail::RFieldBase::DestroyValue(objPtr, dtorOnly);
3046}
3047
3048//------------------------------------------------------------------------------
3049
3051 std::string_view name,
3052 std::shared_ptr<RCollectionNTupleWriter> collectionNTuple,
3053 std::unique_ptr<RNTupleModel> collectionModel)
3054 : RFieldBase(name, "", ENTupleStructure::kCollection, true /* isSimple */)
3055 , fCollectionNTuple(collectionNTuple)
3056{
3057 for (unsigned i = 0; i < collectionModel->GetFieldZero()->fSubFields.size(); ++i) {
3058 auto& subField = collectionModel->GetFieldZero()->fSubFields[i];
3059 Attach(std::move(subField));
3060 }
3061 SetDescription(collectionModel->GetDescription());
3062}
3063
3066{
3067 static RColumnRepresentations representations(
3069 {});
3070 return representations;
3071}
3072
3074{
3075 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(GetColumnRepresentative()[0]), 0));
3076}
3077
3079{
3080 auto onDiskTypes = EnsureCompatibleColumnTypes(desc);
3081 fColumns.emplace_back(Detail::RColumn::Create<ClusterSize_t>(RColumnModel(onDiskTypes[0]), 0));
3082}
3083
3084
3085std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
3087{
3088 auto result = std::make_unique<RCollectionField>(newName, fCollectionNTuple, RNTupleModel::Create());
3089 for (auto& f : fSubFields) {
3090 auto clone = f->Clone(f->GetName());
3091 result->Attach(std::move(clone));
3092 }
3093 return result;
3094}
3095
3097{
3098 *fCollectionNTuple->GetOffsetPtr() = 0;
3099}
3100
3101//------------------------------------------------------------------------------
3102
3103ROOT::Experimental::RAtomicField::RAtomicField(std::string_view fieldName, std::string_view typeName,
3104 std::unique_ptr<Detail::RFieldBase> itemField)
3105 : RFieldBase(fieldName, typeName, ENTupleStructure::kLeaf, false /* isSimple */)
3106{
3107 if (itemField->GetTraits() & kTraitTriviallyConstructible)
3109 if (itemField->GetTraits() & kTraitTriviallyDestructible)
3111 Attach(std::move(itemField));
3112}
3113
3114std::unique_ptr<ROOT::Experimental::Detail::RFieldBase>
3115ROOT::Experimental::RAtomicField::CloneImpl(std::string_view newName) const
3116{
3117 auto newItemField = fSubFields[0]->Clone(fSubFields[0]->GetName());
3118 return std::make_unique<RAtomicField>(newName, GetType(), std::move(newItemField));
3119}
3120
3121std::vector<ROOT::Experimental::Detail::RFieldBase::RValue>
3123{
3124 std::vector<RValue> result;
3125 result.emplace_back(fSubFields[0]->BindValue(value.GetRawPtr()));
3126 return result;
3127}
3128
3130{
3131 visitor.VisitAtomicField(*this);
3132}
size_t fSize
size_t fValueSize
Cppyy::TCppType_t fClass
#define R__likely(expr)
Definition RConfig.hxx:612
#define R__FORWARD_RESULT(res)
Short-hand to return an RResult<T> value from a subroutine to the calling stack frame.
Definition RError.hxx:305
#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:303
#define R__LOG_WARNING(...)
Definition RLogger.hxx:363
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
@ 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)
Definition TError.h:118
#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 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
#define _(A, B)
Definition cfortran.h:108
#define free
Definition civetweb.c:1539
#define malloc
Definition civetweb.c:1536
static std::string GetTypeName(EColumnType type)
Similar to RValue but manages an array of consecutive values.
Definition RField.hxx:203
void * fValues
Pointer to the start of the array.
Definition RField.hxx:208
std::unique_ptr< bool[]> fMaskAvail
Masks invalid values in the array.
Definition RField.hxx:212
void Reset(const RClusterIndex &firstIndex, std::size_t size)
Sets a new range for the bulk.
Definition RField.cxx:299
RBulk & operator=(const RBulk &)=delete
Some fields have multiple possible column representations, e.g.
Definition RField.hxx:120
TypesList_t fDeserializationTypes
The union of the serialization types and the deserialization extra types.
Definition RField.hxx:135
std::vector< ColumnRepresentation_t > TypesList_t
Definition RField.hxx:122
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
Definition RField.hxx:142
A field translates read and write calls from/to underlying columns to/from tree values.
Definition RField.hxx:85
void SetOnDiskId(DescriptorId_t id)
Definition RField.cxx:673
virtual void GenerateColumnsImpl()=0
Creates the backing columns corresponsing to the field type for writing.
std::vector< std::unique_ptr< RFieldBase > > fSubFields
Collections and classes own sub fields.
Definition RField.hxx:340
RConstSchemaIterator cend() const
Definition RField.hxx:646
RFieldBase * fParent
Sub fields point to their mother field.
Definition RField.hxx:342
static RResult< std::unique_ptr< RFieldBase > > Create(const std::string &fieldName, const std::string &canonicalType, const std::string &typeAlias)
Factory method to resurrect a field from the stored on-disk type information.
Definition RField.cxx:362
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:331
void CommitCluster()
Flushes data from active columns to disk and calls CommitClusterImpl.
Definition RField.cxx:658
int fTraits
Properties of the type that allow for optimizations of collections of that type.
Definition RField.hxx:350
static constexpr int kTraitTrivialType
Shorthand for types that are both trivially constructible and destructible.
Definition RField.hxx:101
friend class ROOT::Experimental::RCollectionField
Definition RField.hxx:86
RValue GenerateValue()
Generates an object of the field type and allocates new initialized memory according to the type.
Definition RField.cxx:604
virtual void ReadGlobalImpl(NTupleSize_t globalIndex, void *to)
Definition RField.cxx:579
void SetDescription(std::string_view description)
Definition RField.cxx:666
static RResult< void > EnsureValidFieldName(std::string_view fieldName)
Check whether a given string is a valid field name.
Definition RField.cxx:544
bool fIsSimple
A field qualifies as simple if it is both mappable and has no post-read callback.
Definition RField.hxx:294
std::string fType
The C++ type captured by this field.
Definition RField.hxx:288
std::function< void(void *)> ReadCallback_t
Definition RField.hxx:88
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:619
std::string fName
The field name relative to its parent field.
Definition RField.hxx:286
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:724
void ConnectPageSource(RPageSource &pageSource)
Definition RField.cxx:793
virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec)
General implementation of bulk read.
Definition RField.cxx:584
virtual std::size_t AppendImpl(const void *from)
Operations on values of complex types, e.g.
Definition RField.cxx:573
virtual void DestroyValue(void *objPtr, bool dtorOnly=false) const
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:612
virtual const RColumnRepresentations & GetColumnRepresentations() const
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:555
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:562
std::size_t fNRepetitions
For fixed sized arrays, the array length.
Definition RField.hxx:292
virtual void AcceptVisitor(RFieldVisitor &visitor) const
Definition RField.cxx:831
ENTupleStructure fStructure
The role of this field in the data model structure.
Definition RField.hxx:290
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:737
std::string GetQualifiedFieldName() const
Returns the field name and parent field names separated by dots ("grandparent.parent....
Definition RField.cxx:342
const ColumnRepresentation_t & GetColumnRepresentative() const
Returns the fColumnRepresentative pointee or, if unset, the field's default representative.
Definition RField.cxx:681
static constexpr int kTraitMappable
A field of a fundamental type that can be directly mapped via RField<T>::Map(), i....
Definition RField.hxx:99
void Attach(std::unique_ptr< Detail::RFieldBase > child)
Add a new subfield to the list of nested fields.
Definition RField.cxx:624
std::vector< RFieldBase * > GetSubFields() const
Definition RField.cxx:648
void SetColumnRepresentative(const ColumnRepresentation_t &representative)
Fixes a column representative.
Definition RField.cxx:688
void ConnectPageSink(RPageSink &pageSink, NTupleSize_t firstEntry=0)
Fields and their columns live in the void until connected to a physical page storage.
Definition RField.cxx:772
RConstSchemaIterator cbegin() const
Definition RField.hxx:642
static constexpr int kTraitTriviallyConstructible
No constructor needs to be called, i.e.
Definition RField.hxx:94
static constexpr int kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. DestroyValue() is a no-op.
Definition RField.hxx:96
const ColumnRepresentation_t & EnsureCompatibleColumnTypes(const RNTupleDescriptor &desc) const
Returns the on-disk column types found in the provided descriptor for fOnDiskId.
Definition RField.cxx:700
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:636
std::vector< EColumnType > ColumnRepresentation_t
Definition RField.hxx:103
RColumn * fPrincipalColumn
Points into fColumns.
Definition RField.hxx:346
Abstract base class for classes implementing the visitor design pattern.
virtual void VisitProxiedCollectionField(const RProxiedCollectionField &field)
virtual void VisitBoolField(const RField< bool > &field)
virtual void VisitBitsetField(const RBitsetField &field)
virtual void VisitNullableField(const RNullableField &field)
virtual void VisitFieldZero(const RFieldZero &field)
virtual void VisitRVecField(const RRVecField &field)
virtual void VisitCardinalityField(const RCardinalityField &field)
virtual void VisitEnumField(const REnumField &field)
virtual void VisitField(const Detail::RFieldBase &field)=0
virtual void VisitDoubleField(const RField< double > &field)
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)
Abstract interface to write data into an ntuple.
const RNTupleWriteOptions & GetWriteOptions() const
Returns the sink's write options.
Abstract interface to read data from an ntuple.
const RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
void DestroyValue(void *objPtr, bool dtorOnly=false) const final
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:2462
void ReadInClusterImpl(const RClusterIndex &clusterIndex, void *to) final
Definition RField.cxx:2442
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2424
RArrayField(std::string_view fieldName, std::unique_ptr< Detail::RFieldBase > itemField, std::size_t arrayLength)
Definition RField.cxx:2406
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2484
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2434
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:2474
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2418
RAtomicField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< Detail::RFieldBase > itemField)
Definition RField.cxx:3103
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3115
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:3129
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:3122
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2500
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.cxx:2506
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2517
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2531
RBitsetField(std::string_view fieldName, std::size_t N)
Definition RField.cxx:2491
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2543
const RField< RNTupleCardinality< std::uint32_t > > * As32Bit() const
Definition RField.cxx:904
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.hxx:1489
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:884
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:898
const RField< RNTupleCardinality< std::uint64_t > > * As64Bit() const
Definition RField.cxx:910
The field for a class with dictionary.
Definition RField.hxx:676
static constexpr const char * kPrefixInherited
Prefix used in the subfield names generated for base classes.
Definition RField.hxx:687
void OnConnectPageSource() final
Called by ConnectPageSource() only once connected; derived classes may override this as appropriate.
Definition RField.cxx:1446
size_t GetValueSize() const override
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:1494
void Attach(std::unique_ptr< Detail::RFieldBase > child, RSubFieldInfo info)
Definition RField.cxx:1388
void DestroyValue(void *objPtr, bool dtorOnly=false) const final
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:1477
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:1423
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1416
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:1432
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:1395
void AcceptVisitor(Detail::RFieldVisitor &visitor) const override
Definition RField.cxx:1504
std::uint32_t GetTypeVersion() const final
Indicates an evolution of the C++ type itself.
Definition RField.cxx:1499
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:1484
void ReadInClusterImpl(const RClusterIndex &clusterIndex, void *to) final
Definition RField.cxx:1439
RClassField(std::string_view fieldName, std::string_view className, TClass *classp)
Definition RField.cxx:1332
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
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.cxx:3073
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:3065
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3086
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:729
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1567
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1553
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:1560
REnumField(std::string_view fieldName, std::string_view enumName, TEnum *enump)
Definition RField.cxx:1516
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
The container field for an ntuple model, which itself has no physical representation.
Definition RField.hxx:657
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:848
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:839
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:1282
The generic field for a std::map<KeyType, ValueType> and std::unordered_map<KeyType,...
Definition RField.hxx:1160
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:2744
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2709
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2723
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2755
RMapField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< Detail::RFieldBase > itemField)
Definition RField.cxx:2696
The on-storage meta-data of an ntuple.
const RFieldDescriptor & GetFieldDescriptor(DescriptorId_t fieldId) const
RColumnDescriptorIterable GetColumnIterable() const
static std::unique_ptr< RNTupleModel > Create()
Common user-tunable settings for storing ntuples.
The field for values that may or may not be present in an entry.
Definition RField.hxx:1189
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.cxx:2779
const Detail::RFieldBase::RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2771
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2844
RClusterIndex GetItemIndex(NTupleSize_t globalIndex)
Given the index of the nullable field, returns the corresponding global index of the subfield or,...
Definition RField.cxx:2830
RNullableField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< Detail::RFieldBase > itemField)
Definition RField.cxx:2763
std::size_t AppendValue(const void *from)
Definition RField.cxx:2816
The generic field for std::pair<T1, T2> types.
Definition RField.hxx:1428
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2956
void DestroyValue(void *objPtr, bool dtorOnly=false) const override
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:2971
RPairField(std::string_view fieldName, std::array< std::unique_ptr< Detail::RFieldBase >, 2 > &&itemFields, const std::array< std::size_t, 2 > &offsets)
Definition RField.cxx:2933
Allows for iterating over the elements of a proxied collection.
Definition RField.hxx:770
static RIteratorFuncs GetIteratorFuncs(TVirtualCollectionProxy *proxy, bool readFromDisk)
Definition RField.cxx:1575
The field for a class representing a collection of elements via TVirtualCollectionProxy.
Definition RField.hxx:766
std::size_t AppendImpl(const void *from) override
Operations on values of complex types, e.g.
Definition RField.cxx:1668
RCollectionIterableOnce::RIteratorFuncs fIFuncsRead
Two sets of functions to operate on iterators, to be used depending on the access type.
Definition RField.hxx:842
void AcceptVisitor(Detail::RFieldVisitor &visitor) const override
Definition RField.cxx:1753
std::unique_ptr< TVirtualCollectionProxy > fProxy
Definition RField.hxx:837
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:1587
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1661
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.cxx:1712
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:1704
void DestroyValue(void *objPtr, bool dtorOnly=false) const override
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:1728
RCollectionIterableOnce::RIteratorFuncs fIFuncsWrite
Definition RField.hxx:843
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) override
Definition RField.cxx:1684
size_t GetValueSize() const override
The number of bytes taken by a value of the appropriate type.
Definition RField.hxx:876
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:1742
std::size_t EvalValueSize() const
Evaluate the constant returned by GetValueSize.
Definition RField.cxx:2250
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2180
RRVecField(std::string_view fieldName, std::unique_ptr< Detail::RFieldBase > itemField)
Definition RField.cxx:2006
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:2237
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:2310
void DestroyValue(void *objPtr, bool dtorOnly=false) const override
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:2208
size_t GetAlignment() const override
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.cxx:2303
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2016
size_t GetValueSize() const override
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:2298
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
General implementation of bulk read.
Definition RField.cxx:2116
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.cxx:2188
std::size_t AppendImpl(const void *from) override
Operations on values of complex types, e.g.
Definition RField.cxx:2022
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) override
Definition RField.cxx:2042
The field for an untyped record.
Definition RField.hxx:891
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1810
std::vector< std::size_t > fOffsets
Definition RField.hxx:895
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:1828
void ReadInClusterImpl(const RClusterIndex &clusterIndex, void *to) final
Definition RField.cxx:1835
RRecordField(std::string_view fieldName, std::vector< std::unique_ptr< Detail::RFieldBase > > &&itemFields, const std::vector< std::size_t > &offsets, std::string_view typeName="")
Definition RField.cxx:1760
std::size_t GetItemPadding(std::size_t baseOffset, std::size_t itemAlignment) const
Definition RField.cxx:1799
void DestroyValue(void *objPtr, bool dtorOnly=false) const override
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:1849
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:1819
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:1858
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1868
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:207
RSetField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< Detail::RFieldBase > itemField)
Definition RField.cxx:2681
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2688
The generic field for std::tuple<Ts...> types.
Definition RField.hxx:1452
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const override
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:3025
void DestroyValue(void *objPtr, bool dtorOnly=false) const override
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:3042
RTupleField(std::string_view fieldName, std::vector< std::unique_ptr< Detail::RFieldBase > > &&itemFields, const std::vector< std::size_t > &offsets)
Definition RField.cxx:2992
RUniquePtrField(std::string_view fieldName, std::string_view typeName, std::unique_ptr< Detail::RFieldBase > itemField)
Definition RField.cxx:2851
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2874
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:2915
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2858
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2864
void DestroyValue(void *objPtr, bool dtorOnly=false) const final
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:2904
void DestroyValue(void *objPtr, bool dtorOnly=false) const final
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:2660
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:2619
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
Definition RField.cxx:2669
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:2636
std::vector< ClusterSize_t::ValueType > fNWritten
Definition RField.hxx:1111
size_t fTagOffset
In the std::variant memory layout, at which byte number is the index stored.
Definition RField.hxx:1110
void SetTag(void *variantPtr, std::uint32_t tag) const
Definition RField.cxx:2599
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.cxx:2642
static std::string GetTypeList(const std::vector< Detail::RFieldBase * > &itemFields)
Definition RField.cxx:2550
std::uint32_t GetTag(const void *variantPtr) const
Extracts the index from an std::variant and transforms it into the 1-based index used for the switch ...
Definition RField.cxx:2593
RVariantField(std::string_view fieldName, const std::vector< Detail::RFieldBase * > &itemFields)
Definition RField.cxx:2561
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:2605
size_t GetAlignment() const final
As a rule of thumb, the alignment is equal to the size of the type.
Definition RField.hxx:1142
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:2582
std::unique_ptr< Detail::RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
Definition RField.cxx:1886
void AcceptVisitor(Detail::RFieldVisitor &visitor) const final
Definition RField.cxx:1998
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
Definition RField.cxx:1892
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
Definition RField.cxx:1951
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:1986
void GenerateColumnsImpl() final
Creates the backing columns corresponsing to the field type for writing.
Definition RField.cxx:1959
void ReadGlobalImpl(NTupleSize_t globalIndex, void *to) final
Definition RField.cxx:1913
void DestroyValue(void *objPtr, bool dtorOnly=false) const final
Releases the resources acquired during GenerateValue (memory and constructor) This implementation wor...
Definition RField.cxx:1970
RVectorField(std::string_view fieldName, std::unique_ptr< Detail::RFieldBase > itemField)
Definition RField.cxx:1876
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
TList * GetListOfDataMembers(Bool_t load=kTRUE)
Return list containing the TDataMembers of a class.
Definition TClass.cxx:3770
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:423
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
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
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
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...
const Int_t n
Definition legend1.C:16
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
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
TClass * GetClass(T *)
Definition TClass.h:659
@ 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 ...
Input parameter to ReadBulk() and ReadBulkImpl(). See RBulk class for more information.
Definition RField.hxx:322
bool * fMaskAvail
A bool array of size fCount, indicating the valid values in fValues.
Definition RField.hxx:331
std::vector< unsigned char > * fAuxData
Reference to memory owned by the RBulk class.
Definition RField.hxx:336
const bool * fMaskReq
A bool array of size fCount, indicating the required values in the requested range.
Definition RField.hxx:330
void * fValues
The destination area, which has to be a big enough array of valid objects of the correct type.
Definition RField.hxx:333
RClusterIndex fFirstIndex
Start of the bulk range.
Definition RField.hxx:327
std::size_t fCount
Size of the bulk range.
Definition RField.hxx:328
Wrap the integer in a struct in order to avoid template specialization clash with std::uint32_t.