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