Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldSequenceContainer.cxx
Go to the documentation of this file.
1/// \file RFieldSequenceContainer.cxx
2/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
3/// \date 2024-11-19
4
5#include <ROOT/BitUtils.hxx>
6#include <ROOT/RField.hxx>
7#include <ROOT/RFieldBase.hxx>
10
11#include <limits>
12#include <memory>
13#include <new> // hardware_destructive_interference_size, operator new
14
15namespace {
16
17std::vector<ROOT::RFieldBase::RValue> SplitVector(const std::shared_ptr<void> &valuePtr, ROOT::RFieldBase &itemField)
18{
19 auto *vec = static_cast<std::vector<char> *>(valuePtr.get());
20 const auto itemSize = itemField.GetValueSize();
21 R__ASSERT(itemSize > 0);
22 R__ASSERT((vec->size() % itemSize) == 0);
23 const auto nItems = vec->size() / itemSize;
24 std::vector<ROOT::RFieldBase::RValue> result;
25 result.reserve(nItems);
26 for (unsigned i = 0; i < nItems; ++i) {
27 result.emplace_back(itemField.BindValue(std::shared_ptr<void>(valuePtr, vec->data() + (i * itemSize))));
28 }
29 return result;
30}
31
32std::size_t GetSizeOfVector()
33{
34 return sizeof(std::vector<char>);
35}
36
37std::size_t GetAlignOfVector()
38{
39 return alignof(std::vector<char>);
40}
41
42void ConstructVector(void *where, std::size_t alignOfValue)
43{
44 static_assert(ROOT::RVectorField::kMaxItemAlignment == 4096);
45 // clang-format off
46 switch (alignOfValue) {
47 case 1: new (where) std::vector<ROOT::Internal::RAlignedStorage< 1>>(); break;
48 case 2: new (where) std::vector<ROOT::Internal::RAlignedStorage< 2>>(); break;
49 case 4: new (where) std::vector<ROOT::Internal::RAlignedStorage< 4>>(); break;
50 case 8: new (where) std::vector<ROOT::Internal::RAlignedStorage< 8>>(); break;
51 case 16: new (where) std::vector<ROOT::Internal::RAlignedStorage< 16>>(); break;
52 case 32: new (where) std::vector<ROOT::Internal::RAlignedStorage< 32>>(); break;
53 case 64: new (where) std::vector<ROOT::Internal::RAlignedStorage< 64>>(); break;
54 case 128: new (where) std::vector<ROOT::Internal::RAlignedStorage< 128>>(); break;
55 case 256: new (where) std::vector<ROOT::Internal::RAlignedStorage< 256>>(); break;
56 case 512: new (where) std::vector<ROOT::Internal::RAlignedStorage< 512>>(); break;
57 case 1024: new (where) std::vector<ROOT::Internal::RAlignedStorage<1024>>(); break;
58 case 2048: new (where) std::vector<ROOT::Internal::RAlignedStorage<2048>>(); break;
59 case 4096: new (where) std::vector<ROOT::Internal::RAlignedStorage<4096>>(); break;
60 default: throw ROOT::RException(R__FAIL(std::string("Unsupported alignment: ") + std::to_string(alignOfValue)));
61 }
62 // clang-format on
63}
64
65} // anonymous namespace
66
67ROOT::RArrayField::RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
68 std::size_t arrayLength)
69 : ROOT::RFieldBase(fieldName,
70 "std::array<" + itemField->GetTypeName() + "," +
71 Internal::GetNormalizedInteger(static_cast<unsigned long long>(arrayLength)) + ">",
72 ROOT::ENTupleStructure::kPlain, false /* isSimple */, arrayLength),
73 fItemSize(itemField->GetValueSize()),
74 fArrayLength(arrayLength)
75{
76 fTraits |= itemField->GetTraits() & ~kTraitMappable;
77 if (!itemField->GetTypeAlias().empty()) {
78 fTypeAlias = "std::array<" + itemField->GetTypeAlias() + "," +
79 Internal::GetNormalizedInteger(static_cast<unsigned long long>(arrayLength)) + ">";
80 }
81 Attach(std::move(itemField), "_0");
82}
83
84std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayField::CloneImpl(std::string_view newName) const
85{
86 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
87 return std::make_unique<RArrayField>(newName, std::move(newItemField), fArrayLength);
88}
89
90std::size_t ROOT::RArrayField::AppendImpl(const void *from)
91{
92 std::size_t nbytes = 0;
93 if (fSubfields[0]->IsSimple()) {
94 GetPrincipalColumnOf(*fSubfields[0])->AppendV(from, fArrayLength);
95 nbytes += fArrayLength * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
96 } else {
97 auto arrayPtr = static_cast<const unsigned char *>(from);
98 for (unsigned i = 0; i < fArrayLength; ++i) {
99 nbytes += CallAppendOn(*fSubfields[0], arrayPtr + (i * fItemSize));
100 }
101 }
102 return nbytes;
103}
104
106{
107 if (fSubfields[0]->IsSimple()) {
108 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, to);
109 } else {
110 auto arrayPtr = static_cast<unsigned char *>(to);
111 for (unsigned i = 0; i < fArrayLength; ++i) {
112 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
113 }
114 }
115}
116
118{
119 if (fSubfields[0]->IsSimple()) {
120 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, to);
121 } else {
122 auto arrayPtr = static_cast<unsigned char *>(to);
123 for (unsigned i = 0; i < fArrayLength; ++i) {
124 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, arrayPtr + (i * fItemSize));
125 }
126 }
127}
128
129std::size_t ROOT::RArrayField::ReadBulkImpl(const RBulkSpec &bulkSpec)
130{
131 if (!fSubfields[0]->IsSimple())
132 return RFieldBase::ReadBulkImpl(bulkSpec);
133
134 GetPrincipalColumnOf(*fSubfields[0])
135 ->ReadV(bulkSpec.fFirstIndex * fArrayLength, bulkSpec.fCount * fArrayLength, bulkSpec.fValues);
136 return RBulkSpec::kAllSet;
137}
138
140{
141 static const std::vector<std::string> prefixes = {"std::array<"};
142
143 EnsureMatchingOnDiskField(desc, kDiffTypeName).ThrowOnError();
144 EnsureMatchingTypePrefix(desc, prefixes).ThrowOnError();
145}
146
148{
149 if (fSubfields[0]->GetTraits() & kTraitTriviallyConstructible)
150 return;
151
152 auto arrayPtr = reinterpret_cast<unsigned char *>(where);
153 for (unsigned i = 0; i < fArrayLength; ++i) {
154 CallConstructValueOn(*fSubfields[0], arrayPtr + (i * fItemSize));
155 }
156}
157
158void ROOT::RArrayField::RArrayDeleter::operator()(void *objPtr, bool dtorOnly)
159{
160 if (fItemDeleter) {
161 for (unsigned i = 0; i < fArrayLength; ++i) {
162 fItemDeleter->operator()(reinterpret_cast<unsigned char *>(objPtr) + i * fItemSize, true /* dtorOnly */);
163 }
164 }
165 RDeleter::operator()(objPtr, dtorOnly);
166}
167
168std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayField::GetDeleter() const
169{
170 if (!(fSubfields[0]->GetTraits() & kTraitTriviallyDestructible))
171 return std::make_unique<RArrayDeleter>(fItemSize, fArrayLength, GetAlignment(), GetDeleterOf(*fSubfields[0]));
172 return std::make_unique<RDeleter>(GetAlignment());
173}
174
175std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayField::SplitValue(const RValue &value) const
176{
177 auto valuePtr = value.GetPtr<void>();
178 auto arrayPtr = static_cast<unsigned char *>(valuePtr.get());
179 std::vector<RValue> result;
180 result.reserve(fArrayLength);
181 for (unsigned i = 0; i < fArrayLength; ++i) {
182 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<void>(valuePtr, arrayPtr + (i * fItemSize))));
183 }
184 return result;
185}
186
188{
189 visitor.VisitArrayField(*this);
190}
191
192//------------------------------------------------------------------------------
193
194ROOT::RRVecField::RRVecField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
195 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
196 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
197 fItemSize(itemField->GetValueSize()),
198 fNWritten(0)
199{
200 if (itemField->GetAlignment() > sizeof(std::max_align_t)) {
201 // RVec uses operator new without alignment info
202 throw RException(R__FAIL("RVec does not support over-aligned types"));
203 }
204
205 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
206 fItemDeleter = GetDeleterOf(*itemField);
207 if (!itemField->GetTypeAlias().empty())
208 fTypeAlias = "ROOT::VecOps::RVec<" + itemField->GetTypeAlias() + ">";
209 Attach(std::move(itemField), "_0");
210 fValueSize =
212
213 // Determine if we can optimimize bulk reading
214 if (fSubfields[0]->IsSimple()) {
215 fBulkSubfield = fSubfields[0].get();
216 } else {
217 if (auto f = dynamic_cast<RArrayField *>(fSubfields[0].get())) {
218 auto grandChildFields = fSubfields[0]->GetMutableSubfields();
219 if (grandChildFields[0]->IsSimple()) {
220 fBulkSubfield = grandChildFields[0];
221 fBulkNRepetition = f->GetLength();
222 }
223 }
224 }
225}
226
227std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::CloneImpl(std::string_view newName) const
228{
229 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
230 return std::make_unique<RRVecField>(newName, std::move(newItemField));
231}
232
233std::size_t ROOT::RRVecField::AppendImpl(const void *from)
234{
235 auto [beginPtr, sizePtr, _] = Internal::GetRVecDataMembers(from);
236
237 std::size_t nbytes = 0;
238 if (fSubfields[0]->IsSimple() && *sizePtr) {
239 GetPrincipalColumnOf(*fSubfields[0])->AppendV(*beginPtr, *sizePtr);
240 nbytes += *sizePtr * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
241 } else {
242 for (std::int32_t i = 0; i < *sizePtr; ++i) {
243 nbytes += CallAppendOn(*fSubfields[0], *beginPtr + i * fItemSize);
244 }
245 }
246
247 fNWritten += *sizePtr;
248 fPrincipalColumn->Append(&fNWritten);
249 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
250}
251
252unsigned char *ROOT::RRVecField::ResizeRVec(void *rvec, std::size_t nItems, std::size_t itemSize,
253 const RFieldBase *itemField, RDeleter *itemDeleter)
254
255{
256 if (nItems > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())) {
257 throw RException(R__FAIL("RVec too large: " + std::to_string(nItems)));
258 }
259
260 auto [beginPtr, sizePtr, capacityPtr] = Internal::GetRVecDataMembers(rvec);
261 const std::size_t oldSize = *sizePtr;
262
263 if (oldSize == nItems) {
264 // If neither shrink nor grow is necessary, do nothing.
265 // Note that this case preserves a memory adopting RVec as such. All real resizes in either direction
266 // transform a memory adopting RVec into an owning RVec.
267 return *beginPtr;
268 }
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 = !(itemField->GetTraits() & kTraitTriviallyConstructible);
274 const bool needsDestruct = owns && itemDeleter;
275
276 // Destroy excess elements, if any
277 if (needsDestruct) {
278 for (std::size_t i = nItems; i < oldSize; ++i) {
279 itemDeleter->operator()(*beginPtr + (i * itemSize), true /* dtorOnly */);
280 }
281 }
282
283 // Resize RVec (capacity and size)
284 // In the regime between 0 and 16 items, grow by doubling the size. Grow larger vectors by a factor 1.5 at most.
285 // If the new size is larger than the grown capacity, set the new capacity to the new size (i.e., don't allocate
286 // a larger capacity for vectors the are suddenly vastly larger than their previous state).
287 // TODO(jblomer): investigate a longer look back window to avoid shrinking for an unusual small entry and consider
288 // returning to average or median size (see TBasket::WriteReset()).
289 static constexpr std::int32_t kSmallThreshold = 16;
290 if ((std::int32_t(nItems) > *capacityPtr) || /* must grow */
291 ((std::int32_t(nItems) > kSmallThreshold) && (std::int32_t(nItems) < *capacityPtr / 4))) /* must shrink */ {
292 // Destroy old elements: useless work for trivial types, but in case the element type's constructor
293 // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
294 if (needsDestruct) {
295 // Excess elements were destructed earlier.
296 for (std::size_t i = 0u; i < std::min(oldSize, nItems); ++i) {
297 itemDeleter->operator()(*beginPtr + (i * itemSize), true /* dtorOnly */);
298 }
299 }
300
301 // Increment / decrement capacity geometrically
302 std::size_t newCapacity = nItems;
303 if (*capacityPtr > 0) {
304 constexpr std::size_t maxSize = static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max());
305 const std::size_t oldCap = static_cast<std::size_t>(*capacityPtr);
306 if (nItems > oldCap) {
307 const std::size_t grown = oldCap + ((oldCap < kSmallThreshold) ? oldCap : (oldCap / 2));
308 newCapacity = std::min(maxSize, std::max(nItems, grown));
309 } else {
310 newCapacity = oldCap / 2;
311 assert(nItems <= newCapacity);
312 }
313 }
314
315 Internal::DestroyRVecWithChecks(itemField->GetAlignment(), beginPtr, capacityPtr);
316 // We checked that the item type is not over-aligned
317 *beginPtr = static_cast<unsigned char *>(::operator new(newCapacity * itemSize, std::nothrow));
318 R__ASSERT(*beginPtr != nullptr);
319 *capacityPtr = static_cast<std::int32_t>(newCapacity);
320
321 // Placement new for elements that were already there before the resize. When shrank, we only need to construct
322 // nItems items, because that's what we going to read. When grown, the remaining elements from oldSize until
323 // nItems is constructed a few lines down (we need to do this always when the size grows, even when not changing
324 // the capacity).
325 if (needsConstruct) {
326 for (std::size_t i = 0u; i < std::min(oldSize, nItems); ++i)
327 CallConstructValueOn(*itemField, *beginPtr + (i * itemSize));
328 }
329 }
330 *sizePtr = nItems;
331
332 // Placement new for new elements, if any
333 if (needsConstruct) {
334 for (std::size_t i = oldSize; i < nItems; ++i)
335 CallConstructValueOn(*itemField, *beginPtr + (i * itemSize));
336 }
337
338 return *beginPtr;
339}
340
342{
343 // TODO as a performance optimization, we could assign values to elements of the inline buffer:
344 // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
345
346 // Read collection info for this entry
347 ROOT::NTupleSize_t nItems;
348 RNTupleLocalIndex collectionStart;
349 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
350
351 auto begin = ResizeRVec(to, nItems, fItemSize, fSubfields[0].get(), fItemDeleter.get());
352
353 if (fSubfields[0]->IsSimple() && nItems) {
354 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, begin);
355 return;
356 }
357
358 // Read the new values into the collection elements
359 for (std::size_t i = 0; i < nItems; ++i) {
360 CallReadOn(*fSubfields[0], collectionStart + i, begin + (i * fItemSize));
361 }
362}
363
364std::size_t ROOT::RRVecField::ReadBulkImpl(const RBulkSpec &bulkSpec)
365{
366 if (!fBulkSubfield)
367 return RFieldBase::ReadBulkImpl(bulkSpec);
368
369 if (bulkSpec.fAuxData->empty()) {
370 /// Initialize auxiliary memory: the first sizeof(size_t) bytes store the value size of the item field.
371 /// The following bytes store the item values, consecutively.
372 bulkSpec.fAuxData->resize(sizeof(std::size_t));
373 *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data()) = fBulkNRepetition * fBulkSubfield->GetValueSize();
374 }
375 const auto itemValueSize = *reinterpret_cast<std::size_t *>(bulkSpec.fAuxData->data());
376 unsigned char *itemValueArray = bulkSpec.fAuxData->data() + sizeof(std::size_t);
377 auto [beginPtr, sizePtr, capacityPtr] = Internal::GetRVecDataMembers(bulkSpec.fValues);
378
379 // Get size of the first RVec of the bulk
380 RNTupleLocalIndex firstItemIndex;
381 ROOT::NTupleSize_t collectionSize;
382 fPrincipalColumn->GetCollectionInfo(bulkSpec.fFirstIndex, &firstItemIndex, &collectionSize);
383 *beginPtr = itemValueArray;
384 *sizePtr = collectionSize;
385 *capacityPtr = -1;
386
387 // Set the size of the remaining RVecs of the bulk, going page by page through the RNTuple offset column.
388 // We optimistically assume that bulkSpec.fAuxData is already large enough to hold all the item values in the
389 // given range. If not, we'll fix up the pointers afterwards.
390 auto lastOffset = firstItemIndex.GetIndexInCluster() + collectionSize;
391 ROOT::NTupleSize_t nRemainingValues = bulkSpec.fCount - 1;
392 std::size_t nValues = 1;
393 std::size_t nItems = collectionSize;
394 while (nRemainingValues > 0) {
395 ROOT::NTupleSize_t nElementsUntilPageEnd;
396 const auto offsets =
397 fPrincipalColumn->MapV<ROOT::Internal::RColumnIndex>(bulkSpec.fFirstIndex + nValues, nElementsUntilPageEnd);
398 const std::size_t nBatch = std::min(nRemainingValues, nElementsUntilPageEnd);
399 for (std::size_t i = 0; i < nBatch; ++i) {
400 const auto size = offsets[i] - lastOffset;
401 std::tie(beginPtr, sizePtr, capacityPtr) = Internal::GetRVecDataMembers(
402 reinterpret_cast<unsigned char *>(bulkSpec.fValues) + (nValues + i) * fValueSize);
403 *beginPtr = itemValueArray + nItems * itemValueSize;
404 *sizePtr = size;
405 *capacityPtr = -1;
406
407 nItems += size;
408 lastOffset = offsets[i];
409 }
410 nRemainingValues -= nBatch;
411 nValues += nBatch;
412 }
413
414 bulkSpec.fAuxData->resize(sizeof(std::size_t) + nItems * itemValueSize);
415 // If the vector got reallocated, we need to fix-up the RVecs begin pointers.
416 const auto delta = itemValueArray - (bulkSpec.fAuxData->data() + sizeof(std::size_t));
417 if (delta != 0) {
418 auto beginPtrAsUChar = reinterpret_cast<unsigned char *>(bulkSpec.fValues);
419 for (std::size_t i = 0; i < bulkSpec.fCount; ++i) {
420 *reinterpret_cast<unsigned char **>(beginPtrAsUChar) -= delta;
421 beginPtrAsUChar += fValueSize;
422 }
423 }
424
425 GetPrincipalColumnOf(*fBulkSubfield)
426 ->ReadV(firstItemIndex * fBulkNRepetition, nItems * fBulkNRepetition, itemValueArray - delta);
427 return RBulkSpec::kAllSet;
428}
429
439
441{
442 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
443}
444
446{
447 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
448}
449
450std::unique_ptr<ROOT::RFieldBase> ROOT::RRVecField::BeforeConnectPageSource(Internal::RPageSource &pageSource)
451{
452 if (GetOnDiskId() == kInvalidDescriptorId)
453 return nullptr;
454
455 auto descGuard = pageSource.GetSharedDescriptorGuard();
456 const auto &fieldDesc = descGuard->GetFieldDescriptor(GetOnDiskId());
457 if (fieldDesc.GetTypeName().rfind("std::array<", 0) == 0) {
458 auto substitute = std::make_unique<RArrayAsRVecField>(
459 GetFieldName(), fSubfields[0]->Clone(fSubfields[0]->GetFieldName()), fieldDesc.GetNRepetitions());
460 substitute->SetOnDiskId(GetOnDiskId());
461 return substitute;
462 }
463 return nullptr;
464}
465
467{
468 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
469}
470
471void ROOT::RRVecField::ConstructValue(void *where) const
472{
473 // initialize data members fBegin, fSize, fCapacity
474 // currently the inline buffer is left uninitialized
475 void **beginPtr = new (where)(void *)(nullptr);
476 std::int32_t *sizePtr = new (reinterpret_cast<void *>(beginPtr + 1)) std::int32_t(0);
477 new (sizePtr + 1) std::int32_t(-1);
478}
479
480void ROOT::RRVecField::RRVecDeleter::operator()(void *objPtr, bool dtorOnly)
481{
482 auto [beginPtr, sizePtr, capacityPtr] = Internal::GetRVecDataMembers(objPtr);
483
484 if (fItemDeleter) {
485 for (std::int32_t i = 0; i < *sizePtr; ++i) {
486 fItemDeleter->operator()(*beginPtr + i * fItemSize, true /* dtorOnly */);
487 }
488 }
489
490 Internal::DestroyRVecWithChecks(fItemAlignment, beginPtr, capacityPtr);
491 RDeleter::operator()(objPtr, dtorOnly);
492}
493
494std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RRVecField::GetDeleter() const
495{
496 if (fItemDeleter)
497 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize, GetDeleterOf(*fSubfields[0]));
498 return std::make_unique<RRVecDeleter>(fSubfields[0]->GetAlignment());
499}
500
501std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue &value) const
502{
503 auto [beginPtr, sizePtr, _] = Internal::GetRVecDataMembers(value.GetPtr<void>().get());
504
505 std::vector<RValue> result;
506 result.reserve(*sizePtr);
507 for (std::int32_t i = 0; i < *sizePtr; ++i) {
508 result.emplace_back(
509 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), *beginPtr + i * fItemSize)));
510 }
511 return result;
512}
513
515{
516 return fValueSize;
517}
518
520{
521 return Internal::EvalRVecAlignment(fSubfields[0]->GetAlignment());
522}
523
525{
526 visitor.VisitRVecField(*this);
527}
528
529//------------------------------------------------------------------------------
530
531ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
532 std::optional<std::string_view> emulatedFromType)
533 : ROOT::RFieldBase(fieldName, emulatedFromType ? *emulatedFromType : "std::vector<" + itemField->GetTypeName() + ">",
534 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
535 fItemSize(itemField->GetValueSize()),
536 fNWritten(0)
537{
538 if (itemField->GetAlignment() > kMaxItemAlignment) {
539 throw RException(
540 R__FAIL(std::string("Unsupported vector item alignment: ") + std::to_string(itemField->GetAlignment())));
541 }
542
543 if (emulatedFromType && !emulatedFromType->empty())
545
546 if (!itemField->GetTypeAlias().empty())
547 fTypeAlias = "std::vector<" + itemField->GetTypeAlias() + ">";
548
549 if (!(itemField->GetTraits() & kTraitTriviallyDestructible))
550 fItemDeleter = GetDeleterOf(*itemField);
551 Attach(std::move(itemField), "_0");
552}
553
554ROOT::RVectorField::RVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
555 : RVectorField(fieldName, std::move(itemField), {})
556{
557}
558
559std::unique_ptr<ROOT::RVectorField>
560ROOT::RVectorField::CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField)
561{
562 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(fieldName, itemField->Clone("_0"), ""));
563}
564
565std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::CloneImpl(std::string_view newName) const
566{
567 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
568 auto isUntyped = GetTypeName().empty() || ((fTraits & kTraitEmulatedField) != 0);
569 auto emulatedFromType = isUntyped ? std::make_optional(GetTypeName()) : std::nullopt;
570 return std::unique_ptr<ROOT::RVectorField>(new RVectorField(newName, std::move(newItemField), emulatedFromType));
571}
572
573std::size_t ROOT::RVectorField::AppendImpl(const void *from)
574{
575 auto typedValue = static_cast<const std::vector<char> *>(from);
576 // The order is important here: Profiling showed that the integer division is on the critical path. By moving the
577 // computation of count before R__ASSERT, the compiler can use the result of a single instruction (on x86) also for
578 // the modulo operation. Otherwise, it must perform the division twice because R__ASSERT expands to an external call
579 // of Fatal() in case of failure, which could have side effects that the compiler cannot analyze.
580 auto count = typedValue->size() / fItemSize;
581 R__ASSERT((typedValue->size() % fItemSize) == 0);
582 std::size_t nbytes = 0;
583
584 if (fSubfields[0]->IsSimple() && count) {
585 GetPrincipalColumnOf(*fSubfields[0])->AppendV(typedValue->data(), count);
586 nbytes += count * GetPrincipalColumnOf(*fSubfields[0])->GetElement()->GetPackedSize();
587 } else {
588 for (unsigned i = 0; i < count; ++i) {
589 nbytes += CallAppendOn(*fSubfields[0], typedValue->data() + (i * fItemSize));
590 }
591 }
592
593 fNWritten += count;
594 fPrincipalColumn->Append(&fNWritten);
595 return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
596}
597
598void ROOT::RVectorField::ResizeVector(void *vec, std::size_t nItems, std::size_t itemSize, const RFieldBase &itemField,
599 RDeleter *itemDeleter)
600{
601 auto typedValue = static_cast<std::vector<char> *>(vec);
602
603 // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md
604 assert(itemSize > 0);
605 const auto oldNItems = typedValue->size() / itemSize;
606 const auto availNItems = typedValue->capacity() / itemSize;
607 assert((typedValue->size() % itemSize == 0) && (typedValue->capacity() % itemSize == 0));
608
609 const bool canRealloc = availNItems < nItems;
610 bool allDeallocated = false;
611 if (itemDeleter) {
612 allDeallocated = canRealloc;
613 for (std::size_t i = allDeallocated ? 0 : nItems; i < oldNItems; ++i) {
614 itemDeleter->operator()(typedValue->data() + (i * itemSize), true /* dtorOnly */);
615 }
616 }
617
618 // Resize the vector with correct alignment
619 const auto itemAlignment = itemField.GetAlignment();
620 const auto nbytes = nItems * itemSize;
621
622 auto fnAlignedResize = [](auto &vecOfAlignedStorage, std::size_t targetSize) {
623 constexpr auto valueTypeSize = sizeof(typename std::decay_t<decltype(vecOfAlignedStorage)>::value_type);
624 constexpr auto valueTypeAlignment = alignof(typename std::decay_t<decltype(vecOfAlignedStorage)>::value_type);
625 // Ensure that this function is used with RAlignedStorage as a template argument.
626 // In general, the actual (user-defined) item type may have larger size than alignment.
627 static_assert(valueTypeSize == valueTypeAlignment);
628 assert(targetSize % valueTypeAlignment == 0);
629 vecOfAlignedStorage.resize(targetSize / valueTypeSize);
630 };
631
632 static_assert(kMaxItemAlignment == 4096);
633 // clang-format off
634 switch (itemAlignment) {
635 case 1: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 1>> *>(vec), nbytes); break;
636 case 2: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 2>> *>(vec), nbytes); break;
637 case 4: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 4>> *>(vec), nbytes); break;
638 case 8: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 8>> *>(vec), nbytes); break;
639 case 16: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 16>> *>(vec), nbytes); break;
640 case 32: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 32>> *>(vec), nbytes); break;
641 case 64: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 64>> *>(vec), nbytes); break;
642 case 128: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 128>> *>(vec), nbytes); break;
643 case 256: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 256>> *>(vec), nbytes); break;
644 case 512: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage< 512>> *>(vec), nbytes); break;
645 case 1024: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage<1024>> *>(vec), nbytes); break;
646 case 2048: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage<2048>> *>(vec), nbytes); break;
647 case 4096: fnAlignedResize(*static_cast<std::vector<Internal::RAlignedStorage<4096>> *>(vec), nbytes); break;
648 default: throw RException(R__FAIL(std::string("Unsupported alignment: ") + std::to_string(itemAlignment)));
649 }
650 // clang-format on
651
652 if (!(itemField.GetTraits() & kTraitTriviallyConstructible)) {
653 for (std::size_t i = allDeallocated ? 0 : oldNItems; i < nItems; ++i) {
654 CallConstructValueOn(itemField, typedValue->data() + (i * itemSize));
655 }
656 }
657}
658
660{
661 auto typedValue = static_cast<std::vector<char> *>(to);
662
663 ROOT::NTupleSize_t nItems;
664 RNTupleLocalIndex collectionStart;
665 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
666
667 if (fSubfields[0]->IsSimple()) {
668 typedValue->resize(nItems * fItemSize);
669 if (nItems)
670 GetPrincipalColumnOf(*fSubfields[0])->ReadV(collectionStart, nItems, typedValue->data());
671 return;
672 }
673
674 ResizeVector(to, nItems, fItemSize, *fSubfields[0], fItemDeleter.get());
675
676 for (std::size_t i = 0; i < nItems; ++i) {
677 CallReadOn(*fSubfields[0], collectionStart + i, typedValue->data() + (i * fItemSize));
678 }
679}
680
690
692{
693 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
694}
695
697{
698 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
699}
700
701std::unique_ptr<ROOT::RFieldBase> ROOT::RVectorField::BeforeConnectPageSource(Internal::RPageSource &pageSource)
702{
703 if (GetOnDiskId() == kInvalidDescriptorId)
704 return nullptr;
705
706 auto descGuard = pageSource.GetSharedDescriptorGuard();
707 const auto &fieldDesc = descGuard->GetFieldDescriptor(GetOnDiskId());
708 if (fieldDesc.GetTypeName().rfind("std::array<", 0) == 0) {
709 auto substitute = std::make_unique<RArrayAsVectorField>(
710 GetFieldName(), fSubfields[0]->Clone(fSubfields[0]->GetFieldName()), fieldDesc.GetNRepetitions());
711 substitute->SetOnDiskId(GetOnDiskId());
712 return substitute;
713 }
714 return nullptr;
715}
716
718{
719 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
720}
721
723{
724 ConstructVector(where, fSubfields[0]->GetAlignment());
725}
726
728 : RDeleter(GetAlignOfVector()), fItemAlignment(itemAlignment)
729{
730}
731
732ROOT::RVectorField::RVectorDeleter::RVectorDeleter(std::size_t itemSize, std::size_t itemAlignment,
733 std::unique_ptr<RDeleter> itemDeleter)
734 : RDeleter(GetAlignOfVector()),
735 fItemSize(itemSize),
736 fItemAlignment(itemAlignment),
737 fItemDeleter(std::move(itemDeleter))
738{
739}
740
741void ROOT::RVectorField::RVectorDeleter::operator()(void *objPtr, bool dtorOnly)
742{
743 auto vecPtr = static_cast<std::vector<char> *>(objPtr);
744 if (fItemDeleter) {
745 assert(fItemSize > 0);
746 auto nItems = vecPtr->size() / fItemSize;
747 assert((vecPtr->size() % fItemSize) == 0);
748 for (std::size_t i = 0; i < nItems; ++i) {
749 fItemDeleter->operator()(vecPtr->data() + (i * fItemSize), true /* dtorOnly */);
750 }
751 }
752
753 static_assert(kMaxItemAlignment == 4096);
754 // clang-format off
755 switch (fItemAlignment) {
756 case 1: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 1>> *>(objPtr)); break;
757 case 2: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 2>> *>(objPtr)); break;
758 case 4: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 4>> *>(objPtr)); break;
759 case 8: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 8>> *>(objPtr)); break;
760 case 16: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 16>> *>(objPtr)); break;
761 case 32: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 32>> *>(objPtr)); break;
762 case 64: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 64>> *>(objPtr)); break;
763 case 128: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 128>> *>(objPtr)); break;
764 case 256: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 256>> *>(objPtr)); break;
765 case 512: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage< 512>> *>(objPtr)); break;
766 case 1024: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage<1024>> *>(objPtr)); break;
767 case 2048: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage<2048>> *>(objPtr)); break;
768 case 4096: std::destroy_at(static_cast<std::vector<Internal::RAlignedStorage<4096>> *>(objPtr)); break;
769 default: throw ROOT::RException(R__FAIL(std::string("Unsupported alignment: ") + std::to_string(fItemAlignment)));
770 }
771 // clang-format on
772
773 RDeleter::operator()(objPtr, dtorOnly);
774}
775
776std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RVectorField::GetDeleter() const
777{
778 if (fItemDeleter)
779 return std::make_unique<RVectorDeleter>(fItemSize, fSubfields[0]->GetAlignment(), GetDeleterOf(*fSubfields[0]));
780 return std::make_unique<RVectorDeleter>(fSubfields[0]->GetAlignment());
781}
782
783std::vector<ROOT::RFieldBase::RValue> ROOT::RVectorField::SplitValue(const RValue &value) const
784{
785 return SplitVector(value.GetPtr<void>(), *fSubfields[0]);
786}
787
789{
790 return GetSizeOfVector();
791}
792
794{
795 return GetAlignOfVector();
796}
797
799{
800 visitor.VisitVectorField(*this);
801}
802
803//------------------------------------------------------------------------------
804
805ROOT::RField<std::vector<bool>>::RField(std::string_view name)
806 : ROOT::RFieldBase(name, "std::vector<bool>", ROOT::ENTupleStructure::kCollection, false /* isSimple */)
807{
808 Attach(std::make_unique<RField<bool>>("_0"));
809}
810
811std::size_t ROOT::RField<std::vector<bool>>::AppendImpl(const void *from)
812{
813 auto typedValue = static_cast<const std::vector<bool> *>(from);
814 auto count = typedValue->size();
815 for (unsigned i = 0; i < count; ++i) {
816 bool bval = (*typedValue)[i];
817 CallAppendOn(*fSubfields[0], &bval);
818 }
819 fNWritten += count;
820 fPrincipalColumn->Append(&fNWritten);
821 return count + fPrincipalColumn->GetElement()->GetPackedSize();
822}
823
824void ROOT::RField<std::vector<bool>>::ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to)
825{
826 auto typedValue = static_cast<std::vector<bool> *>(to);
827
828 if (fOnDiskNRepetitions == 0) {
829 ROOT::NTupleSize_t nItems;
830 RNTupleLocalIndex collectionStart;
831 fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nItems);
832 typedValue->resize(nItems);
833 for (std::size_t i = 0; i < nItems; ++i) {
834 bool bval;
835 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
836 (*typedValue)[i] = bval;
837 }
838 } else {
839 typedValue->resize(fOnDiskNRepetitions);
840 for (std::size_t i = 0; i < fOnDiskNRepetitions; ++i) {
841 bool bval;
842 CallReadOn(*fSubfields[0], globalIndex * fOnDiskNRepetitions + i, &bval);
843 (*typedValue)[i] = bval;
844 }
845 }
846}
847
848void ROOT::RField<std::vector<bool>>::ReadInClusterImpl(ROOT::RNTupleLocalIndex localIndex, void *to)
849{
850 auto typedValue = static_cast<std::vector<bool> *>(to);
851
852 if (fOnDiskNRepetitions == 0) {
853 ROOT::NTupleSize_t nItems;
854 RNTupleLocalIndex collectionStart;
855 fPrincipalColumn->GetCollectionInfo(localIndex, &collectionStart, &nItems);
856 typedValue->resize(nItems);
857 for (std::size_t i = 0; i < nItems; ++i) {
858 bool bval;
859 CallReadOn(*fSubfields[0], collectionStart + i, &bval);
860 (*typedValue)[i] = bval;
861 }
862 } else {
863 typedValue->resize(fOnDiskNRepetitions);
864 for (std::size_t i = 0; i < fOnDiskNRepetitions; ++i) {
865 bool bval;
866 CallReadOn(*fSubfields[0], localIndex * fOnDiskNRepetitions + i, &bval);
867 (*typedValue)[i] = bval;
868 }
869 }
870}
871
872const ROOT::RFieldBase::RColumnRepresentations &ROOT::RField<std::vector<bool>>::GetColumnRepresentations() const
873{
874 static RColumnRepresentations representations({{ENTupleColumnType::kSplitIndex64},
878 {{}});
879 return representations;
880}
881
882void ROOT::RField<std::vector<bool>>::GenerateColumns()
883{
884 R__ASSERT(fOnDiskNRepetitions == 0); // fOnDiskNRepetitions must only be used for reading
885 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>();
886}
887
888void ROOT::RField<std::vector<bool>>::GenerateColumns(const ROOT::RNTupleDescriptor &desc)
889{
890 if (fOnDiskNRepetitions == 0)
891 GenerateColumnsImpl<ROOT::Internal::RColumnIndex>(desc);
892}
893
894void ROOT::RField<std::vector<bool>>::ReconcileOnDiskField(const RNTupleDescriptor &desc)
895{
896 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
897
898 if (fieldDesc.GetTypeName().rfind("std::array<", 0) == 0) {
899 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffStructure | kDiffNRepetitions).ThrowOnError();
900
901 if (fieldDesc.GetNRepetitions() == 0) {
902 throw RException(R__FAIL("fixed-size array --> std::vector<bool>: expected repetition count > 0\n" +
903 Internal::GetTypeTraceReport(*this, desc)));
904 }
905 if (fieldDesc.GetStructure() != ENTupleStructure::kPlain) {
906 throw RException(R__FAIL("fixed-size array --> std::vector<bool>: expected plain on-disk field\n" +
907 Internal::GetTypeTraceReport(*this, desc)));
908 }
909 fOnDiskNRepetitions = fieldDesc.GetNRepetitions();
910 } else {
911 EnsureMatchingOnDiskCollection(desc).ThrowOnError();
912 }
913}
914
915std::vector<ROOT::RFieldBase::RValue> ROOT::RField<std::vector<bool>>::SplitValue(const RValue &value) const
916{
917 const auto &typedValue = value.GetRef<std::vector<bool>>();
918 auto count = typedValue.size();
919 std::vector<RValue> result;
920 result.reserve(count);
921 for (unsigned i = 0; i < count; ++i) {
922 if (typedValue[i]) {
923 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(true))));
924 } else {
925 result.emplace_back(fSubfields[0]->BindValue(std::shared_ptr<bool>(new bool(false))));
926 }
927 }
928 return result;
929}
930
931void ROOT::RField<std::vector<bool>>::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
932{
933 visitor.VisitVectorBoolField(*this);
934}
935
936//------------------------------------------------------------------------------
937
938ROOT::RArrayAsRVecField::RArrayAsRVecField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
939 std::size_t arrayLength)
940 : ROOT::RFieldBase(fieldName, "ROOT::VecOps::RVec<" + itemField->GetTypeName() + ">",
941 ROOT::ENTupleStructure::kCollection, false /* isSimple */),
942 fItemSize(itemField->GetValueSize()),
943 fArrayLength(arrayLength)
944{
945 if (!itemField->GetTypeAlias().empty())
946 fTypeAlias = "ROOT::VecOps::RVec<" + itemField->GetTypeAlias() + ">";
947 Attach(std::move(itemField), "_0");
948 fValueSize =
952}
953
954std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsRVecField::CloneImpl(std::string_view newName) const
955{
956 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
957 return std::make_unique<RArrayAsRVecField>(newName, std::move(newItemField), fArrayLength);
958}
959
961{
962 // initialize data members fBegin, fSize, fCapacity
963 // currently the inline buffer is left uninitialized
964 void **beginPtr = new (where)(void *)(nullptr);
965 std::int32_t *sizePtr = new (static_cast<void *>(beginPtr + 1)) std::int32_t(0);
966 new (sizePtr + 1) std::int32_t(-1);
967}
968
969std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter() const
970{
971 if (fItemDeleter) {
972 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment(), fItemSize,
973 GetDeleterOf(*fSubfields[0]));
974 }
975 return std::make_unique<RRVecField::RRVecDeleter>(fSubfields[0]->GetAlignment());
976}
977
979{
980 auto begin = RRVecField::ResizeRVec(to, fArrayLength, fItemSize, fSubfields[0].get(), fItemDeleter.get());
981
982 if (fSubfields[0]->IsSimple()) {
983 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, begin);
984 return;
985 }
986
987 // Read the new values into the collection elements
988 for (std::size_t i = 0; i < fArrayLength; ++i) {
989 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, begin + (i * fItemSize));
990 }
991}
992
994{
995 auto begin = RRVecField::ResizeRVec(to, fArrayLength, fItemSize, fSubfields[0].get(), fItemDeleter.get());
996
997 if (fSubfields[0]->IsSimple()) {
998 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, begin);
999 return;
1000 }
1001
1002 // Read the new values into the collection elements
1003 for (std::size_t i = 0; i < fArrayLength; ++i) {
1004 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, begin + (i * fItemSize));
1005 }
1006}
1007
1009{
1010 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffStructure | kDiffNRepetitions).ThrowOnError();
1011 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1012 if (fieldDesc.GetTypeName().rfind("std::array<", 0) != 0) {
1013 throw RException(R__FAIL("RArrayAsRVecField " + GetQualifiedFieldName() + " expects an on-disk array field\n" +
1014 Internal::GetTypeTraceReport(*this, desc)));
1015 }
1016}
1017
1019{
1020 return Internal::EvalRVecAlignment(fSubfields[0]->GetAlignment());
1021}
1022
1023std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsRVecField::SplitValue(const ROOT::RFieldBase::RValue &value) const
1024{
1025 auto arrayPtr = value.GetPtr<unsigned char>().get();
1026 std::vector<ROOT::RFieldBase::RValue> result;
1027 result.reserve(fArrayLength);
1028 for (unsigned i = 0; i < fArrayLength; ++i) {
1029 result.emplace_back(
1030 fSubfields[0]->BindValue(std::shared_ptr<void>(value.GetPtr<void>(), arrayPtr + (i * fItemSize))));
1031 }
1032 return result;
1033}
1034
1039
1040//------------------------------------------------------------------------------
1041
1042ROOT::RArrayAsVectorField::RArrayAsVectorField(std::string_view fieldName, std::unique_ptr<ROOT::RFieldBase> itemField,
1043 std::size_t arrayLength)
1044 : ROOT::RFieldBase(fieldName, "std::vector<" + itemField->GetTypeName() + ">", ROOT::ENTupleStructure::kCollection,
1045 false /* isSimple */),
1046 fItemSize(itemField->GetValueSize()),
1047 fArrayLength(arrayLength)
1048{
1049 if (!itemField->GetTypeAlias().empty())
1050 fTypeAlias = "std::vector<" + itemField->GetTypeAlias() + ">";
1051 Attach(std::move(itemField), "_0");
1054}
1055
1056std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsVectorField::CloneImpl(std::string_view newName) const
1057{
1058 auto newItemField = fSubfields[0]->Clone(fSubfields[0]->GetFieldName());
1059 return std::make_unique<RArrayAsVectorField>(newName, std::move(newItemField), fArrayLength);
1060}
1061
1063{
1064 throw RException(R__FAIL("RArrayAsVectorField fields must only be used for reading"));
1065}
1066
1068{
1069 ConstructVector(where, fSubfields[0]->GetAlignment());
1070}
1071
1072std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsVectorField::GetDeleter() const
1073{
1074 if (fItemDeleter) {
1075 return std::make_unique<RVectorField::RVectorDeleter>(fItemSize, fSubfields[0]->GetAlignment(),
1076 GetDeleterOf(*fSubfields[0]));
1077 }
1078 return std::make_unique<RVectorField::RVectorDeleter>(fSubfields[0]->GetAlignment());
1079}
1080
1082{
1083 auto typedValue = static_cast<std::vector<char> *>(to);
1084
1085 if (fSubfields[0]->IsSimple()) {
1086 typedValue->resize(fArrayLength * fItemSize);
1087 GetPrincipalColumnOf(*fSubfields[0])->ReadV(globalIndex * fArrayLength, fArrayLength, typedValue->data());
1088 return;
1089 }
1090
1091 RVectorField::ResizeVector(to, fArrayLength, fItemSize, *fSubfields[0], fItemDeleter.get());
1092
1093 for (std::size_t i = 0; i < fArrayLength; ++i) {
1094 CallReadOn(*fSubfields[0], globalIndex * fArrayLength + i, typedValue->data() + (i * fItemSize));
1095 }
1096}
1097
1099{
1100 auto typedValue = static_cast<std::vector<char> *>(to);
1101
1102 if (fSubfields[0]->IsSimple()) {
1103 typedValue->resize(fArrayLength * fItemSize);
1104 GetPrincipalColumnOf(*fSubfields[0])->ReadV(localIndex * fArrayLength, fArrayLength, typedValue->data());
1105 return;
1106 }
1107
1108 RVectorField::ResizeVector(to, fArrayLength, fItemSize, *fSubfields[0], fItemDeleter.get());
1109
1110 for (std::size_t i = 0; i < fArrayLength; ++i) {
1111 CallReadOn(*fSubfields[0], localIndex * fArrayLength + i, typedValue->data() + (i * fItemSize));
1112 }
1113}
1114
1116{
1117 EnsureMatchingOnDiskField(desc, kDiffTypeName | kDiffStructure | kDiffNRepetitions);
1118
1119 const auto &fieldDesc = desc.GetFieldDescriptor(GetOnDiskId());
1120 if (fieldDesc.GetTypeName().rfind("std::array<", 0) != 0) {
1121 throw RException(R__FAIL("RArrayAsVectorField " + GetQualifiedFieldName() + " expects an on-disk array field\n" +
1122 Internal::GetTypeTraceReport(*this, desc)));
1123 }
1124}
1125
1126std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsVectorField::SplitValue(const ROOT::RFieldBase::RValue &value) const
1127{
1128 return SplitVector(value.GetPtr<void>(), *fSubfields[0]);
1129}
1130
1132{
1133 return GetSizeOfVector();
1134}
1135
1137{
1138 return GetAlignOfVector();
1139}
1140
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:322
#define f(i)
Definition RSha256.hxx:104
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:139
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:142
#define _(A, B)
Definition cfortran.h:112
Abstract base class for classes implementing the visitor design pattern.
virtual void VisitVectorField(const ROOT::RVectorField &field)
virtual void VisitVectorBoolField(const ROOT::RField< std::vector< bool > > &field)
virtual void VisitRVecField(const ROOT::RRVecField &field)
virtual void VisitArrayAsRVecField(const ROOT::RArrayAsRVecField &field)
virtual void VisitArrayAsVectorField(const ROOT::RArrayAsVectorField &field)
virtual void VisitArrayField(const ROOT::RArrayField &field)
The in-memory representation of a 32bit or 64bit on-disk index column.
Abstract interface to read data from an ntuple.
RSharedDescriptorGuard GetSharedDescriptorGuard() const
Takes the read lock for the descriptor.
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
What alignof(T) for this type returns.
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
What sizeof(T) for this type returns.
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...
std::unique_ptr< RDeleter > GetDeleter() const final
Returns an RVectorField::RVectorDeleter.
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::unique_ptr< RDeleter > fItemDeleter
void ReconcileOnDiskField(const RNTupleDescriptor &desc) final
For non-artificial fields, check compatibility of the in-memory field and the on-disk field.
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final
RArrayAsVectorField(std::string_view fieldName, std::unique_ptr< RFieldBase > itemField, std::size_t arrayLength)
The itemField argument represents the inner item of the on-disk array, i.e.
void GenerateColumns() final
Implementations in derived classes should create the backing columns corresponding to the field type ...
void ReadInClusterImpl(RNTupleLocalIndex localIndex, 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::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::unique_ptr< RFieldBase > CloneImpl(std::string_view newName) const final
The length of the arrays in this field.
std::size_t GetAlignment() const final
What alignof(T) for this type returns.
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
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:78
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.
virtual size_t GetValueSize() const =0
What sizeof(T) for this type returns.
void Attach(std::unique_ptr< RFieldBase > child, std::string_view expectedChildName="")
Add a new subfield to the list of nested fields.
virtual size_t GetAlignment() const =0
What alignof(T) for this type returns.
std::vector< std::unique_ptr< RFieldBase > > fSubfields
Collections and classes own subfields.
static std::unique_ptr< RDeleter > GetDeleterOf(const RFieldBase &other)
@ 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.
std::uint32_t fTraits
Properties of the type that allow for optimizations of collections of that type.
std::string fTypeAlias
A typedef or using name that was used when creating the field.
bool IsSimple() const
std::uint32_t GetTraits() const
RValue BindValue(std::shared_ptr< void > objPtr)
Creates a value from a memory location with an already constructed object.
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:342
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...
ROOT::NTupleSize_t GetIndexInCluster() const
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
What alignof(T) for this type returns.
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
What sizeof(T) for this type returns.
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 ...
std::size_t GetValueSize() const final
What sizeof(T) for this type returns.
std::unique_ptr< RFieldBase > BeforeConnectPageSource(ROOT::Internal::RPageSource &pageSource) final
Called by ConnectPageSource() before connecting; derived classes may override this as appropriate,...
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.
static void ResizeVector(void *vec, std::size_t nItems, std::size_t itemSize, const RFieldBase &itemField, RDeleter *itemDeleter)
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::size_t GetAlignment() const final
What alignof(T) for this type returns.
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.
std::unique_ptr< RDeleter > fItemDeleter
static constexpr std::size_t kMaxItemAlignment
Maximum alignment of the vector's value type.
std::unique_ptr< RDeleter > GetDeleter() const final
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final
std::tuple< unsigned char **, std::int32_t *, std::int32_t * > GetRVecDataMembers(void *rvecPtr)
Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the R...
void DestroyRVecWithChecks(std::size_t alignOfT, unsigned char **beginPtr, std::int32_t *capacityPtr)
std::string GetNormalizedInteger(const std::string &intTemplateArg)
Appends 'll' or 'ull' to the where necessary and strips the suffix if not needed.
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
std::size_t EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
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...
Storage type whose alignment matches AlignT bytes.
Definition BitUtils.hxx:53
Input parameter to RFieldBase::ReadBulk() and RFieldBase::ReadBulkImpl().
RNTupleLocalIndex fFirstIndex
Start of the bulk range.
void * fValues
The destination area, which has to be an array of valid objects of the correct type large enough to h...
std::size_t fCount
Size of the bulk range.
std::vector< unsigned char > * fAuxData
Reference to memory owned by the RBulkValues class.