Browse Source

Better RTTI object cloning (untested)

Marko Pintera 10 years ago
parent
commit
1ff5442ad7

+ 2 - 0
BansheeUtility/BansheeUtility.vcxproj

@@ -251,6 +251,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="Source\BsBinaryCloner.cpp" />
     <ClCompile Include="Source\BsBounds.cpp" />
     <ClCompile Include="Source\BsCapsule.cpp" />
     <ClCompile Include="Source\BsConvexVolume.cpp" />
@@ -288,6 +289,7 @@
     <ClCompile Include="Source\Win32\BsFileSystem.cpp" />
     <ClCompile Include="Source\Win32\BsTimer.cpp" />
     <ClInclude Include="Include\BsAny.h" />
+    <ClInclude Include="Include\BsBinaryCloner.h" />
     <ClInclude Include="Include\BsBounds.h" />
     <ClInclude Include="Include\BsCapsule.h" />
     <ClInclude Include="Include\BsConvexVolume.h" />

+ 6 - 0
BansheeUtility/BansheeUtility.vcxproj.filters

@@ -302,6 +302,9 @@
     <ClInclude Include="Include\BsSerializedObjectRTTI.h">
       <Filter>Header Files\RTTI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsBinaryCloner.h">
+      <Filter>Header Files\Serialization</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsThreadPool.cpp">
@@ -481,5 +484,8 @@
     <ClCompile Include="Source\BsSerializedObject.cpp">
       <Filter>Source Files\Serialization</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsBinaryCloner.cpp">
+      <Filter>Source Files\Serialization</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 83 - 0
BansheeUtility/Include/BsBinaryCloner.h

@@ -0,0 +1,83 @@
+#pragma once
+
+#include "BsPrerequisitesUtil.h"
+
+namespace BansheeEngine
+{
+	/**
+	 * @brief	Helper class that performs cloning of an object that implements RTTI.
+	 */
+	class BS_UTILITY_EXPORT BinaryCloner
+	{
+	public:
+
+		/**
+		 * @brief	Returns a copy of the provided object with identical data.
+		 *
+		 * @param	object		Object to clone.
+		 * @param	shallow		If false then all referenced objects will be cloned
+		 *						as well, otherwise the references to the original
+		 *						objects will be kept.
+		 */
+		static SPtr<IReflectable> clone(IReflectable* object, bool shallow = false);
+
+	private:
+		struct ObjectReferenceData;
+
+		/**
+		 * @brief	Identifier representing a single field
+		 *			or an array entry in an object.
+		 */
+		struct FieldId
+		{
+			RTTIField* field;
+			INT32 arrayIdx;
+		};
+
+		/**
+		 * @brief	A saved reference to an object with a field
+		 *			identifier that owns it.
+		 */
+		struct ObjectReference
+		{
+			FieldId fieldId;
+			SPtr<IReflectable> object;
+		};
+
+		/**
+		 * @brief	Contains all object references in a portion of an
+		 *			object belonging to a specific class (base and derived 
+		 *			classes count as separate sub-objects).
+		 */
+		struct SubObjectReferenceData
+		{
+			RTTITypeBase* rtti;
+			Vector<ObjectReference> references;
+			Vector<ObjectReferenceData> children;
+		};
+
+		/**
+		 * @brief	Contains all object references in an entire object, as well
+		 *			as the identifier of the field owning this object.
+		 */
+		struct ObjectReferenceData
+		{
+			FieldId fieldId;
+			Vector<SubObjectReferenceData> subObjectData;
+		};
+
+		/**
+		 * @brief	Iterates over the provided object hierarchy and retrieves all object references
+		 *			which are returned in "referenceData" output parameter, also in a hierarchical
+		 *			format for easier parsing.
+		 */
+		static void gatherReferences(IReflectable* object, ObjectReferenceData& referenceData);
+
+		/**
+		 * @brief	Restores a set of references retrieved by "gatherReferences" and applies them to
+		 *			a specific object. Type of the object must be the same as the type that was used
+		 *			when calling "gatherReferences".
+		 */
+		static void restoreReferences(IReflectable* object, const ObjectReferenceData& referenceData);
+	};
+}

+ 0 - 5
BansheeUtility/Include/BsBinaryDiff.h

