summaryrefslogtreecommitdiffhomepage
path: root/archive.h
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2023-01-05 18:10:39 +0100
committerRoland Reichwein <mail@reichwein.it>2023-01-05 18:10:39 +0100
commitcdf2c1132ec9a12ea6d91c8deb1700c1a590c18d (patch)
tree1e792e0de184c2cabf9e923479e224d8b8126edf /archive.h
parent7973293c311e27ff08a1488c9759c1b5b0fda30e (diff)
Added archive.h
Diffstat (limited to 'archive.h')
-rw-r--r--archive.h227
1 files changed, 227 insertions, 0 deletions
diff --git a/archive.h b/archive.h
new file mode 100644
index 0000000..1218505
--- /dev/null
+++ b/archive.h
@@ -0,0 +1,227 @@
+#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(reinterpret_cast<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;
+};
+
+}