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