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/BsBinaryDiff.h" |
4 | #include "Serialization/BsSerializedObject.h" |
5 | #include "Serialization/BsBinarySerializer.h" |
6 | #include "Serialization/BsBinaryCloner.h" |
7 | #include "Reflection/BsRTTIType.h" |
8 | #include "FileSystem/BsDataStream.h" |
9 | |
10 | namespace bs |
11 | { |
12 | SPtr<SerializedObject> IDiff::generateDiff(const SPtr<SerializedObject>& orgObj, |
13 | const SPtr<SerializedObject>& newObj) |
14 | { |
15 | ObjectMap objectMap; |
16 | return generateDiff(orgObj, newObj, objectMap); |
17 | } |
18 | |
19 | SPtr<SerializedInstance> IDiff::generateDiff(RTTITypeBase* rtti, UINT32 fieldType, const SPtr<SerializedInstance>& orgData, |
20 | const SPtr<SerializedInstance>& newData, ObjectMap& objectMap) |
21 | { |
22 | SPtr<SerializedInstance> modification; |
23 | switch (fieldType) |
24 | { |
25 | case SerializableFT_ReflectablePtr: |
26 | case SerializableFT_Reflectable: |
27 | { |
28 | SPtr<SerializedObject> orgObjData = std::static_pointer_cast<SerializedObject>(orgData); |
29 | SPtr<SerializedObject> newObjData = std::static_pointer_cast<SerializedObject>(newData); |
30 | |
31 | auto iterFind = objectMap.find(newObjData); |
32 | if (iterFind != objectMap.end()) |
33 | modification = iterFind->second; |
34 | else |
35 | { |
36 | RTTITypeBase* childRtti = nullptr; |
37 | if (orgObjData->getRootTypeId() == newObjData->getRootTypeId()) |
38 | childRtti = IReflectable::_getRTTIfromTypeId(newObjData->getRootTypeId()); |
39 | |
40 | SPtr<SerializedObject> objectDiff; |
41 | if (childRtti != nullptr) |
42 | { |
43 | IDiff& handler = childRtti->getDiffHandler(); |
44 | objectDiff = handler.generateDiff(orgObjData, newObjData, objectMap); |
45 | } |
46 | |
47 | if (objectDiff != nullptr) |
48 | objectMap[newObjData] = objectDiff; |
49 | |
50 | modification = objectDiff; |
51 | } |
52 | } |
53 | break; |
54 | case SerializableFT_Plain: |
55 | { |
56 | SPtr<SerializedField> orgFieldData = std::static_pointer_cast<SerializedField>(orgData); |
57 | SPtr<SerializedField> newFieldData = std::static_pointer_cast<SerializedField>(newData); |
58 | |
59 | bool isModified = orgFieldData->size != newFieldData->size; |
60 | if (!isModified) |
61 | isModified = memcmp(orgFieldData->value, newFieldData->value, newFieldData->size) != 0; |
62 | |
63 | if (isModified) |
64 | modification = newFieldData->clone(); |
65 | } |
66 | break; |
67 | case SerializableFT_DataBlock: |
68 | { |
69 | SPtr<SerializedDataBlock> orgFieldData = std::static_pointer_cast<SerializedDataBlock>(orgData); |
70 | SPtr<SerializedDataBlock> newFieldData = std::static_pointer_cast<SerializedDataBlock>(newData); |
71 | |
72 | bool isModified = orgFieldData->size != newFieldData->size; |
73 | if (!isModified) |
74 | { |
75 | UINT8* orgStreamData = nullptr; |
76 | if(orgFieldData->stream->isFile()) |
77 | { |
78 | orgStreamData = (UINT8*)bs_stack_alloc(orgFieldData->size); |
79 | orgFieldData->stream->seek(orgFieldData->offset); |
80 | orgFieldData->stream->read(orgStreamData, orgFieldData->size); |
81 | } |
82 | else |
83 | { |
84 | SPtr<MemoryDataStream> orgMemStream = std::static_pointer_cast<MemoryDataStream>(orgFieldData->stream); |
85 | orgStreamData = orgMemStream->getCurrentPtr(); |
86 | } |
87 | |
88 | UINT8* newStreamData = nullptr; |
89 | if (newFieldData->stream->isFile()) |
90 | { |
91 | newStreamData = (UINT8*)bs_stack_alloc(newFieldData->size); |
92 | newFieldData->stream->seek(newFieldData->offset); |
93 | newFieldData->stream->read(newStreamData, newFieldData->size); |
94 | } |
95 | else |
96 | { |
97 | SPtr<MemoryDataStream> newMemStream = std::static_pointer_cast<MemoryDataStream>(newFieldData->stream); |
98 | newStreamData = newMemStream->getCurrentPtr(); |
99 | } |
100 | |
101 | isModified = memcmp(orgStreamData, newStreamData, newFieldData->size) != 0; |
102 | |
103 | if (newFieldData->stream->isFile()) |
104 | bs_stack_free(newStreamData); |
105 | |
106 | if (orgFieldData->stream->isFile()) |
107 | bs_stack_free(orgStreamData); |
108 | } |
109 | |
110 | if (isModified) |
111 | modification = newFieldData->clone(); |
112 | } |
113 | break; |
114 | } |
115 | |
116 | return modification; |
117 | } |
118 | |
119 | void IDiff::applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, |
120 | SerializationContext* context) |
121 | { |
122 | FrameAlloc& alloc = gFrameAlloc(); |
123 | alloc.markFrame(); |
124 | |
125 | FrameVector<DiffCommand> commands; |
126 | |
127 | DiffObjectMap objectMap; |
128 | applyDiff(object, diff, alloc, objectMap, commands, context); |
129 | |
130 | IReflectable* destObject = nullptr; |
131 | RTTITypeBase* rttiInstance = nullptr; |
132 | |
133 | Stack<IReflectable*> objectStack; |
134 | Vector<std::pair<RTTITypeBase*, IReflectable*>> rttiInstances; |
135 | |
136 | for (auto& command : commands) |
137 | { |
138 | bool isArray = (command.type & Diff_ArrayFlag) != 0; |
139 | DiffCommandType type = (DiffCommandType)(command.type & 0xF); |
140 | |
141 | switch (type) |
142 | { |
143 | case Diff_ArraySize: |
144 | command.field->setArraySize(rttiInstance, destObject, command.arraySize); |
145 | break; |
146 | case Diff_ObjectStart: |
147 | { |
148 | destObject = command.object.get(); |
149 | objectStack.push(destObject); |
150 | |
151 | FrameStack<RTTITypeBase*> rttiTypes; |
152 | RTTITypeBase* curRtti = destObject->getRTTI(); |
153 | while (curRtti != nullptr) |
154 | { |
155 | rttiTypes.push(curRtti); |
156 | curRtti = curRtti->getBaseClass(); |
157 | } |
158 | |
159 | // Call base class first, followed by derived classes |
160 | while(!rttiTypes.empty()) |
161 | { |
162 | RTTITypeBase* curRtti = rttiTypes.top(); |
163 | RTTITypeBase* rttiInstance = curRtti->_clone(alloc); |
164 | |
165 | rttiInstances.push_back(std::make_pair(rttiInstance, destObject)); |
166 | rttiInstance->onDeserializationStarted(destObject, context); |
167 | |
168 | rttiTypes.pop(); |
169 | } |
170 | } |
171 | break; |
172 | case Diff_SubObjectStart: |
173 | { |
174 | // Find the instance |
175 | rttiInstance = nullptr; |
176 | for(auto iter = rttiInstances.rbegin(); iter != rttiInstances.rend(); ++iter) |
177 | { |
178 | if(iter->second != destObject) |
179 | break; |
180 | |
181 | if(iter->first->getRTTIId() == command.rttiType->getRTTIId()) |
182 | rttiInstance = iter->first; |
183 | } |
184 | |
185 | assert(rttiInstance); |
186 | } |
187 | break; |
188 | case Diff_ObjectEnd: |
189 | { |
190 | while (!rttiInstances.empty()) |
191 | { |
192 | if(rttiInstances.back().second != destObject) |
193 | break; |
194 | |
195 | RTTITypeBase* rttiInstance = rttiInstances.back().first; |
196 | |
197 | rttiInstance->onDeserializationEnded(destObject, context); |
198 | alloc.destruct(rttiInstance); |
199 | |
200 | rttiInstances.erase(rttiInstances.end() - 1); |
201 | } |
202 | |
203 | objectStack.pop(); |
204 | |
205 | if (!objectStack.empty()) |
206 | destObject = objectStack.top(); |
207 | else |
208 | destObject = nullptr; |
209 | } |
210 | break; |
211 | default: |
212 | break; |
213 | } |
214 | |
215 | if (isArray) |
216 | { |
217 | switch (type) |
218 | { |
219 | case Diff_ReflectablePtr: |
220 | { |
221 | RTTIReflectablePtrFieldBase* field = static_cast<RTTIReflectablePtrFieldBase*>(command.field); |
222 | field->setArrayValue(rttiInstance, destObject, command.arrayIdx, command.object); |
223 | } |
224 | break; |
225 | case Diff_Reflectable: |
226 | { |
227 | RTTIReflectableFieldBase* field = static_cast<RTTIReflectableFieldBase*>(command.field); |
228 | field->setArrayValue(rttiInstance, destObject, command.arrayIdx, *command.object); |
229 | } |
230 | break; |
231 | case Diff_Plain: |
232 | { |
233 | RTTIPlainFieldBase* field = static_cast<RTTIPlainFieldBase*>(command.field); |
234 | field->arrayElemFromBuffer(rttiInstance, destObject, command.arrayIdx, command.value); |
235 | } |
236 | break; |
237 | default: |
238 | break; |
239 | } |
240 | } |
241 | else |
242 | { |
243 | switch (type) |
244 | { |
245 | case Diff_ReflectablePtr: |
246 | { |
247 | RTTIReflectablePtrFieldBase* field = static_cast<RTTIReflectablePtrFieldBase*>(command.field); |
248 | field->setValue(rttiInstance, destObject, command.object); |
249 | } |
250 | break; |
251 | case Diff_Reflectable: |
252 | { |
253 | RTTIReflectableFieldBase* field = static_cast<RTTIReflectableFieldBase*>(command.field); |
254 | field->setValue(rttiInstance, destObject, *command.object); |
255 | } |
256 | break; |
257 | case Diff_Plain: |
258 | { |
259 | RTTIPlainFieldBase* field = static_cast<RTTIPlainFieldBase*>(command.field); |
260 | field->fromBuffer(rttiInstance, destObject, command.value); |
261 | } |
262 | break; |
263 | case Diff_DataBlock: |
264 | { |
265 | RTTIManagedDataBlockFieldBase* field = static_cast<RTTIManagedDataBlockFieldBase*>(command.field); |
266 | field->setValue(rttiInstance, destObject, command.streamValue, command.size); |
267 | } |
268 | break; |
269 | default: |
270 | break; |
271 | } |
272 | } |
273 | } |
274 | |
275 | alloc.clear(); |
276 | } |
277 | |
278 | void IDiff::applyDiff(RTTITypeBase* rtti, const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, |
279 | FrameAlloc& alloc, DiffObjectMap& objectMap, FrameVector<DiffCommand>& diffCommands, SerializationContext* context) |
280 | { |
281 | IDiff& diffHandler = rtti->getDiffHandler(); |
282 | diffHandler.applyDiff(object, diff, alloc, objectMap, diffCommands, context); |
283 | } |
284 | |
285 | bool BinaryDiff::generateDiff(const RTTIField* field, RTTITypeBase* rtti, const SPtr<SerializedInstance>& oldData, |
286 | const SPtr<SerializedInstance>& newData, ObjectMap& objectMap, |
287 | SPtr<SerializedInstance>& modification) |
288 | { |
289 | if (newData != nullptr && oldData != nullptr) |
290 | { |
291 | modification = IDiff::generateDiff(rtti, field->mType, oldData, newData, objectMap); |
292 | return modification != nullptr; |
293 | } |
294 | else if (newData == nullptr) |
295 | { |
296 | switch (field->mType) |
297 | { |
298 | case SerializableFT_Plain: |
299 | modification = bs_shared_ptr_new<SerializedField>(); |
300 | break; |
301 | case SerializableFT_DataBlock: |
302 | modification = bs_shared_ptr_new<SerializedDataBlock>(); |
303 | break; |
304 | default: |
305 | break; |
306 | } |
307 | |
308 | return true; |
309 | } |
310 | else if (oldData == nullptr) |
311 | { |
312 | modification = newData->clone(); |
313 | return modification != nullptr; |
314 | } |
315 | |
316 | return false; |
317 | } |
318 | |
319 | SPtr<SerializedObject> BinaryDiff::generateDiff(const SPtr<SerializedObject>& orgObj, |
320 | const SPtr<SerializedObject>& newObj, ObjectMap& objectMap) |
321 | { |
322 | SPtr<SerializedObject> output; |
323 | for (auto& subObject : newObj->subObjects) |
324 | { |
325 | RTTITypeBase* rtti = IReflectable::_getRTTIfromTypeId(subObject.typeId); |
326 | if (rtti == nullptr) |
327 | continue; |
328 | |
329 | SerializedSubObject* orgSubObject = nullptr; |
330 | for (auto& curSubObject : orgObj->subObjects) |
331 | { |
332 | if (curSubObject.typeId == subObject.typeId) |
333 | { |
334 | orgSubObject = &curSubObject; |
335 | break; |
336 | } |
337 | } |
338 | |
339 | SerializedSubObject* diffSubObject = nullptr; |
340 | for (auto& newEntry : subObject.entries) |
341 | { |
342 | RTTIField* genericField = rtti->findField(newEntry.first); |
343 | if (genericField == nullptr) |
344 | continue; |
345 | |
346 | SPtr<SerializedInstance> newEntryData = newEntry.second.serialized; |
347 | SPtr<SerializedInstance> orgEntryData; |
348 | |
349 | if (orgSubObject != nullptr) |
350 | { |
351 | auto orgEntryFind = orgSubObject->entries.find(newEntry.first); |
352 | if (orgEntryFind != orgSubObject->entries.end()) |
353 | orgEntryData = orgEntryFind->second.serialized; |
354 | } |
355 | |
356 | SPtr<SerializedInstance> modification; |
357 | bool hasModification = false; |
358 | if (genericField->isArray()) |
359 | { |
360 | SPtr<SerializedArray> orgArrayData = std::static_pointer_cast<SerializedArray>(orgEntryData); |
361 | SPtr<SerializedArray> newArrayData = std::static_pointer_cast<SerializedArray>(newEntryData); |
362 | |
363 | SPtr<SerializedArray> serializedArray; |
364 | |
365 | if (newEntryData != nullptr && orgEntryData != nullptr) |
366 | { |
367 | // Check for new or different fields |
368 | for (auto& arrayEntryPair : newArrayData->entries) |
369 | { |
370 | SPtr<SerializedInstance> orgArrayEntryData; |
371 | |
372 | auto iterFind = orgArrayData->entries.find(arrayEntryPair.first); |
373 | if (iterFind != orgArrayData->entries.end()) |
374 | orgArrayEntryData = iterFind->second.serialized; |
375 | |
376 | SPtr<SerializedInstance> arrayModification; |
377 | bool hasArrayModification = generateDiff(genericField, rtti, orgArrayEntryData, |
378 | arrayEntryPair.second.serialized, objectMap, arrayModification); |
379 | |
380 | if (hasArrayModification) |
381 | { |
382 | if (serializedArray == nullptr) |
383 | { |
384 | serializedArray = bs_shared_ptr_new<SerializedArray>(); |
385 | serializedArray->numElements = newArrayData->numElements; |
386 | } |
387 | |
388 | SerializedArrayEntry arrayEntry; |
389 | arrayEntry.index = arrayEntryPair.first; |
390 | arrayEntry.serialized = arrayModification; |
391 | serializedArray->entries[arrayEntryPair.first] = arrayEntry; |
392 | } |
393 | } |
394 | |
395 | if(newArrayData->numElements != orgArrayData->numElements) |
396 | { |
397 | if (serializedArray == nullptr) |
398 | { |
399 | serializedArray = bs_shared_ptr_new<SerializedArray>(); |
400 | serializedArray->numElements = newArrayData->numElements; |
401 | } |
402 | } |
403 | } |
404 | else if (newEntryData == nullptr) |
405 | { |
406 | serializedArray = bs_shared_ptr_new<SerializedArray>(); |
407 | } |
408 | else if (orgEntryData == nullptr) |
409 | { |
410 | serializedArray = std::static_pointer_cast<SerializedArray>(newArrayData->clone()); |
411 | } |
412 | |
413 | modification = serializedArray; |
414 | hasModification = modification != nullptr; |
415 | } |
416 | else |
417 | hasModification = generateDiff(genericField, rtti, orgEntryData, newEntryData, objectMap, modification); |
418 | |
419 | if (hasModification) |
420 | { |
421 | if (output == nullptr) |
422 | output = bs_shared_ptr_new<SerializedObject>(); |
423 | |
424 | if (diffSubObject == nullptr) |
425 | { |
426 | output->subObjects.push_back(SerializedSubObject()); |
427 | diffSubObject = &output->subObjects.back(); |
428 | diffSubObject->typeId = rtti->getRTTIId(); |
429 | } |
430 | |
431 | SerializedEntry modificationEntry; |
432 | modificationEntry.fieldId = genericField->mUniqueId; |
433 | modificationEntry.serialized = modification; |
434 | diffSubObject->entries[genericField->mUniqueId] = modificationEntry; |
435 | } |
436 | } |
437 | } |
438 | |
439 | return output; |
440 | } |
441 | |
442 | void BinaryDiff::applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, |
443 | FrameAlloc& alloc, DiffObjectMap& objectMap, FrameVector<DiffCommand>& diffCommands, SerializationContext* context) |
444 | { |
445 | if (object == nullptr || diff == nullptr || object->getTypeId() != diff->getRootTypeId()) |
446 | return; |
447 | |
448 | // Generate a list of commands per sub-object |
449 | FrameVector<FrameVector<DiffCommand>> commandsPerSubObj; |
450 | |
451 | Stack<RTTITypeBase*> rttiInstances; |
452 | for (auto& subObject : diff->subObjects) |
453 | { |
454 | RTTITypeBase* rtti = IReflectable::_getRTTIfromTypeId(subObject.typeId); |
455 | if (rtti == nullptr) |
456 | continue; |
457 | |
458 | if (!object->isDerivedFrom(rtti)) |
459 | continue; |
460 | |
461 | RTTITypeBase* rttiInstance = rtti->_clone(alloc); |
462 | rttiInstance->onSerializationStarted(object.get(), nullptr); |
463 | rttiInstances.push(rttiInstance); |
464 | |
465 | FrameVector<DiffCommand> commands; |
466 | |
467 | DiffCommand subObjStartCommand; |
468 | subObjStartCommand.rttiType = rtti; |
469 | subObjStartCommand.field = nullptr; |
470 | subObjStartCommand.type = Diff_SubObjectStart; |
471 | |
472 | commands.push_back(subObjStartCommand); |
473 | |
474 | for (auto& diffEntry : subObject.entries) |
475 | { |
476 | RTTIField* genericField = rtti->findField(diffEntry.first); |
477 | if (genericField == nullptr) |
478 | continue; |
479 | |
480 | SPtr<SerializedInstance> diffData = diffEntry.second.serialized; |
481 | |
482 | if (genericField->isArray()) |
483 | { |
484 | SPtr<SerializedArray> diffArray = std::static_pointer_cast<SerializedArray>(diffData); |
485 | |
486 | UINT32 numArrayElements = (UINT32)diffArray->numElements; |
487 | |
488 | DiffCommand arraySizeCommand; |
489 | arraySizeCommand.field = genericField; |
490 | arraySizeCommand.type = Diff_ArraySize | Diff_ArrayFlag; |
491 | arraySizeCommand.arraySize = numArrayElements; |
492 | |
493 | commands.push_back(arraySizeCommand); |
494 | |
495 | switch (genericField->mType) |
496 | { |
497 | case SerializableFT_ReflectablePtr: |
498 | { |
499 | RTTIReflectablePtrFieldBase* field = static_cast<RTTIReflectablePtrFieldBase*>(genericField); |
500 | |
501 | UINT32 orgArraySize = genericField->getArraySize(rttiInstance, object.get()); |
502 | for (auto& arrayElem : diffArray->entries) |
503 | { |
504 | SPtr<SerializedObject> arrayElemData = std::static_pointer_cast<SerializedObject>(arrayElem.second.serialized); |
505 | |
506 | DiffCommand command; |
507 | command.field = genericField; |
508 | command.type = Diff_ReflectablePtr | Diff_ArrayFlag; |
509 | command.arrayIdx = arrayElem.first; |
510 | |
511 | if (arrayElemData == nullptr) |
512 | { |
513 | command.object = nullptr; |
514 | commands.push_back(command); |
515 | } |
516 | else |
517 | { |
518 | bool needsNewObject = arrayElem.first >= orgArraySize; |
519 | |
520 | if (!needsNewObject) |
521 | { |
522 | SPtr<IReflectable> childObj = field->getArrayValue(rttiInstance, object.get(), arrayElem.first); |
523 | if (childObj != nullptr) |
524 | { |
525 | IDiff::applyDiff(childObj->getRTTI(), childObj, arrayElemData, alloc, objectMap, |
526 | commands, context); |
527 | command.object = childObj; |
528 | } |
529 | else |
530 | needsNewObject = true; |
531 | } |
532 | |
533 | if (needsNewObject) |
534 | { |
535 | RTTITypeBase* childRtti = IReflectable::_getRTTIfromTypeId(arrayElemData->getRootTypeId()); |
536 | if (childRtti != nullptr) |
537 | { |
538 | auto findObj = objectMap.find(arrayElemData); |
539 | if (findObj == objectMap.end()) |
540 | { |
541 | SPtr<IReflectable> newObject = childRtti->newRTTIObject(); |
542 | findObj = objectMap.insert(std::make_pair(arrayElemData, newObject)).first; |
543 | } |
544 | |
545 | IDiff::applyDiff(childRtti, findObj->second, arrayElemData, alloc, objectMap, |
546 | commands, context); |
547 | command.object = findObj->second; |
548 | commands.push_back(command); |
549 | } |
550 | else |
551 | { |
552 | command.object = nullptr; |
553 | commands.push_back(command); |
554 | } |
555 | } |
556 | } |
557 | } |
558 | } |
559 | break; |
560 | case SerializableFT_Reflectable: |
561 | { |
562 | RTTIReflectableFieldBase* field = static_cast<RTTIReflectableFieldBase*>(genericField); |
563 | |
564 | UINT32 orgArraySize = genericField->getArraySize(rttiInstance, object.get()); |
565 | |
566 | Vector<SPtr<IReflectable>> newArrayElements(numArrayElements); |
567 | UINT32 minArrayLength = std::min(orgArraySize, numArrayElements); |
568 | for (UINT32 i = 0; i < minArrayLength; i++) |
569 | { |
570 | IReflectable& childObj = field->getArrayValue(rttiInstance, object.get(), i); |
571 | newArrayElements[i] = BinaryCloner::clone(&childObj, true); |
572 | } |
573 | |
574 | for (auto& arrayElem : diffArray->entries) |
575 | { |
576 | SPtr<SerializedObject> arrayElemData = std::static_pointer_cast<SerializedObject>(arrayElem.second.serialized); |
577 | |
578 | if (arrayElem.first < orgArraySize) |
579 | { |
580 | SPtr<IReflectable> childObj = newArrayElements[arrayElem.first]; |
581 | IDiff::applyDiff(childObj->getRTTI(), childObj, arrayElemData, alloc, objectMap, |
582 | commands, context); |
583 | } |
584 | else |
585 | { |
586 | RTTITypeBase* childRtti = IReflectable::_getRTTIfromTypeId(arrayElemData->getRootTypeId()); |
587 | if (childRtti != nullptr) |
588 | { |
589 | SPtr<IReflectable> newObject = childRtti->newRTTIObject(); |
590 | IDiff::applyDiff(childRtti, newObject, arrayElemData, alloc, objectMap, commands, |
591 | context); |
592 | |
593 | newArrayElements[arrayElem.first] = newObject; |
594 | } |
595 | } |
596 | } |
597 | |
598 | for (UINT32 i = 0; i < numArrayElements; i++) |
599 | { |
600 | DiffCommand command; |
601 | command.field = genericField; |
602 | command.type = Diff_Reflectable | Diff_ArrayFlag; |
603 | command.arrayIdx = i; |
604 | command.object = newArrayElements[i]; |
605 | |
606 | commands.push_back(command); |
607 | } |
608 | } |
609 | break; |
610 | case SerializableFT_Plain: |
611 | { |
612 | for (auto& arrayElem : diffArray->entries) |
613 | { |
614 | SPtr<SerializedField> fieldData = std::static_pointer_cast<SerializedField>(arrayElem.second.serialized); |
615 | if (fieldData != nullptr) |
616 | { |
617 | DiffCommand command; |
618 | command.field = genericField; |
619 | command.type = Diff_Plain | Diff_ArrayFlag; |
620 | command.value = fieldData->value; |
621 | command.size = fieldData->size; |
622 | command.arrayIdx = arrayElem.first; |
623 | |
624 | commands.push_back(command); |
625 | } |
626 | } |
627 | } |
628 | break; |
629 | default: |
630 | break; |
631 | } |
632 | } |
633 | else |
634 | { |
635 | switch (genericField->mType) |
636 | { |
637 | case SerializableFT_ReflectablePtr: |
638 | { |
639 | RTTIReflectablePtrFieldBase* field = static_cast<RTTIReflectablePtrFieldBase*>(genericField); |
640 | SPtr<SerializedObject> fieldObjectData = std::static_pointer_cast<SerializedObject>(diffData); |
641 | |
642 | DiffCommand command; |
643 | command.field = genericField; |
644 | command.type = Diff_ReflectablePtr; |
645 | |
646 | if (fieldObjectData == nullptr) |
647 | command.object = nullptr; |
648 | else |
649 | { |
650 | SPtr<IReflectable> childObj = field->getValue(rttiInstance, object.get()); |
651 | if (childObj == nullptr) |
652 | { |
653 | RTTITypeBase* childRtti = IReflectable::_getRTTIfromTypeId(fieldObjectData->getRootTypeId()); |
654 | if (childRtti != nullptr) |
655 | { |
656 | auto findObj = objectMap.find(fieldObjectData); |
657 | if (findObj == objectMap.end()) |
658 | { |
659 | SPtr<IReflectable> newObject = childRtti->newRTTIObject(); |
660 | findObj = objectMap.insert(std::make_pair(fieldObjectData, newObject)).first; |
661 | } |
662 | |
663 | IDiff::applyDiff(childRtti, findObj->second, fieldObjectData, alloc, objectMap, |
664 | commands, context); |
665 | command.object = findObj->second; |
666 | } |
667 | else |
668 | { |
669 | command.object = nullptr; |
670 | } |
671 | } |
672 | else |
673 | { |
674 | IDiff::applyDiff(childObj->getRTTI(), childObj, fieldObjectData, alloc, objectMap, |
675 | commands, context); |
676 | command.object = childObj; |
677 | } |
678 | } |
679 | |
680 | commands.push_back(command); |
681 | } |
682 | break; |
683 | case SerializableFT_Reflectable: |
684 | { |
685 | RTTIReflectableFieldBase* field = static_cast<RTTIReflectableFieldBase*>(genericField); |
686 | SPtr<SerializedObject> fieldObjectData = std::static_pointer_cast<SerializedObject>(diffData); |
687 | |
688 | IReflectable& childObj = field->getValue(rttiInstance, object.get()); |
689 | SPtr<IReflectable> clonedObj = BinaryCloner::clone(&childObj, true); |
690 | |
691 | IDiff::applyDiff(clonedObj->getRTTI(), clonedObj, fieldObjectData, alloc, objectMap, commands, |
692 | context); |
693 | |
694 | DiffCommand command; |
695 | command.field = genericField; |
696 | command.type = Diff_Reflectable; |
697 | command.object = clonedObj; |
698 | |
699 | commands.push_back(command); |
700 | } |
701 | break; |
702 | case SerializableFT_Plain: |
703 | { |
704 | SPtr<SerializedField> diffFieldData = std::static_pointer_cast<SerializedField>(diffData); |
705 | |
706 | if (diffFieldData->size > 0) |
707 | { |
708 | DiffCommand command; |
709 | command.field = genericField; |
710 | command.type = Diff_Plain; |
711 | command.value = diffFieldData->value; |
712 | command.size = diffFieldData->size; |
713 | |
714 | commands.push_back(command); |
715 | } |
716 | } |
717 | break; |
718 | case SerializableFT_DataBlock: |
719 | { |
720 | SPtr<SerializedDataBlock> diffFieldData = std::static_pointer_cast<SerializedDataBlock>(diffData); |
721 | |
722 | DiffCommand command; |
723 | command.field = genericField; |
724 | command.type = Diff_DataBlock; |
725 | command.streamValue = diffFieldData->stream; |
726 | command.value = nullptr; |
727 | command.size = diffFieldData->size; |
728 | |
729 | commands.push_back(command); |
730 | } |
731 | break; |
732 | } |
733 | } |
734 | } |
735 | |
736 | commandsPerSubObj.emplace_back(std::move(commands)); |
737 | } |
738 | |
739 | DiffCommand objStartCommand; |
740 | objStartCommand.field = nullptr; |
741 | objStartCommand.type = Diff_ObjectStart; |
742 | objStartCommand.object = object; |
743 | |
744 | diffCommands.push_back(objStartCommand); |
745 | |
746 | // Go in reverse because when deserializing we want to deserialize base first, and then derived types |
747 | for(auto iter = commandsPerSubObj.rbegin(); iter != commandsPerSubObj.rend(); ++iter) |
748 | diffCommands.insert(diffCommands.end(), iter->begin(), iter->end()); |
749 | |
750 | DiffCommand objEndCommand; |
751 | objEndCommand.field = nullptr; |
752 | objEndCommand.type = Diff_ObjectEnd; |
753 | objEndCommand.object = object; |
754 | |
755 | diffCommands.push_back(objEndCommand); |
756 | |
757 | while (!rttiInstances.empty()) |
758 | { |
759 | RTTITypeBase* rttiInstance = rttiInstances.top(); |
760 | rttiInstance->onSerializationEnded(object.get(), nullptr); |
761 | alloc.destruct(rttiInstance); |
762 | |
763 | rttiInstances.pop(); |
764 | } |
765 | } |
766 | } |