diff options
| -rw-r--r-- | archive.h | 227 | ||||
| -rw-r--r-- | common.mk | 1 | ||||
| -rw-r--r-- | debian/libreichwein-dev.install | 1 | ||||
| -rw-r--r-- | tests/Makefile | 1 | ||||
| -rw-r--r-- | tests/test-archive.cpp | 197 | 
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; +}; + +} @@ -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); +} + | 
