Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #ifndef INCLUDED_BINARYSERIALIZER
19 : #define INCLUDED_BINARYSERIALIZER
20 :
21 : #include "ISerializer.h"
22 :
23 : #include "lib/byte_order.h"
24 :
25 : #include "scriptinterface/ScriptExtraHeaders.h"
26 :
27 : #include <ostream>
28 : #include <streambuf>
29 :
30 : /**
31 : * Wrapper for redirecting ostream writes to CBinarySerializer's impl
32 : */
33 : template<typename T>
34 120 : class CSerializerStreamBuf : public std::streambuf
35 : {
36 : NONCOPYABLE(CSerializerStreamBuf);
37 : T& m_SerializerImpl;
38 : char m_Buffer[2048];
39 : public:
40 120 : CSerializerStreamBuf(T& impl) :
41 120 : m_SerializerImpl(impl)
42 : {
43 120 : setp(m_Buffer, m_Buffer + ARRAY_SIZE(m_Buffer) - 1);
44 120 : }
45 :
46 : protected:
47 : // Override overflow and sync, because older versions of libc++ streams
48 : // write strings as individual characters, then xsputn is never called
49 0 : int overflow(int ch)
50 : {
51 0 : if (ch == traits_type::eof())
52 0 : return traits_type::not_eof(ch);
53 :
54 0 : ENSURE(pptr() <= epptr());
55 0 : *pptr() = ch;
56 0 : pbump(1);
57 0 : sync();
58 0 : return ch;
59 : }
60 :
61 1 : int sync()
62 : {
63 1 : std::ptrdiff_t n = pptr() - pbase();
64 1 : if (n != 0)
65 : {
66 0 : pbump(-n);
67 0 : m_SerializerImpl.Put("stream", reinterpret_cast<const u8*> (pbase()), n);
68 : }
69 1 : return 0;
70 : }
71 :
72 3 : std::streamsize xsputn(const char* s, std::streamsize n)
73 : {
74 3 : m_SerializerImpl.Put("stream", reinterpret_cast<const u8*> (s), n);
75 3 : return n;
76 : }
77 : };
78 :
79 : /**
80 : * PutScriptVal implementation details.
81 : * (Split out from the main class because it's too big to be inlined.)
82 : */
83 : class CBinarySerializerScriptImpl
84 : {
85 : public:
86 : CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer);
87 : ~CBinarySerializerScriptImpl();
88 :
89 : void ScriptString(const char* name, JS::HandleString string);
90 : void HandleScriptVal(JS::HandleValue val);
91 : private:
92 : static void Trace(JSTracer* trc, void* data);
93 :
94 : const ScriptInterface& m_ScriptInterface;
95 : ISerializer& m_Serializer;
96 :
97 : using ObjectTagMap = JS::GCHashMap<JS::Heap<JSObject*>, u32, js::MovableCellHasher<JSObject*>, js::SystemAllocPolicy>;
98 : ObjectTagMap m_ScriptBackrefTags;
99 : u32 m_ScriptBackrefsNext;
100 : u32 GetScriptBackrefTag(JS::HandleObject obj);
101 : };
102 :
103 : /**
104 : * Serialize to a binary stream. T must just implement the Put() method.
105 : * (We use this templated approach to allow compiler inlining.)
106 : */
107 : template <typename T>
108 120 : class CBinarySerializer : public ISerializer
109 : {
110 : NONCOPYABLE(CBinarySerializer);
111 : public:
112 15 : CBinarySerializer(const ScriptInterface& scriptInterface) :
113 15 : m_ScriptImpl(new CBinarySerializerScriptImpl(scriptInterface, *this)),
114 : m_RawStreamBuf(m_Impl),
115 30 : m_RawStream(&m_RawStreamBuf)
116 : {
117 15 : }
118 :
119 : template <typename A>
120 105 : CBinarySerializer(const ScriptInterface& scriptInterface, A& a) :
121 105 : m_ScriptImpl(new CBinarySerializerScriptImpl(scriptInterface, *this)),
122 : m_Impl(a),
123 : m_RawStreamBuf(m_Impl),
124 210 : m_RawStream(&m_RawStreamBuf)
125 : {
126 105 : }
127 :
128 : protected:
129 : /*
130 : The Put* implementations here are designed for subclasses
131 : that want an efficient, portable, deserializable representation.
132 : (Subclasses with different requirements should override these methods.)
133 :
134 : Numbers are converted to little-endian byte strings, for portability
135 : and efficiency.
136 :
137 : Data is not aligned, for storage efficiency.
138 : */
139 :
140 641 : virtual void PutNumber(const char* name, uint8_t value)
141 : {
142 641 : m_Impl.Put(name, (const u8*)&value, sizeof(uint8_t));
143 641 : }
144 :
145 1 : virtual void PutNumber(const char* name, int8_t value)
146 : {
147 1 : m_Impl.Put(name, (const u8*)&value, sizeof(int8_t));
148 1 : }
149 :
150 1 : virtual void PutNumber(const char* name, uint16_t value)
151 : {
152 1 : uint16_t v = to_le16(value);
153 1 : m_Impl.Put(name, (const u8*)&v, sizeof(uint16_t));
154 1 : }
155 :
156 1 : virtual void PutNumber(const char* name, int16_t value)
157 : {
158 1 : int16_t v = (i16)to_le16((u16)value);
159 1 : m_Impl.Put(name, (const u8*)&v, sizeof(int16_t));
160 1 : }
161 :
162 560 : virtual void PutNumber(const char* name, uint32_t value)
163 : {
164 560 : uint32_t v = to_le32(value);
165 560 : m_Impl.Put(name, (const u8*)&v, sizeof(uint32_t));
166 560 : }
167 :
168 139 : virtual void PutNumber(const char* name, int32_t value)
169 : {
170 139 : int32_t v = (i32)to_le32((u32)value);
171 139 : m_Impl.Put(name, (const u8*)&v, sizeof(int32_t));
172 139 : }
173 :
174 1 : virtual void PutNumber(const char* name, float value)
175 : {
176 1 : m_Impl.Put(name, (const u8*)&value, sizeof(float));
177 1 : }
178 :
179 19 : virtual void PutNumber(const char* name, double value)
180 : {
181 19 : m_Impl.Put(name, (const u8*)&value, sizeof(double));
182 19 : }
183 :
184 109 : virtual void PutNumber(const char* name, fixed value)
185 : {
186 109 : int32_t v = (i32)to_le32((u32)value.GetInternalValue());
187 109 : m_Impl.Put(name, (const u8*)&v, sizeof(int32_t));
188 109 : }
189 :
190 251 : virtual void PutBool(const char* name, bool value)
191 : {
192 251 : NumberU8(name, value ? 1 : 0, 0, 1);
193 251 : }
194 :
195 36 : virtual void PutString(const char* name, const std::string& value)
196 : {
197 : // TODO: maybe should intern strings, particularly to save space with script property names
198 36 : PutNumber("string length", (uint32_t)value.length());
199 36 : m_Impl.Put(name, (u8*)value.data(), value.length());
200 36 : }
201 :
202 109 : virtual void PutScriptVal(const char* UNUSED(name), JS::MutableHandleValue value)
203 : {
204 112 : m_ScriptImpl->HandleScriptVal(value);
205 106 : }
206 :
207 234 : virtual void PutRaw(const char* name, const u8* data, size_t len)
208 : {
209 234 : m_Impl.Put(name, data, len);
210 234 : }
211 :
212 1 : virtual std::ostream& GetStream()
213 : {
214 1 : return m_RawStream;
215 : }
216 :
217 : protected:
218 : T m_Impl;
219 :
220 : private:
221 : std::unique_ptr<CBinarySerializerScriptImpl> m_ScriptImpl;
222 :
223 : CSerializerStreamBuf<T> m_RawStreamBuf;
224 : std::ostream m_RawStream;
225 : };
226 :
227 : #endif // INCLUDED_BINARYSERIALIZER
|