Skip to content

Commit 6e9d145

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.
1 parent d9bcecf commit 6e9d145

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
@@ -212,7 +212,12 @@ void ROOT::Internal::BitPacking::PackBits(void *dst, const void *src, std::size_
212212
std::size_t dstIdx = 0;
213213
for (std::size_t i = 0; i < count; ++i) {
214214
Word_t packedWord = 0;
215+
#if R__LITTLE_ENDIAN == 0
216+
memcpy(reinterpret_cast<unsigned char *>(&packedWord) + (sizeof(Word_t) - sizeofSrc), srcArray + i * sizeofSrc,
217+
sizeofSrc);
218+
#else
215219
memcpy(&packedWord, srcArray + i * sizeofSrc, sizeofSrc);
220+
#endif
216221
// truncate the LSB of the item
217222
packedWord >>= sizeofSrc * 8 - nDstBits;
218223

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

239+
ByteSwapIfNecessary(accum);
234240
memcpy(&dstArray[dstIdx++], &accum, sizeof(accum));
235241
accum = 0;
236242
bitsUsed = 0;
@@ -248,8 +254,10 @@ void ROOT::Internal::BitPacking::PackBits(void *dst, const void *src, std::size_
248254
}
249255
}
250256

251-
if (bitsUsed)
257+
if (bitsUsed) {
258+
ByteSwapIfNecessary(accum);
252259
memcpy(&dstArray[dstIdx++], &accum, (bitsUsed + 7) / 8);
260+
}
253261

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

281290
assert(remBytesToLoad >= bytesLoaded);
282291
remBytesToLoad -= bytesLoaded;
@@ -288,7 +297,12 @@ void ROOT::Internal::BitPacking::UnpackBits(void *dst, const void *src, std::siz
288297
std::uint32_t msb = packedBytes << (8 * sizeofDst - nMsb);
289298
Word_t packedWord = msb | prevWordLsb;
290299
prevWordLsb = 0;
300+
#if R__LITTLE_ENDIAN == 0
301+
memcpy(dstArray + dstIdx * sizeofDst,
302+
reinterpret_cast<unsigned char *>(&packedWord) + sizeof(Word_t) - sizeofDst, sizeofDst);
303+
#else
291304
memcpy(dstArray + dstIdx * sizeofDst, &packedWord, sizeofDst);
305+
#endif
292306
++dstIdx;
293307
offInWord = nMsb;
294308
}
@@ -310,7 +324,12 @@ void ROOT::Internal::BitPacking::UnpackBits(void *dst, const void *src, std::siz
310324
assert(nSrcBits + offInWord <= kBitsPerWord);
311325
packedWord >>= offInWord;
312326
packedWord <<= 8 * sizeofDst - nSrcBits;
327+
#if R__LITTLE_ENDIAN == 0
328+
memcpy(dstArray + dstIdx * sizeofDst,
329+
reinterpret_cast<unsigned char *>(&packedWord) + sizeof(Word_t) - sizeofDst, sizeofDst);
330+
#else
313331
memcpy(dstArray + dstIdx * sizeofDst, &packedWord, sizeofDst);
332+
#endif
314333
++dstIdx;
315334
offInWord += nSrcBits;
316335
}

tree/ntuple/src/RColumnElement.hxx

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

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

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

940931
void Unpack(void *dst, const void *src, std::size_t count) const final
@@ -944,9 +935,6 @@ public:
944935
R__ASSERT(GetPackedSize(count) == MinBufSize(count, fBitsOnStorage));
945936

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

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

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

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

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

10341010
const double e = 0.5 + (elem - min) * scale;
10351011
Quantized_t q = static_cast<Quantized_t>(e);
1036-
ByteSwapIfNecessary(q);
10371012

10381013
// double-check we actually used at most `nQuantBits`
10391014
assert(outOfRange || ROOT::Internal::LeadingZeroes(q) >= unusedBits);
@@ -1068,7 +1043,6 @@ int UnquantizeReals(T *dst, const Quantized_t *src, std::size_t count, double mi
10681043
// Undo the LSB-preserving shift performed by QuantizeReals
10691044
assert(ROOT::Internal::TrailingZeroes(elem) >= unusedBits);
10701045
elem >>= unusedBits;
1071-
ByteSwapIfNecessary(elem);
10721046

10731047
const double fq = static_cast<double>(elem);
10741048
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)