auth 0.0.1
Authenticated computation with C++26 modules
Loading...
Searching...
No Matches
serialization.ccm
Go to the documentation of this file.
1// SPDX-FileType: Source
2// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
3// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
4
13module;
14#include <array>
15#include <concepts>
16#include <cstddef>
17#include <cstdint>
18#include <memory>
19#include <span>
20#include <type_traits>
21#include <utility>
22#include <variant>
23#include <vector>
24
25export module auth.serialization;
26export import auth.error;
27
28namespace auth {
29
31export using bytes = std::vector<std::uint8_t>;
32
33template<class... Ts>
34using variant_tag = std::conditional_t<(sizeof...(Ts) <= 256), std::uint8_t, std::uint32_t>;
35
39export struct writer {
40 static constexpr bool reading = false;
42
46 template<std::unsigned_integral T>
47 void field(T value) {
48 if constexpr (std::same_as<T, bool>) {
49 data.push_back(value ? 1 : 0);
50 } else {
51 for (std::size_t i = 0; i < sizeof(T); ++i)
52 data.push_back(static_cast<std::uint8_t>(value >> (i * 8)));
53 }
54 }
55
59 template<class T, std::size_t N>
60 void field(const std::array<T, N>& value) {
61 for (const auto& x : value) field(x);
62 }
63
69 template<class... Ts>
70 void field(const std::variant<Ts...>& value) {
71 if (value.valueless_by_exception()) throw auth_error("valueless variant");
72 field(static_cast<variant_tag<Ts...>>(value.index()));
73 std::visit([this](const auto& x) { this->field(x); }, value);
74 }
75
80 template<class T>
81 void field(const std::unique_ptr<T>& value) {
82 if (!value) throw auth_error("null boxed value");
83 field(*value);
84 }
85
90 template<class T> requires requires(const T& v, writer& s) { v.serialize(s); }
91 void field(const T& value) { value.serialize(*this); }
92
96 template<class... Ts>
97 void operator()(const Ts&... values) { (field(values), ...); }
98};
99
105export struct reader {
106 static constexpr bool reading = true;
107 std::span<const std::uint8_t> data;
108 std::size_t position = 0;
109
114 template<std::unsigned_integral T>
115 void field(T& value) {
116 constexpr auto width = std::same_as<T, bool> ? 1 : sizeof(T);
117 if (data.size() - position < width) throw auth_error("truncated value");
118 if constexpr (std::same_as<T, bool>) {
119 const auto byte = data[position++];
120 if (byte > 1) throw auth_error("invalid boolean");
121 value = byte != 0;
122 } else {
123 value = 0;
124 for (std::size_t i = 0; i < sizeof(T); ++i)
125 value |= static_cast<T>(data[position++]) << (i * 8);
126 }
127 }
128
132 template<class T, std::size_t N>
133 void field(std::array<T, N>& value) {
134 for (auto& x : value) field(x);
135 }
136
141 template<class... Ts>
142 void field(std::variant<Ts...>& value) {
143 variant_tag<Ts...> tag;
144 field(tag);
145 if (tag >= sizeof...(Ts)) throw auth_error("invalid variant index");
146 alternative(value, tag);
147 }
148
152 template<class T>
153 void field(std::unique_ptr<T>& value) {
154 auto decoded = std::make_unique<T>();
155 field(*decoded);
156 value = std::move(decoded);
157 }
158
163 template<class T> requires requires(T& v, reader& s) { v.serialize(s); }
164 void field(T& value) { value.serialize(*this); }
165
169 template<class... Ts>
170 void operator()(Ts&... values) { (field(values), ...); }
171
172private:
173 template<std::size_t I = 0, class... Ts>
174 void alternative(std::variant<Ts...>& value, std::size_t tag) {
175 if (tag == I) field(value.template emplace<I>());
176 else if constexpr (I + 1 < sizeof...(Ts)) alternative<I + 1>(value, tag);
177 }
178};
179
185export template<class T>
186bytes encode(const T& value) {
187 writer stream;
188 stream(value);
189 return std::move(stream.data);
190}
191
201export template<class T>
202std::pair<T, const std::uint8_t*> decode(std::span<const std::uint8_t> data) {
203 T value{};
204 reader stream{data};
205 stream(value);
206 return {std::move(value), data.subspan(stream.position).data()};
207}
208
209} // namespace auth
bytes encode(const T &value)
Serializes a value to an owned byte buffer.
std::vector< std::uint8_t > bytes
An owned buffer of serialized bytes.
std::pair< T, const std::uint8_t * > decode(std::span< const std::uint8_t > data)
Decodes one value, leaving subsequent bytes for the caller.
Reports malformed input, invalid proofs, or hashing failures.
Definition error.ccm:16
Reads checked fields from borrowed bytes, advancing a byte offset.
void field(std::variant< Ts... > &value)
Reads a variant's index and constructs and fills its selected alternative.
void field(std::array< T, N > &value)
Reads each array element in order; no length prefix is consumed.
static constexpr bool reading
Selects the reading path in serializers.
void field(std::unique_ptr< T > &value)
Allocates and reads a boxed value, replacing the pointer on success.
void operator()(Ts &... values)
Reads fields from left to right using the matching field overloads.
void field(T &value)
Reads a little-endian unsigned integer, or a one-byte boolean.
std::size_t position
Offset of the next unread byte.
std::span< const std::uint8_t > data
Borrowed input bytes.
void field(T &value)
Invokes a custom type's serialize method through a mutable reference.
Appends deterministic, byte-aligned fields to an owned buffer.
void field(const std::variant< Ts... > &value)
Writes a variant's alternative index and active value.
void operator()(const Ts &... values)
Writes fields from left to right using the matching field overloads.
void field(const std::array< T, N > &value)
Writes each array element in order, without a length prefix.
bytes data
Serialized output in field order.
static constexpr bool reading
Selects the writing path in serializers.
void field(const std::unique_ptr< T > &value)
Writes a boxed value without pointer or presence bytes.
void field(const T &value)
Invokes a custom type's serialize method through a const reference.
void field(T value)
Writes an unsigned integer in little-endian order.