Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RColumnElement.hxx
Go to the documentation of this file.
1// RColumnElement concrete implementations
2//
3// Note that this file is in the src directory and not in the inc directory because we need the ability
4// to override R__LITTLE_ENDIAN for testing purposes.
5// This is not a particularly clean or correct solution, as the tests that do this will end up with two different
6// definitions of some RColumnElements, so we might want to change this mechanism in the future. In any case, these
7// definitions are implementation details and should not be exposed to a public interface.
8
10#include <ROOT/RNTupleUtil.hxx>
11#include <ROOT/RConfig.hxx>
12#include <ROOT/RError.hxx>
13#include <Byteswap.h>
14
15#include <bitset>
16#include <cassert>
17#include <limits>
18#include <type_traits>
19
20// NOTE: some tests might define R__LITTLE_ENDIAN to simulate a different-endianness machine
21#ifndef R__LITTLE_ENDIAN
22#ifdef R__BYTESWAP
23// `R__BYTESWAP` is defined in RConfig.hxx for little-endian architectures; undefined otherwise
24#define R__LITTLE_ENDIAN 1
25#else
26#define R__LITTLE_ENDIAN 0
27#endif
28#endif /* R__LITTLE_ENDIAN */
29
31
32using Word_t = std::uintmax_t;
33inline constexpr std::size_t kBitsPerWord = sizeof(Word_t) * 8;
34
35/// Returns the minimum safe size (in bytes) of a buffer that is intended to be used as a destination for PackBits
36/// or a source for UnpackBits.
37/// Passing a buffer that's less than this size will cause invalid memory reads and writes.
38constexpr std::size_t MinBufSize(std::size_t count, std::size_t nDstBits)
39{
40 return (count * nDstBits + 7) / 8;
41}
42
43/// Tightly packs `count` items of size `sizeofSrc` contained in `src` into `dst` using `nDstBits` per item.
44/// It must be `0 < sizeofSrc <= 8` and `0 < nDstBits <= sizeofSrc * 8`.
45/// The extra least significant bits are dropped (assuming LE ordering of the items in `src`).
46/// Note that this function doesn't do any byte reordering for you.
47/// IMPORTANT: the size of `dst` must be at least `MinBufSize(count, nBitBits)`
48void PackBits(void *dst, const void *src, std::size_t count, std::size_t sizeofSrc, std::size_t nDstBits);
49
50/// Undoes the effect of `PackBits`. The bits that were truncated in the packed representation
51/// are filled with zeroes.
52/// `src` must be at least `MinBufSize(count, nDstBits)` bytes long.
53/// `dst` must be at least `count * sizeofDst` bytes long.
54void UnpackBits(void *dst, const void *src, std::size_t count, std::size_t sizeofDst, std::size_t nSrcBits);
55
56} // namespace ROOT::Experimental::Internal::BitPacking
57
58namespace {
59
60// In this namespace, common routines are defined for element packing and unpacking of ints and floats.
61// The following conversions and encodings exist:
62//
63// - Byteswap: on big endian machines, ints and floats are byte-swapped to the little endian on-disk format
64// - Cast: in-memory values can be stored in narrower on-disk columns. Currently without bounds checks.
65// For instance, for Double32_t, an in-memory double value is stored as a float on disk.
66// - Split: rearranges the bytes of an array of elements such that all the first bytes are stored first,
67// followed by all the second bytes, etc. This often clusters similar values, e.g. all the zero bytes
68// for arrays of small integers.
69// - Delta: Delta encoding stores on disk the delta to the previous element. This is useful for offsets,
70// because it transforms potentially large offset values into small deltas, which are then better
71// suited for split encoding.
72// - Zigzag: Zigzag encoding is used on signed integers only. It maps x to 2x if x is positive and to -(2x+1) if
73// x is negative. For series of positive and negative values of small absolute value, it will produce
74// a bit pattern that is favorable for split encoding.
75//
76// Encodings/conversions can be fused:
77//
78// - Delta/Zigzag + Splitting (there is no only-delta/zigzag encoding)
79// - (Delta/Zigzag + ) Splitting + Casting
80// - Everything + Byteswap
81
82/// \brief Copy and byteswap `count` elements of size `N` from `source` to `destination`.
83///
84/// Used on big-endian architectures for packing/unpacking elements whose column type requires
85/// a little-endian on-disk representation.
86template <std::size_t N>
87inline void CopyBswap(void *destination, const void *source, std::size_t count)
88{
89 auto dst = reinterpret_cast<typename RByteSwap<N>::value_type *>(destination);
90 auto src = reinterpret_cast<const typename RByteSwap<N>::value_type *>(source);
91 for (std::size_t i = 0; i < count; ++i) {
92 dst[i] = RByteSwap<N>::bswap(src[i]);
93 }
94}
95
96template <std::size_t N>
97void InPlaceBswap(void *array, std::size_t count)
98{
99 auto arr = reinterpret_cast<typename RByteSwap<N>::value_type *>(array);
100 for (std::size_t i = 0; i < count; ++i) {
101 arr[i] = RByteSwap<N>::bswap(arr[i]);
102 }
103}
104
105/// Casts T to one of the ints used in RByteSwap and back to its original type, which may be float or double
106#if R__LITTLE_ENDIAN == 0
107template <typename T>
108inline void ByteSwapIfNecessary(T &value)
109{
110 constexpr auto N = sizeof(T);
111 using bswap_value_type = typename RByteSwap<N>::value_type;
112 void *valuePtr = &value;
113 auto swapped = RByteSwap<N>::bswap(*reinterpret_cast<bswap_value_type *>(valuePtr));
114 *reinterpret_cast<bswap_value_type *>(valuePtr) = swapped;
115}
116template <>
117inline void ByteSwapIfNecessary<char>(char &)
118{
119}
120template <>
121inline void ByteSwapIfNecessary<signed char>(signed char &)
122{
123}
124template <>
125inline void ByteSwapIfNecessary<unsigned char>(unsigned char &)
126{
127}
128#else
129#define ByteSwapIfNecessary(x) ((void)0)
130#endif
131
132/// For integral types, ensures that the value of type SourceT is representable as DestT
133template <typename DestT, typename SourceT>
134inline void EnsureValidRange(SourceT val [[maybe_unused]])
135{
137
138 if constexpr (!std::is_integral_v<DestT> || !std::is_integral_v<SourceT>)
139 return;
140
141 if constexpr (static_cast<double>(std::numeric_limits<SourceT>::min()) <
142 static_cast<double>(std::numeric_limits<DestT>::min())) {
143 if constexpr (!std::is_signed_v<DestT>) {
144 if (val < 0) {
145 throw RException(R__FAIL(std::string("value out of range: ") + std::to_string(val) + " for type " +
146 typeid(DestT).name()));
147 }
148 } else if (val < std::numeric_limits<DestT>::min()) {
149 throw RException(
150 R__FAIL(std::string("value out of range: ") + std::to_string(val) + " for type " + typeid(DestT).name()));
151 }
152 }
153
154 if constexpr (static_cast<double>(std::numeric_limits<SourceT>::max()) >
155 static_cast<double>(std::numeric_limits<DestT>::max())) {
156 if (val > std::numeric_limits<DestT>::max()) {
157 throw RException(
158 R__FAIL(std::string("value out of range: ") + std::to_string(val) + " for type " + typeid(DestT).name()));
159 }
160 }
161}
162
163/// \brief Pack `count` elements into narrower (or wider) type
164///
165/// Used to convert in-memory elements to smaller column types of comatible types
166/// (e.g., double to float, int64 to int32). Takes care of byte swap if necessary.
167template <typename DestT, typename SourceT>
168inline void CastPack(void *destination, const void *source, std::size_t count)
169{
170 static_assert(std::is_convertible_v<SourceT, DestT>);
171 auto dst = reinterpret_cast<DestT *>(destination);
172 auto src = reinterpret_cast<const SourceT *>(source);
173 for (std::size_t i = 0; i < count; ++i) {
174 dst[i] = src[i];
175 ByteSwapIfNecessary(dst[i]);
176 }
177}
178
179/// \brief Unpack `count` on-disk elements into wider (or narrower) in-memory array
180///
181/// Used to convert on-disk elements to larger C++ types of comatible types
182/// (e.g., float to double, int32 to int64). Takes care of byte swap if necessary.
183template <typename DestT, typename SourceT>
184inline void CastUnpack(void *destination, const void *source, std::size_t count)
185{
186 auto dst = reinterpret_cast<DestT *>(destination);
187 auto src = reinterpret_cast<const SourceT *>(source);
188 for (std::size_t i = 0; i < count; ++i) {
189 SourceT val = src[i];
190 ByteSwapIfNecessary(val);
191 EnsureValidRange<DestT, SourceT>(val);
192 dst[i] = val;
193 }
194}
195
196/// \brief Split encoding of elements, possibly into narrower column
197///
198/// Used to first cast and then split-encode in-memory values to the on-disk column. Swap bytes if necessary.
199template <typename DestT, typename SourceT>
200inline void CastSplitPack(void *destination, const void *source, std::size_t count)
201{
202 constexpr std::size_t N = sizeof(DestT);
203 auto splitArray = reinterpret_cast<char *>(destination);
204 auto src = reinterpret_cast<const SourceT *>(source);
205 for (std::size_t i = 0; i < count; ++i) {
206 DestT val = src[i];
207 ByteSwapIfNecessary(val);
208 for (std::size_t b = 0; b < N; ++b) {
209 splitArray[b * count + i] = reinterpret_cast<const char *>(&val)[b];
210 }
211 }
212}
213
214/// \brief Reverse split encoding of elements
215///
216/// Used to first unsplit a column, possibly storing elements in wider C++ types. Swaps bytes if necessary
217template <typename DestT, typename SourceT>
218inline void CastSplitUnpack(void *destination, const void *source, std::size_t count)
219{
220 constexpr std::size_t N = sizeof(SourceT);
221 auto dst = reinterpret_cast<DestT *>(destination);
222 auto splitArray = reinterpret_cast<const char *>(source);
223 for (std::size_t i = 0; i < count; ++i) {
224 SourceT val = 0;
225 for (std::size_t b = 0; b < N; ++b) {
226 reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
227 }
228 ByteSwapIfNecessary(val);
229 EnsureValidRange<DestT, SourceT>(val);
230 dst[i] = val;
231 }
232}
233
234/// \brief Packing of columns with delta + split encoding
235///
236/// Apply split encoding to delta-encoded values, currently used only for index columns
237template <typename DestT, typename SourceT>
238inline void CastDeltaSplitPack(void *destination, const void *source, std::size_t count)
239{
240 constexpr std::size_t N = sizeof(DestT);
241 auto src = reinterpret_cast<const SourceT *>(source);
242 auto splitArray = reinterpret_cast<char *>(destination);
243 for (std::size_t i = 0; i < count; ++i) {
244 DestT val = (i == 0) ? src[0] : src[i] - src[i - 1];
245 ByteSwapIfNecessary(val);
246 for (std::size_t b = 0; b < N; ++b) {
247 splitArray[b * count + i] = reinterpret_cast<char *>(&val)[b];
248 }
249 }
250}
251
252/// \brief Unsplit and unwind delta encoding
253///
254/// Unsplit a column and reverse the delta encoding, currently used only for index columns
255template <typename DestT, typename SourceT>
256inline void CastDeltaSplitUnpack(void *destination, const void *source, std::size_t count)
257{
258 constexpr std::size_t N = sizeof(SourceT);
259 auto splitArray = reinterpret_cast<const char *>(source);
260 auto dst = reinterpret_cast<DestT *>(destination);
261 for (std::size_t i = 0; i < count; ++i) {
262 SourceT val = 0;
263 for (std::size_t b = 0; b < N; ++b) {
264 reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
265 }
266 ByteSwapIfNecessary(val);
267 val = (i == 0) ? val : val + dst[i - 1];
268 EnsureValidRange<DestT, SourceT>(val);
269 dst[i] = val;
270 }
271}
272
273/// \brief Packing of columns with zigzag + split encoding
274///
275/// Apply split encoding to zigzag-encoded values, used for signed integers
276template <typename DestT, typename SourceT>
277inline void CastZigzagSplitPack(void *destination, const void *source, std::size_t count)
278{
279 using UDestT = std::make_unsigned_t<DestT>;
280 constexpr std::size_t kNBitsDestT = sizeof(DestT) * 8;
281 constexpr std::size_t N = sizeof(DestT);
282 auto src = reinterpret_cast<const SourceT *>(source);
283 auto splitArray = reinterpret_cast<char *>(destination);
284 for (std::size_t i = 0; i < count; ++i) {
285 UDestT val = (static_cast<DestT>(src[i]) << 1) ^ (static_cast<DestT>(src[i]) >> (kNBitsDestT - 1));
286 ByteSwapIfNecessary(val);
287 for (std::size_t b = 0; b < N; ++b) {
288 splitArray[b * count + i] = reinterpret_cast<char *>(&val)[b];
289 }
290 }
291}
292
293/// \brief Unsplit and unwind zigzag encoding
294///
295/// Unsplit a column and reverse the zigzag encoding, used for signed integer columns
296template <typename DestT, typename SourceT>
297inline void CastZigzagSplitUnpack(void *destination, const void *source, std::size_t count)
298{
299 using USourceT = std::make_unsigned_t<SourceT>;
300 constexpr std::size_t N = sizeof(SourceT);
301 auto splitArray = reinterpret_cast<const char *>(source);
302 auto dst = reinterpret_cast<DestT *>(destination);
303 for (std::size_t i = 0; i < count; ++i) {
304 USourceT val = 0;
305 for (std::size_t b = 0; b < N; ++b) {
306 reinterpret_cast<char *>(&val)[b] = splitArray[b * count + i];
307 }
308 ByteSwapIfNecessary(val);
309 SourceT sval = static_cast<SourceT>((val >> 1) ^ -(static_cast<SourceT>(val) & 1));
310 EnsureValidRange<DestT, SourceT>(sval);
311 dst[i] = sval;
312 }
313}
314} // namespace
315
316// anonymous namespace because these definitions are not meant to be exported.
317namespace {
318
321
322// testing value for an unknown future column type
323inline constexpr EColumnType kTestFutureType =
324 static_cast<EColumnType>(std::numeric_limits<std::underlying_type_t<EColumnType>>::max() - 1);
325
326template <typename CppT, EColumnType>
327class RColumnElement;
328
329template <typename CppT>
330std::unique_ptr<RColumnElementBase> GenerateColumnElementInternal(EColumnType onDiskType)
331{
332 switch (onDiskType) {
333 case EColumnType::kIndex64: return std::make_unique<RColumnElement<CppT, EColumnType::kIndex64>>();
334 case EColumnType::kIndex32: return std::make_unique<RColumnElement<CppT, EColumnType::kIndex32>>();
335 case EColumnType::kSwitch: return std::make_unique<RColumnElement<CppT, EColumnType::kSwitch>>();
336 case EColumnType::kByte: return std::make_unique<RColumnElement<CppT, EColumnType::kByte>>();
337 case EColumnType::kChar: return std::make_unique<RColumnElement<CppT, EColumnType::kChar>>();
338 case EColumnType::kBit: return std::make_unique<RColumnElement<CppT, EColumnType::kBit>>();
339 case EColumnType::kReal64: return std::make_unique<RColumnElement<CppT, EColumnType::kReal64>>();
340 case EColumnType::kReal32: return std::make_unique<RColumnElement<CppT, EColumnType::kReal32>>();
341 case EColumnType::kReal16: return std::make_unique<RColumnElement<CppT, EColumnType::kReal16>>();
342 case EColumnType::kInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kInt64>>();
343 case EColumnType::kUInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt64>>();
344 case EColumnType::kInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kInt32>>();
345 case EColumnType::kUInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt32>>();
346 case EColumnType::kInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kInt16>>();
347 case EColumnType::kUInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt16>>();
348 case EColumnType::kInt8: return std::make_unique<RColumnElement<CppT, EColumnType::kInt8>>();
349 case EColumnType::kUInt8: return std::make_unique<RColumnElement<CppT, EColumnType::kUInt8>>();
350 case EColumnType::kSplitIndex64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitIndex64>>();
351 case EColumnType::kSplitIndex32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitIndex32>>();
352 case EColumnType::kSplitReal64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitReal64>>();
353 case EColumnType::kSplitReal32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitReal32>>();
354 case EColumnType::kSplitInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitInt64>>();
355 case EColumnType::kSplitUInt64: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitUInt64>>();
356 case EColumnType::kSplitInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitInt32>>();
357 case EColumnType::kSplitUInt32: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitUInt32>>();
358 case EColumnType::kSplitInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitInt16>>();
359 case EColumnType::kSplitUInt16: return std::make_unique<RColumnElement<CppT, EColumnType::kSplitUInt16>>();
360 case EColumnType::kReal32Trunc: return std::make_unique<RColumnElement<CppT, EColumnType::kReal32Trunc>>();
361 case EColumnType::kReal32Quant: return std::make_unique<RColumnElement<CppT, EColumnType::kReal32Quant>>();
362 default:
363 if (onDiskType == kTestFutureType)
364 return std::make_unique<RColumnElement<CppT, kTestFutureType>>();
365 R__ASSERT(false);
366 }
367 // never here
368 return nullptr;
369}
370
371/**
372 * Base class for columns whose on-storage representation is little-endian.
373 * The implementation of `Pack` and `Unpack` takes care of byteswap if the memory page is big-endian.
374 */
375template <typename CppT>
376class RColumnElementLE : public RColumnElementBase {
377protected:
378 explicit RColumnElementLE(std::size_t size, std::size_t bitsOnStorage) : RColumnElementBase(size, bitsOnStorage) {}
379
380public:
381 static constexpr bool kIsMappable = (R__LITTLE_ENDIAN == 1);
382
383 void Pack(void *dst, const void *src, std::size_t count) const final
384 {
385#if R__LITTLE_ENDIAN == 1
386 RColumnElementBase::Pack(dst, src, count);
387#else
388 CopyBswap<sizeof(CppT)>(dst, src, count);
389#endif
390 }
391 void Unpack(void *dst, const void *src, std::size_t count) const final
392 {
393#if R__LITTLE_ENDIAN == 1
394 RColumnElementBase::Unpack(dst, src, count);
395#else
396 CopyBswap<sizeof(CppT)>(dst, src, count);
397#endif
398 }
399}; // class RColumnElementLE
400
401/**
402 * Base class for columns storing elements of wider in-memory types,
403 * such as 64bit in-memory offsets to Index32 columns.
404 */
405template <typename CppT, typename NarrowT>
406class RColumnElementCastLE : public RColumnElementBase {
407protected:
408 explicit RColumnElementCastLE(std::size_t size, std::size_t bitsOnStorage) : RColumnElementBase(size, bitsOnStorage)
409 {
410 }
411
412public:
413 static constexpr bool kIsMappable = false;
414
415 void Pack(void *dst, const void *src, std::size_t count) const final { CastPack<NarrowT, CppT>(dst, src, count); }
416 void Unpack(void *dst, const void *src, std::size_t count) const final
417 {
418 CastUnpack<CppT, NarrowT>(dst, src, count);
419 }
420}; // class RColumnElementCastLE
421
422/**
423 * Base class for split columns whose on-storage representation is little-endian.
424 * The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
425 * As part of the splitting, can also narrow down the type to NarrowT.
426 */
427template <typename CppT, typename NarrowT>
428class RColumnElementSplitLE : public RColumnElementBase {
429protected:
430 explicit RColumnElementSplitLE(std::size_t size, std::size_t bitsOnStorage) : RColumnElementBase(size, bitsOnStorage)
431 {
432 }
433
434public:
435 static constexpr bool kIsMappable = false;
436
437 void Pack(void *dst, const void *src, std::size_t count) const final
438 {
439 CastSplitPack<NarrowT, CppT>(dst, src, count);
440 }
441 void Unpack(void *dst, const void *src, std::size_t count) const final
442 {
443 CastSplitUnpack<CppT, NarrowT>(dst, src, count);
444 }
445}; // class RColumnElementSplitLE
446
447/**
448 * Base class for delta + split columns (index columns) whose on-storage representation is little-endian.
449 * The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
450 * As part of the encoding, can also narrow down the type to NarrowT.
451 */
452template <typename CppT, typename NarrowT>
453class RColumnElementDeltaSplitLE : public RColumnElementBase {
454protected:
455 explicit RColumnElementDeltaSplitLE(std::size_t size, std::size_t bitsOnStorage)
456 : RColumnElementBase(size, bitsOnStorage)
457 {
458 }
459
460public:
461 static constexpr bool kIsMappable = false;
462
463 void Pack(void *dst, const void *src, std::size_t count) const final
464 {
465 CastDeltaSplitPack<NarrowT, CppT>(dst, src, count);
466 }
467 void Unpack(void *dst, const void *src, std::size_t count) const final
468 {
469 CastDeltaSplitUnpack<CppT, NarrowT>(dst, src, count);
470 }
471}; // class RColumnElementDeltaSplitLE
472
473/// Reading of unsplit integer columns to boolean
474template <typename CppIntT>
475class RColumnElementBoolAsUnsplitInt : public RColumnElementBase {
476protected:
477 explicit RColumnElementBoolAsUnsplitInt(std::size_t size, std::size_t bitsOnStorage)
478 : RColumnElementBase(size, bitsOnStorage)
479 {
480 }
481
482public:
483 static constexpr bool kIsMappable = false;
484
485 // We don't implement Pack() because integers must not be written to disk as booleans
486 void Pack(void *, const void *, std::size_t) const final { R__ASSERT(false); }
487
488 void Unpack(void *dst, const void *src, std::size_t count) const final
489 {
490 auto *boolArray = reinterpret_cast<bool *>(dst);
491 auto *intArray = reinterpret_cast<const CppIntT *>(src);
492 for (std::size_t i = 0; i < count; ++i) {
493 boolArray[i] = intArray[i] != 0;
494 }
495 }
496}; // class RColumnElementBoolAsUnsplitInt
497
498/// Reading of split integer columns to boolean
499template <typename CppIntT>
500class RColumnElementBoolAsSplitInt : public RColumnElementBase {
501protected:
502 explicit RColumnElementBoolAsSplitInt(std::size_t size, std::size_t bitsOnStorage)
503 : RColumnElementBase(size, bitsOnStorage)
504 {
505 }
506
507public:
508 static constexpr bool kIsMappable = false;
509
510 // We don't implement Pack() because integers must not be written to disk as booleans
511 void Pack(void *, const void *, std::size_t) const final { R__ASSERT(false); }
512
513 void Unpack(void *dst, const void *src, std::size_t count) const final
514 {
515 constexpr std::size_t N = sizeof(CppIntT);
516 auto *boolArray = reinterpret_cast<bool *>(dst);
517 auto *splitArray = reinterpret_cast<const char *>(src);
518 for (std::size_t i = 0; i < count; ++i) {
519 boolArray[i] = false;
520 for (std::size_t b = 0; b < N; ++b) {
521 if (splitArray[b * count + i]) {
522 boolArray[i] = true;
523 break;
524 }
525 }
526 }
527 }
528}; // RColumnElementBoolAsSplitInt
529
530/// Reading of bit columns as integer
531template <typename CppIntT>
532class RColumnElementIntAsBool : public RColumnElementBase {
533protected:
534 explicit RColumnElementIntAsBool(std::size_t size, std::size_t bitsOnStorage)
535 : RColumnElementBase(size, bitsOnStorage)
536 {
537 }
538
539public:
540 static constexpr bool kIsMappable = false;
541
542 // We don't implement Pack() because booleans must not be written as integers to disk
543 void Pack(void *, const void *, std::size_t) const final { R__ASSERT(false); }
544
545 void Unpack(void *dst, const void *src, std::size_t count) const final
546 {
547 auto *intArray = reinterpret_cast<CppIntT *>(dst);
548 const char *charArray = reinterpret_cast<const char *>(src);
549 std::bitset<8> bitSet;
550 for (std::size_t i = 0; i < count; i += 8) {
551 bitSet = charArray[i / 8];
552 for (std::size_t j = i; j < std::min(count, i + 8); ++j) {
553 intArray[j] = bitSet[j % 8];
554 }
555 }
556 }
557}; // RColumnElementIntAsBool
558
559/**
560 * Base class for zigzag + split columns (signed integer columns) whose on-storage representation is little-endian.
561 * The implementation of `Pack` and `Unpack` takes care of splitting and, if necessary, byteswap.
562 * The NarrowT target type should be an signed integer, which can be smaller than the CppT source type.
563 */
564template <typename CppT, typename NarrowT>
565class RColumnElementZigzagSplitLE : public RColumnElementBase {
566protected:
567 explicit RColumnElementZigzagSplitLE(std::size_t size, std::size_t bitsOnStorage)
568 : RColumnElementBase(size, bitsOnStorage)
569 {
570 }
571
572public:
573 static constexpr bool kIsMappable = false;
574
575 void Pack(void *dst, const void *src, std::size_t count) const final
576 {
577 CastZigzagSplitPack<NarrowT, CppT>(dst, src, count);
578 }
579 void Unpack(void *dst, const void *src, std::size_t count) const final
580 {
581 CastZigzagSplitUnpack<CppT, NarrowT>(dst, src, count);
582 }
583}; // class RColumnElementZigzagSplitLE
584
585////////////////////////////////////////////////////////////////////////////////
586// Pairs of C++ type and column type, like float and EColumnType::kReal32
587////////////////////////////////////////////////////////////////////////////////
588
589////////////////////////////////////////////////////////////////////////////////
590// Part 1: C++ type --> unknown column type
591////////////////////////////////////////////////////////////////////////////////
592
593template <typename CppT, EColumnType ColumnT = EColumnType::kUnknown>
594class RColumnElement : public RColumnElementBase {
595public:
596 RColumnElement() : RColumnElementBase(sizeof(CppT))
597 {
599 R__FAIL(std::string("internal error: no column mapping for this C++ type: ") + typeid(CppT).name() + " --> " +
600 GetColumnTypeName(ColumnT)));
601 }
602
603 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(CppT), EColumnType::kUnknown}; }
604};
605
606template <>
607class RColumnElement<bool, EColumnType::kUnknown> : public RColumnElementBase {
608public:
609 static constexpr std::size_t kSize = sizeof(bool);
610 RColumnElement() : RColumnElementBase(kSize) {}
611 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(bool), EColumnType::kUnknown}; }
612};
613
614template <>
615class RColumnElement<std::byte, EColumnType::kUnknown> : public RColumnElementBase {
616public:
617 static constexpr std::size_t kSize = sizeof(std::byte);
618 RColumnElement() : RColumnElementBase(kSize) {}
619 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::byte), EColumnType::kUnknown}; }
620};
621
622template <>
623class RColumnElement<char, EColumnType::kUnknown> : public RColumnElementBase {
624public:
625 static constexpr std::size_t kSize = sizeof(char);
626 RColumnElement() : RColumnElementBase(kSize) {}
627 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(char), EColumnType::kUnknown}; }
628};
629
630template <>
631class RColumnElement<std::int8_t, EColumnType::kUnknown> : public RColumnElementBase {
632public:
633 static constexpr std::size_t kSize = sizeof(std::int8_t);
634 RColumnElement() : RColumnElementBase(kSize) {}
635 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::int8_t), EColumnType::kUnknown}; }
636};
637
638template <>
639class RColumnElement<std::uint8_t, EColumnType::kUnknown> : public RColumnElementBase {
640public:
641 static constexpr std::size_t kSize = sizeof(std::uint8_t);
642 RColumnElement() : RColumnElementBase(kSize) {}
643 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::uint8_t), EColumnType::kUnknown}; }
644};
645
646template <>
647class RColumnElement<std::int16_t, EColumnType::kUnknown> : public RColumnElementBase {
648public:
649 static constexpr std::size_t kSize = sizeof(std::int16_t);
650 RColumnElement() : RColumnElementBase(kSize) {}
651 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::int16_t), EColumnType::kUnknown}; }
652};
653
654template <>
655class RColumnElement<std::uint16_t, EColumnType::kUnknown> : public RColumnElementBase {
656public:
657 static constexpr std::size_t kSize = sizeof(std::uint16_t);
658 RColumnElement() : RColumnElementBase(kSize) {}
659 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::uint16_t), EColumnType::kUnknown}; }
660};
661
662template <>
663class RColumnElement<std::int32_t, EColumnType::kUnknown> : public RColumnElementBase {
664public:
665 static constexpr std::size_t kSize = sizeof(std::int32_t);
666 RColumnElement() : RColumnElementBase(kSize) {}
667 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::int32_t), EColumnType::kUnknown}; }
668};
669
670template <>
671class RColumnElement<std::uint32_t, EColumnType::kUnknown> : public RColumnElementBase {
672public:
673 static constexpr std::size_t kSize = sizeof(std::uint32_t);
674 RColumnElement() : RColumnElementBase(kSize) {}
675 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::uint32_t), EColumnType::kUnknown}; }
676};
677
678template <>
679class RColumnElement<std::int64_t, EColumnType::kUnknown> : public RColumnElementBase {
680public:
681 static constexpr std::size_t kSize = sizeof(std::int64_t);
682 RColumnElement() : RColumnElementBase(kSize) {}
683 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::int64_t), EColumnType::kUnknown}; }
684};
685
686template <>
687class RColumnElement<std::uint64_t, EColumnType::kUnknown> : public RColumnElementBase {
688public:
689 static constexpr std::size_t kSize = sizeof(std::uint64_t);
690 RColumnElement() : RColumnElementBase(kSize) {}
691 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(std::uint64_t), EColumnType::kUnknown}; }
692};
693
694template <>
695class RColumnElement<float, EColumnType::kUnknown> : public RColumnElementBase {
696public:
697 static constexpr std::size_t kSize = sizeof(float);
698 RColumnElement() : RColumnElementBase(kSize) {}
699 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(float), EColumnType::kUnknown}; }
700};
701
702template <>
703class RColumnElement<double, EColumnType::kUnknown> : public RColumnElementBase {
704public:
705 static constexpr std::size_t kSize = sizeof(double);
706 RColumnElement() : RColumnElementBase(kSize) {}
707 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(double), EColumnType::kUnknown}; }
708};
709
710template <>
711class RColumnElement<ROOT::Experimental::ClusterSize_t, EColumnType::kUnknown> : public RColumnElementBase {
712public:
713 static constexpr std::size_t kSize = sizeof(ROOT::Experimental::ClusterSize_t);
714 RColumnElement() : RColumnElementBase(kSize) {}
715 RIdentifier GetIdentifier() const final
716 {
717 return RIdentifier{typeid(ROOT::Experimental::ClusterSize_t), EColumnType::kUnknown};
718 }
719};
720
721template <>
722class RColumnElement<ROOT::Experimental::RColumnSwitch, EColumnType::kUnknown> : public RColumnElementBase {
723public:
724 static constexpr std::size_t kSize = sizeof(ROOT::Experimental::RColumnSwitch);
725 RColumnElement() : RColumnElementBase(kSize) {}
726 RIdentifier GetIdentifier() const final
727 {
728 return RIdentifier{typeid(ROOT::Experimental::RColumnSwitch), EColumnType::kUnknown};
729 }
730};
731
732////////////////////////////////////////////////////////////////////////////////
733// Part 2: C++ type --> supported column representations,
734// ordered by C++ type
735////////////////////////////////////////////////////////////////////////////////
736
737template <>
738class RColumnElement<ROOT::Experimental::RColumnSwitch, EColumnType::kSwitch> : public RColumnElementBase {
739private:
740 struct RSwitchElement {
741 std::uint64_t fIndex;
742 std::uint32_t fTag;
743 };
744
745public:
746 static constexpr bool kIsMappable = false;
747 static constexpr std::size_t kSize = sizeof(ROOT::Experimental::RColumnSwitch);
748 static constexpr std::size_t kBitsOnStorage = 96;
749 RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
750 bool IsMappable() const final { return kIsMappable; }
751
752 void Pack(void *dst, const void *src, std::size_t count) const final
753 {
754 auto srcArray = reinterpret_cast<const ROOT::Experimental::RColumnSwitch *>(src);
755 auto dstArray = reinterpret_cast<unsigned char *>(dst);
756 for (std::size_t i = 0; i < count; ++i) {
757 RSwitchElement element{srcArray[i].GetIndex(), srcArray[i].GetTag()};
758#if R__LITTLE_ENDIAN == 0
759 element.fIndex = RByteSwap<8>::bswap(element.fIndex);
760 element.fTag = RByteSwap<4>::bswap(element.fTag);
761#endif
762 memcpy(dstArray + i * 12, &element, 12);
763 }
764 }
765
766 void Unpack(void *dst, const void *src, std::size_t count) const final
767 {
768 auto srcArray = reinterpret_cast<const unsigned char *>(src);
769 auto dstArray = reinterpret_cast<ROOT::Experimental::RColumnSwitch *>(dst);
770 for (std::size_t i = 0; i < count; ++i) {
771 RSwitchElement element;
772 memcpy(&element, srcArray + i * 12, 12);
773#if R__LITTLE_ENDIAN == 0
774 element.fIndex = RByteSwap<8>::bswap(element.fIndex);
775 element.fTag = RByteSwap<4>::bswap(element.fTag);
776#endif
777 dstArray[i] =
779 }
780 }
781
782 RIdentifier GetIdentifier() const final
783 {
784 return RIdentifier{typeid(ROOT::Experimental::RColumnSwitch), EColumnType::kSwitch};
785 }
786};
787
788template <>
789class RColumnElement<bool, EColumnType::kBit> : public RColumnElementBase {
790public:
791 static constexpr bool kIsMappable = false;
792 static constexpr std::size_t kSize = sizeof(bool);
793 static constexpr std::size_t kBitsOnStorage = 1;
794 RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
795 bool IsMappable() const final { return kIsMappable; }
796
797 void Pack(void *dst, const void *src, std::size_t count) const final;
798 void Unpack(void *dst, const void *src, std::size_t count) const final;
799
800 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(bool), EColumnType::kBit}; }
801};
802
803template <>
804class RColumnElement<float, EColumnType::kReal16> : public RColumnElementBase {
805public:
806 static constexpr bool kIsMappable = false;
807 static constexpr std::size_t kSize = sizeof(float);
808 static constexpr std::size_t kBitsOnStorage = 16;
809 RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
810 bool IsMappable() const final { return kIsMappable; }
811
812 void Pack(void *dst, const void *src, std::size_t count) const final
813 {
814 const float *floatArray = reinterpret_cast<const float *>(src);
815 std::uint16_t *uint16Array = reinterpret_cast<std::uint16_t *>(dst);
816
817 for (std::size_t i = 0; i < count; ++i) {
818 uint16Array[i] = ROOT::Experimental::Internal::FloatToHalf(floatArray[i]);
819 ByteSwapIfNecessary(uint16Array[i]);
820 }
821 }
822
823 void Unpack(void *dst, const void *src, std::size_t count) const final
824 {
825 float *floatArray = reinterpret_cast<float *>(dst);
826 const std::uint16_t *uint16Array = reinterpret_cast<const std::uint16_t *>(src);
827
828 for (std::size_t i = 0; i < count; ++i) {
829 std::uint16_t val = uint16Array[i];
830 ByteSwapIfNecessary(val);
832 }
833 }
834
835 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(float), EColumnType::kReal16}; }
836};
837
838template <>
839class RColumnElement<double, EColumnType::kReal16> : public RColumnElementBase {
840public:
841 static constexpr bool kIsMappable = false;
842 static constexpr std::size_t kSize = sizeof(double);
843 static constexpr std::size_t kBitsOnStorage = 16;
844 RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
845 bool IsMappable() const final { return kIsMappable; }
846
847 void Pack(void *dst, const void *src, std::size_t count) const final
848 {
849 const double *doubleArray = reinterpret_cast<const double *>(src);
850 std::uint16_t *uint16Array = reinterpret_cast<std::uint16_t *>(dst);
851
852 for (std::size_t i = 0; i < count; ++i) {
853 uint16Array[i] = ROOT::Experimental::Internal::FloatToHalf(static_cast<float>(doubleArray[i]));
854 ByteSwapIfNecessary(uint16Array[i]);
855 }
856 }
857
858 void Unpack(void *dst, const void *src, std::size_t count) const final
859 {
860 double *doubleArray = reinterpret_cast<double *>(dst);
861 const std::uint16_t *uint16Array = reinterpret_cast<const std::uint16_t *>(src);
862
863 for (std::size_t i = 0; i < count; ++i) {
864 std::uint16_t val = uint16Array[i];
865 ByteSwapIfNecessary(val);
866 doubleArray[i] = static_cast<double>(ROOT::Experimental::Internal::HalfToFloat(val));
867 }
868 }
869
870 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(double), EColumnType::kReal16}; }
871};
872
873template <typename T>
874class RColumnElementTrunc : public RColumnElementBase {
875public:
876 static_assert(std::is_floating_point_v<T>);
877 static constexpr bool kIsMappable = false;
878 static constexpr std::size_t kSize = sizeof(T);
879
880 // NOTE: setting bitsOnStorage == 0 by default. This is an invalid value that helps us
881 // catch misuses where RColumnElement is used without having explicitly set its bit width
882 // (which should never happen).
883 RColumnElementTrunc() : RColumnElementBase(kSize, 0) {}
884
885 void SetBitsOnStorage(std::size_t bitsOnStorage) final
886 {
887 const auto &[minBits, maxBits] = GetValidBitRange(EColumnType::kReal32Trunc);
888 R__ASSERT(bitsOnStorage >= minBits && bitsOnStorage <= maxBits);
889 fBitsOnStorage = bitsOnStorage;
890 }
891
892 bool IsMappable() const final { return kIsMappable; }
893
894 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(T), EColumnType::kReal32Trunc}; }
895};
896
897template <>
898class RColumnElement<float, EColumnType::kReal32Trunc> : public RColumnElementTrunc<float> {
899public:
900 void Pack(void *dst, const void *src, std::size_t count) const final
901 {
903
904 R__ASSERT(GetPackedSize(count) == MinBufSize(count, fBitsOnStorage));
905
906#if R__LITTLE_ENDIAN == 0
907 // TODO(gparolini): to avoid this extra allocation we might want to perform byte swapping
908 // directly in the Pack/UnpackBits functions.
909 auto bswapped = std::make_unique<float[]>(count);
910 CopyBswap<sizeof(float)>(bswapped.get(), src, count);
911 const auto *srcLe = bswapped.get();
912#else
913 const auto *srcLe = reinterpret_cast<const float *>(src);
914#endif
915 PackBits(dst, srcLe, count, sizeof(float), fBitsOnStorage);
916 }
917
918 void Unpack(void *dst, const void *src, std::size_t count) const final
919 {
921
922 R__ASSERT(GetPackedSize(count) == MinBufSize(count, fBitsOnStorage));
923
924 UnpackBits(dst, src, count, sizeof(float), fBitsOnStorage);
925#if R__LITTLE_ENDIAN == 0
926 InPlaceBswap<sizeof(float)>(dst, count);
927#endif
928 }
929};
930
931template <>
932class RColumnElement<double, EColumnType::kReal32Trunc> : public RColumnElementTrunc<double> {
933public:
934 void Pack(void *dst, const void *src, std::size_t count) const final
935 {
937
938 R__ASSERT(GetPackedSize(count) == MinBufSize(count, fBitsOnStorage));
939
940 // Cast doubles to float before packing them
941 // TODO(gparolini): avoid this allocation
942 auto srcFloat = std::make_unique<float[]>(count);
943 const double *srcDouble = reinterpret_cast<const double *>(src);
944 for (std::size_t i = 0; i < count; ++i)
945 srcFloat[i] = static_cast<float>(srcDouble[i]);
946
947#if R__LITTLE_ENDIAN == 0
948 // TODO(gparolini): to avoid this extra allocation we might want to perform byte swapping
949 // directly in the Pack/UnpackBits functions.
950 auto bswapped = std::make_unique<float[]>(count);
951 CopyBswap<sizeof(float)>(bswapped.get(), srcFloat.get(), count);
952 const float *srcLe = bswapped.get();
953#else
954 const float *srcLe = reinterpret_cast<const float *>(srcFloat.get());
955#endif
956 PackBits(dst, srcLe, count, sizeof(float), fBitsOnStorage);
957 }
958
959 void Unpack(void *dst, const void *src, std::size_t count) const final
960 {
962
963 R__ASSERT(GetPackedSize(count) == MinBufSize(count, fBitsOnStorage));
964
965 // TODO(gparolini): avoid this allocation
966 auto dstFloat = std::make_unique<float[]>(count);
967 UnpackBits(dstFloat.get(), src, count, sizeof(float), fBitsOnStorage);
968#if R__LITTLE_ENDIAN == 0
969 InPlaceBswap<sizeof(float)>(dstFloat.get(), count);
970#endif
971
972 double *dstDouble = reinterpret_cast<double *>(dst);
973 for (std::size_t i = 0; i < count; ++i)
974 dstDouble[i] = static_cast<double>(dstFloat[i]);
975 }
976};
977
978namespace Quantize {
979
980using Quantized_t = std::uint32_t;
981
982[[maybe_unused]] inline std::size_t LeadingZeroes(std::uint32_t x)
983{
984 if (x == 0)
985 return 64;
986
987#ifdef _MSC_VER
988 unsigned long idx = 0;
989 _BitScanForward(&idx, x);
990 return static_cast<std::size_t>(idx);
991#else
992 return static_cast<std::size_t>(__builtin_clzl(x));
993#endif
994}
995
996[[maybe_unused]] inline std::size_t TrailingZeroes(std::uint32_t x)
997{
998 if (x == 0)
999 return 64;
1000
1001#ifdef _MSC_VER
1002 unsigned long idx = 0;
1003 _BitScanReverse(&idx, x);
1004 return static_cast<std::size_t>(idx);
1005#else
1006 return static_cast<std::size_t>(__builtin_ctzl(x));
1007#endif
1008}
1009
1010/// Converts the array `src` of `count` floating point numbers into an array of their quantized representations.
1011/// Each element of `src` is assumed to be in the inclusive range [min, max].
1012/// The quantized representation will consist of unsigned integers of at most `nQuantBits` (with `nQuantBits <= 8 *
1013/// sizeof(Quantized_t)`). The unused bits are kept in the LSB of the quantized integers, to allow for easy bit packing
1014/// of those integers via BitPacking::PackBits().
1015/// \return The number of values in `src` that were found to be out of range (0 means all values were in range).
1016template <typename T>
1017int QuantizeReals(Quantized_t *dst, const T *src, std::size_t count, double min, double max, std::size_t nQuantBits)
1018{
1019 static_assert(std::is_floating_point_v<T>);
1020 static_assert(sizeof(T) <= sizeof(double));
1021 assert(1 <= nQuantBits && nQuantBits <= 8 * sizeof(Quantized_t));
1022
1023 const std::size_t quantMax = (1ull << nQuantBits) - 1;
1024 const double scale = quantMax / (max - min);
1025 const std::size_t unusedBits = sizeof(Quantized_t) * 8 - nQuantBits;
1026
1027 int nOutOfRange = 0;
1028
1029 for (std::size_t i = 0; i < count; ++i) {
1030 const T elem = src[i];
1031
1032 nOutOfRange += !(min <= elem && elem <= max);
1033
1034 const double e = 0.5 + (elem - min) * scale;
1035 Quantized_t q = static_cast<Quantized_t>(e);
1036 ByteSwapIfNecessary(q);
1037
1038 // double-check we actually used at most `nQuantBits`
1039 assert(LeadingZeroes(q) >= unusedBits);
1040
1041 // we want to leave zeroes in the LSB, not the MSB, because we'll then drop the LSB
1042 // when bit packing.
1043 dst[i] = q << unusedBits;
1044 }
1045
1046 return nOutOfRange;
1047}
1048
1049/// Undoes the transformation performed by QuantizeReals() (assuming the same `count`, `min`, `max` and `nQuantBits`).
1050/// \return The number of unpacked values that were found to be out of range (0 means all values were in range).
1051template <typename T>
1052int UnquantizeReals(T *dst, const Quantized_t *src, std::size_t count, double min, double max, std::size_t nQuantBits)
1053{
1054 static_assert(std::is_floating_point_v<T>);
1055 static_assert(sizeof(T) <= sizeof(double));
1056 assert(1 <= nQuantBits && nQuantBits <= 8 * sizeof(Quantized_t));
1057
1058 const std::size_t quantMax = (1ull << nQuantBits) - 1;
1059 const double scale = (max - min) / quantMax;
1060 const std::size_t unusedBits = sizeof(Quantized_t) * 8 - nQuantBits;
1061 const double eps = std::numeric_limits<double>::epsilon();
1062 const double emin = -eps * scale + min;
1063 const double emax = (static_cast<double>(quantMax) + eps) * scale + min;
1064
1065 int nOutOfRange = 0;
1066
1067 for (std::size_t i = 0; i < count; ++i) {
1068 Quantized_t elem = src[i];
1069 // Undo the LSB-preserving shift performed by QuantizeReals
1070 assert(TrailingZeroes(elem) >= unusedBits);
1071 elem >>= unusedBits;
1072 ByteSwapIfNecessary(elem);
1073
1074 const double fq = static_cast<double>(elem);
1075 const double e = fq * scale + min;
1076 dst[i] = static_cast<T>(e);
1077
1078 nOutOfRange += !(emin <= dst[i] && dst[i] <= emax);
1079 }
1080
1081 return nOutOfRange;
1082}
1083} // namespace Quantize
1084
1085template <typename T>
1086class RColumnElementQuantized : public RColumnElementBase {
1087 static_assert(std::is_floating_point_v<T>);
1088
1089public:
1090 static constexpr bool kIsMappable = false;
1091 static constexpr std::size_t kSize = sizeof(T);
1092
1093 RColumnElementQuantized() : RColumnElementBase(kSize, 0) {}
1094
1095 void SetBitsOnStorage(std::size_t bitsOnStorage) final
1096 {
1097 const auto [minBits, maxBits] = GetValidBitRange(EColumnType::kReal32Quant);
1098 R__ASSERT(bitsOnStorage >= minBits && bitsOnStorage <= maxBits);
1099 fBitsOnStorage = bitsOnStorage;
1100 }
1101
1102 void SetValueRange(double min, double max) final
1103 {
1104 R__ASSERT(min >= std::numeric_limits<T>::lowest());
1105 R__ASSERT(max <= std::numeric_limits<T>::max());
1106 fValueRange = {min, max};
1107 }
1108
1109 bool IsMappable() const final { return kIsMappable; }
1110
1111 void Pack(void *dst, const void *src, std::size_t count) const final
1112 {
1113 using namespace ROOT::Experimental;
1114
1115 // TODO(gparolini): see if we can avoid this allocation
1116 auto quantized = std::make_unique<Quantize::Quantized_t[]>(count);
1117 assert(fValueRange);
1118 const auto [min, max] = *fValueRange;
1119 const int nOutOfRange =
1120 Quantize::QuantizeReals(quantized.get(), reinterpret_cast<const T *>(src), count, min, max, fBitsOnStorage);
1121 if (nOutOfRange) {
1122 throw RException(R__FAIL(std::to_string(nOutOfRange) +
1123 " values were found of of range for quantization while packing (range is [" +
1124 std::to_string(min) + ", " + std::to_string(max) + "])"));
1125 }
1126 Internal::BitPacking::PackBits(dst, quantized.get(), count, sizeof(Quantize::Quantized_t), fBitsOnStorage);
1127 }
1128
1129 void Unpack(void *dst, const void *src, std::size_t count) const final
1130 {
1131 using namespace ROOT::Experimental;
1132
1133 // TODO(gparolini): see if we can avoid this allocation
1134 auto quantized = std::make_unique<Quantize::Quantized_t[]>(count);
1135 assert(fValueRange);
1136 const auto [min, max] = *fValueRange;
1137 Internal::BitPacking::UnpackBits(quantized.get(), src, count, sizeof(Quantize::Quantized_t), fBitsOnStorage);
1138 [[maybe_unused]] const int nOutOfRange =
1139 Quantize::UnquantizeReals(reinterpret_cast<T *>(dst), quantized.get(), count, min, max, fBitsOnStorage);
1140 // NOTE: here, differently from Pack(), we don't ever expect to have values out of range, since the quantized
1141 // integers we pass to UnquantizeReals are by construction limited in value to the proper range. In Pack()
1142 // this is not the case, as the user may give us float values that are out of range.
1143 assert(nOutOfRange == 0);
1144 }
1145
1146 RIdentifier GetIdentifier() const final { return RIdentifier{typeid(T), EColumnType::kReal32Quant}; }
1147};
1148
1149template <>
1150class RColumnElement<float, EColumnType::kReal32Quant> : public RColumnElementQuantized<float> {};
1151
1152template <>
1153class RColumnElement<double, EColumnType::kReal32Quant> : public RColumnElementQuantized<double> {};
1154
1155#define __RCOLUMNELEMENT_SPEC_BODY(CppT, ColumnT, BaseT, BitsOnStorage) \
1156 static constexpr std::size_t kSize = sizeof(CppT); \
1157 static constexpr std::size_t kBitsOnStorage = BitsOnStorage; \
1158 RColumnElement() : BaseT(kSize, kBitsOnStorage) {} \
1159 bool IsMappable() const final \
1160 { \
1161 return kIsMappable; \
1162 } \
1163 RIdentifier GetIdentifier() const final \
1164 { \
1165 return RIdentifier{typeid(CppT), ColumnT}; \
1166 }
1167/// These macros are used to declare `RColumnElement` template specializations below. Additional arguments can be used
1168/// to forward template parameters to the base class, e.g.
1169/// ```
1170/// DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt32, 32,
1171/// RColumnElementCastLE, <std::int64_t, std::int32_t>);
1172/// ```
1173#define DECLARE_RCOLUMNELEMENT_SPEC(CppT, ColumnT, BitsOnStorage, BaseT, ...) \
1174 template <> \
1175 class RColumnElement<CppT, ColumnT> : public BaseT __VA_ARGS__ { \
1176 public: \
1177 __RCOLUMNELEMENT_SPEC_BODY(CppT, ColumnT, BaseT, BitsOnStorage) \
1178 }
1179#define DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(CppT, ColumnT, BitsOnStorage) \
1180 template <> \
1181 class RColumnElement<CppT, ColumnT> : public RColumnElementBase { \
1182 public: \
1183 static constexpr bool kIsMappable = true; \
1184 __RCOLUMNELEMENT_SPEC_BODY(CppT, ColumnT, RColumnElementBase, BitsOnStorage) \
1185 }
1186
1187DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kChar, 8, RColumnElementBoolAsUnsplitInt, <char>);
1188DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kInt8, 8, RColumnElementBoolAsUnsplitInt, <std::int8_t>);
1189DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kUInt8, 8, RColumnElementBoolAsUnsplitInt, <std::uint8_t>);
1190DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kInt16, 16, RColumnElementBoolAsUnsplitInt, <std::int16_t>);
1191DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kUInt16, 16, RColumnElementBoolAsUnsplitInt, <std::uint16_t>);
1192DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kInt32, 32, RColumnElementBoolAsUnsplitInt, <std::int32_t>);
1193DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kUInt32, 32, RColumnElementBoolAsUnsplitInt, <std::uint32_t>);
1194DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kInt64, 64, RColumnElementBoolAsUnsplitInt, <std::int64_t>);
1195DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kUInt64, 64, RColumnElementBoolAsUnsplitInt, <std::uint64_t>);
1196DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kSplitInt16, 16, RColumnElementBoolAsSplitInt, <std::int16_t>);
1197DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kSplitUInt16, 16, RColumnElementBoolAsSplitInt, <std::uint16_t>);
1198DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kSplitInt32, 32, RColumnElementBoolAsSplitInt, <std::int32_t>);
1199DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kSplitUInt32, 32, RColumnElementBoolAsSplitInt, <std::uint32_t>);
1200DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kSplitInt64, 64, RColumnElementBoolAsSplitInt, <std::int64_t>);
1201DECLARE_RCOLUMNELEMENT_SPEC(bool, EColumnType::kSplitUInt64, 64, RColumnElementBoolAsSplitInt, <std::uint64_t>);
1202
1203DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::byte, EColumnType::kByte, 8);
1204
1205DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(char, EColumnType::kChar, 8);
1206DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kInt8, 8, RColumnElementCastLE, <char, std::int8_t>);
1207DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kUInt8, 8, RColumnElementCastLE, <char, std::uint8_t>);
1208DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kInt16, 16, RColumnElementCastLE, <char, std::int16_t>);
1209DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kUInt16, 16, RColumnElementCastLE, <char, std::uint16_t>);
1210DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kInt32, 32, RColumnElementCastLE, <char, std::int32_t>);
1211DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kUInt32, 32, RColumnElementCastLE, <char, std::uint32_t>);
1212DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kInt64, 64, RColumnElementCastLE, <char, std::int64_t>);
1213DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kUInt64, 64, RColumnElementCastLE, <char, std::uint64_t>);
1214DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE, <char, std::int16_t>);
1215DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE, <char, std::uint16_t>);
1216DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE, <char, std::int32_t>);
1217DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE, <char, std::uint32_t>);
1218DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE, <char, std::int64_t>);
1219DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE, <char, std::uint64_t>);
1220DECLARE_RCOLUMNELEMENT_SPEC(char, EColumnType::kBit, 1, RColumnElementIntAsBool, <char>);
1221
1222DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::int8_t, EColumnType::kInt8, 8);
1223DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::int8_t, char>);
1224DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kUInt8, 8, RColumnElementCastLE, <std::int8_t, std::uint8_t>);
1225DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kInt16, 16, RColumnElementCastLE, <std::int8_t, std::int16_t>);
1226DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kUInt16, 16, RColumnElementCastLE, <std::int8_t, std::uint16_t>);
1227DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kInt32, 32, RColumnElementCastLE, <std::int8_t, std::int32_t>);
1228DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kUInt32, 32, RColumnElementCastLE, <std::int8_t, std::uint32_t>);
1229DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kInt64, 64, RColumnElementCastLE, <std::int8_t, std::int64_t>);
1230DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kUInt64, 64, RColumnElementCastLE, <std::int8_t, std::uint64_t>);
1231DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1232 <std::int8_t, std::int16_t>);
1233DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1234 <std::int8_t, std::uint16_t>);
1235DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1236 <std::int8_t, std::int32_t>);
1237DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1238 <std::int8_t, std::uint32_t>);
1239DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1240 <std::int8_t, std::int64_t>);
1241DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1242 <std::int8_t, std::uint64_t>);
1243DECLARE_RCOLUMNELEMENT_SPEC(std::int8_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::int8_t>);
1244
1245DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(std::uint8_t, EColumnType::kUInt8, 8);
1246DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::uint8_t, char>);
1247DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kInt8, 8, RColumnElementCastLE, <std::uint8_t, std::int8_t>);
1248DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kInt16, 16, RColumnElementCastLE, <std::uint8_t, std::int16_t>);
1249DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kUInt16, 16, RColumnElementCastLE,
1250 <std::uint8_t, std::uint16_t>);
1251DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kInt32, 32, RColumnElementCastLE, <std::uint8_t, std::int32_t>);
1252DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kUInt32, 32, RColumnElementCastLE,
1253 <std::uint8_t, std::uint32_t>);
1254DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kInt64, 64, RColumnElementCastLE, <std::uint8_t, std::int64_t>);
1255DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kUInt64, 64, RColumnElementCastLE,
1256 <std::uint8_t, std::uint64_t>);
1257DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1258 <std::uint8_t, std::int16_t>);
1259DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1260 <std::uint8_t, std::uint16_t>);
1261DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1262 <std::uint8_t, std::int32_t>);
1263DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1264 <std::uint8_t, std::uint32_t>);
1265DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1266 <std::uint8_t, std::int64_t>);
1267DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1268 <std::uint8_t, std::uint64_t>);
1269DECLARE_RCOLUMNELEMENT_SPEC(std::uint8_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::uint8_t>);
1270
1271DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kInt16, 16, RColumnElementLE, <std::int16_t>);
1272DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1273 <std::int16_t, std::int16_t>);
1274DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::int16_t, char>);
1275DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kInt8, 8, RColumnElementCastLE, <std::int16_t, std::int8_t>);
1276DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kUInt8, 8, RColumnElementCastLE, <std::int16_t, std::uint8_t>);
1277DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kUInt16, 16, RColumnElementCastLE,
1278 <std::int16_t, std::uint16_t>);
1279DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kInt32, 32, RColumnElementCastLE, <std::int16_t, std::int32_t>);
1280DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kUInt32, 32, RColumnElementCastLE,
1281 <std::int16_t, std::uint32_t>);
1282DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kInt64, 64, RColumnElementCastLE, <std::int16_t, std::int64_t>);
1283DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kUInt64, 64, RColumnElementCastLE,
1284 <std::int16_t, std::uint64_t>);
1285DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1286 <std::int16_t, std::uint16_t>);
1287DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1288 <std::int16_t, std::int32_t>);
1289DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1290 <std::int16_t, std::uint32_t>);
1291DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1292 <std::int16_t, std::int64_t>);
1293DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1294 <std::int16_t, std::uint64_t>);
1295DECLARE_RCOLUMNELEMENT_SPEC(std::int16_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::int16_t>);
1296
1297DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kUInt16, 16, RColumnElementLE, <std::uint16_t>);
1298DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1299 <std::uint16_t, std::uint16_t>);
1300DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::uint16_t, char>);
1301DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kInt8, 8, RColumnElementCastLE, <std::uint16_t, std::int8_t>);
1302DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kUInt8, 8, RColumnElementCastLE, <std::uint16_t, std::uint8_t>);
1303DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kInt16, 16, RColumnElementCastLE,
1304 <std::uint16_t, std::int16_t>);
1305DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kInt32, 32, RColumnElementCastLE,
1306 <std::uint16_t, std::int32_t>);
1307DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kUInt32, 32, RColumnElementCastLE,
1308 <std::uint16_t, std::uint32_t>);
1309DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kInt64, 64, RColumnElementCastLE,
1310 <std::uint16_t, std::int64_t>);
1311DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kUInt64, 64, RColumnElementCastLE,
1312 <std::uint16_t, std::uint64_t>);
1313DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1314 <std::uint16_t, std::int16_t>);
1315DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1316 <std::uint16_t, std::int32_t>);
1317DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1318 <std::uint16_t, std::uint32_t>);
1319DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1320 <std::uint16_t, std::int64_t>);
1321DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1322 <std::uint16_t, std::uint64_t>);
1323DECLARE_RCOLUMNELEMENT_SPEC(std::uint16_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::uint16_t>);
1324
1325DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kInt32, 32, RColumnElementLE, <std::int32_t>);
1326DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1327 <std::int32_t, std::int32_t>);
1328DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::int32_t, char>);
1329DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kInt8, 8, RColumnElementCastLE, <std::int32_t, std::int8_t>);
1330DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kUInt8, 8, RColumnElementCastLE, <std::int32_t, std::uint8_t>);
1331DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kInt16, 16, RColumnElementCastLE, <std::int32_t, std::int16_t>);
1332DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kUInt16, 16, RColumnElementCastLE,
1333 <std::int32_t, std::uint16_t>);
1334DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kUInt32, 32, RColumnElementCastLE,
1335 <std::int32_t, std::uint32_t>);
1336DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kInt64, 64, RColumnElementCastLE, <std::int32_t, std::int64_t>);
1337DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kUInt64, 64, RColumnElementCastLE,
1338 <std::int32_t, std::uint64_t>);
1339DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1340 <std::int32_t, std::int16_t>);
1341DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1342 <std::int32_t, std::uint16_t>);
1343DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1344 <std::int32_t, std::uint32_t>);
1345DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1346 <std::int32_t, std::int64_t>);
1347DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1348 <std::int32_t, std::uint64_t>);
1349DECLARE_RCOLUMNELEMENT_SPEC(std::int32_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::int32_t>);
1350
1351DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kUInt32, 32, RColumnElementLE, <std::uint32_t>);
1352DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1353 <std::uint32_t, std::uint32_t>);
1354DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::uint32_t, char>);
1355DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kInt8, 8, RColumnElementCastLE, <std::uint32_t, std::int8_t>);
1356DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kUInt8, 8, RColumnElementCastLE, <std::uint32_t, std::uint8_t>);
1357DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kInt16, 16, RColumnElementCastLE,
1358 <std::uint32_t, std::int16_t>);
1359DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kUInt16, 16, RColumnElementCastLE,
1360 <std::uint32_t, std::uint16_t>);
1361DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kInt32, 32, RColumnElementCastLE,
1362 <std::uint32_t, std::int32_t>);
1363DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kInt64, 64, RColumnElementCastLE,
1364 <std::uint32_t, std::int64_t>);
1365DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kUInt64, 64, RColumnElementCastLE,
1366 <std::uint32_t, std::uint64_t>);
1367DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1368 <std::uint32_t, std::int16_t>);
1369DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1370 <std::uint32_t, std::uint16_t>);
1371DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1372 <std::uint32_t, std::int32_t>);
1373DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1374 <std::uint32_t, std::int64_t>);
1375DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1376 <std::uint32_t, std::uint64_t>);
1377DECLARE_RCOLUMNELEMENT_SPEC(std::uint32_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::uint32_t>);
1378
1379DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt64, 64, RColumnElementLE, <std::int64_t>);
1380DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1381 <std::int64_t, std::int64_t>);
1382DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::int64_t, char>);
1383DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt8, 8, RColumnElementCastLE, <std::int64_t, std::int8_t>);
1384DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kUInt8, 8, RColumnElementCastLE, <std::int64_t, std::uint8_t>);
1385DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt16, 16, RColumnElementCastLE, <std::int64_t, std::int16_t>);
1386DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kUInt16, 16, RColumnElementCastLE,
1387 <std::int64_t, std::uint16_t>);
1388DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kInt32, 32, RColumnElementCastLE, <std::int64_t, std::int32_t>);
1389DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kUInt32, 32, RColumnElementCastLE,
1390 <std::int64_t, std::uint32_t>);
1391DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kUInt64, 64, RColumnElementCastLE,
1392 <std::int64_t, std::uint64_t>);
1393DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1394 <std::int64_t, std::int16_t>);
1395DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1396 <std::int64_t, std::uint16_t>);
1397DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1398 <std::int64_t, std::int32_t>);
1399DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1400 <std::int64_t, std::uint32_t>);
1401DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1402 <std::int64_t, std::uint64_t>);
1403DECLARE_RCOLUMNELEMENT_SPEC(std::int64_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::int64_t>);
1404
1405DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kUInt64, 64, RColumnElementLE, <std::uint64_t>);
1406DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitUInt64, 64, RColumnElementSplitLE,
1407 <std::uint64_t, std::uint64_t>);
1408DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kChar, 8, RColumnElementCastLE, <std::uint64_t, char>);
1409DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kInt8, 8, RColumnElementCastLE, <std::uint64_t, std::int8_t>);
1410DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kUInt8, 8, RColumnElementCastLE, <std::uint64_t, std::uint8_t>);
1411DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kInt16, 16, RColumnElementCastLE,
1412 <std::uint64_t, std::int16_t>);
1413DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kUInt16, 16, RColumnElementCastLE,
1414 <std::uint64_t, std::uint16_t>);
1415DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kInt32, 32, RColumnElementCastLE,
1416 <std::uint64_t, std::int32_t>);
1417DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kUInt32, 32, RColumnElementCastLE,
1418 <std::uint64_t, std::uint32_t>);
1419DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kInt64, 64, RColumnElementCastLE,
1420 <std::uint64_t, std::int64_t>);
1421DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitInt16, 16, RColumnElementZigzagSplitLE,
1422 <std::uint64_t, std::int16_t>);
1423DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitUInt16, 16, RColumnElementSplitLE,
1424 <std::uint64_t, std::uint16_t>);
1425DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitInt32, 32, RColumnElementZigzagSplitLE,
1426 <std::uint64_t, std::int32_t>);
1427DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitUInt32, 32, RColumnElementSplitLE,
1428 <std::uint64_t, std::uint32_t>);
1429DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kSplitInt64, 64, RColumnElementZigzagSplitLE,
1430 <std::uint64_t, std::int64_t>);
1431DECLARE_RCOLUMNELEMENT_SPEC(std::uint64_t, EColumnType::kBit, 1, RColumnElementIntAsBool, <std::uint64_t>);
1432
1433DECLARE_RCOLUMNELEMENT_SPEC(float, EColumnType::kReal32, 32, RColumnElementLE, <float>);
1434DECLARE_RCOLUMNELEMENT_SPEC(float, EColumnType::kSplitReal32, 32, RColumnElementSplitLE, <float, float>);
1435DECLARE_RCOLUMNELEMENT_SPEC(float, EColumnType::kReal64, 64, RColumnElementCastLE, <float, double>);
1436DECLARE_RCOLUMNELEMENT_SPEC(float, EColumnType::kSplitReal64, 64, RColumnElementSplitLE, <float, double>);
1437
1438DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kReal64, 64, RColumnElementLE, <double>);
1439DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kSplitReal64, 64, RColumnElementSplitLE, <double, double>);
1440DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kReal32, 32, RColumnElementCastLE, <double, float>);
1441DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kSplitReal32, 32, RColumnElementSplitLE, <double, float>);
1442
1443DECLARE_RCOLUMNELEMENT_SPEC(ROOT::Experimental::ClusterSize_t, EColumnType::kIndex64, 64, RColumnElementLE,
1444 <std::uint64_t>);
1445DECLARE_RCOLUMNELEMENT_SPEC(ROOT::Experimental::ClusterSize_t, EColumnType::kIndex32, 32, RColumnElementCastLE,
1446 <std::uint64_t, std::uint32_t>);
1448 RColumnElementDeltaSplitLE, <std::uint64_t, std::uint64_t>);
1450 RColumnElementDeltaSplitLE, <std::uint64_t, std::uint32_t>);
1451
1452template <>
1453class RColumnElement<ROOT::Experimental::Internal::RTestFutureColumn, kTestFutureType> final
1454 : public RColumnElementBase {
1455public:
1456 static constexpr bool kIsMappable = false;
1457 static constexpr std::size_t kSize = sizeof(ROOT::Experimental::Internal::RTestFutureColumn);
1458 static constexpr std::size_t kBitsOnStorage = kSize * 8;
1459 RColumnElement() : RColumnElementBase(kSize, kBitsOnStorage) {}
1460
1461 bool IsMappable() const { return kIsMappable; }
1462 void Pack(void *, const void *, std::size_t) const {}
1463 void Unpack(void *, const void *, std::size_t) const {}
1464
1465 RIdentifier GetIdentifier() const final
1466 {
1467 return RIdentifier{typeid(ROOT::Experimental::Internal::RTestFutureColumn), kTestFutureType};
1468 }
1469};
1470
1471inline void
1472RColumnElement<bool, ROOT::Experimental::EColumnType::kBit>::Pack(void *dst, const void *src, std::size_t count) const
1473{
1474 const bool *boolArray = reinterpret_cast<const bool *>(src);
1475 char *charArray = reinterpret_cast<char *>(dst);
1476 std::bitset<8> bitSet;
1477 std::size_t i = 0;
1478 for (; i < count; ++i) {
1479 bitSet.set(i % 8, boolArray[i]);
1480 if (i % 8 == 7) {
1481 char packed = bitSet.to_ulong();
1482 charArray[i / 8] = packed;
1483 }
1484 }
1485 if (i % 8 != 0) {
1486 char packed = bitSet.to_ulong();
1487 charArray[i / 8] = packed;
1488 }
1489}
1490
1491inline void
1492RColumnElement<bool, ROOT::Experimental::EColumnType::kBit>::Unpack(void *dst, const void *src, std::size_t count) const
1493{
1494 bool *boolArray = reinterpret_cast<bool *>(dst);
1495 const char *charArray = reinterpret_cast<const char *>(src);
1496 std::bitset<8> bitSet;
1497 for (std::size_t i = 0; i < count; i += 8) {
1498 bitSet = charArray[i / 8];
1499 for (std::size_t j = i; j < std::min(count, i + 8); ++j) {
1500 boolArray[j] = bitSet[j % 8];
1501 }
1502 }
1503}
1504
1505} // namespace
#define R__LITTLE_ENDIAN
#define DECLARE_RCOLUMNELEMENT_SPEC_SIMPLE(CppT, ColumnT, BitsOnStorage)
#define DECLARE_RCOLUMNELEMENT_SPEC(CppT, ColumnT, BitsOnStorage, BaseT,...)
These macros are used to declare RColumnElement template specializations below.
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:290
#define b(i)
Definition RSha256.hxx:100
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
char name[80]
Definition TGX11.cxx:110
float * q
@ kSize
Definition TStructNode.h:26
@ kUnknown
Definition TStructNode.h:19
The available trivial, native content types of a column.
A column element encapsulates the translation between basic C++ types and their column representation...
Holds the index and the tag of a kSwitch column.
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Double_t x[n]
Definition legend1.C:17
void PackBits(void *dst, const void *src, std::size_t count, std::size_t sizeofSrc, std::size_t nDstBits)
Tightly packs count items of size sizeofSrc contained in src into dst using nDstBits per item.
constexpr std::size_t MinBufSize(std::size_t count, std::size_t nDstBits)
Returns the minimum safe size (in bytes) of a buffer that is intended to be used as a destination for...
void UnpackBits(void *dst, const void *src, std::size_t count, std::size_t sizeofDst, std::size_t nSrcBits)
Undoes the effect of PackBits.
std::uint16_t FloatToHalf(float value)
Convert an IEEE single-precision float to half-precision.
Definition RFloat16.hxx:96
float HalfToFloat(std::uint16_t value)
Convert an IEEE half-precision float to single-precision.
Definition RFloat16.hxx:132
RClusterSize ClusterSize_t
double T(double x)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
__device__ AFloat max(AFloat x, AFloat y)
Definition Kernels.cuh:207
Helper templated class for swapping bytes; specializations for N={2,4,8} are provided below.
Definition Byteswap.h:124
Wrap the integer in a struct in order to avoid template specialization clash with std::uint64_t.
unsigned char byte
Definition gifdecode.c:10