forked from qdrvm/kagome
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblob.hpp
297 lines (260 loc) · 12.1 KB
/
blob.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <array>
#include <fmt/format.h>
#include <boost/functional/hash.hpp>
#include <scale/scale.hpp>
#include "common/buffer_view.hpp"
#include "common/hexutil.hpp"
#include "common/span_adl.hpp"
#include "macro/endianness_utils.hpp"
#define KAGOME_BLOB_STRICT_TYPEDEF(space_name, class_name, blob_size) \
namespace space_name { \
struct class_name : public ::kagome::common::Blob<blob_size> { \
using Base = ::kagome::common::Blob<blob_size>; \
\
class_name() = default; \
class_name(const class_name &) = default; \
class_name(class_name &&) = default; \
class_name &operator=(const class_name &) = default; \
class_name &operator=(class_name &&) = default; \
\
explicit class_name(const Base &blob) : Base{blob} {} \
explicit class_name(Base &&blob) : Base{std::move(blob)} {} \
\
~class_name() = default; \
\
class_name &operator=(const Base &blob) { \
Blob::operator=(blob); \
return *this; \
} \
\
class_name &operator=(Base &&blob) { \
Blob::operator=(std::move(blob)); \
return *this; \
} \
\
static ::outcome::result<class_name> fromString(std::string_view data) { \
OUTCOME_TRY(blob, Base::fromString(data)); \
return class_name{std::move(blob)}; \
} \
\
static ::outcome::result<class_name> fromHex(std::string_view hex) { \
OUTCOME_TRY(blob, Base::fromHex(hex)); \
return class_name{std::move(blob)}; \
} \
\
static ::outcome::result<class_name> fromHexWithPrefix( \
std::string_view hex) { \
OUTCOME_TRY(blob, Base::fromHexWithPrefix(hex)); \
return class_name{std::move(blob)}; \
} \
\
static ::outcome::result<class_name> fromSpan( \
const common::BufferView &span) { \
OUTCOME_TRY(blob, Base::fromSpan(span)); \
return class_name{std::move(blob)}; \
} \
\
friend inline ::scale::ScaleEncoderStream &operator<<( \
::scale::ScaleEncoderStream &s, \
const space_name::class_name &data) { \
return s << static_cast<const Base &>(data); \
} \
\
friend inline ::scale::ScaleDecoderStream &operator>>( \
::scale::ScaleDecoderStream &s, space_name::class_name &data) { \
return s >> static_cast<Base &>(data); \
} \
}; \
}; \
\
template <> \
struct std::hash<space_name::class_name> { \
auto operator()(const space_name::class_name &key) const { \
/* NOLINTNEXTLINE */ \
return boost::hash_range(key.cbegin(), key.cend()); \
} \
}; \
\
template <> \
struct fmt::formatter<space_name::class_name> \
: fmt::formatter<space_name::class_name::Base> { \
template <typename FormatCtx> \
auto format(const space_name::class_name &blob, \
FormatCtx &ctx) const -> decltype(ctx.out()) { \
return fmt::formatter<space_name::class_name::Base>::format(blob, ctx); \
} \
};
namespace kagome::common {
/**
* Error codes for exceptions that may occur during blob initialization
*/
enum class BlobError : uint8_t { INCORRECT_LENGTH = 1 };
using byte_t = uint8_t;
/**
* Base type which represents blob of fixed size.
*
* std::string is convenient to use but it is not safe.
* We can not specify the fixed length for string.
*
* For std::array it is possible, so we prefer it over std::string.
*/
template <size_t size_>
class Blob : public std::array<byte_t, size_> {
using Array = std::array<byte_t, size_>;
public:
// Next line is required at least for the scale-codec
static constexpr bool is_static_collection = true;
// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
using const_narref = const byte_t (&)[size_];
using const_narptr = const byte_t (*)[size_];
// NOLINTEND(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
/**
* Initialize blob value
*/
constexpr Blob() : Array{} {}
const_narref internal_array_reference() const {
return *const_narptr(this->data());
}
/**
* @brief constructor enabling initializer list
* @param l initializer list
*/
constexpr explicit Blob(const Array &l) : Array{l} {}
/**
* In compile-time returns size of current blob.
*/
static constexpr size_t size() {
return size_;
}
/**
* Converts current blob to std::string
*/
std::string toString() const {
return std::string{this->begin(), this->end()};
}
/**
* Converts current blob to hex string.
*/
std::string toHex() const {
return hex_lower({this->begin(), this->end()});
}
/**
* Create Blob from arbitrary string, putting its bytes into the blob
* @param data arbitrary string containing
* @return result containing Blob object if string has proper size
*/
static outcome::result<Blob<size_>> fromString(std::string_view data) {
if (data.size() != size_) {
return BlobError::INCORRECT_LENGTH;
}
Blob<size_> b;
std::ranges::copy(data, b.begin());
return b;
}
/**
* Create Blob from hex string
* @param hex hex string
* @return result containing Blob object if hex string has proper size and
* is in hex format
*/
static outcome::result<Blob<size_>> fromHex(std::string_view hex) {
OUTCOME_TRY(res, unhex(hex));
return fromSpan(res);
}
/**
* Create Blob from hex string prefixed with 0x
* @param hex hex string
* @return result containing Blob object if hex string has proper size and
* is in hex format
*/
static outcome::result<Blob<size_>> fromHexWithPrefix(
std::string_view hex) {
OUTCOME_TRY(res, unhexWith0x(hex));
return fromSpan(res);
}
/**
* Create Blob from BufferView
*/
static outcome::result<Blob<size_>> fromSpan(const BufferView &span) {
if (span.size() != size_) {
return BlobError::INCORRECT_LENGTH;
}
Blob<size_> blob;
std::ranges::copy(span, blob.begin());
return blob;
}
auto operator<=>(const Blob<size_> &other) const {
return SpanAdl{*this} <=> other;
}
bool operator==(const Blob<size_> &other) const {
return SpanAdl{*this} == other;
}
};
// extern specification of the most frequently instantiated blob
// specializations, used mostly for Hash instantiation
extern template class Blob<8ul>;
extern template class Blob<16ul>;
extern template class Blob<32ul>;
extern template class Blob<64ul>;
// Hash specializations
using Hash64 = Blob<8>;
using Hash128 = Blob<16>;
using Hash256 = Blob<32>;
using Hash512 = Blob<64>;
template <size_t N>
inline std::ostream &operator<<(std::ostream &os, const Blob<N> &blob) {
return os << blob.toHex();
}
} // namespace kagome::common
namespace kagome {
using common::Hash256;
} // namespace kagome
template <size_t N>
struct std::hash<kagome::common::Blob<N>> {
auto operator()(const kagome::common::Blob<N> &blob) const {
return boost::hash_range(blob.data(), blob.data() + N); // NOLINT
}
};
template <size_t N>
struct fmt::formatter<kagome::common::Blob<N>> {
// Presentation format: 's' - short, 'l' - long.
char presentation = N > 4 ? 's' : 'l';
// Parses format specifications of the form ['s' | 'l'].
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 's' || *it == 'l')) {
presentation = *it++;
}
// Check if reached the end of the range:
if (it != end && *it != '}') {
throw format_error("invalid format");
}
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the Blob using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const kagome::common::Blob<N> &blob,
FormatContext &ctx) const -> decltype(ctx.out()) {
if (presentation == 's') {
if constexpr (N > 4) {
uint16_t head = static_cast<uint16_t>(blob[1])
| (static_cast<uint16_t>(blob[0]) << 8);
uint16_t tail = static_cast<uint16_t>(blob[blob.size() - 1])
| (static_cast<uint16_t>(blob[blob.size() - 2]) << 8);
return fmt::format_to(ctx.out(), "0x{:04x}…{:04x}", head, tail);
}
// else fallback to normal print
}
return fmt::format_to(ctx.out(), "0x{}", blob.toHex());
}
};
OUTCOME_HPP_DECLARE_ERROR(kagome::common, BlobError);