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