From be14202a85fe50352f8bb3bdae1b492a1d1760f8 Mon Sep 17 00:00:00 2001 From: StarQTius Date: Fri, 17 May 2024 00:04:39 +0200 Subject: [PATCH] Implement basic_obytestream --- include/upd/basic_obytestream.hpp | 69 ++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/basic_ibytestream.cpp | 15 +------ test/basic_obytestream.cpp | 60 ++++++++++++++++++++++++++ test/mock/serializer_interface.hpp | 17 ++++++++ 5 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 include/upd/basic_obytestream.hpp create mode 100644 test/basic_obytestream.cpp create mode 100644 test/mock/serializer_interface.hpp diff --git a/include/upd/basic_obytestream.hpp b/include/upd/basic_obytestream.hpp new file mode 100644 index 0000000..743fe12 --- /dev/null +++ b/include/upd/basic_obytestream.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include // IWYU pragma: keep +#include +#include + +#include "upd/detail/always_false.hpp" +#include "upd/detail/is_bounded_array.hpp" // IWYU pragma: keep +#include "upd/upd.hpp" + +namespace upd { + +template +class basic_obytestream { +public: + basic_obytestream(Consumer_T consumer, Serializer_T serializer) noexcept + : m_consumer{UPD_FWD(consumer)}, m_serializer{UPD_FWD(serializer)} {} + + template + void encode(const Args &...args) { + if constexpr (sizeof...(args) > 1) { + (encode(UPD_FWD(args)), ...); + } else { + encode_one(UPD_FWD(args)...); + } + } + +private: + template + void encode_one(const T &x) { + if constexpr (std::is_signed_v) { + encode_signed(UPD_FWD(x)); + } else if constexpr (std::is_unsigned_v) { + encode_unsigned(UPD_FWD(x)); + } else if constexpr (detail::is_bounded_array_v) { + encode_bounded_array(UPD_FWD(x)); + } else { + static_assert(UPD_ALWAYS_FALSE, "`T` cannot be serialized"); + } + } + + template + void encode_signed(const T &value) { + auto buf = std::array{}; + + m_serializer.serialize_signed(UPD_FWD(value), buf.size(), buf.data()); + m_consumer = std::copy(buf.begin(), buf.end(), m_consumer); + }; + + template + void encode_unsigned(const T &value) { + auto buf = std::array{}; + + m_serializer.serialize_unsigned(UPD_FWD(value), buf.size(), buf.data()); + m_consumer = std::copy(buf.begin(), buf.end(), m_consumer); + }; + + template + void encode_bounded_array(const T &array) { + for (const auto &value : array) { + encode_one(value); + } + }; + + Consumer_T m_consumer; + Serializer_T m_serializer; +}; + +} // namespace upd diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ebcd4a2..8ed8a11 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -88,6 +88,7 @@ add_custom_target(check_revamp COMMAND ctest -L check_revamp add_revamp_test(basic_tuple basic_tuple.cpp) add_revamp_test(basic_ibytestream basic_ibytestream.cpp) +add_revamp_test(basic_obytestream basic_obytestream.cpp) add_revamp_test(detail_variadic_clean detail/variadic/clean.cpp) add_revamp_test(detail_variadic_clip detail/variadic/clip.cpp) add_revamp_test(literals literals.cpp) diff --git a/test/basic_ibytestream.cpp b/test/basic_ibytestream.cpp index d80275f..780756a 100644 --- a/test/basic_ibytestream.cpp +++ b/test/basic_ibytestream.cpp @@ -9,6 +9,7 @@ #include #include +#include "mock/serializer_interface.hpp" #include "utility/generators.hpp" #include "utility/mocking.hpp" #include @@ -23,20 +24,6 @@ using namespace fakeit; -struct serializer_interface { - serializer_interface(const serializer_interface &) = delete; - auto operator=(const serializer_interface &) -> serializer_interface & = delete; - - serializer_interface(serializer_interface &&) = delete; - auto operator=(serializer_interface &&) -> serializer_interface & = delete; - - virtual void serialize_unsigned(std::uintmax_t value, std::size_t size, std::byte *output) = 0; - virtual void serialize_signed(std::intmax_t value, std::size_t size, std::byte *output) = 0; - virtual auto deserialize_unsigned(const std::byte *input, std::size_t size) -> std::uintmax_t = 0; - virtual auto deserialize_signed(const std::byte *input, std::size_t size) -> std::intmax_t = 0; - virtual ~serializer_interface() = default; -}; - // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) TEST_CASE("Deserializing a packet...") { diff --git a/test/basic_obytestream.cpp b/test/basic_obytestream.cpp new file mode 100644 index 0000000..082450a --- /dev/null +++ b/test/basic_obytestream.cpp @@ -0,0 +1,60 @@ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UPD_ASSERT(...) \ + if (!(__VA_ARGS__)) { \ + throw std::exception{}; \ + } + +#include +#include +#include +#include +#include + +#include "mock/serializer_interface.hpp" +#include "utility/generators.hpp" +#include "utility/mocking.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace fakeit; + +// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers) + +TEST_CASE("Serializing a packet...") { + auto storage = std::vector{}; + auto mock_serializer = Mock{}; + + auto consumer = std::back_inserter(storage); + auto &serializer = mock_serializer.get(); + auto bstream = upd::basic_obytestream{consumer, serializer}; + + When(Method(mock_serializer, serialize_signed)).AlwaysReturn(); + When(Method(mock_serializer, serialize_unsigned)).AlwaysReturn(); + + SECTION("...containing integers and array of integer") { + int int_array[] = {-1, 2, -3, 4}; + unsigned long ulong_array[] = {100, 200}; + bstream.encode(int{-64}, (unsigned int)48, int_array, (unsigned short)16, ulong_array, char{1}); + + Verify(Method(mock_serializer, serialize_signed).Using(-64, sizeof(int), _), + Method(mock_serializer, serialize_unsigned).Using(48, sizeof(unsigned int), _), + Method(mock_serializer, serialize_signed).Using(-1, sizeof(int), _), + Method(mock_serializer, serialize_signed).Using(2, sizeof(int), _), + Method(mock_serializer, serialize_signed).Using(-3, sizeof(int), _), + Method(mock_serializer, serialize_signed).Using(4, sizeof(int), _), + Method(mock_serializer, serialize_unsigned).Using(16, sizeof(unsigned short), _), + Method(mock_serializer, serialize_unsigned).Using(100, sizeof(unsigned long), _), + Method(mock_serializer, serialize_unsigned).Using(200, sizeof(unsigned long), _), + Method(mock_serializer, serialize_signed).Using(1, sizeof(char), _)); + } +} + +// NOLINTEND(cppcoreguidelines-avoid-magic-numbers) diff --git a/test/mock/serializer_interface.hpp b/test/mock/serializer_interface.hpp new file mode 100644 index 0000000..c249d95 --- /dev/null +++ b/test/mock/serializer_interface.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +struct serializer_interface { + serializer_interface(const serializer_interface &) = delete; + auto operator=(const serializer_interface &) -> serializer_interface & = delete; + + serializer_interface(serializer_interface &&) = delete; + auto operator=(serializer_interface &&) -> serializer_interface & = delete; + + virtual void serialize_unsigned(std::uintmax_t value, std::size_t size, std::byte *output) = 0; + virtual void serialize_signed(std::intmax_t value, std::size_t size, std::byte *output) = 0; + virtual auto deserialize_unsigned(const std::byte *input, std::size_t size) -> std::uintmax_t = 0; + virtual auto deserialize_signed(const std::byte *input, std::size_t size) -> std::intmax_t = 0; + virtual ~serializer_interface() = default; +};