Integral and enum serialization
This commit is contained in:
parent
55505b6288
commit
b629833958
@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace metadump {
|
|
||||||
|
|
||||||
}
|
|
||||||
3
include/metadump/metadump.hpp
Normal file
3
include/metadump/metadump.hpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <metadump/mono.hpp>
|
||||||
|
#include <metadump/multi.hpp>
|
||||||
131
include/metadump/mono.hpp
Normal file
131
include/metadump/mono.hpp
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
include/metadump/multi.hpp
Normal file
10
include/metadump/multi.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace metadump {
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
class buffer {
|
||||||
|
// iovec array and per-component buffers
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@ -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
68
test/mono.cpp
Normal 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();
|
||||||
|
}
|
||||||
@ -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();
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user