@@ -92,10 +92,5 @@ namespace BansheeEngine
 		 * @see		applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff)
 		 */
 		static void applyDiff(const SPtr<IReflectable>& object, const SPtr<SerializedObject>& diff, DiffObjectMap& objectMap, Vector<DiffCommand>& diffCommands);
-
-		/**
-		 * @brief	Helper method that clones any object with RTTI implemented.
-		 */
-		static SPtr<IReflectable> clone(IReflectable* object);
 	};
 }

+ 18 - 4
BansheeUtility/Include/BsBinarySerializer.h

@@ -53,18 +53,32 @@ namespace BansheeEngine
 		 * 									"bytesRead" variable, as buffer might not be full completely). User must then
 		 * 									either create a new buffer or empty the existing one, and then return it by the callback.
 		 * 									If the returned buffer address is NULL, encoding is aborted.
+		 * @param	shallow					Determines how to handle referenced objects. If true then references will not be encoded
+		 *									and will be set to null. If false then references will be encoded as well and restored
+		 *									upon decoding.
 		 */
 		void encode(IReflectable* object, UINT8* buffer, UINT32 bufferLength, UINT32* bytesWritten,
-			std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback);
+			std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback,
+			bool shallow = false);
 
 		/**
 		 * @brief	Decodes an object from binary data.
 		 *
 		 * @param 	data  	Binary data to decode.
-		 * @param	dataLength	Length of the data.
+		 * @param	dataLength	Length of the data in bytes.
 		 */
 		std::shared_ptr<IReflectable> decode(UINT8* data, UINT32 dataLength);
 
+		/**
+		 * @brief	Returns a copy of the provided object with indentical data.
+		 *
+		 * @param	object		Object to clone
+		 * @param	shallow		If false then all referenced objects will be cloned
+		 *						as well, otherwise the references to the original
+		 *						objects will be kept.
+		 */
+		std::shared_ptr<IReflectable> clone(IReflectable* object, bool shallow = false);
+
 		/**
 		 * @brief	Decodes an object in memory into an intermediate representation for easier parsing.
 		 *			
@@ -112,7 +126,7 @@ namespace BansheeEngine
 		 * @brief	Encodes a single IReflectable object. 
 		 */
 		UINT8* encodeInternal(IReflectable* object, UINT32 objectId, UINT8* buffer, UINT32& bufferLength, UINT32* bytesWritten,
-			std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback);
+			std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback, bool shallow);
 
 		/**
 		 * @brief	Decodes a single IReflectable object.
@@ -128,7 +142,7 @@ namespace BansheeEngine
 		 * @brief	Helper method for encoding a complex object and copying its data to a buffer.
 		 */
 		UINT8* complexTypeToBuffer(IReflectable* object, UINT8* buffer, UINT32& bufferLength, UINT32* bytesWritten,
-			std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback);
+			std::function<UINT8*(UINT8* buffer, UINT32 bytesWritten, UINT32& newBufferSize)> flushBufferCallback, bool shallow);
 
 		/**
 		 * @brief	Helper method for encoding a data block to a buffer.

+ 1 - 0
BansheeUtility/Include/BsFwdDeclUtil.h

@@ -57,6 +57,7 @@ namespace BansheeEngine
 	class TestOutput;
 	class AsyncOpSyncData;
 	struct RTTIField;
+	struct RTTIReflectablePtrFieldBase;
 	// Reflection
 	class IReflectable;
 	class RTTITypeBase;

+ 11 - 3
BansheeUtility/Include/BsMemorySerializer.h

@@ -23,10 +23,18 @@ namespace BansheeEngine
 		 * @brief	Parses the provided object, serializes all of its data as specified by its
 		 *			RTTIType and returns the data in the form of raw memory.
 		 *
-		 *			All memory is allocated using the provided "allocator". If not specified the default
-		 *			allocator is used.
+		 * @param	object			Object to encode.
+		 * @param	bytesWritten	Output value containing the total number of bytes it took to encode the object.
+		 * @param	allocator		Determines how is memory allocated. If not specified the default allocator is used.
+		 * @param	shallow			Determines how to handle referenced objects. If true then references will not be encoded
+		 *							and will be set to null. If false then references will be encoded as well and restored
+		 *							upon decoding.
+		 *
+		 * @return	A buffer containing the encoded object. It is up to the user to release the buffer memory when
+		 *			no longer needed.
 		 */
