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