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