make StructLayout serializable

This commit is contained in:
MihailRis 2024-09-14 18:56:37 +03:00
parent 8447cf5ad5
commit f5d2a1f1b7
5 changed files with 134 additions and 8 deletions

View File

@ -1,5 +1,6 @@
#include "StructLayout.hpp"
#include <map>
#include <cstring>
#include <climits>
#include <string.h>
@ -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<std::string_view, FieldType> 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<std::string_view, FieldConvertStrategy> map {
{"reset", FieldConvertStrategy::RESET},
{"clamp", FieldConvertStrategy::CLAMP}
};
return map.at(name);
}
StructLayout StructLayout::create(const std::vector<Field>& fields) {
std::vector<Field> builtFields = fields;
std::unordered_map<std::string, int> indices;
@ -325,3 +347,42 @@ std::string_view StructLayout::getChars(
auto ptr = reinterpret_cast<const char*>(src + field.offset);
return std::string_view(ptr, strnlen(ptr, field.elements));
}
std::unique_ptr<dynamic::Map> StructLayout::serialize() const {
auto map = std::make_unique<dynamic::Map>();
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<Field> fields;
for (auto& [name, value] : src->values) {
if (auto fieldmapptr = std::get_if<dynamic::Map_sptr>(&value)) {
const auto& fieldmap = *fieldmapptr;
auto typeName = fieldmap->get<std::string>("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<std::string>("convert-strategy")
);
}
fields.push_back(Field {type, name, elements, convertStrategy});
}
}
*this = create(fields);
}

View File

@ -7,6 +7,7 @@
#include <optional>
#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<int>(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<int>(FieldType::COUNT)] = {
const int sizes[] = {
1, 2, 4, 8, 4, 8, 1
};
return sizes[static_cast<int>(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<int>(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<Field> fields;
std::unordered_map<std::string, int> indices;
public:
StructLayout(
int totalSize,
std::vector<Field> 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<Field>& fields);
std::unique_ptr<dynamic::Map> serialize() const override;
void deserialize(dynamic::Map* src) override;
};
}

View File

@ -7,6 +7,6 @@
class Serializable {
public:
virtual ~Serializable() { }
virtual std::unique_ptr<dynamic::Map> serialize() const = 0;
virtual std::unique_ptr<dynamic::Map> serialize() const = 0;
virtual void deserialize(dynamic::Map* src) = 0;
};

View File

@ -3,6 +3,7 @@
#include <cstdint>
#include <cstring>
#include <vector>
#include <limits>
#include <stdexcept>
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<Tsize>::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)) {

View File

@ -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<Field> 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);
}