xrpld
Loading...
Searching...
No Matches
codec.h
1#pragma once
2
3// Disable lz4 deprecation warning due to incompatibility with clang attributes
4#include <array>
5#include <cstdint>
6#include <stdexcept>
7#include <utility>
8#define LZ4_DISABLE_DEPRECATE_WARNINGS
9
10#include <xrpl/basics/contract.h>
11#include <xrpl/basics/safe_cast.h>
12#include <xrpl/nodestore/NodeObject.h>
13#include <xrpl/nodestore/detail/Varint.h>
14#include <xrpl/protocol/HashPrefix.h>
15
16#include <nudb/detail/field.hpp>
17
18#include <lz4.h>
19
20#include <cstddef>
21#include <cstring>
22#include <string>
23
24namespace xrpl::node_store {
25
26template <class BufferFactory>
27std::pair<void const*, std::size_t>
28lz4Decompress(void const* in, std::size_t inSize, BufferFactory&& bf)
29{
30 if (static_cast<int>(inSize) < 0)
31 Throw<std::runtime_error>("lz4_decompress: integer overflow (input)");
32
33 std::size_t outSize = 0;
34
35 auto const n = readVarint(reinterpret_cast<std::uint8_t const*>(in), inSize, outSize);
36
37 if (n == 0 || n >= inSize)
38 Throw<std::runtime_error>("lz4_decompress: invalid blob");
39
40 if (static_cast<int>(outSize) <= 0)
41 Throw<std::runtime_error>("lz4_decompress: integer overflow (output)");
42
43 void* const out = bf(outSize);
44
45 if (LZ4_decompress_safe(
46 reinterpret_cast<char const*>(in) + n,
47 reinterpret_cast<char*>(out),
48 static_cast<int>(inSize - n),
49 static_cast<int>(outSize)) != static_cast<int>(outSize))
50 Throw<std::runtime_error>("lz4_decompress: LZ4_decompress_safe");
51
52 return {out, outSize};
53}
54
55template <class BufferFactory>
57lz4Compress(void const* in, std::size_t inSize, BufferFactory&& bf)
58{
60 using namespace nudb::detail;
63 auto const n = writeVarint(vi.data(), inSize);
64 auto const outMax = LZ4_compressBound(inSize);
65 auto* out = reinterpret_cast<std::uint8_t*>(bf(n + outMax));
66 result.first = out;
67 std::memcpy(out, vi.data(), n);
68 auto const outSize = LZ4_compress_default(
69 reinterpret_cast<char const*>(in), reinterpret_cast<char*>(out + n), inSize, outMax);
70 if (outSize == 0)
71 Throw<std::runtime_error>("lz4 compress");
72 result.second = n + outSize;
73 return result;
74}
75
76//------------------------------------------------------------------------------
77
78/*
79 object types:
80
81 0 = Uncompressed
82 1 = lz4 compressed
83 2 = inner node compressed
84 3 = full inner node
85*/
86
87template <class BufferFactory>
89nodeobjectDecompress(void const* in, std::size_t inSize, BufferFactory&& bf)
90{
91 using namespace nudb::detail;
92
93 auto const* p = reinterpret_cast<std::uint8_t const*>(in);
94 std::size_t type = 0;
95 auto const vn = readVarint(p, inSize, type);
96 if (vn == 0)
97 Throw<std::runtime_error>("nodeobject decompress");
98 p += vn;
99 inSize -= vn;
100
102 switch (type)
103 {
104 case 0: // uncompressed
105 {
106 result.first = p;
107 result.second = inSize;
108 break;
109 }
110 case 1: // lz4
111 {
112 result = lz4Decompress(p, inSize, bf);
113 break;
114 }
115 case 2: // compressed v1 inner node
116 {
117 auto const hs = field<std::uint16_t>::size; // Mask
118 if (inSize < hs + 32)
119 {
121 "nodeobject codec v1: short inner node size: " + std::string("in_size = ") +
122 std::to_string(inSize) + " hs = " + std::to_string(hs));
123 }
124 istream is(p, inSize);
125 std::uint16_t mask = 0;
126 read<std::uint16_t>(is, mask); // Mask
127 inSize -= hs;
128 result.second = 525;
129 void* const out = bf(result.second);
130 result.first = out;
131 ostream os(out, result.second);
136 if (mask == 0)
137 Throw<std::runtime_error>("nodeobject codec v1: empty inner node");
138 std::uint16_t bit = 0x8000;
139 for (int i = 16; i--; bit >>= 1)
140 {
141 if (mask & bit)
142 {
143 if (inSize < 32)
144 {
146 "nodeobject codec v1: short inner node subsize: " +
147 std::string("in_size = ") + std::to_string(inSize) +
148 " i = " + std::to_string(i));
149 }
150 std::memcpy(os.data(32), is(32), 32);
151 inSize -= 32;
152 }
153 else
154 {
155 std::memset(os.data(32), 0, 32);
156 }
157 }
158 if (inSize > 0)
159 {
161 "nodeobject codec v1: long inner node, in_size = " + std::to_string(inSize));
162 }
163 break;
164 }
165 case 3: // full v1 inner node
166 {
167 if (inSize != 16 * 32)
168 { // hashes
170 "nodeobject codec v1: short full inner node, in_size = " +
171 std::to_string(inSize));
172 }
173 istream is(p, inSize);
174 result.second = 525;
175 void* const out = bf(result.second);
176 result.first = out;
177 ostream os(out, result.second);
182 write(os, is(512), 512);
183 break;
184 }
185 default:
186 Throw<std::runtime_error>("nodeobject codec: bad type=" + std::to_string(type));
187 };
188 return result;
189}
190
191template <class = void>
192void const*
194{
195 static std::array<char, 32> kV{};
196 return kV.data();
197}
198
199template <class BufferFactory>
201nodeobjectCompress(void const* in, std::size_t inSize, BufferFactory&& bf)
202{
203 using std::runtime_error;
204 using namespace nudb::detail;
205
206 // Check for inner node v1
207 if (inSize == 525)
208 {
209 istream is(in, inSize);
210 std::uint32_t index = 0;
211 std::uint32_t unused = 0;
212 std::uint8_t kind = 0;
213 std::uint32_t prefix = 0;
214 read<std::uint32_t>(is, index);
215 read<std::uint32_t>(is, unused);
216 read<std::uint8_t>(is, kind);
217 read<std::uint32_t>(is, prefix);
219 {
220 std::size_t n = 0;
221 std::uint16_t mask = 0;
223 for (unsigned bit = 0x8000; bit; bit >>= 1)
224 {
225 void const* const h = is(32);
226 if (std::memcmp(h, zero32(), 32) == 0)
227 continue;
228 std::memcpy(vh.data() + (32 * n), h, 32);
229 mask |= bit;
230 ++n;
231 }
233 if (n < 16)
234 {
235 // 2 = v1 inner node compressed
236 auto const type = 2U;
237 auto const vs = sizeVarint(type);
238 result.second = vs + field<std::uint16_t>::size + // mask
239 (n * 32); // hashes
240 auto* out = reinterpret_cast<std::uint8_t*>(bf(result.second));
241 result.first = out;
242 ostream os(out, result.second);
243 write<Varint>(os, type);
244 write<std::uint16_t>(os, mask);
245 write(os, vh.data(), n * 32);
246 return result;
247 }
248 // 3 = full v1 inner node
249 auto const type = 3U;
250 auto const vs = sizeVarint(type);
251 result.second = vs + (n * 32); // hashes
252 auto* out = reinterpret_cast<std::uint8_t*>(bf(result.second));
253 result.first = out;
254 ostream os(out, result.second);
255 write<Varint>(os, type);
256 write(os, vh.data(), n * 32);
257 return result;
258 }
259 }
260
262
263 static constexpr std::size_t kCodecType = 1;
264 auto const vn = writeVarint(vi.data(), kCodecType);
266 switch (kCodecType)
267 {
268 // case 0 was uncompressed data; we always compress now.
269 case 1: // lz4
270 {
271 std::uint8_t* p = nullptr;
272 auto const lzr = node_store::lz4Compress(in, inSize, [&p, &vn, &bf](std::size_t n) {
273 p = reinterpret_cast<std::uint8_t*>(bf(vn + n));
274 return p + vn;
275 });
276 std::memcpy(p, vi.data(), vn);
277 result.first = p;
278 result.second = vn + lzr.second;
279 break;
280 }
281 default:
282 Throw<std::logic_error>("nodeobject codec: unknown=" + std::to_string(kCodecType));
283 };
284 return result;
285}
286
287// Modifies an inner node to erase the ledger
288// sequence and type information so the codec
289// verification can pass.
290//
291template <class = void>
292void
293filterInner(void* in, std::size_t inSize)
294{
295 using namespace nudb::detail;
296
297 // Check for inner node
298 if (inSize == 525)
299 {
300 istream is(in, inSize);
301 std::uint32_t index = 0;
302 std::uint32_t unused = 0;
303 std::uint8_t kind = 0;
304 std::uint32_t prefix = 0;
305 read<std::uint32_t>(is, index);
306 read<std::uint32_t>(is, unused);
307 read<std::uint8_t>(is, kind);
308 read<std::uint32_t>(is, prefix);
310 {
311 ostream os(in, 9);
315 }
316 }
317}
318
319} // namespace xrpl::node_store
T data(T... args)
T memcmp(T... args)
T memcpy(T... args)
T memset(T... args)
std::pair< void const *, std::size_t > nodeobjectCompress(void const *in, std::size_t inSize, BufferFactory &&bf)
Definition codec.h:201
std::size_t readVarint(void const *buf, std::size_t buflen, std::size_t &t)
Definition Varint.h:37
std::pair< void const *, std::size_t > lz4Decompress(void const *in, std::size_t inSize, BufferFactory &&bf)
Definition codec.h:28
std::pair< void const *, std::size_t > lz4Compress(void const *in, std::size_t inSize, BufferFactory &&bf)
Definition codec.h:57
std::size_t writeVarint(void *p0, std::size_t v)
Definition Varint.h:87
std::size_t sizeVarint(T v)
Definition Varint.h:73
void write(nudb::detail::ostream &os, std::size_t t)
Definition Varint.h:120
std::pair< void const *, std::size_t > nodeobjectDecompress(void const *in, std::size_t inSize, BufferFactory &&bf)
Definition codec.h:89
void read(nudb::detail::istream &is, std::size_t &u)
Definition Varint.h:106
void const * zero32()
Definition codec.h:193
void filterInner(void *in, std::size_t inSize)
Definition codec.h:293
constexpr Dest safeCast(Src s) noexcept
Definition safe_cast.h:21
@ InnerNode
inner node in V1 tree
Definition HashPrefix.h:54
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
T to_string(T... args)