StormByte 2.0.0
C++26 foundation of the StormByte suite
 
Loading...
Searching...
No Matches
serializable.txx
Go to the documentation of this file.
1/*
2* Copyright (C) 2024-2026 David C. Manuelda (StormBytePP)
3*
4* This file is part of StormByte.
5*
6* StormByte original source is dual-licensed:
7*
8* 1. GNU Lesser General Public License v3.0 (or later)
9* You may redistribute and/or modify this file under the terms of the
10* GNU Lesser General Public License as published by the Free Software
11* Foundation, either version 3 of the License, or (at your option)
12* any later version.
13*
14* 2. Commercial license
15* Alternatively, this file may be used under the terms of a commercial
16* license agreement with the copyright holder
17* (David C. Manuelda <StormByte@gmail.com>).
18*
19* Both licenses apply only to original StormByte source in this repository.
20* They do not cover other StormByte modules or any third-party material
21* shipped with this repository (including everything under thirdparty/),
22* which remains under its own license.
23*
24* Neither license grants any patent rights. Any patent licenses required
25* to use this software or third-party components must be obtained separately
26* from the patent holders.
27*
28* StormByte is distributed in the hope that it will be useful,
29* but WITHOUT ANY WARRANTY; without even the implied warranty of
30* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31* GNU Lesser General Public License for more details.
32*
33* You should have received a copy of the GNU Lesser General Public License
34* version 3 along with StormByte. If not, see
35* <https://www.gnu.org/licenses/lgpl-3.0.html>.
36*
37* SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-StormByte-Commercial
38*/
39
40#pragma once
41
42// Out-of-line implementation of StormByte::Serializable, included by serializable.hxx.
43// See serializable.hxx for documentation of each member.
44
45namespace StormByte {
46 template<typename T>
47 BinaryData Serializable<T>::Serialize() const noexcept {
48 if constexpr (Type::Optional<T>) {
49 return SerializeOptional();
50 } else if constexpr (Type::Pair<T>) {
51 return SerializePair();
52 } else if constexpr (Type::Container<T>) {
53 return SerializeContainer();
54 } else if constexpr (Type::TriviallyCopyable<T>) {
55 return SerializeTrivial();
56 } else {
57 return Detail::Codec<DecayedT>::Write(m_data);
58 }
59 }
60
61 template<typename T>
62 Expected<T, DeserializeError> Serializable<T>::Deserialize(std::span<const std::byte> data) noexcept {
63 if constexpr (Type::Optional<T>) {
64 return DeserializeOptional(data);
65 } else if constexpr (Type::Pair<T>) {
66 return DeserializePair(data);
67 } else if constexpr (Type::Container<T>) {
68 return DeserializeContainer(data);
69 } else if constexpr (Type::TriviallyCopyable<T>) {
70 return DeserializeTrivial(data);
71 } else {
72 return Detail::Codec<DecayedT>::Read(data);
73 }
74 }
75
76 template<typename T>
77 Expected<T, DeserializeError> Serializable<T>::Deserialize(const BinaryData& data) noexcept {
78 return Deserialize(data.span());
79 }
80
81 template<typename T>
82 ByteSize Serializable<T>::Size(const DecayedT& data) noexcept {
83 if constexpr (Type::Optional<T>) {
84 return SizeOptional(data);
85 } else if constexpr (Type::Pair<T>) {
86 return SizePair(data);
87 } else if constexpr (Type::Container<T>) {
88 return SizeContainer(data);
89 } else if constexpr (Type::TriviallyCopyable<T>) {
90 return ByteSize{sizeof(data)};
91 } else {
92 return Detail::Codec<DecayedT>::Size(data);
93 }
94 }
95
96 template<typename T>
97 template<typename U>
98 BinaryData Serializable<T>::SerializeTrivial() const noexcept
99 requires Type::TriviallyCopyable<U> {
100 DecayedT value = m_data;
101
102 if constexpr (!Type::SameAs<DecayedT, bool> &&
103 std::endian::native != std::endian::little) {
104 value = Type::Detail::swap_endian(value);
105 }
106
107 const auto* raw = reinterpret_cast<const std::byte*>(&value);
108 return BinaryData(raw, ByteSize{sizeof(value)});
109 }
110
111 template<typename T>
112 template<typename U>
113 BinaryData Serializable<T>::SerializeContainer() const noexcept
114 requires Type::Container<U> {
115 const std::uint64_t size = static_cast<std::uint64_t>(m_data.size());
116 BinaryData buffer = Serializable<std::uint64_t>(size).Serialize();
117 buffer.reserve(ByteSize{static_cast<std::size_t>(buffer.size())} + SizeContainer(m_data));
118 for (const auto& element : m_data) {
119 Serializable<std::remove_cvref_t<decltype(element)>> element_serial(element);
120 append_bytes(buffer, element_serial.Serialize());
121 }
122 return buffer;
123 }
124
125 template<typename T>
126 template<typename U>
127 BinaryData Serializable<T>::SerializePair() const noexcept
128 requires Type::Pair<U> {
129 Serializable<std::remove_cvref_t<typename T::first_type>> first_serial(m_data.first);
130 Serializable<std::remove_cvref_t<typename T::second_type>> second_serial(m_data.second);
131 BinaryData buffer;
132 buffer.reserve(SizePair(m_data));
133 append_bytes(buffer, first_serial.Serialize());
134 append_bytes(buffer, second_serial.Serialize());
135 return buffer;
136 }
137
138 template<typename T>
139 template<typename U>
140 BinaryData Serializable<T>::SerializeOptional() const noexcept
141 requires Type::Optional<U> {
142 const bool has_value = m_data.has_value();
143 BinaryData buffer;
144 buffer.reserve(SizeOptional(m_data));
145 append_bytes(buffer, Serializable<bool>(has_value).Serialize());
146 if (m_data.has_value()) {
147 Serializable<std::remove_cvref_t<decltype(m_data.value())>> value_serial(m_data.value());
148 append_bytes(buffer, value_serial.Serialize());
149 }
150 return buffer;
151 }
152
153 template<typename T>
154 template<typename U>
155 ByteSize Serializable<T>::SizeContainer(const DecayedT& data) noexcept
156 requires Type::Container<U> {
157 ByteSize size{sizeof(std::uint64_t)};
158 for (const auto& element : data) {
159 size += Serializable<std::remove_cvref_t<decltype(element)>>::Size(element);
160 }
161 return size;
162 }
163
164 template<typename T>
165 template<typename U>
166 ByteSize Serializable<T>::SizePair(const DecayedT& data) noexcept
167 requires Type::Pair<U> {
168 return
169 Serializable<std::remove_cvref_t<typename T::first_type>>::Size(data.first) +
170 Serializable<std::remove_cvref_t<typename T::second_type>>::Size(data.second);
171 }
172
173 template<typename T>
174 template<typename U>
175 ByteSize Serializable<T>::SizeOptional(const DecayedT& data) noexcept
176 requires Type::Optional<U> {
177 ByteSize size{sizeof(bool)};
178 if (data.has_value()) {
179 size += Serializable<std::remove_cvref_t<decltype(data.value())>>::Size(data.value());
180 }
181 return size;
182 }
183
184 template<typename T>
185 template<typename U>
186 Expected<T, DeserializeError> Serializable<T>::DeserializeTrivial(std::span<const std::byte> data) noexcept
187 requires Type::TriviallyCopyable<U> {
188 if constexpr (Type::SameAs<T, bool>) {
189 if (data.empty())
190 return Unexpected<DeserializeError>("Insufficient data for bool");
191
192 const auto raw = static_cast<unsigned char>(data[0]);
193 if (raw != 0 && raw != 1)
194 return Unexpected<DeserializeError>("Invalid bool value in stream");
195
196 return raw != 0;
197 } else {
198 if (data.size() < sizeof(T))
199 return Unexpected<DeserializeError>("Insufficient data for deserialization");
200
201 T result;
202 std::memcpy(&result, data.data(), sizeof(T));
203
204 if constexpr (std::endian::native != std::endian::little) {
205 result = Type::Detail::swap_endian(result);
206 }
207
208 return result;
209 }
210 }
211
212 template<typename T>
213 template<typename U>
214 Expected<T, DeserializeError> Serializable<T>::DeserializeContainer(std::span<const std::byte> data) noexcept
215 requires Type::Container<U> {
216 std::size_t offset = 0;
217
218 if (offset + sizeof(std::uint64_t) > data.size())
219 return Unexpected<DeserializeError>("Insufficient data for container size");
220
221 auto expected_container_size = Serializable<std::uint64_t>::Deserialize(
222 data.subspan(offset, sizeof(std::uint64_t)));
223 if (!expected_container_size)
224 return Unexpected(expected_container_size.error());
225
226 const std::uint64_t size = expected_container_size.value();
227 offset += sizeof(std::uint64_t);
228
229 if (size > static_cast<std::uint64_t>(data.size() - offset))
230 return Unexpected<DeserializeError>("Claimed container size exceeds remaining buffer");
231 if constexpr (Type::Array<T>) {
232 if (size != static_cast<std::uint64_t>(std::tuple_size_v<T>))
233 return Unexpected<DeserializeError>("Array size does not match serialized element count");
234 }
235
236 T container;
237 for (std::uint64_t i = 0; i < size; ++i) {
238 using ElementT = std::remove_cvref_t<typename T::value_type>;
239
240 if (offset >= data.size())
241 return Unexpected<DeserializeError>("Insufficient data for container element");
242
243 auto expected_element = Serializable<ElementT>::Deserialize(data.subspan(offset));
244 if (!expected_element)
245 return Unexpected(expected_element.error());
246
247 const ByteSize element_size = Serializable<ElementT>::Size(expected_element.value());
248 if constexpr (Type::Array<T>) {
249 container[static_cast<std::size_t>(i)] = std::move(expected_element.value());
250 } else {
251 container.insert(container.end(), std::move(expected_element.value()));
252 }
253 offset += static_cast<std::size_t>(element_size);
254 }
255 return container;
256 }
257
258 template<typename T>
259 template<typename U>
260 Expected<T, DeserializeError> Serializable<T>::DeserializePair(std::span<const std::byte> data) noexcept
261 requires Type::Pair<U> {
262 using FirstT = std::remove_cvref_t<typename T::first_type>;
263 using SecondT = std::remove_cvref_t<typename T::second_type>;
264
265 auto expected_first = Serializable<FirstT>::Deserialize(data);
266 if (!expected_first)
267 return Unexpected(expected_first.error());
268
269 const ByteSize first_size = Serializable<FirstT>::Size(expected_first.value());
270 if (static_cast<std::size_t>(first_size) > data.size())
271 return Unexpected<DeserializeError>("Insufficient data for pair second");
272
273 auto expected_second = Serializable<SecondT>::Deserialize(data.subspan(static_cast<std::size_t>(first_size)));
274 if (!expected_second)
275 return Unexpected(expected_second.error());
276
277 return T{ std::move(expected_first.value()), std::move(expected_second.value()) };
278 }
279
280 template<typename T>
281 template<typename U>
282 Expected<T, DeserializeError> Serializable<T>::DeserializeOptional(std::span<const std::byte> data) noexcept
283 requires Type::Optional<U> {
284 auto expected_has = Serializable<bool>::Deserialize(data);
285 if (!expected_has)
286 return Unexpected(expected_has.error());
287
288 if (!expected_has.value())
289 return T{};
290
291 const ByteSize flag_size = Serializable<bool>::Size(true);
292 if (static_cast<std::size_t>(flag_size) > data.size())
293 return Unexpected<DeserializeError>("Insufficient data for optional value");
294
295 using ValueT = std::remove_cvref_t<typename T::value_type>;
296 auto expected_value = Serializable<ValueT>::Deserialize(data.subspan(static_cast<std::size_t>(flag_size)));
297 if (!expected_value)
298 return Unexpected(expected_value.error());
299
300 return T{ std::move(expected_value.value()) };
301 }
302}