#pragma once #include "dtcore/inc/dtstring.h" #include "dtcore/inc/dtbytebuffer.h" #include "dtcore/inc/dtvector.h" #include "dtcore/inc/dtqueue.h" #include "dtcore/inc/dtmap.h" #include "dtcore/inc/dtset.h" #include "dtcore/inc/dtstrongtype.h" #include "dtcore/inc/dtchrono.h" #include "dtcore/inc/dterror.h" #pragma warning(push) #include #pragma warning(pop) namespace dt { static const char SERIALIZE_VERSION_MEMBER_NAME[] = "_version"; enum StreamType { StreamType_None = 0, StreamType_Binary = 1, StreamType_String = 2, StreamType_S8 = 3, StreamType_S16 = 4, StreamType_S32 = 5, StreamType_S64 = 6, StreamType_U8 = 7, StreamType_U16 = 8, StreamType_U32 = 9, StreamType_U64 = 10, StreamType_Float = 11, StreamType_Double = 12, StreamType_Bool = 13, StreamType_Object = 14, StreamType_List = 15, }; // Stream interface for serialization. See StreamTextOutput, StreamBinaryInput and // StreamBinaryOutput classes for actual implementation of this interface. class Stream { public: DT_CORE_DLL_ENTRY virtual ~Stream() {} DT_CORE_DLL_ENTRY virtual void SerializeBinary(const ByteBuffer& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeBinary(ByteBuffer& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeString(const String& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeString(String& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const S8& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(S8& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const S16& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(S16& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const S32& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(S32& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const S64& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(S64& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const U8& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(U8& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const U16& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(U16& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const U32& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(U32& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const U64& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(U64& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const float& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(float& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeNumeral(const double& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeNumeral(double& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeBool(const bool& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeBool(bool& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeSize(const SizeT& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeSize(SizeT& data) = 0; DT_CORE_DLL_ENTRY virtual void SerializeVersion(U8& data) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeMember(const char* name) = 0; DT_CORE_DLL_ENTRY virtual void SerializeKey(const String& name) = 0; DT_CORE_DLL_ENTRY virtual bool SerializeKey(String& name) = 0; DT_CORE_DLL_ENTRY virtual void ObjectStart(const void* pointer) = 0; DT_CORE_DLL_ENTRY virtual void ObjectEnd(const void* pointer) = 0; DT_CORE_DLL_ENTRY virtual void ListStart(const void* pointer) = 0; DT_CORE_DLL_ENTRY virtual void ListEnd(const void* pointer) = 0; DT_CORE_DLL_ENTRY virtual bool IsInput() const = 0; }; // Base class that define the basic serialization methods class Serializable { public: DT_CORE_DLL_ENTRY virtual ~Serializable() {} DT_CORE_DLL_ENTRY virtual void Serialize(U8 version, Stream& stream) = 0; DT_CORE_DLL_ENTRY virtual U8 GetSerializeVersion() const { return 1; } }; // Helper macros for serialization #define dtVariable(variable) dt::StreamMember(#variable, variable) #define dtVariableNamed(variable, variableName) dt::StreamMember(variableName, variable) #define dtVariableEnumNamed(variable, variableName) dt::StreamMemberEnum(variableName, variable) #define dtMember(member) dt::StreamMember(#member, this->member) #define dtMemberEnum(member) dt::StreamMemberEnum(#member, this->member) // Helper template class for serialization of class members template class StreamMember { public: StreamMember(const char* name, T& member) : m_Name(name) , m_Member(member) {} const char* GetName() const { return m_Name; } T& GetMember() const { return m_Member; } private: const char* m_Name; T& m_Member; }; template class StreamMemberEnum { public: StreamMemberEnum(const char* name, T& member) : m_Name(name) , m_Member(member) {} const char* GetName() const { return m_Name; } T& GetMember() const { return m_Member; } private: const char* m_Name; T& m_Member; }; class StreamObjectStart { public: DT_CORE_DLL_ENTRY StreamObjectStart(const void* pointer) : m_Pointer(pointer) {} DT_CORE_DLL_ENTRY const void* GetPointer() const { return m_Pointer; } private: const void* m_Pointer; }; class StreamObjectEnd { public: DT_CORE_DLL_ENTRY StreamObjectEnd(const void* pointer) : m_Pointer(pointer) {} DT_CORE_DLL_ENTRY const void* GetPointer() const { return m_Pointer; } private: const void* m_Pointer; }; class StreamListStart { public: DT_CORE_DLL_ENTRY StreamListStart(const void* pointer) : m_Pointer(pointer) {} DT_CORE_DLL_ENTRY const void* GetPointer() const { return m_Pointer; } private: const void* m_Pointer; }; class StreamListEnd { public: DT_CORE_DLL_ENTRY StreamListEnd(const void* pointer) : m_Pointer(pointer) {} DT_CORE_DLL_ENTRY const void* GetPointer() const { return m_Pointer; } private: const void* m_Pointer; }; // Serialization macros template Stream& operator<<(Stream& stream, const Vector& data) { stream.ListStart(&data); DT_ASSERT(!stream.IsInput()); SizeT size(data.Size()); stream.SerializeSize(size); for (auto i = data.Begin(); i != data.End(); ++i) { stream << *i; } stream.ListEnd(&data); return stream; } template Stream& operator<<(Stream& stream, Vector& data) { stream.ListStart(&data); if (stream.IsInput()) { SizeT size(0); stream.SerializeSize(size); data.Resize(size); } else { SizeT size(data.Size()); stream.SerializeSize(size); } for (auto i = data.Begin(); i != data.End(); ++i) { stream << *i; } stream.ListEnd(&data); return stream; } template Stream& operator<<(Stream& stream, const Queue& data) { stream.ListStart(&data); DT_ASSERT(!stream.IsInput()); SizeT size(data.Size()); stream.SerializeSize(size); for (auto i = data.Begin(); i != data.End(); ++i) { stream << *i; } stream.ListEnd(&data); return stream; } template Stream& operator<<(Stream& stream, Queue& data) { stream.ListStart(&data); if (stream.IsInput()) { SizeT size(0); stream.SerializeSize(size); data.Resize(size); } else { SizeT size(data.Size()); stream.SerializeSize(size); } for (auto i = data.Begin(); i != data.End(); ++i) { stream << *i; } stream.ListEnd(&data); return stream; } template Stream& operator<<(Stream& stream, const Map& data) { stream.ObjectStart(&data); SizeT size(data.Size()); stream.SerializeSize(size); DT_ASSERT(!stream.IsInput()); for (const auto& i : data) { stream.SerializeKey(i.first); stream << i.second; } stream.ObjectEnd(&data); return stream; } #pragma warning(push) #pragma warning(disable: 4700) // uninitialized local variable 'value' used template Stream& operator<<(Stream& stream, Map& data) { stream.ObjectStart(&data); SizeT size(data.Size()); stream.SerializeSize(size); if (stream.IsInput()) { for (SizeT i = 0; i < size; i++) { String key; stream.SerializeKey(key); VALUE value; stream << value; data.InsertOrAssign(key, value); } } else { for (const auto& i : data) { stream.SerializeKey(i.first); stream << i.second; } } stream.ObjectEnd(&data); return stream; } #pragma warning(pop) template Stream& operator<<(Stream& stream, const Map& data) { stream.ObjectStart(&data); SizeT size(data.Size()); stream.SerializeSize(size); DT_ASSERT(!stream.IsInput()); for (const auto& i : data) { String key; key << i.first; stream.SerializeKey(key); stream << i.second; } stream.ObjectEnd(&data); return stream; } #pragma warning(push) #pragma warning(disable: 4700) // uninitialized local variable 'value' used template Stream& operator<<(Stream& stream, Map& data) { stream.ObjectStart(&data); SizeT size(data.Size()); stream.SerializeSize(size); if (stream.IsInput()) { String keyStr; KEY key; VALUE value; for (SizeT i = 0; i < size; i++) { stream.SerializeKey(keyStr); stream << value; data.InsertOrAssign(keyStr >> key, value); } } else { for (const auto& i : data) { String key; key << i.first; stream.SerializeKey(key); stream << i.second; } } stream.ObjectEnd(&data); return stream; } #pragma warning(pop) template Stream& operator<<(Stream& stream, const Set& data) { stream.ListStart(&data); SizeT size(data.Size()); stream.SerializeSize(size); DT_ASSERT(!stream.IsInput()); for (auto i = data.Begin(); i != data.End(); ++i) { stream << *i; } stream.ListEnd(&data); return stream; } #pragma warning(push) #pragma warning(disable: 4700) // uninitialized local variable 'value' used template Stream& operator<<(Stream& stream, Set& data) { stream.ListStart(&data); SizeT size(data.Size()); stream.SerializeSize(size); if (stream.IsInput()) { VALUE value; for (SizeT i = 0; i < size; i++) { stream << value; data.Insert(value); } } else { for (auto i = data.Begin(); i != data.End(); ++i) { stream << *i; } } stream.ListEnd(&data); return stream; } #pragma warning(pop) inline Stream& operator<<(Stream& stream, String& data) { stream.SerializeString(data); return stream; } inline Stream& operator<<(Stream& stream, const String& data) { stream.SerializeString(data); return stream; } template inline Stream& operator<<(Stream& stream, StrongType& data) { stream.SerializeNumeral(data.UnboxValue()); return stream; } template inline Stream& operator<<(Stream& stream, const StrongType& data) { stream.SerializeNumeral(data.UnboxValue()); return stream; } template inline Stream& operator<<(Stream& stream, std::chrono::duration& data) { if (stream.IsInput()) { Rep r = 0; if (stream.SerializeNumeral(r)) { data = std::chrono::duration(r); } } else { stream.SerializeNumeral(data.count()); } return stream; } template inline Stream& operator<<(Stream& stream, const std::chrono::duration& data) { stream.SerializeNumeral(data.count()); return stream; } inline Stream& operator<<(Stream& stream, const char* data) { stream.SerializeString(data); return stream; } inline Stream& operator<<(Stream& stream, S8& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const S8& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, S16& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const S16& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, S32& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const S32& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, S64& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const S64& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, U8& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const U8& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, U16& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const U16& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, U32& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const U32& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, U64& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const U64& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, float& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const float& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, double& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, const double& data) { stream.SerializeNumeral(data); return stream; } inline Stream& operator<<(Stream& stream, bool& data) { stream.SerializeBool(data); return stream; } inline Stream& operator<<(Stream& stream, const bool& data) { stream.SerializeBool(data); return stream; } inline Stream& operator<<(Stream& stream, ByteBuffer& data) { stream.SerializeBinary(data); return stream; } inline Stream& operator<<(Stream& stream, const ByteBuffer& data) { stream.SerializeBinary(data); return stream; } template inline Stream& operator<<(Stream& stream, const StreamMember& member) { if (stream.SerializeMember(member.GetName())) { stream << member.GetMember(); } return stream; } template inline Stream& operator<<(Stream& stream, const StreamMemberEnum& member) { if (stream.SerializeMember(member.GetName())) { if (stream.IsInput()) { S32 data(0); stream.SerializeNumeral(data); member.GetMember() = static_cast(data); } else { stream.SerializeNumeral(static_cast(member.GetMember())); } } return stream; } inline Stream& operator<<(Stream& stream, const StreamObjectStart& scope) { stream.ObjectStart(scope.GetPointer()); return stream; } inline Stream& operator<<(Stream& stream, const StreamObjectEnd& scope) { stream.ObjectEnd(scope.GetPointer()); return stream; } template inline Stream& operator<<(Stream& stream, T& object) { U8 version(object.GetSerializeVersion()); stream.ObjectStart(&object); stream.SerializeVersion(version); object.Serialize(version, stream); stream.ObjectEnd(&object); return stream; } template inline Stream& operator<<(Stream& stream, const T& object) { U8 version(object.GetSerializeVersion()); stream.ObjectStart(&object); stream.SerializeVersion(version); ((T&)object).Serialize(version, stream); stream.ObjectEnd(&object); return stream; } template inline Stream& operator<<(Stream& stream, std::shared_ptr& object) { if (stream.IsInput()) { if (!object) { object = std::make_shared(); } } else { DT_ASSERT(object); // nullptr not supported } U8 version(object->GetSerializeVersion()); stream.ObjectStart(object.get()); stream.SerializeVersion(version); object->Serialize(version, stream); stream.ObjectEnd(object.get()); return stream; } template inline Stream& operator<<(Stream& stream, const std::shared_ptr& object) { DT_ASSERT(object); // nullptr not supported U8 version(object->GetSerializeVersion()); stream.ObjectStart(object.get()); stream.SerializeVersion(version); object->Serialize(version, stream); stream.ObjectEnd(object.get()); return stream; } template inline Stream& operator<<(Stream& stream, T* object) { U8 version(object ? object->GetSerializeVersion() : 1); stream.ObjectStart(object); stream.SerializeVersion(version); if (object) { object->Serialize(version, stream); } stream.ObjectEnd(object); return stream; } template inline Stream& operator<<(Stream& stream, const T*) { // Stream input with pointers is not supported and we do not plan // on adding this. The function is required to compile because // streaming is bi-directional. DT_ASSERT_NOT_IMPLEMENTED(); return stream; } DT_CORE_DLL_ENTRY String& operator<<(String& out, const Serializable& object); }