From c67b867a319b78bad233c065585a79040725a4f8 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 29 Aug 2024 11:57:05 +0300 Subject: [PATCH] add StructMapper read/write methods --- src/data/StructMapper.cpp | 162 +++++++++++++++++++++++++++++++++++++ src/data/StructMapper.hpp | 40 ++++++++- test/data/StructMapper.cpp | 23 ++++++ 3 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 src/data/StructMapper.cpp create mode 100644 test/data/StructMapper.cpp diff --git a/src/data/StructMapper.cpp b/src/data/StructMapper.cpp new file mode 100644 index 00000000..238fd665 --- /dev/null +++ b/src/data/StructMapper.cpp @@ -0,0 +1,162 @@ +#include "StructMapper.hpp" + +#include +#include +#include + +#include "util/data_io.hpp" + +using namespace data; + +static_assert(sizeof(float) == sizeof(int32_t)); +static_assert(sizeof(double) == sizeof(int64_t)); + +StructMapping StructMapping::create(const std::vector& fields) { + std::vector builtFields = fields; + std::unordered_map indices; + + for (Field& field : builtFields) { + field.size = sizeof_type(field.type) * field.elements; + } + std::sort(builtFields.begin(), builtFields.end(), + [](const Field& a, const Field& b) { + return a.size > b.size; + } + ); + int offset = 0; + for (int i = 0; i < builtFields.size(); i++) { + auto& field = builtFields[i]; + field.offset = offset; + indices[field.name] = i; + offset += field.size; + } + return StructMapping( + offset, std::move(builtFields), std::move(indices)); +} + +const Field& StructMapping::requreField(const std::string& name) const { + auto found = indices.find(name); + if (found == indices.end()) { + throw std::runtime_error("field '"+name+"' does not exist"); + } + return *&fields.at(found->second); +} + + +template +static void set_int(ubyte* dst, integer_t value) { + T out_value = static_cast(value); + out_value = dataio::le2h(out_value); + *reinterpret_cast(dst) = out_value; +} + +void StructMapping::setInteger( + ubyte* dst, integer_t value, const std::string& name, int index +) { + const auto& field = requreField(name); + if (index < 0 || index >= field.elements) { + throw std::runtime_error( + "index out of bounds [0, "+std::to_string(field.elements)+"]"); + } + auto ptr = dst + field.offset + index * sizeof_type(field.type); + switch (field.type) { + case FieldType::I8: set_int(ptr, value); break; + case FieldType::I16: set_int(ptr, value); break; + case FieldType::I32: set_int(ptr, value); break; + case FieldType::I64: set_int(ptr, value); break; + case FieldType::CHAR: set_int(ptr, value); break; + case FieldType::F32: + case FieldType::F64: + setNumber(dst, static_cast(value), name, index); + break; + default: + throw std::runtime_error("type error"); + } +} + +void StructMapping::setNumber( + ubyte* dst, number_t value, const std::string& name, int index +) { + const auto& field = requreField(name); + if (index < 0 || index >= field.elements) { + throw std::runtime_error( + "index out of bounds [0, "+std::to_string(field.elements)+"]"); + } + auto ptr = dst + field.offset + index * sizeof_type(field.type); + switch (field.type) { + case FieldType::F32: { + float fval = static_cast(value); + int32_t ival; + std::memcpy(&ival, &fval, sizeof(int32_t)); + set_int(ptr, ival); + break; + } + case FieldType::F64: { + double fval = static_cast(value); + int64_t ival; + std::memcpy(&ival, &fval, sizeof(int64_t)); + set_int(ptr, ival); + break; + } + default: + throw std::runtime_error("type error"); + } +} + +template +static T get_int(const ubyte* src) { + return dataio::le2h(*reinterpret_cast(src)); +} + +integer_t StructMapping::getInteger( + const ubyte* src, const std::string& name, int index +) const { + const auto& field = requreField(name); + if (index < 0 || index >= field.elements) { + throw std::runtime_error( + "index out of bounds [0, "+std::to_string(field.elements)+"]"); + } + auto ptr = src + field.offset + index * sizeof_type(field.type); + switch (field.type) { + case FieldType::I8: return get_int(ptr); + case FieldType::I16: return get_int(ptr); + case FieldType::I32: return get_int(ptr); + case FieldType::I64: return get_int(ptr); + case FieldType::CHAR: return get_int(ptr); + default: + throw std::runtime_error("type error"); + } +} + +number_t StructMapping::getNumber( + const ubyte* src, const std::string& name, int index +) const { + const auto& field = requreField(name); + if (index < 0 || index >= field.elements) { + throw std::runtime_error( + "index out of bounds [0, "+std::to_string(field.elements)+"]"); + } + auto ptr = src + field.offset + index * sizeof_type(field.type); + switch (field.type) { + case FieldType::F32: { + float fval; + auto ival = get_int(ptr); + std::memcpy(&fval, &ival, sizeof(float)); + return fval; + } + case FieldType::F64: { + double fval; + auto ival = get_int(ptr); + std::memcpy(&fval, &ival, sizeof(double)); + return fval; + } + case FieldType::I8: + case FieldType::I16: + case FieldType::I32: + case FieldType::I64: + case FieldType::CHAR: + return getInteger(src, name, index); + + } + throw std::runtime_error("type error"); +} diff --git a/src/data/StructMapper.hpp b/src/data/StructMapper.hpp index 1c92f882..32c33ed1 100644 --- a/src/data/StructMapper.hpp +++ b/src/data/StructMapper.hpp @@ -8,26 +8,64 @@ namespace data { enum class FieldType { - I8, I16, I32, I64, F32, F64 + I8=0, I16, I32, I64, F32, F64, CHAR, + COUNT }; + inline constexpr int sizeof_type(FieldType type) { + const int sizes[static_cast(FieldType::COUNT)] = { + 1, 2, 4, 8, 4, 8, 1 + }; + return sizes[static_cast(type)]; + } + struct Field { FieldType type; std::string name; + /// @brief Number of field elements (array size) + int elements; + /// @brief Byte offset of the field int offset; + /// @brief Byte size of the field + int size; }; class StructMapping { + int totalSize; std::vector fields; std::unordered_map indices; public: + StructMapping( + int totalSize, + std::vector fields, + std::unordered_map indices + ) : totalSize(totalSize), + fields(std::move(fields)), + indices(std::move(indices)) + {} + const Field* getField(const std::string& name) const { auto found = indices.find(name); if (found == indices.end()) { return nullptr; } + return &fields.at(found->second); } + + const Field& requreField(const std::string& name) const; + + integer_t getInteger(const ubyte* src, const std::string& name, int index=0) const; + number_t getNumber(const ubyte* src, const std::string& name, int index=0) const; + + void setInteger(ubyte* dst, integer_t value, const std::string& name, int index=0); + void setNumber(ubyte* dst, number_t value, const std::string& name, int index=0); + + int size() const { + return totalSize; + } + + static StructMapping create(const std::vector& fields); }; class StructAccess { diff --git a/test/data/StructMapper.cpp b/test/data/StructMapper.cpp new file mode 100644 index 00000000..60c874b1 --- /dev/null +++ b/test/data/StructMapper.cpp @@ -0,0 +1,23 @@ +#include "data/StructMapper.hpp" + +#include + +using namespace data; + +TEST(StructMapper, ReadWrite) { + ubyte buffer[16] {}; + + std::vector fields { + Field {FieldType::I8, "a", 1}, + Field {FieldType::I32, "b", 1}, + Field {FieldType::F32, "f", 1}, + }; + auto mapping = StructMapping::create(fields); + EXPECT_EQ(mapping.size(), 9); + + mapping.setInteger(buffer, 42, "a"); + EXPECT_EQ(mapping.getInteger(buffer, "a"), 42); + + mapping.setNumber(buffer, 3.141592f, "f"); + EXPECT_FLOAT_EQ(mapping.getNumber(buffer, "f"), 3.141592f); +}