-		UINT8* encode(IReflectable* object, UINT32& bytesWritten, std::function<void*(UINT32)> allocator = nullptr);
+		UINT8* encode(IReflectable* object, UINT32& bytesWritten, std::function<void*(UINT32)> allocator = nullptr, 
+			bool shallow = false);
 
 		/**
 		 * @brief	Deserializes an IReflectable object by reading the binary data from the provided

+ 202 - 0
BansheeUtility/Source/BsBinaryCloner.cpp

@@ -0,0 +1,202 @@
+#include "BsBinaryCloner.h"
+#include "BsIReflectable.h"
+#include "BsRTTIType.h"
+#include "BsRTTIField.h"
+#include "BsRTTIPlainField.h"
+#include "BsRTTIReflectableField.h"
+#include "BsRTTIReflectablePtrField.h"
+#include "BsRTTIManagedDataBlockField.h"
+#include "BsMemorySerializer.h"
+
+namespace BansheeEngine
+{
+	SPtr<IReflectable> BinaryCloner::clone(IReflectable* object, bool shallow)
+	{
+		if (object == nullptr)
+			return nullptr;
+
+		ObjectReferenceData referenceData;
+		if (shallow)
+			gatherReferences(object, referenceData);
+
+		MemorySerializer ms;
+		UINT32 dataSize = 0;
+		UINT8* data = ms.encode(object, dataSize, &bs_alloc, shallow);
+		SPtr<IReflectable> clonedObj = ms.decode(data, dataSize);
+
+		if (shallow)
+			restoreReferences(clonedObj.get(), referenceData);
+
+		bs_free(data);
+		return clonedObj;
+	}
+
+	void BinaryCloner::gatherReferences(IReflectable* object, ObjectReferenceData& referenceData)
+	{
+		if (object == nullptr)
+			return;
+
+		Stack<IReflectable*> todo;
+
+		RTTITypeBase* rtti = object->getRTTI();
+		Stack<RTTITypeBase*> rttiTypes;
+		while (rtti != nullptr)
+		{
+			rtti->onSerializationStarted(object);
+			SubObjectReferenceData* subObjectData = nullptr;
+
+			UINT32 numFields = rtti->getNumFields();
+			for (UINT32 i = 0; i < numFields; i++)
+			{
+				RTTIField* field = rtti->getField(i);
+				FieldId fieldId;
+				fieldId.field = field;
+				fieldId.arrayIdx = -1;
+
+				if (field->isArray())
+				{
+					UINT32 numElements = field->getArraySize(object);
+
+					for (UINT32 j = 0; j < numElements; j++)
+					{
+						fieldId.arrayIdx = j;
+
+						if (field->mType == SerializableFT_ReflectablePtr)
+						{
+							RTTIReflectablePtrFieldBase* curField = static_cast<RTTIReflectablePtrFieldBase*>(field);
+							SPtr<IReflectable> childObj = curField->getArrayValue(object, j);
+
+							if (childObj != nullptr)
+							{
+								if (subObjectData == nullptr)
+								{
+									referenceData.subObjectData.push_back(SubObjectReferenceData());
+									subObjectData = &referenceData.subObjectData.back();
+									subObjectData->rtti = rtti;
+								}
+
+								subObjectData->references.push_back(ObjectReference());
+								ObjectReference& reference = subObjectData->references.back();
+								reference.fieldId = fieldId;
+								reference.object = childObj;
+							}
+						}
+						else if (field->mType == SerializableFT_Reflectable)
+						{
+							RTTIReflectableFieldBase* curField = static_cast<RTTIReflectableFieldBase*>(field);
+							IReflectable* childObj = &curField->getArrayValue(object, j);
+							
+							if (subObjectData == nullptr)
+							{
+								referenceData.subObjectData.push_back(SubObjectReferenceData());
+								subObjectData = &referenceData.subObjectData.back();
+								subObjectData->rtti = rtti;
+							}
+
+							subObjectData->children.push_back(ObjectReferenceData());
+							ObjectReferenceData childData = subObjectData->children.back();
+							childData.fieldId = fieldId;
+
+							gatherReferences(childObj, childData);
+						}
+					}
+				}
+				else
+				{
+					if (field->mType == SerializableFT_ReflectablePtr)
+					{
+						RTTIReflectablePtrFieldBase* curField = static_cast<RTTIReflectablePtrFieldBase*>(field);
+						SPtr<IReflectable> childObj = curField->getValue(object);
+
+						if (childObj != nullptr)
+						{
+							if (subObjectData == nullptr)
+							{
+								referenceData.subObjectData.push_back(SubObjectReferenceData());
+								subObjectData = &referenceData.subObjectData.back();
+								subObjectData->rtti = rtti;
+							}
+
+							subObjectData->references.push_back(ObjectReference());
+							ObjectReference& reference = subObjectData->references.back();
+							reference.fieldId = fieldId;
+							reference.object = childObj;
+						}
+					}
+					else if (field->mType == SerializableFT_Reflectable)
+					{
+						RTTIReflectableFieldBase* curField = static_cast<RTTIReflectableFieldBase*>(field);
+						IReflectable* childObj = &curField->getValue(object);
+
+						if (subObjectData == nullptr)
+						{
+							referenceData.subObjectData.push_back(SubObjectReferenceData());
+							subObjectData = &referenceData.subObjectData.back();
+							subObjectData->rtti = rtti;
+						}
+
+						subObjectData->children.push_back(ObjectReferenceData());
+						ObjectReferenceData childData = subObjectData->children.back();
+						childData.fieldId = fieldId;
+
+						gatherReferences(childObj, childData);
+					}
+				}
+			}
+
+			rttiTypes.push(rtti);
+			rtti = rtti->getBaseClass();
+		}
+
+		while (!rttiTypes.empty())
+		{
+			rtti = rttiTypes.top();
+			rttiTypes.pop();
+
+			rtti->onSerializationEnded(object);
+		}
+	}
+
+	void BinaryCloner::restoreReferences(IReflectable* object, const ObjectReferenceData& referenceData)
+	{
+		for (auto& subObject : referenceData.subObjectData)
+		{
+			if (subObject.references.size() > 0)
+			{
+				subObject.rtti->onDeserializationStarted(object);
+
+				for (auto& reference : subObject.references)
+				{
+					RTTIReflectablePtrFieldBase* curField = static_cast<RTTIReflectablePtrFieldBase*>(reference.fieldId.field);
+
+					if (curField->isArray())
+						curField->setArrayValue(object, reference.fieldId.arrayIdx, reference.object);
+					else
+						curField->setValue(object, reference.object);
+				}
+
+				subObject.rtti->onDeserializationEnded(object);
+			}
+
+			if (subObject.children.size() > 0)
+			{
+				subObject.rtti->onSerializationStarted(object);
+
+				for (auto& childObjectData : subObject.children)
+				{
+					RTTIReflectableFieldBase* curField = static_cast<RTTIReflectableFieldBase*>(childObjectData.fieldId.field);
+
+					IReflectable* childObj = nullptr;
+					if (curField->isArray())
+						childObj = &curField->getArrayValue(object, childObjectData.fieldId.arrayIdx);
+					else
+						childObj = &curField->getValue(object);
+
+					restoreReferences(childObj, childObjectData);
+				}
+
+				subObject.rtti->onSerializationEnded(object);
+			}
+		}
+	}
+}

+ 3 - 14
BansheeUtility/Source/BsBinaryDiff.cpp

@@ -1,6 +1,6 @@
 #include "BsBinaryDiff.h"
 #include "BsBinarySerializer.h"
-#include "BsMemorySerializer.h"
+#include "BsBinaryCloner.h"
 #include "BsRTTIType.h"
 
 namespace BansheeEngine
@@ -425,7 +425,7 @@ namespace BansheeEngine
 						for (UINT32 i = 0; i < minArrayLength; i++)
 						{
 							IReflectable& childObj = field->getArrayValue(object.get(), i);
-							newArrayElements[i] = clone(&childObj);
+							newArrayElements[i] = BinaryCloner::clone(&childObj, true);
 						}
 
 						for (auto& arrayElem : diffArray->entries)
@@ -535,7 +535,7 @@ namespace BansheeEngine
 						SPtr<SerializedObject> fieldObjectData = std::static_pointer_cast<SerializedObject>(diffData);
 
 						IReflectable& childObj = field->getValue(object.get());
-						std::shared_ptr<IReflectable> clonedObj = clone(&childObj);
+						std::shared_ptr<IReflectable> clonedObj = BinaryCloner::clone(&childObj, true);
 
 						applyDiff(clonedObj, fieldObjectData);
 
@@ -594,15 +594,4 @@ namespace BansheeEngine
 			rttiTypes.pop();
 		}
 	}
-
-	SPtr<IReflectable> BinaryDiff::clone(IReflectable* object)
-	{
-		UINT32 bufferSize = 0;
-		MemorySerializer serializer;
-		UINT8* buffer = serializer.encode(object, bufferSize, &bs_alloc);
-		std::shared_ptr<IReflectable> clonedObj = serializer.decode(buffer, bufferSize);
-		bs_free(buffer);
-
-		return clonedObj;
-	}
 }

+ 20 - 10
BansheeUtility/Source/BsBinarySerializer.cpp

@@ -43,7 +43,8 @@ namespace BansheeEngine
 	{
 	}
 
-	void BinarySerializer::encode(IReflectable* object, UINT8* buffer, UINT32 bufferLength, UINT32* bytesWritten, std::function<UINT8*(UINT8*, UINT32, UINT32&)> flushBufferCallback)
+	void BinarySerializer::encode(IReflectable* object, UINT8* buffer, UINT32 bufferLength, 
+		UINT32* bytesWritten, std::function<UINT8*(UINT8*, UINT32, UINT32&)> flushBufferCallback, bool shallow)
 	{
 		mObjectsToEncode.clear();
 		mObjectAddrToId.clear();
@@ -56,7 +57,7 @@ namespace BansheeEngine
 		UINT32 objectId = findOrCreatePersistentId(object);
 		
 		// Encode primary object and its value types
-		buffer = encodeInternal(object, objectId, buffer, bufferLength, bytesWritten, flushBufferCallback);
+		buffer = encodeInternal(object, objectId, buffer, bufferLength, bytesWritten, flushBufferCallback, shallow);
 		if(buffer == nullptr)
 		{
 			BS_EXCEPT(InternalErrorException, 
@@ -80,7 +81,8 @@ namespace BansheeEngine
 				serializedObjects.insert(curObjectid);
 				mObjectsToEncode.erase(iter);
 
-				buffer = encodeInternal(curObject.get(), curObjectid, buffer, bufferLength, bytesWritten, flushBufferCallback);
+				buffer = encodeInternal(curObject.get(), curObjectid, buffer, 
+					bufferLength, bytesWritten, flushBufferCallback, shallow);
 				if(buffer == nullptr)
 				{
 					BS_EXCEPT(InternalErrorException, 
@@ -153,7 +155,7 @@ namespace BansheeEngine
 	}
 
 	UINT8* BinarySerializer::encodeInternal(IReflectable* object, UINT32 objectId, UINT8* buffer, UINT32& bufferLength, 
-		UINT32* bytesWritten, std::function<UINT8*(UINT8*, UINT32, UINT32&)> flushBufferCallback)
+		UINT32* bytesWritten, std::function<UINT8*(UINT8*, UINT32, UINT32&)> flushBufferCallback, bool shallow)
 	{
 		RTTITypeBase* si = object->getRTTI();
 		bool isBaseClass = false;
@@ -192,7 +194,10 @@ namespace BansheeEngine
 
 							for(UINT32 arrIdx = 0; arrIdx < arrayNumElems; arrIdx++)
 							{
-								std::shared_ptr<IReflectable> childObject = curField->getArrayValue(object, arrIdx); 
+								std::shared_ptr<IReflectable> childObject;
+								
+								if (!shallow)
+									childObject = curField->getArrayValue(object, arrIdx);
 
 								UINT32 objId = registerObjectPtr(childObject);
 								COPY_TO_BUFFER(&objId, sizeof(UINT32))
@@ -208,7 +213,8 @@ namespace BansheeEngine
 							{
 								IReflectable& childObject = curField->getArrayValue(object, arrIdx);
 
-								buffer = complexTypeToBuffer(&childObject, buffer, bufferLength, bytesWritten, flushBufferCallback);
+								buffer = complexTypeToBuffer(&childObject, buffer, bufferLength, 
+									bytesWritten, flushBufferCallback, shallow);
 								if(buffer == nullptr)
 								{
 									si->onSerializationEnded(object);
@@ -268,7 +274,10 @@ namespace BansheeEngine
 					case SerializableFT_ReflectablePtr:
 						{
 							RTTIReflectablePtrFieldBase* curField = static_cast<RTTIReflectablePtrFieldBase*>(curGenericField);
-							std::shared_ptr<IReflectable> childObject = curField->getValue(object); 
+							std::shared_ptr<IReflectable> childObject;
+							
+							if (!shallow)
+								childObject = curField->getValue(object);
 
 							UINT32 objId = registerObjectPtr(childObject);
 							COPY_TO_BUFFER(&objId, sizeof(UINT32))
@@ -280,7 +289,8 @@ namespace BansheeEngine
 							RTTIReflectableFieldBase* curField = static_cast<RTTIReflectableFieldBase*>(curGenericField);
 							IReflectable& childObject = curField->getValue(object);
 
-							buffer = complexTypeToBuffer(&childObject, buffer, bufferLength, bytesWritten, flushBufferCallback);
+							buffer = complexTypeToBuffer(&childObject, buffer, bufferLength, 
+								bytesWritten, flushBufferCallback, shallow);
 							if(buffer == nullptr)
 							{
 								si->onSerializationEnded(object);
@@ -1238,7 +1248,7 @@ namespace BansheeEngine
 	}
 
 	UINT8* BinarySerializer::complexTypeToBuffer(IReflectable* object, UINT8* buffer, UINT32& bufferLength, 
-		UINT32* bytesWritten, std::function<UINT8*(UINT8*, UINT32, UINT32&)> flushBufferCallback)
+		UINT32* bytesWritten, std::function<UINT8*(UINT8*, UINT32, UINT32&)> flushBufferCallback, bool shallow)
 	{
 		int complexTypeSize = 0;
 		if(object != nullptr)
@@ -1247,7 +1257,7 @@ namespace BansheeEngine
 		COPY_TO_BUFFER(&complexTypeSize, COMPLEX_TYPE_FIELD_SIZE)
 
 		if(object != nullptr)
-			return encodeInternal(object, 0, buffer, bufferLength, bytesWritten, flushBufferCallback);
+			return encodeInternal(object, 0, buffer, bufferLength, bytesWritten, flushBufferCallback, shallow);
 
 		return buffer;
 	}

+ 4 - 2
BansheeUtility/Source/BsMemorySerializer.cpp

@@ -14,7 +14,8 @@ namespace BansheeEngine
 	MemorySerializer::~MemorySerializer()
 	{ }
 
-	UINT8* MemorySerializer::encode(IReflectable* object, UINT32& bytesWritten, std::function<void*(UINT32)> allocator)
+	UINT8* MemorySerializer::encode(IReflectable* object, UINT32& bytesWritten, 
+		std::function<void*(UINT32)> allocator, bool shallow)
 	{
 		using namespace std::placeholders;
 
@@ -26,7 +27,8 @@ namespace BansheeEngine
 
 		mBufferPieces.push_back(piece);
 
-		bs.encode(object, piece.buffer, WRITE_BUFFER_SIZE, &bytesWritten, std::bind(&MemorySerializer::flushBuffer, this, _1, _2, _3));
+		bs.encode(object, piece.buffer, WRITE_BUFFER_SIZE, &bytesWritten, 
+			std::bind(&MemorySerializer::flushBuffer, this, _1, _2, _3), shallow);
 
 		UINT8* resultBuffer;
 		if(allocator != nullptr)

+ 0 - 3
TODO.txt

@@ -32,9 +32,6 @@ IMMEDIATE:
  - Create a native only unit test for binary diff
  - Test and debug native diff
 
-Is cloning broken?
- - What if I clone a Reflectable that references a Reflectable by pointer? Then that will be cloned as well but it shouldn't be
-
 See "Prefabs" gdoc for later goals
 See "Brainstorm" gdoc for ideas about how to solve the ID issue (and see below)