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 | |
7 | namespace 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 | |