Skip to content

Commit 15c413d

Browse files
committed
[ntuple] fix bit truncation on big endian
Moves the endianess handling inside BitPacking::[Pack|Unpack]Bits. Fixes truncated and quantized real values on big endian architectures. (cherry picked from commit 6e9d145)
1 parent 3af8619 commit 15c413d

3 files changed

Lines changed: 22 additions & 53 deletions

File tree

tree/ntuple/src/RColumnElement.cxx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,12 @@ void ROOT::Internal::BitPacking::PackBits(void *dst, const void *src, std::size_
213213
std::size_t dstIdx = 0;
214214
for (std::size_t i = 0; i < count; ++i) {
215215
Word_t packedWord = 0;
216+
#if R__LITTLE_ENDIAN == 0
217+
memcpy(reinterpret_cast<unsigned char *>(&packedWord) + (sizeof(Word_t) - sizeofSrc), srcArray + i * sizeofSrc,
218+
sizeofSrc);
219+
#else
216220
memcpy(&packedWord, srcArray + i * sizeofSrc, sizeofSrc);
221+
#endif
217222
// truncate the LSB of the item
218223
packedWord >>= sizeofSrc * 8 - nDstBits;
219224

@@ -232,6 +237,7 @@ void ROOT::Internal::BitPacking::PackBits(void *dst, const void *src, std::size_
232237
accum |= (packedWordLsb << bitsUsed);
233238
}
234239

240+
ByteSwapIfNecessary(accum);
235241
memcpy(&dstArray[dstIdx++], &accum, sizeof(accum));
236242
accum = 0;
237243
bitsUsed = 0;
@@ -249,8 +255,10 @@ void ROOT::Internal::BitPacking::PackBits(void *dst, const void *src, std::size_
249255
}
250256
}
251257

252-
if (bitsUsed)
258+
if (bitsUsed) {
259+
ByteSwapIfNecessary(accum);
253260
memcpy(&dstArray[dstIdx++], &accum, (bitsUsed + 7) / 8);
261+
}
254262

255263
[[maybe_unused]] auto expDstCount = (count * nDstBits + kBitsPerWord - 1) / kBitsPerWord;
256264
assert(dstIdx == expDstCount);
@@ -278,6 +286,7 @@ void ROOT::Internal::BitPacking::UnpackBits(void *dst, const void *src, std::siz
278286
Word_t packedBytes = 0;
279287
std::size_t bytesLoaded = std::min(remBytesToLoad, sizeof(Word_t));
280288
memcpy(&packedBytes, &srcArray[i], bytesLoaded);
289+
ByteSwapIfNecessary(packedBytes);
281290

282291
assert(remBytesToLoad >= bytesLoaded);
283292
remBytesToLoad -= bytesLoaded;
@@ -289,7 +298,12 @@ void ROOT::Internal::BitPacking::UnpackBits(void *dst, const void *src, std::siz
289298
std::uint32_t msb = packedBytes << (8 * sizeofDst - nMsb);
290299
Word_t packedWord = msb | prevWordLsb;
291300
prevWordLsb = 0;
301+
#if R__LITTLE_ENDIAN == 0
302+
memcpy(dstArray + dstIdx * sizeofDst,
303+
reinterpret_cast<unsigned char *>(&packedWord) + sizeof(Word_t) - sizeofDst, sizeofDst);
304+
#else
292305
memcpy(dstArray + dstIdx * sizeofDst, &packedWord, sizeofDst);
306+
#endif
293307
++dstIdx;
294308
offInWord = nMsb;
295309
}
@@ -311,7 +325,12 @@ void ROOT::Internal::BitPacking::UnpackBits(void *dst, const void *src, std::siz
311325
assert(nSrcBits + offInWord <= kBitsPerWord);
312326
packedWord >>= offInWord;
313327
packedWord <<= 8 * sizeofDst - nSrcBits;
328+
#if R__LITTLE_ENDIAN == 0
329+
memcpy(dstArray + dstIdx * sizeofDst,
330+
reinterpret_cast<unsigned char *>(&packedWord) + sizeof(Word_t) - sizeofDst, sizeofDst);
331+
#else
314332
memcpy(dstArray + dstIdx * sizeofDst, &packedWord, sizeofDst);
333+
#endif
315334
++dstIdx;
316335
offInWord += nSrcBits;
317336
}

tree/ntuple/src/RColumnElement.hxx

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -924,16 +924,7 @@ public:
924924

925925
R__ASSERT(GetPackedSize(count) == MinBufSize(count, fBitsOnStorage));
926926

927-
#if R__LITTLE_ENDIAN == 0
928-
// TODO(gparolini): to avoid this extra allocation we might want to perform byte swapping
929-
// directly in the Pack/UnpackBits functions.
930-
auto bswapped = MakeUninitArray<float>(count);
931-
CopyBswap<sizeof(float)>(bswapped.get(), src, count);
932-
const auto *srcLe = bswapped.get();
933-
#else
934-
const auto *srcLe = reinterpret_cast<const float *>(src);
935-
#endif
936-
PackBits(dst, srcLe, count, sizeof(float), fBitsOnStorage);
927+
PackBits(dst, src, count, sizeof(float), fBitsOnStorage);
937928
}
938929

939930
void Unpack(void *dst, const void *src, std::size_t count) const final
@@ -943,9 +934,6 @@ public:
943934
R__ASSERT(GetPackedSize(count) == MinBufSize(count, fBitsOnStorage));
944935

945936
UnpackBits(dst, src, count, sizeof(float), fBitsOnStorage);
946-
#if R__LITTLE_ENDIAN == 0
947-
InPlaceBswap<sizeof(float)>(dst, count);
948-
#endif
949937
}
950938
};
951939

