From f5d2a1f1b7e07efb8f8ffa71a3ebfefe81a00224 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 14 Sep 2024 18:56:37 +0300 Subject: [PATCH] make StructLayout serializable --- src/data/StructLayout.cpp | 61 +++++++++++++++++++++++++++++++++ src/data/StructLayout.hpp | 51 ++++++++++++++++++++++++--- src/interfaces/Serializable.hpp | 2 +- src/util/SmallHeap.hpp | 11 ++++-- test/data/StructLayout.cpp | 17 +++++++++ 5 files changed, 134 insertions(+), 8 deletions(-) diff --git a/src/data/StructLayout.cpp b/src/data/StructLayout.cpp index 9124b68a..b434b326 100644 --- a/src/data/StructLayout.cpp +++ b/src/data/StructLayout.cpp @@ -1,5 +1,6 @@ #include "StructLayout.hpp" +#include #include #include #include @@ -13,6 +14,27 @@ using namespace data; static_assert(sizeof(float) == sizeof(int32_t)); static_assert(sizeof(double) == sizeof(int64_t)); +FieldType data::FieldType_from_string(std::string_view name) { + std::map map { + {"int8", FieldType::I8}, + {"int16", FieldType::I16}, + {"int32", FieldType::I32}, + {"int64", FieldType::I64}, + {"float32", FieldType::F32}, + {"float64", FieldType::F64}, + {"char", FieldType::CHAR}, + }; + return map.at(name); +} + +FieldConvertStrategy data::FieldConvertStrategy_from_string(std::string_view name) { + std::map map { + {"reset", FieldConvertStrategy::RESET}, + {"clamp", FieldConvertStrategy::CLAMP} + }; + return map.at(name); +} + StructLayout StructLayout::create(const std::vector& fields) { std::vector builtFields = fields; std::unordered_map indices; @@ -325,3 +347,42 @@ std::string_view StructLayout::getChars( auto ptr = reinterpret_cast(src + field.offset); return std::string_view(ptr, strnlen(ptr, field.elements)); } + +std::unique_ptr StructLayout::serialize() const { + auto map = std::make_unique(); + for (const auto& [name, index] : indices) { + auto& fieldmap = map->putMap(name); + const auto& field = fields[index]; + fieldmap.put("type", to_string(field.type)); + if (field.elements != 1) { + fieldmap.put("length", field.elements); + } + if (field.convertStrategy != FieldConvertStrategy::RESET) { + fieldmap.put("convert-strategy", to_string(field.convertStrategy)); + } + } + return map; +} + +void StructLayout::deserialize(dynamic::Map* src) { + std::vector fields; + for (auto& [name, value] : src->values) { + if (auto fieldmapptr = std::get_if(&value)) { + const auto& fieldmap = *fieldmapptr; + + auto typeName = fieldmap->get("type"); + FieldType type = FieldType_from_string(typeName); + + int elements = fieldmap->get("length", 1); + + auto convertStrategy = FieldConvertStrategy::RESET; + if (fieldmap->has("convert-strategy")) { + convertStrategy = FieldConvertStrategy_from_string( + fieldmap->get("convert-strategy") + ); + } + fields.push_back(Field {type, name, elements, convertStrategy}); + } + } + *this = create(fields); +} diff --git a/src/data/StructLayout.hpp b/src/data/StructLayout.hpp index b30bcea6..35942457 100644 --- a/src/data/StructLayout.hpp +++ b/src/data/StructLayout.hpp @@ -7,6 +7,7 @@ #include #include "typedefs.hpp" +#include "interfaces/Serializable.hpp" namespace data { enum class FieldType { @@ -14,6 +15,14 @@ namespace data { COUNT }; + inline const char* to_string(FieldType type) { + const char* names[] = { + "int8", "int16", "int32", "int64", "float32", "float64", "char" + }; + return names[static_cast(type)]; + } + FieldType FieldType_from_string(std::string_view name); + /// @brief Sorted by severity enum class FieldIncapatibilityType { NONE = 0, @@ -28,7 +37,7 @@ namespace data { }; inline constexpr int sizeof_type(FieldType type) { - const int sizes[static_cast(FieldType::COUNT)] = { + const int sizes[] = { 1, 2, 4, 8, 4, 8, 1 }; return sizes[static_cast(type)]; @@ -42,10 +51,18 @@ namespace data { /// @brief Strategy will be used if value can't be left the same on conversion enum class FieldConvertStrategy { /// @brief Reset field value - RESET, + RESET = 0, /// @brief Clamp field value if out of type range CLAMP }; + + inline const char* to_string(FieldConvertStrategy strategy) { + const char* names[] = { + "reset", "clamp" + }; + return names[static_cast(strategy)]; + } + FieldConvertStrategy FieldConvertStrategy_from_string(std::string_view name); struct Field { FieldType type; @@ -58,13 +75,25 @@ namespace data { int offset; /// @brief Byte size of the field int size; + + bool operator==(const Field& o) const { + return type == o.type && + name == o.name && + elements == o.elements && + convertStrategy == o.convertStrategy && + offset == o.offset && + size == o.size; + } + bool operator!=(const Field& o) const { + return !operator==(o); + } }; - class StructLayout { + class StructLayout : public Serializable { int totalSize; std::vector fields; std::unordered_map indices; - public: + StructLayout( int totalSize, std::vector fields, @@ -73,6 +102,16 @@ namespace data { fields(std::move(fields)), indices(std::move(indices)) {} + public: + StructLayout() : StructLayout(0, {}, {}) {} + + bool operator==(const StructLayout& o) const { + // if fields are completely equal then structures are equal + return fields == o.fields; + } + bool operator!=(const StructLayout& o) const { + return !operator==(o); + } /// @brief Get field by name. Returns nullptr if field not found. /// @param name field name @@ -83,7 +122,6 @@ namespace data { if (found == indices.end()) { return nullptr; } - return &fields.at(found->second); } @@ -183,5 +221,8 @@ namespace data { [[nodiscard]] static StructLayout create(const std::vector& fields); + + std::unique_ptr serialize() const override; + void deserialize(dynamic::Map* src) override; }; } diff --git a/src/interfaces/Serializable.hpp b/src/interfaces/Serializable.hpp index 89962079..7c15fffa 100644 --- a/src/interfaces/Serializable.hpp +++ b/src/interfaces/Serializable.hpp @@ -7,6 +7,6 @@ class Serializable { public: virtual ~Serializable() { } - virtual std::unique_ptr serialize() const = 0; + virtual std::unique_ptr serialize() const = 0; virtual void deserialize(dynamic::Map* src) = 0; }; diff --git a/src/util/SmallHeap.hpp b/src/util/SmallHeap.hpp index aea3f51d..0279f465 100644 --- a/src/util/SmallHeap.hpp +++ b/src/util/SmallHeap.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace util { @@ -62,9 +63,15 @@ namespace util { /// @param size entry size /// @return temporary entry pointer /// @attention pointer becomes invalid after allocate(...) or free(...) - uint8_t* allocate(Tindex index, Tsize size) { + uint8_t* allocate(Tindex index, size_t size) { + const auto maxSize = std::numeric_limits::max(); + if (size > maxSize) { + throw std::invalid_argument( + "requested "+std::to_string(size)+" bytes but limit is "+ + std::to_string(maxSize)); + } if (size == 0) { - throw std::runtime_error("size is 0"); + throw std::invalid_argument("zero size"); } ptrdiff_t offset = 0; if (auto found = find(index)) { diff --git a/test/data/StructLayout.cpp b/test/data/StructLayout.cpp index bf6f8e1d..ce183a11 100644 --- a/test/data/StructLayout.cpp +++ b/test/data/StructLayout.cpp @@ -114,3 +114,20 @@ TEST(StructLayout, ConvertWithLoss) { EXPECT_EQ(dstLayout.getChars(dst, "text"), "tru"); EXPECT_EQ(dstLayout.getInteger(dst, "someint"), INT8_MAX); } + +TEST(StructLayout, Serialization) { + std::vector fields { + Field {FieldType::CHAR, "text", 5}, + Field {FieldType::I16, "someint", 1}, + Field {FieldType::F64, "pi", 1}, + }; + auto layout1 = StructLayout::create(fields); + auto serialized = layout1.serialize(); + + std::cout << *serialized << std::endl; + + StructLayout layout2; + layout2.deserialize(serialized.get()); + + EXPECT_EQ(layout1, layout2); +}