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 static const std::vector<std::string> prefixes = {"std::array<"};
80
81 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
82 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName);
83 EnsureMatchingTypePrefix(fieldDesc, prefixes);
84}
85
87{
88 if (fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)
89 return;
90
91 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
92 for (unsigned i = 0; i < fArrayLength; ++i) {
93 CallConstructValueOn(*fSubfields[0], arrayPtr + (i * fItemSize));
94 }
95}
96
98{
99 if (fItemDeleter) {
100 for (unsigned i = 0; i < fArrayLength; ++i) {
101 fItemDeleter->operator()(reinterpret_cast<unsigned char *>(objPtr) + i * fItemSize, true /* dtorOnly */);
102 }
103 }
104 RDeleter::operator()(objPtr, dtorOnly);
105}
106
107std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayField::GetDeleter() const
108{
109 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyDestructible))
110 return std::make_unique<RArrayDeleter>(fItemSize, fArrayLength, GetDeleterOf(*fSubfields[0]));
111 return std::make_unique<RDeleter>();
112}
113
114std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayField::SplitValue(const RValue &value) const
115{
116 auto valuePtr = value.GetPtr<void>();
117 auto arrayPtr = static_cast<unsigned char *>(valuePtr.get());
118 std::vector<RValue> result;
119 result.reserve(fArrayLength);
120 for (unsigned i = 0; i < fArrayLength; ++i) {
121 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, arrayPtr + (i * fItemSize))));
122 }
123 return result;
124}
125
127{
128 visitor.VisitArrayField(*this);
129}
130
131//------------------------------------------------------------------------------
132
133namespace {
134
135/// Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the RVec object.
136/// Returns pointers to fBegin, fSize and fCapacity in a std::tuple.
137std::tuple<void **, std::int32_t *, std::int32_t *> GetRVecDataMembers(void *rvecPtr)
138{
139 void **begin = reinterpret_cast<void **>(rvecPtr);
140 // int32_t fSize is the second data member (after 1 void*)
141 std::int32_t *size = reinterpret_cast<std::int32_t *>(begin + 1);
142 R__ASSERT(*size >= 0);
143 // int32_t fCapacity is the third data member (1 int32_t after fSize)
144 std::int32_t *capacity = size + 1;
145 R__ASSERT(*capacity >= -1);
146 return {begin, size, capacity};
147}
148
149std::tuple<const void *const *, const std::int32_t *, const std::int32_t *> GetRVecDataMembers(const void *rvecPtr)
150{
151 return {GetRVecDataMembers(const_cast<void *>(rvecPtr))};
152}
153
154std::size_t EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
155{
156 // the size of an RVec<T> is the size of its 4 data-members + optional padding:
157 //
158 // data members:
159 // - void *fBegin
160 // - int32_t fSize
161 // - int32_t fCapacity
162 // - the char[] inline storage, which is aligned like T
163 //
164 // padding might be present:
165 // - between fCapacity and the char[] buffer aligned like T
166 // - after the char[] buffer
167
168 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
169
170 // mimic the logic of RVecInlineStorageSize, but at runtime
171 const auto inlineStorageSz = [&] {
172 constexpr unsigned cacheLineSize = R__HARDWARE_INTERFERENCE_SIZE;
173 const unsigned elementsPerCacheLine = (cacheLineSize - dataMemberSz) / sizeOfT;
174 constexpr unsigned maxInlineByteSize = 1024;
175 const unsigned nElements =
176 elementsPerCacheLine >= 8 ? elementsPerCacheLine : (sizeOfT * 8 > maxInlineByteSize ? 0 : 8);
177 return nElements * sizeOfT;
178 }();
179
180 // compute padding between first 3 datamembers and inline buffer
181 // (there should be no padding between the first 3 data members)
183 if (paddingMiddle != 0)
185
186 // padding at the end of the object
188 if (paddingEnd != 0)
190
192}
193
194std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
195{
196 // the alignment of an RVec<T> is the largest among the alignments of its data members
197 // (including the inline buffer which has the same alignment as the RVec::value_type)
198 return std::max({alignof(void *), alignof(std::int32_t), alignOfSubfield});
199}
200
201void DestroyRVecWithChecks(std::size_t alignOfT, void **beginPtr, char *begin, std::int32_t *capacityPtr)
202{
203 // figure out if we are in the small state, i.e. begin == &inlineBuffer
204 // there might be padding between fCapacity and the inline buffer, so we compute it here
205 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
207 if (paddingMiddle != 0)
209 const bool isSmall = (begin == (reinterpret_cast<char *>(beginPtr) + dataMemberSz + paddingMiddle));
210
211 const bool owns = (*capacityPtr != -1);
212 if (!isSmall && owns)
213 free(begin);
214}
215
216} // anonymous namespace
217
218ROOT::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
219 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
220 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
221 fItemSize(itemField->GetValueSize()),
222 fNWritten(0)
223{
224 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
226 Attach(std::move(itemField));
228}
229
230std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::CloneImpl(std::string_view newName) const
231{
232 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
233 return std::make_unique<RRVecField>(newName, std::move(newItemField));
234}
235
236std::size_t ROOT::RRVecField::AppendImpl(const void *from)
237{
238 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(from);
239
240 std::size_t nbytes = 0;
241 if (fSubfields[0]->IsSimple() && *sizePtr) {
242 GetPrincipalColumnOf(*fSubfields[0])->AppendV(*beginPtr, *sizePtr);
243 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
244 } else {
245 auto begin = reinterpret_cast<const char *>(*beginPtr); // for pointer arithmetics
246 for (std::int32_t i = 0; i < *sizePtr; ++i) {
247 nbytes += CallAppendOn(*fSubfields[0], begin + i * fItemSize);
248 }
249 }
250
251 fNWritten += *sizePtr;
252 fPrincipalColumn->Append(&fNWritten);
253 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
254}
255
257{
258 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
259 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
260
262
263 // Read collection info for this entry
266 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
267 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
268 const std::size_t oldSize = *sizePtr;
269
270 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
271 // on the element construction/destrution.
272 const bool owns = (*capacityPtr != -1);
273 const bool needsConstruct = !(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible);
274 const bool needsDestruct = owns && fItemDeleter;
275
276 // Destroy excess elements, if any
277 if (needsDestruct) {
278 for (std::size_t i = nItems; i < oldSize; ++i) {
279 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
280 }
281 }
282
283 // Resize RVec (capacity and size)
284 if (std::int32_t(nItems) > *capacityPtr) { // must reallocate
285 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
286 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
287 if (needsDestruct) {
288 for (std::size_t i = 0u; i < oldSize; ++i) {
289 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
290 }
291 }
292
293 // TODO Increment capacity by a factor rather than just enough to fit the elements.
294 if (owns) {
295 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
296 free(*beginPtr);
297 }
298 // We trust that malloc returns a buffer with large enough alignment.
299 // This might not be the case if T in RVec<T> is over-aligned.
300 *beginPtr = malloc(nItems * fItemSize);
301 R__ASSERT(*beginPtr != nullptr);
302 begin = reinterpret_cast<char *>(*beginPtr);
304
305 // Placement new for elements that were already there before the resize
306 if (needsConstruct) {
307 for (std::size_t i = 0u; i < oldSize; ++i)
308 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
309 }
310 }
311 *sizePtr = nItems;
312
313 // Placement new for new elements, if any
314 if (needsConstruct) {
315 for (std::size_t i = oldSize; i < nItems; ++i)
316 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
317 }
318
319 if (fSubfields[0]->IsSimple() && nItems) {
320 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, begin);
321 return;
322 }
323
324 // Read the new values into the collection elements
325 for (std::size_t i = 0; i < nItems; ++i) {
326 CallReadOn(*fSubfields[0], collectionStart + i, begin + (i * fItemSize));
327 }
328}
329
331{
332 if (!fSubfields[0]->IsSimple())
334
335 if (bulkSpec.fAuxData->empty()) {
336 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
337 /// The following bytes store the item values, consecutively.
338 bulkSpec.fAuxData->resize(sizeof(std::size_t));
339 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fSubfields[0]->GetValueSize();
340 }
341 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
342 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
344
345 // Get size of the first RVec of the bulk
348 this->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
351 *capacityPtr = -1;
352
353 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
354 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
355 // given range. If not, we'll fix up the pointers afterwards.
356 auto lastOffset = firstItemIndex.GetIndexInCluster() + collectionSize;
358 std::size_t nValues = 1;
359 std::size_t nItems = collectionSize;
360 while (nRemainingValues > 0) {
362 const auto offsets =
363 fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
364 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
365 for (std::size_t i = 0; i < nBatch; ++i) {
366 const auto size = offsets[i] - lastOffset;
367 std::tie(beginPtr, sizePtr, capacityPtr) =
368 GetRVecDataMembers(reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
370 *sizePtr = size;
371 *capacityPtr = -1;
372
373 nItems += size;
374 lastOffset = offsets[i];
375 }
377 nValues += nBatch;
378 }
379
380 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
381 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
382 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
383 if (delta != 0) {
384 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
385 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
386 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
388 }
389 }
390
391 GetPrincipalColumnOf(*fSubfields[0])->ReadV(firstItemIndex, nItems, itemValueArray - delta);
392 return RBulkSpec::kAllSet;
393}
394
404
409
414
416{
417 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName);
418}
419
421{
422 // initialize data members fBegin, fSize, fCapacity
423 // currently the inline buffer is left uninitialized
424 void **beginPtr = new (where)(void *)(nullptr);
425 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
426 new (sizePtr + 1) std::int32_t(-1);
427}
428
430{
432
433 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
434 if (fItemDeleter) {
435 for (std::int32_t i = 0; i < *sizePtr; ++i) {
436 fItemDeleter->operator()(begin + i * fItemSize, true /* dtorOnly */);
437 }
438 }
439
440 DestroyRVecWithChecks(fItemAlignment, beginPtr, begin, capacityPtr);
441 RDeleter::operator()(objPtr, dtorOnly);
442}
443
444std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RRVecField::GetDeleter() const
445{
446 if (fItemDeleter)
447 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubfields[0]));
448 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment());
449}
450
451std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue &value) const
452{
453 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetPtr<void>().get());
454
455 std::vector<RValue> result;
456 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
457 result.reserve(*sizePtr);
458 for (std::int32_t i = 0; i < *sizePtr; ++i) {
459 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), begin + i * fItemSize)));
460 }
461 return result;
462}
463
465{
466 return fValueSize;
467}
468
470{
471 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
472}
473
475{
476 visitor.VisitRVecField(*this);
477}
478
479//------------------------------------------------------------------------------
480
481ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
482 std::optional<std::string_view> emulatedFromType)
483 : ROOT::RFieldBase(fieldName, emulatedFromType ? *emulatedFromType : "std::vector<" + itemField->GetTypeName() + ">",
484 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
485 fItemSize(itemField->GetValueSize()),
486 fNWritten(0)
487{
488 if (emulatedFromType && !emulatedFromType->empty())
490
491 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
493 Attach(std::move(itemField));
494}
495
496ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
498{
499}
500
501std::unique_ptr<ROOT::RVectorField>
502ROOT::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
503{
504 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(fieldName, std::move(itemField), ""));
505}
506
507std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::CloneImpl(std::string_view newName) const
508{
509 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
510 auto isUntyped = GetTypeName().empty() || ((fTraits & kTraitEmulatedField) != 0);
511 auto emulatedFromType = isUntyped ? std::make_optional(GetTypeName()) : std::nullopt;
512 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(newName, std::move(newItemField), emulatedFromType));
513}
514
515std::size_t ROOT::RVectorField::AppendImpl(const void *from)
516{
517 auto typedValue = static_cast<const std::vector<char> *>(from);
518 // The order is important here: Profiling showed that the integer division is on the critical path. By moving the
519 // computation of count before R__ASSERT, the compiler can use the result of a single instruction (on x86) also for
520 // the modulo operation. Otherwise, it must perform the division twice because R__ASSERT expands to an external call
521 // of Fatal() in case of failure, which could have side effects that the compiler cannot analyze.
522 auto count = typedValue->size() / fItemSize;
523 R__ASSERT((typedValue->size() % fItemSize) == 0);
524 std::size_t nbytes = 0;
525
526 if (fSubfields[0]->IsSimple() && count) {
527 GetPrincipalColumnOf(*fSubfields[0])->AppendV(typedValue->data(), count);
528 nbytes += count * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
529 } else {
530 for (unsigned i = 0; i < count; ++i) {
531 nbytes += CallAppendOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
532 }
533 }
534
535 fNWritten += count;
536 fPrincipalColumn->Append(&fNWritten);
537 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
538}
539
541{
542 auto typedValue = static_cast<std::vector<char> *>(to);
543
546 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
547
548 if (fSubfields[0]->IsSimple()) {
549 typedValue->resize(nItems * fItemSize);
550 if (nItems)
551 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, typedValue->data());
552 return;
553 }
554
555 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md
556 R__ASSERT(fItemSize > 0);
557 const auto oldNItems = typedValue->size() / fItemSize;
558 const bool canRealloc = oldNItems < nItems;
559 bool allDeallocated = false;
560 if (fItemDeleter) {
562 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
563 fItemDeleter->operator()(typedValue->data() + (i * fItemSize), true /* dtorOnly */);
564 }
565 }
566 typedValue->resize(nItems * fItemSize);
567 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)) {
568 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
569 CallConstructValueOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
570 }
571 }
572
573 for (std::size_t i = 0; i < nItems; ++i) {
574 CallReadOn(*fSubfields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
575 }
576}
577
587
592
597
599{
600 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName);
601}
602
604{
605 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
606 if (fItemDeleter) {
607 R__ASSERT(fItemSize > 0);
608 R__ASSERT((vecPtr->size() % fItemSize) == 0);
609 auto nItems = vecPtr->size() / fItemSize;
610 for (std::size_t i = 0; i < nItems; ++i) {
611 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
612 }
613 }
614 std::destroy_at(vecPtr);
615 RDeleter::operator()(objPtr, dtorOnly);
616}
617
618std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVectorField::GetDeleter() const
619{
620 if (fItemDeleter)
621 return std::make_unique<RVectorDeleter>(fItemSize, GetDeleterOf(*fSubfields[0]));
622 return std::make_unique<RVectorDeleter>();
623}
624
625std::vector<ROOT::RFieldBase::RValue> ROOT::RVectorField::SplitValue(const RValue &value) const
626{
627 auto valuePtr = value.GetPtr<void>();
628 auto *vec = static_cast<std::vector<char> *>(valuePtr.get());
629 R__ASSERT(fItemSize > 0);
630 R__ASSERT((vec->size() % fItemSize) == 0);
631 auto nItems = vec->size() / fItemSize;
632 std::vector<RValue> result;
633 result.reserve(nItems);
634 for (unsigned i = 0; i < nItems; ++i) {
635 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, vec->data() + (i * fItemSize))));
636 }
637 return result;
638}
639
641{
642 visitor.VisitVectorField(*this);
643}
644
645//------------------------------------------------------------------------------
646
647ROOT::RField<std::vector<bool>>::RField(std::string_view name)
648 : ROOT::RFieldBase(name, "std::vector<bool>", ROOT::ENTupleStructure::kCollection, false /* isSimple */)
649{
650 Attach(std::make_unique<RField<bool>>("_0"));
651}
652
653std::size_t ROOT::RField<std::vector<bool>>::AppendImpl(const void *from)
654{
655 auto typedValue = static_cast<const std::vector<bool> *>(from);
656 auto count = typedValue->size();
657 for (unsigned i = 0; i < count; ++i) {
658 bool bval = (*typedValue)[i];
659 CallAppendOn(*fSubfields[0], &bval);
660 }
661 fNWritten += count;
662 fPrincipalColumn->Append(&fNWritten);
663 return count + fPrincipalColumn->GetElement()->GetPackedSize();
664}
665
667{
668 auto typedValue = static_cast<std::vector<bool> *>(to);
669
671 RNTupleLocalIndex collectionStart;
672 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
673
674 typedValue->resize(nItems);
675 for (unsigned i = 0; i < nItems; ++i) {
676 bool bval;
677 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
678 (*typedValue)[i] = bval;
679 }
680}
681
682const ROOT::RFieldBase::RColumnRepresentations &ROOT::RField<std::vector<bool>>::GetColumnRepresentations() const
683{
684 static RColumnRepresentations representations({{ENTupleColumnType::kSplitIndex64},
688 {});
689 return representations;
690}
691
692void ROOT::RField<std::vector<bool>>::GenerateColumns()
693{
695}
696
697void ROOT::RField<std::vector<bool>>::GenerateColumns(const ROOT::RNTupleDescriptor &desc)
698{
700}
701
702void ROOT::RField<std::vector<bool>>::ReconcileOnDiskField(const RNTupleDescriptor &desc)
703{
704 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName);
705}
706
707std::vector<ROOT::RFieldBase::RValue> ROOT::RField<std::vector<bool>>::SplitValue(const RValue &value) const
708{
709 const auto &typedValue = value.GetRef<std::vector<bool>>();
710 auto count = typedValue.size();
711 std::vector<RValue> result;
712 result.reserve(count);
713 for (unsigned i = 0; i < count; ++i) {
714 if (typedValue[i])
715 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
716 else
717 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
718 }
719 return result;
720}
721
723{
724 visitor.VisitVectorBoolField(*this);
725}
726
727//------------------------------------------------------------------------------
728
729ROOT::RArrayAsRVecField::RArrayAsRVecField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
730 std::size_t arrayLength)
731 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
732 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
733 fItemSize(itemField->GetValueSize()),
734 fArrayLength(arrayLength)
735{
736 Attach(std::move(itemField));
740}
741
742std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsRVecField::CloneImpl(std::string_view newName) const
743{
744 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
745 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
746}
747
749{
750 // initialize data members fBegin, fSize, fCapacity
751 void **beginPtr = new (where)(void *)(nullptr);
752 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
753 std::int32_t *capacityPtr = new (sizePtr + 1) std::int32_t(0);
754
755 // Create the RVec with the known fixed size, do it once here instead of
756 // every time the value is read in `Read*Impl` functions
757 char *begin = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
758
759 // Early return if the RVec has already been allocated.
760 if (*sizePtr == std::int32_t(fArrayLength))
761 return;
762
763 // Need to allocate the RVec if it is the first time the value is being created.
764 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
765 // on the element construction.
766 const bool owns = (*capacityPtr != -1); // RVec is adopting the memory
767 const bool needsConstruct = !(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible);
768 const bool needsDestruct = owns && fItemDeleter;
769
770 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
771 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
772 if (needsDestruct) {
773 for (std::int32_t i = 0; i < *sizePtr; ++i) {
774 fItemDeleter->operator()(begin + (i * fItemSize), true /* dtorOnly */);
775 }
776 }
777
778 // TODO: Isn't the RVec always owning in this case?
779 if (owns) {
780 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
781 free(*beginPtr);
782 }
783
784 *beginPtr = malloc(fArrayLength * fItemSize);
785 R__ASSERT(*beginPtr != nullptr);
786 // Re-assign begin pointer after allocation
787 begin = reinterpret_cast<char *>(*beginPtr);
788 // Size and capacity are equal since the field data type is std::array
789 *sizePtr = fArrayLength;
790 *capacityPtr = fArrayLength;
791
792 // Placement new for the array elements
793 if (needsConstruct) {
794 for (std::size_t i = 0; i < fArrayLength; ++i)
795 CallConstructValueOn(*fSubfields[0], begin + (i * fItemSize));
796 }
797}
798
799std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter() const
800{
801 if (fItemDeleter) {
802 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize,
803 GetDeleterOf(*fSubfields[0]));
804 }
805 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment());
806}
807
809{
810
811 auto [beginPtr, _, __] = GetRVecDataMembers(to);
812 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
813
814 if (fSubfields[0]->IsSimple()) {
815 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, rvecBeginPtr);
816 return;
817 }
818
819 // Read the new values into the collection elements
820 for (std::size_t i = 0; i < fArrayLength; ++i) {
821 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, rvecBeginPtr + (i * fItemSize));
822 }
823}
824
826{
827 auto [beginPtr, _, __] = GetRVecDataMembers(to);
828 auto rvecBeginPtr = reinterpret_cast<char *>(*beginPtr); // for pointer arithmetics
829
830 const auto &clusterId = localIndex.GetClusterId();
831 const auto &indexInCluster = localIndex.GetIndexInCluster();
832
833 if (fSubfields[0]->IsSimple()) {
834 GetPrincipalColumnOf(*fSubfields[0])
835 ->ReadV(RNTupleLocalIndex(clusterId, indexInCluster * fArrayLength), fArrayLength, rvecBeginPtr);
836 return;
837 }
838
839 // Read the new values into the collection elements
840 for (std::size_t i = 0; i < fArrayLength; ++i) {
841 CallReadOn(*fSubfields[0], RNTupleLocalIndex(clusterId, indexInCluster * fArrayLength + i),
842 rvecBeginPtr + (i * fItemSize));
843 }
844}
845
847{
848 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
849 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName | kDiffTypeVersion | kDiffStructure | kDiffNRepetitions);
850 if (fieldDesc.GetTypeName().rfind("std::array<", 0) != 0) {
851 throw RException(R__FAIL("RArrayAsRVecField " + GetQualifiedFieldName() + " expects an on-disk array field"));
852 }
853}
854
856{
857 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
858}
859
860std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsRVecField::SplitValue(const ROOT::RFieldBase::RValue &value) const
861{
862 auto arrayPtr = value.GetPtr<unsigned char>().get();
863 std::vector<ROOT::RFieldBase::RValue> result;
864 result.reserve(fArrayLength);
865 for (unsigned i = 0; i < fArrayLength; ++i) {
866 result.emplace_back(
867 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
868 }
869 return result;
870}
871
873{
874 visitor.VisitArrayAsRVecField(*this);
875}
size_t fValueSize
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
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.
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::size_t GetValueSize() const final
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...
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::vector< RValue > SplitValue(const RValue &value) const final
Creates the list of direct child values given an existing value for this field.
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
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:292
The on-storage metadata of an RNTuple.
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
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)
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
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
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
std::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
Called by Clone(), which additionally copies the on-disk ID.
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.
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().