Integral and enum serialization

This commit is contained in:
York Jasper Niebuhr 2025-10-03 23:13:51 +02:00
parent 55505b6288
commit b629833958
7 changed files with 214 additions and 18 deletions

View File

@ -1,5 +0,0 @@
#pragma once
namespace metadump {
}

View File

@ -0,0 +1,3 @@
#pragma once
#include <metadump/mono.hpp>
#include <metadump/multi.hpp>

131
include/metadump/mono.hpp Normal file
View File

@ -0,0 +1,131 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <concepts>
#include <bit>
#include <cstring>
namespace metadump {
constexpr std::size_t SIZE_DYNAMIC = 0;
template<std::size_t SIZE = SIZE_DYNAMIC>
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<SIZE_DYNAMIC> {
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<typename T>
concept integral = std::is_integral_v<T>;
template<typename T>
concept enumeration = std::is_enum_v<T>;
/*
template<typename I, template<typename...> typename T>
struct is_specialization : std::false_type{};
template<template<typename...> typename T, typename... IArgs>
struct is_specialization<T<IArgs...>, T> : std::true_type{};
template<typename T>
concept is_serializable = std::is_base_of_v<Serializable, T>;
is_(dynamic/fixed)_serializable
template<typename T>
concept is_container = is_specialization<T, std::vector>::value; // list, ...
is_(dynamic/fixed)_container
template<typename T>
concept is_forced_default = is_specialization<T, SerialForcedDefault>::value;
template<typename T>
concept is_default = (!serial_integral<T> && !serial_enum<T> && !serial_serializable<T> &&
!serial_container<T> && !serial_forced_default<T>);
default -> reflection to serialize all public members
*/
}
template<typename T>
struct serializer;
// Integral values -> convert to LITTLE endian
template<typeswitch::integral T>
struct serializer<T> {
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<T>(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<T>(tmp);
else
return false;
return true;
}
};
// Enum serialization -> LITTLE endian integer
template<typeswitch::enumeration T>
struct serializer<T> {
static constexpr bool SERIAL_SIZE_DYNAMIC = false;
using INTEGRAL_TYPE = std::underlying_type_t<T>;
static constexpr std::size_t serial_size() {
return serializer<INTEGRAL_TYPE>::serial_size();
}
static bool dump(uint8_t* data, const T& v) {
const INTEGRAL_TYPE& i = reinterpret_cast<const INTEGRAL_TYPE&>(v);
return serializer<INTEGRAL_TYPE>::dump(data, i);
}
static bool load(const uint8_t* data, T& v) {
INTEGRAL_TYPE& i = reinterpret_cast<INTEGRAL_TYPE&>(v);
return serializer<INTEGRAL_TYPE>::load(data, i);
}
};
}
}

View File

@ -0,0 +1,10 @@
#pragma once
namespace metadump {
template<typename... Ts>
class buffer {
// iovec array and per-component buffers
};
}

View File

@ -1,2 +1,2 @@
add_executable(metadump_test_simple simple.cpp) add_executable(metadump_mono mono.cpp)
target_link_libraries(metadump_test_simple PRIVATE metadump GTest::GTest) target_link_libraries(metadump_mono PRIVATE metadump GTest::GTest)

68
test/mono.cpp Normal file
View File

@ -0,0 +1,68 @@
#include <gtest/gtest.h>
#include <metadump/metadump.hpp>
template<typename T>
using serializer = metadump::mono::serializer<T>;
TEST(MONO, INTEGRAL) {
uint8_t buf[8];
// Serialization of uint8_t
static_assert(!serializer<uint8_t>::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint8_t)!");
static_assert(serializer<uint8_t>::serial_size() == sizeof(uint8_t), "Integral serialization size is incorrect (uint8_t)!");
uint8_t a8 = 0x01, b8 = 0;
ASSERT_TRUE(serializer<uint8_t>::dump(buf, a8)) << "Failed to dump uint8_t!";
ASSERT_TRUE(serializer<uint8_t>::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<uint16_t>::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint16_t)!");
static_assert(serializer<uint16_t>::serial_size() == sizeof(uint16_t), "Integral serialization size is incorrect (uint16_t)!");
uint16_t a16 = 0x0102, b16 = 0;
ASSERT_TRUE(serializer<uint16_t>::dump(buf, a16)) << "Failed to dump uint16_t!";
ASSERT_TRUE(serializer<uint16_t>::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<uint32_t>::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint32_t)!");
static_assert(serializer<uint32_t>::serial_size() == sizeof(uint32_t), "Integral serialization size is incorrect (uint32_t)!");
uint32_t a32 = 0x01020304, b32 = 0;
ASSERT_TRUE(serializer<uint32_t>::dump(buf, a32)) << "Failed to dump uint32_t!";
ASSERT_TRUE(serializer<uint32_t>::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<uint64_t>::SERIAL_SIZE_DYNAMIC, "Integral serialization size should be fixed (uint64_t)!");
static_assert(serializer<uint64_t>::serial_size() == sizeof(uint64_t), "Integral serialization size is incorrect (uint64_t)!");
uint64_t a64 = 0x0102030405060708ull, b64 = 0;
ASSERT_TRUE(serializer<uint64_t>::dump(buf, a64)) << "Failed to dump uint64_t!";
ASSERT_TRUE(serializer<uint64_t>::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<ENUM>::SERIAL_SIZE_DYNAMIC, "Enum serialization size should be fixed!");
static_assert(serializer<ENUM>::serial_size() == sizeof(uint32_t), "Enum serialization size does not match internal type!");
ENUM a = ENUM::D, b = ENUM::A;
ASSERT_TRUE(serializer<ENUM>::dump(buf, a)) << "Failed to dump enum!";
ASSERT_TRUE(serializer<ENUM>::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();
}

View File

@ -1,11 +0,0 @@
#include <gtest/gtest.h>
#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();
}