#pragma once #include <boost/coroutine2/coroutine.hpp> #include <boost/endian/conversion.hpp> #include <cstdint> #include <ostream> #include <iostream> #include <istream> #include <sstream> #include <string> #include <vector> typedef boost::coroutines2::coroutine<void> 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<class T> OArchive& operator &(T& v) { v.serialize(*this); return *this; }; template <class T> 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<uint64_t*>(v)); }; OArchive& operator &(std::vector<uint8_t>& v) { uint32_t size = static_cast<uint32_t>(v.size()); *this & size; os.write((char*)v.data(), size); return *this; }; OArchive& operator &(std::string& v) { uint32_t size = static_cast<uint32_t>(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<class T> IArchive& operator &(T& v) { v.serialize(*this); return *this; }; template <class T> 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<size_t>(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<int64_t*>(uv); return *this; }; template <class T> 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<uint32_t>(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<uint8_t>& 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<class Archive, class T> void serialize(Archive& ar, T& v) { ar & v; } template<class T> OArchive& operator <<(OArchive& ar, T& v) { serialize(ar, v); return ar; }; template<class T> IArchive& operator >>(IArchive& ar, T& v) { serialize(ar, v); return ar; }; }