LCOV - code coverage report
Current view: top level - source/simulation2/serialization - BinarySerializer.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 68 78 87.2 %
Date: 2023-01-19 00:18:29 Functions: 52 92 56.5 %

          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

Generated by: LCOV version 1.13