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