StormByte C++ Library 0.0.9999
StormByte is a comprehensive, cross-platform C++ library aimed at easing system programming, configuration management, logging, and database handling tasks. This library provides a unified API that abstracts away the complexities and inconsistencies of different platforms (Windows, Linux).
Loading...
Searching...
No Matches
serializable.hxx
1#pragma once
2
3#include <StormByte/exception.hxx>
4#include <StormByte/expected.hxx>
5#include <StormByte/helpers.hxx>
6#include <StormByte/type_traits.hxx>
7
8#include <bit>
9#include <cstring>
10#include <cstdint>
11#include <optional>
12#include <span>
13#include <utility>
14
22namespace StormByte {
28 template<typename T>
30 using DecayedT = std::decay_t<T>;
31
32 public:
37 Serializable(const DecayedT& data) noexcept : m_data(data) {}
38
43 Serializable(const Serializable& other) noexcept = delete;
44
49 Serializable(Serializable&& other) noexcept = delete;
50
54 ~Serializable() noexcept = default;
55
61 Serializable& operator=(const Serializable& other) noexcept = delete;
62
68 Serializable& operator=(Serializable&& other) noexcept = delete;
69
78 std::vector<std::byte> Serialize() const noexcept {
79 if constexpr (std::is_trivially_copyable_v<T>) {
80 return SerializeTrivial();
81 } else if constexpr (is_container<T>::value) {
82 return SerializeContainer();
83 } else if constexpr (is_pair<T>::value) {
84 return SerializePair();
85 } else if constexpr (is_optional<T>::value) {
86 return SerializeOptional();
87 } else {
88 return SerializeComplex();
89 }
90 }
91
102 static StormByte::Expected<T, DeserializeError> Deserialize(std::span<const std::byte> data) noexcept {
103 if constexpr (std::is_trivially_copyable_v<T>) {
104 return DeserializeTrivial(data);
105 } else if constexpr (is_container<T>::value) {
106 return DeserializeContainer(data);
107 } else if constexpr (is_pair<T>::value) {
108 return DeserializePair(data);
109 } else if constexpr (is_optional<T>::value) {
110 return DeserializeOptional(data);
111 } else {
112 return DeserializeComplex(data);
113 }
114 }
115
126 static StormByte::Expected<T, DeserializeError> Deserialize(const std::vector<std::byte>& data) noexcept {
127 return Deserialize(std::span<const std::byte>(data.data(), data.size()));
128 }
129
138 static std::size_t Size(const DecayedT& data) noexcept {
139 if constexpr (std::is_trivially_copyable_v<T>) {
140 return sizeof(data);
141 } else if constexpr (is_container<T>::value) {
142 return SizeContainer(data);
143 } else if constexpr (is_pair<T>::value) {
144 return SizePair(data);
145 } else if constexpr (is_optional<T>::value) {
146 return SizeOptional(data);
147 } else {
148 return SizeComplex(data);
149 }
150 }
151
152 private:
153 const DecayedT& m_data;
154
163 std::vector<std::byte> SerializeTrivial() const noexcept {
164 DecayedT value = m_data;
165
166 if constexpr (std::endian::native != std::endian::little) {
167 value = swap_endian(value);
168 }
169
170 return { reinterpret_cast<const std::byte*>(&value),
171 reinterpret_cast<const std::byte*>(&value) + sizeof(value) };
172 }
173
182 std::vector<std::byte> SerializeComplex() const noexcept;
183
192 std::vector<std::byte> SerializeContainer() const noexcept {
193 std::uint64_t size = static_cast<std::uint64_t>(m_data.size());
194 Serializable<std::uint64_t> size_serial(size);
195 std::vector<std::byte> buffer = size_serial.Serialize();
196 buffer.reserve(buffer.size() + SizeContainer(m_data));
197 for (const auto& element: m_data) {
198 Serializable<std::decay_t<decltype(element)>> element_serial(element);
199 auto element_data = element_serial.Serialize();
200 append_vector(buffer, std::move(element_data));
201 }
202 return buffer;
203 }
204
213 std::vector<std::byte> SerializePair() const noexcept {
214 Serializable<std::decay_t<typename T::first_type>> first_serial(m_data.first);
215 Serializable<std::decay_t<typename T::second_type>> second_serial(m_data.second);
216 std::vector<std::byte> buffer;
217 buffer.reserve(SizePair(m_data));
218 auto first_data = first_serial.Serialize();
219 auto second_data = second_serial.Serialize();
220 append_vector(buffer, std::move(first_data));
221 append_vector(buffer, std::move(second_data));
222 return buffer;
223 }
224
233 std::vector<std::byte> SerializeOptional() const noexcept {
234 bool has_value = m_data.has_value();
235 std::vector<std::byte> buffer;
236 buffer.reserve(SizeOptional(m_data));
237 auto has_value_data = Serializable<bool>(has_value).Serialize();
238 append_vector(buffer, std::move(has_value_data));
239 if (m_data.has_value()) {
240 Serializable<std::decay_t<decltype(m_data.value())>> value_serial(m_data.value());
241 auto value_data = value_serial.Serialize();
242 append_vector(buffer, std::move(value_data));
243 }
244 return buffer;
245 }
246
256 static std::size_t SizeComplex(const DecayedT& data) noexcept;
257
266 static std::size_t SizeContainer(const DecayedT& data) noexcept {
267 std::size_t size = sizeof(std::uint64_t);
268 for (const auto& element: data) {
269 size += Serializable<std::decay_t<decltype(element)>>::Size(element);
270 }
271 return size;
272 }
273
283 static std::size_t SizePair(const DecayedT& data) noexcept {
284 return
285 Serializable<std::decay_t<typename T::first_type>>::Size(data.first) +
286 Serializable<std::decay_t<typename T::second_type>>::Size(data.second);
287 }
288
298 static std::size_t SizeOptional(const DecayedT& data) noexcept {
299 std::size_t size = sizeof(bool);
300 if (data.has_value()) {
301 size += Serializable<std::decay_t<decltype(data.value())>>::Size(data.value());
302 }
303 return size;
304 }
305
316 static StormByte::Expected<T, DeserializeError> DeserializeTrivial(std::span<const std::byte> data) noexcept {
317 if (data.size() < sizeof(T))
318 return StormByte::Unexpected<DeserializeError>("Insufficient data for deserialization");
319 T result;
320 std::memcpy(&result, data.data(), sizeof(T));
321
322 if constexpr (std::endian::native != std::endian::little) {
323 result = swap_endian(result);
324 }
325
326 return result;
327 }
328
339 static StormByte::Expected<T, DeserializeError> DeserializeTrivial(const std::vector<std::byte>& data) noexcept {
340 return DeserializeTrivial(std::span<const std::byte>(data.data(), data.size()));
341 }
342
353 static StormByte::Expected<T, DeserializeError> DeserializeComplex(std::span<const std::byte> data) noexcept;
354
365 static StormByte::Expected<T, DeserializeError> DeserializeComplex(const std::vector<std::byte>& data) noexcept {
366 return DeserializeComplex(std::span<const std::byte>(data.data(), data.size()));
367 }
368
379 static StormByte::Expected<T, DeserializeError> DeserializeContainer(std::span<const std::byte> data) noexcept {
380 std::size_t offset = 0;
381
382 // Deserialize the container size
383 if (offset + sizeof(std::size_t) > data.size())
384 return StormByte::Unexpected<DeserializeError>("Insufficient data for container size");
385
386 auto expected_container_size = Serializable<std::size_t>::Deserialize(data.subspan(offset, sizeof(std::size_t)));
387 if (!expected_container_size)
388 return StormByte::Unexpected(expected_container_size.error());
389
390 std::size_t size = expected_container_size.value();
391 offset += sizeof(std::size_t);
392
393 // Reject malicious / absurd sizes early
394 if (size > data.size() - offset)
395 return StormByte::Unexpected<DeserializeError>("Claimed container size exceeds remaining buffer");
396
397 T container;
398 for (std::size_t i = 0; i < size; ++i) {
399 using ElementT = std::decay_t<typename T::value_type>;
400
401 if (offset >= data.size())
402 return StormByte::Unexpected<DeserializeError>("Insufficient data for container element");
403
404 auto expected_element = Serializable<ElementT>::Deserialize(data.subspan(offset));
405 if (!expected_element)
406 return StormByte::Unexpected(expected_element.error());
407
408 // Calculate the actual size of the deserialized element
409 std::size_t element_size = Serializable<ElementT>::Size(expected_element.value());
410 container.insert(container.end(), std::move(expected_element.value()));
411 offset += element_size;
412 }
413 return container;
414 }
415
426 static StormByte::Expected<T, DeserializeError> DeserializeContainer(const std::vector<std::byte>& data) noexcept {
427 return DeserializeContainer(std::span<const std::byte>(data.data(), data.size()));
428 }
429
440 static StormByte::Expected<T, DeserializeError> DeserializePair(std::span<const std::byte> data) noexcept {
441 using FirstT = std::decay_t<typename T::first_type>;
442 using SecondT = std::decay_t<typename T::second_type>;
443 std::size_t offset = 0;
444
445 // Deserialize first element
446 auto expected_first = Serializable<FirstT>::Deserialize(data.subspan(offset));
447 if (!expected_first)
448 return StormByte::Unexpected(expected_first.error());
449
450 offset += Serializable<FirstT>::Size(expected_first.value());
451
452 // Deserialize second element
453 if (offset >= data.size())
454 return StormByte::Unexpected<DeserializeError>("Insufficient data for pair second element");
455
456 auto expected_second = Serializable<SecondT>::Deserialize(data.subspan(offset));
457 if (!expected_second)
458 return StormByte::Unexpected(expected_second.error());
459
460 return T { std::move(expected_first.value()), std::move(expected_second.value()) };
461 }
462
473 static StormByte::Expected<T, DeserializeError> DeserializePair(const std::vector<std::byte>& data) noexcept {
474 return DeserializePair(std::span<const std::byte>(data.data(), data.size()));
475 }
476
487 static StormByte::Expected<T, DeserializeError> DeserializeOptional(std::span<const std::byte> data) noexcept {
488 std::size_t offset = 0;
489
490 // Deserialize the has_value boolean
491 if (offset + sizeof(bool) > data.size())
492 return StormByte::Unexpected<DeserializeError>("Insufficient data for optional flag");
493
494 auto expected_has_value = Serializable<bool>::Deserialize(data.subspan(offset, sizeof(bool)));
495 if (!expected_has_value)
496 return StormByte::Unexpected(expected_has_value.error());
497
498 offset += sizeof(bool);
499
500 if (expected_has_value.value()) {
501 if (offset >= data.size())
502 return StormByte::Unexpected<DeserializeError>("Insufficient data for optional value");
503
504 using ValueT = std::decay_t<typename T::value_type>;
505 auto expected_value = Serializable<ValueT>::Deserialize(data.subspan(offset));
506 if (!expected_value)
507 return StormByte::Unexpected(expected_value.error());
508
509 return T { std::move(expected_value.value()) };
510 } else {
511 return T {};
512 }
513 }
514
525 static StormByte::Expected<T, DeserializeError> DeserializeOptional(const std::vector<std::byte>& data) noexcept {
526 return DeserializeOptional(std::span<const std::byte>(data.data(), data.size()));
527 }
528 };
529}
The class to serialize and deserialize data.
Definition serializable.hxx:29
Serializable(const DecayedT &data) noexcept
The constructor of the Serializable class.
Definition serializable.hxx:37
static std::size_t Size(const DecayedT &data) noexcept
Calculates the serialized size of the data.
Definition serializable.hxx:138
static StormByte::Expected< T, DeserializeError > Deserialize(std::span< const std::byte > data) noexcept
Deserializes data from a byte span.
Definition serializable.hxx:102
Serializable(const Serializable &other) noexcept=delete
The copy constructor of the Serializable class.
Serializable(Serializable &&other) noexcept=delete
The move constructor of the Serializable class.
static StormByte::Expected< T, DeserializeError > Deserialize(const std::vector< std::byte > &data) noexcept
Deserializes data from a byte vector.
Definition serializable.hxx:126
std::vector< std::byte > Serialize() const noexcept
Serializes the data into a byte vector.
Definition serializable.hxx:78
~Serializable() noexcept=default
The destructor of the Serializable class.
Main namespace for the StormByte library.
auto Unexpected(std::shared_ptr< E > error_ptr)
Creates an std::unexpected with a shared pointer to the error.
Definition expected.hxx:44
std::conditional_t< Type::Reference< T >, std::expected< std::reference_wrapper< std::remove_reference_t< T > >, std::shared_ptr< E > >, std::expected< T, std::shared_ptr< E > > > Expected
Expected type with support for reference types.
Definition expected.hxx:33