9
9
#include < ROOT/RFieldUtils.hxx>
10
10
11
11
#include < cstdlib> // for malloc, free
12
+ #include < limits>
12
13
#include < memory>
13
14
#include < new> // hardware_destructive_interference_size
14
15
@@ -134,19 +135,20 @@ namespace {
134
135
135
136
// / Retrieve the addresses of the data members of a generic RVec from a pointer to the beginning of the RVec object.
136
137
// / Returns pointers to fBegin, fSize and fCapacity in a std::tuple.
137
- std::tuple<void **, std::int32_t *, std::int32_t *> GetRVecDataMembers (void *rvecPtr)
138
+ std::tuple<unsigned char **, std::int32_t *, std::int32_t *> GetRVecDataMembers (void *rvecPtr)
138
139
{
139
- void **begin = reinterpret_cast <void **>(rvecPtr);
140
+ unsigned char **beginPtr = reinterpret_cast <unsigned char **>(rvecPtr);
140
141
// int32_t fSize is the second data member (after 1 void*)
141
- std::int32_t *size = reinterpret_cast <std::int32_t *>(begin + 1 );
142
+ std::int32_t *size = reinterpret_cast <std::int32_t *>(beginPtr + 1 );
142
143
R__ASSERT (*size >= 0 );
143
144
// int32_t fCapacity is the third data member (1 int32_t after fSize)
144
145
std::int32_t *capacity = size + 1 ;
145
146
R__ASSERT (*capacity >= -1 );
146
- return {begin , size, capacity};
147
+ return {beginPtr , size, capacity};
147
148
}
148
149
149
- std::tuple<const void *const *, const std::int32_t *, const std::int32_t *> GetRVecDataMembers (const void *rvecPtr)
150
+ std::tuple<const unsigned char *const *, const std::int32_t *, const std::int32_t *>
151
+ GetRVecDataMembers (const void *rvecPtr)
150
152
{
151
153
return {GetRVecDataMembers (const_cast <void *>(rvecPtr))};
152
154
}
@@ -198,19 +200,19 @@ std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
198
200
return std::max ({alignof (void *), alignof (std::int32_t ), alignOfSubfield});
199
201
}
200
202
201
- void DestroyRVecWithChecks (std::size_t alignOfT, void **beginPtr, char *begin , std::int32_t *capacityPtr)
203
+ void DestroyRVecWithChecks (std::size_t alignOfT, unsigned char **beginPtr , std::int32_t *capacityPtr)
202
204
{
203
205
// figure out if we are in the small state, i.e. begin == &inlineBuffer
204
206
// there might be padding between fCapacity and the inline buffer, so we compute it here
205
207
constexpr auto dataMemberSz = sizeof (void *) + 2 * sizeof (std::int32_t );
206
208
auto paddingMiddle = dataMemberSz % alignOfT;
207
209
if (paddingMiddle != 0 )
208
210
paddingMiddle = alignOfT - paddingMiddle;
209
- const bool isSmall = (begin == (reinterpret_cast <char *>(beginPtr) + dataMemberSz + paddingMiddle));
211
+ const bool isSmall = (*beginPtr == (reinterpret_cast <unsigned char *>(beginPtr) + dataMemberSz + paddingMiddle));
210
212
211
213
const bool owns = (*capacityPtr != -1 );
212
214
if (!isSmall && owns)
213
- free (begin );
215
+ free (*beginPtr );
214
216
}
215
217
216
218
} // anonymous namespace
@@ -242,9 +244,8 @@ std::size_t ROOT::RRVecField::AppendImpl(const void *from)
242
244
GetPrincipalColumnOf (*fSubfields [0 ])->AppendV (*beginPtr, *sizePtr);
243
245
nbytes += *sizePtr * GetPrincipalColumnOf (*fSubfields [0 ])->GetElement ()->GetPackedSize ();
244
246
} else {
245
- auto begin = reinterpret_cast <const char *>(*beginPtr); // for pointer arithmetics
246
247
for (std::int32_t i = 0 ; i < *sizePtr; ++i) {
247
- nbytes += CallAppendOn (*fSubfields [0 ], begin + i * fItemSize );
248
+ nbytes += CallAppendOn (*fSubfields [0 ], *beginPtr + i * fItemSize );
248
249
}
249
250
}
250
251
@@ -253,30 +254,27 @@ std::size_t ROOT::RRVecField::AppendImpl(const void *from)
253
254
return nbytes + fPrincipalColumn ->GetElement ()->GetPackedSize ();
254
255
}
255
256
256
- void ROOT::RRVecField::ReadGlobalImpl (ROOT::NTupleSize_t globalIndex, void *to)
257
- {
258
- // TODO as a performance optimization, we could assign values to elements of the inline buffer:
259
- // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
257
+ unsigned char *ROOT::RRVecField::ResizeRVec (void *rvec, std::size_t nItems, std::size_t itemSize,
258
+ const RFieldBase *itemField, RDeleter *itemDeleter)
260
259
261
- auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers (to);
260
+ {
261
+ if (nItems > static_cast <std::size_t >(std::numeric_limits<std::int32_t >::max ())) {
262
+ throw RException (R__FAIL (" RVec too large: " + std::to_string (nItems)));
263
+ }
262
264
263
- // Read collection info for this entry
264
- ROOT::NTupleSize_t nItems;
265
- RNTupleLocalIndex collectionStart;
266
- fPrincipalColumn ->GetCollectionInfo (globalIndex, &collectionStart, &nItems);
267
- char *begin = reinterpret_cast <char *>(*beginPtr); // for pointer arithmetics
265
+ auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers (rvec);
268
266
const std::size_t oldSize = *sizePtr;
269
267
270
268
// See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
271
269
// on the element construction/destrution.
272
270
const bool owns = (*capacityPtr != -1 );
273
- const bool needsConstruct = !(fSubfields [ 0 ] ->GetTraits () & kTraitTriviallyConstructible );
274
- const bool needsDestruct = owns && fItemDeleter ;
271
+ const bool needsConstruct = !(itemField ->GetTraits () & kTraitTriviallyConstructible );
272
+ const bool needsDestruct = owns && itemDeleter ;
275
273
276
274
// Destroy excess elements, if any
277
275
if (needsDestruct) {
278
276
for (std::size_t i = nItems; i < oldSize; ++i) {
279
- fItemDeleter ->operator ()(begin + (i * fItemSize ), true /* dtorOnly */ );
277
+ itemDeleter ->operator ()(*beginPtr + (i * itemSize ), true /* dtorOnly */ );
280
278
}
281
279
}
282
280
@@ -286,7 +284,7 @@ void ROOT::RRVecField::ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to)
286
284
// allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
287
285
if (needsDestruct) {
288
286
for (std::size_t i = 0u ; i < oldSize; ++i) {
289
- fItemDeleter ->operator ()(begin + (i * fItemSize ), true /* dtorOnly */ );
287
+ itemDeleter ->operator ()(*beginPtr + (i * itemSize ), true /* dtorOnly */ );
290
288
}
291
289
}
292
290
@@ -297,25 +295,39 @@ void ROOT::RRVecField::ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to)
297
295
}
298
296
// We trust that malloc returns a buffer with large enough alignment.
299
297
// This might not be the case if T in RVec<T> is over-aligned.
300
- *beginPtr = malloc (nItems * fItemSize );
298
+ *beginPtr = static_cast < unsigned char *>( malloc (nItems * itemSize) );
301
299
R__ASSERT (*beginPtr != nullptr );
302
- begin = reinterpret_cast <char *>(*beginPtr);
303
300
*capacityPtr = nItems;
304
301
305
302
// Placement new for elements that were already there before the resize
306
303
if (needsConstruct) {
307
304
for (std::size_t i = 0u ; i < oldSize; ++i)
308
- CallConstructValueOn (*fSubfields [ 0 ], begin + (i * fItemSize ));
305
+ CallConstructValueOn (*itemField, *beginPtr + (i * itemSize ));
309
306
}
310
307
}
311
308
*sizePtr = nItems;
312
309
313
310
// Placement new for new elements, if any
314
311
if (needsConstruct) {
315
312
for (std::size_t i = oldSize; i < nItems; ++i)
316
- CallConstructValueOn (*fSubfields [ 0 ], begin + (i * fItemSize ));
313
+ CallConstructValueOn (*itemField, *beginPtr + (i * itemSize ));
317
314
}
318
315
316
+ return *beginPtr;
317
+ }
318
+
319
+ void ROOT::RRVecField::ReadGlobalImpl (ROOT::NTupleSize_t globalIndex, void *to)
320
+ {
321
+ // TODO as a performance optimization, we could assign values to elements of the inline buffer:
322
+ // if size < inline buffer size: we save one allocation here and usage of the RVec skips a pointer indirection
323
+
324
+ // Read collection info for this entry
325
+ ROOT::NTupleSize_t nItems;
326
+ RNTupleLocalIndex collectionStart;
327
+ fPrincipalColumn ->GetCollectionInfo (globalIndex, &collectionStart, &nItems);
328
+
329
+ auto begin = ResizeRVec (to, nItems, fItemSize , fSubfields [0 ].get (), fItemDeleter .get ());
330
+
319
331
if (fSubfields [0 ]->IsSimple () && nItems) {
320
332
GetPrincipalColumnOf (*fSubfields [0 ])->ReadV (collectionStart, nItems, begin);
321
333
return ;
@@ -430,14 +442,13 @@ void ROOT::RRVecField::RRVecDeleter::operator()(void *objPtr, bool dtorOnly)
430
442
{
431
443
auto [beginPtr, sizePtr, capacityPtr] = GetRVecDataMembers (objPtr);
432
444
433
- char *begin = reinterpret_cast <char *>(*beginPtr); // for pointer arithmetics
434
445
if (fItemDeleter ) {
435
446
for (std::int32_t i = 0 ; i < *sizePtr; ++i) {
436
- fItemDeleter ->operator ()(begin + i * fItemSize , true /* dtorOnly */ );
447
+ fItemDeleter ->operator ()(*beginPtr + i * fItemSize , true /* dtorOnly */ );
437
448
}
438
449
}
439
450
440
- DestroyRVecWithChecks (fItemAlignment , beginPtr, begin, capacityPtr);
451
+ DestroyRVecWithChecks (fItemAlignment , beginPtr, capacityPtr);
441
452
RDeleter::operator ()(objPtr, dtorOnly);
442
453
}
443
454
@@ -453,10 +464,10 @@ std::vector<ROOT::RFieldBase::RValue> ROOT::RRVecField::SplitValue(const RValue
453
464
auto [beginPtr, sizePtr, _] = GetRVecDataMembers (value.GetPtr <void >().get ());
454
465
455
466
std::vector<RValue> result;
456
- char *begin = reinterpret_cast <char *>(*beginPtr); // for pointer arithmetics
457
467
result.reserve (*sizePtr);
458
468
for (std::int32_t i = 0 ; i < *sizePtr; ++i) {
459
- result.emplace_back (fSubfields [0 ]->BindValue (std::shared_ptr<void >(value.GetPtr <void >(), begin + i * fItemSize )));
469
+ result.emplace_back (
470
+ fSubfields [0 ]->BindValue (std::shared_ptr<void >(value.GetPtr <void >(), *beginPtr + i * fItemSize )));
460
471
}
461
472
return result;
462
473
}
@@ -748,52 +759,10 @@ std::unique_ptr<ROOT::RFieldBase> ROOT::RArrayAsRVecField::CloneImpl(std::string
748
759
void ROOT::RArrayAsRVecField::ConstructValue (void *where) const
749
760
{
750
761
// initialize data members fBegin, fSize, fCapacity
762
+ // currently the inline buffer is left uninitialized
751
763
void **beginPtr = new (where)(void *)(nullptr );
752
- std::int32_t *sizePtr = new (reinterpret_cast <void *>(beginPtr + 1 )) std::int32_t (0 );
753
- std::int32_t *capacityPtr = new (sizePtr + 1 ) std::int32_t (0 );
754
-
755
- // Create the RVec with the known fixed size, do it once here instead of
756
- // every time the value is read in `Read*Impl` functions
757
- char *begin = reinterpret_cast <char *>(*beginPtr); // for pointer arithmetics
758
-
759
- // Early return if the RVec has already been allocated.
760
- if (*sizePtr == std::int32_t (fArrayLength ))
761
- return ;
762
-
763
- // Need to allocate the RVec if it is the first time the value is being created.
764
- // See "semantics of reading non-trivial objects" in RNTuple's Architecture.md for details
765
- // on the element construction.
766
- const bool owns = (*capacityPtr != -1 ); // RVec is adopting the memory
767
- const bool needsConstruct = !(fSubfields [0 ]->GetTraits () & kTraitTriviallyConstructible );
768
- const bool needsDestruct = owns && fItemDeleter ;
769
-
770
- // Destroy old elements: useless work for trivial types, but in case the element type's constructor
771
- // allocates memory we need to release it here to avoid memleaks (e.g. if this is an RVec<RVec<int>>)
772
- if (needsDestruct) {
773
- for (std::int32_t i = 0 ; i < *sizePtr; ++i) {
774
- fItemDeleter ->operator ()(begin + (i * fItemSize ), true /* dtorOnly */ );
775
- }
776
- }
777
-
778
- // TODO: Isn't the RVec always owning in this case?
779
- if (owns) {
780
- // *beginPtr points to the array of item values (allocated in an earlier call by the following malloc())
781
- free (*beginPtr);
782
- }
783
-
784
- *beginPtr = malloc (fArrayLength * fItemSize );
785
- R__ASSERT (*beginPtr != nullptr );
786
- // Re-assign begin pointer after allocation
787
- begin = reinterpret_cast <char *>(*beginPtr);
788
- // Size and capacity are equal since the field data type is std::array
789
- *sizePtr = fArrayLength ;
790
- *capacityPtr = fArrayLength ;
791
-
792
- // Placement new for the array elements
793
- if (needsConstruct) {
794
- for (std::size_t i = 0 ; i < fArrayLength ; ++i)
795
- CallConstructValueOn (*fSubfields [0 ], begin + (i * fItemSize ));
796
- }
764
+ std::int32_t *sizePtr = new (static_cast <void *>(beginPtr + 1 )) std::int32_t (0 );
765
+ new (sizePtr + 1 ) std::int32_t (-1 );
797
766
}
798
767
799
768
std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter () const
@@ -807,39 +776,36 @@ std::unique_ptr<ROOT::RFieldBase::RDeleter> ROOT::RArrayAsRVecField::GetDeleter(
807
776
808
777
void ROOT::RArrayAsRVecField::ReadGlobalImpl (ROOT::NTupleSize_t globalIndex, void *to)
809
778
{
810
-
811
- auto [beginPtr, _, __] = GetRVecDataMembers (to);
812
- auto rvecBeginPtr = reinterpret_cast <char *>(*beginPtr); // for pointer arithmetics
779
+ auto begin = RRVecField::ResizeRVec (to, fArrayLength , fItemSize , fSubfields [0 ].get (), fItemDeleter .get ());
813
780
814
781
if (fSubfields [0 ]->IsSimple ()) {
815
- GetPrincipalColumnOf (*fSubfields [0 ])->ReadV (globalIndex * fArrayLength , fArrayLength , rvecBeginPtr );
782
+ GetPrincipalColumnOf (*fSubfields [0 ])->ReadV (globalIndex * fArrayLength , fArrayLength , begin );
816
783
return ;
817
784
}
818
785
819
786
// Read the new values into the collection elements
820
787
for (std::size_t i = 0 ; i < fArrayLength ; ++i) {
821
- CallReadOn (*fSubfields [0 ], globalIndex * fArrayLength + i, rvecBeginPtr + (i * fItemSize ));
788
+ CallReadOn (*fSubfields [0 ], globalIndex * fArrayLength + i, begin + (i * fItemSize ));
822
789
}
823
790
}
824
791
825
792
void ROOT::RArrayAsRVecField::ReadInClusterImpl (RNTupleLocalIndex localIndex, void *to)
826
793
{
827
- auto [beginPtr, _, __] = GetRVecDataMembers (to);
828
- auto rvecBeginPtr = reinterpret_cast <char *>(*beginPtr); // for pointer arithmetics
794
+ auto begin = RRVecField::ResizeRVec (to, fArrayLength , fItemSize , fSubfields [0 ].get (), fItemDeleter .get ());
829
795
830
796
const auto &clusterId = localIndex.GetClusterId ();
831
797
const auto &indexInCluster = localIndex.GetIndexInCluster ();
832
798
833
799
if (fSubfields [0 ]->IsSimple ()) {
834
800
GetPrincipalColumnOf (*fSubfields [0 ])
835
- ->ReadV (RNTupleLocalIndex (clusterId, indexInCluster * fArrayLength ), fArrayLength , rvecBeginPtr );
801
+ ->ReadV (RNTupleLocalIndex (clusterId, indexInCluster * fArrayLength ), fArrayLength , begin );
836
802
return ;
837
803
}
838
804
839
805
// Read the new values into the collection elements
840
806
for (std::size_t i = 0 ; i < fArrayLength ; ++i) {
841
807
CallReadOn (*fSubfields [0 ], RNTupleLocalIndex (clusterId, indexInCluster * fArrayLength + i),
842
- rvecBeginPtr + (i * fItemSize ));
808
+ begin + (i * fItemSize ));
843
809
}
844
810
}
845
811
0 commit comments