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