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