/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_BINARYSERIALIZER #define INCLUDED_BINARYSERIALIZER #include "ISerializer.h" #include "scriptinterface/third_party/ObjectToIDMap.h" #include "lib/byte_order.h" #include "lib/allocators/arena.h" #include /** * Wrapper for redirecting ostream writes to CBinarySerializer's impl */ template class CSerializerStreamBuf : public std::streambuf { NONCOPYABLE(CSerializerStreamBuf); T& m_SerializerImpl; char m_Buffer[2048]; public: CSerializerStreamBuf(T& impl) : m_SerializerImpl(impl) { setp(m_Buffer, m_Buffer + ARRAY_SIZE(m_Buffer) - 1); } protected: // Override overflow and sync, because older versions of libc++ streams // write strings as individual characters, then xsputn is never called int overflow(int ch) { if (ch == traits_type::eof()) return traits_type::not_eof(ch); ENSURE(pptr() <= epptr()); *pptr() = ch; pbump(1); sync(); return ch; } int sync() { std::ptrdiff_t n = pptr() - pbase(); if (n != 0) { pbump(-n); m_SerializerImpl.Put("stream", reinterpret_cast (pbase()), n); } return 0; } std::streamsize xsputn(const char* s, std::streamsize n) { m_SerializerImpl.Put("stream", reinterpret_cast (s), n); return n; } }; /** * PutScriptVal implementation details. * (Split out from the main class because it's too big to be inlined.) */ class CBinarySerializerScriptImpl { public: CBinarySerializerScriptImpl(ScriptInterface& scriptInterface, ISerializer& serializer); void ScriptString(const char* name, JS::HandleString string); void HandleScriptVal(JS::HandleValue val); void SetSerializablePrototypes(shared_ptr > prototypes); private: ScriptInterface& m_ScriptInterface; ISerializer& m_Serializer; ObjectIdCache m_ScriptBackrefs; u32 m_ScriptBackrefsNext; u32 GetScriptBackrefTag(JS::HandleObject obj); shared_ptr > m_SerializablePrototypes; bool IsSerializablePrototype(JS::HandleObject prototype); std::wstring GetPrototypeName(JS::HandleObject prototype); }; /** * Serialize to a binary stream. T must just implement the Put() method. * (We use this templated approach to allow compiler inlining.) */ template class CBinarySerializer : public ISerializer { NONCOPYABLE(CBinarySerializer); public: CBinarySerializer(ScriptInterface& scriptInterface) : m_ScriptImpl(new CBinarySerializerScriptImpl(scriptInterface, *this)), m_RawStreamBuf(m_Impl), m_RawStream(&m_RawStreamBuf) { } template CBinarySerializer(ScriptInterface& scriptInterface, A& a) : m_ScriptImpl(new CBinarySerializerScriptImpl(scriptInterface, *this)), m_Impl(a), m_RawStreamBuf(m_Impl), m_RawStream(&m_RawStreamBuf) { } virtual void SetSerializablePrototypes(shared_ptr >& prototypes) { m_ScriptImpl->SetSerializablePrototypes(prototypes); } protected: /* The Put* implementations here are designed for subclasses that want an efficient, portable, deserializable representation. (Subclasses with different requirements should override these methods.) Numbers are converted to little-endian byte strings, for portability and efficiency. Data is not aligned, for storage efficiency. */ virtual void PutNumber(const char* name, uint8_t value) { m_Impl.Put(name, (const u8*)&value, sizeof(uint8_t)); } virtual void PutNumber(const char* name, int8_t value) { m_Impl.Put(name, (const u8*)&value, sizeof(int8_t)); } virtual void PutNumber(const char* name, uint16_t value) { uint16_t v = to_le16(value); m_Impl.Put(name, (const u8*)&v, sizeof(uint16_t)); } virtual void PutNumber(const char* name, int16_t value) { int16_t v = (i16)to_le16((u16)value); m_Impl.Put(name, (const u8*)&v, sizeof(int16_t)); } virtual void PutNumber(const char* name, uint32_t value) { uint32_t v = to_le32(value); m_Impl.Put(name, (const u8*)&v, sizeof(uint32_t)); } virtual void PutNumber(const char* name, int32_t value) { int32_t v = (i32)to_le32((u32)value); m_Impl.Put(name, (const u8*)&v, sizeof(int32_t)); } virtual void PutNumber(const char* name, float value) { m_Impl.Put(name, (const u8*)&value, sizeof(float)); } virtual void PutNumber(const char* name, double value) { m_Impl.Put(name, (const u8*)&value, sizeof(double)); } virtual void PutNumber(const char* name, fixed value) { int32_t v = (i32)to_le32((u32)value.GetInternalValue()); m_Impl.Put(name, (const u8*)&v, sizeof(int32_t)); } virtual void PutBool(const char* name, bool value) { NumberU8(name, value ? 1 : 0, 0, 1); } virtual void PutString(const char* name, const std::string& value) { // TODO: maybe should intern strings, particularly to save space with script property names PutNumber("string length", (uint32_t)value.length()); m_Impl.Put(name, (u8*)value.data(), value.length()); } virtual void PutScriptVal(const char* UNUSED(name), JS::MutableHandleValue value) { m_ScriptImpl->HandleScriptVal(value); } virtual void PutRaw(const char* name, const u8* data, size_t len) { m_Impl.Put(name, data, len); } virtual std::ostream& GetStream() { return m_RawStream; } protected: T m_Impl; private: std::unique_ptr m_ScriptImpl; CSerializerStreamBuf m_RawStreamBuf; std::ostream m_RawStream; }; #endif // INCLUDED_BINARYSERIALIZER