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 "Prerequisites/BsPrerequisitesUtil.h"
6#include "Serialization/BsSerializedObject.h"
7#include "Reflection/BsRTTIField.h"
8
9namespace bs
10{
11 /** @addtogroup Serialization
12 * @{
13 */
14
15 class IReflectable;
16 struct RTTIReflectableFieldBase;
17 struct RTTIReflectablePtrFieldBase;
18 struct SerializationContext;
19
20 /**
21 * Encodes/decodes all the fields of the provided object into/from a binary format. Fields are encoded using their
22 * unique IDs. Encoded data will remain compatible for decoding even if you modify the encoded class, as long as you
23 * assign new unique field IDs to added/modified fields.
24 *
25 * Like for any serializable class, fields are defined in RTTIType that each IReflectable class must be able to return.
26 *
27 * Any data the object or its children are pointing to will also be serialized (unless the pointer isn't registered in
28 * RTTIType). Upon decoding the pointer addresses will be set to proper values.
29 */
30 class BS_UTILITY_EXPORT BinarySerializer
31 {
32 public:
33 BinarySerializer();
34
35 /**
36 * Encodes all serializable fields provided by @p object into a binary format. Data is written in chunks. Whenever a
37 * chunk is filled a callback is triggered that gives the user opportunity to expand or empty the buffer (for
38 * example write the chunk to disk)
39 *
40 * @param[in] object Object to encode into binary format.
41 * @param[out] buffer Preallocated buffer where the data will be stored.
42 * @param[in] bufferLength Length of the buffer, in bytes.
43 * @param[out] bytesWritten Length of the data that was actually written to the buffer, in bytes.
44 * @param[in] flushBufferCallback This callback will get called whenever the buffer gets full (Be careful to
45 * check the provided @p bytesRead variable, as buffer might not be full
46 * completely). User must then either create a new buffer or empty the existing
47 * one, and then return it by the callback. If the returned buffer address is
48 * NULL, encoding is aborted.
49 * @param[in] shallow Determines how to handle referenced objects. If true then references will
50 * not be encoded and will be set to null. If false then references will be
51 * encoded as well and restored upon decoding.
52 * @param[in] context Optional object that will be passed along to all serialized objects through
53 * their serialization callbacks. Can be used for controlling serialization,
54 * maintaining state or sharing information between objects during
55 * serialization.
56 */
57 void encode(IReflectable* object, UINT8* buffer, UINT32 bufferLength, UINT32* bytesWritten,
58 std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback,
59 bool shallow = false, SerializationContext* context = nullptr);
60
61 /**
62 * Decodes an object from binary data.
63 *
64 * @param[in] data Binary data to decode.
65 * @param[in] dataLength Length of the data in bytes.
66 * @param[in] context Optional object that will be passed along to all serialized objects through
67 * their deserialization callbacks. Can be used for controlling deserialization,
68 * maintaining state or sharing information between objects during deserialization.
69 * @param[in] progress Optional callback that will occassionally trigger, reporting the current progress
70 * of the operation. The reported value is in range [0, 1].
71 *
72 * @note
73 * Child elements are guaranteed to be fully deserialized before their parents, except for fields marked with WeakRef flag.
74 */
75 SPtr<IReflectable> decode(const SPtr<DataStream>& data, UINT32 dataLength, SerializationContext* context = nullptr,
76 std::function<void(float)> progress = nullptr);
77 private:
78 /** Determines how many bytes need to be read before the progress report callback is triggered. */
79 static constexpr UINT32 REPORT_AFTER_BYTES = 32768;
80
81 struct ObjectMetaData
82 {
83 UINT32 objectMeta;
84 UINT32 typeId;
85 };
86
87 struct ObjectToEncode
88 {
89 ObjectToEncode(UINT32 _objectId, const SPtr<IReflectable>& _object)
90 :objectId(_objectId), object(_object)
91 { }
92
93 UINT32 objectId;
94 SPtr<IReflectable> object;
95 };
96
97 struct ObjectToDecode
98 {
99 ObjectToDecode(const SPtr<IReflectable>& _object, size_t offset = 0)
100 :object(_object), offset(offset)
101 { }
102
103 SPtr<IReflectable> object;
104 bool isDecoded = false;
105 bool decodeInProgress = false; // Used for error reporting circular references
106 size_t offset;
107 };
108
109 /** Encodes a single IReflectable object. */
110 UINT8* encodeEntry(IReflectable* object, UINT32 objectId, UINT8* buffer, UINT32& bufferLength, UINT32* bytesWritten,
111 std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback, bool shallow);
112
113 /** Decodes a single IReflectable object. */
114 bool decodeEntry(const SPtr<DataStream>& data, size_t dataLength, const SPtr<IReflectable>& output);
115
116 /** Helper method for encoding a complex object and copying its data to a buffer. */
117 UINT8* complexTypeToBuffer(IReflectable* object, UINT8* buffer, UINT32& bufferLength, UINT32* bytesWritten,
118 std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback, bool shallow);
119
120 /** Helper method for encoding a data block to a buffer. */
121 UINT8* dataBlockToBuffer(UINT8* data, UINT32 size, UINT8* buffer, UINT32& bufferLength, UINT32* bytesWritten,
122 std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback);
123
124 /** Finds an existing, or creates a unique unique identifier for the specified object. */
125 UINT32 findOrCreatePersistentId(IReflectable* object);
126
127 /**
128 * Finds or creates an id for the provided object and returns it. And it adds the object to a list of objects that
129 * need to be encoded, if it's not already there.
130 */
131 UINT32 registerObjectPtr(SPtr<IReflectable> object);
132
133 /** Encodes data required for representing a serialized field, into 4 bytes. */
134 static UINT32 encodeFieldMetaData(UINT16 id, UINT8 size, bool array,
135 SerializableFieldType type, bool hasDynamicSize, bool terminator);
136
137 /** Decode meta field that was encoded using encodeFieldMetaData().*/
138 static void decodeFieldMetaData(UINT32 encodedData, UINT16& id, UINT8& size, bool& array,
139 SerializableFieldType& type, bool& hasDynamicSize, bool& terminator);
140
141 /**
142 * Encodes data required for representing an object identifier, into 8 bytes.
143 *
144 * @param[in] objId Unique ID of the object instance.
145 * @param[in] objTypeId Unique ID of the object type.
146 * @param[in] isBaseClass true if this object is base class (that is, just a part of a larger object).
147 *
148 * @note Id can be a maximum of 30 bits, as two bits are reserved.
149 */
150 static ObjectMetaData encodeObjectMetaData(UINT32 objId, UINT32 objTypeId, bool isBaseClass);
151
152 /** Decode meta field that was encoded using encodeObjectMetaData. */
153 static void decodeObjectMetaData(ObjectMetaData encodedData, UINT32& objId, UINT32& objTypeId, bool& isBaseClass);
154
155 /** Returns true if the provided encoded meta data represents object meta data. */
156 static bool isObjectMetaData(UINT32 encodedData);
157
158 Map<UINT32, ObjectToDecode> mDecodeObjectMap;
159 Vector<ObjectToEncode> mObjectsToEncode;
160 UnorderedMap<void*, UINT32> mObjectAddrToId;
161 UINT32 mLastUsedObjectId = 1;
162 UINT32 mTotalBytesWritten;
163 UINT32 mTotalBytesRead = 0;
164 UINT32 mTotalBytesToRead = 0;
165 UINT32 mNextProgressReport = REPORT_AFTER_BYTES;
166 FrameAlloc* mAlloc = nullptr;
167
168 SerializationContext* mContext = nullptr;
169 std::function<void(float)> mReportProgress = nullptr;
170
171 static constexpr const int META_SIZE = 4; // Meta field size
172 static constexpr const int NUM_ELEM_FIELD_SIZE = 4; // Size of the field storing number of array elements
173 static constexpr const int COMPLEX_TYPE_FIELD_SIZE = 4; // Size of the field storing the size of a child complex type
174 static constexpr const int DATA_BLOCK_TYPE_FIELD_SIZE = 4;
175 };
176
177 // TODO - Potential improvements:
178 // - I will probably want to extract a generalized Serializer class so we can re-use the code in text or other serializers
179 // - Encode does a chunk-based encode so that we don't need to know the buffer size in advance, and don't have to use
180 // a lot of memory for the buffer. Consider doing something similar for decode.
181 // - Add a simple encode method that doesn't require a callback, instead it calls the callback internally and creates
182 // the buffer internally.
183
184 /** @} */
185}
186