From b62983395892ab3f97e443cb1a41a2f8e9047b2c Mon Sep 17 00:00:00 2001 From: York Jasper Niebuhr Date: Fri, 3 Oct 2025 23:13:51 +0200 Subject: [PATCH] Integral and enum serialization --- include/metadump.hpp | 5 -- include/metadump/metadump.hpp | 3 + include/metadump/mono.hpp | 131 ++++++++++++++++++++++++++++++++++ include/metadump/multi.hpp | 10 +++ test/CMakeLists.txt | 4 +- test/mono.cpp | 68 ++++++++++++++++++ test/simple.cpp | 11 --- 7 files changed, 214 insertions(+), 18 deletions(-) delete mode 100644 include/metadump.hpp create mode 100644 include/metadump/metadump.hpp create mode 100644 include/metadump/mono.hpp create mode 100644 include/metadump/multi.hpp create mode 100644 test/mono.cpp delete mode 100644 test/simple.cpp diff --git a/include/metadump.hpp b/include/metadump.hpp deleted file mode 100644 index 26694bd..0000000 --- a/include/metadump.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -namespace metadump { - -} diff --git a/include/metadump/metadump.hpp b/include/metadump/metadump.hpp new file mode 100644 index 0000000..02a78c0 --- /dev/null +++ b/include/metadump/metadump.hpp @@ -0,0 +1,3 @@ +#pragma once +#include +#include diff --git a/include/metadump/mono.hpp b/include/metadump/mono.hpp new file mode 100644 index 0000000..71b7693 --- /dev/null +++ b/include/metadump/mono.hpp @@ -0,0 +1,131 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace metadump { + +constexpr std::size_t SIZE_DYNAMIC = 0; + +template +struct serializable { + constexpr std::size_t serial_size() const { return SIZE; }; + virtual bool serial_dump(uint8_t* data) const = 0; + virtual bool serial_load(const uint8_t* data) = 0; +}; + +template<> +struct serializable { + virtual std::size_t serial_size() const = 0; + virtual bool serial_dump(uint8_t* data, std::size_t n) const = 0; + virtual bool serial_load(const uint8_t* data, std::size_t n) = 0; +}; + +namespace mono { + +// Concepts to determine serialization method + +namespace typeswitch { + +template +concept integral = std::is_integral_v; + +template +concept enumeration = std::is_enum_v; + +/* +template typename T> +struct is_specialization : std::false_type{}; + +template typename T, typename... IArgs> +struct is_specialization, T> : std::true_type{}; + +template +concept is_serializable = std::is_base_of_v; + +is_(dynamic/fixed)_serializable + +template +concept is_container = is_specialization::value; // list, ... + +is_(dynamic/fixed)_container + +template +concept is_forced_default = is_specialization::value; + +template +concept is_default = (!serial_integral && !serial_enum && !serial_serializable && + !serial_container && !serial_forced_default); + +default -> reflection to serialize all public members +*/ + +} + +template +struct serializer; + +// Integral values -> convert to LITTLE endian +template +struct serializer { + static constexpr bool SERIAL_SIZE_DYNAMIC = false; + + static constexpr std::size_t serial_size() { + return sizeof(T); + } + + static bool dump(uint8_t* data, const T& v) { + T tmp; // Little endian + if constexpr (std::endian::native == std::endian::little) + tmp = v; + else if constexpr (std::endian::native == std::endian::big) + tmp = std::byteswap(v); + else + return false; + + std::memcpy(data, &tmp, sizeof(T)); + return true; + } + + static bool load(const uint8_t* data, T& v) { + T tmp; // Little endian + std::memcpy(&tmp, data, sizeof(T)); + + if constexpr (std::endian::native == std::endian::little) + v = tmp; + else if constexpr (std::endian::native == std::endian::big) + v = std::byteswap(tmp); + else + return false; + + return true; + } +}; + +// Enum serialization -> LITTLE endian integer +template +struct serializer { + static constexpr bool SERIAL_SIZE_DYNAMIC = false; + using INTEGRAL_TYPE = std::underlying_type_t; + + static constexpr std::size_t serial_size() { + return serializer::serial_size(); + } + + static bool dump(uint8_t* data, const T& v) { + const INTEGRAL_TYPE& i = reinterpret_cast(v); + return serializer::dump(data, i); + } + + static bool load(const uint8_t* data, T& v) { + INTEGRAL_TYPE& i = reinterpret_cast(v); + return serializer::load(data, i); + } +}; + +} + +} diff --git a/include/metadump/multi.hpp b/include/metadump/multi.hpp new file mode 100644 index 0000000..d9a4936 --- /dev/null +++ b/include/metadump/multi.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace metadump { + +template +class buffer { + // iovec array and per-component buffers +}; + +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4294db6..576eac9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,2 +1,2 @@ -add_executable(metadump_test_simple simple.cpp) -target_link_libraries(metadump_test_simple PRIVATE metadump GTest::GTest) +add_executable(metadump_mono mono.cpp) +target_link_libraries(metadump_mono PRIVATE metadump GTest::GTest) diff --git a/test/mono.cpp b/test/mono.cpp new file mode 100644 index 0000000..9ba66fd --- /dev/null +++ b/test/mono.cpp @@ -0,0 +1,68 @@ +#include +#include + +template +using serializer = metadump::mono::serializer; + +TEST(MONO, INTEGRAL) { + uint8_t buf[8]; + + // Serialization of uint8_t + + static_assert(!serializer::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint8_t)!"); + static_assert(serializer::serial_size() == sizeof(uint8_t), "Integral serialization size is incorrect (uint8_t)!"); + uint8_t a8 = 0x01, b8 = 0; + + ASSERT_TRUE(serializer::dump(buf, a8)) << "Failed to dump uint8_t!"; + ASSERT_TRUE(serializer::load(buf, b8)) << "Failed to load uint8_t!"; + ASSERT_EQ(a8, b8) << "Deserialized uint8_t does not match serialization input!"; + + // Serialization of uint16_t + + static_assert(!serializer::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint16_t)!"); + static_assert(serializer::serial_size() == sizeof(uint16_t), "Integral serialization size is incorrect (uint16_t)!"); + uint16_t a16 = 0x0102, b16 = 0; + + ASSERT_TRUE(serializer::dump(buf, a16)) << "Failed to dump uint16_t!"; + ASSERT_TRUE(serializer::load(buf, b16)) << "Failed to load uint16_t!"; + ASSERT_EQ(a16, b16) << "Deserialized uint16_t does not match serialization input!"; + + // Serialization of uint32_t + + static_assert(!serializer::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint32_t)!"); + static_assert(serializer::serial_size() == sizeof(uint32_t), "Integral serialization size is incorrect (uint32_t)!"); + uint32_t a32 = 0x01020304, b32 = 0; + + ASSERT_TRUE(serializer::dump(buf, a32)) << "Failed to dump uint32_t!"; + ASSERT_TRUE(serializer::load(buf, b32)) << "Failed to load uint32_t!"; + ASSERT_EQ(a32, b32) << "Deserialized uint32_t does not match serialization input!"; + + // Serialization of uint64_t + + static_assert(!serializer::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint64_t)!"); + static_assert(serializer::serial_size() == sizeof(uint64_t), "Integral serialization size is incorrect (uint64_t)!"); + uint64_t a64 = 0x0102030405060708ull, b64 = 0; + + ASSERT_TRUE(serializer::dump(buf, a64)) << "Failed to dump uint64_t!"; + ASSERT_TRUE(serializer::load(buf, b64)) << "Failed to load uint64_t!"; + ASSERT_EQ(a64, b64) << "Deserialized uint64_t does not match serialization input!"; +} + +TEST(MONO, ENUM) { + uint8_t buf[4]; + enum class ENUM : uint32_t { A, B, C, D, E }; + + static_assert(!serializer::SERIAL_SIZE_DYNAMIC, "Enum serialization size should be fixed!"); + static_assert(serializer::serial_size() == sizeof(uint32_t), "Enum serialization size does not match internal type!"); + + ENUM a = ENUM::D, b = ENUM::A; + + ASSERT_TRUE(serializer::dump(buf, a)) << "Failed to dump enum!"; + ASSERT_TRUE(serializer::load(buf, b)) << "Failed to load enum!"; + ASSERT_EQ(a, b) << "Deserialized enum does not match serialization input!"; +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/simple.cpp b/test/simple.cpp deleted file mode 100644 index 135ffac..0000000 --- a/test/simple.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include "metadump.hpp" - -TEST(METADUMP_SIMPLE, EMPTY) { - ASSERT_TRUE(false) << "Tests not implemented!"; -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}