Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:10:37

0001 /// \file ROOT/RColumnElement.hxx
0002 /// \ingroup NTuple ROOT7
0003 /// \author Jakob Blomer <jblomer@cern.ch>
0004 /// \date 2018-10-09
0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
0006 /// is welcome!
0007 
0008 /*************************************************************************
0009  * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.               *
0010  * All rights reserved.                                                  *
0011  *                                                                       *
0012  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0013  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0014  *************************************************************************/
0015 
0016 #ifndef ROOT7_RColumnElement
0017 #define ROOT7_RColumnElement
0018 
0019 #include <ROOT/RColumnModel.hxx>
0020 #include <ROOT/RConfig.hxx>
0021 #include <ROOT/RError.hxx>
0022 #include <ROOT/RFloat16.hxx>
0023 #include <ROOT/RNTupleUtil.hxx>
0024 
0025 #include <Byteswap.h>
0026 #include <TError.h>
0027 
0028 #include <cstring> // for memcpy
0029 #include <cstddef> // for std::byte
0030 #include <cstdint>
0031 #include <memory>
0032 #include <string>
0033 #include <type_traits>
0034 #include <typeinfo>
0035 #include <utility>
0036 
0037 #ifndef R__LITTLE_ENDIAN
0038 #ifdef R__BYTESWAP
0039 // `R__BYTESWAP` is defined in RConfig.hxx for little-endian architectures; undefined otherwise
0040 #define R__LITTLE_ENDIAN 1
0041 #else
0042 #define R__LITTLE_ENDIAN 0
0043 #endif
0044 #endif /* R__LITTLE_ENDIAN */
0045 
0046 namespace {
0047 
0048 // In this namespace, common routines are defined for element packing and unpacking of ints and floats.
0049 // The following conversions and encodings exist:
0050 //
0051 //   - Byteswap:  on big endian machines, ints and floats are byte-swapped to the little endian on-disk format
0052 //   - Cast:      in-memory values can be stored in narrower on-disk columns.  Currently without bounds checks.
0053 //                For instance, for Double32_t, an in-memory double value is stored as a float on disk.
0054 //   - Split:     rearranges the bytes of an array of elements such that all the first bytes are stored first,
0055 //                followed by all the second bytes, etc. This often clusters similar values, e.g. all the zero bytes
0056 //                for arrays of small integers.
0057 //   - Delta:     Delta encoding stores on disk the delta to the previous element.  This is useful for offsets,
0058 //                because it transforms potentially large offset values into small deltas, which are then better
0059 //                suited for split encoding.
0060 //   - Zigzag:    Zigzag encoding is used on signed integers only. It maps x to 2x if x is positive and to -(2x+1) if
0061 //                x is negative. For series of positive and negative values of small absolute value, it will produce
0062 //                a bit pattern that is favorable for split encoding.
0063 //
0064 // Encodings/conversions can be fused:
0065 //
0066 //  - Delta/Zigzag + Splitting (there is no only-delta/zigzag encoding)
0067 //  - (Delta/Zigzag + ) Splitting + Casting
0068 //  - Everything + Byteswap
0069 
0070 /// \brief Copy and byteswap `count` elements of size `N` from `source` to `destination`.
0071 ///
0072 /// Used on big-endian architectures for packing/unpacking elements whose column type requires
0073 /// a little-endian on-disk representation.
0074 template <std::size_t N>
0075 void CopyBswap(void *destination, const void *source, std::size_t count)
0076 {
0077    auto dst = reinterpret_cast<typename RByteSwap<N>::value_type *>(destination);
0078    auto src = reinterpret_cast<const typename RByteSwap<N>::value_type *>(source);
0079    for (std::size_t i = 0; i < count; ++i) {
0080       dst[i] = RByteSwap<N>::bswap(src[i]);
0081    }
0082 }
0083 
0084 /// Casts T to one of the ints used in RByteSwap and back to its original type, which may be float or double
0085 #if R__LITTLE_ENDIAN == 0
0086 template <typename T>
0087 void ByteSwapIfNecessary(T &value)
0088 {
0089    constexpr auto N = sizeof(T);
0090    using bswap_value_type = typename RByteSwap<N>::value_type;
0091    void *valuePtr = &value;
0092    auto swapped = RByteSwap<N>::bswap(*reinterpret_cast<bswap_value_type *>(valuePtr));
0093    *reinterpret_cast<bswap_value_type *>(valuePtr) = swapped;
0094 }
0095 #else
0096 #define ByteSwapIfNecessary(x) ((void)0)
0097 #endif
0098 
0099 /// \brief Pack `count` elements into narrower (or wider) type
0100 ///
0101 /// Used to convert in-memory elements to smaller column types of comatible types
0102 /// (e.g., double to float, int64 to int32). Takes care of byte swap if necessary.
0103 template <typename DestT, typename SourceT>
0104 static void CastPack(void *destination, const void *source, std::size_t count)
0105 {
0106    static_assert(std::is_convertible_v<SourceT, DestT>);
0107    auto dst = reinterpret_cast<DestT *>(destination);
0108    auto src = reinterpret_cast<const SourceT *>(source);
0109    for (std::size_t i = 0; i < count; ++i) {
0110       dst[i] = src[i];
0111       ByteSwapIfNecessary(dst[i]);
0112    }
0113 }
0114 
0115 /// \brief Unpack `count` on-disk elements into wider (or narrower) in-memory array
0116 ///
0117 /// Used to convert on-disk elements to larger C++ types of comatible types
0118 /// (e.g., float to double, int32 to int64). Takes care of byte swap if necessary.
0119 template <typename DestT, typename SourceT>
0120 static void CastUnpack(void *destination, const void *source, std::size_t count)
0121 {
0122    auto dst = reinterpret_cast<DestT *>(destination);
0123    auto src = reinterpret_cast<const SourceT *>(source);
0124    for (std::size_t i = 0; i < count; ++i) {
0125       SourceT val = src[i];
0126       ByteSwapIfNecessary(val);
0127       dst[i] = val;
0128    }
0129 }
0130 
0131 /// \brief Split encoding of elements, possibly into narrower column
0132 ///
0133 /// Used to first cast and then split-encode in-memory values to the on-disk column. Swap bytes if necessary.
0134 template <typename DestT, typename SourceT>
0135 static void CastSplitPack(void *destination, const void *source, std::size_t count)
0136 {
0137    constexpr std::size_t N = sizeof(DestT);
0138    auto splitArray = reinterpret_cast<char *>(destination);
0139    auto src = reinterpret_cast<const SourceT *>(source);
0140    for (std::size_t i = 0; i < count; ++i) {
0141       DestT val = src[i];
0142       ByteSwapIfNecessary(val);
0143       for (std::size_t b = 0; b < N; ++b) {
0144          splitArray[b * count + i] = reinterpret_cast<const char *>(&val)[b];
0145       }
0146    }
0147 }
0148 
0149 /// \brief Reverse split encoding of elements
0150 ///
0151 /// Used to first unsplit a column, possibly storing elements in wider C++ types. Swaps bytes if necessary
0152 template <typename DestT, typename SourceT>
0153 static void CastSplitUnpack(void *destination, const void *source, std::size_t count)
0154 {
0155    constexpr std::size_t N = sizeof(SourceT);
0156    auto dst = reinterpret_cast<DestT *>(destination);
0157    auto splitArray = reinterpret_cast<const char *>(source);
0158    for (std::size_t i = 0; i < count; ++i) {
0159       SourceT val = 0;
0160       for (std::size_t b = 0; b < N; ++b) {
0161          reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
0162       }
0163       ByteSwapIfNecessary(val);
0164       dst[i] = val;
0165    }
0166 }
0167 
0168 /// \brief Packing of columns with delta + split encoding
0169 ///
0170 /// Apply split encoding to delta-encoded values, currently used only for index columns
0171 template <typename DestT, typename SourceT>
0172 static void CastDeltaSplitPack(void *destination, const void *source, std::size_t count)
0173 {
0174    constexpr std::size_t N = sizeof(DestT);
0175    auto src = reinterpret_cast<const SourceT *>(source);
0176    auto splitArray = reinterpret_cast<char *>(destination);
0177    for (std::size_t i = 0; i < count; ++i) {
0178       DestT val = (i == 0) ? src[0] : src[i] - src[i - 1];
0179       ByteSwapIfNecessary(val);
0180       for (std::size_t b = 0; b < N; ++b) {
0181          splitArray[b * count + i] = reinterpret_cast<char *>(&val)[b];
0182       }
0183    }
0184 }
0185 
0186 /// \brief Unsplit and unwind delta encoding
0187 ///
0188 /// Unsplit a column and reverse the delta encoding, currently used only for index columns
0189 template <typename DestT, typename SourceT>
0190 static void CastDeltaSplitUnpack(void *destination, const void *source, std::size_t count)
0191 {
0192    constexpr std::size_t N = sizeof(SourceT);
0193    auto splitArray = reinterpret_cast<const char *>(source);
0194    auto dst = reinterpret_cast<DestT *>(destination);
0195    for (std::size_t i = 0; i < count; ++i) {
0196       SourceT val = 0;
0197       for (std::size_t b = 0; b < N; ++b) {
0198          reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
0199       }
0200       ByteSwapIfNecessary(val);
0201       dst[i] = (i == 0) ? val : dst[i - 1] + val;
0202    }
0203 }
0204 
0205 /// \brief Packing of columns with zigzag + split encoding
0206 ///
0207 /// Apply split encoding to zigzag-encoded values, used for signed integers
0208 template <typename DestT, typename SourceT>
0209 static void CastZigzagSplitPack(void *destination, const void *source, std::size_t count)
0210 {
0211    using UDestT = std::make_unsigned_t<DestT>;
0212    constexpr std::size_t kNBitsDestT = sizeof(DestT) * 8;
0213    constexpr std::size_t N = sizeof(DestT);
0214    auto src = reinterpret_cast<const SourceT *>(source);
0215    auto splitArray = reinterpret_cast<char *>(destination);
0216    for (std::size_t i = 0; i < count; ++i) {
0217       UDestT val = (static_cast<DestT>(src[i]) << 1) ^ (static_cast<DestT>(src[i]) >> (kNBitsDestT - 1));
0218       ByteSwapIfNecessary(val);
0219       for (std::size_t b = 0; b < N; ++b) {
0220          splitArray[b * count + i] = reinterpret_cast<char *>(&val)[b];
0221       }
0222    }
0223 }
0224 
0225 /// \brief Unsplit and unwind zigzag encoding
0226 ///
0227 /// Unsplit a column and reverse the zigzag encoding, used for signed integer columns
0228 template <typename DestT, typename SourceT>
0229 static void CastZigzagSplitUnpack(void *destination, const void *source, std::size_t count)
0230 {
0231    using USourceT = std::make_unsigned_t<SourceT>;
0232    constexpr std::size_t N = sizeof(SourceT);
0233    auto splitArray = reinterpret_cast<const char *>(source);
0234    auto dst = reinterpret_cast<DestT *>(destination);
0235    for (std::size_t i = 0; i < count; ++i) {
0236       USourceT val = 0;
0237       for (std::size_t b = 0; b < N; ++b) {
0238          reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
0239       }
0240       ByteSwapIfNecessary(val);
0241       dst[i] = static_cast<SourceT>((val >> 1) ^ -(static_cast<SourceT>(val) & 1));
0242    }
0243 }
0244 
0245 } // anonymous namespace
0246 
0247 namespace ROOT {
0248 namespace Experimental {
0249 namespace Internal {
0250 
0251 // clang-format off
0252 /**
0253 \class ROOT::Experimental::Internal::RColumnElementBase
0254 \ingroup NTuple
0255 \brief A column element encapsulates the translation between basic C++ types and their column representation.
0256 
0257 Usually the on-disk element should map bitwise to the in-memory element. Sometimes that's not the case
0258 though, for instance on big endian platforms or for bools.
0259 
0260 There is a template specialization for every valid pair of C++ type and column representation.
0261 These specialized child classes are responsible for overriding `Pack()` / `Unpack()` for packing / unpacking elements
0262 as appropriate.
0263 */
0264 // clang-format on
0265 class RColumnElementBase {
0266 protected:
0267    /// Size of the C++ value that corresponds to the on-disk element
0268    std::size_t fSize;
0269    std::size_t fBitsOnStorage;
0270 
0271    explicit RColumnElementBase(std::size_t size, std::size_t bitsOnStorage = 0)
0272       : fSize(size), fBitsOnStorage(bitsOnStorage ? bitsOnStorage : 8 * size)
0273    {
0274    }
0275 
0276 public:
0277    RColumnElementBase(const RColumnElementBase& other) = default;
0278    RColumnElementBase(RColumnElementBase&& other) = default;
0279    RColumnElementBase& operator =(const RColumnElementBase& other) = delete;
0280    RColumnElementBase& operator =(RColumnElementBase&& other) = default;
0281    virtual ~RColumnElementBase() = default;
0282 
0283    /// If CppT == void, use the default C++ type for the given column type
0284    template <typename CppT = void>
0285    static std::unique_ptr<RColumnElementBase> Generate(EColumnType type);
0286    static std::size_t GetBitsOnStorage(EColumnType type);
0287    static std::string GetTypeName(EColumnType type);
0288 
0289    /// Derived, typed classes tell whether the on-storage layout is bitwise identical to the memory layout
0290    virtual bool IsMappable() const
0291    {
0292       R__ASSERT(false);
0293       return false;
0294    }
0295 
0296    /// If the on-storage layout and the in-memory layout differ, packing creates an on-disk page from an in-memory page
0297    virtual void Pack(void *destination, void *source, std::size_t count) const
0298    {
0299       std::memcpy(destination, source, count);
0300    }
0301 
0302    /// If the on-storage layout and the in-memory layout differ, unpacking creates a memory page from an on-storage page
0303    virtual void Unpack(void *destination, void *source, std::size_t count) const
0304    {
0305       std::memcpy(destination, source, count);
0306    }
0307 
0308    std::size_t GetSize() const { return fSize; }
0309    std::size_t GetBitsOnStorage() const { return fBitsOnStorage; }
0310    std::size_t GetPackedSize(std::size_t nElements = 1U) const { return (nElements * fBitsOnStorage + 7) / 8; }
0311 }; // class RColumnElementBase
0312 
0313 /**
0314  * Base class for columns whose on-storage representation is little-endian.
0315  * The implementation of `Pack` and `Unpack` takes care of byteswap if the memory page is big-endian.
0316  */
0317 template <typename CppT>
0318 class RColumnElementLE : public RColumnElementBase {
0319 protected:
0320    explicit RColumnElementLE(std::size_t size, std::size_t bitsOnStorage) : RColumnElementBase(size, bitsOnStorage) {}
0321 
0322 public:
0323    static constexpr bool kIsMappable = (R__LITTLE_ENDIAN == 1);
0324 
0325    void Pack(void *dst, void *src, std::size_t count) const final
0326    {
0327 #if R__LITTLE_ENDIAN == 1
0328       RColumnElementBase::Pack(dst, src, count);
0329 #else
0330       CopyBswap<sizeof(CppT)>(dst, src, count);
0331 #endif
0332    }
0333    void Unpack(void *dst, void *src, std::size_t count) const final
0334    {
0335 #if R__LITTLE_ENDIAN == 1
0336       RColumnElementBase::Unpack(dst, src, count);
0337 #else
0338       CopyBswap<sizeof(CppT)>(dst, src, count);
0339 #endif
0340    }
0341 }; // class RColumnElementLE
0342 
0343 /**
0344  * Base class for columns storing elements of wider in-memory types,
0345  * such as 64bit in-memory offsets to Index32 columns.
0346  */
0347 template <typename CppT, typename NarrowT>
0348 class RColumnElementCastLE : public RColumnElementBase {
0349 protected:
0350    explicit RColumnElementCastLE(std::size_t size, std::size_t bitsOnStorage) : RColumnElementBase(size, bitsOnStorage)
0351    {
0352    }
0353 
0354 public:
0355    static constexpr bool kIsMappable = false;
0356 
0357    void Pack(void *dst, void *src, std::size_t count) const final { CastPack<NarrowT, CppT>(dst, src, count); }
0358    void Unpack(void *dst, void *src, std::size_t count) const final { CastUnpack<CppT, NarrowT>(dst, src, count); }
0359 }; // class RColumnElementCastLE
0360 
0361 /**
0362  * Base class for split columns whose on-storage representation is little-endian.
0363  * The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
0364  * As part of the splitting, can also narrow down the type to NarrowT.
0365  */
0366 template <typename CppT, typename NarrowT>
0367 class RColumnElementSplitLE : public RColumnElementBase {
0368 protected:
0369    explicit RColumnElementSplitLE(std::size_t size, std::size_t bitsOnStorage) : RColumnElementBase(size, bitsOnStorage)
0370    {
0371    }
0372 
0373 public:
0374    static constexpr bool kIsMappable = false;
0375 
0376    void Pack(void *dst, void *src, std::size_t count) const final { CastSplitPack<NarrowT, CppT>(dst, src, count); }
0377    void Unpack(void *dst, void *src, std::size_t count) const final { CastSplitUnpack<CppT, NarrowT>(dst, src, count); }
0378 }; // class RColumnElementSplitLE
0379 
0380 /**
0381  * Base class for delta + split columns (index columns) whose on-storage representation is little-endian.
0382  * The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
0383  * As part of the encoding, can also narrow down the type to NarrowT.
0384  */
0385 template <typename CppT, typename NarrowT>
0386 class RColumnElementDeltaSplitLE : public RColumnElementBase {
0387 protected:
0388    explicit RColumnElementDeltaSplitLE(std::size_t size, std::size_t bitsOnStorage)
0389       : RColumnElementBase(size, bitsOnStorage)
0390    {
0391    }
0392 
0393 public:
0394    static constexpr bool kIsMappable = false;
0395 
0396    void Pack(void *dst, void *src, std::size_t count) const final
0397    {
0398       CastDeltaSplitPack<NarrowT, CppT>(dst, src, count);
0399    }
0400    void Unpack(void *dst, void *src, std::size_t count) const final
0401    {
0402       CastDeltaSplitUnpack<CppT, NarrowT>(dst, src, count);
0403    }
0404 }; // class RColumnElementDeltaSplitLE
0405 
0406 /**
0407  * Base class for zigzag + split columns (signed integer columns) whose on-storage representation is little-endian.
0408  * The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
0409  * The NarrowT target type should be an signed integer, which can be smaller than the CppT source type.
0410  */
0411 template <typename CppT, typename NarrowT>
0412 class RColumnElementZigzagSplitLE : public RColumnElementBase {
0413 protected:
0414    explicit RColumnElementZigzagSplitLE(std::size_t size, std::size_t bitsOnStorage)
0415       : RColumnElementBase(size, bitsOnStorage)
0416    {
0417    }
0418 
0419 public:
0420    static constexpr bool kIsMappable = false;
0421 
0422    void Pack(void *dst, void *src, std::size_t count) const final
0423    {
0424       CastZigzagSplitPack<NarrowT, CppT>(dst, src, count);
0425    }
0426    void Unpack(void *dst, void *src, std::size_t count) const final
0427    {
0428       CastZigzagSplitUnpack<CppT, NarrowT>(dst, src, count);
0429    }
0430 }; // class RColumnElementZigzagSplitLE
0431 
0432 ////////////////////////////////////////////////////////////////////////////////
0433 // Pairs of C++ type and column type, like float and EColumnType::kReal32
0434 ////////////////////////////////////////////////////////////////////////////////
0435 
0436 ////////////////////////////////////////////////////////////////////////////////
0437 // Part 1: C++ type --> unknown column type
0438 ////////////////////////////////////////////////////////////////////////////////
0439 
0440 template <typename CppT, EColumnType ColumnT = EColumnType::kUnknown>
0441 class RColumnElement : public RColumnElementBase {
0442 public:
0443    RColumnElement() : RColumnElementBase(sizeof(CppT))
0444    {
0445       throw RException(R__FAIL(std::string("internal error: no column mapping for this C++ type: ") +
0446                                typeid(CppT).name() + " --> " + GetTypeName(ColumnT)));
0447    }
0448 };
0449 
0450 template <>
0451 class RColumnElement<bool, EColumnType::kUnknown> : public RColumnElementBase {
0452 public:
0453    static constexpr std::size_t kSize = sizeof(bool);
0454    RColumnElement() : RColumnElementBase(kSize) {}
0455 };
0456 
0457 template <>
0458 class RColumnElement<std::byte, EColumnType::kUnknown> : public RColumnElementBase {
0459 public:
0460    static constexpr std::size_t kSize = sizeof(std::byte);
0461    RColumnElement() : RColumnElementBase(kSize) {}
0462 };
0463 
0464 template <>
0465 class RColumnElement<char, EColumnType::kUnknown> : public RColumnElementBase {
0466 public:
0467    static constexpr std::size_t kSize = sizeof(char);
0468    RColumnElement() : RColumnElementBase(kSize) {}
0469 };
0470 
0471 template <>
0472 class RColumnElement<std::int8_t, EColumnType::kUnknown> : public RColumnElementBase {
0473 public:
0474    static constexpr std::size_t kSize = sizeof(std::int8_t);
0475    RColumnElement() : RColumnElementBase(kSize) {}
0476 };
0477 
0478 template <>
0479 class RColumnElement<std::uint8_t, EColumnType::kUnknown> : public RColumnElementBase {
0480 public:
0481    static constexpr std::size_t kSize = sizeof(std::uint8_t);
0482    RColumnElement() : RColumnElementBase(kSize) {}
0483 };
0484 
0485 template <>
0486 class RColumnElement<std::int16_t, EColumnType::kUnknown> : public RColumnElementBase {
0487 public:
0488    static constexpr std::size_t kSize = sizeof(std::int16_t);
0489    RColumnElement() : RColumnElementBase(kSize) {}
0490 };
0491 
0492 template <>
0493 class RColumnElement<std::uint16_t, EColumnType::kUnknown> : public RColumnElementBase {
0494 public:
0495    static constexpr std::size_t kSize = sizeof(std::uint16_t);
0496    RColumnElement() : RColumnElementBase(kSize) {}
0497 };
0498 
0499 template <>
0500 class RColumnElement<std::int32_t, EColumnType::kUnknown> : public RColumnElementBase {
0501 public:
0502    static constexpr std::size_t kSize = sizeof(std::int32_t);
0503    RColumnElement() : RColumnElementBase(kSize) {}
0504 };
0505 
0506 template <>
0507 class RColumnElement<std::uint32_t, EColumnType::kUnknown> : public RColumnElementBase {
0508 public:
0509    static constexpr std::size_t kSize = sizeof(std::uint32_t);
0510    RColumnElement() : RColumnElementBase(kSize) {}
0511 };
0512 
0513 template <>
0514 class RColumnElement<std::int64_t, EColumnType::kUnknown> : public RColumnElementBase {
0515 public:
0516    static constexpr std::size_t kSize = sizeof(std::int64_t);
0517    RColumnElement() : RColumnElementBase(kSize) {}
0518 };
0519 
0520 template <>
0521 class RColumnElement<std::uint64_t, EColumnType::kUnknown> : public RColumnElementBase {
0522 public:
0523    static constexpr std::size_t kSize = sizeof(std::uint64_t);
0524    RColumnElement() : RColumnElementBase(kSize) {}
0525 };
0526 
0527 template <>
0528 class RColumnElement<float, EColumnType::kUnknown> : public RColumnElementBase {
0529 public:
0530    static constexpr std::size_t kSize = sizeof(float);
0531    RColumnElement() : RColumnElementBase(kSize) {}
0532 };
0533 
0534 template <>
0535 class RColumnElement<double, EColumnType::kUnknown> : public RColumnElementBase {
0536 public:
0537    static constexpr std::size_t kSize = sizeof(double);
0538    RColumnElement() : RColumnElementBase(kSize) {}
0539 };
0540 
0541 template <>
0542 class RColumnElement<ClusterSize_t, EColumnType::kUnknown> : public RColumnElementBase {
0543 public:
0544    static constexpr std::size_t kSize = sizeof(ClusterSize_t);
0545    RColumnElement() : RColumnElementBase(kSize) {}
0546 };
0547 
0548 template <>
0549 class RColumnElement<RColumnSwitch, EColumnType::kUnknown> : public RColumnElementBase {
0550 public:
0551    static constexpr std::size_t kSize = sizeof(RColumnSwitch);
0552    RColumnElement() : RColumnElementBase(kSize) {}
0553 };
0554 
0555 ////////////////////////////////////////////////////////////////////////////////
0556 // Part 2: C++ type --> supported column representations,
0557 //         ordered by C++ type
0558 ////////////////////////////////////////////////////////////////////////////////
0559 
0560 template <>
0561 class RColumnElement<RColumnSwitch, EColumnType::kSwitch> : public RColumnElementBase {
0562 private:
0563    struct RSwitchElement {
0564       std::uint64_t fIndex;
0565       std::uint32_t fTag;
0566    };
0567 
0568 public:
0569    static constexpr bool kIsMappable = false;
0570    static constexpr std::size_t kSize = sizeof(ROOT::Experimental::RColumnSwitch);
0571    static constexpr std::size_t kBitsOnStorage = 96;
0572    RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
0573    bool IsMappable() const final { return kIsMappable; }
0574 
0575    void Pack(void *dst, void *src, std::size_t count) const final
0576    {
0577       auto srcArray = reinterpret_cast<ROOT::Experimental::RColumnSwitch *>(src);
0578       auto dstArray = reinterpret_cast<unsigned char *>(dst);
0579       for (std::size_t i = 0; i < count; ++i) {
0580          RSwitchElement element{srcArray[i].GetIndex(), srcArray[i].GetTag()};
0581 #if R__LITTLE_ENDIAN == 0
0582          element.fIndex = RByteSwap<8>::bswap(element.fIndex);
0583          element.fTag = RByteSwap<8>::bswap(element.fTag);
0584 #endif
0585          memcpy(dstArray + i * 12, &element, 12);
0586       }
0587    }
0588 
0589    void Unpack(void *dst, void *src, std::size_t count) const final
0590    {
0591       auto srcArray = reinterpret_cast<unsigned char *>(src);
0592       auto dstArray = reinterpret_cast<ROOT::Experimental::RColumnSwitch *>(dst);
0593       for (std::size_t i = 0; i < count; ++i) {
0594          RSwitchElement element;
0595          memcpy(&element, srcArray + i * 12, 12);
0596 #if R__LITTLE_ENDIAN == 0
0597          element.fIndex = RByteSwap<8>::bswap(element.fIndex);
0598          element.fTag = RByteSwap<8>::bswap(element.fTag);
0599 #endif
0600          dstArray[i] = ROOT::Experimental::RColumnSwitch(ClusterSize_t{element.fIndex}, element.fTag);
0601       }
0602    }
0603 };
0604 
0605 template <>
0606 class RColumnElement<bool, EColumnType::kBit> : public RColumnElementBase {
0607 public:
0608    static constexpr bool kIsMappable = false;
0609    static constexpr std::size_t kSize = sizeof(bool);
0610    static constexpr std::size_t kBitsOnStorage = 1;
0611    RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
0612    bool IsMappable() const final { return kIsMappable; }
0613 
0614    void Pack(void *dst, void *src, std::size_t count) const final;
0615    void Unpack(void *dst, void *src, std::size_t count) const final;
0616 };
0617 
0618 template <>
0619 class RColumnElement<float, EColumnType::kReal16> : public RColumnElementBase {
0620 public:
0621    static constexpr bool kIsMappable = false;
0622    static constexpr std::size_t kSize = sizeof(float);
0623    static constexpr std::size_t kBitsOnStorage = 16;
0624    RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
0625    bool IsMappable() const final { return kIsMappable; }
0626 
0627    void Pack(void *dst, void *src, std::size_t count) const final
0628    {
0629       float *floatArray = reinterpret_cast<float *>(src);
0630       std::uint16_t *uint16Array = reinterpret_cast<std::uint16_t *>(dst);
0631 
0632       for (std::size_t i = 0; i < count; ++i) {
0633          uint16Array[i] = FloatToHalf(floatArray[i]);
0634          ByteSwapIfNecessary(uint16Array[i]);
0635       }
0636    }
0637 
0638    void Unpack(void *dst, void *src, std::size_t count) const final
0639    {
0640       float *floatArray = reinterpret_cast<float *>(dst);
0641       std::uint16_t *uint16Array = reinterpret_cast<std::uint16_t *>(src);
0642 
0643       for (std::size_t i = 0; i < count; ++i) {
0644          ByteSwapIfNecessary(floatArray[i]);
0645          floatArray[i] = HalfToFloat(uint16Array[i]);
0646       }
0647    }
0648 };
0649 
0650 #define __RCOLUMNELEMENT_SPEC_BODY(CppT, BaseT, BitsOnStorage)  \
0651    static constexpr std::size_t kSize = sizeof(CppT);           \
0652    static constexpr std::size_t kBitsOnStorage = BitsOnStorage; \
0653    RColumnElement() : BaseT(kSize, kBitsOnStorage) {}           \
0654    bool IsMappable() const final                                \
0655    {                                                            \
0656       return kIsMappable;                                       \
0657    }
0658 /// These macros are used to declare `RColumnElement` template specializations below.  Additional arguments can be used
0659 /// to forward template parameters to the base class, e.g.
0660 /// ```
0661 /// DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt32, 32,
0662 ///                             RColumnElementCastLE, <std::int64_t, std::int32_t>);
0663 /// ```
0664 #define DECLARE_RCOLUMNELEMENT_SPEC(CppT, ColumnT, BitsOnStorage, BaseT, ...) \
0665    template <>                                                                \
0666    class RColumnElement<CppT, ColumnT> : public BaseT __VA_ARGS__ {           \
0667    public:                                                                    \
0668       __RCOLUMNELEMENT_SPEC_BODY(CppT, BaseT, BitsOnStorage)                  \
0669    }
0670 #define DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(CppT, ColumnT, BitsOnStorage)  \
0671    template <>                                                            \
0672    class RColumnElement<CppT, ColumnT> : public RColumnElementBase {      \
0673    public:                                                                \
0674       static constexpr bool kIsMappable = true;                           \
0675       __RCOLUMNELEMENT_SPEC_BODY(CppT, RColumnElementBase, BitsOnStorage) \
0676    }
0677 
0678 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::byte, EColumnType::kByte, 8);
0679 
0680 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(char, EColumnType::kByte, 8);
0681 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(char, EColumnType::kChar, 8);
0682 
0683 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::int8_t, EColumnType::kInt8, 8);
0684 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::int8_t, EColumnType::kUInt8, 8);
0685 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::int8_t, EColumnType::kByte, 8);
0686 
0687 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::uint8_t, EColumnType::kUInt8, 8);
0688 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::uint8_t, EColumnType::kInt8, 8);
0689 DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::uint8_t, EColumnType::kByte, 8);
0690 
0691 DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kInt16, 16, RColumnElementLE, <std::int16_t>);
0692 DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kUInt16, 16, RColumnElementLE, <std::int16_t>);
0693 DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
0694                             <std::int16_t, std::int16_t>);
0695 DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
0696                             <std::int16_t, std::uint16_t>);
0697 
0698 DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kUInt16, 16, RColumnElementLE, <std::uint16_t>);
0699 DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kInt16, 16, RColumnElementLE, <std::uint16_t>);
0700 DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
0701                             <std::uint16_t, std::uint16_t>);
0702 DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
0703                             <std::uint16_t, std::int16_t>);
0704 
0705 DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kInt32, 32, RColumnElementLE, <std::int32_t>);
0706 DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kUInt32, 32, RColumnElementLE, <std::int32_t>);
0707 DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
0708                             <std::int32_t, std::int32_t>);
0709 DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
0710                             <std::int32_t, std::uint32_t>);
0711 
0712 DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kUInt32, 32, RColumnElementLE, <std::uint32_t>);
0713 DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kInt32, 32, RColumnElementLE, <std::uint32_t>);
0714 DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
0715                             <std::uint32_t, std::uint32_t>);
0716 DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
0717                             <std::uint32_t, std::int32_t>);
0718 
0719 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt64, 64, RColumnElementLE, <std::int64_t>);
0720 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kUInt64, 64, RColumnElementLE, <std::int64_t>);
0721 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
0722                             <std::int64_t, std::int64_t>);
0723 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
0724                             <std::int64_t, std::uint64_t>);
0725 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt32, 32, RColumnElementCastLE, <std::int64_t, std::int32_t>);
0726 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kUInt32, 32, RColumnElementCastLE,
0727                             <std::int64_t, std::uint32_t>);
0728 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
0729                             <std::int64_t, std::int32_t>);
0730 DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
0731                             <std::int64_t, std::uint32_t>);
0732 
0733 DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kUInt64, 64, RColumnElementLE, <std::uint64_t>);
0734 DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kInt64, 64, RColumnElementLE, <std::uint64_t>);
0735 DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
0736                             <std::uint64_t, std::uint64_t>);
0737 DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
0738                             <std::uint64_t, std::int64_t>);
0739 
0740 DECLARE_RCOLUMNELEMENT_SPEC(float, EColumnType::kReal32, 32, RColumnElementLE, <float>);
0741 DECLARE_RCOLUMNELEMENT_SPEC(float, EColumnType::kSplitReal32, 32, RColumnElementSplitLE, <float, float>);
0742 
0743 DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kReal64, 64, RColumnElementLE, <double>);
0744 DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kSplitReal64, 64, RColumnElementSplitLE, <double, double>);
0745 DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kReal32, 32, RColumnElementCastLE, <double, float>);
0746 DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kSplitReal32, 32, RColumnElementSplitLE, <double, float>);
0747 
0748 DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kIndex64, 64, RColumnElementLE, <std::uint64_t>);
0749 DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kIndex32, 32, RColumnElementCastLE,
0750                             <std::uint64_t, std::uint32_t>);
0751 DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kSplitIndex64, 64, RColumnElementDeltaSplitLE,
0752                             <std::uint64_t, std::uint64_t>);
0753 DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kSplitIndex32, 32, RColumnElementDeltaSplitLE,
0754                             <std::uint64_t, std::uint32_t>);
0755 
0756 template <typename CppT>
0757 std::unique_ptr<RColumnElementBase> RColumnElementBase::Generate(EColumnType type)
0758 {
0759    switch (type) {
0760    case EColumnType::kIndex64: return std::make_unique<RColumnElement<CppT, EColumnType::kIndex64>>();
0761    case EColumnType::kIndex32: return std::make_unique<RColumnElement<CppT, EColumnType::kIndex32>>();
0762    case EColumnType::kSwitch: return std::make_unique<RColumnElement<CppT, EColumnType::kSwitch>>();
0763    case EColumnType::kByte: return std::make_unique<RColumnElement<CppT, EColumnType::kByte>>();
0764    case EColumnType::kChar: return std::make_unique<RColumnElement<CppT, EColumnType::kChar>>();
0765    case EColumnType::kBit: return std::make_unique<RColumnElement<CppT, EColumnType::kBit>>();
0766    case EColumnType::kReal64: return std::make_unique<RColumnElement<CppT, EColumnType::kReal64>>();
0767    case EColumnType::kReal32: return std::make_unique<RColumnElement<CppT, EColumnType::kReal32>>();
0768    case EColumnType::kReal16: return std::make_unique<RColumnElement<CppT, EColumnType::kReal16>>();
0769    case EColumnType::kInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kInt64>>();
0770    case EColumnType::kUInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt64>>();
0771    case EColumnType::kInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kInt32>>();
0772    case EColumnType::kUInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt32>>();
0773    case EColumnType::kInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kInt16>>();
0774    case EColumnType::kUInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt16>>();
0775    case EColumnType::kInt8: return std::make_unique<RColumnElement<CppT, EColumnType::kInt8>>();
0776    case EColumnType::kUInt8: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt8>>();
0777    case EColumnType::kSplitIndex64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitIndex64>>();
0778    case EColumnType::kSplitIndex32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitIndex32>>();
0779    case EColumnType::kSplitReal64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitReal64>>();
0780    case EColumnType::kSplitReal32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitReal32>>();
0781    case EColumnType::kSplitInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitInt64>>();
0782    case EColumnType::kSplitUInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitUInt64>>();
0783    case EColumnType::kSplitInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitInt32>>();
0784    case EColumnType::kSplitUInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitUInt32>>();
0785    case EColumnType::kSplitInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitInt16>>();
0786    case EColumnType::kSplitUInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitUInt16>>();
0787    default: R__ASSERT(false);
0788    }
0789    // never here
0790    return nullptr;
0791 }
0792 
0793 template <>
0794 std::unique_ptr<RColumnElementBase> RColumnElementBase::Generate<void>(EColumnType type);
0795 
0796 } // namespace Internal
0797 } // namespace Experimental
0798 } // namespace ROOT
0799 
0800 #endif