From 77229b8d9aae249d3a95d7213bdb6326d05755a2 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 29 Aug 2024 15:10:51 +0300 Subject: [PATCH] add StructMapping::setUnicode --- src/data/StructMapper.cpp | 35 ++++++++++++++----- src/data/StructMapper.hpp | 69 ++++++++++++++++++++++++++++++++++++-- test/data/StructMapper.cpp | 15 +++++++++ 3 files changed, 108 insertions(+), 11 deletions(-) diff --git a/src/data/StructMapper.cpp b/src/data/StructMapper.cpp index 1e26f816..c52e0589 100644 --- a/src/data/StructMapper.cpp +++ b/src/data/StructMapper.cpp @@ -1,10 +1,12 @@ #include "StructMapper.hpp" #include +#include #include #include #include "util/data_io.hpp" +#include "util/stringutil.hpp" using namespace data; @@ -55,7 +57,7 @@ void StructMapping::setInteger( ) { const auto& field = requreField(name); if (index < 0 || index >= field.elements) { - throw std::runtime_error( + throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); } auto ptr = dst + field.offset + index * sizeof_type(field.type); @@ -79,7 +81,7 @@ void StructMapping::setNumber( ) { const auto& field = requreField(name); if (index < 0 || index >= field.elements) { - throw std::runtime_error( + throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); } auto ptr = dst + field.offset + index * sizeof_type(field.type); @@ -103,7 +105,7 @@ void StructMapping::setNumber( } } -void StructMapping::setChars( +size_t StructMapping::setChars( ubyte* dst, std::string_view value, const std::string& name ) { const auto& field = requreField(name); @@ -111,8 +113,23 @@ void StructMapping::setChars( throw std::runtime_error("'char' field type required"); } auto ptr = reinterpret_cast(dst + field.offset); - std::memcpy(ptr, value.data(), - std::min(value.size(), static_cast(field.elements))); + auto size = std::min(value.size(), static_cast(field.elements)); + std::memcpy(ptr, value.data(), size); + return size; +} + +size_t StructMapping::setUnicode( + ubyte* dst, std::string_view value, const std::string& name +) { + const auto& field = requreField(name); + if (field.type != FieldType::CHAR) { + throw std::runtime_error("'char' field type required"); + } + auto text = std::string_view(value.data(), value.size()); + size_t size = util::crop_utf8(text, field.elements); + auto ptr = reinterpret_cast(dst + field.offset); + std::memcpy(ptr, value.data(), size); + return size; } template @@ -125,7 +142,7 @@ integer_t StructMapping::getInteger( ) const { const auto& field = requreField(name); if (index < 0 || index >= field.elements) { - throw std::runtime_error( + throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); } auto ptr = src + field.offset + index * sizeof_type(field.type); @@ -145,7 +162,7 @@ number_t StructMapping::getNumber( ) const { const auto& field = requreField(name); if (index < 0 || index >= field.elements) { - throw std::runtime_error( + throw std::out_of_range( "index out of bounds [0, "+std::to_string(field.elements)+"]"); } auto ptr = src + field.offset + index * sizeof_type(field.type); @@ -180,6 +197,6 @@ std::string_view StructMapping::getChars( if (field.type != FieldType::CHAR) { throw std::runtime_error("'char' field type required"); } - auto ptr = src + field.offset; - return std::string_view(reinterpret_cast(ptr), field.elements); + auto ptr = reinterpret_cast(src + field.offset); + return std::string_view(ptr, strnlen(ptr, field.elements)); } diff --git a/src/data/StructMapper.hpp b/src/data/StructMapper.hpp index 358105f9..7f1716b5 100644 --- a/src/data/StructMapper.hpp +++ b/src/data/StructMapper.hpp @@ -44,6 +44,9 @@ namespace data { indices(std::move(indices)) {} + /// @brief Get field by name. Returns nullptr if field not found. + /// @param name field name + /// @return nullable field pointer const Field* getField(const std::string& name) const { auto found = indices.find(name); if (found == indices.end()) { @@ -53,16 +56,78 @@ namespace data { return &fields.at(found->second); } + /// @brief Get field by name + /// @throws std::runtime_exception - field not found + /// @param name field name + /// @return read-only field reference const Field& requreField(const std::string& name) const; + /// @brief Get integer from specified field. + /// Types: (i8, i16, i32, i64, char) + /// @throws std::runtime_exception - field not found + /// @throws std::out_of_range - index is out of range [0, elements-1] + /// @param src source buffer + /// @param name field name + /// @param index array index + /// @return field value + [[nodiscard]] integer_t getInteger(const ubyte* src, const std::string& name, int index=0) const; + + /// @brief Get floating-point number from specified field. + /// Types: (f32, f64, i8, i16, i32, i64, char) + /// @throws std::runtime_exception - field not found + /// @throws std::out_of_range - index is out of range [0, elements-1] + /// @param src source buffer + /// @param name field name + /// @param index array index + /// @return field value + [[nodiscard]] number_t getNumber(const ubyte* src, const std::string& name, int index=0) const; + + /// @brief Get read-only chars array as string_view. + /// @param src source buffer + /// @param name field name + [[nodiscard]] std::string_view getChars(const ubyte* src, const std::string& name) const; + /// @brief Set field integer value. + /// Types: (i8, i16, i32, i64, f32, f64, char) + /// @throws std::runtime_exception - field not found + /// @throws std::out_of_range - index is out of range [0, elements-1] + /// @param dst destination buffer + /// @param value value + /// @param name field name + /// @param index array index 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); - void setChars(ubyte* dst, std::string_view value, const std::string& name); + /// @brief Set field numeric value. + /// Types: (f32, f64) + /// @throws std::runtime_exception - field not found + /// @throws std::out_of_range - index is out of range [0, elements-1] + /// @param dst destination buffer + /// @param value value + /// @param name field name + /// @param index array index + void setNumber(ubyte* dst, number_t value, const std::string& name, int index=0); + + /// @brief Replace chars array to given ASCII string + /// @throws std::runtime_exception - field not found + /// @see StructMapper::setUnicode - utf-8 version of setChars + /// @param dst destination buffer + /// @param value ASCII string + /// @param name field name + /// @return number of written string chars + size_t setChars(ubyte* dst, std::string_view value, const std::string& name); + + /// @brief Unicode-safe version of setChars + /// @throws std::runtime_exception - field not found + /// @param dst destination buffer + /// @param value utf-8 string + /// @param name field name + /// @return number of written string chars + size_t setUnicode(ubyte* dst, std::string_view value, const std::string& name); + + /// @return total structure size (bytes) int size() const { return totalSize; } diff --git a/test/data/StructMapper.cpp b/test/data/StructMapper.cpp index 67aeb97b..78e9204a 100644 --- a/test/data/StructMapper.cpp +++ b/test/data/StructMapper.cpp @@ -24,3 +24,18 @@ TEST(StructMapper, ReadWrite) { mapping.setChars(buffer, "hello", "s"); EXPECT_EQ(mapping.getChars(buffer, "s"), "hell"); } + +TEST(StructMapper, Unicode) { + ubyte buffer[8] {}; + std::vector fields { + Field {FieldType::CHAR, "text", 5}, + }; + auto mapping = StructMapping::create(fields); + EXPECT_EQ(mapping.size(), 5); + + mapping.setUnicode(buffer, u8"テキストデモ", "text"); + EXPECT_EQ(mapping.getChars(buffer, "text"), std::string(u8"テ")); + + mapping.setUnicode(buffer, u8"пример", "text"); + EXPECT_EQ(mapping.getChars(buffer, "text"), std::string(u8"пр")); +}