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 <cstring>
9#include <optional>
10#include <span>
11#include <utility>
12
20namespace StormByte {
26 template<typename T>
28 using DecayedT = std::decay_t<T>;
29
30 public:
35 Serializable(const DecayedT& data) noexcept : m_data(data) {}
36
41 Serializable(const Serializable& other) noexcept = delete;
42
47 Serializable(Serializable&& other) noexcept = delete;
48
52 ~Serializable() noexcept = default;
53
59 Serializable& operator=(const Serializable& other) noexcept = delete;
60
66 Serializable& operator=(Serializable&& other) noexcept = delete;
67
76 std::vector<std::byte> Serialize() const noexcept {
77 if constexpr (std::is_trivially_copyable_v<T>) {
78 return SerializeTrivial();
79 } else if constexpr (is_container<T>::value) {
80 return SerializeContainer();
81 } else if constexpr (is_pair<T>::value) {
82 return SerializePair();
83 } else if constexpr (is_optional<T>::value) {
84 return SerializeOptional();
85 } else {
86 return SerializeComplex();
87 }
88 }
89
100 static StormByte::Expected<T, DeserializeError> Deserialize(std::span<const std::byte> data) noexcept {
101 if constexpr (std::is_trivially_copyable_v<T>) {
102 return DeserializeTrivial(data);
103 } else if constexpr (is_container<T>::value) {
104 return DeserializeContainer(data);
105 } else if constexpr (is_pair<T>::value) {
106 return DeserializePair(data);
107 } else if constexpr (is_optional<T>::value) {
108 return DeserializeOptional(data);
109 } else {
110 return DeserializeComplex(data);
111 }
112 }
113
124 static StormByte::Expected<T, DeserializeError> Deserialize(const std::vector<std::byte>& data) noexcept {
125 return Deserialize(std::span<const std::byte>(data.data(), data.size()));
126 }
127
136 static std::size_t Size(const DecayedT& data) noexcept {
137 if constexpr (std::is_trivially_copyable_v<T>) {
138 return sizeof(data);
139 } else if constexpr (is_container<T>::value) {
140 return SizeContainer(data);
141 } else if constexpr (is_pair<T>::value) {
142 return SizePair(data);
143 } else if constexpr (is_optional<T>::value) {
144 return SizeOptional(data);
145 } else {
146 return SizeComplex(data);
147 }
148 }
149
150 private:
151 const DecayedT& m_data;
152
161 std::vector<std::byte> SerializeTrivial() const noexcept {
162 return { reinterpret_cast<const std::byte*>(&m_data), reinterpret_cast<const std::byte*>(&m_data) + sizeof(m_data) };
163 }
164
173 std::vector<std::byte> SerializeComplex() const noexcept;
174
183 std::vector<std::byte> SerializeContainer() const noexcept {
184 std::size_t size = m_data.size();
185 Serializable<std::size_t> size_serial(size);
186 std::vector<std::byte> buffer = size_serial.Serialize();
187 buffer.reserve(buffer.size() + SizeContainer(m_data));
188 for (const auto& element: m_data) {
189 Serializable<std::decay_t<decltype(element)>> element_serial(element);
190 auto element_data = element_serial.Serialize();
191 append_vector(buffer, std::move(element_data));
192 }
193 return buffer;
194 }
195
204 std::vector<std::byte> SerializePair() const noexcept {
205 Serializable<std::decay_t<typename T::first_type>> first_serial(m_data.first);
206 Serializable<std::decay_t<typename T::second_type>> second_serial(m_data.second);
207 std::vector<std::byte> buffer;
208 buffer.reserve(SizePair(m_data));
209 auto first_data = first_serial.Serialize();
210 auto second_data = second_serial.Serialize();
211 append_vector(buffer, std::move(first_data));
212 append_vector(buffer, std::move(second_data));
213 return buffer;
214 }
215
224 std::vector<std::byte> SerializeOptional() const noexcept {
225 bool has_value = m_data.has_value();
226 std::vector<std::byte> buffer;
227 buffer.reserve(SizeOptional(m_data));
228 auto has_value_data = Serializable<bool>(has_value).Serialize();
229 append_vector(buffer, std::move(has_value_data));
230 if (m_data.has_value()) {
231 Serializable<std::decay_t<decltype(m_data.value())>> value_serial(m_data.value());
232 auto value_data = value_serial.Serialize();
233 append_vector(buffer, std::move(value_data));
234 }
235 return buffer;
236 }
237
247 static std::size_t SizeComplex(const DecayedT& data) noexcept;
248
258 static std::size_t SizeContainer(const DecayedT& data) noexcept {
259 std::size_t size = sizeof(std::size_t);
260 for (const auto& element: data) {
261 size += Serializable<std::decay_t<decltype(element)>>::Size(element);
262 }
263 return size;
264 }
265
275 static std::size_t SizePair(const DecayedT& data) noexcept {
276 return
277 Serializable<std::decay_t<typename T::first_type>>::Size(data.first) +
278 Serializable<std::decay_t<typename T::second_type>>::Size(data.second);
279 }
280
290 static std::size_t SizeOptional(const DecayedT& data) noexcept {
291 std::size_t size = sizeof(bool);
292 if (data.has_value()) {
293 size += Serializable<std::decay_t<decltype(data.value())>>::Size(data.value());
294 }
295 return size;
296 }
297
308 static StormByte::Expected<T, DeserializeError> DeserializeTrivial(std::span<const std::byte> data) noexcept {
309 if (data.size() < sizeof(T))
310 return StormByte::Unexpected<DeserializeError>("Insufficient data for deserialization");
311 T result;
312 std::memcpy(&result, data.data(), sizeof(T));
313 return result;
314 }
315
326 static StormByte::Expected<T, DeserializeError> DeserializeTrivial(const std::vector<std::byte>& data) noexcept {
327 return DeserializeTrivial(std::span<const std::byte>(data.data(), data.size()));
328 }
329
340 static StormByte::Expected<T, DeserializeError> DeserializeComplex(std::span<const std::byte> data) noexcept;
341
352 static StormByte::Expected<T, DeserializeError> DeserializeComplex(const std::vector<std::byte>& data) noexcept {
353 return DeserializeComplex(std::span<const std::byte>(data.data(), data.size()));
354 }
355
366 static StormByte::Expected<T, DeserializeError> DeserializeContainer(std::span<const std::byte> data) noexcept {
367 std::size_t offset = 0;
368
369 // Deserialize the container size
370 if (offset + sizeof(std::size_t) > data.size())
371 return StormByte::Unexpected<DeserializeError>("Insufficient data for container size");
372
373 auto expected_container_size = Serializable<std::size_t>::Deserialize(data.subspan(offset, sizeof(std::size_t)));
374 if (!expected_container_size)
375 return StormByte::Unexpected(expected_container_size.error());
376
377 std::size_t size = expected_container_size.value();
378 offset += sizeof(std::size_t);
379
380 T container;
381 for (std::size_t i = 0; i < size; ++i) {
382 using ElementT = std::decay_t<typename T::value_type>;
383
384 if (offset >= data.size())
385 return StormByte::Unexpected<DeserializeError>("Insufficient data for container element");
386
387 auto expected_element = Serializable<ElementT>::Deserialize(data.subspan(offset));
388 if (!expected_element)
389 return StormByte::Unexpected(expected_element.error());
390
391 // Calculate the actual size of the deserialized element
392 std::size_t element_size = Serializable<ElementT>::Size(expected_element.value());
393 container.insert(container.end(), std::move(expected_element.value()));
394 offset += element_size;
395 }
396 return container;
397 }
398
409 static StormByte::Expected<T, DeserializeError> DeserializeContainer(const std::vector<std::byte>& data) noexcept {
410 return DeserializeContainer(std::span<const std::byte>(data.data(), data.size()));
411 }
412
423 static StormByte::Expected<T, DeserializeError> DeserializePair(std::span<const std::byte> data) noexcept {
424 using FirstT = std::decay_t<typename T::first_type>;
425 using SecondT = std::decay_t<typename T::second_type>;
426 std::size_t offset = 0;
427
428 // Deserialize first element
429 auto expected_first = Serializable<FirstT>::Deserialize(data.subspan(offset));
430 if (!expected_first)
431 return StormByte::Unexpected(expected_first.error());
432
433 offset += Serializable<FirstT>::Size(expected_first.value());
434
435 // Deserialize second element
436 if (offset >= data.size())
437 return StormByte::Unexpected<DeserializeError>("Insufficient data for pair second element");
438
439 auto expected_second = Serializable<SecondT>::Deserialize(data.subspan(offset));
440 if (!expected_second)
441 return StormByte::Unexpected(expected_second.error());
442
443 return T { std::move(expected_first.value()), std::move(expected_second.value()) };
444 }
445
456 static StormByte::Expected<T, DeserializeError> DeserializePair(const std::vector<std::byte>& data) noexcept {
457 return DeserializePair(std::span<const std::byte>(data.data(), data.size()));
458 }
459
470 static StormByte::Expected<T, DeserializeError> DeserializeOptional(std::span<const std::byte> data) noexcept {
471 std::size_t offset = 0;
472
473 // Deserialize the has_value boolean
474 if (offset + sizeof(bool) > data.size())
475 return StormByte::Unexpected<DeserializeError>("Insufficient data for optional flag");
476
477 auto expected_has_value = Serializable<bool>::Deserialize(data.subspan(offset, sizeof(bool)));
478 if (!expected_has_value)
479 return StormByte::Unexpected(expected_has_value.error());
480
481 offset += sizeof(bool);
482
483 if (expected_has_value.value()) {
484 if (offset >= data.size())
485 return StormByte::Unexpected<DeserializeError>("Insufficient data for optional value");
486
487 using ValueT = std::decay_t<typename T::value_type>;
488 auto expected_value = Serializable<ValueT>::Deserialize(data.subspan(offset));
489 if (!expected_value)
490 return StormByte::Unexpected(expected_value.error());
491
492 return T { std::move(expected_value.value()) };
493 } else {
494 return T {};
495 }
496 }
497
508 static StormByte::Expected<T, DeserializeError> DeserializeOptional(const std::vector<std::byte>& data) noexcept {
509 return DeserializeOptional(std::span<const std::byte>(data.data(), data.size()));
510 }
511 };
512}
The class to serialize and deserialize data.
Definition serializable.hxx:27
Serializable(const DecayedT &data) noexcept
The constructor of the Serializable class.
Definition serializable.hxx:35
static std::size_t Size(const DecayedT &data) noexcept
Calculates the serialized size of the data.
Definition serializable.hxx:136
static StormByte::Expected< T, DeserializeError > Deserialize(std::span< const std::byte > data) noexcept
Deserializes data from a byte span.
Definition serializable.hxx:100
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:124
std::vector< std::byte > Serialize() const noexcept
Serializes the data into a byte vector.
Definition serializable.hxx:76
~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