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 DestroyRVecWithChecks(fSubfields[0]->GetAlignment(), beginPtr, begin, capacityPtr);
291 // We trust that malloc returns a buffer with large enough alignment.
292 // This might not be the case if T in RVec<T> is over-aligned.
293 *beginPtr = malloc(nItems * fItemSize);
294 R__ASSERT(*beginPtr != nullptr);
295 begin = reinterpret_cast<char *>(*beginPtr);
297
298 // Placement new for elements that were already there before the resize
299 if (needsConstruct) {
300 for (std::size_t i = 0u; i < oldSize; ++i)
301 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
302 }
303 }
304 *sizePtr = nItems;
305
306 // Placement new for new elements, if any
307 if (needsConstruct) {
308 for (std::size_t i = oldSize; i < nItems; ++i)
309 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
310 }
311
312 if (fSubfields[0]->IsSimple() && nItems) {
313 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, begin);
314 return;
315 }
316
317 // Read the new values into the collection elements
318 for (std::size_t i = 0; i < nItems; ++i) {
319 CallReadOn(*fSubfields[0], collectionStart + i, begin + (i * fItemSize));
320 }
321}
322
324{
325 if (!fSubfields[0]->IsSimple())
327
328 if (bulkSpec.fAuxData->empty()) {
329 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
330 /// The following bytes store the item values, consecutively.
331 bulkSpec.fAuxData->resize(sizeof(std::size_t));
332 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fSubfields[0]->GetValueSize();
333 }
334 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
335 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
337
338 // Get size of the first RVec of the bulk
341 fPrincipalColumn->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
344 *capacityPtr = -1;
345
346 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
347 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
348 // given range. If not, we'll fix up the pointers afterwards.
349 auto lastOffset = firstItemIndex.GetIndexInCluster() + collectionSize;
351 std::size_t nValues = 1;
352 std::size_t nItems = collectionSize;
353 while (nRemainingValues > 0) {
355 const auto offsets =
356 fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
357 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
358 for (std::size_t i = 0; i < nBatch; ++i) {
359 const auto size = offsets[i] - lastOffset;
360 std::tie(beginPtr, sizePtr, capacityPtr) =
361 GetRVecDataMembers(reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
363 *sizePtr = size;
364 *capacityPtr = -1;
365
366 nItems += size;
367 lastOffset = offsets[i];
368 }
370 nValues += nBatch;
371 }
372
373 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
374 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
375 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
376 if (delta != 0) {
377 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
378 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
379 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
381 }
382 }
383
384 GetPrincipalColumnOf(*fSubfields[0])->ReadV(firstItemIndex, nItems, itemValueArray - delta);
385 return RBulkSpec::kAllSet;
386}
387
397
402
407
409{
410 // initialize data members fBegin, fSize, fCapacity
411 // currently the inline buffer is left uninitialized
412 void **beginPtr = new (where)(void *)(nullptr);
413 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
414 new (sizePtr + 1) std::int32_t(-1);
415}
416
418{
420
421 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
422 if (fItemDeleter) {
423 for (std::int32_t i = 0; i < *sizePtr; ++i) {
424 fItemDeleter->operator()(begin + i * fItemSize, true /* dtorOnly */);
425 }
426 }
427
428 DestroyRVecWithChecks(fItemAlignment, beginPtr, begin, capacityPtr);
429 RDeleter::operator()(objPtr, dtorOnly);
430}
431
432std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RRVecField::GetDeleter() const
433{
434 if (fItemDeleter)
435 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubfields[0]));
436 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment());
437}
438
439std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue &value) const
440{
441 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetPtr<void>().get());
442
443 std::vector<RValue> result;
444 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
445 result.reserve(*sizePtr);
446 for (std::int32_t i = 0; i < *sizePtr; ++i) {
447 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), begin + i * fItemSize)));
448 }
449 return result;
450}
451
453{
454 return fValueSize;
455}
456
458{
459 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
460}
461
463{
464 visitor.VisitRVecField(*this);
465}
466
467//------------------------------------------------------------------------------
468
469ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField, bool isUntyped)
470 : ROOT::RFieldBase(fieldName, isUntyped ? "" : "std::vector<" + itemField->GetTypeName() + ">",
471 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
472 fItemSize(itemField->GetValueSize()),
473 fNWritten(0)
474{
475 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
477 Attach(std::move(itemField));
478}
479
480ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
482{
483}
484
485std::unique_ptr<ROOT::RVectorField>
486ROOT::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
487{
488 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(fieldName, itemField->Clone("_0"), true));
489}
490
491std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::CloneImpl(std::string_view newName) const
492{
493 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
494 return std::unique_ptr<ROOT::RVectorField>(
495 new RVectorField(newName, std::move(newItemField), GetTypeName().empty()));
496}
497
498std::size_t ROOT::RVectorField::AppendImpl(const void *from)
499{
500 auto typedValue = static_cast<const std::vector<char> *>(from);
501 // The order is important here: Profiling showed that the integer division is on the critical path. By moving the
502 // computation of count before R__ASSERT, the compiler can use the result of a single instruction (on x86) also for
503 // the modulo operation. Otherwise, it must perform the division twice because R__ASSERT expands to an external call
504 // of Fatal() in case of failure, which could have side effects that the compiler cannot analyze.
505 auto count = typedValue->size() / fItemSize;
506 R__ASSERT((typedValue->size() % fItemSize) == 0);
507 std::size_t nbytes = 0;
508
509 if (fSubfields[0]->IsSimple() && count) {
510 GetPrincipalColumnOf(*fSubfields[0])->AppendV(typedValue->data(), count);
511 nbytes += count * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
512 } else {
513 for (unsigned i = 0; i < count; ++i) {
514 nbytes += CallAppendOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
515 }
516 }
517
518 fNWritten += count;
519 fPrincipalColumn->Append(&fNWritten);
520 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
521}
522
524{
525 auto typedValue = static_cast<std::vector<char> *>(to);
526
529 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
530
531 if (fSubfields[0]->IsSimple()) {
532 typedValue->resize(nItems * fItemSize);
533 if (nItems)
534 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, typedValue->data());
535 return;
536 }
537
538 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md
539 R__ASSERT(fItemSize > 0);
540 const auto oldNItems = typedValue->size() / fItemSize;
541 const bool canRealloc = oldNItems < nItems;
542 bool allDeallocated = false;
543 if (fItemDeleter) {
545 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
546 fItemDeleter->operator()(typedValue->data() + (i * fItemSize), true /* dtorOnly */);
547 }
548 }
549 typedValue->resize(nItems * fItemSize);
550 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)) {
551 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
552 CallConstructValueOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
553 }
554 }
555
556 for (std::size_t i = 0; i < nItems; ++i) {
557 CallReadOn(*fSubfields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
558 }
559}
560
570
575
580
582{
583 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
584 if (fItemDeleter) {
585 R__ASSERT(fItemSize > 0);
586 R__ASSERT((vecPtr->size() % fItemSize) == 0);
587 auto nItems = vecPtr->size() / fItemSize;
588 for (std::size_t i = 0; i < nItems; ++i) {
589 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
590 }
591 }
592 std::destroy_at(vecPtr);
593 RDeleter::operator()(objPtr, dtorOnly);
594}
595
596std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVectorField::GetDeleter() const
597{
598 if (fItemDeleter)
599 return std::make_unique<RVectorDeleter>(fItemSize, GetDeleterOf(*fSubfields[0]));
600 return std::make_unique<RVectorDeleter>();
601}
602
603std::vector<ROOT::RFieldBase::RValue> ROOT::RVectorField::SplitValue(const RValue &value) const
604{
605 auto vec = value.GetPtr<std::vector<char>>();
606 R__ASSERT(fItemSize > 0);
607 R__ASSERT((vec->size() % fItemSize) == 0);
608 auto nItems = vec->size() / fItemSize;
609 std::vector<RValue> result;
610 result.reserve(nItems);
611 for (unsigned i = 0; i < nItems; ++i) {
612 result.emplace_back(
613 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), vec->data() + (i * fItemSize))));
614 }
615 return result;
616}
617
619{
620 visitor.VisitVectorField(*this);
621}
622
623//------------------------------------------------------------------------------
624
625ROOT::RField<std::vector<bool>>::RField(std::string_view name)
626 : ROOT::RFieldBase(name, "std::vector<bool>", ROOT::ENTupleStructure::kCollection, false /* isSimple */)
627{
628 Attach(std::make_unique<RField<bool>>("_0"));
629}
630
631std::size_t ROOT::RField<std::vector<bool>>::AppendImpl(const void *from)
632{
633 auto typedValue = static_cast<const std::vector<bool> *>(from);
634 auto count = typedValue->size();
635 for (unsigned i = 0; i < count; ++i) {
636 bool bval = (*typedValue)[i];
637 CallAppendOn(*fSubfields[0], &bval);
638 }
639 fNWritten += count;
640 fPrincipalColumn->Append(&fNWritten);
641 return count + fPrincipalColumn->GetElement()->GetPackedSize();
642}
643
645{
646 auto typedValue = static_cast<std::vector<bool> *>(to);
647
649 RNTupleLocalIndex collectionStart;
650 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
651
652 typedValue->resize(nItems);
653 for (unsigned i = 0; i < nItems; ++i) {
654 bool bval;
655 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
656 (*typedValue)[i] = bval;
657 }
658}
659
660const ROOT::RFieldBase::RColumnRepresentations &ROOT::RField<std::vector<bool>>::GetColumnRepresentations() const
661{
662 static RColumnRepresentations representations({{ENTupleColumnType::kSplitIndex64},
666 {});
667 return representations;
668}
669
670void ROOT::RField<std::vector<bool>>::GenerateColumns()
671{
673}
674
675void ROOT::RField<std::vector<bool>>::GenerateColumns(const ROOT::RNTupleDescriptor &desc)
676{
678}
679
680std::vector<ROOT::RFieldBase::RValue> ROOT::RField<std::vector<bool>>::SplitValue(const RValue &value) const
681{
682 const auto &typedValue = value.GetRef<std::vector<bool>>();
683 auto count = typedValue.size();
684 std::vector<RValue> result;
685 result.reserve(count);
686 for (unsigned i = 0; i < count; ++i) {
687 if (typedValue[i])
688 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
689 else
690 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
691 }
692 return result;
693}
694
696{
697 visitor.VisitVectorBoolField(*this);
698}
699
700//------------------------------------------------------------------------------
701
702ROOT::RArrayAsRVecField::RArrayAsRVecField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
703 std::size_t arrayLength)
704 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
705 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
706 fItemSize(itemField->GetValueSize()),
707 fArrayLength(arrayLength)
708{
709 Attach(std::move(itemField));
713}
714
715std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsRVecField::CloneImpl(std::string_view newName) const
716{
717 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
718 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
719}
720
722{
723 // initialize data members fBegin, fSize, fCapacity
724 void **beginPtr = new (where)(void *)(nullptr);
725 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
726 std::int32_t *capacityPtr = new (sizePtr + 1) std::int32_t(0);
727
728 // Create the RVec with the known fixed size, do it once here instead of
729 // every time the value is read in `Read*Impl` functions
730 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
731
732 // Early return if the RVec has already been allocated.
733 if (*sizePtr == std::int32_t(fArrayLength))
734 return;
735
736 // Need to allocate the RVec if it is the first time the value is being created.
737 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
738 // on the element construction.
739 const bool owns = (*capacityPtr != -1); // RVec is adopting the memory
740 const bool needsConstruct = !(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible);
741 const bool needsDestruct = owns && fItemDeleter;
742
743 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
744 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
745 if (needsDestruct) {
746 for (std::int32_t i = 0; i < *sizePtr; ++i) {
747 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
748 }
749 }
750
751 // TODO: Isn't the RVec always owning in this case?
752 if (owns) {
753 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
754 free(*beginPtr);
755 }
756
757 *beginPtr = malloc(fArrayLength * fItemSize);
758 R__ASSERT(*beginPtr != nullptr);
759 // Re-assign begin pointer after allocation
760 begin = reinterpret_cast<char *>(*beginPtr);
761 // Size and capacity are equal since the field data type is std::array
762 *sizePtr = fArrayLength;
763 *capacityPtr = fArrayLength;
764
765 // Placement new for the array elements
766 if (needsConstruct) {
767 for (std::size_t i = 0; i < fArrayLength; ++i)
768 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
769 }
770}
771
772std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter() const
773{
774 if (fItemDeleter) {
775 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize,
776 GetDeleterOf(*fSubfields[0]));
777 }
778 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment());
779}
780
782{
783
784 auto [beginPtr, _, __] = GetRVecDataMembers(to);
785 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
786
787 if (fSubfields[0]->IsSimple()) {
788 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, rvecBeginPtr);
789 return;
790 }
791
792 // Read the new values into the collection elements
793 for (std::size_t i = 0; i < fArrayLength; ++i) {
794 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, rvecBeginPtr + (i * fItemSize));
795 }
796}
797
799{
800 auto [beginPtr, _, __] = GetRVecDataMembers(to);
801 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
802
803 const auto &clusterId = localIndex.GetClusterId();
804 const auto &indexInCluster = localIndex.GetIndexInCluster();
805
806 if (fSubfields[0]->IsSimple()) {
807 GetPrincipalColumnOf(*fSubfields[0])
808 ->ReadV(RNTupleLocalIndex(clusterId, indexInCluster * fArrayLength), fArrayLength, rvecBeginPtr);
809 return;
810 }
811
812 // Read the new values into the collection elements
813 for (std::size_t i = 0; i < fArrayLength; ++i) {
814 CallReadOn(*fSubfields[0], RNTupleLocalIndex(clusterId, indexInCluster * fArrayLength + i),
815 rvecBeginPtr + (i * fItemSize));
816 }
817}
818
820{
821 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
822}
823
824std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsRVecField::SplitValue(const ROOT::RFieldBase::RValue &value) const
825{
826 auto arrayPtr = value.GetPtr<unsigned char>().get();
827 std::vector<ROOT::RFieldBase::RValue> result;
828 result.reserve(fArrayLength);
829 for (unsigned i = 0; i < fArrayLength; ++i) {
830 result.emplace_back(
831 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
832 }
833 return result;
834}
835
837{
838 visitor.VisitArrayAsRVecField(*this);
839}
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:288
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().