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 <limits>
13#include <memory>
14#include <new> // hardware_destructive_interference_size
15
16ROOT::RArrayField::RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
17 std::size_t arrayLength)
19 "std::array<" + itemField->GetTypeName() + "," +
20 Internal::GetNormalizedInteger(static_cast<unsigned long long>(arrayLength)) + ">",
21 ROOT::ENTupleStructure::kPlain, false /* isSimple */, arrayLength),
22 fItemSize(itemField->GetValueSize()),
23 fArrayLength(arrayLength)
24{
25 fTraits |= itemField->GetTraits() & ~kTraitMappable;
26 Attach(std::move(itemField));
27}
28
29std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayField::CloneImpl(std::string_view newName) const
30{
31 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
32 return std::make_unique<RArrayField>(newName, std::move(newItemField), fArrayLength);
33}
34
35std::size_t ROOT::RArrayField::AppendImpl(const void *from)
36{
37 std::size_t nbytes = 0;
38 if (fSubfields[0]->IsSimple()) {
39 GetPrincipalColumnOf(*fSubfields[0])->AppendV(from, fArrayLength);
40 nbytes += fArrayLength * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
41 } else {
42 auto arrayPtr = static_cast<const unsigned char *>(from);
43 for (unsigned i = 0; i < fArrayLength; ++i) {
44 nbytes += CallAppendOn(*fSubfields[0], arrayPtr + (i * fItemSize));
45 }
46 }
47 return nbytes;
48}
49
51{
52 if (fSubfields[0]->IsSimple()) {
53 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, to);
54 } else {
55 auto arrayPtr = static_cast<unsigned char *>(to);
56 for (unsigned i = 0; i < fArrayLength; ++i) {
57 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
58 }
59 }
60}
61
63{
64 if (fSubfields[0]->IsSimple()) {
65 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, to);
66 } else {
67 auto arrayPtr = static_cast<unsigned char *>(to);
68 for (unsigned i = 0; i < fArrayLength; ++i) {
69 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
70 }
71 }
72}
73
75{
76 if (!fSubfields[0]->IsSimple())
78
79 GetPrincipalColumnOf(*fSubfields[0])
80 ->ReadV(bulkSpec.fFirstIndex * fArrayLength, bulkSpec.fCount * fArrayLength, bulkSpec.fValues);
81 return RBulkSpec::kAllSet;
82}
83
85{
86 static const std::vector<std::string> prefixes = {"std::array<"};
87
88 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
89 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName).ThrowOnError();
90 EnsureMatchingTypePrefix(fieldDesc, prefixes).ThrowOnError();
91}
92
94{
95 if (fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)
96 return;
97
98 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
99 for (unsigned i = 0; i < fArrayLength; ++i) {
100 CallConstructValueOn(*fSubfields[0], arrayPtr + (i * fItemSize));
101 }
102}
103
105{
106 if (fItemDeleter) {
107 for (unsigned i = 0; i < fArrayLength; ++i) {
108 fItemDeleter->operator()(reinterpret_cast<unsigned char *>(objPtr) + i * fItemSize, true /* dtorOnly */);
109 }
110 }
111 RDeleter::operator()(objPtr, dtorOnly);
112}
113
114std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayField::GetDeleter() const
115{
116 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyDestructible))
117 return std::make_unique<RArrayDeleter>(fItemSize, fArrayLength, GetDeleterOf(*fSubfields[0]));
118 return std::make_unique<RDeleter>();
119}
120
121std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayField::SplitValue(const RValue &value) const
122{
123 auto valuePtr = value.GetPtr<void>();
124 auto arrayPtr = static_cast<unsigned char *>(valuePtr.get());
125 std::vector<RValue> result;
126 result.reserve(fArrayLength);
127 for (unsigned i = 0; i < fArrayLength; ++i) {
128 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, arrayPtr + (i * fItemSize))));
129 }
130 return result;
131}
132
134{
135 visitor.VisitArrayField(*this);
136}
137
138//------------------------------------------------------------------------------
139
140namespace {
141
142/// Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the RVec object.
143/// Returns pointers to fBegin, fSize and fCapacity in a std::tuple.
144std::tuple<unsigned char **, std::int32_t *, std::int32_t *> GetRVecDataMembers(void *rvecPtr)
145{
146 unsigned char **beginPtr = reinterpret_cast<unsigned char **>(rvecPtr);
147 // int32_t fSize is the second data member (after 1 void*)
148 std::int32_t *size = reinterpret_cast<std::int32_t *>(beginPtr + 1);
149 R__ASSERT(*size >= 0);
150 // int32_t fCapacity is the third data member (1 int32_t after fSize)
151 std::int32_t *capacity = size + 1;
152 R__ASSERT(*capacity >= -1);
153 return {beginPtr, size, capacity};
154}
155
156std::tuple<const unsigned char *const *, const std::int32_t *, const std::int32_t *>
157GetRVecDataMembers(const void *rvecPtr)
158{
159 return {GetRVecDataMembers(const_cast<void *>(rvecPtr))};
160}
161
162std::size_t EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
163{
164 // the size of an RVec<T> is the size of its 4 data-members + optional padding:
165 //
166 // data members:
167 // - void *fBegin
168 // - int32_t fSize
169 // - int32_t fCapacity
170 // - the char[] inline storage, which is aligned like T
171 //
172 // padding might be present:
173 // - between fCapacity and the char[] buffer aligned like T
174 // - after the char[] buffer
175
176 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
177
178 // mimic the logic of RVecInlineStorageSize, but at runtime
179 const auto inlineStorageSz = [&] {
180 constexpr unsigned cacheLineSize = R__HARDWARE_INTERFERENCE_SIZE;
181 const unsigned elementsPerCacheLine = (cacheLineSize - dataMemberSz) / sizeOfT;
182 constexpr unsigned maxInlineByteSize = 1024;
183 const unsigned nElements =
184 elementsPerCacheLine >= 8 ? elementsPerCacheLine : (sizeOfT * 8 > maxInlineByteSize ? 0 : 8);
185 return nElements * sizeOfT;
186 }();
187
188 // compute padding between first 3 datamembers and inline buffer
189 // (there should be no padding between the first 3 data members)
191 if (paddingMiddle != 0)
193
194 // padding at the end of the object
196 if (paddingEnd != 0)
198
200}
201
202std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
203{
204 // the alignment of an RVec<T> is the largest among the alignments of its data members
205 // (including the inline buffer which has the same alignment as the RVec::value_type)
206 return std::max({alignof(void *), alignof(std::int32_t), alignOfSubfield});
207}
208
209void DestroyRVecWithChecks(std::size_t alignOfT, unsigned char **beginPtr, std::int32_t *capacityPtr)
210{
211 // figure out if we are in the small state, i.e. begin == &inlineBuffer
212 // there might be padding between fCapacity and the inline buffer, so we compute it here
213 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
215 if (paddingMiddle != 0)
217 const bool isSmall = (*beginPtr == (reinterpret_cast<unsigned char *>(beginPtr) + dataMemberSz + paddingMiddle));
218
219 const bool owns = (*capacityPtr != -1);
220 if (!isSmall && owns)
221 free(*beginPtr);
222}
223
224} // anonymous namespace
225
226ROOT::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
227 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
228 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
229 fItemSize(itemField->GetValueSize()),
230 fNWritten(0)
231{
232 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
234 Attach(std::move(itemField));
236
237 // Determine if we can optimimize bulk reading
238 if (fSubfields[0]->IsSimple()) {
239 fBulkSubfield = fSubfields[0].get();
240 } else {
241 if (auto f = dynamic_cast<RArrayField *>(fSubfields[0].get())) {
242 auto grandChildFields = fSubfields[0]->GetMutableSubfields();
243 if (grandChildFields[0]->IsSimple()) {
245 fBulkNRepetition = f->GetLength();
246 }
247 }
248 }
249}
250
251std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::CloneImpl(std::string_view newName) const
252{
253 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
254 return std::make_unique<RRVecField>(newName, std::move(newItemField));
255}
256
257std::size_t ROOT::RRVecField::AppendImpl(const void *from)
258{
259 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(from);
260
261 std::size_t nbytes = 0;
262 if (fSubfields[0]->IsSimple() && *sizePtr) {
263 GetPrincipalColumnOf(*fSubfields[0])->AppendV(*beginPtr, *sizePtr);
264 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
265 } else {
266 for (std::int32_t i = 0; i < *sizePtr; ++i) {
267 nbytes += CallAppendOn(*fSubfields[0], *beginPtr + i * fItemSize);
268 }
269 }
270
271 fNWritten += *sizePtr;
272 fPrincipalColumn->Append(&fNWritten);
273 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
274}
275
276unsigned char *ROOT::RRVecField::ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize,
278
279{
280 if (nItems > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())) {
281 throw RException(R__FAIL("RVec too large: " + std::to_string(nItems)));
282 }
283
285 const std::size_t oldSize = *sizePtr;
286
287 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
288 // on the element construction/destrution.
289 const bool owns = (*capacityPtr != -1);
290 const bool needsConstruct = !(itemField->GetTraits() & kTraitTriviallyConstructible);
291 const bool needsDestruct = owns && itemDeleter;
292
293 // Destroy excess elements, if any
294 if (needsDestruct) {
295 for (std::size_t i = nItems; i < oldSize; ++i) {
296 itemDeleter->operator()(*beginPtr + (i * itemSize), true /* dtorOnly */);
297 }
298 }
299
300 // Resize RVec (capacity and size)
301 if (std::int32_t(nItems) > *capacityPtr) { // must reallocate
302 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
303 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
304 if (needsDestruct) {
305 for (std::size_t i = 0u; i < oldSize; ++i) {
306 itemDeleter->operator()(*beginPtr + (i * itemSize), true /* dtorOnly */);
307 }
308 }
309
310 // TODO Increment capacity by a factor rather than just enough to fit the elements.
311 if (owns) {
312 // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
313 free(*beginPtr);
314 }
315 // We trust that malloc returns a buffer with large enough alignment.
316 // This might not be the case if T in RVec<T> is over-aligned.
317 *beginPtr = static_cast<unsigned char *>(malloc(nItems * itemSize));
318 R__ASSERT(*beginPtr != nullptr);
320
321 // Placement new for elements that were already there before the resize
322 if (needsConstruct) {
323 for (std::size_t i = 0u; i < oldSize; ++i)
324 CallConstructValueOn(*itemField, *beginPtr + (i * itemSize));
325 }
326 }
327 *sizePtr = nItems;
328
329 // Placement new for new elements, if any
330 if (needsConstruct) {
331 for (std::size_t i = oldSize; i < nItems; ++i)
332 CallConstructValueOn(*itemField, *beginPtr + (i * itemSize));
333 }
334
335 return *beginPtr;
336}
337
339{
340 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
341 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
342
343 // Read collection info for this entry
346 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
347
348 auto begin = ResizeRVec(to, nItems, fItemSize, fSubfields[0].get(), fItemDeleter.get());
349
350 if (fSubfields[0]->IsSimple() && nItems) {
351 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, begin);
352 return;
353 }
354
355 // Read the new values into the collection elements
356 for (std::size_t i = 0; i < nItems; ++i) {
357 CallReadOn(*fSubfields[0], collectionStart + i, begin + (i * fItemSize));
358 }
359}
360
362{
363 if (!fBulkSubfield)
365
366 if (bulkSpec.fAuxData->empty()) {
367 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
368 /// The following bytes store the item values, consecutively.
369 bulkSpec.fAuxData->resize(sizeof(std::size_t));
370 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fBulkNRepetition * fBulkSubfield->GetValueSize();
371 }
372 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
373 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
375
376 // Get size of the first RVec of the bulk
379 fPrincipalColumn->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
382 *capacityPtr = -1;
383
384 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
385 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
386 // given range. If not, we'll fix up the pointers afterwards.
387 auto lastOffset = firstItemIndex.GetIndexInCluster() + collectionSize;
389 std::size_t nValues = 1;
390 std::size_t nItems = collectionSize;
391 while (nRemainingValues > 0) {
393 const auto offsets =
394 fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
395 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
396 for (std::size_t i = 0; i < nBatch; ++i) {
397 const auto size = offsets[i] - lastOffset;
398 std::tie(beginPtr, sizePtr, capacityPtr) =
399 GetRVecDataMembers(reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
401 *sizePtr = size;
402 *capacityPtr = -1;
403
404 nItems += size;
405 lastOffset = offsets[i];
406 }
408 nValues += nBatch;
409 }
410
411 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
412 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
413 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
414 if (delta != 0) {
415 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
416 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
417 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
419 }
420 }
421
422 GetPrincipalColumnOf(*fBulkSubfield)
423 ->ReadV(firstItemIndex * fBulkNRepetition, nItems * fBulkNRepetition, itemValueArray - delta);
424 return RBulkSpec::kAllSet;
425}
426
436
441
446
448{
449 if (GetOnDiskId() == kInvalidDescriptorId)
450 return nullptr;
451
452 const auto descGuard = pageSource.GetSharedDescriptorGuard();
453 const auto &fieldDesc = descGuard->GetFieldDescriptor(GetOnDiskId());
454 if (fieldDesc.GetTypeName().rfind("std::array<", 0) == 0) {
455 auto substitute = std::make_unique<RArrayAsRVecField>(
456 GetFieldName(), fSubfields[0]->Clone(fSubfields[0]->GetFieldName()), fieldDesc.GetNRepetitions());
457 substitute->SetOnDiskId(GetOnDiskId());
458 return substitute;
459 }
460 return nullptr;
461}
462
464{
465 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName).ThrowOnError();
466}
467
469{
470 // initialize data members fBegin, fSize, fCapacity
471 // currently the inline buffer is left uninitialized
472 void **beginPtr = new (where)(void *)(nullptr);
473 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
474 new (sizePtr + 1) std::int32_t(-1);
475}
476
478{
480
481 if (fItemDeleter) {
482 for (std::int32_t i = 0; i < *sizePtr; ++i) {
483 fItemDeleter->operator()(*beginPtr + i * fItemSize, true /* dtorOnly */);
484 }
485 }
486
488 RDeleter::operator()(objPtr, dtorOnly);
489}
490
491std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RRVecField::GetDeleter() const
492{
493 if (fItemDeleter)
494 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubfields[0]));
495 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment());
496}
497
498std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue &value) const
499{
500 auto [beginPtr, sizePtr, _] = GetRVecDataMembers(value.GetPtr<void>().get());
501
502 std::vector<RValue> result;
503 result.reserve(*sizePtr);
504 for (std::int32_t i = 0; i < *sizePtr; ++i) {
505 result.emplace_back(
506 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), *beginPtr + i * fItemSize)));
507 }
508 return result;
509}
510
512{
513 return fValueSize;
514}
515
517{
518 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
519}
520
522{
523 visitor.VisitRVecField(*this);
524}
525
526//------------------------------------------------------------------------------
527
528ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
529 std::optional<std::string_view> emulatedFromType)
530 : ROOT::RFieldBase(fieldName, emulatedFromType ? *emulatedFromType : "std::vector<" + itemField->GetTypeName() + ">",
531 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
532 fItemSize(itemField->GetValueSize()),
533 fNWritten(0)
534{
535 if (emulatedFromType && !emulatedFromType->empty())
537
538 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
540 Attach(std::move(itemField));
541}
542
543ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
545{
546}
547
548std::unique_ptr<ROOT::RVectorField>
549ROOT::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
550{
551 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(fieldName, std::move(itemField), ""));
552}
553
554std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::CloneImpl(std::string_view newName) const
555{
556 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
557 auto isUntyped = GetTypeName().empty() || ((fTraits & kTraitEmulatedField) != 0);
558 auto emulatedFromType = isUntyped ? std::make_optional(GetTypeName()) : std::nullopt;
559 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(newName, std::move(newItemField), emulatedFromType));
560}
561
562std::size_t ROOT::RVectorField::AppendImpl(const void *from)
563{
564 auto typedValue = static_cast<const std::vector<char> *>(from);
565 // The order is important here: Profiling showed that the integer division is on the critical path. By moving the
566 // computation of count before R__ASSERT, the compiler can use the result of a single instruction (on x86) also for
567 // the modulo operation. Otherwise, it must perform the division twice because R__ASSERT expands to an external call
568 // of Fatal() in case of failure, which could have side effects that the compiler cannot analyze.
569 auto count = typedValue->size() / fItemSize;
570 R__ASSERT((typedValue->size() % fItemSize) == 0);
571 std::size_t nbytes = 0;
572
573 if (fSubfields[0]->IsSimple() && count) {
574 GetPrincipalColumnOf(*fSubfields[0])->AppendV(typedValue->data(), count);
575 nbytes += count * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
576 } else {
577 for (unsigned i = 0; i < count; ++i) {
578 nbytes += CallAppendOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
579 }
580 }
581
582 fNWritten += count;
583 fPrincipalColumn->Append(&fNWritten);
584 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
585}
586
588{
589 auto typedValue = static_cast<std::vector<char> *>(to);
590
593 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
594
595 if (fSubfields[0]->IsSimple()) {
596 typedValue->resize(nItems * fItemSize);
597 if (nItems)
598 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, typedValue->data());
599 return;
600 }
601
602 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md
603 R__ASSERT(fItemSize > 0);
604 const auto oldNItems = typedValue->size() / fItemSize;
605 const bool canRealloc = oldNItems < nItems;
606 bool allDeallocated = false;
607 if (fItemDeleter) {
609 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
610 fItemDeleter->operator()(typedValue->data() + (i * fItemSize), true /* dtorOnly */);
611 }
612 }
613 typedValue->resize(nItems * fItemSize);
614 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)) {
615 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
616 CallConstructValueOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
617 }
618 }
619
620 for (std::size_t i = 0; i < nItems; ++i) {
621 CallReadOn(*fSubfields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
622 }
623}
624
634
639
644
646{
647 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName).ThrowOnError();
648}
649
651{
652 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
653 if (fItemDeleter) {
654 R__ASSERT(fItemSize > 0);
655 R__ASSERT((vecPtr->size() % fItemSize) == 0);
656 auto nItems = vecPtr->size() / fItemSize;
657 for (std::size_t i = 0; i < nItems; ++i) {
658 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
659 }
660 }
661 std::destroy_at(vecPtr);
662 RDeleter::operator()(objPtr, dtorOnly);
663}
664
665std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVectorField::GetDeleter() const
666{
667 if (fItemDeleter)
668 return std::make_unique<RVectorDeleter>(fItemSize, GetDeleterOf(*fSubfields[0]));
669 return std::make_unique<RVectorDeleter>();
670}
671
672std::vector<ROOT::RFieldBase::RValue> ROOT::RVectorField::SplitValue(const RValue &value) const
673{
674 auto valuePtr = value.GetPtr<void>();
675 auto *vec = static_cast<std::vector<char> *>(valuePtr.get());
676 R__ASSERT(fItemSize > 0);
677 R__ASSERT((vec->size() % fItemSize) == 0);
678 auto nItems = vec->size() / fItemSize;
679 std::vector<RValue> result;
680 result.reserve(nItems);
681 for (unsigned i = 0; i < nItems; ++i) {
682 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, vec->data() + (i * fItemSize))));
683 }
684 return result;
685}
686
688{
689 visitor.VisitVectorField(*this);
690}
691
692//------------------------------------------------------------------------------
693
694ROOT::RField<std::vector<bool>>::RField(std::string_view name)
695 : ROOT::RFieldBase(name, "std::vector<bool>", ROOT::ENTupleStructure::kCollection, false /* isSimple */)
696{
697 Attach(std::make_unique<RField<bool>>("_0"));
698}
699
700std::size_t ROOT::RField<std::vector<bool>>::AppendImpl(const void *from)
701{
702 auto typedValue = static_cast<const std::vector<bool> *>(from);
703 auto count = typedValue->size();
704 for (unsigned i = 0; i < count; ++i) {
705 bool bval = (*typedValue)[i];
706 CallAppendOn(*fSubfields[0], &bval);
707 }
708 fNWritten += count;
709 fPrincipalColumn->Append(&fNWritten);
710 return count + fPrincipalColumn->GetElement()->GetPackedSize();
711}
712
714{
715 auto typedValue = static_cast<std::vector<bool> *>(to);
716
718 RNTupleLocalIndex collectionStart;
719 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
720
721 typedValue->resize(nItems);
722 for (unsigned i = 0; i < nItems; ++i) {
723 bool bval;
724 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
725 (*typedValue)[i] = bval;
726 }
727}
728
729const ROOT::RFieldBase::RColumnRepresentations &ROOT::RField<std::vector<bool>>::GetColumnRepresentations() const
730{
731 static RColumnRepresentations representations({{ENTupleColumnType::kSplitIndex64},
735 {});
736 return representations;
737}
738
739void ROOT::RField<std::vector<bool>>::GenerateColumns()
740{
742}
743
744void ROOT::RField<std::vector<bool>>::GenerateColumns(const ROOT::RNTupleDescriptor &desc)
745{
747}
748
749void ROOT::RField<std::vector<bool>>::ReconcileOnDiskField(const RNTupleDescriptor &desc)
750{
751 EnsureMatchingOnDiskField(desc.GetFieldDescriptor(GetOnDiskId()), kDiffTypeName).ThrowOnError();
752}
753
754std::vector<ROOT::RFieldBase::RValue> ROOT::RField<std::vector<bool>>::SplitValue(const RValue &value) const
755{
756 const auto &typedValue = value.GetRef<std::vector<bool>>();
757 auto count = typedValue.size();
758 std::vector<RValue> result;
759 result.reserve(count);
760 for (unsigned i = 0; i < count; ++i) {
761 if (typedValue[i])
762 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
763 else
764 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
765 }
766 return result;
767}
768
770{
771 visitor.VisitVectorBoolField(*this);
772}
773
774//------------------------------------------------------------------------------
775
776ROOT::RArrayAsRVecField::RArrayAsRVecField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
777 std::size_t arrayLength)
778 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
779 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
780 fItemSize(itemField->GetValueSize()),
781 fArrayLength(arrayLength)
782{
783 Attach(std::move(itemField));
787}
788
789std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsRVecField::CloneImpl(std::string_view newName) const
790{
791 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
792 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
793}
794
796{
797 // initialize data members fBegin, fSize, fCapacity
798 // currently the inline buffer is left uninitialized
799 void **beginPtr = new (where)(void *)(nullptr);
800 std::int32_t *sizePtr = new (static_cast<void *>(beginPtr + 1)) std::int32_t(0);
801 new (sizePtr + 1) std::int32_t(-1);
802}
803
804std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter() const
805{
806 if (fItemDeleter) {
807 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize,
808 GetDeleterOf(*fSubfields[0]));
809 }
810 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment());
811}
812
814{
815 auto begin = RRVecField::ResizeRVec(to, fArrayLength, fItemSize, fSubfields[0].get(), fItemDeleter.get());
816
817 if (fSubfields[0]->IsSimple()) {
818 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, begin);
819 return;
820 }
821
822 // Read the new values into the collection elements
823 for (std::size_t i = 0; i < fArrayLength; ++i) {
824 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, begin + (i * fItemSize));
825 }
826}
827
829{
830 auto begin = RRVecField::ResizeRVec(to, fArrayLength, fItemSize, fSubfields[0].get(), fItemDeleter.get());
831
832 if (fSubfields[0]->IsSimple()) {
833 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, begin);
834 return;
835 }
836
837 // Read the new values into the collection elements
838 for (std::size_t i = 0; i < fArrayLength; ++i) {
839 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, begin + (i * fItemSize));
840 }
841}
842
844{
845 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
846 EnsureMatchingOnDiskField(fieldDesc, kDiffTypeName | kDiffTypeVersion | kDiffStructure | kDiffNRepetitions)
847 .ThrowOnError();
848 if (fieldDesc.GetTypeName().rfind("std::array<", 0) != 0) {
849 throw RException(R__FAIL("RArrayAsRVecField " + GetQualifiedFieldName() + " expects an on-disk array field"));
850 }
851}
852
854{
855 return EvalRVecAlignment(fSubfields[0]->GetAlignment());
856}
857
858std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsRVecField::SplitValue(const ROOT::RFieldBase::RValue &value) const
859{
860 auto arrayPtr = value.GetPtr<unsigned char>().get();
861 std::vector<ROOT::RFieldBase::RValue> result;
862 result.reserve(fArrayLength);
863 for (unsigned i = 0; i < fArrayLength; ++i) {
864 result.emplace_back(
865 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
866 }
867 return result;
868}
869
871{
872 visitor.VisitArrayAsRVecField(*this);
873}
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
#define f(i)
Definition RSha256.hxx:104
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.
Abstract interface to read data from an ntuple.
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
Template specializations for C++ std::array and C-style arrays.
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.
std::size_t ReadBulkImpl(const RBulkSpec &bulkSpec) final
General implementation of bulk read.
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.
A functor to release the memory acquired by CreateValue() (memory and constructor).
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.
bool IsSimple() const
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:313
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.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
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.
static unsigned char * ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize, const RFieldBase *itemField, RDeleter *itemDeleter)
std::unique_ptr< RDeleter > fItemDeleter
const RColumnRepresentations & GetColumnRepresentations() const final
Implementations in derived classes should return a static RColumnRepresentations object.
RFieldBase * fBulkSubfield
May be a direct PoD subfield or a sub-subfield of a fixed-size array of PoD.
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.
constexpr DescriptorId_t kInvalidDescriptorId
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().