Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldSequenceContainer.cxx
Go to the documentation of this file.
1/// \file RFieldSequenceContainer.cxx
2/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
3/// \date 2024-11-19
4
5#include <ROOT/BitUtils.hxx>
6#include <ROOT/RField.hxx>
7#include <ROOT/RFieldBase.hxx>
10
11#include <limits>
12#include <memory>
13#include <new> // hardware_destructive_interference_size, operator new
14
15namespace {
16
17std::vector<ROOT::RFieldBase::RValue> SplitVector(const std::shared_ptr<void> &valuePtr, ROOT::RFieldBase &itemField)
18{
19 auto *vec = static_cast<std::vector<char> *>(valuePtr.get());
20 const auto itemSize = itemField.GetValueSize();
21 R__ASSERT(itemSize > 0);
22 R__ASSERT((vec->size() % itemSize) == 0);
23 const auto nItems = vec->size() / itemSize;
24 std::vector<ROOT::RFieldBase::RValue> result;
25 result.reserve(nItems);
26 for (unsigned i = 0; i < nItems; ++i) {
27 result.emplace_back(itemField.BindValue(std::shared_ptr<void>(valuePtr, vec->data() + (i * itemSize))));
28 }
29 return result;
30}
31
32std::size_t GetSizeOfVector()
33{
34 return sizeof(std::vector<char>);
35}
36
37std::size_t GetAlignOfVector()
38{
39 return alignof(std::vector<char>);
40}
41
42void ConstructVector(void *where, std::size_t alignOfValue)
43{
44 static_assert(ROOT::RVectorField::kMaxItemAlignment == 4096);
45 // clang-format off
46 switch (alignOfValue) {
47 case 1: new (where) std::vector<ROOT::Internal::RAlignedStorage< 1>>(); break;
48 case 2: new (where) std::vector<ROOT::Internal::RAlignedStorage< 2>>(); break;
49 case 4: new (where) std::vector<ROOT::Internal::RAlignedStorage< 4>>(); break;
50 case 8: new (where) std::vector<ROOT::Internal::RAlignedStorage< 8>>(); break;
51 case 16: new (where) std::vector<ROOT::Internal::RAlignedStorage< 16>>(); break;
52 case 32: new (where) std::vector<ROOT::Internal::RAlignedStorage< 32>>(); break;
53 case 64: new (where) std::vector<ROOT::Internal::RAlignedStorage< 64>>(); break;
54 case 128: new (where) std::vector<ROOT::Internal::RAlignedStorage< 128>>(); break;
55 case 256: new (where) std::vector<ROOT::Internal::RAlignedStorage< 256>>(); break;
56 case 512: new (where) std::vector<ROOT::Internal::RAlignedStorage< 512>>(); break;
57 case 1024: new (where) std::vector<ROOT::Internal::RAlignedStorage<1024>>(); break;
58 case 2048: new (where) std::vector<ROOT::Internal::RAlignedStorage<2048>>(); break;
59 case 4096: new (where) std::vector<ROOT::Internal::RAlignedStorage<4096>>(); break;
60 default: throw ROOT::RException(R__FAIL(std::string("Unsupported alignment: ") + std::to_string(alignOfValue)));
61 }
62 // clang-format on
63}
64
65} // anonymous namespace
66
67ROOT::RArrayField::RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
68 std::size_t arrayLength)
69 : ROOT::RFieldBase(fieldName,
70 "std::array<" + itemField->GetTypeName() + "," +
71 Internal::GetNormalizedInteger(static_cast<unsigned long long>(arrayLength)) + ">",
72 ROOT::ENTupleStructure::kPlain, false /* isSimple */, arrayLength),
73 fItemSize(itemField->GetValueSize()),
74 fArrayLength(arrayLength)
75{
76 fTraits |= itemField->GetTraits() & ~kTraitMappable;
77 if (!itemField->GetTypeAlias().empty()) {
78 fTypeAlias = "std::array<" + itemField->GetTypeAlias() + "," +
79 Internal::GetNormalizedInteger(static_cast<unsigned long long>(arrayLength)) + ">";
80 }
81 Attach(std::move(itemField), "_0");
82}
83
84std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayField::CloneImpl(std::string_view newName) const
85{
86 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
87 return std::make_unique<RArrayField>(newName, std::move(newItemField), fArrayLength);
88}
89
90std::size_t ROOT::RArrayField::AppendImpl(const void *from)
91{
92 std::size_t nbytes = 0;
93 if (fSubfields[0]->IsSimple()) {
94 GetPrincipalColumnOf(*fSubfields[0])->AppendV(from, fArrayLength);
95 nbytes += fArrayLength * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
96 } else {
97 auto arrayPtr = static_cast<const unsigned char *>(from);
98 for (unsigned i = 0; i < fArrayLength; ++i) {
99 nbytes += CallAppendOn(*fSubfields[0], arrayPtr + (i * fItemSize));
100 }
101 }
102 return nbytes;
103}
104
106{
107 if (fSubfields[0]->IsSimple()) {
108 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, to);
109 } else {
110 auto arrayPtr = static_cast<unsigned char *>(to);
111 for (unsigned i = 0; i < fArrayLength; ++i) {
112 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
113 }
114 }
115}
116
118{
119 if (fSubfields[0]->IsSimple()) {
120 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, to);
121 } else {
122 auto arrayPtr = static_cast<unsigned char *>(to);
123 for (unsigned i = 0; i < fArrayLength; ++i) {
124 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
125 }
126 }
127}
128
129std::size_t ROOT::RArrayField::ReadBulkImpl(const RBulkSpec &bulkSpec)
130{
131 if (!fSubfields[0]->IsSimple())
132 return RFieldBase::ReadBulkImpl(bulkSpec);
133
134 GetPrincipalColumnOf(*fSubfields[0])
135 ->ReadV(bulkSpec.fFirstIndex * fArrayLength, bulkSpec.fCount * fArrayLength, bulkSpec.fValues);
136 return RBulkSpec::kAllSet;
137}
138
140{
141 static const std::vector<std::string> prefixes = {"std::array<"};
142
143 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
144 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
145}
146
148{
149 if (fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)
150 return;
151
152 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
153 for (unsigned i = 0; i < fArrayLength; ++i) {
154 CallConstructValueOn(*fSubfields[0], arrayPtr + (i * fItemSize));
155 }
156}
157
158void ROOT::RArrayField::RArrayDeleter::operator()(void *objPtr, bool dtorOnly)
159{
160 if (fItemDeleter) {
161 for (unsigned i = 0; i < fArrayLength; ++i) {
162 fItemDeleter->operator()(reinterpret_cast<unsigned char *>(objPtr) + i * fItemSize, true /* dtorOnly */);
163 }
164 }
165 RDeleter::operator()(objPtr, dtorOnly);
166}
167
168std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayField::GetDeleter() const
169{
170 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyDestructible))
171 return std::make_unique<RArrayDeleter>(fItemSize, fArrayLength, GetAlignment(), GetDeleterOf(*fSubfields[0]));
172 return std::make_unique<RDeleter>(GetAlignment());
173}
174
175std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayField::SplitValue(const RValue &value) const
176{
177 auto valuePtr = value.GetPtr<void>();
178 auto arrayPtr = static_cast<unsigned char *>(valuePtr.get());
179 std::vector<RValue> result;
180 result.reserve(fArrayLength);
181 for (unsigned i = 0; i < fArrayLength; ++i) {
182 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, arrayPtr + (i * fItemSize))));
183 }
184 return result;
185}
186
188{
189 visitor.VisitArrayField(*this);
190}
191
192//------------------------------------------------------------------------------
193
194ROOT::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
195 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
196 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
197 fItemSize(itemField->GetValueSize()),
198 fNWritten(0)
199{
200 if (itemField->GetAlignment() > sizeof(std::max_align_t)) {
201 // RVec uses operator new without alignment info
202 throw RException(R__FAIL("RVec does not support over-aligned types"));
203 }
204
205 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
206 fItemDeleter = GetDeleterOf(*itemField);
207 if (!itemField->GetTypeAlias().empty())
208 fTypeAlias = "ROOT::VecOps::RVec<" + itemField->GetTypeAlias() + ">";
209 Attach(std::move(itemField), "_0");
210 fValueSize =
212
213 // Determine if we can optimimize bulk reading
214 if (fSubfields[0]->IsSimple()) {
215 fBulkSubfield = fSubfields[0].get();
216 } else {
217 if (auto f = dynamic_cast<RArrayField *>(fSubfields[0].get())) {
218 auto grandChildFields = fSubfields[0]->GetMutableSubfields();
219 if (grandChildFields[0]->IsSimple()) {
220 fBulkSubfield = grandChildFields[0];
221 fBulkNRepetition = f->GetLength();
222 }
223 }
224 }
225}
226
227std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::CloneImpl(std::string_view newName) const
228{
229 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
230 return std::make_unique<RRVecField>(newName, std::move(newItemField));
231}
232
233std::size_t ROOT::RRVecField::AppendImpl(const void *from)
234{
235 auto [beginPtr, sizePtr, _] = Internal::GetRVecDataMembers(from);
236
237 std::size_t nbytes = 0;
238 if (fSubfields[0]->IsSimple() && *sizePtr) {
239 GetPrincipalColumnOf(*fSubfields[0])->AppendV(*beginPtr, *sizePtr);
240 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
241 } else {
242 for (std::int32_t i = 0; i < *sizePtr; ++i) {
243 nbytes += CallAppendOn(*fSubfields[0], *beginPtr + i * fItemSize);
244 }
245 }
246
247 fNWritten += *sizePtr;
248 fPrincipalColumn->Append(&fNWritten);
249 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
250}
251
252unsigned char *ROOT::RRVecField::ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize,
253 const RFieldBase *itemField, RDeleter *itemDeleter)
254
255{
256 if (nItems > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())) {
257 throw RException(R__FAIL("RVec too large: " + std::to_string(nItems)));
258 }
259
260 auto [beginPtr, sizePtr, capacityPtr] = Internal::GetRVecDataMembers(rvec);
261 const std::size_t oldSize = *sizePtr;
262
263 if (oldSize == nItems) {
264 // If neither shrink nor grow is necessary, do nothing.
265 // Note that this case preserves a memory adopting RVec as such. All real resizes in either direction
266 // transform a memory adopting RVec into an owning RVec.
267 return *beginPtr;
268 }
269
270 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
271 // on the element construction/destrution.
272 const bool owns = (*capacityPtr != -1);
273 const bool needsConstruct = !(itemField->GetTraits() & kTraitTriviallyConstructible);
274 const bool needsDestruct = owns && itemDeleter;
275
276 // Destroy excess elements, if any
277 if (needsDestruct) {
278 for (std::size_t i = nItems; i < oldSize; ++i) {
279 itemDeleter->operator()(*beginPtr + (i * itemSize), true /* dtorOnly */);
280 }
281 }
282
283 // Resize RVec (capacity and size)
284 // In the regime between 0 and 16 items, grow by doubling the size. Grow larger vectors by a factor 1.5 at most.
285 // If the new size is larger than the grown capacity, set the new capacity to the new size (i.e., don't allocate
286 // a larger capacity for vectors the are suddenly vastly larger than their previous state).
287 // TODO(jblomer): investigate a longer look back window to avoid shrinking for an unusual small entry and consider
288 // returning to average or median size (see TBasket::WriteReset()).
289 static constexpr std::int32_t kSmallThreshold = 16;
290 if ((std::int32_t(nItems) > *capacityPtr) || /* must grow */
291 ((std::int32_t(nItems) > kSmallThreshold) && (std::int32_t(nItems) < *capacityPtr / 4))) /* must shrink */ {
292 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
293 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
294 if (needsDestruct) {
295 // Excess elements were destructed earlier.
296 for (std::size_t i = 0u; i < std::min(oldSize, nItems); ++i) {
297 itemDeleter->operator()(*beginPtr + (i * itemSize), true /* dtorOnly */);
298 }
299 }
300
301 // Increment / decrement capacity geometrically
302 std::size_t newCapacity = nItems;
303 if (*capacityPtr > 0) {
304 constexpr std::size_t maxSize = static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max());
305 const std::size_t oldCap = static_cast<std::size_t>(*capacityPtr);
306 if (nItems > oldCap) {
307 const std::size_t grown = oldCap + ((oldCap < kSmallThreshold) ? oldCap : (oldCap / 2));
308 newCapacity = std::min(maxSize, std::max(nItems, grown));
309 } else {
310 newCapacity = oldCap / 2;
311 assert(nItems <= newCapacity);
312 }
313 }
314
315 Internal::DestroyRVecWithChecks(itemField->GetAlignment(), beginPtr, capacityPtr);
316 // We checked that the item type is not over-aligned
317 *beginPtr = static_cast<unsigned char *>(::operator new(newCapacity * itemSize, std::nothrow));
318 R__ASSERT(*beginPtr != nullptr);
319 *capacityPtr = static_cast<std::int32_t>(newCapacity);
320
321 // Placement new for elements that were already there before the resize. When shrank, we only need to construct
322 // nItems items, because that's what we going to read. When grown, the remaining elements from oldSize until
323 // nItems is constructed a few lines down (we need to do this always when the size grows, even when not changing
324 // the capacity).
325 if (needsConstruct) {
326 for (std::size_t i = 0u; i < std::min(oldSize, nItems); ++i)
327 CallConstructValueOn(*itemField, *beginPtr + (i * itemSize));
328 }
329 }
330 *sizePtr = nItems;
331
332 // Placement new for new elements, if any
333 if (needsConstruct) {
334 for (std::size_t i = oldSize; i < nItems; ++i)
335 CallConstructValueOn(*itemField, *beginPtr + (i * itemSize));
336 }
337
338 return *beginPtr;
339}
340
342{
343 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
344 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
345
346 // Read collection info for this entry
347 ROOT::NTupleSize_t nItems;
348 RNTupleLocalIndex collectionStart;
349 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
350
351 auto begin = ResizeRVec(to, nItems, fItemSize, fSubfields[0].get(), fItemDeleter.get());
352
353 if (fSubfields[0]->IsSimple() && nItems) {
354 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, begin);
355 return;
356 }
357
358 // Read the new values into the collection elements
359 for (std::size_t i = 0; i < nItems; ++i) {
360 CallReadOn(*fSubfields[0], collectionStart + i, begin + (i * fItemSize));
361 }
362}
363
364std::size_t ROOT::RRVecField::ReadBulkImpl(const RBulkSpec &bulkSpec)
365{
366 if (!fBulkSubfield)
367 return RFieldBase::ReadBulkImpl(bulkSpec);
368
369 if (bulkSpec.fAuxData->empty()) {
370 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
371 /// The following bytes store the item values, consecutively.
372 bulkSpec.fAuxData->resize(sizeof(std::size_t));
373 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fBulkNRepetition * fBulkSubfield->GetValueSize();
374 }
375 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
376 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
377 auto [beginPtr, sizePtr, capacityPtr] = Internal::GetRVecDataMembers(bulkSpec.fValues);
378
379 // Get size of the first RVec of the bulk
380 RNTupleLocalIndex firstItemIndex;
381 ROOT::NTupleSize_t collectionSize;
382 fPrincipalColumn->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
383 *beginPtr = itemValueArray;
384 *sizePtr = collectionSize;
385 *capacityPtr = -1;
386
387 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
388 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
389 // given range. If not, we'll fix up the pointers afterwards.
390 auto lastOffset = firstItemIndex.GetIndexInCluster() + collectionSize;
391 ROOT::NTupleSize_t nRemainingValues = bulkSpec.fCount - 1;
392 std::size_t nValues = 1;
393 std::size_t nItems = collectionSize;
394 while (nRemainingValues > 0) {
395 ROOT::NTupleSize_t nElementsUntilPageEnd;
396 const auto offsets =
397 fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
398 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
399 for (std::size_t i = 0; i < nBatch; ++i) {
400 const auto size = offsets[i] - lastOffset;
401 std::tie(beginPtr, sizePtr, capacityPtr) = Internal::GetRVecDataMembers(
402 reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
403 *beginPtr = itemValueArray + nItems * itemValueSize;
404 *sizePtr = size;
405 *capacityPtr = -1;
406
407 nItems += size;
408 lastOffset = offsets[i];
409 }
410 nRemainingValues -= nBatch;
411 nValues += nBatch;
412 }
413
414 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
415 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
416 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
417 if (delta != 0) {
418 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
419 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
420 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
421 beginPtrAsUChar += fValueSize;
422 }
423 }
424
425 GetPrincipalColumnOf(*fBulkSubfield)
426 ->ReadV(firstItemIndex * fBulkNRepetition, nItems * fBulkNRepetition, itemValueArray - delta);
427 return RBulkSpec::kAllSet;
428}
429
439
441{
442 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
443}
444
446{
447 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
448}
449
450std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::BeforeConnectPageSource(Internal::RPageSource &pageSource)
451{
452 if (GetOnDiskId() == kInvalidDescriptorId)
453 return nullptr;
454
455 auto descGuard = pageSource.GetSharedDescriptorGuard();
456 const auto &fieldDesc = descGuard->GetFieldDescriptor(GetOnDiskId());
457 if (fieldDesc.GetTypeName().rfind("std::array<", 0) == 0) {
458 auto substitute = std::make_unique<RArrayAsRVecField>(
459 GetFieldName(), fSubfields[0]->Clone(fSubfields[0]->GetFieldName()), fieldDesc.GetNRepetitions());
460 substitute->SetOnDiskId(GetOnDiskId());
461 return substitute;
462 }
463 return nullptr;
464}
465
467{
468 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
469}
470
471void ROOT::RRVecField::ConstructValue(void *where) const
472{
473 // initialize data members fBegin, fSize, fCapacity
474 // currently the inline buffer is left uninitialized
475 void **beginPtr = new (where)(void *)(nullptr);
476 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
477 new (sizePtr + 1) std::int32_t(-1);
478}
479
480void ROOT::RRVecField::RRVecDeleter::operator()(void *objPtr, bool dtorOnly)
481{
482 auto [beginPtr, sizePtr, capacityPtr] = Internal::GetRVecDataMembers(objPtr);
483
484 if (fItemDeleter) {
485 for (std::int32_t i = 0; i < *sizePtr; ++i) {
486 fItemDeleter->operator()(*beginPtr + i * fItemSize, true /* dtorOnly */);
487 }
488 }
489
490 Internal::DestroyRVecWithChecks(fItemAlignment, beginPtr, capacityPtr);
491 RDeleter::operator()(objPtr, dtorOnly);
492}
493
494std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RRVecField::GetDeleter() const
495{
496 if (fItemDeleter)
497 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubfields[0]));
498 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment());
499}
500
501std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue &value) const
502{
503 auto [beginPtr, sizePtr, _] = Internal::GetRVecDataMembers(value.GetPtr<void>().get());
504
505 std::vector<RValue> result;
506 result.reserve(*sizePtr);
507 for (std::int32_t i = 0; i < *sizePtr; ++i) {
508 result.emplace_back(
509 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), *beginPtr + i * fItemSize)));
510 }
511 return result;
512}
513
515{
516 return fValueSize;
517}
518
520{
521 return Internal::EvalRVecAlignment(fSubfields[0]->GetAlignment());
522}
523
525{
526 visitor.VisitRVecField(*this);
527}
528
529//------------------------------------------------------------------------------
530
531ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
532 const ROOT::RFieldDescriptor *emulatedFrom)
533 : ROOT::RFieldBase(fieldName,
534 emulatedFrom ? emulatedFrom->GetTypeName() : "std::vector<" + itemField->GetTypeName() + ">",
535 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
536 fItemSize(itemField->GetValueSize()),
537 fNWritten(0)
538{
539 if (itemField->GetAlignment() > kMaxItemAlignment) {
540 throw RException(
541 R__FAIL(std::string("Unsupported vector item alignment: ") + std::to_string(itemField->GetAlignment())));
542 }
543
544 if (emulatedFrom && !emulatedFrom->GetTypeName().empty()) {
546 if (emulatedFrom->IsSoACollection())
548 fEmulatedTypeVersion = emulatedFrom->GetTypeVersion();
549 fEmulatedTypeChecksum = emulatedFrom->GetTypeChecksum().value_or(0);
550 }
551
552 if (!itemField->GetTypeAlias().empty())
553 fTypeAlias = "std::vector<" + itemField->GetTypeAlias() + ">";
554
555 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
556 fItemDeleter = GetDeleterOf(*itemField);
557 Attach(std::move(itemField), "_0");
558}
559
560ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
561 : RVectorField(fieldName, std::move(itemField), nullptr)
562{
563}
564
565std::unique_ptr<ROOT::RVectorField>
566ROOT::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
567{
569 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(fieldName, itemField->Clone("_0"), &desc));
570}
571
572std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::CloneImpl(std::string_view newName) const
573{
574 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
575 auto isUntyped = GetTypeName().empty() || ((fTraits & kTraitEmulatedField) != 0);
577 if (isUntyped) {
579 desc = builder.TypeVersion(GetTypeVersion())
580 .TypeChecksum(GetTypeChecksum())
581 .TypeName(GetTypeName())
582 // we need an Id for MoveDescriptor to succeed, but we're not going to use it.
583 .FieldId(42)
584 // we also need a Structure for the same reason.
587 .Unwrap();
588 }
589 return std::unique_ptr<ROOT::RVectorField>(
590 new RVectorField(newName, std::move(newItemField), isUntyped ? &desc : nullptr));
591}
592
593std::size_t ROOT::RVectorField::AppendImpl(const void *from)
594{
595 auto typedValue = static_cast<const std::vector<char> *>(from);
596 // The order is important here: Profiling showed that the integer division is on the critical path. By moving the
597 // computation of count before R__ASSERT, the compiler can use the result of a single instruction (on x86) also for
598 // the modulo operation. Otherwise, it must perform the division twice because R__ASSERT expands to an external call
599 // of Fatal() in case of failure, which could have side effects that the compiler cannot analyze.
600 auto count = typedValue->size() / fItemSize;
601 R__ASSERT((typedValue->size() % fItemSize) == 0);
602 std::size_t nbytes = 0;
603
604 if (fSubfields[0]->IsSimple() && count) {
605 GetPrincipalColumnOf(*fSubfields[0])->AppendV(typedValue->data(), count);
606 nbytes += count * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
607 } else {
608 for (unsigned i = 0; i < count; ++i) {
609 nbytes += CallAppendOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
610 }
611 }
612
613 fNWritten += count;
614 fPrincipalColumn->Append(&fNWritten);
615 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
616}
617
618void ROOT::RVectorField::ResizeVector(void *vec, std::size_t nItems, std::size_t itemSize, const RFieldBase &itemField,
619 RDeleter *itemDeleter)
620{
621 auto typedValue = static_cast<std::vector<char> *>(vec);
622
623 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md
624 assert(itemSize > 0);
625 const auto oldNItems = typedValue->size() / itemSize;
626 const auto availNItems = typedValue->capacity() / itemSize;
627 assert((typedValue->size() % itemSize == 0) && (typedValue->capacity() % itemSize == 0));
628
629 const bool canRealloc = availNItems < nItems;
630 bool allDeallocated = false;
631 if (itemDeleter) {
632 allDeallocated = canRealloc;
633 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
634 itemDeleter->operator()(typedValue->data() + (i * itemSize), true /* dtorOnly */);
635 }
636 }
637
638 // Resize the vector with correct alignment
639 const auto itemAlignment = itemField.GetAlignment();
640 const auto nbytes = nItems * itemSize;
641
642 auto fnAlignedResize = [](auto &vecOfAlignedStorage, std::size_t targetSize) {
643 constexpr auto valueTypeSize = sizeof(typename std::decay_t<decltype(vecOfAlignedStorage)>::value_type);
644 constexpr auto valueTypeAlignment = alignof(typename std::decay_t<decltype(vecOfAlignedStorage)>::value_type);
645 // Ensure that this function is used with RAlignedStorage as a template argument.
646 // In general, the actual (user-defined) item type may have larger size than alignment.
647 static_assert(valueTypeSize == valueTypeAlignment);
648 assert(targetSize % valueTypeAlignment == 0);
649 vecOfAlignedStorage.resize(targetSize / valueTypeSize);
650 };
651
652 static_assert(kMaxItemAlignment == 4096);
653 // clang-format off
654 switch (itemAlignment) {
655 case 1: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 1>> *>(vec), nbytes); break;
656 case 2: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 2>> *>(vec), nbytes); break;
657 case 4: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 4>> *>(vec), nbytes); break;
658 case 8: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 8>> *>(vec), nbytes); break;
659 case 16: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 16>> *>(vec), nbytes); break;
660 case 32: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 32>> *>(vec), nbytes); break;
661 case 64: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 64>> *>(vec), nbytes); break;
662 case 128: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 128>> *>(vec), nbytes); break;
663 case 256: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 256>> *>(vec), nbytes); break;
664 case 512: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 512>> *>(vec), nbytes); break;
665 case 1024: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage<1024>> *>(vec), nbytes); break;
666 case 2048: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage<2048>> *>(vec), nbytes); break;
667 case 4096: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage<4096>> *>(vec), nbytes); break;
668 default: throw RException(R__FAIL(std::string("Unsupported alignment: ") + std::to_string(itemAlignment)));
669 }
670 // clang-format on
671
672 if (!(itemField.GetTraits() & kTraitTriviallyConstructible)) {
673 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
674 CallConstructValueOn(itemField, typedValue->data() + (i * itemSize));
675 }
676 }
677}
678
680{
681 auto typedValue = static_cast<std::vector<char> *>(to);
682
683 ROOT::NTupleSize_t nItems;
684 RNTupleLocalIndex collectionStart;
685 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
686
687 if (fSubfields[0]->IsSimple()) {
688 typedValue->resize(nItems * fItemSize);
689 if (nItems)
690 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, typedValue->data());
691 return;
692 }
693
694 ResizeVector(to, nItems, fItemSize, *fSubfields[0], fItemDeleter.get());
695
696 for (std::size_t i = 0; i < nItems; ++i) {
697 CallReadOn(*fSubfields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
698 }
699}
700
710
712{
713 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
714}
715
717{
718 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
719}
720
721std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::BeforeConnectPageSource(Internal::RPageSource &pageSource)
722{
723 if (GetOnDiskId() == kInvalidDescriptorId)
724 return nullptr;
725
726 auto descGuard = pageSource.GetSharedDescriptorGuard();
727 const auto &fieldDesc = descGuard->GetFieldDescriptor(GetOnDiskId());
728 if (fieldDesc.GetTypeName().rfind("std::array<", 0) == 0) {
729 auto substitute = std::make_unique<RArrayAsVectorField>(
730 GetFieldName(), fSubfields[0]->Clone(fSubfields[0]->GetFieldName()), fieldDesc.GetNRepetitions());
731 substitute->SetOnDiskId(GetOnDiskId());
732 return substitute;
733 }
734 return nullptr;
735}
736
738{
739 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
740}
741
743{
744 ConstructVector(where, fSubfields[0]->GetAlignment());
745}
746
748 : RDeleter(GetAlignOfVector()), fItemAlignment(itemAlignment)
749{
750}
751
752ROOT::RVectorField::RVectorDeleter::RVectorDeleter(std::size_t itemSize, std::size_t itemAlignment,
753 std::unique_ptr<RDeleter> itemDeleter)
754 : RDeleter(GetAlignOfVector()),
755 fItemSize(itemSize),
756 fItemAlignment(itemAlignment),
757 fItemDeleter(std::move(itemDeleter))
758{
759}
760
761void ROOT::RVectorField::RVectorDeleter::operator()(void *objPtr, bool dtorOnly)
762{
763 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
764 if (fItemDeleter) {
765 assert(fItemSize > 0);
766 auto nItems = vecPtr->size() / fItemSize;
767 assert((vecPtr->size() % fItemSize) == 0);
768 for (std::size_t i = 0; i < nItems; ++i) {
769 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
770 }
771 }
772
773 static_assert(kMaxItemAlignment == 4096);
774 // clang-format off
775 switch (fItemAlignment) {
776 case 1: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 1>> *>(objPtr)); break;
777 case 2: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 2>> *>(objPtr)); break;
778 case 4: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 4>> *>(objPtr)); break;
779 case 8: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 8>> *>(objPtr)); break;
780 case 16: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 16>> *>(objPtr)); break;
781 case 32: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 32>> *>(objPtr)); break;
782 case 64: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 64>> *>(objPtr)); break;
783 case 128: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 128>> *>(objPtr)); break;
784 case 256: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 256>> *>(objPtr)); break;
785 case 512: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 512>> *>(objPtr)); break;
786 case 1024: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage<1024>> *>(objPtr)); break;
787 case 2048: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage<2048>> *>(objPtr)); break;
788 case 4096: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage<4096>> *>(objPtr)); break;
789 default: throw ROOT::RException(R__FAIL(std::string("Unsupported alignment: ") + std::to_string(fItemAlignment)));
790 }
791 // clang-format on
792
793 RDeleter::operator()(objPtr, dtorOnly);
794}
795
796std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVectorField::GetDeleter() const
797{
798 if (fItemDeleter)
799 return std::make_unique<RVectorDeleter>(fItemSize, fSubfields[0]->GetAlignment(), GetDeleterOf(*fSubfields[0]));
800 return std::make_unique<RVectorDeleter>(fSubfields[0]->GetAlignment());
801}
802
803std::vector<ROOT::RFieldBase::RValue> ROOT::RVectorField::SplitValue(const RValue &value) const
804{
805 return SplitVector(value.GetPtr<void>(), *fSubfields[0]);
806}
807
809{
810 return GetSizeOfVector();
811}
812
814{
815 return GetAlignOfVector();
816}
817
819{
820 visitor.VisitVectorField(*this);
821}
822
823//------------------------------------------------------------------------------
824
825ROOT::RField<std::vector<bool>>::RField(std::string_view name)
826 : ROOT::RFieldBase(name, "std::vector<bool>", ROOT::ENTupleStructure::kCollection, false /* isSimple */)
827{
828 Attach(std::make_unique<RField<bool>>("_0"));
829}
830
831std::size_t ROOT::RField<std::vector<bool>>::AppendImpl(const void *from)
832{
833 auto typedValue = static_cast<const std::vector<bool> *>(from);
834 auto count = typedValue->size();
835 for (unsigned i = 0; i < count; ++i) {
836 bool bval = (*typedValue)[i];
837 CallAppendOn(*fSubfields[0], &bval);
838 }
839 fNWritten += count;
840 fPrincipalColumn->Append(&fNWritten);
841 return count + fPrincipalColumn->GetElement()->GetPackedSize();
842}
843
844void ROOT::RField<std::vector<bool>>::ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to)
845{
846 auto typedValue = static_cast<std::vector<bool> *>(to);
847
848 if (fOnDiskNRepetitions == 0) {
849 ROOT::NTupleSize_t nItems;
850 RNTupleLocalIndex collectionStart;
851 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
852 typedValue->resize(nItems);
853 for (std::size_t i = 0; i < nItems; ++i) {
854 bool bval;
855 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
856 (*typedValue)[i] = bval;
857 }
858 } else {
859 typedValue->resize(fOnDiskNRepetitions);
860 for (std::size_t i = 0; i < fOnDiskNRepetitions; ++i) {
861 bool bval;
862 CallReadOn(*fSubfields[0], globalIndex * fOnDiskNRepetitions + i, &bval);
863 (*typedValue)[i] = bval;
864 }
865 }
866}
867
868void ROOT::RField<std::vector<bool>>::ReadInClusterImpl(ROOT::RNTupleLocalIndex localIndex, void *to)
869{
870 auto typedValue = static_cast<std::vector<bool> *>(to);
871
872 if (fOnDiskNRepetitions == 0) {
873 ROOT::NTupleSize_t nItems;
874 RNTupleLocalIndex collectionStart;
875 fPrincipalColumn->GetCollectionInfo(localIndex, &collectionStart, &nItems);
876 typedValue->resize(nItems);
877 for (std::size_t i = 0; i < nItems; ++i) {
878 bool bval;
879 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
880 (*typedValue)[i] = bval;
881 }
882 } else {
883 typedValue->resize(fOnDiskNRepetitions);
884 for (std::size_t i = 0; i < fOnDiskNRepetitions; ++i) {
885 bool bval;
886 CallReadOn(*fSubfields[0], localIndex * fOnDiskNRepetitions + i, &bval);
887 (*typedValue)[i] = bval;
888 }
889 }
890}
891
892const ROOT::RFieldBase::RColumnRepresentations &ROOT::RField<std::vector<bool>>::GetColumnRepresentations() const
893{
894 static RColumnRepresentations representations({{ENTupleColumnType::kSplitIndex64},
898 {{}});
899 return representations;
900}
901
902void ROOT::RField<std::vector<bool>>::GenerateColumns()
903{
904 R__ASSERT(fOnDiskNRepetitions == 0); // fOnDiskNRepetitions must only be used for reading
905 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
906}
907
908void ROOT::RField<std::vector<bool>>::GenerateColumns(const ROOT::RNTupleDescriptor &desc)
909{
910 if (fOnDiskNRepetitions == 0)
911 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
912}
913
914void ROOT::RField<std::vector<bool>>::ReconcileOnDiskField(const RNTupleDescriptor &desc)
915{
916 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
917
918 if (fieldDesc.GetTypeName().rfind("std::array<", 0) == 0) {
919 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffStructure | kDiffNRepetitions).ThrowOnError();
920
921 if (fieldDesc.GetNRepetitions() == 0) {
922 throw RException(R__FAIL("fixed-size array --> std::vector<bool>: expected repetition count > 0\n" +
923 Internal::GetTypeTraceReport(*this, desc)));
924 }
925 if (fieldDesc.GetStructure() != ENTupleStructure::kPlain) {
926 throw RException(R__FAIL("fixed-size array --> std::vector<bool>: expected plain on-disk field\n" +
927 Internal::GetTypeTraceReport(*this, desc)));
928 }
929 fOnDiskNRepetitions = fieldDesc.GetNRepetitions();
930 } else {
931 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
932 }
933}
934
935std::vector<ROOT::RFieldBase::RValue> ROOT::RField<std::vector<bool>>::SplitValue(const RValue &value) const
936{
937 const auto &typedValue = value.GetRef<std::vector<bool>>();
938 auto count = typedValue.size();
939 std::vector<RValue> result;
940 result.reserve(count);
941 for (unsigned i = 0; i < count; ++i) {
942 if (typedValue[i]) {
943 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
944 } else {
945 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
946 }
947 }
948 return result;
949}
950
951void ROOT::RField<std::vector<bool>>::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
952{
953 visitor.VisitVectorBoolField(*this);
954}
955
956//------------------------------------------------------------------------------
957
958ROOT::RArrayAsRVecField::RArrayAsRVecField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
959 std::size_t arrayLength)
960 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
961 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
962 fItemSize(itemField->GetValueSize()),
963 fArrayLength(arrayLength)
964{
965 if (!itemField->GetTypeAlias().empty())
966 fTypeAlias = "ROOT::VecOps::RVec<" + itemField->GetTypeAlias() + ">";
967 Attach(std::move(itemField), "_0");
968 fValueSize =
972}
973
974std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsRVecField::CloneImpl(std::string_view newName) const
975{
976 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
977 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
978}
979
981{
982 // initialize data members fBegin, fSize, fCapacity
983 // currently the inline buffer is left uninitialized
984 void **beginPtr = new (where)(void *)(nullptr);
985 std::int32_t *sizePtr = new (static_cast<void *>(beginPtr + 1)) std::int32_t(0);
986 new (sizePtr + 1) std::int32_t(-1);
987}
988
989std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter() const
990{
991 if (fItemDeleter) {
992 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize,
993 GetDeleterOf(*fSubfields[0]));
994 }
995 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment());
996}
997
999{
1000 auto begin = RRVecField::ResizeRVec(to, fArrayLength, fItemSize, fSubfields[0].get(), fItemDeleter.get());
1001
1002 if (fSubfields[0]->IsSimple()) {
1003 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, begin);
1004 return;
1005 }
1006
1007 // Read the new values into the collection elements
1008 for (std::size_t i = 0; i < fArrayLength; ++i) {
1009 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, begin + (i * fItemSize));
1010 }
1011}
1012
1014{
1015 auto begin = RRVecField::ResizeRVec(to, fArrayLength, fItemSize, fSubfields[0].get(), fItemDeleter.get());
1016
1017 if (fSubfields[0]->IsSimple()) {
1018 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, begin);
1019 return;
1020 }
1021
1022 // Read the new values into the collection elements
1023 for (std::size_t i = 0; i < fArrayLength; ++i) {
1024 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, begin + (i * fItemSize));
1025 }
1026}
1027
1029{
1030 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffStructure | kDiffNRepetitions).ThrowOnError();
1031 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1032 if (fieldDesc.GetTypeName().rfind("std::array<", 0) != 0) {
1033 throw RException(R__FAIL("RArrayAsRVecField " + GetQualifiedFieldName() + " expects an on-disk array field\n" +
1034 Internal::GetTypeTraceReport(*this, desc)));
1035 }
1036}
1037
1039{
1040 return Internal::EvalRVecAlignment(fSubfields[0]->GetAlignment());
1041}
1042
1043std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsRVecField::SplitValue(const ROOT::RFieldBase::RValue &value) const
1044{
1045 auto arrayPtr = value.GetPtr<unsigned char>().get();
1046 std::vector<ROOT::RFieldBase::RValue> result;
1047 result.reserve(fArrayLength);
1048 for (unsigned i = 0; i < fArrayLength; ++i) {
1049 result.emplace_back(
1050 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
1051 }
1052 return result;
1053}
1054
1059
1060//------------------------------------------------------------------------------
1061
1062ROOT::RArrayAsVectorField::RArrayAsVectorField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
1063 std::size_t arrayLength)
1064 : ROOT::RFieldBase(fieldName, "std::vector<" + itemField->GetTypeName() + ">", ROOT::ENTupleStructure::kCollection,
1065 false /* isSimple */),
1066 fItemSize(itemField->GetValueSize()),
1067 fArrayLength(arrayLength)
1068{
1069 if (!itemField->GetTypeAlias().empty())
1070 fTypeAlias = "std::vector<" + itemField->GetTypeAlias() + ">";
1071 Attach(std::move(itemField), "_0");
1074}
1075
1076std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsVectorField::CloneImpl(std::string_view newName) const
1077{
1078 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
1079 return std::make_unique<RArrayAsVectorField>(newName, std::move(newItemField), fArrayLength);
1080}
1081
1083{
1084 throw RException(R__FAIL("RArrayAsVectorField fields must only be used for reading"));
1085}
1086
1088{
1089 ConstructVector(where, fSubfields[0]->GetAlignment());
1090}
1091
1092std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsVectorField::GetDeleter() const
1093{
1094 if (fItemDeleter) {
1095 return std::make_unique<RVectorField::RVectorDeleter>(fItemSize, fSubfields[0]->GetAlignment(),
1096 GetDeleterOf(*fSubfields[0]));
1097 }
1098 return std::make_unique<RVectorField::RVectorDeleter>(fSubfields[0]->GetAlignment());
1099}
1100
1102{
1103 auto typedValue = static_cast<std::vector<char> *>(to);
1104
1105 if (fSubfields[0]->IsSimple()) {
1106 typedValue->resize(fArrayLength * fItemSize);
1107 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, typedValue->data());
1108 return;
1109 }
1110
1111 RVectorField::ResizeVector(to, fArrayLength, fItemSize, *fSubfields[0], fItemDeleter.get());
1112
1113 for (std::size_t i = 0; i < fArrayLength; ++i) {
1114 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, typedValue->data() + (i * fItemSize));
1115 }
1116}
1117
1119{
1120 auto typedValue = static_cast<std::vector<char> *>(to);
1121
1122 if (fSubfields[0]->IsSimple()) {
1123 typedValue->resize(fArrayLength * fItemSize);
1124 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, typedValue->data());
1125 return;
1126 }
1127
1128 RVectorField::ResizeVector(to, fArrayLength, fItemSize, *fSubfields[0], fItemDeleter.get());
1129
1130 for (std::size_t i = 0; i < fArrayLength; ++i) {
1131 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, typedValue->data() + (i * fItemSize));
1132 }
1133}
1134
1136{
1137 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffStructure | kDiffNRepetitions);
1138
1139 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1140 if (fieldDesc.GetTypeName().rfind("std::array<", 0) != 0) {
1141 throw RException(R__FAIL("RArrayAsVectorField " + GetQualifiedFieldName() + " expects an on-disk array field\n" +
1142 Internal::GetTypeTraceReport(*this, desc)));
1143 }
1144}
1145
1146std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsVectorField::SplitValue(const ROOT::RFieldBase::RValue &value) const
1147{
1148 return SplitVector(value.GetPtr<void>(), *fSubfields[0]);
1149}
1150
1152{
1153 return GetSizeOfVector();
1154}
1155
1157{
1158 return GetAlignOfVector();
1159}
1160
size_t fValueSize
#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:322
#define f(i)
Definition RSha256.hxx:104
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:139
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 value
char name[80]
Definition TGX11.cxx:142
#define _(A, B)
Definition cfortran.h:112
Abstract base class for classes implementing the visitor design pattern.
virtual void VisitVectorField(const ROOT::RVectorField &field)
virtual void VisitVectorBoolField(const ROOT::RField< std::vector< bool > > &field)
virtual void VisitRVecField(const ROOT::RRVecField &field)
virtual void VisitArrayAsRVecField(const ROOT::RArrayAsRVecField &field)
virtual void VisitArrayAsVectorField(const ROOT::RArrayAsVectorField &field)
virtual void VisitArrayField(const ROOT::RArrayField &field)
The in-memory representation of a 32bit or 64bit on-disk index column.
A helper class for piece-wise construction of an RFieldDescriptor.
RResult< RFieldDescriptor > MoveDescriptor()
Attempt to make a field descriptor.
RFieldDescriptorBuilder & Structure(const ROOT::ENTupleStructure &structure)
RFieldDescriptorBuilder & TypeVersion(std::uint32_t typeVersion)
Abstract interface to read data from an ntuple.
RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
std::unique_ptr< RDeleter > fItemDeleter
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
RArrayAsRVecField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
Constructor of the field.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
std::unique_ptr< RDeleter > GetDeleter() const final
Returns an RRVecField::RRVecDeleter.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
The size of a value of this field, i.e. an RVec.
std::vector< RFieldBase::RValue > SplitValue(const RFieldBase::RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::size_t fValueSize
The length of the arrays in this field.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::unique_ptr< RDeleter > GetDeleter() const final
Returns an RVectorField::RVectorDeleter.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::unique_ptr< RDeleter > fItemDeleter
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RArrayAsVectorField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
The itemField argument represents the inner item of the on-disk array, i.e.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::vector< RFieldBase::RValue > SplitValue(const RFieldBase::RValue &value) const final
Creates the list of direct child values given an existing value for this field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
The length of the arrays in this field.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
void operator()(void *objPtr, bool dtorOnly) final
Template specializations for C++ std::array and C-style arrays.
std::unique_ptr< RDeleter > GetDeleter() const final
RArrayField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
General implementation of bulk read.
void ReadInClusterImpl(RNTupleLocalIndex localIndex, void *to) final
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
The list of column representations a field can have.
A functor to release the memory acquired by CreateValue() (memory and constructor).
Points to an object with RNTuple I/O support and keeps a pointer to the corresponding field.
A field translates read and write calls from/to underlying columns to/from tree values.
virtual size_t GetValueSize() const =0
What sizeof(T) for this type returns.
void Attach(std::unique_ptr< RFieldBase > child, std::string_view expectedChildName="")
Add a new subfield to the list of nested fields.
virtual size_t GetAlignment() const =0
What alignof(T) for this type returns.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
@ kTraitEmulatedField
This field is a user defined type that was missing dictionaries and was reconstructed from the on-dis...
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
@ kTraitSoACollection
The field represents a collection in SoA layout.
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
bool IsSimple() const
std::uint32_t GetTraits() const
RValue BindValue(std::shared_ptr< void > objPtr)
Creates a value from a memory location with an already constructed object.
virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec)
General implementation of bulk read.
Metadata stored for every field of an RNTuple.
std::optional< std::uint32_t > GetTypeChecksum() const
std::uint32_t GetTypeVersion() const
const std::string & GetTypeName() const
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:342
The on-storage metadata of an RNTuple.
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
ROOT::NTupleSize_t GetIndexInCluster() const
void operator()(void *objPtr, bool dtorOnly) final
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
size_t GetAlignment() const final
What alignof(T) for this type returns.
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
General implementation of bulk read.
RRVecField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
static unsigned char * ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize, const RFieldBase *itemField, RDeleter *itemDeleter)
std::unique_ptr< RDeleter > fItemDeleter
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
RFieldBase * fBulkSubfield
May be a direct PoD subfield or a sub-subfield of a fixed-size array of PoD.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::unique_ptr< RDeleter > GetDeleter() const final
void operator()(void *objPtr, bool dtorOnly) final
Template specializations for C++ std::vector.
static std::unique_ptr< RVectorField > CreateUntyped(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
static void ResizeVector(void *vec, std::size_t nItems, std::size_t itemSize, const RFieldBase &itemField, RDeleter *itemDeleter)
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
RVectorField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, const ROOT::RFieldDescriptor *emulatedFrom)
Creates a possibly-untyped VectorField.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
void ConstructValue(void *where) const final
Constructs value in a given location of size at least GetValueSize(). Called by the base class' Creat...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
std::unique_ptr< RDeleter > fItemDeleter
static constexpr std::size_t kMaxItemAlignment
Maximum alignment of the vector's value type.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::tuple< unsigned char **, std::int32_t *, std::int32_t * > GetRVecDataMembers(void *rvecPtr)
Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the R...
void DestroyRVecWithChecks(std::size_t alignOfT, unsigned char **beginPtr, std::int32_t *capacityPtr)
std::string GetNormalizedInteger(const std::string &intTemplateArg)
Appends 'll' or 'ull' to the where necessary and strips the suffix if not needed.
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
std::size_t EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
Storage type whose alignment matches AlignT bytes.
Definition BitUtils.hxx:53
Input parameter to RFieldBase::ReadBulk() and RFieldBase::ReadBulkImpl().
RNTupleLocalIndex fFirstIndex
Start of the bulk range.
void * fValues
The destination area, which has to be an array of valid objects of the correct type large enough to h...
std::size_t fCount
Size of the bulk range.
std::vector< unsigned char > * fAuxData
Reference to memory owned by the RBulkValues class.