0ad/source/simulation2/serialization/BinarySerializer.h
2015-10-15 03:31:30 +00:00

235 lines
6.0 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*/
#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 <map>
/**
* Wrapper for redirecting ostream writes to CBinarySerializer's impl
*/
template<typename T>
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<const u8*> (pbase()), n);
}
return 0;
}
std::streamsize xsputn(const char* s, std::streamsize n)
{
m_SerializerImpl.Put("stream", reinterpret_cast<const u8*> (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<ObjectIdCache<std::wstring> > prototypes);
private:
ScriptInterface& m_ScriptInterface;
ISerializer& m_Serializer;
ObjectIdCache<u32> m_ScriptBackrefs;
u32 m_ScriptBackrefsNext;
u32 GetScriptBackrefTag(JS::HandleObject obj);
shared_ptr<ObjectIdCache<std::wstring> > 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 <typename T>
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 <typename A>
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<ObjectIdCache<std::wstring> >& 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<CBinarySerializerScriptImpl> m_ScriptImpl;
CSerializerStreamBuf<T> m_RawStreamBuf;
std::ostream m_RawStream;
};
#endif // INCLUDED_BINARYSERIALIZER