rippled
Loading...
Searching...
No Matches
codec.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#ifndef RIPPLE_NODESTORE_CODEC_H_INCLUDED
21#define RIPPLE_NODESTORE_CODEC_H_INCLUDED
22
23// Disable lz4 deprecation warning due to incompatibility with clang attributes
24#define LZ4_DISABLE_DEPRECATE_WARNINGS
25
26#include <xrpl/basics/contract.h>
27#include <xrpl/basics/safe_cast.h>
28#include <xrpl/nodestore/NodeObject.h>
29#include <xrpl/nodestore/detail/varint.h>
30#include <xrpl/protocol/HashPrefix.h>
31
32#include <nudb/detail/field.hpp>
33
34#include <lz4.h>
35
36#include <cstddef>
37#include <cstring>
38#include <string>
39
40namespace ripple {
41namespace NodeStore {
42
43template <class BufferFactory>
45lz4_decompress(void const* in, std::size_t in_size, BufferFactory&& bf)
46{
47 if (static_cast<int>(in_size) < 0)
48 Throw<std::runtime_error>("lz4_decompress: integer overflow (input)");
49
50 std::size_t outSize = 0;
51
52 auto const n = read_varint(
53 reinterpret_cast<std::uint8_t const*>(in), in_size, outSize);
54
55 if (n == 0 || n >= in_size)
56 Throw<std::runtime_error>("lz4_decompress: invalid blob");
57
58 if (static_cast<int>(outSize) <= 0)
59 Throw<std::runtime_error>("lz4_decompress: integer overflow (output)");
60
61 void* const out = bf(outSize);
62
63 if (LZ4_decompress_safe(
64 reinterpret_cast<char const*>(in) + n,
65 reinterpret_cast<char*>(out),
66 static_cast<int>(in_size - n),
67 static_cast<int>(outSize)) != static_cast<int>(outSize))
68 Throw<std::runtime_error>("lz4_decompress: LZ4_decompress_safe");
69
70 return {out, outSize};
71}
72
73template <class BufferFactory>
75lz4_compress(void const* in, std::size_t in_size, BufferFactory&& bf)
76{
78 using namespace nudb::detail;
81 auto const n = write_varint(vi.data(), in_size);
82 auto const out_max = LZ4_compressBound(in_size);
83 std::uint8_t* out = reinterpret_cast<std::uint8_t*>(bf(n + out_max));
84 result.first = out;
85 std::memcpy(out, vi.data(), n);
86 auto const out_size = LZ4_compress_default(
87 reinterpret_cast<char const*>(in),
88 reinterpret_cast<char*>(out + n),
89 in_size,
90 out_max);
91 if (out_size == 0)
92 Throw<std::runtime_error>("lz4 compress");
93 result.second = n + out_size;
94 return result;
95}
96
97//------------------------------------------------------------------------------
98
99/*
100 object types:
101
102 0 = Uncompressed
103 1 = lz4 compressed
104 2 = inner node compressed
105 3 = full inner node
106*/
107
108template <class BufferFactory>
110nodeobject_decompress(void const* in, std::size_t in_size, BufferFactory&& bf)
111{
112 using namespace nudb::detail;
113
114 std::uint8_t const* p = reinterpret_cast<std::uint8_t const*>(in);
115 std::size_t type;
116 auto const vn = read_varint(p, in_size, type);
117 if (vn == 0)
118 Throw<std::runtime_error>("nodeobject decompress");
119 p += vn;
120 in_size -= vn;
121
123 switch (type)
124 {
125 case 0: // uncompressed
126 {
127 result.first = p;
128 result.second = in_size;
129 break;
130 }
131 case 1: // lz4
132 {
133 result = lz4_decompress(p, in_size, bf);
134 break;
135 }
136 case 2: // compressed v1 inner node
137 {
138 auto const hs = field<std::uint16_t>::size; // Mask
139 if (in_size < hs + 32)
140 Throw<std::runtime_error>(
141 "nodeobject codec v1: short inner node size: " +
142 std::string("in_size = ") + std::to_string(in_size) +
143 " hs = " + std::to_string(hs));
144 istream is(p, in_size);
145 std::uint16_t mask;
146 read<std::uint16_t>(is, mask); // Mask
147 in_size -= hs;
148 result.second = 525;
149 void* const out = bf(result.second);
150 result.first = out;
151 ostream os(out, result.second);
152 write<std::uint32_t>(os, 0);
153 write<std::uint32_t>(os, 0);
154 write<std::uint8_t>(os, hotUNKNOWN);
155 write<std::uint32_t>(
156 os, static_cast<std::uint32_t>(HashPrefix::innerNode));
157 if (mask == 0)
158 Throw<std::runtime_error>(
159 "nodeobject codec v1: empty inner node");
160 std::uint16_t bit = 0x8000;
161 for (int i = 16; i--; bit >>= 1)
162 {
163 if (mask & bit)
164 {
165 if (in_size < 32)
166 Throw<std::runtime_error>(
167 "nodeobject codec v1: short inner node subsize: " +
168 std::string("in_size = ") +
169 std::to_string(in_size) +
170 " i = " + std::to_string(i));
171 std::memcpy(os.data(32), is(32), 32);
172 in_size -= 32;
173 }
174 else
175 {
176 std::memset(os.data(32), 0, 32);
177 }
178 }
179 if (in_size > 0)
180 Throw<std::runtime_error>(
181 "nodeobject codec v1: long inner node, in_size = " +
182 std::to_string(in_size));
183 break;
184 }
185 case 3: // full v1 inner node
186 {
187 if (in_size != 16 * 32) // hashes
188 Throw<std::runtime_error>(
189 "nodeobject codec v1: short full inner node, in_size = " +
190 std::to_string(in_size));
191 istream is(p, in_size);
192 result.second = 525;
193 void* const out = bf(result.second);
194 result.first = out;
195 ostream os(out, result.second);
196 write<std::uint32_t>(os, 0);
197 write<std::uint32_t>(os, 0);
198 write<std::uint8_t>(os, hotUNKNOWN);
199 write<std::uint32_t>(
200 os, static_cast<std::uint32_t>(HashPrefix::innerNode));
201 write(os, is(512), 512);
202 break;
203 }
204 default:
205 Throw<std::runtime_error>(
206 "nodeobject codec: bad type=" + std::to_string(type));
207 };
208 return result;
209}
210
211template <class = void>
212void const*
214{
215 static std::array<char, 32> v{};
216 return v.data();
217}
218
219template <class BufferFactory>
221nodeobject_compress(void const* in, std::size_t in_size, BufferFactory&& bf)
222{
223 using std::runtime_error;
224 using namespace nudb::detail;
225
226 // Check for inner node v1
227 if (in_size == 525)
228 {
229 istream is(in, in_size);
230 std::uint32_t index;
231 std::uint32_t unused;
232 std::uint8_t kind;
233 std::uint32_t prefix;
234 read<std::uint32_t>(is, index);
235 read<std::uint32_t>(is, unused);
236 read<std::uint8_t>(is, kind);
237 read<std::uint32_t>(is, prefix);
238 if (safe_cast<HashPrefix>(prefix) == HashPrefix::innerNode)
239 {
240 std::size_t n = 0;
241 std::uint16_t mask = 0;
243 for (unsigned bit = 0x8000; bit; bit >>= 1)
244 {
245 void const* const h = is(32);
246 if (std::memcmp(h, zero32(), 32) == 0)
247 continue;
248 std::memcpy(vh.data() + 32 * n, h, 32);
249 mask |= bit;
250 ++n;
251 }
253 if (n < 16)
254 {
255 // 2 = v1 inner node compressed
256 auto const type = 2U;
257 auto const vs = size_varint(type);
258 result.second = vs + field<std::uint16_t>::size + // mask
259 n * 32; // hashes
261 reinterpret_cast<std::uint8_t*>(bf(result.second));
262 result.first = out;
263 ostream os(out, result.second);
264 write<varint>(os, type);
265 write<std::uint16_t>(os, mask);
266 write(os, vh.data(), n * 32);
267 return result;
268 }
269 // 3 = full v1 inner node
270 auto const type = 3U;
271 auto const vs = size_varint(type);
272 result.second = vs + n * 32; // hashes
274 reinterpret_cast<std::uint8_t*>(bf(result.second));
275 result.first = out;
276 ostream os(out, result.second);
277 write<varint>(os, type);
278 write(os, vh.data(), n * 32);
279 return result;
280 }
281 }
282
284
285 constexpr std::size_t codecType = 1;
286 auto const vn = write_varint(vi.data(), codecType);
288 switch (codecType)
289 {
290 // case 0 was uncompressed data; we always compress now.
291 case 1: // lz4
292 {
293 std::uint8_t* p;
294 auto const lzr = NodeStore::lz4_compress(
295 in, in_size, [&p, &vn, &bf](std::size_t n) {
296 p = reinterpret_cast<std::uint8_t*>(bf(vn + n));
297 return p + vn;
298 });
299 std::memcpy(p, vi.data(), vn);
300 result.first = p;
301 result.second = vn + lzr.second;
302 break;
303 }
304 default:
305 Throw<std::logic_error>(
306 "nodeobject codec: unknown=" + std::to_string(codecType));
307 };
308 return result;
309}
310
311// Modifies an inner node to erase the ledger
312// sequence and type information so the codec
313// verification can pass.
314//
315template <class = void>
316void
318{
319 using namespace nudb::detail;
320
321 // Check for inner node
322 if (in_size == 525)
323 {
324 istream is(in, in_size);
325 std::uint32_t index;
326 std::uint32_t unused;
327 std::uint8_t kind;
328 std::uint32_t prefix;
329 read<std::uint32_t>(is, index);
330 read<std::uint32_t>(is, unused);
331 read<std::uint8_t>(is, kind);
332 read<std::uint32_t>(is, prefix);
333 if (safe_cast<HashPrefix>(prefix) == HashPrefix::innerNode)
334 {
335 ostream os(in, 9);
336 write<std::uint32_t>(os, 0);
337 write<std::uint32_t>(os, 0);
338 write<std::uint8_t>(os, hotUNKNOWN);
339 }
340 }
341}
342
343} // namespace NodeStore
344} // namespace ripple
345
346#endif
T data(T... args)
T memcmp(T... args)
T memcpy(T... args)
T memset(T... args)
void const * zero32()
Definition codec.h:213
std::size_t read_varint(void const *buf, std::size_t buflen, std::size_t &t)
Definition varint.h:57
std::size_t size_varint(T v)
Definition varint.h:90
void filter_inner(void *in, std::size_t in_size)
Definition codec.h:317
std::pair< void const *, std::size_t > lz4_compress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition codec.h:75
void write(nudb::detail::ostream &os, std::size_t t)
Definition varint.h:134
std::pair< void const *, std::size_t > nodeobject_decompress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition codec.h:110
std::size_t write_varint(void *p0, std::size_t v)
Definition varint.h:103
std::pair< void const *, std::size_t > nodeobject_compress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition codec.h:221
std::pair< void const *, std::size_t > lz4_decompress(void const *in, std::size_t in_size, BufferFactory &&bf)
Definition codec.h:45
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
@ hotUNKNOWN
Definition NodeObject.h:33
@ innerNode
inner node in V1 tree
T to_string(T... args)