NcEngine
BinarySerializationDetail.h
1#pragma once
2
3#include "ncutility/NcError.h"
4
5#include <algorithm>
6#include <array>
7#include <iostream>
8#include <optional>
9#include <ranges>
10#include <string>
11#include <unordered_map>
12#include <vector>
13
15namespace nc::serialize::binary
16{
17template<class T>
18concept TriviallyCopyable = requires { requires std::is_trivially_copyable_v<T>; };
19
20template<class T>
21concept Aggregate = requires { requires std::is_aggregate_v<T>; };
22
23// Max supported member count for automatic aggregate serialization
24inline constexpr auto g_aggregateMaxMemberCount = 16ull;
25
26// Arbitrarily large value returned from MemberCount() when a type has too many members.
27inline constexpr size_t g_failedMemberCount = 0xFFFFFFFFFFFFFFFF;
28
29// A type which is implicitly convertable to all other types
30struct UniversalType{ template<class T> operator T() const; };
31
32// Count the number of members in an aggregate, returns g_failedMemberCount for types with > 16 members
33template<class T>
34 requires requires { std::is_aggregate_v<T>; }
35consteval auto MemberCount() -> size_t
36{
37 using U = UniversalType;
38 if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
39 return g_failedMemberCount;
40 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
41 return 16ull;
42 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
43 return 15ull;
44 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
45 return 14ull;
46 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
47 return 13ull;
48 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
49 return 12ull;
50 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
51 return 11ull;
52 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
53 return 10ull;
54 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
55 return 9ull;
56 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
57 return 8ull;
58 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}, U{}}; })
59 return 7ull;
60 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}, U{}}; })
61 return 6ull;
62 else if constexpr (requires { T{U{}, U{}, U{}, U{}, U{}}; })
63 return 5ull;
64 else if constexpr (requires { T{U{}, U{}, U{}, U{}}; })
65 return 4ull;
66 else if constexpr (requires { T{U{}, U{}, U{}}; })
67 return 3ull;
68 else if constexpr (requires { T{U{}, U{}}; })
69 return 2ull;
70 else if constexpr (requires { T{U{}}; })
71 return 1ull;
72 else
73 return 0ull;
74}
75
76// Concept for aggregate types that have automatic serialization support
77template<class T>
78concept UnpackableAggregate = Aggregate<T>
79 && !TriviallyCopyable<T>
80 && (MemberCount<T>() <= g_aggregateMaxMemberCount);
81
82template<class T>
83void Serialize(std::ostream& stream, const T& in);
84
85template<TriviallyCopyable T>
86void Serialize(std::ostream& stream, const T& in);
87
88template<UnpackableAggregate T>
89void Serialize(std::ostream& stream, const T& in);
90
91template<class T>
92void Serialize(std::ostream& stream, const std::vector<T>& in);
93
94template<class T, size_t I>
95void Serialize(std::ostream& stream, const std::array<T, I>& in);
96
97template<class T, class U>
98void Serialize(std::ostream& stream, const std::pair<T, U>& in);
99
100template<class K, class V>
101void Serialize(std::ostream& stream, const std::unordered_map<K, V>& in);
102
103template<class T>
104void Serialize(std::ostream& stream, const std::optional<T>& in);
105
106template<class T>
107void Deserialize(std::istream& stream, T& out);
108
109template<TriviallyCopyable T>
110void Deserialize(std::istream& stream, T& in);
111
112template<UnpackableAggregate T>
113void Deserialize(std::istream& stream, T& out);
114
115template<class T>
116void Deserialize(std::istream& stream, std::vector<T>& out);
117
118template<class T, size_t I>
119void Deserialize(std::istream& stream, std::array<T, I>& out);
120
121template<class T, class U>
122void Deserialize(std::istream& stream, std::pair<T, U>& out);
123
124template<class K, class V>
125void Deserialize(std::istream& stream, std::unordered_map<K, V>& out);
126
127template<class T>
128void Deserialize(std::istream& stream, std::optional<T>& out);
129
130template<class... Args>
131void SerializeMultiple(std::ostream& stream, Args&&... args)
132{
133 (Serialize(stream, args), ...);
134}
135
136template<class... Args>
137void DeserializeMultiple(std::istream& stream, Args&&... args)
138{
139 (Deserialize(stream, args), ...);
140}
141
142template<class C>
143void SerializeTrivialContainer(std::ostream& stream, const C& container)
144{
145 Serialize(stream, container.size());
146 stream.write(reinterpret_cast<const char*>(container.data()), sizeof(typename C::value_type) * container.size());
147}
148
149template<class C>
150void DeserializeTrivialContainer(std::istream& stream, C& container)
151{
152 auto size = size_t{};
153 Deserialize(stream, size);
154 container.resize(size);
155 stream.read(reinterpret_cast<char*>(container.data()), sizeof(typename C::value_type) * size);
156}
157
158template<class C>
159void SerializeNonTrivialContainer(std::ostream& stream, const C& container)
160{
161 auto size = container.size();
162 stream.write(reinterpret_cast<char*>(&size), sizeof(size));
163 for (const auto& obj : container) Serialize(stream, obj);
164}
165
166template<class C>
167void DeserializeNonTrivialContainer(std::istream& stream, C& container)
168{
169 auto count = size_t{};
170 Deserialize(stream, count);
171 container.reserve(count);
172 std::generate_n(std::back_inserter(container), count, [&stream]()
173 {
174 auto out = typename C::value_type{};
175 Deserialize(stream, out);
176 return out;
177 });
178}
179
180template<TriviallyCopyable T>
181void Serialize(std::ostream& stream, const T& in)
182{
183 stream.write(reinterpret_cast<const char*>(&in), sizeof(T));
184}
185
186template<TriviallyCopyable T>
187void Deserialize(std::istream& stream, T& out)
188{
189 stream.read(reinterpret_cast<char*>(&out), sizeof(T));
190}
191
192template<>
193inline void Serialize(std::ostream& stream, const std::string& in)
194{
195 SerializeTrivialContainer(stream, in);
196}
197
198template<>
199inline void Deserialize(std::istream& stream, std::string& out)
200{
201 DeserializeTrivialContainer(stream, out);
202}
203
204template<class T>
205void Serialize(std::ostream& stream, const std::vector<T>& in)
206{
207 if constexpr (std::is_trivially_copyable_v<T>)
208 SerializeTrivialContainer(stream, in);
209 else
210 SerializeNonTrivialContainer(stream, in);
211}
212
213template<class T>
214void Deserialize(std::istream& stream, std::vector<T>& out)
215{
216 if constexpr (std::is_trivially_copyable_v<T>)
217 DeserializeTrivialContainer(stream, out);
218 else
219 DeserializeNonTrivialContainer(stream, out);
220}
221
222template<class T, size_t I>
223void Serialize(std::ostream& stream, const std::array<T, I>& in)
224{
225 if constexpr(std::is_trivially_copyable_v<T>)
226 SerializeTrivialContainer(stream, in);
227 else
228 SerializeNonTrivialContainer(stream, in);
229}
230
231template<class T, size_t I>
232void Deserialize(std::istream& stream, std::array<T, I>& out)
233{
234 auto size = size_t{};
235 Deserialize(stream, size);
236 NC_ASSERT(size == out.size(), "Expected array size does not match stream contents");
237
238 if constexpr(std::is_trivially_copyable_v<T>)
239 {
240 stream.read(reinterpret_cast<char*>(out.data()), sizeof(T) * size);
241 }
242 else
243 {
244 std::ranges::for_each(out, [&stream](auto&& obj)
245 {
246 Deserialize(stream, obj);
247 });
248 }
249}
250
251template<class T, class U>
252void Serialize(std::ostream& stream, const std::pair<T, U>& in)
253{
254 SerializeMultiple(stream, in.first, in.second);
255}
256
257template<class T, class U>
258void Deserialize(std::istream& stream, std::pair<T, U>& out)
259{
260 DeserializeMultiple(stream, out.first, out.second);
261}
262
263template<class K, class V>
264void Serialize(std::ostream& stream, const std::unordered_map<K, V>& in)
265{
266 SerializeNonTrivialContainer(stream, in);
267}
268
269template<class K, class V>
270void Deserialize(std::istream& stream, std::unordered_map<K, V>& out)
271{
272 auto count = size_t{};
273 Deserialize(stream, count);
274 out.reserve(count);
275 std::generate_n(std::inserter(out, std::end(out)), count, [&stream]()
276 {
277 auto pair = std::pair<K, V>{};
278 Deserialize(stream, pair);
279 return pair;
280 });
281}
282
283template<class T>
284void Serialize(std::ostream& stream, const std::optional<T>& in)
285{
286 in.has_value()
287 ? SerializeMultiple(stream, true, in.value())
288 : Serialize(stream, false);
289}
290
291template<class T>
292void Deserialize(std::istream& stream, std::optional<T>& out)
293{
294 auto hasValue = false;
295 Deserialize(stream, hasValue);
296 if (hasValue)
297 {
298 out = T{};
299 Deserialize(stream, out.value());
300 }
301 else
302 {
303 out = std::nullopt;
304 }
305}
306
307template<UnpackableAggregate T>
308void Serialize(std::ostream& stream, const T& in)
309{
310 static constexpr auto memberCount = MemberCount<T>();
311 static_assert(memberCount <= g_aggregateMaxMemberCount);
312
313 if constexpr (memberCount == 16)
314 {
315 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16] = in;
316 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16);
317 }
318 else if constexpr (memberCount == 15)
319 {
320 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15] = in;
321 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15);
322 }
323 else if constexpr (memberCount == 14)
324 {
325 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14] = in;
326 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14);
327 }
328 else if constexpr (memberCount == 13)
329 {
330 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13] = in;
331 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13);
332 }
333 else if constexpr (memberCount == 12)
334 {
335 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12] = in;
336 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12);
337 }
338 else if constexpr (memberCount == 11)
339 {
340 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11] = in;
341 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11);
342 }
343 else if constexpr (memberCount == 10)
344 {
345 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10] = in;
346 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10);
347 }
348 else if constexpr (memberCount == 9)
349 {
350 const auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9] = in;
351 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9);
352 }
353 else if constexpr (memberCount == 8)
354 {
355 const auto& [m1, m2, m3, m4, m5, m6, m7, m8] = in;
356 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8);
357 }
358 else if constexpr (memberCount == 7)
359 {
360 const auto& [m1, m2, m3, m4, m5, m6, m7] = in;
361 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7);
362 }
363 else if constexpr (memberCount == 6)
364 {
365 const auto& [m1, m2, m3, m4, m5, m6] = in;
366 SerializeMultiple(stream, m1, m2, m3, m4, m5, m6);
367 }
368 else if constexpr (memberCount == 5)
369 {
370 const auto& [m1, m2, m3, m4, m5] = in;
371 SerializeMultiple(stream, m1, m2, m3, m4, m5);
372 }
373 else if constexpr (memberCount == 4)
374 {
375 const auto& [m1, m2, m3, m4] = in;
376 SerializeMultiple(stream, m1, m2, m3, m4);
377 }
378 else if constexpr (memberCount == 3)
379 {
380 const auto& [m1, m2, m3] = in;
381 SerializeMultiple(stream, m1, m2, m3);
382 }
383 else if constexpr (memberCount == 2)
384 {
385 const auto& [m1, m2] = in;
386 SerializeMultiple(stream, m1, m2);
387 }
388 else if constexpr (memberCount == 1)
389 {
390 const auto& [m1] = in;
391 SerializeMultiple(stream, m1);
392 }
393}
394
395template<UnpackableAggregate T>
396void Deserialize(std::istream& stream, T& out)
397{
398 static constexpr auto memberCount = MemberCount<T>();
399 static_assert(memberCount <= g_aggregateMaxMemberCount);
400
401 if constexpr (memberCount == 16)
402 {
403 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16] = out;
404 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16);
405 }
406 else if constexpr (memberCount == 15)
407 {
408 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15] = out;
409 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15);
410 }
411 else if constexpr (memberCount == 14)
412 {
413 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14] = out;
414 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14);
415 }
416 else if constexpr (memberCount == 13)
417 {
418 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13] = out;
419 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13);
420 }
421 else if constexpr (memberCount == 12)
422 {
423 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12] = out;
424 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12);
425 }
426 else if constexpr (memberCount == 11)
427 {
428 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11] = out;
429 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11);
430 }
431 else if constexpr (memberCount == 10)
432 {
433 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10] = out;
434 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10);
435 }
436 else if constexpr (memberCount == 9)
437 {
438 auto& [m1, m2, m3, m4, m5, m6, m7, m8, m9] = out;
439 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8, m9);
440 }
441 else if constexpr (memberCount == 8)
442 {
443 auto& [m1, m2, m3, m4, m5, m6, m7, m8] = out;
444 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7, m8);
445 }
446 else if constexpr (memberCount == 7)
447 {
448 auto& [m1, m2, m3, m4, m5, m6, m7] = out;
449 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6, m7);
450 }
451 else if constexpr (memberCount == 6)
452 {
453 auto& [m1, m2, m3, m4, m5, m6] = out;
454 DeserializeMultiple(stream, m1, m2, m3, m4, m5, m6);
455 }
456 else if constexpr (memberCount == 5)
457 {
458 auto& [m1, m2, m3, m4, m5] = out;
459 DeserializeMultiple(stream, m1, m2, m3, m4, m5);
460 }
461 else if constexpr (memberCount == 4)
462 {
463 auto& [m1, m2, m3, m4] = out;
464 DeserializeMultiple(stream, m1, m2, m3, m4);
465 }
466 else if constexpr (memberCount == 3)
467 {
468 auto& [m1, m2, m3] = out;
469 DeserializeMultiple(stream, m1, m2, m3);
470 }
471 else if constexpr (memberCount == 2)
472 {
473 auto& [m1, m2] = out;
474 DeserializeMultiple(stream, m1, m2);
475 }
476 else if constexpr (memberCount == 1)
477 {
478 auto& [m1] = out;
479 DeserializeMultiple(stream, m1);
480 }
481}
482} // namespace nc::serialize::binary
constexpr nc::serialize::cpo::DeserializeFn Deserialize
Deserialize an object from a stream.
Definition: BinarySerialization.h:47
constexpr nc::serialize::cpo::SerializeFn Serialize
Serialize an object to a stream.
Definition: BinarySerialization.h:41
constexpr size_t g_aggregateMaxMemberCount
The maximum number of members an aggregate may have for default serialization.
Definition: BinarySerialization.h:50