Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
serialize.hpp
Go to the documentation of this file.
1
30#pragma once
34#include <array>
35#include <cassert>
36#include <iostream>
37#include <map>
38#include <memory>
39#include <optional>
40#include <type_traits>
41#include <vector>
42
43#ifndef __i386__
44__extension__ using uint128_t = unsigned __int128;
45#endif
46
47// clang-format off
48// disabling the following style guides:
49// cert-dcl58-cpp , restricts modifying the standard library. We need to do this for portable serialization methods
50// cppcoreguidelines-pro-type-reinterpret-cast, we heavily use reinterpret-cast and would be difficult to refactor this out
51// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast, cert-dcl58-cpp)
52// clang-format on
53
54template <typename T>
56
57namespace serialize {
58// Forward declare derived msgpack methods
59void read(auto& it, msgpack_concepts::HasMsgPack auto& obj);
60void write(auto& buf, const msgpack_concepts::HasMsgPack auto& obj);
61
62// Basic integer read / write, to / from raw buffers.
63// Pointers to buffers are advanced by length of type.
64inline void read(uint8_t const*& it, uint8_t& value)
65{
66 value = *it;
67 it += 1;
68}
69
70inline void write(uint8_t*& it, uint8_t value)
71{
72 *it = value;
73 it += 1;
74}
75
76inline void read(uint8_t const*& it, bool& value)
77{
78 value = (*it != 0U);
79 it += 1;
80}
81
82inline void write(uint8_t*& it, bool value)
83{
84 *it = static_cast<uint8_t>(value);
85 it += 1;
86}
87
88inline void read(uint8_t const*& it, uint16_t& value)
89{
90 value = ntohs(*reinterpret_cast<uint16_t const*>(it)); // NOLINT
91 it += 2;
92}
93
94inline void write(uint8_t*& it, uint16_t value)
95{
96 *reinterpret_cast<uint16_t*>(it) = htons(value); // NOLINT
97 it += 2;
98}
99
100inline void read(uint8_t const*& it, uint32_t& value)
101{
102 value = ntohl(*reinterpret_cast<uint32_t const*>(it)); // NOLINT
103 it += 4;
104}
105
106inline void write(uint8_t*& it, uint32_t value)
107{
108 *reinterpret_cast<uint32_t*>(it) = htonl(value);
109 it += 4;
110}
111
112inline void read(uint8_t const*& it, uint64_t& value)
113{
114 value = ntohll(*reinterpret_cast<uint64_t const*>(it));
115 it += 8;
116}
117
118inline void write(uint8_t*& it, uint64_t value)
119{
120 *reinterpret_cast<uint64_t*>(it) = htonll(value);
121 it += 8;
122}
123
124#ifdef __APPLE__
125inline void read(uint8_t const*& it, unsigned long& value)
126{
127 value = ntohll(*reinterpret_cast<unsigned long const*>(it));
128 it += 8;
129}
130
131inline void write(uint8_t*& it, unsigned long value)
132{
133 *reinterpret_cast<unsigned long*>(it) = htonll(value);
134 it += 8;
135}
136#endif
137
138#ifndef __i386__
139inline void read(uint8_t const*& it, uint128_t& value)
140{
141 uint64_t hi, lo; // NOLINT
142 read(it, hi);
143 read(it, lo);
144 value = (static_cast<uint128_t>(hi) << 64) | lo;
145}
146inline void write(uint8_t*& it, uint128_t value)
147{
148 auto hi = static_cast<uint64_t>(value >> 64);
149 auto lo = static_cast<uint64_t>(value);
150 write(it, hi);
151 write(it, lo);
152}
153#endif
154
155// Reading / writing integer types to / from vectors.
156void read(std::vector<uint8_t> const& buf, std::integral auto& value)
157{
158 const auto* ptr = &buf[0];
159 read(ptr, value);
160}
161
162void write(std::vector<uint8_t>& buf, const std::integral auto& value)
163{
164 buf.resize(buf.size() + sizeof(value));
165 uint8_t* ptr = &*buf.end() - sizeof(value);
166 write(ptr, value);
167}
168
169// Reading writing integer types to / from streams.
170void read(std::istream& is, std::integral auto& value)
171{
172 std::array<uint8_t, sizeof(value)> buf;
173 is.read(reinterpret_cast<char*>(buf.data()), sizeof(value));
174 uint8_t const* ptr = &buf[0];
175 read(ptr, value);
176}
177
178void write(std::ostream& os, const std::integral auto& value)
179{
180 std::array<uint8_t, sizeof(value)> buf;
181 uint8_t* ptr = &buf[0];
182 write(ptr, value);
183 os.write(reinterpret_cast<char*>(buf.data()), sizeof(value));
184}
185} // namespace serialize
186
187namespace std {
188inline void read(auto& buf, std::integral auto& value)
189{
191}
192
193inline void write(auto& buf, std::integral auto value)
194{
196}
197
198// Optimized specialisation for reading arrays of bytes from a raw buffer.
199template <size_t N> inline void read(uint8_t const*& it, std::array<uint8_t, N>& value)
200{
201 std::copy(it, it + N, value.data());
202 it += N;
203}
204
205// Optimized specialisation for writing arrays of bytes to a raw buffer.
206template <size_t N> inline void write(uint8_t*& buf, std::array<uint8_t, N> const& value)
207{
208 std::copy(value.begin(), value.end(), buf);
209 buf += N;
210}
211
212// Optimized specialisation for reading vectors of bytes from a raw buffer.
213inline void read(uint8_t const*& it, std::vector<uint8_t>& value)
214{
215 uint32_t size = 0;
216 read(it, size);
217 value.resize(size);
218 std::copy(it, it + size, value.data());
219 it += size;
220}
221
222// Optimized specialisation for writing vectors of bytes to a raw buffer.
223inline void write(uint8_t*& buf, std::vector<uint8_t> const& value)
224{
225 write(buf, static_cast<uint32_t>(value.size()));
226 std::copy(value.begin(), value.end(), buf);
227 buf += value.size();
228}
229
230// Optimized specialisation for reading vectors of bytes from an input stream.
231inline void read(std::istream& is, std::vector<uint8_t>& value)
232{
233 uint32_t size = 0;
234 read(is, size);
235 value.resize(size);
236 is.read(reinterpret_cast<char*>(value.data()), static_cast<std::streamsize>(size));
237}
238
239// Optimized specialisation for writing vectors of bytes to an output stream.
240inline void write(std::ostream& os, std::vector<uint8_t> const& value)
241{
242 write(os, static_cast<uint32_t>(value.size()));
243 os.write(reinterpret_cast<const char*>(value.data()), static_cast<std::streamsize>(value.size()));
244}
245
246// Optimized specialisation for writing arrays of bytes to a vector.
247template <size_t N> inline void write(std::vector<uint8_t>& buf, std::array<uint8_t, N> const& value)
248{
249 buf.resize(buf.size() + N);
250 auto* ptr = &*buf.end() - N;
251 write(ptr, value);
252}
253
254// Optimized specialisation for writing arrays of bytes to an output stream.
255template <size_t N> inline void write(std::ostream& os, std::array<uint8_t, N> const& value)
256{
257 os.write(reinterpret_cast<char*>(value.data()), value.size());
258}
259
260// Generic read of array of types from supported buffer types.
261template <typename B, typename T, size_t N> inline void read(B& it, std::array<T, N>& value)
262{
263 using serialize::read;
264 for (size_t i = 0; i < N; ++i) {
265 read(it, value[i]);
266 }
267}
268
269// Generic write of array of types to supported buffer types.
270template <typename B, typename T, size_t N> inline void write(B& buf, std::array<T, N> const& value)
271{
272 using serialize::write;
273 for (size_t i = 0; i < N; ++i) {
274 write(buf, value[i]);
275 }
276}
277
278// Generic read of vector of types from supported buffer types.
279template <typename B, typename T, typename A> inline void read(B& it, std::vector<T, A>& value)
280{
281 using serialize::read;
282 uint32_t size = 0;
283 read(it, size);
284 value.resize(size);
285 for (size_t i = 0; i < size; ++i) {
286 read(it, value[i]);
287 }
288}
289
290// Generic write of vector of types to supported buffer types.
291template <typename B, typename T, typename A> inline void write(B& buf, std::vector<T, A> const& value)
292{
293 using serialize::write;
294 write(buf, static_cast<uint32_t>(value.size()));
295 for (size_t i = 0; i < value.size(); ++i) {
296 write(buf, value[i]);
297 }
298}
299
300// Read string from supported buffer types.
301template <typename B> inline void read(B& it, std::string& value)
302{
303 using serialize::read;
304 std::vector<uint8_t> buf;
305 read(it, buf);
306 value = std::string(buf.begin(), buf.end());
307}
308
309// Write of strings to supported buffer types.
310template <typename B> inline void write(B& buf, std::string const& value)
311{
312 using serialize::write;
313 write(buf, std::vector<uint8_t>(value.begin(), value.end()));
314}
315
316// Read std::pair.
317template <typename B, typename T, typename U> inline void read(B& it, std::pair<T, U>& value)
318{
319 using serialize::read;
320 read(it, value.first);
321 read(it, value.second);
322}
323
324// Write std::pair.
325template <typename B, typename T, typename U> inline void write(B& buf, std::pair<T, U> const& value)
326{
327 using serialize::write;
328 write(buf, value.first);
329 write(buf, value.second);
330}
331
332// Read std::shared_ptr.
333template <typename B, typename T> inline void read(B& it, std::shared_ptr<T>& value_ptr)
334{
335 using serialize::read;
336 T value;
337 read(it, value);
338 value_ptr = std::make_shared<T>(value);
339}
340
341// Write std::shared_ptr.
342template <typename B, typename T> inline void write(B& buf, std::shared_ptr<T> const& value_ptr)
343{
344 using serialize::write;
345 write(buf, *value_ptr);
346}
347
348// Read std::map
349template <typename B, typename T, typename U> inline void read(B& it, std::map<T, U>& value)
350{
351 using serialize::read;
352 value.clear();
353 uint32_t size = 0;
354 read(it, size);
355 for (size_t i = 0; i < size; ++i) {
357 read(it, v);
358 value.emplace(std::move(v));
359 }
360}
361
362// Write std::map.
363template <typename B, typename T, typename U> inline void write(B& buf, std::map<T, U> const& value)
364{
365 using serialize::write;
366 write(buf, static_cast<uint32_t>(value.size()));
367 for (auto const& kv : value) {
368 write(buf, kv);
369 }
370}
371
372// Read std::optional<T>.
373template <typename B, typename T> inline void read(B& it, std::optional<T>& opt_value)
374{
375 using serialize::read;
376 bool is_nullopt = false;
378 if (is_nullopt) {
380 return;
381 }
382 T value;
383 read(it, value);
384 opt_value = T(value);
385}
386
387template <typename T>
388concept HasGetAll = requires(T t) { t.get_all(); } && !msgpack_concepts::HasMsgPack<T>;
389
390// Write out a struct that defines get_all()
391template <typename B, HasGetAll T> inline void write(B& buf, T const& value)
392{
393 using serialize::write;
394 for (auto& reference : value.get_all()) {
396 }
397}
398
399// Write std::optional<T>.
400// Note: It takes up a different amount of space, depending on whether it's std::nullopt or populated with an actual
401// value.
402template <typename B, typename T> inline void write(B& buf, std::optional<T> const& opt_value)
403{
404 using serialize::write;
405 if (opt_value) {
406 write(buf, false); // is not nullopt
408 return;
409 }
410 write(buf, true); // is nullopt
411}
412
413} // namespace std
414
415// Helper functions that have return values.
416template <typename T, typename B> T from_buffer(B const& buffer, size_t offset = 0)
417{
418 using serialize::read;
419 T result;
420 const auto* ptr = reinterpret_cast<uint8_t const*>(&buffer[offset]);
421 read(ptr, result);
422 return result;
423}
424
425template <typename T> std::vector<uint8_t> to_buffer(T const& value)
426{
427 using serialize::write;
428 std::vector<uint8_t> buf;
429 write(buf, value);
430 return buf;
431}
432
440template <typename T> uint8_t* to_heap_buffer(T const& value)
441{
442 using serialize::write;
443
444 // Initial serialization of the value. Creates a vector of bytes.
445 auto buf = to_buffer(value);
446
447 // Serialize this byte vector, giving us a length prefixed buffer of bytes.
448 auto heap_buf = to_buffer(buf);
449 // Get the heap buffer size, rounded up to the next 64 byte boundary.
450 // Passing a non-multiple of 64 bytes to aligned_alloc is undefined behaviour.
451 auto heap_buf_size_aligned = (heap_buf.size() + 63) & ~static_cast<size_t>(63);
452
453 auto* ptr = reinterpret_cast<uint8_t*>(aligned_alloc(64, heap_buf_size_aligned));
454 std::copy(heap_buf.begin(), heap_buf.end(), ptr);
455 return ptr;
456}
457
458template <typename T> std::vector<T> many_from_buffer(std::vector<uint8_t> const& buffer)
459{
460 const size_t num_elements = buffer.size() / sizeof(T);
461 std::vector<T> elements;
462 for (size_t i = 0; i < num_elements; ++i) {
463 elements.push_back(from_buffer<T>(buffer, i * sizeof(T)));
464 }
465 return elements;
466}
467
468// By default, if calling to_buffer on a vector of types, we don't prefix the vector size.
469template <bool include_size = false, typename T> std::vector<uint8_t> to_buffer(std::vector<T> const& value)
470{
471 using serialize::write;
472 std::vector<uint8_t> buf;
473 if (include_size) {
474 write(buf, value);
475 } else {
476 for (auto e : value) {
477 write(buf, e);
478 }
479 }
480 return buf;
481}
482
483// Some types to describe fixed size buffers for c_bind arguments.
484using in_buf32 = uint8_t const*;
485using out_buf32 = uint8_t*;
486using in_buf64 = uint8_t const*;
487using out_buf64 = uint8_t*;
488using in_buf128 = uint8_t const*;
489using out_buf128 = uint8_t*;
490
491// Variable length string buffers. Prefixed with length.
492using in_str_buf = uint8_t const*;
493using out_str_buf = uint8_t**;
494
495// Use these to pass a raw memory pointer.
496using in_ptr = void* const*;
497using out_ptr = void**;
498
499namespace serialize {
500
505inline void _read_msgpack_field(auto& it, auto& field)
506{
507 using namespace serialize;
508 read(it, field);
509}
510
516inline void read(auto& it, msgpack_concepts::HasMsgPack auto& obj)
517{
518 msgpack::msgpack_apply(obj, [&](auto&... obj_fields) {
519 // apply 'read' to each object field
520 (_read_msgpack_field(it, obj_fields), ...);
521 });
522};
523
528inline void _write_msgpack_field(auto& it, const auto& field)
529{
530 using namespace serialize;
531 write(it, field);
532}
538inline void write(auto& buf, const msgpack_concepts::HasMsgPack auto& obj)
539{
540 msgpack::msgpack_apply(obj, [&](auto&... obj_fields) {
541 // apply 'write' to each object field
542 (_write_msgpack_field(buf, obj_fields), ...);
543 });
544}
545} // namespace serialize
546// clang-format off
547// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast, cert-dcl58-cpp)
548// clang-format on
uint8_t const * buf
Definition data_store.hpp:9
ssize_t offset
Definition engine.cpp:36
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
void write(const T t)
Definition g1.test.cpp:409
void msgpack_apply(const auto &func, auto &... args)
Helper method for better error reporting. Clang does not give the best errors for lambdas.
void _write_msgpack_field(auto &it, const auto &field)
Helper method for better error reporting. Clang does not give the best errors for "auto....
void _read_msgpack_field(auto &it, auto &field)
Helper method for better error reporting. Clang does not give the best errors for "auto....
void read(auto &it, msgpack_concepts::HasMsgPack auto &obj)
Automatically derived read for any object that defines .msgpack() (implicitly defined by MSGPACK_FIEL...
void write(auto &buf, const msgpack_concepts::HasMsgPack auto &obj)
Automatically derived write for any object that defines .msgpack() (implicitly defined by MSGPACK_FIE...
STL namespace.
void read(auto &buf, std::integral auto &value)
void write(auto &buf, std::integral auto value)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint8_t const * in_buf128
T from_buffer(B const &buffer, size_t offset=0)
void *const * in_ptr
uint8_t ** out_str_buf
std::vector< uint8_t > to_buffer(T const &value)
uint8_t * to_heap_buffer(T const &value)
uint8_t const * in_str_buf
uint8_t * out_buf32
uint8_t * out_buf128
std::vector< T > many_from_buffer(std::vector< uint8_t > const &buffer)
uint8_t const * in_buf32
uint8_t const * in_buf64
uint8_t * out_buf64
void ** out_ptr
unsigned __int128 uint128_t
Definition serialize.hpp:44