Skip to content
Snippets Groups Projects
Commit 8cc76cab authored by Jakob Blomer's avatar Jakob Blomer
Browse files

[ntuple] restructure packing and unpacking

parent f09f0a07
Branches
No related tags found
No related merge requests found
......@@ -41,12 +41,32 @@
#endif /* R__LITTLE_ENDIAN */
namespace {
// In this namespace, common routines are defined for element packing and unpacking of ints and floats.
// The following conversions and encodings exist:
//
// - Byteswap: on big endian machines, ints and floats are byte-swapped to the little endian on-disk format
// - Casting: in-memory values can be stored in narrower on-disk columns. Currently without bounds checks.
// For instance, for Double32_t, an in-memory double value is stored as a float on disk.
// - Split: rearranges the bytes of an array of elements such that all the first bytes are stored first,
// followed by all the second bytes, etc. This often clusters similar values, e.g. all the zero bytes
// for arrays of small integers.
// - Delta: Delta encoding stores on disk the delta to the previous element. This is useful for offsets,
// because it transforms potentially large offset values into small deltas, which are then better
// suited for split encoding.
//
// Encodings/conversions can be fused:
//
// - Delta + Splitting (there is no only-delta encoding)
// - (Delta + ) Splitting + Casting
// - Everything + Byteswap
/// \brief Copy and byteswap `count` elements of size `N` from `source` to `destination`.
///
/// Used on big-endian architectures for packing/unpacking elements whose column type requires
/// a little-endian on-disk representation.
template <std::size_t N>
static void CopyElementsBswap(void *destination, const void *source, std::size_t count)
static void CopyBswap(void *destination, const void *source, std::size_t count)
{
auto dst = reinterpret_cast<typename RByteSwap<N>::value_type *>(destination);
auto src = reinterpret_cast<const typename RByteSwap<N>::value_type *>(source);
......@@ -55,65 +75,126 @@ static void CopyElementsBswap(void *destination, const void *source, std::size_t
}
}
/// \brief Re-arranges the bytes of the array of N-byte elements in columnar layout.
/// Casts T to one of the ints used in RByteSwap and back to its original type, which may be float or double
#if R__LITTLE_ENDIAN == 0
template <typename T>
void ByteSwapIfNecessary(T &value)
{
constexpr auto N = sizeof(T);
using bswap_value_type = typename RByteSwap<N>::value_type;
void *valuePtr = &value;
auto swapped = RByteSwap<N>::bswap(*reinterpret_cast<bswap_value_type *>(valuePtr));
*reinterpret_cast<bswap_value_type *>(valuePtr) = swapped;
}
#else
#define ByteSwapIfNecessary(x) ((void)0)
#endif
/// \brief Pack `count` elements into narrower (or wider) type
///
/// Used to convert in-memory elements to smaller column types of comatible types
/// (e.g., double to float, int64 to int32). Takes care of byte swap if necessary.
template <typename DestT, typename SourceT>
static void CastPack(void *destination, const void *source, std::size_t count)
{
auto dst = reinterpret_cast<DestT *>(destination);
auto src = reinterpret_cast<const SourceT *>(source);
for (std::size_t i = 0; i < count; ++i) {
dst[i] = src[i];
ByteSwapIfNecessary(dst[i]);
}
}
/// \brief Unpack `count` on-disk elements into wider (or narrower) in-memory array
///
/// Assumes that the elements are stored in little-endian layout. The destination stores
/// all the first bytes first, then all the second bytes, etc.
template <std::size_t N>
static void SplitElementsLE(void *destination, const void *source, std::size_t count)
/// Used to convert on-disk elements to larger C++ types of comatible types
/// (e.g., float to double, int32 to int64). Takes care of byte swap if necessary.
template <typename DestT, typename SourceT>
static void CastUnpack(void *destination, const void *source, std::size_t count)
{
const char *unsplitArray = reinterpret_cast<const char *>(source);
char *splitArray = reinterpret_cast<char *>(destination);
auto dst = reinterpret_cast<DestT *>(destination);
auto src = reinterpret_cast<const SourceT *>(source);
for (std::size_t i = 0; i < count; ++i) {
DestT val = src[i];
ByteSwapIfNecessary(val);
dst[i] = val;
}
}
/// \brief Split encoding of elements, possibly into narrower column
///
/// Used to first cast and then split-encode in-memory values to the on-disk column. Swap bytes if necessary.
template <typename DestT, typename SourceT>
static void CastSplitPack(void *destination, const void *source, std::size_t count)
{
constexpr std::size_t N = sizeof(DestT);
auto splitArray = reinterpret_cast<char *>(destination);
auto src = reinterpret_cast<const SourceT *>(source);
for (std::size_t i = 0; i < count; ++i) {
DestT val = src[i];
ByteSwapIfNecessary(val);
for (std::size_t b = 0; b < N; ++b) {
splitArray[b * count + i] = unsplitArray[N * i + b];
splitArray[b * count + i] = reinterpret_cast<const char *>(&val)[b];
}
}
}
/// Reverse of SplitElements. Stores LE values in the destination buffer.
/// Note that the split version is always splitting on LE values.
template <std::size_t N>
static void UnsplitElementsLE(void *destination, const void *source, std::size_t count)
/// \brief Reverse split encoding of elements
///
/// Used to first unsplit a column, possibly storing elements in wider C++ types. Swaps bytes if necessary
template <typename DestT, typename SourceT>
static void CastSplitUnpack(void *destination, const void *source, std::size_t count)
{
const char *splitArray = reinterpret_cast<const char *>(source);
char *unsplitArray = reinterpret_cast<char *>(destination);
constexpr std::size_t N = sizeof(SourceT);
auto dst = reinterpret_cast<DestT *>(destination);
auto splitArray = reinterpret_cast<const char *>(source);
for (std::size_t i = 0; i < count; ++i) {
SourceT val = 0;
for (std::size_t b = 0; b < N; ++b) {
unsplitArray[N * i + b] = splitArray[b * count + i];
reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
}
ByteSwapIfNecessary(val);
dst[i] = val;
}
}
/// \brief Re-arranges the bytes of the array of N-byte elements in columnar layout.
/// \brief Packing of index columns with delta + split encoding
///
/// Assumes that the elements are stored in big-endian layout and byte-swaps them during the splitting.
/// The destination stores all the first bytes first of the elements in LE layout, then all the second bytes, etc.
template <std::size_t N>
static void SplitElementsBE(void *destination, const void *source, std::size_t count)
/// Apply split encoding to delta-encoded index values
template <typename DestT, typename SourceT>
static void CastDeltaSplitPack(void *destination, const void *source, std::size_t count)
{
const char *unsplitArray = reinterpret_cast<const char *>(source);
char *splitArray = reinterpret_cast<char *>(destination);
constexpr std::size_t N = sizeof(DestT);
auto src = reinterpret_cast<const SourceT *>(source);
auto splitArray = reinterpret_cast<char *>(destination);
for (std::size_t i = 0; i < count; ++i) {
DestT val = (i == 0) ? src[0] : src[i] - src[i - 1];
ByteSwapIfNecessary(val);
for (std::size_t b = 0; b < N; ++b) {
splitArray[b * count + i] = unsplitArray[N * i + (N - (b + 1))];
splitArray[b * count + i] = reinterpret_cast<char *>(&val)[b];
}
}
}
/// Reverse of SplitElements. Stores BE values in the destination buffer.
/// Note that the split version is always splitting on LE values.
template <std::size_t N>
static void UnsplitElementsBE(void *destination, const void *source, std::size_t count)
/// \brief Unsplit and unwind delta encoding
///
/// Unsplit an index column and reverse the delta encoding
template <typename DestT, typename SourceT>
static void CastDeltaSplitUnpack(void *destination, const void *source, std::size_t count)
{
const char *splitArray = reinterpret_cast<const char *>(source);
char *unsplitArray = reinterpret_cast<char *>(destination);
constexpr std::size_t N = sizeof(SourceT);
auto splitArray = reinterpret_cast<const char *>(source);
auto dst = reinterpret_cast<DestT *>(destination);
for (std::size_t i = 0; i < count; ++i) {
SourceT val;
for (std::size_t b = 0; b < N; ++b) {
unsplitArray[N * i + (N - (b + 1))] = splitArray[b * count + i];
reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
}
ByteSwapIfNecessary(val);
dst[i] = (i == 0) ? val : dst[i - 1] + val;
}
}
} // anonymous namespace
namespace ROOT {
......@@ -211,7 +292,7 @@ public:
#if R__LITTLE_ENDIAN == 1
RColumnElementBase::Pack(dst, src, count);
#else
CopyElementsBswap<sizeof(CppT)>(dst, src, count);
CopyBswap<sizeof(CppT)>(dst, src, count);
#endif
}
void Unpack(void *dst, void *src, std::size_t count) const final
......@@ -219,42 +300,69 @@ public:
#if R__LITTLE_ENDIAN == 1
RColumnElementBase::Unpack(dst, src, count);
#else
CopyElementsBswap<sizeof(CppT)>(dst, src, count);
CopyBswap<sizeof(CppT)>(dst, src, count);
#endif
}
}; // class RColumnElementLE
/**
* Base class for columns storing elements of wider in-memory types,
* such as 64bit in-memory offsets to Index32 columns.
*/
template <typename CppT, typename NarrowT>
class RColumnElementCastLE : public RColumnElementBase {
public:
static constexpr bool kIsMappable = false;
RColumnElementCastLE(void *rawContent, std::size_t size) : RColumnElementBase(rawContent, size) {}
void Pack(void *dst, void *src, std::size_t count) const final { CastPack<NarrowT, CppT>(dst, src, count); }
void Unpack(void *dst, void *src, std::size_t count) const final { CastUnpack<CppT, NarrowT>(dst, src, count); }
}; // class RColumnElementCastLE
/**
* Base class for split columns whose on-storage representation is little-endian.
* The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
* As part of the splitting, can also narrow down the type to NarrowT.
*/
template <typename CppT>
template <typename CppT, typename NarrowT>
class RColumnElementSplitLE : public RColumnElementBase {
public:
static constexpr bool kIsMappable = false;
RColumnElementSplitLE(void *rawContent, std::size_t size) : RColumnElementBase(rawContent, size) {}
void Pack(void *dst, void *src, std::size_t count) const final { CastSplitPack<NarrowT, CppT>(dst, src, count); }
void Unpack(void *dst, void *src, std::size_t count) const final { CastSplitUnpack<CppT, NarrowT>(dst, src, count); }
}; // class RColumnElementSplitLE
/**
* Base class for delta + split columns (index columns) whose on-storage representation is little-endian.
* The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
* As part of the encoding, can also narrow down the type to NarrowT.
*/
template <typename CppT, typename NarrowT>
class RColumnElementDeltaSplitLE : public RColumnElementBase {
public:
static constexpr bool kIsMappable = false;
RColumnElementDeltaSplitLE(void *rawContent, std::size_t size) : RColumnElementBase(rawContent, size) {}
void Pack(void *dst, void *src, std::size_t count) const final
{
#if R__LITTLE_ENDIAN == 1
SplitElementsLE<sizeof(CppT)>(dst, src, count);
#else
SplitElementsBE<sizeof(CppT)>(dst, src, count);
#endif
CastDeltaSplitPack<NarrowT, CppT>(dst, src, count);
}
void Unpack(void *dst, void *src, std::size_t count) const final
{
#if R__LITTLE_ENDIAN == 1
UnsplitElementsLE<sizeof(CppT)>(dst, src, count);
#else
UnsplitElementsBE<sizeof(CppT)>(dst, src, count);
#endif
CastDeltaSplitUnpack<CppT, NarrowT>(dst, src, count);
}
}; // class RColumnElementSplitLE
}; // class RColumnElementDeltaSplitLE
////////////////////////////////////////////////////////////////////////////////
// Pairs of C++ type and column type, like float and EColumnType::kReal32
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Part 1: C++ type --> unknown column type
////////////////////////////////////////////////////////////////////////////////
/**
* Pairs of C++ type and column type, like float and EColumnType::kReal32
*/
template <typename CppT, EColumnType ColumnT = EColumnType::kUnknown>
class RColumnElement : public RColumnElementBase {
public:
......@@ -363,42 +471,43 @@ public:
explicit RColumnElement(RColumnSwitch *value) : RColumnElementBase(value, kSize) {}
};
template <>
class RColumnElement<float, EColumnType::kReal32> : public RColumnElementLE<float> {
public:
static constexpr std::size_t kSize = sizeof(float);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(float *value) : RColumnElementLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
////////////////////////////////////////////////////////////////////////////////
// Part 2: C++ type --> supported column representations,
// ordered by C++ type
////////////////////////////////////////////////////////////////////////////////
template <>
class RColumnElement<float, EColumnType::kSplitReal32> : public RColumnElementSplitLE<float> {
class RColumnElement<bool, EColumnType::kBit> : public RColumnElementBase {
public:
static constexpr std::size_t kSize = sizeof(float);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(float *value) : RColumnElementSplitLE(value, kSize) {}
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(bool);
static constexpr std::size_t kBitsOnStorage = 1;
explicit RColumnElement(bool *value) : RColumnElementBase(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
void Pack(void *dst, void *src, std::size_t count) const final;
void Unpack(void *dst, void *src, std::size_t count) const final;
};
template <>
class RColumnElement<double, EColumnType::kReal64> : public RColumnElementLE<double> {
class RColumnElement<char, EColumnType::kByte> : public RColumnElementBase {
public:
static constexpr std::size_t kSize = sizeof(double);
static constexpr bool kIsMappable = true;
static constexpr std::size_t kSize = sizeof(char);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(double *value) : RColumnElementLE(value, kSize) {}
explicit RColumnElement(char *value) : RColumnElementBase(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<double, EColumnType::kSplitReal64> : public RColumnElementSplitLE<double> {
class RColumnElement<char, EColumnType::kChar> : public RColumnElementBase {
public:
static constexpr std::size_t kSize = sizeof(double);
static constexpr bool kIsMappable = true;
static constexpr std::size_t kSize = sizeof(char);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(double *value) : RColumnElementSplitLE(value, kSize) {}
explicit RColumnElement(char *value) : RColumnElementBase(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
......@@ -415,23 +524,23 @@ public:
};
template <>
class RColumnElement<std::uint8_t, EColumnType::kInt8> : public RColumnElementBase {
class RColumnElement<std::int8_t, EColumnType::kByte> : public RColumnElementBase {
public:
static constexpr bool kIsMappable = true;
static constexpr std::size_t kSize = sizeof(std::uint8_t);
static constexpr std::size_t kSize = sizeof(std::int8_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::uint8_t *value) : RColumnElementBase(value, kSize) {}
explicit RColumnElement(std::int8_t *value) : RColumnElementBase(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::int8_t, EColumnType::kByte> : public RColumnElementBase {
class RColumnElement<std::uint8_t, EColumnType::kInt8> : public RColumnElementBase {
public:
static constexpr bool kIsMappable = true;
static constexpr std::size_t kSize = sizeof(std::int8_t);
static constexpr std::size_t kSize = sizeof(std::uint8_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::int8_t *value) : RColumnElementBase(value, kSize) {}
explicit RColumnElement(std::uint8_t *value) : RColumnElementBase(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
......@@ -458,27 +567,29 @@ public:
};
template <>
class RColumnElement<std::uint16_t, EColumnType::kInt16> : public RColumnElementLE<std::uint16_t> {
class RColumnElement<std::int16_t, EColumnType::kSplitInt16>
: public RColumnElementSplitLE<std::int16_t, std::int16_t> {
public:
static constexpr std::size_t kSize = sizeof(std::uint16_t);
static constexpr std::size_t kSize = sizeof(std::int16_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::uint16_t *value) : RColumnElementLE(value, kSize) {}
explicit RColumnElement(std::int16_t *value) : RColumnElementSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::int16_t, EColumnType::kSplitInt16> : public RColumnElementSplitLE<std::int16_t> {
class RColumnElement<std::uint16_t, EColumnType::kInt16> : public RColumnElementLE<std::uint16_t> {
public:
static constexpr std::size_t kSize = sizeof(std::int16_t);
static constexpr std::size_t kSize = sizeof(std::uint16_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::int16_t *value) : RColumnElementSplitLE(value, kSize) {}
explicit RColumnElement(std::uint16_t *value) : RColumnElementLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::uint16_t, EColumnType::kSplitInt16> : public RColumnElementSplitLE<std::uint16_t> {
class RColumnElement<std::uint16_t, EColumnType::kSplitInt16>
: public RColumnElementSplitLE<std::uint16_t, std::uint16_t> {
public:
static constexpr std::size_t kSize = sizeof(std::uint16_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
......@@ -498,27 +609,29 @@ public:
};
template <>
class RColumnElement<std::uint32_t, EColumnType::kInt32> : public RColumnElementLE<std::uint32_t> {
class RColumnElement<std::int32_t, EColumnType::kSplitInt32>
: public RColumnElementSplitLE<std::int32_t, std::int32_t> {
public:
static constexpr std::size_t kSize = sizeof(std::uint32_t);
static constexpr std::size_t kSize = sizeof(std::int32_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::uint32_t *value) : RColumnElementLE(value, kSize) {}
explicit RColumnElement(std::int32_t *value) : RColumnElementSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::int32_t, EColumnType::kSplitInt32> : public RColumnElementSplitLE<std::int32_t> {
class RColumnElement<std::uint32_t, EColumnType::kInt32> : public RColumnElementLE<std::uint32_t> {
public:
static constexpr std::size_t kSize = sizeof(std::int32_t);
static constexpr std::size_t kSize = sizeof(std::uint32_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::int32_t *value) : RColumnElementSplitLE(value, kSize) {}
explicit RColumnElement(std::uint32_t *value) : RColumnElementLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::uint32_t, EColumnType::kSplitInt32> : public RColumnElementSplitLE<std::uint32_t> {
class RColumnElement<std::uint32_t, EColumnType::kSplitInt32>
: public RColumnElementSplitLE<std::uint32_t, std::uint32_t> {
public:
static constexpr std::size_t kSize = sizeof(std::uint32_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
......@@ -538,139 +651,159 @@ public:
};
template <>
class RColumnElement<std::uint64_t, EColumnType::kInt64> : public RColumnElementLE<std::uint64_t> {
class RColumnElement<std::int64_t, EColumnType::kSplitInt64>
: public RColumnElementSplitLE<std::int64_t, std::int64_t> {
public:
static constexpr std::size_t kSize = sizeof(std::uint64_t);
static constexpr std::size_t kSize = sizeof(std::int64_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::uint64_t *value) : RColumnElementLE(value, kSize) {}
explicit RColumnElement(std::int64_t *value) : RColumnElementSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::int64_t, EColumnType::kSplitInt64> : public RColumnElementSplitLE<std::int64_t> {
class RColumnElement<std::int64_t, EColumnType::kInt32> : public RColumnElementCastLE<std::int64_t, std::int32_t> {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(std::int64_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
static constexpr std::size_t kBitsOnStorage = 32;
explicit RColumnElement(std::int64_t *value) : RColumnElementCastLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::int64_t, EColumnType::kSplitInt32>
: public RColumnElementSplitLE<std::int64_t, std::int32_t> {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(std::int64_t);
static constexpr std::size_t kBitsOnStorage = 32;
explicit RColumnElement(std::int64_t *value) : RColumnElementSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<std::uint64_t, EColumnType::kSplitInt64> : public RColumnElementSplitLE<std::uint64_t> {
class RColumnElement<std::uint64_t, EColumnType::kInt64> : public RColumnElementLE<std::uint64_t> {
public:
static constexpr std::size_t kSize = sizeof(std::uint64_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::uint64_t *value) : RColumnElementSplitLE(value, kSize) {}
explicit RColumnElement(std::uint64_t *value) : RColumnElementLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<ClusterSize_t, EColumnType::kIndex32> : public RColumnElementBase {
class RColumnElement<std::uint64_t, EColumnType::kSplitInt64>
: public RColumnElementSplitLE<std::uint64_t, std::uint64_t> {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(ClusterSize_t);
static constexpr std::size_t kBitsOnStorage = 32;
explicit RColumnElement(ClusterSize_t *value) : RColumnElementBase(value, kSize) {}
static constexpr std::size_t kSize = sizeof(std::uint64_t);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(std::uint64_t *value) : RColumnElementSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
void Pack(void *dst, void *src, std::size_t count) const final;
void Unpack(void *dst, void *src, std::size_t count) const final;
};
template <>
class RColumnElement<ClusterSize_t, EColumnType::kSplitIndex32> : public RColumnElementBase {
class RColumnElement<float, EColumnType::kReal32> : public RColumnElementLE<float> {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(ClusterSize_t);
static constexpr std::size_t kBitsOnStorage = 32;
explicit RColumnElement(ClusterSize_t *value) : RColumnElementBase(value, kSize) {}
static constexpr std::size_t kSize = sizeof(float);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(float *value) : RColumnElementLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
void Pack(void *dst, void *src, std::size_t count) const final;
void Unpack(void *dst, void *src, std::size_t count) const final;
};
template <>
class RColumnElement<RColumnSwitch, EColumnType::kSwitch> : public RColumnElementBase {
class RColumnElement<float, EColumnType::kSplitReal32> : public RColumnElementSplitLE<float, float> {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(ROOT::Experimental::RColumnSwitch);
static constexpr std::size_t kBitsOnStorage = 64;
explicit RColumnElement(RColumnSwitch *value) : RColumnElementBase(value, kSize) {}
static constexpr std::size_t kSize = sizeof(float);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(float *value) : RColumnElementSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
void Pack(void *dst, void *src, std::size_t count) const final;
void Unpack(void *dst, void *src, std::size_t count) const final;
};
template <>
class RColumnElement<char, EColumnType::kByte> : public RColumnElementBase {
class RColumnElement<double, EColumnType::kReal64> : public RColumnElementLE<double> {
public:
static constexpr bool kIsMappable = true;
static constexpr std::size_t kSize = sizeof(char);
static constexpr std::size_t kSize = sizeof(double);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(char *value) : RColumnElementBase(value, kSize) {}
explicit RColumnElement(double *value) : RColumnElementLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<char, EColumnType::kChar> : public RColumnElementBase {
class RColumnElement<double, EColumnType::kSplitReal64> : public RColumnElementSplitLE<double, double> {
public:
static constexpr bool kIsMappable = true;
static constexpr std::size_t kSize = sizeof(char);
static constexpr std::size_t kSize = sizeof(double);
static constexpr std::size_t kBitsOnStorage = kSize * 8;
explicit RColumnElement(char *value) : RColumnElementBase(value, kSize) {}
explicit RColumnElement(double *value) : RColumnElementSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
};
template <>
class RColumnElement<bool, EColumnType::kBit> : public RColumnElementBase {
class RColumnElement<ClusterSize_t, EColumnType::kIndex32> : public RColumnElementCastLE<std::uint64_t, std::uint32_t> {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(bool);
static constexpr std::size_t kBitsOnStorage = 1;
explicit RColumnElement(bool *value) : RColumnElementBase(value, kSize) {}
static constexpr std::size_t kSize = sizeof(ClusterSize_t);
static constexpr std::size_t kBitsOnStorage = 32;
explicit RColumnElement(ClusterSize_t *value) : RColumnElementCastLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
void Pack(void *dst, void *src, std::size_t count) const final;
void Unpack(void *dst, void *src, std::size_t count) const final;
};
template <>
class RColumnElement<std::int64_t, EColumnType::kInt32> : public RColumnElementBase {
class RColumnElement<ClusterSize_t, EColumnType::kSplitIndex32>
: public RColumnElementDeltaSplitLE<std::uint64_t, std::uint32_t> {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(std::int64_t);
static constexpr std::size_t kSize = sizeof(ClusterSize_t);
static constexpr std::size_t kBitsOnStorage = 32;
explicit RColumnElement(std::int64_t *value) : RColumnElementBase(value, kSize) {}
explicit RColumnElement(ClusterSize_t *value) : RColumnElementDeltaSplitLE(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
void Pack(void *dst, void *src, std::size_t count) const final;
void Unpack(void *dst, void *src, std::size_t count) const final;
};
template <>
class RColumnElement<std::int64_t, EColumnType::kSplitInt32> : public RColumnElementBase {
class RColumnElement<RColumnSwitch, EColumnType::kSwitch> : public RColumnElementBase {
public:
static constexpr bool kIsMappable = false;
static constexpr std::size_t kSize = sizeof(std::int64_t);
static constexpr std::size_t kBitsOnStorage = 32;
explicit RColumnElement(std::int64_t *value) : RColumnElementBase(value, kSize) {}
static constexpr std::size_t kSize = sizeof(ROOT::Experimental::RColumnSwitch);
static constexpr std::size_t kBitsOnStorage = 64;
explicit RColumnElement(RColumnSwitch *value) : RColumnElementBase(value, kSize) {}
bool IsMappable() const final { return kIsMappable; }
std::size_t GetBitsOnStorage() const final { return kBitsOnStorage; }
void Pack(void *dst, void *src, std::size_t count) const final;
void Unpack(void *dst, void *src, std::size_t count) const final;
void Pack(void *dst, void *src, std::size_t count) const final
{
auto srcArray = reinterpret_cast<ROOT::Experimental::RColumnSwitch *>(src);
auto uint64Array = reinterpret_cast<std::uint64_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
uint64Array[i] =
(static_cast<std::uint64_t>(srcArray[i].GetTag()) << 44) | (srcArray[i].GetIndex() & 0x0fffffffffff);
#if R__LITTLE_ENDIAN == 0
uint64Array[i] = RByteSwap<8>::bswap(uint64Array[i]);
#endif
}
}
void Unpack(void *dst, void *src, std::size_t count) const final
{
auto uint64Array = reinterpret_cast<std::uint64_t *>(src);
auto dstArray = reinterpret_cast<ROOT::Experimental::RColumnSwitch *>(dst);
for (std::size_t i = 0; i < count; ++i) {
#if R__LITTLE_ENDIAN == 1
const auto value = uint64Array[i];
#else
const auto value = RByteSwap<8>::bswap(uint64Array[i]);
#endif
dstArray[i] = ROOT::Experimental::RColumnSwitch(
ClusterSize_t{static_cast<RClusterSize::ValueType>(value & 0x0fffffffffff)}, (value >> 44));
}
}
};
template <typename CppT>
......
......@@ -103,38 +103,6 @@ std::string ROOT::Experimental::Detail::RColumnElementBase::GetTypeName(EColumnT
}
}
void ROOT::Experimental::Detail::RColumnElement<ROOT::Experimental::RColumnSwitch,
ROOT::Experimental::EColumnType::kSwitch>::Pack(void *dst, void *src,
std::size_t count) const
{
auto srcArray = reinterpret_cast<ROOT::Experimental::RColumnSwitch *>(src);
auto uint64Array = reinterpret_cast<std::uint64_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
uint64Array[i] =
(static_cast<std::uint64_t>(srcArray[i].GetTag()) << 44) | (srcArray[i].GetIndex() & 0x0fffffffffff);
#if R__LITTLE_ENDIAN == 0
uint64Array[i] = RByteSwap<8>::bswap(uint64Array[i]);
#endif
}
}
void ROOT::Experimental::Detail::RColumnElement<
ROOT::Experimental::RColumnSwitch, ROOT::Experimental::EColumnType::kSwitch>::Unpack(void *dst, void *src,
std::size_t count) const
{
auto uint64Array = reinterpret_cast<std::uint64_t *>(src);
auto dstArray = reinterpret_cast<ROOT::Experimental::RColumnSwitch *>(dst);
for (std::size_t i = 0; i < count; ++i) {
#if R__LITTLE_ENDIAN == 1
const auto value = uint64Array[i];
#else
const auto value = RByteSwap<8>::bswap(uint64Array[i]);
#endif
dstArray[i] = ROOT::Experimental::RColumnSwitch(
ClusterSize_t{static_cast<RClusterSize::ValueType>(value & 0x0fffffffffff)}, (value >> 44));
}
}
void ROOT::Experimental::Detail::RColumnElement<bool, ROOT::Experimental::EColumnType::kBit>::Pack(
void *dst, void *src, std::size_t count) const
{
......@@ -168,125 +136,3 @@ void ROOT::Experimental::Detail::RColumnElement<bool, ROOT::Experimental::EColum
}
}
}
void ROOT::Experimental::Detail::RColumnElement<
ROOT::Experimental::ClusterSize_t, ROOT::Experimental::EColumnType::kIndex32>::Pack(void *dst, void *src,
std::size_t count) const
{
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(src);
std::int32_t *int32Array = reinterpret_cast<std::int32_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
int32Array[i] = int64Array[i];
#if R__LITTLE_ENDIAN == 0
int32Array[i] = RByteSwap<4>::bswap(int32Array[i]);
#endif
}
}
void ROOT::Experimental::Detail::RColumnElement<
ROOT::Experimental::ClusterSize_t, ROOT::Experimental::EColumnType::kIndex32>::Unpack(void *dst, void *src,
std::size_t count) const
{
std::int32_t *int32Array = reinterpret_cast<std::int32_t *>(src);
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
int64Array[i] = int32Array[i];
#if R__LITTLE_ENDIAN == 0
int64Array[i] = RByteSwap<8>::bswap(int64Array[i]);
#endif
}
}
void ROOT::Experimental::Detail::RColumnElement<
ROOT::Experimental::ClusterSize_t, ROOT::Experimental::EColumnType::kSplitIndex32>::Pack(void *dst, void *src,
std::size_t count) const
{
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(src);
char *int32SplitArray = reinterpret_cast<char *>(dst);
for (std::size_t i = 0; i < count; ++i) {
std::uint32_t val = (i == 0) ? int64Array[0] : int64Array[i] - int64Array[i - 1];
#if R__LITTLE_ENDIAN == 0
val = RByteSwap<4>::bswap(val);
#endif
for (std::size_t b = 0; b < 4; ++b) {
int32SplitArray[b * count + i] = reinterpret_cast<char *>(&val)[b];
}
}
}
void ROOT::Experimental::Detail::RColumnElement<
ROOT::Experimental::ClusterSize_t, ROOT::Experimental::EColumnType::kSplitIndex32>::Unpack(void *dst, void *src,
std::size_t count) const
{
char *int32SplitArray = reinterpret_cast<char *>(src);
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
std::int32_t v;
for (std::size_t b = 0; b < 4; ++b) {
reinterpret_cast<char *>(&v)[b] = int32SplitArray[b * count + i];
}
#if R__LITTLE_ENDIAN == 0
v = RByteSwap<4>::bswap(v);
#endif
int64Array[i] = (i == 0) ? v : int64Array[i - 1] + v;
}
}
void ROOT::Experimental::Detail::RColumnElement<std::int64_t, ROOT::Experimental::EColumnType::kInt32>::Pack(
void *dst, void *src, std::size_t count) const
{
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(src);
std::int32_t *int32Array = reinterpret_cast<std::int32_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
int32Array[i] = int64Array[i];
#if R__LITTLE_ENDIAN == 0
int32Array[i] = RByteSwap<4>::bswap(int32Array[i]);
#endif
}
}
void ROOT::Experimental::Detail::RColumnElement<std::int64_t, ROOT::Experimental::EColumnType::kInt32>::Unpack(
void *dst, void *src, std::size_t count) const
{
std::int32_t *int32Array = reinterpret_cast<std::int32_t *>(src);
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
int64Array[i] = int32Array[i];
#if R__LITTLE_ENDIAN == 0
int64Array[i] = RByteSwap<8>::bswap(int64Array[i]);
#endif
}
}
void ROOT::Experimental::Detail::RColumnElement<std::int64_t, ROOT::Experimental::EColumnType::kSplitInt32>::Pack(
void *dst, void *src, std::size_t count) const
{
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(src);
char *int32SplitArray = reinterpret_cast<char *>(dst);
for (std::size_t i = 0; i < count; ++i) {
std::int32_t v = int64Array[i];
#if R__LITTLE_ENDIAN == 0
v = RByteSwap<4>::bswap(v);
#endif
for (std::size_t b = 0; b < 4; ++b) {
int32SplitArray[b * count + i] = reinterpret_cast<char *>(&v)[b];
}
}
}
void ROOT::Experimental::Detail::RColumnElement<std::int64_t, ROOT::Experimental::EColumnType::kSplitInt32>::Unpack(
void *dst, void *src, std::size_t count) const
{
char *int32SplitArray = reinterpret_cast<char *>(src);
std::int64_t *int64Array = reinterpret_cast<std::int64_t *>(dst);
for (std::size_t i = 0; i < count; ++i) {
std::int32_t v = 0;
for (std::size_t b = 0; b < 4; ++b) {
reinterpret_cast<char *>(&v)[b] = int32SplitArray[b * count + i];
}
#if R__LITTLE_ENDIAN == 0
v = RByteSwap<4>::bswap(v);
#endif
int64Array[i] = v;
}
}
......@@ -7,15 +7,17 @@
#include <type_traits>
#include <utility>
template <typename PodT, ROOT::Experimental::EColumnType ColumnT>
template <typename PodT, typename NarrowT, ROOT::Experimental::EColumnType ColumnT>
struct Helper {
using Pod_t = PodT;
using Narrow_t = NarrowT;
static constexpr ROOT::Experimental::EColumnType kColumnType = ColumnT;
};
template <ROOT::Experimental::EColumnType ColumnT>
struct Helper<ROOT::Experimental::ClusterSize_t, ColumnT> {
template <typename NarrowT, ROOT::Experimental::EColumnType ColumnT>
struct Helper<ROOT::Experimental::ClusterSize_t, NarrowT, ColumnT> {
using Pod_t = std::uint64_t;
using Narrow_t = NarrowT;
static constexpr ROOT::Experimental::EColumnType kColumnType = ColumnT;
};
......@@ -37,24 +39,21 @@ public:
using Helper_t = HelperT;
};
using PackingRealTypes = ::testing::Types<Helper<double, ROOT::Experimental::EColumnType::kSplitReal64>,
Helper<float, ROOT::Experimental::EColumnType::kSplitReal32>>;
TYPED_TEST_CASE(PackingReal, PackingRealTypes);
using PackingRealTypes = ::testing::Types<Helper<double, double, ROOT::Experimental::EColumnType::kSplitReal64>,
Helper<float, float, ROOT::Experimental::EColumnType::kSplitReal32>>;
TYPED_TEST_SUITE(PackingReal, PackingRealTypes);
using PackingIntTypes = ::testing::Types<Helper<std::int64_t, ROOT::Experimental::EColumnType::kSplitInt64>,
Helper<std::uint64_t, ROOT::Experimental::EColumnType::kSplitInt64>,
Helper<std::int32_t, ROOT::Experimental::EColumnType::kSplitInt32>,
Helper<std::uint32_t, ROOT::Experimental::EColumnType::kSplitInt32>,
Helper<std::int16_t, ROOT::Experimental::EColumnType::kSplitInt16>,
Helper<std::uint16_t, ROOT::Experimental::EColumnType::kSplitInt16>>;
using PackingIntTypes =
::testing::Types<Helper<std::int64_t, std::int64_t, ROOT::Experimental::EColumnType::kSplitInt64>,
Helper<std::uint64_t, std::uint64_t, ROOT::Experimental::EColumnType::kSplitInt64>,
Helper<std::int32_t, std::int32_t, ROOT::Experimental::EColumnType::kSplitInt32>,
Helper<std::uint32_t, std::uint32_t, ROOT::Experimental::EColumnType::kSplitInt32>,
Helper<std::int16_t, std::int16_t, ROOT::Experimental::EColumnType::kSplitInt16>,
Helper<std::uint16_t, std::uint16_t, ROOT::Experimental::EColumnType::kSplitInt16>>;
TYPED_TEST_SUITE(PackingInt, PackingIntTypes);
using PackingRealTypes = ::testing::Types<Helper<double, ROOT::Experimental::EColumnType::kSplitReal64>,
Helper<float, ROOT::Experimental::EColumnType::kSplitReal32>>;
TYPED_TEST_SUITE(PackingReal, PackingRealTypes);
using PackingIndexTypes =
::testing::Types<Helper<ROOT::Experimental::ClusterSize_t, ROOT::Experimental::EColumnType::kSplitIndex32>>;
using PackingIndexTypes = ::testing::Types<
Helper<ROOT::Experimental::ClusterSize_t, std::uint32_t, ROOT::Experimental::EColumnType::kSplitIndex32>>;
TYPED_TEST_SUITE(PackingIndex, PackingIndexTypes);
TEST(Packing, Bitfield)
......@@ -111,6 +110,7 @@ TEST(Packing, RColumnSwitch)
TYPED_TEST(PackingReal, SplitReal)
{
using Pod_t = typename TestFixture::Helper_t::Pod_t;
using Narrow_t = typename TestFixture::Helper_t::Narrow_t;
ROOT::Experimental::Detail::RColumnElement<Pod_t, TestFixture::Helper_t::kColumnType> element(nullptr);
element.Pack(nullptr, nullptr, 0);
......@@ -118,11 +118,11 @@ TYPED_TEST(PackingReal, SplitReal)
std::array<Pod_t, 7> mem{0.0,
42.0,
std::numeric_limits<Pod_t>::min(),
std::numeric_limits<Pod_t>::max(),
std::numeric_limits<Pod_t>::lowest(),
std::numeric_limits<Pod_t>::infinity(),
std::numeric_limits<Pod_t>::denorm_min()};
std::numeric_limits<Narrow_t>::min(),
std::numeric_limits<Narrow_t>::max(),
std::numeric_limits<Narrow_t>::lowest(),
std::numeric_limits<Narrow_t>::infinity(),
std::numeric_limits<Narrow_t>::denorm_min()};
std::array<Pod_t, 7> packed;
std::array<Pod_t, 7> cmp;
......@@ -135,13 +135,14 @@ TYPED_TEST(PackingReal, SplitReal)
TYPED_TEST(PackingInt, SplitInt)
{
using Pod_t = typename TestFixture::Helper_t::Pod_t;
using Narrow_t = typename TestFixture::Helper_t::Narrow_t;
ROOT::Experimental::Detail::RColumnElement<Pod_t, TestFixture::Helper_t::kColumnType> element(nullptr);
element.Pack(nullptr, nullptr, 0);
element.Unpack(nullptr, nullptr, 0);
std::array<Pod_t, 5> mem{0, std::is_signed_v<Pod_t> ? -42 : 1, 42, std::numeric_limits<Pod_t>::min(),
std::numeric_limits<Pod_t>::max()};
std::array<Pod_t, 5> mem{0, std::is_signed_v<Pod_t> ? -42 : 1, 42, std::numeric_limits<Narrow_t>::min(),
std::numeric_limits<Narrow_t>::max()};
std::array<Pod_t, 5> packed;
std::array<Pod_t, 5> cmp;
......@@ -154,12 +155,13 @@ TYPED_TEST(PackingInt, SplitInt)
TYPED_TEST(PackingIndex, SplitIndex)
{
using Pod_t = typename TestFixture::Helper_t::Pod_t;
using Narrow_t = typename TestFixture::Helper_t::Narrow_t;
ROOT::Experimental::Detail::RColumnElement<ClusterSize_t, TestFixture::Helper_t::kColumnType> element(nullptr);
element.Pack(nullptr, nullptr, 0);
element.Unpack(nullptr, nullptr, 0);
std::array<Pod_t, 5> mem{0, 1, 1, 42, std::numeric_limits<Pod_t>::max()};
std::array<Pod_t, 5> mem{0, 1, 1, 42, std::numeric_limits<Narrow_t>::max()};
std::array<Pod_t, 5> packed;
std::array<Pod_t, 5> cmp;
......@@ -208,8 +210,6 @@ TEST(Packing, OnDiskEncoding)
*e->Get<double>("double") = std::nextafter(1., 2.); // 0x3ff0 0000 0000 0001
*e->Get<ClusterSize_t>("index32") = 39916801; // 0x0261 1501
writer->CommitCluster();
writer->Fill(*e);
*e->Get<std::int16_t>("int16") = -2;
......@@ -256,6 +256,10 @@ TEST(Packing, OnDiskEncoding)
EXPECT_EQ(memcmp(sealedPage.fBuffer, expDouble, sizeof(expDouble)), 0);
source->LoadSealedPage(fnGetColumnId("index32"), RClusterIndex(0, 0), sealedPage);
for (unsigned i = 0; i < sealedPage.fSize; ++i) {
printf("0x%02x ", reinterpret_cast<const char *>(sealedPage.fBuffer)[i]);
}
printf("\n");
unsigned char expIndex[] = {0x01, 0x07, 0x15, 0x00, 0x61, 0x00, 0x02, 0x00};
EXPECT_EQ(memcmp(sealedPage.fBuffer, expIndex, sizeof(expIndex)), 0);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment