Pixie
Loading...
Searching...
No Matches
serialization.h
Go to the documentation of this file.
1#pragma once
2
7
8#include <algorithm>
9#include <array>
10#include <bit>
11#include <concepts>
12#include <cstddef>
13#include <cstdint>
14#include <limits>
15#include <memory>
16#include <span>
17#include <stdexcept>
18#include <string>
19#include <type_traits>
20#include <utility>
21#include <vector>
22
23namespace pixie {
24
28enum class DeserializationValidation : std::uint8_t {
34
40};
41
45class SerializationError : public std::invalid_argument {
46 public:
50 SerializationError(std::string message, std::size_t byte_offset)
51 : std::invalid_argument(std::move(message) + " at byte " +
52 std::to_string(byte_offset)),
53 byte_offset_(byte_offset) {}
54
56 std::size_t byte_offset() const noexcept { return byte_offset_; }
57
58 private:
59 std::size_t byte_offset_;
60};
61
70template <class Sink>
71concept SeekableBinaryOutputSink = requires(Sink& sink,
72 std::size_t position,
73 std::span<const std::byte> bytes) {
74 { sink.write(bytes) } -> std::same_as<void>;
75 { sink.write_at(position, bytes) } -> std::same_as<void>;
76 { sink.finish() } -> std::same_as<void>;
77};
78
86 public:
88 bool empty() const noexcept { return data_.empty(); }
89
91 std::size_t size_bytes() const noexcept { return data_.size(); }
92
94 std::span<const std::byte> bytes() const noexcept { return data_; }
95
97 void reserve_bytes(std::size_t size_bytes) { data_.reserve(size_bytes); }
98
100 void write(std::span<const std::byte> bytes) {
101 if (bytes.size() > data_.max_size() - data_.size()) {
102 throw std::length_error("Binary output is too large");
103 }
104 data_.insert(data_.end(), bytes.begin(), bytes.end());
105 }
106
111 void write_at(std::size_t position, std::span<const std::byte> bytes) {
112 if (position > data_.size() || bytes.size() > data_.size() - position) {
113 throw std::out_of_range("Binary output patch is outside the output");
114 }
115 std::ranges::copy(bytes, data_.begin() + position);
116 }
117
119 void finish() noexcept {}
120
122 std::vector<std::byte> take() { return std::exchange(data_, {}); }
123
124 private:
125 std::vector<std::byte> data_;
126};
127
128static_assert(SeekableBinaryOutputSink<VectorOutputSink>);
129
137 public:
139 explicit SpanOutputSink(std::span<std::byte> storage) : storage_(storage) {}
140
142 bool empty() const noexcept { return size_bytes_ == 0; }
143
145 std::size_t size_bytes() const noexcept { return size_bytes_; }
146
148 std::size_t capacity_bytes() const noexcept { return storage_.size(); }
149
151 std::span<const std::byte> bytes() const noexcept {
152 return storage_.first(size_bytes_);
153 }
154
159 void write(std::span<const std::byte> bytes) {
160 if (bytes.size() > storage_.size() - size_bytes_) {
161 throw std::length_error("Fixed binary output capacity exceeded");
162 }
163 std::ranges::copy(bytes, storage_.begin() + size_bytes_);
164 size_bytes_ += bytes.size();
165 }
166
171 void write_at(std::size_t position, std::span<const std::byte> bytes) {
172 if (position > size_bytes_ || bytes.size() > size_bytes_ - position) {
173 throw std::out_of_range("Binary output patch is outside the output");
174 }
175 std::ranges::copy(bytes, storage_.begin() + position);
176 }
177
179 void finish() noexcept {}
180
181 private:
182 std::span<std::byte> storage_;
183 std::size_t size_bytes_ = 0;
184};
185
186static_assert(SeekableBinaryOutputSink<SpanOutputSink>);
187
199 public:
201 static constexpr std::size_t kDefaultBufferBytes = 64 * 1024;
202
210 template <SeekableBinaryOutputSink Sink>
211 explicit BinaryWriter(Sink& sink,
212 std::size_t buffer_bytes = kDefaultBufferBytes)
213 : owned_buffer_(allocate_buffer(buffer_bytes)),
214 buffer_(owned_buffer_.get(), buffer_bytes) {
215 bind(sink);
216 }
217
226 template <SeekableBinaryOutputSink Sink>
227 BinaryWriter(Sink& sink, std::span<std::byte> buffer) : buffer_(buffer) {
228 if (buffer.empty()) {
229 throw std::invalid_argument(
230 "BinaryWriter staging buffer must be non-empty");
231 }
232 bind(sink);
233 }
234
235 BinaryWriter(const BinaryWriter&) = delete;
236 BinaryWriter& operator=(const BinaryWriter&) = delete;
237 BinaryWriter(BinaryWriter&&) = delete;
238 BinaryWriter& operator=(BinaryWriter&&) = delete;
239
241 bool empty() const noexcept { return size_bytes() == 0; }
242
244 std::size_t size_bytes() const noexcept {
245 return flushed_bytes_ + buffered_bytes_;
246 }
247
249 std::size_t buffer_size_bytes() const noexcept { return buffer_.size(); }
250
252 void write_u8(std::uint8_t value) { write_unsigned(value); }
253
255 void write_u16(std::uint16_t value) { write_unsigned(value); }
256
258 void write_u32(std::uint32_t value) { write_unsigned(value); }
259
261 void write_u64(std::uint64_t value) { write_unsigned(value); }
262
264 void write_i8(std::int8_t value) {
265 write_unsigned(std::bit_cast<std::uint8_t>(value));
266 }
267
269 void write_i16(std::int16_t value) {
270 write_unsigned(std::bit_cast<std::uint16_t>(value));
271 }
272
274 void write_i32(std::int32_t value) {
275 write_unsigned(std::bit_cast<std::uint32_t>(value));
276 }
277
279 void write_i64(std::int64_t value) {
280 write_unsigned(std::bit_cast<std::uint64_t>(value));
281 }
282
287 void write_size(std::size_t value) {
288 if constexpr (sizeof(std::size_t) > sizeof(std::uint64_t)) {
289 if (value > std::numeric_limits<std::uint64_t>::max()) {
290 throw std::length_error("Size does not fit in the wire format");
291 }
292 }
293 write_u64(static_cast<std::uint64_t>(value));
294 }
295
297 void write_bytes(std::span<const std::byte> bytes) {
298 require_open();
299 require_output_size(bytes.size());
300 while (!bytes.empty()) {
301 if (buffered_bytes_ != 0) {
302 const std::size_t count =
303 std::min(bytes.size(), buffer_.size() - buffered_bytes_);
304 std::ranges::copy(bytes.first(count),
305 buffer_.begin() + buffered_bytes_);
306 buffered_bytes_ += count;
307 bytes = bytes.subspan(count);
308 if (buffered_bytes_ == buffer_.size()) {
309 flush_buffer();
310 }
311 continue;
312 }
313
314 if (bytes.size() >= buffer_.size()) {
315 const std::size_t count = bytes.size();
316 write_to_sink(bytes);
317 flushed_bytes_ += count;
318 return;
319 }
320
321 std::ranges::copy(bytes, buffer_.begin());
322 buffered_bytes_ = bytes.size();
323 return;
324 }
325 }
326
328 void write_zeros(std::size_t count) {
329 require_open();
330 require_output_size(count);
331 while (count != 0) {
332 const std::size_t writable = buffer_.size() - buffered_bytes_;
333 const std::size_t chunk = std::min(count, writable);
334 std::fill_n(buffer_.begin() + buffered_bytes_, chunk, std::byte{0});
335 buffered_bytes_ += chunk;
336 count -= chunk;
337 if (buffered_bytes_ == buffer_.size()) {
338 flush_buffer();
339 }
340 }
341 }
342
347 void align_to(std::size_t alignment) {
348 require_open();
349 if (alignment == 0) {
350 throw std::invalid_argument("Serialization alignment must be non-zero");
351 }
352 const std::size_t remainder = size_bytes() % alignment;
353 if (remainder != 0) {
354 write_zeros(alignment - remainder);
355 }
356 }
357
363 std::size_t write_u64_placeholder() {
364 const std::size_t position = size_bytes();
365 write_u64(0);
366 return position;
367 }
368
373 void patch_u64(std::size_t position, std::uint64_t value) {
374 require_open();
375 if (position > size_bytes() || size_bytes() - position < sizeof(value)) {
376 throw std::out_of_range("Serialization patch is outside the output");
377 }
378
379 std::array<std::byte, sizeof(value)> encoded{};
380 for (std::size_t byte = 0; byte < sizeof(value); ++byte) {
381 encoded[byte] = static_cast<std::byte>((value >> (byte * 8)) & 0xffu);
382 }
383
384 if (position >= flushed_bytes_) {
385 const std::size_t buffered_position = position - flushed_bytes_;
386 std::ranges::copy(encoded, buffer_.begin() + buffered_position);
387 return;
388 }
389
390 if (position + encoded.size() > flushed_bytes_) {
391 flush_buffer();
392 }
393 write_at_sink(position, encoded);
394 }
395
400 void flush() {
401 require_open();
402 flush_buffer();
403 }
404
412 void finish() {
413 if (state_ == State::kFinished) {
414 return;
415 }
416 require_open();
417 flush_buffer();
418 try {
419 finish_sink_(sink_);
420 state_ = State::kFinished;
421 } catch (...) {
422 state_ = State::kFailed;
423 throw;
424 }
425 }
426
427 private:
428 enum class State { kOpen, kFinished, kFailed };
429
430 using WriteSink = void (*)(void*, std::span<const std::byte>);
431 using WriteAtSink = void (*)(void*, std::size_t, std::span<const std::byte>);
432 using FinishSink = void (*)(void*);
433
434 static std::unique_ptr<std::byte[]> allocate_buffer(
435 std::size_t buffer_bytes) {
436 if (buffer_bytes == 0) {
437 throw std::invalid_argument(
438 "BinaryWriter staging buffer must be non-empty");
439 }
440 return std::make_unique<std::byte[]>(buffer_bytes);
441 }
442
443 template <SeekableBinaryOutputSink Sink>
444 void bind(Sink& sink) noexcept {
445 sink_ = std::addressof(sink);
446 write_sink_ = [](void* output, std::span<const std::byte> bytes) {
447 static_cast<Sink*>(output)->write(bytes);
448 };
449 write_at_sink_ = [](void* output, std::size_t position,
450 std::span<const std::byte> bytes) {
451 static_cast<Sink*>(output)->write_at(position, bytes);
452 };
453 finish_sink_ = [](void* output) { static_cast<Sink*>(output)->finish(); };
454 }
455
456 void require_open() const {
457 if (state_ == State::kFinished) {
458 throw std::logic_error("BinaryWriter is already finished");
459 }
460 if (state_ == State::kFailed) {
461 throw std::logic_error("BinaryWriter is unusable after sink failure");
462 }
463 }
464
465 void require_output_size(std::size_t count) const {
466 if (count > std::numeric_limits<std::size_t>::max() - size_bytes()) {
467 throw std::length_error("Serialized output is too large");
468 }
469 }
470
471 void flush_buffer() {
472 if (buffered_bytes_ == 0) {
473 return;
474 }
475 const std::size_t count = buffered_bytes_;
476 write_to_sink(buffer_.first(count));
477 flushed_bytes_ += count;
478 buffered_bytes_ = 0;
479 }
480
481 void write_to_sink(std::span<const std::byte> bytes) {
482 try {
483 write_sink_(sink_, bytes);
484 } catch (...) {
485 state_ = State::kFailed;
486 throw;
487 }
488 }
489
490 void write_at_sink(std::size_t position, std::span<const std::byte> bytes) {
491 try {
492 write_at_sink_(sink_, position, bytes);
493 } catch (...) {
494 state_ = State::kFailed;
495 throw;
496 }
497 }
498
499 template <std::unsigned_integral T>
500 void write_unsigned(T value) {
501 std::array<std::byte, sizeof(T)> encoded{};
502 for (std::size_t byte = 0; byte < sizeof(T); ++byte) {
503 encoded[byte] = static_cast<std::byte>((value >> (byte * 8)) & T{0xff});
504 }
505 write_bytes(encoded);
506 }
507
508 void* sink_ = nullptr;
509 WriteSink write_sink_ = nullptr;
510 WriteAtSink write_at_sink_ = nullptr;
511 FinishSink finish_sink_ = nullptr;
512 std::unique_ptr<std::byte[]> owned_buffer_;
513 std::span<std::byte> buffer_;
514 std::size_t flushed_bytes_ = 0;
515 std::size_t buffered_bytes_ = 0;
516 State state_ = State::kOpen;
517};
518
527 public:
529 explicit BinaryReader(std::span<const std::byte> data) : data_(data) {}
530
532 bool empty() const noexcept { return remaining() == 0; }
533
535 std::size_t size_bytes() const noexcept { return data_.size(); }
536
538 std::size_t position() const noexcept { return offset_; }
539
541 std::size_t byte_offset() const noexcept { return absolute_position(); }
542
544 std::size_t remaining() const noexcept { return data_.size() - offset_; }
545
547 std::span<const std::byte> remaining_bytes() const noexcept {
548 return data_.subspan(offset_);
549 }
550
552 std::uint8_t read_u8() { return read_unsigned<std::uint8_t>(); }
553
555 std::uint16_t read_u16() { return read_unsigned<std::uint16_t>(); }
556
558 std::uint32_t read_u32() { return read_unsigned<std::uint32_t>(); }
559
561 std::uint64_t read_u64() { return read_unsigned<std::uint64_t>(); }
562
564 std::int8_t read_i8() {
565 return std::bit_cast<std::int8_t>(read_unsigned<std::uint8_t>());
566 }
567
569 std::int16_t read_i16() {
570 return std::bit_cast<std::int16_t>(read_unsigned<std::uint16_t>());
571 }
572
574 std::int32_t read_i32() {
575 return std::bit_cast<std::int32_t>(read_unsigned<std::uint32_t>());
576 }
577
579 std::int64_t read_i64() {
580 return std::bit_cast<std::int64_t>(read_unsigned<std::uint64_t>());
581 }
582
587 std::size_t read_size() {
588 BinaryReader candidate = *this;
589 const std::size_t field_offset = candidate.absolute_position();
590 const std::uint64_t encoded = candidate.read_u64();
591 if (encoded > std::numeric_limits<std::size_t>::max()) {
592 throw std::length_error("Serialized size at byte " +
593 std::to_string(field_offset) +
594 " does not fit in size_t");
595 }
596 *this = candidate;
597 return static_cast<std::size_t>(encoded);
598 }
599
604 std::span<const std::byte> read_bytes(std::size_t count) {
605 require(count);
606 const std::span<const std::byte> result = data_.subspan(offset_, count);
607 offset_ += count;
608 return result;
609 }
610
615 BinaryReader read_subreader(std::size_t count) {
616 const std::size_t origin = absolute_position();
617 return BinaryReader(read_bytes(count), origin);
618 }
619
624 void skip(std::size_t count) {
625 require(count);
626 offset_ += count;
627 }
628
633 void require_zero_padding(std::size_t maximum) {
634 if (remaining() > maximum) {
635 throw SerializationError("Unexpected serialized payload bytes",
636 absolute_position());
637 }
638 BinaryReader candidate = *this;
639 while (!candidate.empty()) {
640 const std::size_t byte_offset = candidate.absolute_position();
641 if (candidate.read_u8() != 0) {
642 throw SerializationError("Non-zero serialized payload padding",
644 }
645 }
646 *this = candidate;
647 }
648
649 private:
650 BinaryReader(std::span<const std::byte> data, std::size_t origin)
651 : data_(data), origin_(origin) {}
652
653 std::size_t absolute_position() const noexcept { return origin_ + offset_; }
654
655 void require(std::size_t count) const {
656 if (count > remaining()) {
657 throw SerializationError("Truncated serialized input",
658 absolute_position());
659 }
660 }
661
662 template <std::unsigned_integral T>
663 T read_unsigned() {
664 require(sizeof(T));
665 T value = 0;
666 for (std::size_t byte = 0; byte < sizeof(T); ++byte) {
667 value |=
668 static_cast<T>(std::to_integer<std::uint8_t>(data_[offset_ + byte]))
669 << (byte * 8);
670 }
671 offset_ += sizeof(T);
672 return value;
673 }
674
675 std::span<const std::byte> data_;
676 std::size_t offset_ = 0;
677 std::size_t origin_ = 0;
678};
679
692template <class Derived>
694 public:
696 void serialize(BinaryWriter& writer) const
697 requires requires(const Derived& value, BinaryWriter& output) {
698 { value.serialize_impl(output) } -> std::same_as<void>;
699 }
700 {
701 derived().serialize_impl(writer);
702 }
703
709 template <class... Context>
710 static Derived deserialize(BinaryReader& reader, Context&&... context)
711 requires requires(BinaryReader& input, Context&&... arguments) {
712 {
713 Derived::deserialize_impl(input, std::forward<Context>(arguments)...)
714 } -> std::same_as<Derived>;
715 }
716 {
717 BinaryReader candidate = reader;
718 Derived result =
719 Derived::deserialize_impl(candidate, std::forward<Context>(context)...);
720 reader = candidate;
721 return result;
722 }
723
729 template <class... Context>
730 static Derived deserialize(std::span<const std::byte>& data,
731 Context&&... context)
732 requires requires(BinaryReader& input, Context&&... arguments) {
733 {
734 Derived::deserialize_impl(input, std::forward<Context>(arguments)...)
735 } -> std::same_as<Derived>;
736 }
737 {
738 BinaryReader reader(data);
739 Derived result = deserialize(reader, std::forward<Context>(context)...);
740 data = data.subspan(reader.position());
741 return result;
742 }
743
744 private:
745 const Derived& derived() const { return static_cast<const Derived&>(*this); }
746};
747
749template <class T>
750concept Serializable = requires(const T& value, BinaryWriter& writer) {
751 { value.serialize(writer) } -> std::same_as<void>;
752};
753
757template <class T, class... Context>
758concept Deserializable = requires(BinaryReader& reader, Context&&... context) {
759 {
760 T::deserialize(reader, std::forward<Context>(context)...)
761 } -> std::same_as<T>;
762};
763
764} // namespace pixie
Bounds-checked reader for canonical little-endian binary data.
Definition serialization.h:526
std::size_t size_bytes() const noexcept
Return the total number of bytes in this reader.
Definition serialization.h:535
std::int64_t read_i64()
Read a signed little-endian 64-bit integer.
Definition serialization.h:579
std::uint16_t read_u16()
Read an unsigned little-endian 16-bit integer.
Definition serialization.h:555
BinaryReader(std::span< const std::byte > data)
Construct a reader over caller-owned data.
Definition serialization.h:529
void skip(std::size_t count)
Advance by exactly count bytes.
Definition serialization.h:624
std::int8_t read_i8()
Read a signed eight-bit integer.
Definition serialization.h:564
std::size_t remaining() const noexcept
Return the number of unconsumed bytes.
Definition serialization.h:544
std::span< const std::byte > remaining_bytes() const noexcept
Return all currently unconsumed bytes.
Definition serialization.h:547
std::uint8_t read_u8()
Read an unsigned eight-bit integer.
Definition serialization.h:552
BinaryReader read_subreader(std::size_t count)
Read a bounded region as an independent child reader.
Definition serialization.h:615
std::span< const std::byte > read_bytes(std::size_t count)
Read exactly count uninterpreted bytes.
Definition serialization.h:604
std::uint64_t read_u64()
Read an unsigned little-endian 64-bit integer.
Definition serialization.h:561
std::int32_t read_i32()
Read a signed little-endian 32-bit integer.
Definition serialization.h:574
void require_zero_padding(std::size_t maximum)
Consume at most maximum trailing zero-padding bytes.
Definition serialization.h:633
std::uint32_t read_u32()
Read an unsigned little-endian 32-bit integer.
Definition serialization.h:558
bool empty() const noexcept
Return whether all input bytes have been consumed.
Definition serialization.h:532
std::size_t position() const noexcept
Return the number of bytes consumed by this reader.
Definition serialization.h:538
std::size_t byte_offset() const noexcept
Return the current byte offset in the outermost input.
Definition serialization.h:541
std::size_t read_size()
Read an unsigned 64-bit size and convert it to size_t.
Definition serialization.h:587
std::int16_t read_i16()
Read a signed little-endian 16-bit integer.
Definition serialization.h:569
Bounded-buffer writer for canonical little-endian binary data.
Definition serialization.h:198
bool empty() const noexcept
Return whether no bytes have been written.
Definition serialization.h:241
void write_i16(std::int16_t value)
Write a signed 16-bit integer in little-endian order.
Definition serialization.h:269
std::size_t buffer_size_bytes() const noexcept
Return the maximum staging-buffer size.
Definition serialization.h:249
void write_bytes(std::span< const std::byte > bytes)
Append bytes without interpretation.
Definition serialization.h:297
void write_i64(std::int64_t value)
Write a signed 64-bit integer in little-endian order.
Definition serialization.h:279
void write_i32(std::int32_t value)
Write a signed 32-bit integer in little-endian order.
Definition serialization.h:274
BinaryWriter(Sink &sink, std::size_t buffer_bytes=kDefaultBufferBytes)
Construct a writer with an owned, fixed-size staging buffer.
Definition serialization.h:211
void write_u32(std::uint32_t value)
Write an unsigned 32-bit integer in little-endian order.
Definition serialization.h:258
void write_u8(std::uint8_t value)
Write an unsigned eight-bit integer.
Definition serialization.h:252
void finish()
Deliver all bytes and complete the sink.
Definition serialization.h:412
static constexpr std::size_t kDefaultBufferBytes
Default bounded staging-buffer size.
Definition serialization.h:201
BinaryWriter(Sink &sink, std::span< std::byte > buffer)
Construct a writer over a caller-owned staging buffer.
Definition serialization.h:227
std::size_t size_bytes() const noexcept
Return the logical number of bytes written.
Definition serialization.h:244
void align_to(std::size_t alignment)
Pad with zero bytes to the next multiple of alignment.
Definition serialization.h:347
void write_i8(std::int8_t value)
Write a signed eight-bit integer.
Definition serialization.h:264
void write_zeros(std::size_t count)
Append count zero bytes.
Definition serialization.h:328
void write_u16(std::uint16_t value)
Write an unsigned 16-bit integer in little-endian order.
Definition serialization.h:255
void patch_u64(std::size_t position, std::uint64_t value)
Replace an existing 64-bit field with value.
Definition serialization.h:373
std::size_t write_u64_placeholder()
Write a zero 64-bit field and return its byte position.
Definition serialization.h:363
void write_u64(std::uint64_t value)
Write an unsigned 64-bit integer in little-endian order.
Definition serialization.h:261
void write_size(std::size_t value)
Write a platform size as an unsigned 64-bit integer.
Definition serialization.h:287
void flush()
Deliver currently buffered bytes while keeping the writer open.
Definition serialization.h:400
CRTP facade for optional binary serialization and deserialization.
Definition serialization.h:693
static Derived deserialize(std::span< const std::byte > &data, Context &&... context)
Restore exactly Derived and advance a mutable byte span.
Definition serialization.h:730
static Derived deserialize(BinaryReader &reader, Context &&... context)
Restore exactly Derived and advance reader on success.
Definition serialization.h:710
void serialize(BinaryWriter &writer) const
Serialize this value through its concrete implementation.
Definition serialization.h:696
Error raised while decoding malformed serialized data.
Definition serialization.h:45
SerializationError(std::string message, std::size_t byte_offset)
Construct an error for byte_offset in the input artifact.
Definition serialization.h:50
std::size_t byte_offset() const noexcept
Return the byte offset associated with the error.
Definition serialization.h:56
bool empty() const noexcept
Return whether no output bytes have been written.
Definition serialization.h:142
void write_at(std::size_t position, std::span< const std::byte > bytes)
Replace output bytes beginning at position.
Definition serialization.h:171
std::size_t size_bytes() const noexcept
Return the number of output bytes written.
Definition serialization.h:145
std::size_t capacity_bytes() const noexcept
Return the fixed output capacity in bytes.
Definition serialization.h:148
std::span< const std::byte > bytes() const noexcept
Return the prefix written so far.
Definition serialization.h:151
SpanOutputSink(std::span< std::byte > storage)
Construct an empty output over caller-owned storage.
Definition serialization.h:139
void write(std::span< const std::byte > bytes)
Append bytes to the output.
Definition serialization.h:159
void finish() noexcept
Complete this fixed-span output.
Definition serialization.h:179
Explicit owning sink for binary data collected in memory.
Definition serialization.h:85
bool empty() const noexcept
Return whether no output bytes have been written.
Definition serialization.h:88
std::span< const std::byte > bytes() const noexcept
Return a view of all output bytes.
Definition serialization.h:94
void reserve_bytes(std::size_t size_bytes)
Reserve memory for at least size_bytes output bytes.
Definition serialization.h:97
void finish() noexcept
Complete this in-memory output.
Definition serialization.h:119
std::vector< std::byte > take()
Transfer the bytes and reset this sink to an empty state.
Definition serialization.h:122
void write_at(std::size_t position, std::span< const std::byte > bytes)
Replace output bytes beginning at position.
Definition serialization.h:111
std::size_t size_bytes() const noexcept
Return the number of output bytes written.
Definition serialization.h:91
void write(std::span< const std::byte > bytes)
Append bytes to the output.
Definition serialization.h:100
Type restorable exactly from a reader and the listed context types.
Definition serialization.h:758
Contract for an append-only output that supports checked overwrites.
Definition serialization.h:71
Type exposing the common binary serialization operation.
Definition serialization.h:750
DeserializationValidation
Validation strength used while restoring serialized indexes.
Definition serialization.h:28
@ kQuick
Check framing, dimensions, references, and other conditions needed for memory-safe terminating querie...
Definition serialization.h:33
@ kFull
Additionally authenticate all source-derived metadata against the supplied source contents.
Definition serialization.h:39