1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "Allocators/BsMemoryAllocator.h"
6#include "Prerequisites/BsFwdDeclUtil.h" // For TIDs
7#include "Prerequisites/BsTypes.h" // For UINT32
8
9#include <limits>
10#include <type_traits> // For std::is_pod
11#include <utility> // For std::pair
12#include <vector>
13
14namespace bs
15{
16 /** @addtogroup Utility
17 * @{
18 */
19
20 /** @addtogroup RTTI
21 * @{
22 */
23
24 /**
25 * Template that you may specialize with a class if you want to provide simple serialization for it.
26 *
27 * Any type that uses the "plain" field in the RTTI system must specialize this class.
28 *
29 * @note
30 * Normally you will want to implement IReflectable interface if you want to provide serialization
31 * as that interface properly handles versioning, nested objects, pointer handling and more.
32 *
33 * @note
34 * This class is useful for types you can easily serialize using a memcpy (built-in types like int/float/etc), or
35 * types you cannot modify so they implement IReflectable interface (like std::string or std::vector).
36 *
37 * @see RTTITypeBase
38 * @see RTTIField
39 */
40 template<class T>
41 struct RTTIPlainType
42 {
43 static_assert(std::is_pod<T>::value,
44 "Provided type isn't plain-old-data. You need to specialize RTTIPlainType template in order to serialize this type. "\
45 " (Or call BS_ALLOW_MEMCPY_SERIALIZATION(type) macro if you are sure the type can be properly serialized using just memcpy.)");
46
47 enum { id = 0 /**< Unique id for the serializable type. */ };
48 enum { hasDynamicSize = 0 /**< 0 (Object has static size less than 255 bytes, for example int) or 1 (Dynamic size with no size restriction, for example string) */ };
49
50 /** Serializes the provided object into the provided pre-allocated memory buffer. */
51 static void toMemory(const T& data, char* memory)
52 {
53 memcpy(memory, &data, sizeof(T));
54 }
55
56 /**
57 * Deserializes a previously allocated object from the provided memory buffer. Return the number of bytes read
58 * from the memory buffer.
59 */
60 static UINT32 fromMemory(T& data, char* memory)
61 {
62 memcpy(&data, memory, sizeof(T));
63 return sizeof(T);
64 }
65
66 /** Returns the size of the provided object. (Works for both static and dynamic size types) */
67 static UINT32 getDynamicSize(const T& data)
68 {
69 return sizeof(T);
70 }
71 };
72
73 /**
74 * Helper method when serializing known data types that have valid
75 * RTTIPlainType specialization.
76 *
77 * Returns the size of the element. If elements serializable type is
78 * specialized with hasDynamicSize == true, the dynamic size is calculated,
79 * otherwise sizeof() is used.
80 */
81 template<class ElemType>
82 UINT32 rttiGetElemSize(const ElemType& data)
83 {
84 if(RTTIPlainType<ElemType>::hasDynamicSize == 1)
85 return RTTIPlainType<ElemType>::getDynamicSize(data);
86 else
87 return sizeof(ElemType);
88 }
89
90 /**
91 * Helper method when serializing known data types that have valid
92 * RTTIPlainType specialization.
93 *
94 * Writes the specified data into memory, advances the memory pointer by the
95 * bytes written and returns pointer to new memory.
96 */
97 template<class ElemType>
98 char* rttiWriteElem(const ElemType& data, char* memory)
99 {
100 RTTIPlainType<ElemType>::toMemory(data, memory);
101
102 return memory + rttiGetElemSize(data);
103 }
104
105 /**
106 * Helper method when serializing known data types that have valid
107 * RTTIPlainType specialization.
108 *
109 * Writes the specified data into memory, advances the memory pointer by the
110 * bytes written and returns pointer to new memory. Also increases the size
111 * value by the size of the written element.
112 */
113 template<class ElemType>
114 char* rttiWriteElem(const ElemType& data, char* memory, UINT32& size)
115 {
116 RTTIPlainType<ElemType>::toMemory(data, memory);
117
118 UINT32 elemSize = rttiGetElemSize(data);
119 size += elemSize;
120
121 return memory + elemSize;
122 }
123
124 /**
125 * Helper method when serializing known data types that have valid
126 * RTTIPlainType specialization.
127 *
128 * Reads the specified data into memory, advances the memory pointer by the
129 * bytes read and returns pointer to new memory.
130 */
131 template<class ElemType>
132 char* rttiReadElem(ElemType& data, char* memory)
133 {
134 UINT32 size = RTTIPlainType<ElemType>::fromMemory(data, memory);
135
136 return memory + size;
137 }
138
139 /**
140 * Helper method when serializing known data types that have valid
141 * RTTIPlainType specialization.
142 *
143 * Reads the specified data into memory, advances the memory pointer by the
144 * bytes read and returns pointer to new memory. Also increases the size
145 * value by the size of the read element.
146 */
147 template<class ElemType>
148 char* rttiReadElem(ElemType& data, char* memory, UINT32& size)
149 {
150 UINT32 elemSize = RTTIPlainType<ElemType>::fromMemory(data, memory);
151
152 size += elemSize;
153 return memory + elemSize;
154 }
155
156 /** Helper for checking for existance of rttiEnumFields method on a class. */
157 template <class T>
158 struct has_rttiEnumFields
159 {
160 struct dummy {};
161
162 template <typename C, typename P>
163 static auto test(P* p) -> decltype(std::declval<C>().rttiEnumFields(*p), std::true_type());
164
165 template <typename, typename>
166 static std::false_type test(...);
167
168 typedef decltype(test<T, dummy>(nullptr)) type;
169 static const bool value = std::is_same<std::true_type, decltype(test<T, dummy>(nullptr))>::value;
170 };
171
172 /**
173 * Notify the RTTI system that the specified type may be serialized just by using a memcpy.
174 *
175 * @note Internally this creates a basic RTTIPlainType<T> specialization for the type.
176 *
177 * @see RTTIPlainType<T>
178 */
179#define BS_ALLOW_MEMCPY_SERIALIZATION(type) \
180 static_assert (std::is_trivially_copyable<type>()==true, \
181 #type " is not trivially copyable"); \
182 template<> struct RTTIPlainType<type> \
183 { enum { id=0 }; enum { hasDynamicSize = 0 }; \
184 static void toMemory(const type& data, char* memory) \
185 { memcpy(memory, &data, sizeof(type)); } \
186 static UINT32 fromMemory(type& data, char* memory) \
187 { memcpy(&data, memory, sizeof(type)); return sizeof(type); } \
188 static UINT32 getDynamicSize(const type& data) \
189 { return sizeof(type); } \
190 };
191
192 /** @cond SPECIALIZATIONS */
193
194 /**
195 * RTTIPlainType for std::vector.
196 *
197 * @see RTTIPlainType
198 */
199 template<class T> struct RTTIPlainType<std::vector<T, StdAlloc<T>>>
200 {
201 enum { id = TID_Vector }; enum { hasDynamicSize = 1 };
202
203 /** @copydoc RTTIPlainType::toMemory */
204 static void toMemory(const std::vector<T, StdAlloc<T>>& data, char* memory)
205 {
206 UINT32 size = sizeof(UINT32);
207 char* memoryStart = memory;
208 memory += sizeof(UINT32);
209
210 UINT32 numElements = (UINT32)data.size();
211 memcpy(memory, &numElements, sizeof(UINT32));
212 memory += sizeof(UINT32);
213 size += sizeof(UINT32);
214
215 for(const auto& item : data)
216 {
217 UINT32 elementSize = rttiGetElemSize(item);
218 RTTIPlainType<T>::toMemory(item, memory);
219
220 memory += elementSize;
221 size += elementSize;
222 }
223
224 memcpy(memoryStart, &size, sizeof(UINT32));
225 }
226
227 /** @copydoc RTTIPlainType::fromMemory */
228 static UINT32 fromMemory(std::vector<T, StdAlloc<T>>& data, char* memory)
229 {
230 UINT32 size = 0;
231 memcpy(&size, memory, sizeof(UINT32));
232 memory += sizeof(UINT32);
233
234 UINT32 numElements;
235 memcpy(&numElements, memory, sizeof(UINT32));
236 memory += sizeof(UINT32);
237
238 data.clear();
239 for(UINT32 i = 0; i < numElements; i++)
240 {
241 T element;
242 UINT32 elementSize = RTTIPlainType<T>::fromMemory(element, memory);
243 data.push_back(element);
244
245 memory += elementSize;
246 }
247
248 return size;
249 }
250
251 /** @copydoc RTTIPlainType::getDynamicSize */
252 static UINT32 getDynamicSize(const std::vector<T, StdAlloc<T>>& data)
253 {
254 UINT64 dataSize = sizeof(UINT32) * 2;
255
256 for(const auto& item : data)
257 dataSize += rttiGetElemSize(item);
258
259 assert(dataSize <= std::numeric_limits<UINT32>::max());
260
261 return (UINT32)dataSize;
262 }
263 };
264
265 /**
266 * RTTIPlainType for std::list.
267 *
268 * @see RTTIPlainType
269 */
270 template<class T> struct RTTIPlainType<std::list<T, StdAlloc<T>>>
271 {
272 enum { id = TID_List }; enum { hasDynamicSize = 1 };
273
274 /** @copydoc RTTIPlainType::toMemory */
275 static void toMemory(const std::list<T, StdAlloc<T>>& data, char* memory)
276 {
277 UINT32 size = sizeof(UINT32);
278 char* memoryStart = memory;
279 memory += sizeof(UINT32);
280
281 UINT32 numElements = (UINT32)data.size();
282 memcpy(memory, &numElements, sizeof(UINT32));
283 memory += sizeof(UINT32);
284 size += sizeof(UINT32);
285
286 for(const auto& item : data)
287 {
288 UINT32 elementSize = rttiGetElemSize(item);
289 RTTIPlainType<T>::toMemory(item, memory);
290
291 memory += elementSize;
292 size += elementSize;
293 }
294
295 memcpy(memoryStart, &size, sizeof(UINT32));
296 }
297
298 /** @copydoc RTTIPlainType::fromMemory */
299 static UINT32 fromMemory(std::list<T, StdAlloc<T>>& data, char* memory)
300 {
301 UINT32 size = 0;
302 memcpy(&size, memory, sizeof(UINT32));
303 memory += sizeof(UINT32);
304
305 UINT32 numElements;
306 memcpy(&numElements, memory, sizeof(UINT32));
307 memory += sizeof(UINT32);
308
309 for(UINT32 i = 0; i < numElements; i++)
310 {
311 T element;
312 UINT32 elementSize = RTTIPlainType<T>::fromMemory(element, memory);
313 data.push_back(element);
314
315 memory += elementSize;
316 }
317
318 return size;
319 }
320
321 /** @copydoc RTTIPlainType::getDynamicSize */
322 static UINT32 getDynamicSize(const std::list<T, StdAlloc<T>>& data)
323 {
324 UINT64 dataSize = sizeof(UINT32) * 2;
325
326 for(const auto& item : data)
327 dataSize += rttiGetElemSize(item);
328
329 assert(dataSize <= std::numeric_limits<UINT32>::max());
330
331 return (UINT32)dataSize;
332 }
333 };
334
335 /**
336 * RTTIPlainType for std::set.
337 *
338 * @see RTTIPlainType
339 */
340 template<class T> struct RTTIPlainType<std::set<T, std::less<T>, StdAlloc<T>>>
341 {
342 enum { id = TID_Set }; enum { hasDynamicSize = 1 };
343
344 /** @copydoc RTTIPlainType::toMemory */
345 static void toMemory(const std::set<T, std::less<T>, StdAlloc<T>>& data, char* memory)
346 {
347 UINT32 size = sizeof(UINT32);
348 char* memoryStart = memory;
349 memory += sizeof(UINT32);
350
351 UINT32 numElements = (UINT32)data.size();
352 memcpy(memory, &numElements, sizeof(UINT32));
353 memory += sizeof(UINT32);
354 size += sizeof(UINT32);
355
356 for(const auto& item : data)
357 {
358 UINT32 elementSize = rttiGetElemSize(item);
359 RTTIPlainType<T>::toMemory(item, memory);
360
361 memory += elementSize;
362 size += elementSize;
363 }
364
365 memcpy(memoryStart, &size, sizeof(UINT32));
366 }
367
368 /** @copydoc RTTIPlainType::fromMemory */
369 static UINT32 fromMemory(std::set<T, std::less<T>, StdAlloc<T>>& data, char* memory)
370 {
371 UINT32 size = 0;
372 memcpy(&size, memory, sizeof(UINT32));
373 memory += sizeof(UINT32);
374
375 UINT32 numElements;
376 memcpy(&numElements, memory, sizeof(UINT32));
377 memory += sizeof(UINT32);
378
379 for(UINT32 i = 0; i < numElements; i++)
380 {
381 T element;
382 UINT32 elementSize = RTTIPlainType<T>::fromMemory(element, memory);
383 data.insert(element);
384
385 memory += elementSize;
386 }
387
388 return size;
389 }
390
391 /** @copydoc RTTIPlainType::getDynamicSize */
392 static UINT32 getDynamicSize(const std::set<T, std::less<T>, StdAlloc<T>>& data)
393 {
394 UINT64 dataSize = sizeof(UINT32) * 2;
395
396 for(const auto& item : data)
397 dataSize += rttiGetElemSize(item);
398
399 assert(dataSize <= std::numeric_limits<UINT32>::max());
400
401 return (UINT32)dataSize;
402 }
403 };
404
405 /**
406 * RTTIPlainType for std::map.
407 *
408 * @see RTTIPlainType
409 */
410 template<class Key, class Value> struct RTTIPlainType<std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>>
411 {
412 enum { id = TID_Map }; enum { hasDynamicSize = 1 };
413
414 /** @copydoc RTTIPlainType::toMemory */
415 static void toMemory(const std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data, char* memory)
416 {
417 UINT32 size = sizeof(UINT32);
418 char* memoryStart = memory;
419 memory += sizeof(UINT32);
420
421 UINT32 numElements = (UINT32)data.size();
422 memcpy(memory, &numElements, sizeof(UINT32));
423 memory += sizeof(UINT32);
424 size += sizeof(UINT32);
425
426 for(const auto& item : data)
427 {
428 UINT32 keySize = rttiGetElemSize(item.first);
429 RTTIPlainType<Key>::toMemory(item.first, memory);
430
431 memory += keySize;
432 size += keySize;
433
434 UINT32 valueSize = rttiGetElemSize(item.second);
435 RTTIPlainType<Value>::toMemory(item.second, memory);
436
437 memory += valueSize;
438 size += valueSize;
439 }
440
441 memcpy(memoryStart, &size, sizeof(UINT32));
442 }
443
444 /** @copydoc RTTIPlainType::fromMemory */
445 static UINT32 fromMemory(std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data, char* memory)
446 {
447 UINT32 size = 0;
448 memcpy(&size, memory, sizeof(UINT32));
449 memory += sizeof(UINT32);
450
451 UINT32 numElements;
452 memcpy(&numElements, memory, sizeof(UINT32));
453 memory += sizeof(UINT32);
454
455 for(UINT32 i = 0; i < numElements; i++)
456 {
457 Key key;
458 UINT32 keySize = RTTIPlainType<Key>::fromMemory(key, memory);
459 memory += keySize;
460
461 Value value;
462 UINT32 valueSize = RTTIPlainType<Value>::fromMemory(value, memory);
463 memory += valueSize;
464
465 data[key] = value;
466 }
467
468 return size;
469 }
470
471 /** @copydoc RTTIPlainType::getDynamicSize */
472 static UINT32 getDynamicSize(const std::map<Key, Value, std::less<Key>, StdAlloc<std::pair<const Key, Value>>>& data)
473 {
474 UINT64 dataSize = sizeof(UINT32) * 2;
475
476 for(const auto& item : data)
477 {
478 dataSize += rttiGetElemSize(item.first);
479 dataSize += rttiGetElemSize(item.second);
480 }
481
482 assert(dataSize <= std::numeric_limits<UINT32>::max());
483
484 return (UINT32)dataSize;
485 }
486 };
487
488 /**
489 * RTTIPlainType for std::unordered_map.
490 *
491 * @see RTTIPlainType
492 */
493 template<class Key, class Value>
494 struct RTTIPlainType<std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>, StdAlloc<std::pair<const Key, Value>>>>
495 {
496 enum { id = TID_UnorderedMap }; enum { hasDynamicSize = 1 };
497
498 typedef std::unordered_map<Key, Value, std::hash<Key>, std::equal_to<Key>, StdAlloc<std::pair<const Key, Value>>> MapType;
499
500 /** @copydoc RTTIPlainType::toMemory */
501 static void toMemory(const MapType& data, char* memory)
502 {
503 UINT32 size = sizeof(UINT32);
504 char* memoryStart = memory;
505 memory += sizeof(UINT32);
506
507 UINT32 numElements = (UINT32)data.size();
508 memcpy(memory, &numElements, sizeof(UINT32));
509 memory += sizeof(UINT32);
510 size += sizeof(UINT32);
511
512 for(const auto& item : data)
513 {
514 UINT32 keySize = rttiGetElemSize(item.first);
515 RTTIPlainType<Key>::toMemory(item.first, memory);
516
517 memory += keySize;
518 size += keySize;
519
520 UINT32 valueSize = rttiGetElemSize(item.second);
521 RTTIPlainType<Value>::toMemory(item.second, memory);
522
523 memory += valueSize;
524 size += valueSize;
525 }
526
527 memcpy(memoryStart, &size, sizeof(UINT32));
528 }
529
530 /** @copydoc RTTIPlainType::fromMemory */
531 static UINT32 fromMemory(MapType& data, char* memory)
532 {
533 UINT32 size = 0;
534 memcpy(&size, memory, sizeof(UINT32));
535 memory += sizeof(UINT32);
536
537 UINT32 numElements;
538 memcpy(&numElements, memory, sizeof(UINT32));
539 memory += sizeof(UINT32);
540
541 for (UINT32 i = 0; i < numElements; i++)
542 {
543 Key key;
544 UINT32 keySize = RTTIPlainType<Key>::fromMemory(key, memory);
545 memory += keySize;
546
547 Value value;
548 UINT32 valueSize = RTTIPlainType<Value>::fromMemory(value, memory);
549 memory += valueSize;
550
551 data[key] = value;
552 }
553
554 return size;
555 }
556
557 /** @copydoc RTTIPlainType::getDynamicSize */
558 static UINT32 getDynamicSize(const MapType& data)
559 {
560 UINT64 dataSize = sizeof(UINT32)* 2;
561
562 for(const auto& item : data)
563 {
564 dataSize += RTTIPlainType<Key>::getDynamicSize(item.first);
565 dataSize += RTTIPlainType<Value>::getDynamicSize(item.second);
566 }
567
568 assert(dataSize <= std::numeric_limits<UINT32>::max());
569
570 return (UINT32)dataSize;
571 }
572 };
573
574 /**
575 * RTTIPlainType for std::unordered_set.
576 *
577 * @see RTTIPlainType
578 */
579 template<class Key>
580 struct RTTIPlainType<std::unordered_set<Key, std::hash<Key>, std::equal_to<Key>, StdAlloc<Key>>>
581 {
582 enum { id = TID_UnorderedSet }; enum { hasDynamicSize = 1 };
583
584 typedef std::unordered_set<Key, std::hash<Key>, std::equal_to<Key>, StdAlloc<Key>> MapType;
585
586 /** @copydoc RTTIPlainType::toMemory */
587 static void toMemory(const MapType& data, char* memory)
588 {
589 UINT32 size = sizeof(UINT32);
590 char* memoryStart = memory;
591 memory += sizeof(UINT32);
592
593 UINT32 numElements = (UINT32)data.size();
594 memcpy(memory, &numElements, sizeof(UINT32));
595 memory += sizeof(UINT32);
596 size += sizeof(UINT32);
597
598 for(const auto& item : data)
599 {
600 UINT32 keySize = rttiGetElemSize(item);
601 RTTIPlainType<Key>::toMemory(item, memory);
602
603 memory += keySize;
604 size += keySize;
605 }
606
607 memcpy(memoryStart, &size, sizeof(UINT32));
608 }
609
610 /** @copydoc RTTIPlainType::fromMemory */
611 static UINT32 fromMemory(MapType& data, char* memory)
612 {
613 UINT32 size = 0;
614 memcpy(&size, memory, sizeof(UINT32));
615 memory += sizeof(UINT32);
616
617 UINT32 numElements;
618 memcpy(&numElements, memory, sizeof(UINT32));
619 memory += sizeof(UINT32);
620
621 for (UINT32 i = 0; i < numElements; i++)
622 {
623 Key key;
624 UINT32 keySize = RTTIPlainType<Key>::fromMemory(key, memory);
625 memory += keySize;
626
627 data.insert(key);
628 }
629
630 return size;
631 }
632
633 /** @copydoc RTTIPlainType::getDynamicSize */
634 static UINT32 getDynamicSize(const MapType& data)
635 {
636 UINT64 dataSize = sizeof(UINT32)* 2;
637
638 for(const auto& item : data)
639 {
640 dataSize += rttiGetElemSize(item);
641 }
642
643 assert(dataSize <= std::numeric_limits<UINT32>::max());
644
645 return (UINT32)dataSize;
646 }
647 };
648
649 /**
650 * RTTIPlainType for std::pair.
651 *
652 * @see RTTIPlainType
653 */
654 template<class A, class B> struct RTTIPlainType<std::pair<A, B>>
655 {
656 enum { id = TID_Pair }; enum { hasDynamicSize = 1 };
657
658 /** @copydoc RTTIPlainType::toMemory */
659 static void toMemory(const std::pair<A, B>& data, char* memory)
660 {
661 UINT32 size = sizeof(UINT32);
662 char* memoryStart = memory;
663 memory += sizeof(UINT32);
664
665 UINT32 firstSize = rttiGetElemSize(data.first);
666 RTTIPlainType<A>::toMemory(data.first, memory);
667
668 memory += firstSize;
669 size += firstSize;
670
671 UINT32 secondSize = rttiGetElemSize(data.second);
672 RTTIPlainType<B>::toMemory(data.second, memory);
673
674 memory += secondSize;
675 size += secondSize;
676
677 memcpy(memoryStart, &size, sizeof(UINT32));
678 }
679
680 /** @copydoc RTTIPlainType::fromMemory */
681 static UINT32 fromMemory(std::pair<A, B>& data, char* memory)
682 {
683 UINT32 size = 0;
684 memcpy(&size, memory, sizeof(UINT32));
685 memory += sizeof(UINT32);
686
687 UINT32 firstSize = RTTIPlainType<A>::fromMemory(data.first, memory);
688 memory += firstSize;
689
690 UINT32 secondSize = RTTIPlainType<B>::fromMemory(data.second, memory);
691 memory += secondSize;
692
693 return size;
694 }
695
696 /** @copydoc RTTIPlainType::getDynamicSize */
697 static UINT32 getDynamicSize(const std::pair<A, B>& data)
698 {
699 UINT64 dataSize = sizeof(UINT32);
700 dataSize += rttiGetElemSize(data.first);
701 dataSize += rttiGetElemSize(data.second);
702
703 assert(dataSize <= std::numeric_limits<UINT32>::max());
704
705 return (UINT32)dataSize;
706 }
707 };
708
709 /** @endcond */
710
711 /** @} */
712 /** @} */
713}
714