Project
Loading...
Searching...
No Matches
RootFraming.h
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#ifndef O2_FRAMEWORK_AODNATIVE_TEST_ROOTFRAMING_H_
13#define O2_FRAMEWORK_AODNATIVE_TEST_ROOTFRAMING_H_
14
15// Helpers to build ROOT-framed compressed payloads and TBasket keys in memory,
16// so that codecs and the decoder can be tested without ROOT or a file.
17
19#include "AODNative/Codecs.h"
20#include "AODNative/Errors.h"
21#include <cstring>
22#include <lz4.h>
23#include <lzma.h>
24#include <stdexcept>
25#include <string>
26#include <vector>
27#include <zlib.h>
28#include <zstd.h>
29
31{
32
33enum struct Algo { ZSTD,
34 ZLIB,
35 LZMA,
36 LZ4,
37 None };
38
39inline std::vector<uint8_t> compressChunk(Algo algo, std::span<const uint8_t> in)
40{
41 std::vector<uint8_t> out;
42 switch (algo) {
43 case Algo::ZSTD: {
44 out.resize(ZSTD_compressBound(in.size()));
45 out.resize(ZSTD_compress(out.data(), out.size(), in.data(), in.size(), 5));
46 } break;
47 case Algo::ZLIB: {
48 uLongf n = compressBound(in.size());
49 out.resize(n);
50 compress2(out.data(), &n, in.data(), in.size(), 6);
51 out.resize(n);
52 } break;
53 case Algo::LZMA: {
54 out.resize(lzma_stream_buffer_bound(in.size()));
55 size_t pos = 0;
56 lzma_easy_buffer_encode(6, LZMA_CHECK_CRC32, nullptr, in.data(), in.size(), out.data(), &pos, out.size());
57 out.resize(pos);
58 } break;
59 case Algo::LZ4: {
60 std::vector<uint8_t> block(LZ4_compressBound(in.size()));
61 block.resize(LZ4_compress_default((char const*)in.data(), (char*)block.data(), in.size(), block.size()));
62 auto checksum = xxh64(block);
63 for (int i = 7; i >= 0; --i) {
64 out.push_back((checksum >> (8 * i)) & 0xff);
65 }
66 out.insert(out.end(), block.begin(), block.end());
67 } break;
68 case Algo::None:
69 throw std::logic_error("no chunk for uncompressed data");
70 }
71 return out;
72}
73
76inline std::vector<uint8_t> rootCompress(Algo algo, std::span<const uint8_t> in, size_t chunkSize = 0xffffff)
77{
78 static const char* tags[] = {"ZS", "ZL", "XZ", "L4"};
79 std::vector<uint8_t> out;
80 for (size_t pos = 0; pos < in.size(); pos += chunkSize) {
81 auto piece = in.subspan(pos, std::min(chunkSize, in.size() - pos));
82 auto c = compressChunk(algo, piece);
83 out.push_back(tags[(int)algo][0]);
84 out.push_back(tags[(int)algo][1]);
85 out.push_back(algo == Algo::ZLIB ? 8 : 1);
86 for (auto v : {c.size(), piece.size()}) {
87 out.push_back(v & 0xff);
88 out.push_back((v >> 8) & 0xff);
89 out.push_back((v >> 16) & 0xff);
90 }
91 out.insert(out.end(), c.begin(), c.end());
92 }
93 return out;
94}
95
97 std::vector<uint8_t> bytes;
98 template <typename T>
99 void put(T v)
100 {
101 for (int i = sizeof(T) - 1; i >= 0; --i) {
102 bytes.push_back((uint64_t(v) >> (8 * i)) & 0xff);
103 }
104 }
105 void string(std::string const& s)
106 {
107 bytes.push_back(s.size());
108 bytes.insert(bytes.end(), s.begin(), s.end());
109 }
110};
111
114inline std::vector<uint8_t> makeBasket(Algo algo, std::span<const uint8_t> values, int32_t nevbuf, size_t tailBytes = 0, size_t chunkSize = 0xffffff)
115{
116 std::vector<uint8_t> raw(values.begin(), values.end());
117 // The tail is poisoned: a decoder that reads past the value region sees it.
118 raw.resize(values.size() + tailBytes, 0xEE);
119 std::vector<uint8_t> payload = algo == Algo::None ? raw : rootCompress(algo, raw, chunkSize);
120
122 auto build = [&](uint16_t keylen) {
123 h.bytes.clear();
124 h.put<uint32_t>(keylen + payload.size()); // fNbytes
125 h.put<int16_t>(4); // key version
126 h.put<uint32_t>(raw.size()); // fObjlen
127 h.put<uint32_t>(0); // fDatime
128 h.put<uint16_t>(keylen);
129 h.put<uint16_t>(1); // fCycle
130 h.put<uint32_t>(0); // fSeekKey
131 h.put<uint32_t>(0); // fSeekPdir
132 h.string("TBasket"); // class, name, title
133 h.string("fX");
134 h.string("O2track");
135 h.put<uint16_t>(3); // fVersion
136 h.put<int32_t>(32000); // fBufferSize
137 h.put<int32_t>(0); // fNevBufSize
138 h.put<int32_t>(nevbuf); // fNevBuf
139 h.put<int32_t>(keylen + values.size()); // fLast
140 h.put<uint8_t>(tailBytes ? 1 : 2); // flag
141 };
142 build(0);
143 auto keylen = (uint16_t)h.bytes.size();
144 build(keylen);
145 h.bytes.insert(h.bytes.end(), payload.begin(), payload.end());
146 return h.bytes;
147}
148
151{
152 public:
153 explicit MemoryByteSource(std::vector<uint8_t> data, bool mappable = true) : mData(std::move(data)), mMappable(mappable) {}
154 uint64_t size() const override { return mData.size(); }
155 void readInto(uint64_t off, size_t len, void* dst) override
156 {
157 if (off > mData.size() || len > mData.size() - off) {
158 throw CorruptData("memory", off, "read past the end");
159 }
160 if (len == 0) {
161 return;
162 }
163 std::memcpy(dst, mData.data() + off, len);
164 ++mCalls;
165 mBytes += len;
166 }
167 void readvInto(std::span<ReadRequest> requests) override
168 {
169 for (auto& r : requests) {
170 readInto(r.offset, r.length, r.dst);
171 }
172 }
173 std::span<const uint8_t> tryMap(uint64_t off, size_t len) override
174 {
175 if (!mMappable) {
176 return {};
177 }
178 if (off > mData.size() || len > mData.size() - off) {
179 throw CorruptData("memory", off, "map past the end");
180 }
181 return std::span<const uint8_t>(mData).subspan(off, len);
182 }
183 uint64_t bytesRead() const override { return mBytes; }
184 uint64_t readCalls() const override { return mCalls; }
185 std::string url() const override { return "memory"; }
186
187 private:
188 std::vector<uint8_t> mData;
189 bool mMappable;
190 uint64_t mBytes = 0, mCalls = 0;
191};
192
193} // namespace o2::aod::native::test
194
195#endif // O2_FRAMEWORK_AODNATIVE_TEST_ROOTFRAMING_H_
int32_t i
o2::raw::RawFileWriter * raw
uint16_t pos
Definition RawData.h:3
uint32_t c
Definition RawData.h:2
Class for time synchronization of RawReader instances.
A file made of concatenated in-memory buffers.
std::span< const uint8_t > tryMap(uint64_t off, size_t len) override
A view of the bytes without copying them, if the backend can provide it.
void readInto(uint64_t off, size_t len, void *dst) override
Throws IoError, or CorruptData when the range is past the end of the file.
MemoryByteSource(std::vector< uint8_t > data, bool mappable=true)
uint64_t bytesRead() const override
uint64_t readCalls() const override
std::string url() const override
void readvInto(std::span< ReadRequest > requests) override
GLdouble n
Definition glcorearb.h:1982
const GLdouble * v
Definition glcorearb.h:832
GLenum GLenum dst
Definition glcorearb.h:1767
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
GLboolean * data
Definition glcorearb.h:298
GLboolean r
Definition glcorearb.h:1233
GLenum GLenum GLsizei len
Definition glcorearb.h:4232
std::vector< uint8_t > compressChunk(Algo algo, std::span< const uint8_t > in)
Definition RootFraming.h:39
std::vector< uint8_t > makeBasket(Algo algo, std::span< const uint8_t > values, int32_t nevbuf, size_t tailBytes=0, size_t chunkSize=0xffffff)
std::vector< uint8_t > rootCompress(Algo algo, std::span< const uint8_t > in, size_t chunkSize=0xffffff)
Definition RootFraming.h:76
uint64_t xxh64(std::span< const uint8_t > data)
XXH64 with seed 0, as used for the LZ4 chunk checksum.
Definition Codecs.cxx:319
void string(std::string const &s)
Marks an empty item in the context.