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::kPlain, 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 valuePtr = value.GetPtr<void>();
108 auto arrayPtr = static_cast<unsigned char *>(valuePtr.get());
109 std::vector<RValue> result;
110 result.reserve(fArrayLength);
111 for (unsigned i = 0; i < fArrayLength; ++i) {
112 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, 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 constexpr unsigned cacheLineSize = R__HARDWARE_INTERFERENCE_SIZE;
164 const unsigned elementsPerCacheLine = (cacheLineSize - dataMemberSz) / sizeOfT;
165 constexpr unsigned maxInlineByteSize = 1024;
166 const unsigned nElements =
167 elementsPerCacheLine >= 8 ? elementsPerCacheLine : (sizeOfT * 8 > maxInlineByteSize ? 0 : 8);
168 return nElements * sizeOfT;
169 }();
170
171 // compute padding between first 3 datamembers and inline buffer
172 // (there should be no padding between the first 3 data members)
174 if (paddingMiddle != 0)
176
177 // padding at the end of the object
179 if (paddingEnd != 0)
181
183}
184
185std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
186{
187 // the alignment of an RVec<T> is the largest among the alignments of its data members
188 // (including the inline buffer which has the same alignment as the RVec::value_type)
189 return std::max({alignof(void *), alignof(std::int32_t), alignOfSubfield});
190}
191
192void DestroyRVecWithChecks(std::size_t alignOfT, void **beginPtr, char *begin, std::int32_t *capacityPtr)
193{
194 // figure out if we are in the small state, i.e. begin == &inlineBuffer
195 // there might be padding between fCapacity and the inline buffer, so we compute it here
196 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
198 if (paddingMiddle != 0)
200 const bool isSmall = (begin == (reinterpret_cast<char *>(beginPtr) + dataMemberSz + paddingMiddle));
201
202 const bool owns = (*capacityPtr != -1);
203 if (!isSmall && owns)
204 free(begin);
205}
206
207} // anonymous namespace
208
209ROOT::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
210 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
211 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
212 fItemSize(itemField->GetValueSize()),
213 fNWritten(0)
214{
215 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
217 Attach(std::move(itemField));
219}
220
221std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::CloneImpl(std::string_view newName) const
222{
223 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
224 return std::make_unique<RRVecField>(newName, std::move(newItemField));
225}
226
227std::size_t ROOT::RRVecField::AppendImpl(const void *from)
228{
229 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(from);
230
231 std::size_t nbytes = 0;
232 if (fSubfields[0]->IsSimple() && *sizePtr) {
233 GetPrincipalColumnOf(*fSubfields[0])->AppendV(*beginPtr, *sizePtr);
234 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
235 } else {
236 auto begin = reinterpret_cast<const char *>(*beginPtr); // for pointer arithmetics
237 for (std::int32_t i = 0; i < *sizePtr; ++i) {
238 nbytes += CallAppendOn(*fSubfields[0], begin + i * fItemSize);
239 }
240 }
241
242 fNWritten += *sizePtr;
243 fPrincipalColumn->Append(&fNWritten);
244 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
245}
246
248{
249 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
250 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
251
253
254 // Read collection info for this entry
257 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
258 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
259 const std::size_t oldSize = *sizePtr;
260
261 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
262 // on the element construction/destrution.
263 const bool owns = (*capacityPtr != -1);
264 const bool needsConstruct = !(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible);
265 const bool needsDestruct = owns && fItemDeleter;
266
267 // Destroy excess elements, if any
268 if (needsDestruct) {
269 for (std::size_t i = nItems; i < oldSize; ++i) {
270 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
271 }
272 }
273
274 // Resize RVec (capacity and size)
275 if (std::int32_t(nItems) > *capacityPtr) { // must reallocate
276 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
277 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
278 if (needsDestruct) {
279 for (std::size_t i = 0u; i < oldSize; ++i) {
280 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
281 }
282 }
283
284 // TODO Increment capacity by a factor rather than just enough to fit the elements.
285 if (owns) {
286 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
287 free(*beginPtr);
288 }
289 // We trust that malloc returns a buffer with large enough alignment.
290 // This might not be the case if T in RVec<T> is over-aligned.
291 *beginPtr = malloc(nItems * fItemSize);
292 R__ASSERT(*beginPtr != nullptr);
293 begin = reinterpret_cast<char *>(*beginPtr);
295
296 // Placement new for elements that were already there before the resize
297 if (needsConstruct) {
298 for (std::size_t i = 0u; i < oldSize; ++i)
299 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
300 }
301 }
302 *sizePtr = nItems;
303
304 // Placement new for new elements, if any
305 if (needsConstruct) {
306 for (std::size_t i = oldSize; i < nItems; ++i)
307 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
308 }
309
310 if (fSubfields[0]->IsSimple() && nItems) {
311 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, begin);
312 return;
313 }
314
315 // Read the new values into the collection elements
316 for (std::size_t i = 0; i < nItems; ++i) {
317 CallReadOn(*fSubfields[0], collectionStart + i, begin + (i * fItemSize));
318 }
319}
320
322{
323 if (!fSubfields[0]->IsSimple())
325
326 if (bulkSpec.fAuxData->empty()) {
327 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
328 /// The following bytes store the item values, consecutively.
329 bulkSpec.fAuxData->resize(sizeof(std::size_t));
330 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fSubfields[0]->GetValueSize();
331 }
332 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
333 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
335
336 // Get size of the first RVec of the bulk
339 this->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
342 *capacityPtr = -1;
343
344 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
345 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
346 // given range. If not, we'll fix up the pointers afterwards.
347 auto lastOffset = firstItemIndex.GetIndexInCluster() + collectionSize;
349 std::size_t nValues = 1;
350 std::size_t nItems = collectionSize;
351 while (nRemainingValues > 0) {
353 const auto offsets =
354 fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
355 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
356 for (std::size_t i = 0; i < nBatch; ++i) {
357 const auto size = offsets[i] - lastOffset;
358 std::tie(beginPtr, sizePtr, capacityPtr) =
359 GetRVecDataMembers(reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
361 *sizePtr = size;
362 *capacityPtr = -1;
363
364 nItems += size;
365 lastOffset = offsets[i];
366 }
368 nValues += nBatch;
369 }
370
371 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
372 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
373 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
374 if (delta != 0) {
375 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
376 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
377 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
379 }
380 }
381
382 GetPrincipalColumnOf(*fSubfields[0])->ReadV(firstItemIndex, nItems, itemValueArray - delta);
383 return RBulkSpec::kAllSet;
384}
385
395
400
405
407{
408 // initialize data members fBegin, fSize, fCapacity
409 // currently the inline buffer is left uninitialized
410 void **beginPtr = new (where)(void *)(nullptr);
411 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
412 new (sizePtr + 1) std::int32_t(-1);
413}
414
416{
418
419 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
420 if (fItemDeleter) {
421 for (std::int32_t i = 0; i < *sizePtr; ++i) {
422 fItemDeleter->operator()(begin + i * fItemSize, true /* dtorOnly */);
423 }
424 }
425
426 DestroyRVecWithChecks(fItemAlignment, beginPtr, begin, capacityPtr);
427 RDeleter::operator()(objPtr, dtorOnly);
428}
429
430std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RRVecField::GetDeleter() const
431{
432 if (fItemDeleter)
433 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubfields[0]));
434 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment());
435}
436
437std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue &value) const
438{
439 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetPtr<void>().get());
440
441 std::vector<RValue> result;
442 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
443 result.reserve(*sizePtr);
444 for (std::int32_t i = 0; i < *sizePtr; ++i) {
445 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), begin + i * fItemSize)));
446 }
447 return result;
448}
449
451{
452 return fValueSize;
453}
454
456{
457 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
458}
459
461{
462 visitor.VisitRVecField(*this);
463}
464
465//------------------------------------------------------------------------------
466
467ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
468 std::optional<std::string_view> emulatedFromType)
469 : ROOT::RFieldBase(fieldName, emulatedFromType ? *emulatedFromType : "std::vector<" + itemField->GetTypeName() + ">",
470 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
471 fItemSize(itemField->GetValueSize()),
472 fNWritten(0)
473{
474 if (emulatedFromType && !emulatedFromType->empty())
476
477 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
479 Attach(std::move(itemField));
480}
481
482ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
484{
485}
486
487std::unique_ptr<ROOT::RVectorField>
488ROOT::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
489{
490 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(fieldName, std::move(itemField), ""));
491}
492
493std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::CloneImpl(std::string_view newName) const
494{
495 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
496 auto isUntyped = GetTypeName().empty() || ((fTraits & kTraitEmulatedField) != 0);
497 auto emulatedFromType = isUntyped ? std::make_optional(GetTypeName()) : std::nullopt;
498 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(newName, std::move(newItemField), emulatedFromType));
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 valuePtr = value.GetPtr<void>();
609 auto *vec = static_cast<std::vector<char> *>(valuePtr.get());
610 R__ASSERT(fItemSize > 0);
611 R__ASSERT((vec->size() % fItemSize) == 0);
612 auto nItems = vec->size() / fItemSize;
613 std::vector<RValue> result;
614 result.reserve(nItems);
615 for (unsigned i = 0; i < nItems; ++i) {
616 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, 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:1578
#define malloc
Definition civetweb.c:1575
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.
std::uint32_t GetTraits() const
@ kTraitEmulatedField
This field is a user defined type that was missing dictionaries and was reconstructed from the on-dis...
@ kTraitTriviallyDestructible
The type is cleaned up just by freeing its memory. I.e. the destructor performs a no-op.
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:287
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.
RVectorField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::optional< std::string_view > emulatedFromType)
Creates a possibly-untyped VectorField.
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
std::string GetNormalizedInteger(const std::string &intTemplateArg)
Appends 'll' or 'ull' to the where necessary and strips the suffix if not needed.
Namespace for new ROOT classes and functions.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
ENTupleStructure
The fields in the RNTuple data model tree can carry different structural information about the type s...
Input parameter to RFieldBase::ReadBulk() and RFieldBase::ReadBulkImpl().