#include "BsBinaryDiff.h" #include "BsSerializedObject.h" #include "BsBinarySerializer.h" #include "BsBinaryCloner.h" #include "BsRTTIType.h" namespace BansheeEngine { SPtr IDiff::generateDiff(const SPtr& orgObj, const SPtr& newObj) { ObjectMap objectMap; return generateDiff(orgObj, newObj, objectMap); } SPtr IDiff::generateDiff(RTTITypeBase* rtti, UINT32 fieldType, const SPtr& orgData, const SPtr& newData, ObjectMap& objectMap) { SPtr modification; switch (fieldType) { case SerializableFT_ReflectablePtr: case SerializableFT_Reflectable: { SPtr orgObjData = std::static_pointer_cast(orgData); SPtr newObjData = std::static_pointer_cast(newData); auto iterFind = objectMap.find(newObjData); if (iterFind != objectMap.end()) modification = iterFind->second; else { RTTITypeBase* childRtti = nullptr; if (orgObjData->getRootTypeId() == newObjData->getRootTypeId()) childRtti = IReflectable::_getRTTIfromTypeId(newObjData->getRootTypeId()); SPtr objectDiff; if (childRtti != nullptr) { IDiff& handler = childRtti->getDiffHandler(); objectDiff = handler.generateDiff(orgObjData, newObjData, objectMap); } if (objectDiff != nullptr) objectMap[newObjData] = objectDiff; modification = objectDiff; } } break; case SerializableFT_Plain: case SerializableFT_DataBlock: { SPtr orgFieldData = std::static_pointer_cast(orgData); SPtr newFieldData = std::static_pointer_cast(newData); bool isModified = orgFieldData->size != newFieldData->size; if (!isModified) isModified = memcmp(orgFieldData->value, newFieldData->value, newFieldData->size) != 0; if (isModified) modification = newFieldData->clone(); } break; } return modification; } void IDiff::applyDiff(const SPtr& object, const SPtr& diff) { Vector commands; DiffObjectMap objectMap; applyDiff(object, diff, objectMap, commands); IReflectable* destObject = nullptr; Stack objectStack; for (auto& command : commands) { bool isArray = (command.type & Diff_ArrayFlag) != 0; DiffCommandType type = (DiffCommandType)(command.type & 0xF); switch (type) { case Diff_ArraySize: command.field->setArraySize(destObject, command.arraySize); break; case Diff_ObjectStart: { destObject = command.object.get(); objectStack.push(destObject); RTTITypeBase* curRtti = destObject->getRTTI(); while (curRtti != nullptr) { curRtti->onDeserializationStarted(destObject); curRtti = curRtti->getBaseClass(); } } break; case Diff_ObjectEnd: { Stack rttiTypes; RTTITypeBase* curRtti = destObject->getRTTI(); while (curRtti != nullptr) { rttiTypes.push(curRtti); curRtti = curRtti->getBaseClass(); } while (!rttiTypes.empty()) { rttiTypes.top()->onDeserializationEnded(destObject); rttiTypes.pop(); } objectStack.pop(); if (!objectStack.empty()) destObject = objectStack.top(); else destObject = nullptr; } break; } if (isArray) { switch (type) { case Diff_ReflectablePtr: { RTTIReflectablePtrFieldBase* field = static_cast(command.field); field->setArrayValue(destObject, command.arrayIdx, command.object); } break; case Diff_Reflectable: { RTTIReflectableFieldBase* field = static_cast(command.field); field->setArrayValue(destObject, command.arrayIdx, *command.object); } break; case Diff_Plain: { RTTIPlainFieldBase* field = static_cast(command.field); field->arrayElemFromBuffer(destObject, command.arrayIdx, command.value); } break; } } else { switch (type) { case Diff_ReflectablePtr: { RTTIReflectablePtrFieldBase* field = static_cast(command.field); field->setValue(destObject, command.object); } break; case Diff_Reflectable: { RTTIReflectableFieldBase* field = static_cast(command.field); field->setValue(destObject, *command.object); } break; case Diff_Plain: { RTTIPlainFieldBase* field = static_cast(command.field); field->fromBuffer(destObject, command.value); } break; case Diff_DataBlock: { RTTIManagedDataBlockFieldBase* field = static_cast(command.field); UINT8* dataCopy = field->allocate(destObject, command.size); memcpy(dataCopy, command.value, command.size); ManagedDataBlock value(dataCopy, command.size); // Not managed because I assume the owner class will decide whether to delete the data or keep it field->setValue(destObject, value); } break; } } } } void IDiff::applyDiff(RTTITypeBase* rtti, const SPtr& object, const SPtr& diff, DiffObjectMap& objectMap, Vector& diffCommands) { IDiff& diffHandler = rtti->getDiffHandler(); diffHandler.applyDiff(object, diff, objectMap, diffCommands); } SPtr BinaryDiff::generateDiff(const SPtr& orgObj, const SPtr& newObj, ObjectMap& objectMap) { SPtr output; for (auto& subObject : newObj->subObjects) { RTTITypeBase* rtti = IReflectable::_getRTTIfromTypeId(subObject.typeId); if (rtti == nullptr) continue; SerializedSubObject* orgSubObject = nullptr; for (auto& curSubObject : orgObj->subObjects) { if (curSubObject.typeId == subObject.typeId) { orgSubObject = &curSubObject; break; } } SerializedSubObject* diffSubObject = nullptr; for (auto& newEntry : subObject.entries) { RTTIField* genericField = rtti->findField(newEntry.first); if (genericField == nullptr) continue; SPtr newEntryData = newEntry.second.serialized; SPtr orgEntryData; if (orgSubObject != nullptr) { auto orgEntryFind = orgSubObject->entries.find(newEntry.first); if (orgEntryFind != orgSubObject->entries.end()) orgEntryData = orgEntryFind->second.serialized; } SPtr modification; bool hasModification = false; if (genericField->isArray()) { SPtr orgArrayData = std::static_pointer_cast(orgEntryData); SPtr newArrayData = std::static_pointer_cast(newEntryData); SPtr serializedArray; if (newEntryData != nullptr && orgEntryData != nullptr) { for (auto& arrayEntryPair : newArrayData->entries) { SPtr arrayModification; auto iterFind = orgArrayData->entries.find(arrayEntryPair.first); if (iterFind == orgArrayData->entries.end()) arrayModification = arrayEntryPair.second.serialized->clone(); else { arrayModification = IDiff::generateDiff(rtti, genericField->mType, iterFind->second.serialized, arrayEntryPair.second.serialized, objectMap); } if (arrayModification != nullptr) { if (serializedArray == nullptr) { serializedArray = bs_shared_ptr_new(); serializedArray->numElements = newArrayData->numElements; } SerializedArrayEntry arrayEntry; arrayEntry.index = arrayEntryPair.first; arrayEntry.serialized = arrayModification; serializedArray->entries[arrayEntryPair.first] = arrayEntry; } } } else if (newEntryData == nullptr) { serializedArray = bs_shared_ptr_new(); } else if (orgEntryData == nullptr) { serializedArray = std::static_pointer_cast(newArrayData->clone()); } modification = serializedArray; hasModification = modification != nullptr; } else { if (newEntryData != nullptr && orgEntryData != nullptr) { modification = IDiff::generateDiff(rtti, genericField->mType, orgEntryData, newEntryData, objectMap); hasModification = modification != nullptr; } else if (newEntryData == nullptr) { switch (genericField->mType) { case SerializableFT_Plain: case SerializableFT_DataBlock: modification = bs_shared_ptr_new(); break; } hasModification = true; } else if (orgEntryData == nullptr) { modification = newEntryData->clone(); hasModification = modification != nullptr; } } if (hasModification) { if (output == nullptr) output = bs_shared_ptr_new(); if (diffSubObject == nullptr) { output->subObjects.push_back(SerializedSubObject()); diffSubObject = &output->subObjects.back(); diffSubObject->typeId = rtti->getRTTIId(); } SerializedEntry modificationEntry; modificationEntry.fieldId = genericField->mUniqueId; modificationEntry.serialized = modification; diffSubObject->entries[genericField->mUniqueId] = modificationEntry; } } } return output; } void BinaryDiff::applyDiff(const SPtr& object, const SPtr& diff, DiffObjectMap& objectMap, Vector& diffCommands) { if (object == nullptr || diff == nullptr || object->getTypeId() != diff->getRootTypeId()) return; DiffCommand objStartCommand; objStartCommand.field = nullptr; objStartCommand.type = Diff_ObjectStart; objStartCommand.object = object; diffCommands.push_back(objStartCommand); Stack rttiTypes; for (auto& subObject : diff->subObjects) { for (auto& diffEntry : subObject.entries) { RTTITypeBase* rtti = IReflectable::_getRTTIfromTypeId(subObject.typeId); if (rtti == nullptr) continue; if (!object->isDerivedFrom(rtti)) continue; rtti->onSerializationStarted(object.get()); rttiTypes.push(rtti); RTTIField* genericField = rtti->findField(diffEntry.first); if (genericField == nullptr) continue; SPtr diffData = diffEntry.second.serialized; if (genericField->isArray()) { SPtr diffArray = std::static_pointer_cast(diffData); UINT32 numArrayElements = (UINT32)diffArray->numElements; DiffCommand arraySizeCommand; arraySizeCommand.field = genericField; arraySizeCommand.type = Diff_ArraySize | Diff_ArrayFlag; arraySizeCommand.arraySize = numArrayElements; diffCommands.push_back(arraySizeCommand); switch (genericField->mType) { case SerializableFT_ReflectablePtr: { RTTIReflectablePtrFieldBase* field = static_cast(genericField); UINT32 orgArraySize = genericField->getArraySize(object.get()); for (auto& arrayElem : diffArray->entries) { SPtr arrayElemData = std::static_pointer_cast(arrayElem.second.serialized); DiffCommand command; command.field = genericField; command.type = Diff_ReflectablePtr | Diff_ArrayFlag; command.arrayIdx = arrayElem.first; if (arrayElemData == nullptr) { command.object = nullptr; diffCommands.push_back(command); } else { bool needsNewObject = arrayElem.first >= orgArraySize; if (!needsNewObject) { SPtr childObj = field->getArrayValue(object.get(), arrayElem.first); if (childObj != nullptr) { IDiff::applyDiff(childObj->getRTTI(), childObj, arrayElemData, objectMap, diffCommands); command.object = childObj; } else needsNewObject = true; } if (needsNewObject) { RTTITypeBase* childRtti = IReflectable::_getRTTIfromTypeId(arrayElemData->getRootTypeId()); if (childRtti != nullptr) { auto findObj = objectMap.find(arrayElemData); if (findObj == objectMap.end()) { SPtr newObject = childRtti->newRTTIObject(); findObj = objectMap.insert(std::make_pair(arrayElemData, newObject)).first; } IDiff::applyDiff(childRtti, findObj->second, arrayElemData, objectMap, diffCommands); command.object = findObj->second; diffCommands.push_back(command); } else { command.object = nullptr; diffCommands.push_back(command); } } } } } break; case SerializableFT_Reflectable: { RTTIReflectableFieldBase* field = static_cast(genericField); UINT32 orgArraySize = genericField->getArraySize(object.get()); Vector> newArrayElements(numArrayElements); UINT32 minArrayLength = std::min(orgArraySize, numArrayElements); for (UINT32 i = 0; i < minArrayLength; i++) { IReflectable& childObj = field->getArrayValue(object.get(), i); newArrayElements[i] = BinaryCloner::clone(&childObj, true); } for (auto& arrayElem : diffArray->entries) { SPtr arrayElemData = std::static_pointer_cast(arrayElem.second.serialized); if (arrayElem.first < orgArraySize) { SPtr childObj = newArrayElements[arrayElem.first]; IDiff::applyDiff(childObj->getRTTI(), childObj, arrayElemData, objectMap, diffCommands); } else { RTTITypeBase* childRtti = IReflectable::_getRTTIfromTypeId(arrayElemData->getRootTypeId()); if (childRtti != nullptr) { SPtr newObject = childRtti->newRTTIObject(); IDiff::applyDiff(childRtti, newObject, arrayElemData, objectMap, diffCommands); newArrayElements[arrayElem.first] = newObject; } } } for (UINT32 i = 0; i < numArrayElements; i++) { DiffCommand command; command.field = genericField; command.type = Diff_Reflectable | Diff_ArrayFlag; command.arrayIdx = i; command.object = newArrayElements[i]; diffCommands.push_back(command); } } break; case SerializableFT_Plain: { for (auto& arrayElem : diffArray->entries) { SPtr fieldData = std::static_pointer_cast(arrayElem.second.serialized); if (fieldData != nullptr) { DiffCommand command; command.field = genericField; command.type = Diff_Plain | Diff_ArrayFlag; command.value = fieldData->value; command.size = fieldData->size; command.arrayIdx = arrayElem.first; diffCommands.push_back(command); } } } break; } } else { switch (genericField->mType) { case SerializableFT_ReflectablePtr: { RTTIReflectablePtrFieldBase* field = static_cast(genericField); SPtr fieldObjectData = std::static_pointer_cast(diffData); DiffCommand command; command.field = genericField; command.type = Diff_ReflectablePtr; if (fieldObjectData == nullptr) command.object = nullptr; else { SPtr childObj = field->getValue(object.get()); if (childObj == nullptr) { RTTITypeBase* childRtti = IReflectable::_getRTTIfromTypeId(fieldObjectData->getRootTypeId()); if (childRtti != nullptr) { auto findObj = objectMap.find(fieldObjectData); if (findObj == objectMap.end()) { SPtr newObject = childRtti->newRTTIObject(); findObj = objectMap.insert(std::make_pair(fieldObjectData, newObject)).first; } IDiff::applyDiff(childRtti, findObj->second, fieldObjectData, objectMap, diffCommands); command.object = findObj->second; } else { command.object = nullptr; } } else { IDiff::applyDiff(childObj->getRTTI(), childObj, fieldObjectData, objectMap, diffCommands); command.object = childObj; } } diffCommands.push_back(command); } break; case SerializableFT_Reflectable: { RTTIReflectableFieldBase* field = static_cast(genericField); SPtr fieldObjectData = std::static_pointer_cast(diffData); IReflectable& childObj = field->getValue(object.get()); std::shared_ptr clonedObj = BinaryCloner::clone(&childObj, true); IDiff::applyDiff(clonedObj->getRTTI(), clonedObj, fieldObjectData, objectMap, diffCommands); DiffCommand command; command.field = genericField; command.type = Diff_Reflectable; command.object = clonedObj; diffCommands.push_back(command); } break; case SerializableFT_Plain: { SPtr diffFieldData = std::static_pointer_cast(diffData); if (diffFieldData->size > 0) { DiffCommand command; command.field = genericField; command.type = Diff_Plain; command.value = diffFieldData->value; command.size = diffFieldData->size; diffCommands.push_back(command); } } break; case SerializableFT_DataBlock: { SPtr diffFieldData = std::static_pointer_cast(diffData); DiffCommand command; command.field = genericField; command.type = Diff_DataBlock; command.value = diffFieldData->value; command.size = diffFieldData->size; diffCommands.push_back(command); } break; } } } } DiffCommand objEndCommand; objEndCommand.field = nullptr; objEndCommand.type = Diff_ObjectEnd; objEndCommand.object = object; diffCommands.push_back(objEndCommand); while (!rttiTypes.empty()) { rttiTypes.top()->onSerializationEnded(object.get()); rttiTypes.pop(); } } }