@@ -965,16 +953,7 @@ public:
965953
for (std::size_t i = 0; i < count; ++i)
966954
srcFloat[i] = static_cast<float>(srcDouble[i]);
967955

968-
#if R__LITTLE_ENDIAN == 0
969-
// TODO(gparolini): to avoid this extra allocation we might want to perform byte swapping
970-
// directly in the Pack/UnpackBits functions.
971-
auto bswapped = MakeUninitArray<float>(count);
972-
CopyBswap<sizeof(float)>(bswapped.get(), srcFloat.get(), count);
973-
const float *srcLe = bswapped.get();
974-
#else
975-
const float *srcLe = reinterpret_cast<const float *>(srcFloat.get());
976-
#endif
977-
PackBits(dst, srcLe, count, sizeof(float), fBitsOnStorage);
956+
PackBits(dst, reinterpret_cast<const float *>(srcFloat.get()), count, sizeof(float), fBitsOnStorage);
978957
}
979958

980959
void Unpack(void *dst, const void *src, std::size_t count) const final
@@ -986,9 +965,6 @@ public:
986965
// TODO(gparolini): avoid this allocation
987966
auto dstFloat = MakeUninitArray<float>(count);
988967
UnpackBits(dstFloat.get(), src, count, sizeof(float), fBitsOnStorage);
989-
#if R__LITTLE_ENDIAN == 0
990-
InPlaceBswap<sizeof(float)>(dstFloat.get(), count);
991-
#endif
992968

993969
double *dstDouble = reinterpret_cast<double *>(dst);
994970
for (std::size_t i = 0; i < count; ++i)
@@ -1062,7 +1038,6 @@ int QuantizeReals(Quantized_t *dst, const T *src, std::size_t count, double min,
10621038

10631039
const double e = 0.5 + (elem - min) * scale;
10641040
Quantized_t q = static_cast<Quantized_t>(e);
1065-
ByteSwapIfNecessary(q);
10661041

10671042
// double-check we actually used at most `nQuantBits`
10681043
assert(outOfRange || LeadingZeroes(q) >= unusedBits);
@@ -1097,7 +1072,6 @@ int UnquantizeReals(T *dst, const Quantized_t *src, std::size_t count, double mi
10971072
// Undo the LSB-preserving shift performed by QuantizeReals
10981073
assert(TrailingZeroes(elem) >= unusedBits);
10991074
elem >>= unusedBits;
1100-
ByteSwapIfNecessary(elem);
11011075

11021076
const double fq = static_cast<double>(elem);
11031077
double e = fq * scale + min;

tree/ntuple/test/ntuple_endian.cxx

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -224,27 +224,3 @@ TEST(RColumnElementEndian, DeltaSplit)
224224
32));
225225
#endif
226226
}
227-
228-
TEST(RColumnElementEndian, Real32Trunc)
229-
{
230-
#ifndef R__BYTESWAP
231-
GTEST_SKIP() << "Skipping test on big endian node";
232-
#else
233-
RColumnElement<float, ENTupleColumnType::kReal32Trunc> element;
234-
element.SetBitsOnStorage(12);
235-
236-
RPageSinkMock sink1(element);
237-
unsigned char buf1[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
238-
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
239-
RPage page1(buf1, nullptr, sizeof(float), 4);
240-
page1.GrowUnchecked(4);
241-
sink1.CommitPage(RPageStorage::ColumnHandle_t{}, page1);
242-
243-
EXPECT_EQ(0, memcmp(sink1.GetPages()[0].GetBuffer(), "\x00\x00\x04\x80\x00\x0c", 6));
244-
245-
RPageSourceMock source1(sink1.GetPages(), element);
246-
auto page2Ref = source1.LoadPage(RPageStorage::ColumnHandle_t{}, NTupleSize_t{0});
247-
EXPECT_EQ(
248-
0, memcmp(page2Ref.Get().GetBuffer(), "\x00\x00\x00\x00\x04\x00\x00\x00\x08\x00\x00\x00\x0c\x00\x00\x00", 16));
249-
#endif
250-
}

0 commit comments

Comments
 (0)