make StructLayout serializable
This commit is contained in:
parent
8447cf5ad5
commit
f5d2a1f1b7
@ -1,5 +1,6 @@
|
|||||||
#include "StructLayout.hpp"
|
#include "StructLayout.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -13,6 +14,27 @@ using namespace data;
|
|||||||
static_assert(sizeof(float) == sizeof(int32_t));
|
static_assert(sizeof(float) == sizeof(int32_t));
|
||||||
static_assert(sizeof(double) == sizeof(int64_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) {
|
StructLayout StructLayout::create(const std::vector<Field>& fields) {
|
||||||
std::vector<Field> builtFields = fields;
|
std::vector<Field> builtFields = fields;
|
||||||
std::unordered_map<std::string, int> indices;
|
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);
|
auto ptr = reinterpret_cast<const char*>(src + field.offset);
|
||||||
return std::string_view(ptr, strnlen(ptr, field.elements));
|
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 <optional>
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
#include "interfaces/Serializable.hpp"
|
||||||
|
|
||||||
namespace data {
|
namespace data {
|
||||||
enum class FieldType {
|
enum class FieldType {
|
||||||
@ -14,6 +15,14 @@ namespace data {
|
|||||||
COUNT
|
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
|
/// @brief Sorted by severity
|
||||||
enum class FieldIncapatibilityType {
|
enum class FieldIncapatibilityType {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
@ -28,7 +37,7 @@ namespace data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline constexpr int sizeof_type(FieldType type) {
|
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
|
1, 2, 4, 8, 4, 8, 1
|
||||||
};
|
};
|
||||||
return sizes[static_cast<int>(type)];
|
return sizes[static_cast<int>(type)];
|
||||||
@ -42,11 +51,19 @@ namespace data {
|
|||||||
/// @brief Strategy will be used if value can't be left the same on conversion
|
/// @brief Strategy will be used if value can't be left the same on conversion
|
||||||
enum class FieldConvertStrategy {
|
enum class FieldConvertStrategy {
|
||||||
/// @brief Reset field value
|
/// @brief Reset field value
|
||||||
RESET,
|
RESET = 0,
|
||||||
/// @brief Clamp field value if out of type range
|
/// @brief Clamp field value if out of type range
|
||||||
CLAMP
|
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 {
|
struct Field {
|
||||||
FieldType type;
|
FieldType type;
|
||||||
std::string name;
|
std::string name;
|
||||||
@ -58,13 +75,25 @@ namespace data {
|
|||||||
int offset;
|
int offset;
|
||||||
/// @brief Byte size of the field
|
/// @brief Byte size of the field
|
||||||
int size;
|
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;
|
int totalSize;
|
||||||
std::vector<Field> fields;
|
std::vector<Field> fields;
|
||||||
std::unordered_map<std::string, int> indices;
|
std::unordered_map<std::string, int> indices;
|
||||||
public:
|
|
||||||
StructLayout(
|
StructLayout(
|
||||||
int totalSize,
|
int totalSize,
|
||||||
std::vector<Field> fields,
|
std::vector<Field> fields,
|
||||||
@ -73,6 +102,16 @@ namespace data {
|
|||||||
fields(std::move(fields)),
|
fields(std::move(fields)),
|
||||||
indices(std::move(indices))
|
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.
|
/// @brief Get field by name. Returns nullptr if field not found.
|
||||||
/// @param name field name
|
/// @param name field name
|
||||||
@ -83,7 +122,6 @@ namespace data {
|
|||||||
if (found == indices.end()) {
|
if (found == indices.end()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &fields.at(found->second);
|
return &fields.at(found->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,5 +221,8 @@ namespace data {
|
|||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static StructLayout create(const std::vector<Field>& fields);
|
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 {
|
class Serializable {
|
||||||
public:
|
public:
|
||||||
virtual ~Serializable() { }
|
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;
|
virtual void deserialize(dynamic::Map* src) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <limits>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
@ -62,9 +63,15 @@ namespace util {
|
|||||||
/// @param size entry size
|
/// @param size entry size
|
||||||
/// @return temporary entry pointer
|
/// @return temporary entry pointer
|
||||||
/// @attention pointer becomes invalid after allocate(...) or free(...)
|
/// @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) {
|
if (size == 0) {
|
||||||
throw std::runtime_error("size is 0");
|
throw std::invalid_argument("zero size");
|
||||||
}
|
}
|
||||||
ptrdiff_t offset = 0;
|
ptrdiff_t offset = 0;
|
||||||
if (auto found = find(index)) {
|
if (auto found = find(index)) {
|
||||||
|
|||||||
@ -114,3 +114,20 @@ TEST(StructLayout, ConvertWithLoss) {
|
|||||||
EXPECT_EQ(dstLayout.getChars(dst, "text"), "tru");
|
EXPECT_EQ(dstLayout.getChars(dst, "text"), "tru");
|
||||||
EXPECT_EQ(dstLayout.getInteger(dst, "someint"), INT8_MAX);
|
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