make StructLayout serializable
This commit is contained in:
parent
8447cf5ad5
commit
f5d2a1f1b7
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user