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