summaryrefslogtreecommitdiffhomepage
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
parent7973293c311e27ff08a1488c9759c1b5b0fda30e (diff)
Added archive.h
-rw-r--r--archive.h227
-rw-r--r--common.mk1
-rw-r--r--debian/libreichwein-dev.install1
-rw-r--r--tests/Makefile1
-rw-r--r--tests/test-archive.cpp197
5 files changed, 427 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;
+};
+
+}
diff --git a/common.mk b/common.mk
index aa68033..c8a51f1 100644
--- a/common.mk
+++ b/common.mk
@@ -96,3 +96,4 @@ LIBS+= \
-lstdc++fs
endif
+LIBS+=-lboost_context -lboost_system
diff --git a/debian/libreichwein-dev.install b/debian/libreichwein-dev.install
index 38b90c8..3539ae9 100644
--- a/debian/libreichwein-dev.install
+++ b/debian/libreichwein-dev.install
@@ -1,3 +1,4 @@
+usr/include/libreichwein/archive.h
usr/include/libreichwein/base64.h
usr/include/libreichwein/file.h
usr/include/libreichwein/mime.h
diff --git a/tests/Makefile b/tests/Makefile
index 44f50dc..6ae6f12 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -21,6 +21,7 @@ UNITS=\
url.cpp
UNITTESTS=\
+ test-archive.cpp \
test-base64.cpp \
test-file.cpp \
test-mime.cpp \
diff --git a/tests/test-archive.cpp b/tests/test-archive.cpp
new file mode 100644
index 0000000..2ffa2cd
--- /dev/null
+++ b/tests/test-archive.cpp
@@ -0,0 +1,197 @@
+#include <gtest/gtest.h>
+
+#include <sstream>
+#include <string>
+
+#include "archive.h"
+
+using namespace std::string_literals;
+
+class ArchiveTest: public ::testing::Test
+{
+protected:
+ ArchiveTest(){}
+ ~ArchiveTest() override{}
+ void SetUp() override{}
+ void TearDown() override{}
+};
+
+struct MyStruct
+{
+ uint8_t member1;
+ uint16_t member2;
+ uint32_t member3;
+ uint64_t member4;
+ int64_t member5;
+ std::vector<uint8_t> member6;
+ std::string member7;
+
+ template <class Archive>
+ void serialize (Archive& ar)
+ {
+ ar & member1;
+ ar & member2;
+ ar & member3;
+ ar & member4;
+ ar & member5;
+ ar & member6;
+ ar & member7;
+ }
+
+};
+
+bool operator==(const MyStruct& lhs, const MyStruct& rhs)
+{
+ return lhs.member1 == rhs.member1 &&
+ lhs.member2 == rhs.member2 &&
+ lhs.member3 == rhs.member3 &&
+ lhs.member4 == rhs.member4 &&
+ lhs.member5 == rhs.member5 &&
+ lhs.member6 == rhs.member6 &&
+ lhs.member7 == rhs.member7;
+}
+
+TEST_F(ArchiveTest, archive)
+{
+ std::stringstream ss;
+ Serialization::OArchive oa(ss);
+ MyStruct mystruct0{123, 45678, 3, 4, 5, {1, 2}, "abc"};
+ oa << mystruct0;
+
+ EXPECT_EQ(ss.str().size(), 36);
+
+ Serialization::IArchive ia(ss);
+ MyStruct mystruct1{};
+ ia >> mystruct1;
+
+ EXPECT_EQ(mystruct0, mystruct1);
+}
+
+TEST_F(ArchiveTest, out)
+{
+ std::stringstream ss(std::ios_base::in|std::ios_base::out|std::ios_base::binary);
+
+ Serialization::OArchive oarchive(ss);
+
+ uint64_t v1(123456789);
+ oarchive << v1;
+
+ ASSERT_EQ(ss.str().size(), sizeof(uint64_t));
+
+ uint32_t v2(87654321);
+ oarchive << v2;
+
+ ASSERT_EQ(ss.str().size(), sizeof(uint64_t) + sizeof(uint32_t));
+
+ uint16_t v3(9876);
+ oarchive << v3;
+
+ ASSERT_EQ(ss.str().size(), sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint16_t));
+
+ uint8_t v4(2);
+ oarchive << v4;
+
+ ASSERT_EQ(ss.str().size(), sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t));
+
+ std::string v5("abcd");
+ oarchive << v5;
+
+ ASSERT_EQ(ss.str().size(), sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t) + 4+4);
+
+ std::vector<uint8_t> v6 = {1, 2, 3, 4, 5};
+ oarchive << v6;
+
+ ASSERT_EQ(ss.str().size(), sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint8_t) + 4+4 + 4+5);
+
+ ASSERT_EQ(ss.str(), std::string("\x00\x00\x00\x00\x07\x5b\xcd\x15\x05\x39\x7f\xb1\x26\x94\x02" "\x00\x00\x00\x04" "abcd\x00\x00\x00\x05\x01\x02\x03\x04\x05", 32));
+}
+
+TEST_F(ArchiveTest, in)
+{
+ std::stringstream ss(std::ios_base::in|std::ios_base::out|std::ios_base::binary);
+
+ std::string v1("\x00\x00\x00\x00\x07\x5b\xcd\x15\x05\x39\x7f\xb1\x26\x94\x02" "\x00\x00\x00\x04" "abcd\x00\x00\x00\x05\x01\x02\x03\x04\x05", 32);
+ ss.write(v1.data(), v1.size());
+
+ Serialization::IArchive iarchive(ss);
+
+ ASSERT_EQ(ss.str().size(), 32);
+
+ uint64_t x1;
+ iarchive >> x1;
+
+ ASSERT_EQ(x1, 123456789);
+
+ uint32_t x2;
+ iarchive >> x2;
+
+ ASSERT_EQ(x2, 87654321);
+
+ uint16_t x3;
+ iarchive >> x3;
+
+ ASSERT_EQ(x3, 9876);
+
+ uint8_t x4;
+ iarchive >> x4;
+
+ ASSERT_EQ(x4, 2);
+
+ std::string x5;
+ iarchive >> x5;
+ EXPECT_EQ(x5, "abcd");
+
+ std::vector<uint8_t> x6;
+ iarchive >> x6;
+
+ std::vector<uint8_t> v{1, 2, 3, 4, 5};
+ ASSERT_EQ(x6, v);
+}
+
+TEST_F(ArchiveTest, in_coro)
+{
+ std::stringstream ss(std::ios_base::in|std::ios_base::out|std::ios_base::binary);
+
+ coro_t::pull_type coro([&](coro_t::push_type& sink)
+ {
+ sink();
+ sink();
+ std::string v1("\x00\x00\x00\x00\x07\x5b\xcd\x15\x05\x39\x7f\xb1\x26\x94\x02" "\x00\x00\x00\x04" "abcd\x00\x00\x00\x05\x01\x02\x03\x04\x05", 32);
+ for (int i = 0; i < v1.size(); i++) {
+ ss.write(v1.data() + i, 1);
+ sink();
+ }
+ sink();
+ });
+ Serialization::IArchive iarchive(ss, coro);
+
+ uint64_t x1;
+ iarchive >> x1;
+
+ ASSERT_EQ(x1, 123456789);
+
+ uint32_t x2;
+ iarchive >> x2;
+
+ ASSERT_EQ(x2, 87654321);
+
+ uint16_t x3;
+ iarchive >> x3;
+
+ ASSERT_EQ(x3, 9876);
+
+ uint8_t x4;
+ iarchive >> x4;
+
+ ASSERT_EQ(x4, 2);
+
+ std::string x5;
+ iarchive >> x5;
+ EXPECT_EQ(x5, "abcd");
+
+ std::vector<uint8_t> x6;
+ iarchive >> x6;
+ std::vector<uint8_t> v{1, 2, 3, 4, 5};
+ ASSERT_EQ(x6, v);
+}
+