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#include "Serialization/BsBinaryCompare.h"
4#include "Reflection/BsRTTIType.h"
5#include "FileSystem/BsDataStream.h"
6
7namespace bs
8{
9 namespace impl
10 {
11 template<class T>
12 struct ScopeGuard
13 {
14 ~ScopeGuard()
15 {
16 callback();
17 }
18
19 T callback;
20 };
21
22 template<class T>
23 ScopeGuard<T> make_scope_guard(T callback) { return ScopeGuard<T>{callback}; }
24 }
25
26 BinaryCompare::BinaryCompare()
27 :mAlloc(&gFrameAlloc())
28 { }
29
30 bool BinaryCompare::run(IReflectable& a, IReflectable& b)
31 {
32 mAlloc->markFrame();
33 bool output = compare(a, b);
34 mObjectMap.clear();
35 mAlloc->clear();
36
37 return output;
38 }
39
40 bool BinaryCompare::compare(IReflectable& a, IReflectable& b)
41 {
42 RTTITypeBase* rtti = a.getRTTI();
43
44 if(rtti != b.getRTTI())
45 return false;
46
47 struct RTTIPair
48 {
49 RTTITypeBase* rttiA;
50 RTTITypeBase* rttiB;
51 };
52
53 FrameStack<RTTIPair> rttiInstances;
54 auto cleanup = impl::make_scope_guard([&]()
55 {
56 while (!rttiInstances.empty())
57 {
58 RTTIPair rttiPair = rttiInstances.top();
59 rttiPair.rttiA->onSerializationEnded(&a, mContext);
60 rttiPair.rttiB->onSerializationEnded(&b, mContext);
61 mAlloc->destruct(rttiPair.rttiA);
62 mAlloc->destruct(rttiPair.rttiB);
63
64 rttiInstances.pop();
65 }
66 });
67
68 // If an object has base classes, we need to iterate through all of them
69 do
70 {
71 RTTITypeBase* rttiInstanceA = rtti->_clone(*mAlloc);
72 RTTITypeBase* rttiInstanceB = rtti->_clone(*mAlloc);
73 rttiInstances.push({ rttiInstanceA, rttiInstanceB });
74
75 rttiInstanceA->onSerializationStarted(&a, mContext);
76 rttiInstanceB->onSerializationStarted(&b, mContext);
77
78 const UINT32 numFields = rtti->getNumFields();
79 for (UINT32 i = 0; i < numFields; i++)
80 {
81 RTTIField* curGenericField = rtti->getField(i);
82 if (curGenericField->mIsVectorType)
83 {
84 const UINT32 arrayNumElemsA = curGenericField->getArraySize(rttiInstanceA, &a);
85 const UINT32 arrayNumElemsB = curGenericField->getArraySize(rttiInstanceB, &b);
86
87 if(arrayNumElemsA != arrayNumElemsB)
88 return false;
89
90 switch (curGenericField->mType)
91 {
92 case SerializableFT_ReflectablePtr:
93 {
94 auto curField = static_cast<RTTIReflectablePtrFieldBase*>(curGenericField);
95
96 for (UINT32 arrIdx = 0; arrIdx < arrayNumElemsA; arrIdx++)
97 {
98 SPtr<IReflectable> childObjectA = curField->getArrayValue(rttiInstanceA, &a, arrIdx);
99 SPtr<IReflectable> childObjectB = curField->getArrayValue(rttiInstanceB, &b, arrIdx);
100
101 if (childObjectA != childObjectB)
102 {
103 if (childObjectA == nullptr || childObjectB == nullptr)
104 return false;
105
106 RTTITypeBase* childRtti = nullptr;
107 if (childObjectA->getRTTI() == childObjectB->getRTTI())
108 childRtti = childObjectA->getRTTI();
109
110 if (childRtti != nullptr)
111 {
112 ICompare& handler = childRtti->getCompareHandler();
113 if (!handler.run(*childObjectA, *childObjectB))
114 return false;
115 }
116 else
117 return false;
118 }
119 }
120
121 break;
122 }
123 case SerializableFT_Reflectable:
124 {
125 auto curField = static_cast<RTTIReflectableFieldBase*>(curGenericField);
126
127 for (UINT32 arrIdx = 0; arrIdx < arrayNumElemsA; arrIdx++)
128 {
129 IReflectable& childObjectA = curField->getArrayValue(rttiInstanceA, &a, arrIdx);
130 IReflectable& childObjectB = curField->getArrayValue(rttiInstanceB, &b, arrIdx);
131
132 RTTITypeBase* childRtti = nullptr;
133 if (childObjectA.getRTTI() == childObjectB.getRTTI())
134 childRtti = childObjectA.getRTTI();
135
136 if (childRtti != nullptr)
137 {
138 ICompare& handler = childRtti->getCompareHandler();
139 if (!handler.run(childObjectA, childObjectB))
140 return false;
141 }
142 else
143 return false;
144 }
145
146 break;
147 }
148 case SerializableFT_Plain:
149 {
150 auto curField = static_cast<RTTIPlainFieldBase*>(curGenericField);
151
152 for (UINT32 arrIdx = 0; arrIdx < arrayNumElemsA; arrIdx++)
153 {
154 UINT32 typeSizeA = 0;
155 UINT32 typeSizeB = 0;
156 if (curField->hasDynamicSize())
157 {
158 typeSizeA = curField->getArrayElemDynamicSize(rttiInstanceA, &a, arrIdx);
159 typeSizeB = curField->getArrayElemDynamicSize(rttiInstanceB, &b, arrIdx);
160 }
161 else
162 typeSizeA = typeSizeB = curField->getTypeSize();
163
164 if(typeSizeA != typeSizeB)
165 return false;
166
167 // Note: Ideally avoid doing copies here, and compare field values directly
168 auto dataA = bs_managed_stack_alloc(typeSizeA);
169 auto dataB = bs_managed_stack_alloc(typeSizeB);
170
171 curField->arrayElemToBuffer(rttiInstanceA, &a, arrIdx, dataA);
172 curField->arrayElemToBuffer(rttiInstanceB, &b, arrIdx, dataB);
173
174 if(memcmp(dataA, dataB, typeSizeA) != 0)
175 return false;
176 }
177
178 break;
179 }
180 default:
181 BS_EXCEPT(InternalErrorException,
182 "Error encoding data. Encountered a type I don't know how to encode. Type: " + toString(UINT32(curGenericField->mType)) +
183 ", Is array: " + toString(curGenericField->mIsVectorType));
184 }
185 }
186 else
187 {
188 switch (curGenericField->mType)
189 {
190 case SerializableFT_ReflectablePtr:
191 {
192 auto curField = static_cast<RTTIReflectablePtrFieldBase*>(curGenericField);
193
194 SPtr<IReflectable> childObjectA = curField->getValue(rttiInstanceA, &a);
195 SPtr<IReflectable> childObjectB = curField->getValue(rttiInstanceB, &b);
196
197 if (childObjectA != childObjectB)
198 {
199 if(childObjectA == nullptr || childObjectB == nullptr)
200 return false;
201
202 RTTITypeBase* childRtti = nullptr;
203 if (childObjectA->getRTTI() == childObjectB->getRTTI())
204 childRtti = childObjectA->getRTTI();
205
206 if (childRtti != nullptr)
207 {
208 ICompare& handler = childRtti->getCompareHandler();
209 if (!handler.run(*childObjectA, *childObjectB))
210 return false;
211 }
212 else
213 return false;
214 }
215
216 break;
217 }
218 case SerializableFT_Reflectable:
219 {
220 auto curField = static_cast<RTTIReflectableFieldBase*>(curGenericField);
221
222 IReflectable& childObjectA = curField->getValue(rttiInstanceA, &a);
223 IReflectable& childObjectB = curField->getValue(rttiInstanceB, &b);
224
225 RTTITypeBase* childRtti = nullptr;
226 if (childObjectA.getRTTI() == childObjectB.getRTTI())
227 childRtti = childObjectA.getRTTI();
228
229 if (childRtti != nullptr)
230 {
231 ICompare& handler = childRtti->getCompareHandler();
232 if(!handler.run(childObjectA, childObjectB))
233 return false;
234 }
235 else
236 return false;
237
238 break;
239 }
240 case SerializableFT_Plain:
241 {
242 auto curField = static_cast<RTTIPlainFieldBase*>(curGenericField);
243
244 UINT32 typeSizeA = 0;
245 UINT32 typeSizeB = 0;
246 if (curField->hasDynamicSize())
247 {
248 typeSizeA = curField->getDynamicSize(rttiInstanceA, &a);
249 typeSizeB = curField->getDynamicSize(rttiInstanceB, &b);
250 }
251 else
252 typeSizeA = typeSizeB = curField->getTypeSize();
253
254 if (typeSizeA != typeSizeB)
255 return false;
256
257 // Note: Ideally avoid doing copies here, and compare field values directly
258 auto dataA = bs_managed_stack_alloc(typeSizeA);
259 auto dataB = bs_managed_stack_alloc(typeSizeB);
260
261 curField->toBuffer(rttiInstanceA, &a, dataA);
262 curField->toBuffer(rttiInstanceB, &b, dataB);
263
264 if (memcmp(dataA, dataB, typeSizeA) != 0)
265 return false;
266
267 break;
268 }
269 case SerializableFT_DataBlock:
270 {
271 auto curField = static_cast<RTTIManagedDataBlockFieldBase*>(curGenericField);
272
273 UINT32 dataBlockSizeA = 0, dataBlockSizeB = 0;
274 SPtr<DataStream> blockStreamA = curField->getValue(rttiInstanceA, &a, dataBlockSizeA);
275 SPtr<DataStream> blockStreamB = curField->getValue(rttiInstanceB, &b, dataBlockSizeB);
276
277 if(dataBlockSizeA != dataBlockSizeB)
278 return false;
279
280 auto dataA = bs_managed_stack_alloc(dataBlockSizeA);
281 auto dataB = bs_managed_stack_alloc(dataBlockSizeB);
282
283 blockStreamA->read(dataA, dataBlockSizeA);
284 blockStreamB->read(dataB, dataBlockSizeB);
285
286 if(memcmp(dataA, dataB, dataBlockSizeA) != 0)
287 return false;
288
289 break;
290 }
291 default:
292 BS_EXCEPT(InternalErrorException,
293 "Error encoding data. Encountered a type I don't know how to encode. Type: " + toString(UINT32(curGenericField->mType)) +
294 ", Is array: " + toString(curGenericField->mIsVectorType));
295 }
296 }
297 }
298
299 rtti = rtti->getBaseClass();
300
301 } while (rtti != nullptr); // Repeat until we reach the top of the inheritance hierarchy
302
303 return true;
304 }
305}
306