diff --git a/include/ylt/struct_pack.hpp b/include/ylt/struct_pack.hpp index ab33cd92e..2f291f4e9 100644 --- a/include/ylt/struct_pack.hpp +++ b/include/ylt/struct_pack.hpp @@ -34,6 +34,7 @@ #include "struct_pack/type_id.hpp" #include "struct_pack/type_trait.hpp" #include "struct_pack/unpacker.hpp" +#include "struct_pack/user_helper.hpp" #include "struct_pack/varint.hpp" #if __has_include() && __cplusplus > 202002L diff --git a/include/ylt/struct_pack/endian_wrapper.hpp b/include/ylt/struct_pack/endian_wrapper.hpp index 2798353a3..dd683a7b8 100644 --- a/include/ylt/struct_pack/endian_wrapper.hpp +++ b/include/ylt/struct_pack/endian_wrapper.hpp @@ -270,184 +270,4 @@ STRUCT_PACK_INLINE bool low_bytes_read_wrapper(reader_t& reader, T& elem) { } } } // namespace detail -template -STRUCT_PACK_INLINE void write(Writer& writer, const T& t) { - if constexpr (std::is_fundamental_v) { - detail::write_wrapper(writer, (const char*)&t); - } - else if constexpr (detail::array) { - if constexpr (detail::is_little_endian_copyable && - std::is_fundamental_v< - std::remove_reference_t>) { - write_bytes_array(writer, (const char*)t.data(), sizeof(T)); - } - else { - for (auto& e : t) write(writer, e); - } - } - else if constexpr (detail::string || detail::container) { - std::uint64_t len = t.size(); - detail::write_wrapper<8>(writer, (char*)&len); - if constexpr (detail::continuous_container && - detail::is_little_endian_copyable && - std::is_fundamental_v) { - write_bytes_array(writer, (const char*)t.data(), len * sizeof(t[0])); - } - else { - for (auto& e : t) write(writer, e); - } - } - else { - static_assert(!sizeof(T), "not support type"); - } -} -template -STRUCT_PACK_INLINE void write(Writer& writer, const T* t, std::size_t length) { - if constexpr (std::is_fundamental_v) { - if constexpr (detail::is_little_endian_copyable) { - write_bytes_array(writer, (const char*)t, sizeof(T) * length); - } - else { - for (std::size_t i = 0; i < length; ++i) write(writer, t[i]); - } - } - else { - static_assert(!sizeof(T), "not support type"); - } -} -template -STRUCT_PACK_INLINE constexpr std::size_t get_write_size(const T& t) { - if constexpr (std::is_fundamental_v) { - return sizeof(T); - } - else if constexpr (detail::array) { - if constexpr (std::is_fundamental_v< - std::remove_reference_t>) { - return sizeof(T); - } - else { - std::size_t ret = 0; - for (auto& e : t) ret += get_write_size(e); - return ret; - } - } - else if constexpr (detail::string || detail::container) { - std::size_t ret = 8; - if constexpr (detail::continuous_container && - detail::is_little_endian_copyable && - std::is_fundamental_v) { - ret += t.size() * sizeof(t[0]); - } - else { - for (auto& e : t) ret += get_write_size(e); - } - return ret; - } - else { - static_assert(!sizeof(T), "not support type"); - } -} -template -STRUCT_PACK_INLINE constexpr std::size_t get_write_size(const T* t, - std::size_t length) { - return sizeof(T) * length; -} -template -STRUCT_PACK_INLINE struct_pack::errc read(Reader& reader, T& t) { - if constexpr (std::is_fundamental_v) { - if SP_UNLIKELY (!detail::read_wrapper(reader, (char*)&t)) { - return struct_pack::errc::no_buffer_space; - } - else { - return {}; - } - } - else if constexpr (detail::array) { - if constexpr (std::is_fundamental_v< - std::remove_reference_t> && - detail::is_little_endian_copyable) { - if SP_UNLIKELY (!read_bytes_array(reader, (char*)t.data(), sizeof(T))) { - return struct_pack::errc::no_buffer_space; - } - else { - return {}; - } - } - else { - struct_pack::errc ec; - for (auto& e : t) { - ec = read(reader, e); - if SP_UNLIKELY (ec != struct_pack::errc{}) { - return ec; - } - } - return struct_pack::errc{}; - } - } - else if constexpr (detail::string || detail::container) { - std::uint64_t sz; - auto ec = read(reader, sz); - if SP_UNLIKELY (ec != struct_pack::errc{}) { - return ec; - } - if constexpr (detail::continuous_container && - std::is_fundamental_v< - std::remove_reference_t> && - detail::is_little_endian_copyable && - checkable_reader_t) { - if SP_UNLIKELY (sz > UINT64_MAX / sizeof(t[0]) || sz > SIZE_MAX) { - return struct_pack::errc::invalid_buffer; - } - std::size_t mem_size = sz * sizeof(t[0]); - if SP_UNLIKELY (!reader.check(mem_size)) { - return struct_pack::errc::no_buffer_space; - } - detail::resize(t, sz); - if SP_UNLIKELY (!read_bytes_array(reader, (char*)t.data(), mem_size)) { - return struct_pack::errc::no_buffer_space; - } - return struct_pack::errc{}; - } - else { - t.clear(); - for (std::size_t i = 0; i < sz; ++i) { - t.push_back(typename T::value_type{}); - ec = read(reader, t.back()); - if SP_UNLIKELY (ec != struct_pack::errc{}) { - return ec; - } - } - return struct_pack::errc{}; - } - } - else { - static_assert(!sizeof(T), "not support type"); - } -} -template -struct_pack::errc read(Reader& reader, T* t, std::size_t length) { - if constexpr (std::is_fundamental_v) { - if constexpr (detail::is_little_endian_copyable) { - if SP_UNLIKELY (!read_bytes_array(reader, (char*)t, sizeof(T) * length)) { - return struct_pack::errc::no_buffer_space; - } - else { - return {}; - } - } - else { - struct_pack::errc ec{}; - for (std::size_t i = 0; i < length; ++i) { - ec = read(reader, t[i]); - if SP_UNLIKELY (ec != struct_pack::errc{}) { - return ec; - } - }; - return ec; - } - } - else { - static_assert(!sizeof(T), "not support type"); - } -} }; // namespace struct_pack \ No newline at end of file diff --git a/include/ylt/struct_pack/packer.hpp b/include/ylt/struct_pack/packer.hpp index 7ee110b52..9182fd576 100644 --- a/include/ylt/struct_pack/packer.hpp +++ b/include/ylt/struct_pack/packer.hpp @@ -32,12 +32,19 @@ template < #else typename writer, #endif - typename serialize_type> + typename serialize_type, bool force_optimize = false> class packer { + constexpr inline static serialize_buffer_size useless_info{}; public: packer(writer &writer_, const serialize_buffer_size &info) - : writer_(writer_), info(info) { + : writer_(writer_), info_(info) { +#if __cpp_concepts < 201907L + static_assert(writer_t, + "The writer type must satisfy requirements!"); +#endif + } + packer(writer &writer_) : writer_(writer_), info_(useless_info) { #if __cpp_concepts < 201907L static_assert(writer_t, "The writer type must satisfy requirements!"); @@ -70,7 +77,6 @@ class packer { } } - private: template static constexpr uint32_t STRUCT_PACK_INLINE calculate_raw_hash() { if constexpr (sizeof...(Args) == 0) { @@ -100,10 +106,10 @@ class packer { write_wrapper(writer_, (char *)&hash_head); } if constexpr (hash_head % 2) { // has more metainfo - auto metainfo = info.metainfo(); + auto metainfo = info_.metainfo(); write_wrapper(writer_, (char *)&metainfo); if constexpr (serialize_static_config::has_compatible) { - std::size_t sz = info.size(); + std::size_t sz = info_.size(); switch (metainfo & 0b11) { case 1: low_bytes_write_wrapper<2>(writer_, sz); @@ -132,7 +138,6 @@ class packer { } } - private: template constexpr void STRUCT_PACK_INLINE serialize_many(const First &first_item, @@ -302,50 +307,56 @@ class packer { } else if constexpr (map_container || container) { auto size = item.size(); + if constexpr (size_type == 1) { low_bytes_write_wrapper(writer_, size); } -#ifdef STRUCT_PACK_OPTIMIZE - else if constexpr (size_type == 2) { - low_bytes_write_wrapper(writer_, size); - } - else if constexpr (size_type == 4) { - low_bytes_write_wrapper(writer_, size); - } - else if constexpr (size_type == 8) { - if constexpr (sizeof(std::size_t) >= 8) { - low_bytes_write_wrapper(writer_, size); - } - else { - static_assert(!sizeof(T), "illegal size_type"); - } - } else { - static_assert(!sizeof(item), "illegal size_type."); - } +#ifdef STRUCT_PACK_OPTIMIZE + constexpr bool struct_pack_optimize = true; #else - else { - auto size = item.size(); - switch ((info.metainfo() & 0b11000) >> 3) { - case 1: - low_bytes_write_wrapper<2>(writer_, size); - break; - case 2: - low_bytes_write_wrapper<4>(writer_, size); - break; - case 3: + constexpr bool struct_pack_optimize = false; +#endif + if constexpr (force_optimize || struct_pack_optimize) { + if constexpr (size_type == 2) { + low_bytes_write_wrapper(writer_, size); + } + else if constexpr (size_type == 4) { + low_bytes_write_wrapper(writer_, size); + } + else if constexpr (size_type == 8) { if constexpr (sizeof(std::size_t) >= 8) { - low_bytes_write_wrapper<8>(writer_, size); + low_bytes_write_wrapper(writer_, size); } else { - unreachable(); + static_assert(!sizeof(T), "illegal size_type"); } - break; - default: - unreachable(); + } + else { + static_assert(!sizeof(item), "illegal size_type."); + } + } + else { + switch ((info_.metainfo() & 0b11000) >> 3) { + case 1: + low_bytes_write_wrapper<2>(writer_, size); + break; + case 2: + low_bytes_write_wrapper<4>(writer_, size); + break; + case 3: + if constexpr (sizeof(std::size_t) >= 8) { + low_bytes_write_wrapper<8>(writer_, size); + } + else { + unreachable(); + } + break; + default: + unreachable(); + } } } -#endif if constexpr (trivially_copyable_container && is_little_endian_copyable) { @@ -522,7 +533,7 @@ class packer { template friend constexpr serialize_buffer_size get_needed_size(const T &t); writer &writer_; - const serialize_buffer_size &info; + const serialize_buffer_size &info_; }; template = 201907L -template +template #else -template +template #endif class unpacker { public: @@ -85,10 +87,6 @@ class unpacker { unpacker(const unpacker &) = delete; unpacker &operator=(const unpacker &) = delete; - template - friend struct deserialize_one_derived_class_helper; - STRUCT_PACK_INLINE unpacker(Reader &reader) : reader_(reader) { #if __cpp_concepts < 201907L static_assert(reader_t, @@ -96,6 +94,9 @@ class unpacker { #endif } + template + friend STRUCT_PACK_INLINE struct_pack::errc read(Reader &reader, T &t); + template STRUCT_PACK_MAY_INLINE struct_pack::errc deserialize(T &t, Args &...args) { using Type = get_args_type; @@ -277,7 +278,6 @@ class unpacker { return err_code; } - private: template STRUCT_PACK_INLINE struct_pack::errc deserialize_compatibles( T &t, std::index_sequence, Args &...args) { @@ -878,58 +878,67 @@ class unpacker { return struct_pack::errc::no_buffer_space; } } -#ifdef STRUCT_PACK_OPTIMIZE - else if constexpr (size_type == 2) { - if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, size)) { - return struct_pack::errc::no_buffer_space; - } - } - else if constexpr (size_type == 4) { - if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, size)) { - return struct_pack::errc::no_buffer_space; - } - } - else if constexpr (size_type == 8) { - if constexpr (sizeof(std::size_t) >= 8) { - if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, size)) { - return struct_pack::errc::no_buffer_space; - } - } - else { - static_assert("illegal branch"); - } - } else { - static_assert(!sizeof(T), "illegal size_type"); - } +#ifdef STRUCT_PACK_OPTIMIZE + constexpr bool struct_pack_optimize = true; #else - else { - switch (size_type_) { - case 1: - if SP_UNLIKELY (!low_bytes_read_wrapper<2>(reader_, size)) { + constexpr bool struct_pack_optimize = false; +#endif + if constexpr (force_optimize || struct_pack_optimize) { + if constexpr (size_type == 2) { + if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, + size)) { return struct_pack::errc::no_buffer_space; } - break; - case 2: - if SP_UNLIKELY (!low_bytes_read_wrapper<4>(reader_, size)) { + } + else if constexpr (size_type == 4) { + if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, + size)) { return struct_pack::errc::no_buffer_space; } - break; - case 3: + } + else if constexpr (size_type == 8) { if constexpr (sizeof(std::size_t) >= 8) { - if SP_UNLIKELY (!low_bytes_read_wrapper<8>(reader_, size)) { + if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, + size)) { return struct_pack::errc::no_buffer_space; } } else { - unreachable(); + static_assert("illegal branch"); } - break; - default: - unreachable(); + } + else { + static_assert(!sizeof(T), "illegal size_type"); + } + } + else { + switch (size_type_) { + case 1: + if SP_UNLIKELY (!low_bytes_read_wrapper<2>(reader_, size)) { + return struct_pack::errc::no_buffer_space; + } + break; + case 2: + if SP_UNLIKELY (!low_bytes_read_wrapper<4>(reader_, size)) { + return struct_pack::errc::no_buffer_space; + } + break; + case 3: + if constexpr (sizeof(std::size_t) >= 8) { + if SP_UNLIKELY (!low_bytes_read_wrapper<8>(reader_, size)) { + return struct_pack::errc::no_buffer_space; + } + } + else { + unreachable(); + } + break; + default: + unreachable(); + } } } -#endif if (size == 0) { return {}; } diff --git a/include/ylt/struct_pack/user_helper.hpp b/include/ylt/struct_pack/user_helper.hpp new file mode 100644 index 000000000..d2bd13318 --- /dev/null +++ b/include/ylt/struct_pack/user_helper.hpp @@ -0,0 +1,85 @@ +#pragma once +#include +#include + +#include "ylt/struct_pack/calculate_size.hpp" +#include "ylt/struct_pack/packer.hpp" +#include "ylt/struct_pack/reflection.hpp" +#include "ylt/struct_pack/unpacker.hpp" +namespace struct_pack { +template +STRUCT_PACK_INLINE void write(Writer& writer, const T& t) { + struct_pack::detail::packer packer{writer}; + packer.template serialize_one(t); +}; +template +STRUCT_PACK_INLINE void write(Writer& writer, const T* t, std::size_t len) { + if constexpr (detail::is_trivial_serializable::value && + detail::is_little_endian_copyable) { + write_bytes_array(writer, (char*)t, sizeof(T) * len); + } + else { + struct_pack::detail::packer packer{writer}; + for (std::size_t i = 0; i < len; ++i) { + packer.template serialize_one(t[i]); + } + } +}; +template +STRUCT_PACK_INLINE struct_pack::errc read(Reader& reader, T& t) { + struct_pack::detail::unpacker unpacker{ + reader}; + return unpacker.template deserialize_one(t); +}; +template +STRUCT_PACK_INLINE struct_pack::errc read(Reader& reader, T* t, + std::size_t len) { + if constexpr (detail::is_trivial_serializable::value && + detail::is_little_endian_copyable) { + if constexpr (!ifSkip) { + if SP_UNLIKELY (!read_bytes_array(reader, (char*)t, sizeof(T) * len)) { + return struct_pack::errc::no_buffer_space; + } + } + else { + return reader.ignore(sizeof(T) * len) ? errc{} : errc::no_buffer_space; + } + } + else { + struct_pack::detail::unpacker unpacker{ + reader}; + for (std::size_t i = 0; i < len; ++i) { + auto code = + unpacker.template deserialize_one( + t[i]); + if SP_UNLIKELY (code != struct_pack::errc{}) { + return code; + } + } + } + return {}; +}; +template +STRUCT_PACK_INLINE constexpr std::size_t get_write_size(const T& t) { + auto sz = struct_pack::detail::calculate_one_size(t); + return sz.size_cnt * size_width + sz.total; +}; +template +STRUCT_PACK_INLINE constexpr std::size_t get_write_size(const T* t, + std::size_t len) { + if constexpr (detail::is_trivial_serializable::value) { + return sizeof(T) * len; + } + else { + size_info sz{}; + for (std::size_t i = 0; i < len; ++i) { + sz += struct_pack::detail::calculate_one_size(t[i]); + } + return sz.size_cnt * size_width + sz.total; + } +}; +}; // namespace struct_pack \ No newline at end of file diff --git a/src/struct_pack/tests/test_user_defined_type.cpp b/src/struct_pack/tests/test_user_defined_type.cpp index 32835e4c6..68f8f04e6 100644 --- a/src/struct_pack/tests/test_user_defined_type.cpp +++ b/src/struct_pack/tests/test_user_defined_type.cpp @@ -84,14 +84,14 @@ struct_pack::errc sp_deserialize_to(Reader& reader, array2D& ar) { if (auto ec = struct_pack::read(reader, ar.y); ec != struct_pack::errc{}) { return ec; } - auto length = 1ull * ar.x * ar.y * sizeof(float); if constexpr (struct_pack::checkable_reader_t) { + auto length = struct_pack::get_write_size(ar.p, 1ull * ar.x * ar.y); if (!reader.check(length)) { // some input(such as memory) allow us check // length before read data. return struct_pack::errc::no_buffer_space; } } - ar.p = (float*)malloc(length); + ar.p = (float*)malloc(1ull * ar.x * ar.y * sizeof(float)); auto ec = struct_pack::read(reader, ar.p, 1ull * ar.x * ar.y); if (ec != struct_pack::errc{}) { free(ar.p); @@ -101,15 +101,17 @@ struct_pack::errc sp_deserialize_to(Reader& reader, array2D& ar) { template struct_pack::errc sp_deserialize_to_with_skip(Reader& reader, array2D& ar) { - if (auto ec = struct_pack::read(reader, ar.name); ec != struct_pack::errc{}) { + if (auto ec = struct_pack::read( + reader, ar.name); // skip this field + ec != struct_pack::errc{}) { return ec; } - if (auto ec = struct_pack::read(reader, ar.values); - ec != struct_pack::errc{}) { + if (auto ec = struct_pack::read(reader, ar.values); + ec != struct_pack::errc{}) { // skip this field return ec; } - if (auto ec = struct_pack::read(reader, ar.values2); - ec != struct_pack::errc{}) { + if (auto ec = struct_pack::read(reader, ar.values2); + ec != struct_pack::errc{}) { // skip this field return ec; } if (auto ec = struct_pack::read(reader, ar.x); ec != struct_pack::errc{}) { @@ -118,9 +120,11 @@ struct_pack::errc sp_deserialize_to_with_skip(Reader& reader, array2D& ar) { if (auto ec = struct_pack::read(reader, ar.y); ec != struct_pack::errc{}) { return ec; } - auto length = 1ull * ar.x * ar.y * sizeof(float); - if (!reader.ignore(length)) { - return struct_pack::errc::no_buffer_space; + + if (auto ec = struct_pack::read(reader, ar.p, + 1ull * ar.x * ar.y); + ec != struct_pack::errc{}) { + return ec; } return {}; }