Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Loading...
Searching...
No Matches
RFieldSequenceContainer.cxx
Go to the documentation of this file.
1/// \file RFieldSequenceContainer.cxx
2/// \ingroup NTuple ROOT7
3/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
4/// \date 2024-11-19
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#include <ROOT/RField.hxx>
9#include <ROOT/RFieldBase.hxx>
11#include <ROOT/RFieldUtils.hxx>
12
13#include <cstdlib> // for malloc, free
14#include <memory>
15#include <new> // hardware_destructive_interference_size
16
17ROOT::RArrayField::RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
18 std::size_t arrayLength)
20 "std::array<" + itemField->GetTypeName() + "," +
21 Internal::GetNormalizedInteger(static_cast<unsigned long long>(arrayLength)) + ">",
22 ROOT::ENTupleStructure::kLeaf, false /* isSimple */, arrayLength),
23 fItemSize(itemField->GetValueSize()),
24 fArrayLength(arrayLength)
25{
26 fTraits |= itemField->GetTraits() & ~kTraitMappable;
27 Attach(std::move(itemField));
28}
29
30std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayField::CloneImpl(std::string_view newName) const
31{
32 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
33 return std::make_unique<RArrayField>(newName, std::move(newItemField), fArrayLength);
34}
35
36std::size_t ROOT::RArrayField::AppendImpl(const void *from)
37{
38 std::size_t nbytes = 0;
39 if (fSubfields[0]->IsSimple()) {
40 GetPrincipalColumnOf(*fSubfields[0])->AppendV(from, fArrayLength);
41 nbytes += fArrayLength * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
42 } else {
43 auto arrayPtr = static_cast<const unsigned char *>(from);
44 for (unsigned i = 0; i < fArrayLength; ++i) {
45 nbytes += CallAppendOn(*fSubfields[0], arrayPtr + (i * fItemSize));
46 }
47 }
48 return nbytes;
49}
50
52{
53 if (fSubfields[0]->IsSimple()) {
54 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, to);
55 } else {
56 auto arrayPtr = static_cast<unsigned char *>(to);
57 for (unsigned i = 0; i < fArrayLength; ++i) {
58 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
59 }
60 }
61}
62
64{
65 if (fSubfields[0]->IsSimple()) {
66 GetPrincipalColumnOf(*fSubfields[0])
67 ->ReadV(RNTupleLocalIndex(localIndex.GetClusterId(), localIndex.GetIndexInCluster() * fArrayLength),
68 fArrayLength, to);
69 } else {
70 auto arrayPtr = static_cast<unsigned char *>(to);
71 for (unsigned i = 0; i < fArrayLength; ++i) {
72 CallReadOn(*fSubfields[0],
73 RNTupleLocalIndex(localIndex.GetClusterId(), localIndex.GetIndexInCluster() * fArrayLength + i),
74 arrayPtr + (i * fItemSize));
75 }
76 }
77}
78
80{
81 if (fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)
82 return;
83
84 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
85 for (unsigned i = 0; i < fArrayLength; ++i) {
86 CallConstructValueOn(*fSubfields[0], arrayPtr + (i * fItemSize));
87 }
88}
89
91{
92 if (fItemDeleter) {
93 for (unsigned i = 0; i < fArrayLength; ++i) {
94 fItemDeleter->operator()(reinterpret_cast<unsigned char *>(objPtr) + i * fItemSize, true /* dtorOnly */);
95 }
96 }
97 RDeleter::operator()(objPtr, dtorOnly);
98}
99
100std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayField::GetDeleter() const
101{
102 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyDestructible))
103 return std::make_unique<RArrayDeleter>(fItemSize, fArrayLength, GetDeleterOf(*fSubfields[0]));
104 return std::make_unique<RDeleter>();
105}
106
107std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayField::SplitValue(const RValue &value) const
108{
109 auto arrayPtr = value.GetPtr<unsigned char>().get();
110 std::vector<RValue> result;
111 result.reserve(fArrayLength);
112 for (unsigned i = 0; i < fArrayLength; ++i) {
113 result.emplace_back(
114 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
115 }
116 return result;
117}
118
120{
121 visitor.VisitArrayField(*this);
122}
123
124//------------------------------------------------------------------------------
125
126namespace {
127
128/// Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the RVec object.
129/// Returns pointers to fBegin, fSize and fCapacity in a std::tuple.
130std::tuple<void **, std::int32_t *, std::int32_t *> GetRVecDataMembers(void *rvecPtr)
131{
132 void **begin = reinterpret_cast<void **>(rvecPtr);
133 // int32_t fSize is the second data member (after 1 void*)
134 std::int32_t *size = reinterpret_cast<std::int32_t *>(begin + 1);
135 R__ASSERT(*size >= 0);
136 // int32_t fCapacity is the third data member (1 int32_t after fSize)
137 std::int32_t *capacity = size + 1;
138 R__ASSERT(*capacity >= -1);
139 return {begin, size, capacity};
140}
141
142std::tuple<const void *const *, const std::int32_t *, const std::int32_t *> GetRVecDataMembers(const void *rvecPtr)
143{
144 return {GetRVecDataMembers(const_cast<void *>(rvecPtr))};
145}
146
147std::size_t EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
148{
149 // the size of an RVec<T> is the size of its 4 data-members + optional padding:
150 //
151 // data members:
152 // - void *fBegin
153 // - int32_t fSize
154 // - int32_t fCapacity
155 // - the char[] inline storage, which is aligned like T
156 //
157 // padding might be present:
158 // - between fCapacity and the char[] buffer aligned like T
159 // - after the char[] buffer
160
161 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
162
163 // mimic the logic of RVecInlineStorageSize, but at runtime
164 const auto inlineStorageSz = [&] {
165#ifdef R__HAS_HARDWARE_INTERFERENCE_SIZE
166 // hardware_destructive_interference_size is a C++17 feature but many compilers do not implement it yet
167 constexpr unsigned cacheLineSize = std::hardware_destructive_interference_size;
168#else
169 constexpr unsigned cacheLineSize = 64u;
170#endif
171 const unsigned elementsPerCacheLine = (cacheLineSize - dataMemberSz) / sizeOfT;
172 constexpr unsigned maxInlineByteSize = 1024;
173 const unsigned nElements =
174 elementsPerCacheLine >= 8 ? elementsPerCacheLine : (sizeOfT * 8 > maxInlineByteSize ? 0 : 8);
175 return nElements * sizeOfT;
176 }();
177
178 // compute padding between first 3 datamembers and inline buffer
179 // (there should be no padding between the first 3 data members)
181 if (paddingMiddle != 0)
183
184 // padding at the end of the object
186 if (paddingEnd != 0)
188
190}
191
192std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
193{
194 // the alignment of an RVec<T> is the largest among the alignments of its data members
195 // (including the inline buffer which has the same alignment as the RVec::value_type)
196 return std::max({alignof(void *), alignof(std::int32_t), alignOfSubfield});
197}
198
199void DestroyRVecWithChecks(std::size_t alignOfT, void **beginPtr, char *begin, std::int32_t *capacityPtr)
200{
201 // figure out if we are in the small state, i.e. begin == &inlineBuffer
202 // there might be padding between fCapacity and the inline buffer, so we compute it here
203 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
205 if (paddingMiddle != 0)
207 const bool isSmall = (begin == (reinterpret_cast<char *>(beginPtr) + dataMemberSz + paddingMiddle));
208
209 const bool owns = (*capacityPtr != -1);
210 if (!isSmall && owns)
211 free(begin);
212}
213
214} // anonymous namespace
215
216ROOT::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
217 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
218 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
219 fItemSize(itemField->GetValueSize()),
220 fNWritten(0)
221{
222 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
224 Attach(std::move(itemField));
226}
227
228std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::CloneImpl(std::string_view newName) const
229{
230 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
231 return std::make_unique<RRVecField>(newName, std::move(newItemField));
232}
233
234std::size_t ROOT::RRVecField::AppendImpl(const void *from)
235{
236 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(from);
237
238 std::size_t nbytes = 0;
239 if (fSubfields[0]->IsSimple() && *sizePtr) {
240 GetPrincipalColumnOf(*fSubfields[0])->AppendV(*beginPtr, *sizePtr);
241 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
242 } else {
243 auto begin = reinterpret_cast<const char *>(*beginPtr); // for pointer arithmetics
244 for (std::int32_t i = 0; i < *sizePtr; ++i) {
245 nbytes += CallAppendOn(*fSubfields[0], begin + i * fItemSize);
246 }
247 }
248
249 fNWritten += *sizePtr;
250 fPrincipalColumn->Append(&fNWritten);
251 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
252}
253
255{
256 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
257 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
258
260
261 // Read collection info for this entry
264 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
265 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
266 const std::size_t oldSize = *sizePtr;
267
268 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
269 // on the element construction/destrution.
270 const bool owns = (*capacityPtr != -1);
271 const bool needsConstruct = !(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible);
272 const bool needsDestruct = owns && fItemDeleter;
273
274 // Destroy excess elements, if any
275 if (needsDestruct) {
276 for (std::size_t i = nItems; i < oldSize; ++i) {
277 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
278 }
279 }
280
281 // Resize RVec (capacity and size)
282 if (std::int32_t(nItems) > *capacityPtr) { // must reallocate
283 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
284 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
285 if (needsDestruct) {
286 for (std::size_t i = 0u; i < oldSize; ++i) {
287 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
288 }
289 }
290
291 // TODO Increment capacity by a factor rather than just enough to fit the elements.
292 if (owns) {
293 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
294 free(*beginPtr);
295 }
296 // We trust that malloc returns a buffer with large enough alignment.
297 // This might not be the case if T in RVec<T> is over-aligned.
298 *beginPtr = malloc(nItems * fItemSize);
299 R__ASSERT(*beginPtr != nullptr);
300 begin = reinterpret_cast<char *>(*beginPtr);
302
303 // Placement new for elements that were already there before the resize
304 if (needsConstruct) {
305 for (std::size_t i = 0u; i < oldSize; ++i)
306 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
307 }
308 }
309 *sizePtr = nItems;
310
311 // Placement new for new elements, if any
312 if (needsConstruct) {
313 for (std::size_t i = oldSize; i < nItems; ++i)
314 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
315 }
316
317 if (fSubfields[0]->IsSimple() && nItems) {
318 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, begin);
319 return;
320 }
321
322 // Read the new values into the collection elements
323 for (std::size_t i = 0; i < nItems; ++i) {
324 CallReadOn(*fSubfields[0], collectionStart + i, begin + (i * fItemSize));
325 }
326}
327
329{
330 if (!fSubfields[0]->IsSimple())
332
333 if (bulkSpec.fAuxData->empty()) {
334 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
335 /// The following bytes store the item values, consecutively.
336 bulkSpec.fAuxData->resize(sizeof(std::size_t));
337 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fSubfields[0]->GetValueSize();
338 }
339 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
340 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
342
343 // Get size of the first RVec of the bulk
346 this->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
349 *capacityPtr = -1;
350
351 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
352 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
353 // given range. If not, we'll fix up the pointers afterwards.
354 auto lastOffset = firstItemIndex.GetIndexInCluster() + collectionSize;
356 std::size_t nValues = 1;
357 std::size_t nItems = collectionSize;
358 while (nRemainingValues > 0) {
360 const auto offsets =
361 fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
362 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
363 for (std::size_t i = 0; i < nBatch; ++i) {
364 const auto size = offsets[i] - lastOffset;
365 std::tie(beginPtr, sizePtr, capacityPtr) =
366 GetRVecDataMembers(reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
368 *sizePtr = size;
369 *capacityPtr = -1;
370
371 nItems += size;
372 lastOffset = offsets[i];
373 }
375 nValues += nBatch;
376 }
377
378 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
379 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
380 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
381 if (delta != 0) {
382 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
383 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
384 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
386 }
387 }
388
389 GetPrincipalColumnOf(*fSubfields[0])->ReadV(firstItemIndex, nItems, itemValueArray - delta);
390 return RBulkSpec::kAllSet;
391}
392
402
407
412
414{
415 // initialize data members fBegin, fSize, fCapacity
416 // currently the inline buffer is left uninitialized
417 void **beginPtr = new (where)(void *)(nullptr);
418 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
419 new (sizePtr + 1) std::int32_t(-1);
420}
421
423{
425
426 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
427 if (fItemDeleter) {
428 for (std::int32_t i = 0; i < *sizePtr; ++i) {
429 fItemDeleter->operator()(begin + i * fItemSize, true /* dtorOnly */);
430 }
431 }
432
433 DestroyRVecWithChecks(fItemAlignment, beginPtr, begin, capacityPtr);
434 RDeleter::operator()(objPtr, dtorOnly);
435}
436
437std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RRVecField::GetDeleter() const
438{
439 if (fItemDeleter)
440 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubfields[0]));
441 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment());
442}
443
444std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue &value) const
445{
446 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetPtr<void>().get());
447
448 std::vector<RValue> result;
449 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
450 result.reserve(*sizePtr);
451 for (std::int32_t i = 0; i < *sizePtr; ++i) {
452 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), begin + i * fItemSize)));
453 }
454 return result;
455}
456
458{
459 return fValueSize;
460}
461
463{
464 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
465}
466
468{
469 visitor.VisitRVecField(*this);
470}
471
472//------------------------------------------------------------------------------
473
474ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField, bool isUntyped)
475 : ROOT::RFieldBase(fieldName, isUntyped ? "" : "std::vector<" + itemField->GetTypeName() + ">",
476 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
477 fItemSize(itemField->GetValueSize()),
478 fNWritten(0)
479{
480 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
482 Attach(std::move(itemField));
483}
484
485ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
487{
488}
489
490std::unique_ptr<ROOT::RVectorField>
491ROOT::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
492{
493 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(fieldName, std::move(itemField), true));
494}
495
496std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::CloneImpl(std::string_view newName) const
497{
498 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
499 return std::unique_ptr<ROOT::RVectorField>(
500 new RVectorField(newName, std::move(newItemField), GetTypeName().empty()));
501}
502
503std::size_t ROOT::RVectorField::AppendImpl(const void *from)
504{
505 auto typedValue = static_cast<const std::vector<char> *>(from);
506 // The order is important here: Profiling showed that the integer division is on the critical path. By moving the
507 // computation of count before R__ASSERT, the compiler can use the result of a single instruction (on x86) also for
508 // the modulo operation. Otherwise, it must perform the division twice because R__ASSERT expands to an external call
509 // of Fatal() in case of failure, which could have side effects that the compiler cannot analyze.
510 auto count = typedValue->size() / fItemSize;
511 R__ASSERT((typedValue->size() % fItemSize) == 0);
512 std::size_t nbytes = 0;
513
514 if (fSubfields[0]->IsSimple() && count) {
515 GetPrincipalColumnOf(*fSubfields[0])->AppendV(typedValue->data(), count);
516 nbytes += count * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
517 } else {
518 for (unsigned i = 0; i < count; ++i) {
519 nbytes += CallAppendOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
520 }
521 }
522
523 fNWritten += count;
524 fPrincipalColumn->Append(&fNWritten);
525 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
526}
527
529{
530 auto typedValue = static_cast<std::vector<char> *>(to);
531
534 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
535
536 if (fSubfields[0]->IsSimple()) {
537 typedValue->resize(nItems * fItemSize);
538 if (nItems)
539 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, typedValue->data());
540 return;
541 }
542
543 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md
544 R__ASSERT(fItemSize > 0);
545 const auto oldNItems = typedValue->size() / fItemSize;
546 const bool canRealloc = oldNItems < nItems;
547 bool allDeallocated = false;
548 if (fItemDeleter) {
550 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
551 fItemDeleter->operator()(typedValue->data() + (i * fItemSize), true /* dtorOnly */);
552 }
553 }
554 typedValue->resize(nItems * fItemSize);
555 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)) {
556 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
557 CallConstructValueOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
558 }
559 }
560
561 for (std::size_t i = 0; i < nItems; ++i) {
562 CallReadOn(*fSubfields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
563 }
564}
565
575
580
585
587{
588 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
589 if (fItemDeleter) {
590 R__ASSERT(fItemSize > 0);
591 R__ASSERT((vecPtr->size() % fItemSize) == 0);
592 auto nItems = vecPtr->size() / fItemSize;
593 for (std::size_t i = 0; i < nItems; ++i) {
594 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
595 }
596 }
597 std::destroy_at(vecPtr);
598 RDeleter::operator()(objPtr, dtorOnly);
599}
600
601std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVectorField::GetDeleter() const
602{
603 if (fItemDeleter)
604 return std::make_unique<RVectorDeleter>(fItemSize, GetDeleterOf(*fSubfields[0]));
605 return std::make_unique<RVectorDeleter>();
606}
607
608std::vector<ROOT::RFieldBase::RValue> ROOT::RVectorField::SplitValue(const RValue &value) const
609{
610 auto vec = value.GetPtr<std::vector<char>>();
611 R__ASSERT(fItemSize > 0);
612 R__ASSERT((vec->size() % fItemSize) == 0);
613 auto nItems = vec->size() / fItemSize;
614 std::vector<RValue> result;
615 result.reserve(nItems);
616 for (unsigned i = 0; i < nItems; ++i) {
617 result.emplace_back(
618 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), vec->data() + (i * fItemSize))));
619 }
620 return result;
621}
622
624{
625 visitor.VisitVectorField(*this);
626}
627
628//------------------------------------------------------------------------------
629
630ROOT::RField<std::vector<bool>>::RField(std::string_view name)
631 : ROOT::RFieldBase(name, "std::vector<bool>", ROOT::ENTupleStructure::kCollection, false /* isSimple */)
632{
633 Attach(std::make_unique<RField<bool>>("_0"));
634}
635
636std::size_t ROOT::RField<std::vector<bool>>::AppendImpl(const void *from)
637{
638 auto typedValue = static_cast<const std::vector<bool> *>(from);
639 auto count = typedValue->size();
640 for (unsigned i = 0; i < count; ++i) {
641 bool bval = (*typedValue)[i];
642 CallAppendOn(*fSubfields[0], &bval);
643 }
644 fNWritten += count;
645 fPrincipalColumn->Append(&fNWritten);
646 return count + fPrincipalColumn->GetElement()->GetPackedSize();
647}
648
650{
651 auto typedValue = static_cast<std::vector<bool> *>(to);
652
654 RNTupleLocalIndex collectionStart;
655 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
656
657 typedValue->resize(nItems);
658 for (unsigned i = 0; i < nItems; ++i) {
659 bool bval;
660 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
661 (*typedValue)[i] = bval;
662 }
663}
664
665const ROOT::RFieldBase::RColumnRepresentations &ROOT::RField<std::vector<bool>>::GetColumnRepresentations() const
666{
667 static RColumnRepresentations representations({{ENTupleColumnType::kSplitIndex64},
671 {});
672 return representations;
673}
674
675void ROOT::RField<std::vector<bool>>::GenerateColumns()
676{
678}
679
680void ROOT::RField<std::vector<bool>>::GenerateColumns(const ROOT::RNTupleDescriptor &desc)
681{
683}
684
685std::vector<ROOT::RFieldBase::RValue> ROOT::RField<std::vector<bool>>::SplitValue(const RValue &value) const
686{
687 const auto &typedValue = value.GetRef<std::vector<bool>>();
688 auto count = typedValue.size();
689 std::vector<RValue> result;
690 result.reserve(count);
691 for (unsigned i = 0; i < count; ++i) {
692 if (typedValue[i])
693 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
694 else
695 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
696 }
697 return result;
698}
699
701{
702 visitor.VisitVectorBoolField(*this);
703}
704
705//------------------------------------------------------------------------------
706
707ROOT::RArrayAsRVecField::RArrayAsRVecField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
708 std::size_t arrayLength)
709 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
710 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
711 fItemSize(itemField->GetValueSize()),
712 fArrayLength(arrayLength)
713{
714 Attach(std::move(itemField));
718}
719
720std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsRVecField::CloneImpl(std::string_view newName) const
721{
722 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
723 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
724}
725
727{
728 // initialize data members fBegin, fSize, fCapacity
729 void **beginPtr = new (where)(void *)(nullptr);
730 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
731 std::int32_t *capacityPtr = new (sizePtr + 1) std::int32_t(0);
732
733 // Create the RVec with the known fixed size, do it once here instead of
734 // every time the value is read in `Read*Impl` functions
735 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
736
737 // Early return if the RVec has already been allocated.
738 if (*sizePtr == std::int32_t(fArrayLength))
739 return;
740
741 // Need to allocate the RVec if it is the first time the value is being created.
742 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
743 // on the element construction.
744 const bool owns = (*capacityPtr != -1); // RVec is adopting the memory
745 const bool needsConstruct = !(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible);
746 const bool needsDestruct = owns && fItemDeleter;
747
748 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
749 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
750 if (needsDestruct) {
751 for (std::int32_t i = 0; i < *sizePtr; ++i) {
752 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
753 }
754 }
755
756 // TODO: Isn't the RVec always owning in this case?
757 if (owns) {
758 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
759 free(*beginPtr);
760 }
761
762 *beginPtr = malloc(fArrayLength * fItemSize);
763 R__ASSERT(*beginPtr != nullptr);
764 // Re-assign begin pointer after allocation
765 begin = reinterpret_cast<char *>(*beginPtr);
766 // Size and capacity are equal since the field data type is std::array
767 *sizePtr = fArrayLength;
768 *capacityPtr = fArrayLength;
769
770 // Placement new for the array elements
771 if (needsConstruct) {
772 for (std::size_t i = 0; i < fArrayLength; ++i)
773 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
774 }
775}
776
777std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter() const
778{
779 if (fItemDeleter) {
780 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize,
781 GetDeleterOf(*fSubfields[0]));
782 }
783 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment());
784}
785
787{
788
789 auto [beginPtr, _, __] = GetRVecDataMembers(to);
790 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
791
792 if (fSubfields[0]->IsSimple()) {
793 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, rvecBeginPtr);
794 return;
795 }
796
797 // Read the new values into the collection elements
798 for (std::size_t i = 0; i < fArrayLength; ++i) {
799 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, rvecBeginPtr + (i * fItemSize));
800 }
801}
802
804{
805 auto [beginPtr, _, __] = GetRVecDataMembers(to);
806 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
807
808 const auto &clusterId = localIndex.GetClusterId();
809 const auto &indexInCluster = localIndex.GetIndexInCluster();
810
811 if (fSubfields[0]->IsSimple()) {
812 GetPrincipalColumnOf(*fSubfields[0])
813 ->ReadV(RNTupleLocalIndex(clusterId, indexInCluster * fArrayLength), fArrayLength, rvecBeginPtr);
814 return;
815 }
816
817 // Read the new values into the collection elements
818 for (std::size_t i = 0; i < fArrayLength; ++i) {
819 CallReadOn(*fSubfields[0], RNTupleLocalIndex(clusterId, indexInCluster * fArrayLength + i),
820 rvecBeginPtr + (i * fItemSize));
821 }
822}
823
825{
826 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
827}
828
829std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsRVecField::SplitValue(const ROOT::RFieldBase::RValue &value) const
830{
831 auto arrayPtr = value.GetPtr<unsigned char>().get();
832 std::vector<ROOT::RFieldBase::RValue> result;
833 result.reserve(fArrayLength);
834 for (unsigned i = 0; i < fArrayLength; ++i) {
835 result.emplace_back(
836 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
837 }
838 return result;
839}
840
842{
843 visitor.VisitArrayAsRVecField(*this);
844}
size_t fValueSize
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
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:110
#define _(A, B)
Definition cfortran.h:108
#define free
Definition civetweb.c:1539
#define malloc
Definition civetweb.c:1536
Abstract base class for classes implementing the visitor design pattern.
The in-memory representation of a 32bit or 64bit on-disk index column.
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
As a rule of thumb, the alignment is equal to the size of the type.
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.
std::size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
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...
void operator()(void *objPtr, bool dtorOnly) final
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.
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...
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
The list of column representations a field can have.
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.
void Attach(std::unique_ptr< RFieldBase > child)
Add a new subfield to the list of nested fields.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
std::uint32_t GetTraits() const
virtual std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec)
General implementation of bulk read.
Classes with dictionaries that can be inspected by TClass.
Definition RField.hxx:286
The on-storage metadata of an RNTuple.
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
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
As a rule of thumb, the alignment is equal to the size of the type.
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
General implementation of bulk read.
RRVecField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField)
size_t GetValueSize() const final
The number of bytes taken by a value of the appropriate type.
std::size_t AppendImpl(const void *from) final
Operations on values of complex types, e.g.
std::unique_ptr< RDeleter > fItemDeleter
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
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 ...
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
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.
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
std::unique_ptr< RDeleter > GetDeleter() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
RVectorField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, bool isUntyped)
std::string GetNormalizedInteger(const std::string &intTemplateArg)
Appends 'll' or 'ull' to the where necessary and strips the suffix if not needed.
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
ENTupleStructure
The fields in the ntuple model tree can carry different structural information about the type system.
Input parameter to RFieldBase::ReadBulk() and RFieldBase::ReadBulkImpl().