#pragma once #include #include #include #include #include #include #include #include #include typedef boost::coroutines2::coroutine coro_t; // Serialization, similar to Boost Serialization // but for portable binary archive // using big endian coding (network byte order) namespace Serialization { class OArchive { public: OArchive(std::ostream& os): os(os) {} ~OArchive() {} template OArchive& operator &(T& v) { v.serialize(*this); return *this; }; template OArchive& write_fundamental(T& v) { T value = boost::endian::native_to_big(v); os.write((char*)&value, sizeof(value)); return *this; } OArchive& operator &(uint8_t& v) { return write_fundamental(v); }; OArchive& operator &(uint16_t& v) { return write_fundamental(v); }; OArchive& operator &(uint32_t& v) { return write_fundamental(v); }; OArchive& operator &(uint64_t& v) { return write_fundamental(v); }; OArchive& operator &(int64_t& v) { return write_fundamental(*reinterpret_cast(v)); }; OArchive& operator &(std::vector& v) { uint32_t size = static_cast(v.size()); *this & size; os.write((char*)v.data(), size); return *this; }; OArchive& operator &(std::string& v) { uint32_t size = static_cast(v.size()); *this & size; os.write((char*)v.data(), v.size()); return *this; }; private: std::ostream &os; }; class IArchive { public: IArchive(std::istream& is): is(is) {} IArchive(std::stringstream& is, coro_t::pull_type& coro) : is(is), mStringStream(&is), mCoro(&coro) {} ~IArchive() {} template IArchive& operator &(T& v) { v.serialize(*this); return *this; }; template IArchive& read_fundamental(T& v) { // in coroutine case, wait for input, if necessary if (mCoro && mStringStream) { while (true) { auto pindex {mStringStream->tellp()}; auto gindex {mStringStream->tellg()}; if (pindex != std::stringstream::pos_type(-1) && gindex != std::stringstream::pos_type(-1) && pindex > gindex) { if (static_cast(pindex - gindex) < sizeof(v)) (*mCoro)(); else break; } else { std::cerr << "Error: read_fundamental: Bad stringstream indices: " << pindex << ", " << gindex << std::endl; break; } } } // now, we have enough bytes available T value; is.read((char*)&value, sizeof(value)); v = boost::endian::big_to_native(value); return *this; } IArchive& operator &(uint8_t& v) { return read_fundamental(v); }; IArchive& operator &(uint16_t& v) { return read_fundamental(v); }; IArchive& operator &(uint32_t& v) { return read_fundamental(v); }; IArchive& operator &(uint64_t& v) { return read_fundamental(v); }; IArchive& operator &(int64_t& v) { uint64_t uv; read_fundamental(uv); v = *reinterpret_cast(uv); return *this; }; template IArchive& read_bytes_vector(T& v) { uint32_t size; *this & size; v.resize(size); // in coroutine case, wait for input, if necessary if (mCoro && mStringStream) { while (true) { auto pindex {mStringStream->tellp()}; auto gindex {mStringStream->tellg()}; if (pindex != std::stringstream::pos_type(-1) && gindex != std::stringstream::pos_type(-1) && pindex > gindex) { if (static_cast(pindex - gindex) < size) (*mCoro)(); else break; } else { std::cerr << "Error: read_bytes_vector: Bad stringstream indices: " << pindex << ", " << gindex << std::endl; break; } } } // now, we have enough bytes available is.read((char*)v.data(), size); return *this; } IArchive& operator &(std::vector& v) { return read_bytes_vector(v); }; IArchive& operator &(std::string& v) { return read_bytes_vector(v); }; private: std::istream &is; std::stringstream* mStringStream{ }; // for i/o sizes access coro_t::pull_type* mCoro{ }; // optional for coroutine }; // - Free functions ---------------------------------------------------------- template void serialize(Archive& ar, T& v) { ar & v; } template OArchive& operator <<(OArchive& ar, T& v) { serialize(ar, v); return ar; }; template IArchive& operator >>(IArchive& ar, T& v) { serialize(ar, v); return ar; }; }