OnPremise C++ Client

Kumamon
Kumamon
edited March 2017 in Native
Hi,
I'm trying to understand how Photon handle Object, Value, and Dictionaries and had a look on Object.h to see what is copied or not.

@/* Exit Games Common - C++ Client Lib
 * Copyright (C) 2004-2017 by Exit Games GmbH. All rights reserved.
 * http://www.photonengine.com
 * mailto:developer@photonengine.com
 */

#pragma once

#include "Common-cpp/inc/Base.h"

class EGUtilsHelper;

namespace ExitGames
{
	namespace Common
	{
		namespace Helpers
		{
			class SerializerImplementation;
			class DeSerializerImplementation;
		}

		class Object : public Base
		{
		public:
			using ToString::toString;

			Object(void);
			virtual ~Object(void);

			Object(const Object& toCopy);
			virtual Object& operator=(const Object& toCopy);

			bool operator==(const Object& toCompare) const;
			bool operator!=(const Object& toCompare) const;

			nByte getType(void) const;
			nByte getCustomType(void) const;
			const short* getSizes(void) const;
			unsigned int getDimensions(void) const;
			JString& toString(JString& retStr, bool withTypes=false) const;
		protected:
			Object(const void* data, nByte type, nByte customType, bool makeCopy);
			Object(const void* data, nByte type, nByte customType, int size, bool makeCopy);
			Object(const nByte* data, int size, bool makeCopy);
			Object(const void* data, nByte type, nByte customType, unsigned int dimensions, const short* sizes, bool makeCopy);

			const void* getData(void) const;
			const void* const* getDataPointer(void) const;
			virtual Object& assign(const Object& toCopy);
		private:
			union Data
			{
				nByte byteData;
				short shortData;
				int intData;
				int64 int64Data;
				float floatData;
				double doubleData;
				bool boolData;
			};

			void setSizes(const short* sizes, unsigned int dimensions=1);
			void setDimensions(unsigned int dimensions);
			void setData(const void* data);
			void setDataNoCopy(void* data);
			void setType(nByte type);
			void setCustomType(nByte customType);
			void set(const void* data, nByte type, nByte customType, bool makeCopy);
			void set(const void* data, nByte type, nByte customType, int size, bool makeCopy);
			void set(const void* data, nByte type, nByte customType, unsigned int dimensions, const short* sizes, bool makeCopy);
			void setWithoutCleanup(const void* data, nByte type, nByte customType, bool makeCopy);
			void setWithoutCleanup(const void* data, nByte type, nByte customType, int size, bool makeCopy);
			void setWithoutCleanup(const void* data, nByte type, nByte customType, unsigned int dimensions, const short* sizes, bool makeCopy);
			void setToNULL(void);
			void cleanup(const void* pData=0, unsigned int recursionDepth=0);
			void copyArray(const void* pDataIn, void** pDataOut, unsigned int recursionDepth=0) const;
			bool equalsArray(const void* pData1, const void* pData2, unsigned int recursionDepth=0) const;
			static JString& toStringHelper(const Object& object, JString& retStr, bool withTypes=false, bool butWithOutTopLevelTypes=false);
			JString payloadTypeToString(void) const;
			bool compareHelper(const void* pData1, const void* pData2, nByte type, nByte customType, unsigned int dimensions, const short* arraySizes, unsigned int recursionDepth) const;
			static Data constructDataInstance(nByte payload);
			static Data constructDataInstance(short payload);
			static Data constructDataInstance(int payload);
			static Data constructDataInstance(int64 payload);
			static Data constructDataInstance(float payload);
			static Data constructDataInstance(double payload);
			static Data constructDataInstance(bool payload);

			Data mData;
			void* mpData;
			int mSize;
			short* mpSizes;
			nByte mType;
			nByte mCustomType;
			unsigned int mDimensions;

			friend class DictionaryBase;
			template<typename EKeyType, typename EValueType> friend class Dictionary;
			friend class Hashtable;
			friend class Helpers::SerializerImplementation;
			friend class Helpers::DeSerializerImplementation;
			friend class ::EGUtilsHelper;
		};
	}
}
I'm wondering what is really behind void* mpData attribute, as the structure can handle Data or byte array or JString, and so what protected constructors Object(...) copy or not ?
Anyone worked on that?
Thanks :)

Comments

  • Hi @Kumamon.

    I am sorry, but I am not sure if I understand your question.

    What are you intending to do?

    The information about the kind of data that is stored inside an Object instance (and if it is stored inside mData or inside mpData), is encoded in the mType and the mDimensions member variables.
    The value of mType can be any of the values in Common::TypeCode. If the kind of data stored is a primitive type (any of the types, for which you can see a constructDataInstance() overload) and not an array, then it is stored in mData, otherwise in mpData.
    mDimensions determines if the stored data is an array or not. A value of 0 means, it is not an array, a value of 1 means, it is a 1D array, a value of 2 means, a 2D array, and so on.

    If the protected constructors make a copy of the payload or not does not depend on the type of the payload data, but on what the caller of such a constructor is passing for the 'makeCopy' parameter.
    These constructors get called by Objects subclass-templates KeyObject and ValueObject, which are the classes that should be used to create new Object instances.
    As you can read in the API reference for those 2 class templates, their constructors always store a deep copy of the passed payload. You can also see this behavior in the code in KeyObject.h and ValueObject.h.
    This assures that an application can safely modify the original data without accidentally also accessing the data inside the Object-instance.
    If neither the copy nor the original get actually modified, then the compiler might optimize the copy-operation away.
  • Thanks @Kaiserludi :)
    That's an explanation, it's a bit more clear. I was trying to know how the values are handled, I was not sure that the data were copied from the buffer, but as you said it seems it is. GetDataCopy also does a copy a second time so, or we have to use getdataaccess I guess.
    Anyway how does the protected constructors knows how much bytes they have to copy in case of makeCopy is true?
  • Hi @Kumamon.

    GetDataCopy also does a copy a second time so, or we have to use getdataaccess I guess.

    If you want to access the payload by copy or by address depends on how you want to access it and what kind of data you want to access.
    If you want to access a simple int, just accessing it by copy is cheaper and simpler than accessing it by address and accessing data by copy has the advantage that you can retrieve data from temporary Object instances and that data stays valid after the lifetime of that object instance.
    If you want to access a rather big array, JString, Dictionary or Hashtable or CustomType instance, it might be preferable to access it by address instead to avoid the overhead of copying it too often.
    However for small instances of class types it doesn't matter too much. Copying a 5 character string one more time than necessary won't have any recognizable performance impact, if you don't want to do it in code that runs a couple thousand times per ms. However if you have a 2mb array, then you absolutely want to access it by address, if you don't actually need a copy of it.


    Anyway how does the protected constructors knows how much bytes they have to copy in case of makeCopy is true?

    The copy code just iterates over all elements of all dimensions of the array (non-array data gets handled as if it was a 1D array of size 1) and calls operator= for each of the elements.
    All supported classes provide implementations for operator=() that know how to copy an instance of that class and how to figure out how big that instance is.
    In the case of a CustomType CustomTypeBase::duplicate() gets used and it is up to your CustomType implementation that its implementation for that function knows how to properly copy an instance of that type.
  • It's very nice explained @Kaiserludi, thanks! I see the problem with big structure so the pointer is better. I will have a closer look to that back on Monday, anyway thanks for your help, highly appreciated :smiley: