GMP 0.4.0
Generative Metaprogramming library for C++
Loading...
Searching...
No Matches
serializer.hpp
Go to the documentation of this file.
1// ___ __ __ ___
2// / __| \/ | _ \ GMP(Generative Metaprogramming)
3// | (_ | |\/| | _/ version 0.4.0
4// \___|_| |_|_| https://github.com/lkimuk/gmp
5//
6// SPDX-FileCopyrightText: 2023-2026 Miles Li <https://www.cppmore.com/>
7// SPDX-License-Identifier: MIT
8//
9// This file is part of the GMP (Generative Metaprogramming) library.
10// Full project source: https://github.com/lkimuk/gmp
11
12#ifndef GMP_SERIALIZATION_SERIALIZER_HPP_
13#define GMP_SERIALIZATION_SERIALIZER_HPP_
14
15#include <cmath>
16#include <concepts>
17#include <limits>
18#include <string>
19#include <string_view>
20#include <type_traits>
21#include <utility>
22
23#include <gmp/meta/meta.hpp>
25#include <gmp/serialization/detail/schema.hpp>
26#include <gmp/serialization/detail/std_types.hpp>
29
30namespace gmp {
31
32template <typename Writer>
35public:
38
40 return options_;
41 }
42
43 template <typename T> serialization_result<void> encode(const T &value) {
44 return encode_value(value);
45 }
46
48 public:
49 template <typename T> serialization_result<void> field(std::string_view name, const T &value) {
50 auto status = owner_.write_key(name);
51 if (!status) {
52 return status;
53 }
54 status = owner_.encode_value(value);
55 if (!status) {
56 return prepend_serialization_path(status.error(), name);
57 }
58 ++count_;
59 return {};
60 }
61
62 private:
63 friend class basic_serializer;
64
65 explicit object_archive(basic_serializer &owner) : owner_(owner) {}
66
67 basic_serializer &owner_;
68 std::size_t count_ = 0;
69 };
70
72 public:
73 template <typename T> serialization_result<void> element(const T &value) {
74 auto status = owner_.encode_value(value);
75 if (!status) {
76 return prepend_serialization_path(status.error(), std::to_string(count_));
77 }
78 ++count_;
79 return {};
80 }
81
82 private:
83 friend class basic_serializer;
84
85 explicit array_archive(basic_serializer &owner) : owner_(owner) {}
86
87 basic_serializer &owner_;
88 std::size_t count_ = 0;
89 };
90
91 template <typename F> serialization_result<void> object(std::size_t size, F &&write_fields) {
92 if (size > options_.max_container_size) {
94 "container size exceeds configured limit");
95 }
96 depth_guard depth(*this);
97 if (!depth) {
99 "maximum serialization depth exceeded");
100 }
101 auto status = begin_object(size);
102 if (!status) {
103 return status;
104 }
105 structure_guard structure(*this, true);
106 object_archive archive(*this);
107 status = std::forward<F>(write_fields)(archive);
108 if (!status) {
109 return status;
110 }
111 if (archive.count_ != size) {
113 "custom object field count does not match declared size");
114 }
115 return structure.finish();
116 }
117
118 template <typename F> serialization_result<void> array(std::size_t size, F &&write_elements) {
119 if (size > options_.max_container_size) {
121 "container size exceeds configured limit");
122 }
123 depth_guard depth(*this);
124 if (!depth) {
126 "maximum serialization depth exceeded");
127 }
128 auto status = begin_array(size);
129 if (!status) {
130 return status;
131 }
132 structure_guard structure(*this, false);
133 array_archive archive(*this);
134 status = std::forward<F>(write_elements)(archive);
135 if (!status) {
136 return status;
137 }
138 if (archive.count_ != size) {
140 "custom array element count does not match declared size");
141 }
142 return structure.finish();
143 }
144
146 return writer_.write_null();
147 }
148
150 return writer_.write_bool(v);
151 }
152
154 return writer_.write_signed(v);
155 }
156
158 return writer_.write_unsigned(v);
159 }
160
162 return writer_.write_floating(v);
163 }
164
166 return writer_.write_string(v);
167 }
168
170 return writer_.begin_array(n);
171 }
172
174 return writer_.end_array();
175 }
176
178 return writer_.begin_object(n);
179 }
180
182 return writer_.write_key(v);
183 }
184
186 return writer_.end_object();
187 }
188
189private:
190 class depth_guard {
191 public:
192 explicit depth_guard(basic_serializer &s) : s_(s), ok_(s.depth_ < s.options_.max_depth) {
193 if (ok_) {
194 ++s_.depth_;
195 }
196 }
197
198 ~depth_guard() {
199 if (ok_) {
200 --s_.depth_;
201 }
202 }
203
204 explicit operator bool() const noexcept {
205 return ok_;
206 }
207
208 private:
209 basic_serializer &s_;
210 bool ok_;
211 };
212
213 class structure_guard {
214 public:
215 structure_guard(basic_serializer &owner, bool object) : owner_(owner), object_(object) {}
216
217 ~structure_guard() {
218 if (active_) {
219 if (object_) {
220 (void)owner_.end_object();
221 } else {
222 (void)owner_.end_array();
223 }
224 }
225 }
226
227 serialization_result<void> finish() {
228 active_ = false;
229 return object_ ? owner_.end_object() : owner_.end_array();
230 }
231
232 private:
233 basic_serializer &owner_;
234 bool object_;
235 bool active_ = true;
236 };
237
238 template <typename T> serialization_result<void> encode_value(const T &value) {
239 using U = detail::unqualified_t<T>;
240 if constexpr (requires(basic_serializer & a, const U &v) {
241 {
242 serialization_traits<U>::serialize(a, v)
243 } -> std::same_as<serialization_result<void>>;
244 }) {
245 return serialization_traits<U>::serialize(*this, value);
246 } else if constexpr (std::same_as<U, std::nullptr_t>) {
247 return write_null();
248 } else if constexpr (std::same_as<U, bool>) {
249 return write_bool(value);
250 } else if constexpr (std::is_integral_v<U> && std::is_signed_v<U>) {
251 return write_signed(static_cast<std::int64_t>(value));
252 } else if constexpr (std::is_integral_v<U> && std::is_unsigned_v<U>) {
253 return write_unsigned(static_cast<std::uint64_t>(value));
254 } else if constexpr (std::is_floating_point_v<U>) {
255 if (!std::isfinite(static_cast<double>(value))) {
257 "non-finite floating point value");
258 }
259 return write_floating(static_cast<double>(value));
260 } else if constexpr (std::same_as<U, std::string>) {
261 return write_string(value);
262 } else if constexpr (std::same_as<U, std::string_view>) {
263 return write_string(value);
264 } else if constexpr (detail::is_char_array_v<T>) {
265 constexpr auto extent = std::extent_v<std::remove_reference_t<T>>;
266 const auto size = extent != 0 && value[extent - 1] == '\0' ? extent - 1 : extent;
267 return write_string(std::string_view(value, size));
268 } else if constexpr (std::same_as<U, const char *> || std::same_as<U, char *>) {
269 if (value == nullptr) {
270 return write_null();
271 }
272 return write_string(std::string_view(value));
273 } else if constexpr (std::is_enum_v<U>) {
274 if (options_.enums == enum_encoding::underlying) {
275 return encode_value(gmp::enum_underlying(value));
276 }
277 auto i = gmp::enum_index(value);
278 if (!i) {
280 "enum value is not reflected");
281 }
283 } else if constexpr (detail::is_optional_v<U>) {
284 return value ? encode_value(*value) : write_null();
285 } else if constexpr (detail::is_sequence_v<U> || detail::is_set_v<U>) {
286 if (value.size() > options_.max_container_size) {
288 "container size exceeds configured limit");
289 }
290 depth_guard guard(*this);
291 if (!guard) {
293 "maximum serialization depth exceeded");
294 }
295 auto r = begin_array(value.size());
296 if (!r) {
297 return r;
298 }
299 std::size_t i = 0;
300 for (const auto &e : value) {
301 serialization_result<void> q;
302 if constexpr (detail::is_sequence_v<U>) {
303 using element_type = typename detail::sequence_traits<U>::value_type;
304 if constexpr (std::same_as<element_type, bool>) {
305 q = encode_value(static_cast<bool>(e));
306 } else {
307 q = encode_value(e);
308 }
309 } else {
310 q = encode_value(e);
311 }
312 if (!q) {
313 return prepend_serialization_path(q.error(), std::to_string(i));
314 }
315 ++i;
316 }
317 return end_array();
318 } else if constexpr (detail::is_array_v<U> || detail::is_pair_v<U> ||
319 detail::is_tuple_like_v<U>) {
320 constexpr auto N = std::tuple_size_v<U>;
321 if (N > options_.max_container_size) {
323 "container size exceeds configured limit");
324 }
325 depth_guard guard(*this);
326 if (!guard) {
328 "maximum serialization depth exceeded");
329 }
330 auto r = begin_array(N);
331 if (!r) {
332 return r;
333 }
334 auto each = [&]<std::size_t... I>(std::index_sequence<I...>) {
335 serialization_result<void> status;
336 auto encode_element = [&]<std::size_t J>() {
337 if (!status) {
338 return;
339 }
340 status = encode_value(std::get<J>(value));
341 if (!status) {
342 status.error() = prepend_serialization_path(status.error(), std::to_string(J));
343 }
344 };
345 (encode_element.template operator()<I>(), ...);
346 return status;
347 };
348 r = each(std::make_index_sequence<N>{});
349 if (!r) {
350 return r;
351 }
352 return end_array();
353 } else if constexpr (detail::is_map_v<U>) {
354 if (value.size() > options_.max_container_size) {
356 "container size exceeds configured limit");
357 }
358 depth_guard guard(*this);
359 if (!guard) {
361 "maximum serialization depth exceeded");
362 }
363 using K = typename detail::map_traits<U>::key_type;
364 if constexpr (detail::is_string_key_v<K>) {
365 auto r = begin_object(value.size());
366 if (!r) {
367 return r;
368 }
369 for (const auto &[k, v] : value) {
370 if (!(r = write_key(k))) {
371 return r;
372 }
373 if (!(r = encode_value(v))) {
374 return prepend_serialization_path(r.error(), k);
375 }
376 }
377 return end_object();
378 } else {
379 auto r = begin_array(value.size());
380 if (!r) {
381 return r;
382 }
383 std::size_t index = 0;
384 for (const auto &[k, v] : value) {
385 depth_guard entry_depth(*this);
386 if (!entry_depth) {
388 "maximum serialization depth exceeded",
389 std::to_string(index));
390 }
391 auto q = begin_array(2);
392 if (!q) {
393 return q;
394 }
395 if (!(q = encode_value(k))) {
396 return prepend_serialization_path(q.error(), std::to_string(index) + ".0");
397 }
398 if (!(q = encode_value(v))) {
399 return prepend_serialization_path(q.error(), std::to_string(index) + ".1");
400 }
401 if (!(q = end_array())) {
402 return q;
403 }
404 ++index;
405 }
406 return end_array();
407 }
408 } else if constexpr (detail::is_variant_v<U>) {
409 if (value.valueless_by_exception()) {
411 "valueless variant cannot be serialized");
412 }
413 if (std::size_t{2} > options_.max_container_size) {
415 "container size exceeds configured limit");
416 }
417 depth_guard guard(*this);
418 if (!guard) {
420 "maximum serialization depth exceeded");
421 }
422 auto r = begin_object(2);
423 if (!r) {
424 return r;
425 }
426 if (!(r = write_key("index"))) {
427 return r;
428 }
429 if (!(r = write_unsigned(value.index()))) {
430 return r;
431 }
432 if (!(r = write_key("value"))) {
433 return r;
434 }
435 r = std::visit([&](const auto &x) { return encode_value(x); }, value);
436 if (!r) {
437 return r;
438 }
439 return end_object();
440 } else if constexpr (reflectable<U>) {
441 static_assert(detail::valid_schema<U>(),
442 "serialization_schema contains an unknown/duplicate member "
443 "or an overlapping wire/alias name");
444 constexpr auto N = member_count_v<U>;
445 constexpr auto serialized_size = detail::serialized_member_count<U>();
446 if (serialized_size > options_.max_container_size) {
448 "container size exceeds configured limit");
449 }
450 depth_guard guard(*this);
451 if (!guard) {
453 "maximum serialization depth exceeded");
454 }
456 if (!r) {
457 return r;
458 }
459 auto each = [&]<std::size_t... I>(std::index_sequence<I...>) {
460 serialization_result<void> status;
461 auto one = [&](auto ic) {
462 constexpr std::size_t J = decltype(ic)::value;
463 using descriptor = detail::field_descriptor_t<U, J>;
464 if constexpr (!descriptor::is_transient) {
465 constexpr auto name = detail::descriptor_wire<descriptor>();
466 if (status) {
467 if (!(status = write_key(name))) {
468 return;
469 }
470 status = encode_value(member_ref<J>(value));
471 if (!status) {
472 status.error() = prepend_serialization_path(status.error(), name);
473 }
474 }
475 }
476 };
477 (one(std::integral_constant<std::size_t, I>{}), ...);
478 return status;
479 };
480 r = each(std::make_index_sequence<N>{});
481 if (!r) {
482 return r;
483 }
484 return end_object();
485 } else {
486 static_assert(detail::always_false_v<U>,
487 "type is not serializable; specialize serialization_traits<T>");
488 }
489 }
490
491 Writer &writer_;
492 serialization_options options_;
493 std::size_t depth_ = 0;
494};
495
496} // namespace gmp
497
498#endif // GMP_SERIALIZATION_SERIALIZER_HPP_
serialization_result< void > element(const T &value)
serialization_result< void > field(std::string_view name, const T &value)
serialization_result< void > end_object()
serialization_result< void > write_signed(std::int64_t v)
serialization_result< void > object(std::size_t size, F &&write_fields)
serialization_result< void > write_floating(double v)
serialization_result< void > array(std::size_t size, F &&write_elements)
serialization_result< void > write_unsigned(std::uint64_t v)
serialization_result< void > write_string(std::string_view v)
serialization_result< void > write_null()
serialization_result< void > end_array()
basic_serializer(Writer &writer, serialization_options options={})
serialization_result< void > write_bool(bool v)
serialization_result< void > encode(const T &value)
const serialization_options & options() const noexcept
serialization_result< void > begin_array(std::size_t n)
serialization_result< void > write_key(std::string_view v)
serialization_result< void > begin_object(std::size_t n)
consteval auto enum_values()
Get all enumerator values of an enumeration type at compile-time.
Definition meta.hpp:193
constexpr auto enum_index(E value) -> std::optional< std::size_t >
Get the index of an enumerator value in enum_values<E>().
Definition meta.hpp:380
constexpr auto enum_underlying(E value) noexcept -> std::underlying_type_t< E >
Return the underlying integer value of an enumerator.
Definition meta.hpp:414
Definition lock.hpp:21
serialization_error make_serialization_error(serialization_errc code, std::string message, std::string path={}, std::size_t offset=serialization_error::unknown_offset)
Definition error.hpp:160
serialization_error prepend_serialization_path(serialization_error error, std::string_view segment)
Definition error.hpp:165
std::size_t max_container_size
Definition options.hpp:32