diff --git a/.github/workflows/s390x.yml b/.github/workflows/s390x.yml index a5899873a..3edac918d 100644 --- a/.github/workflows/s390x.yml +++ b/.github/workflows/s390x.yml @@ -33,4 +33,5 @@ jobs: -DBUILD_CORO_HTTP=OFF -DBUILD_CORO_IO=OFF -DBUILD_CORO_RPC=OFF -DBUILD_EASYLOG=OFF -DBUILD_STRUCT_JSON=OFF -DBUILD_STRUCT_XML=OFF -DBUILD_STRUCT_YAML=OFF -DBUILD_UTIL=OFF -DBUILD_STRUCT_PB=OFF -DBUILD_EXAMPLES=OFF -DBUILD_BENCHMARK=OFF cmake --build ${{github.workspace}}/build -j cd ${{github.workspace}}/build/output/tests - ./struct_pack_test \ No newline at end of file + ./struct_pack_test + ./struct_pack_test_with_optimize \ No newline at end of file diff --git a/include/ylt/struct_pack.hpp b/include/ylt/struct_pack.hpp index 3d5b2c50a..971874801 100644 --- a/include/ylt/struct_pack.hpp +++ b/include/ylt/struct_pack.hpp @@ -616,7 +616,7 @@ template ())>; expected ret; - auto ec = get_field_to(ret.value(), v); + auto ec = get_field_to(ret.value(), v); if SP_UNLIKELY (ec != struct_pack::errc{}) { ret = unexpected{ec}; } @@ -627,7 +627,7 @@ template [[nodiscard]] STRUCT_PACK_INLINE auto get_field(const char *data, size_t size) { using T_Field = std::tuple_element_t())>; expected ret; - auto ec = get_field_to(ret.value(), data, size); + auto ec = get_field_to(ret.value(), data, size); if SP_UNLIKELY (ec != struct_pack::errc{}) { ret = unexpected{ec}; } @@ -644,7 +644,7 @@ template ())>; expected ret; - auto ec = get_field_to(ret.value(), reader); + auto ec = get_field_to(ret.value(), reader); if SP_UNLIKELY (ec != struct_pack::errc{}) { ret = unexpected{ec}; } diff --git a/include/ylt/struct_pack/calculate_size.hpp b/include/ylt/struct_pack/calculate_size.hpp index a1c691ea6..f9133da36 100644 --- a/include/ylt/struct_pack/calculate_size.hpp +++ b/include/ylt/struct_pack/calculate_size.hpp @@ -28,19 +28,31 @@ namespace struct_pack { struct serialize_buffer_size; namespace detail { -template +template constexpr size_info STRUCT_PACK_INLINE calculate_payload_size(const Args &...items); -template +template +constexpr std::size_t STRUCT_PACK_INLINE +calculate_fast_varint_size(const Args &...items); + +template constexpr size_info inline calculate_one_size(const T &item) { - constexpr auto id = get_type_id>(); + constexpr auto id = get_type_id, parent_tag>(); static_assert(id != detail::type_id::type_end_flag); using type = remove_cvref_t; static_assert(!std::is_pointer_v); size_info ret{}; if constexpr (id == type_id::monostate_t) { } + else if constexpr (detail::varint_t) { + if constexpr (is_enable_fast_varint_coding(parent_tag)) { + // skip it. It has been calculated in parent. + } + else { + ret.total = detail::calculate_varint_size(item); + } + } else if constexpr (std::is_fundamental_v || std::is_enum_v || id == type_id::int128_t || id == type_id::uint128_t || id == type_id::bitset_t) { @@ -49,9 +61,6 @@ constexpr size_info inline calculate_one_size(const T &item) { else if constexpr (is_trivial_view_v) { return calculate_one_size(item.get()); } - else if constexpr (detail::varint_t) { - ret.total = detail::calculate_varint_size(item); - } else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value) { ret.total = sizeof(type); @@ -147,8 +156,19 @@ constexpr size_info inline calculate_one_size(const T &item) { }); } else { - visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA { - ret += calculate_payload_size(items...); + constexpr uint64_t tag = get_parent_tag(); + if constexpr (is_enable_fast_varint_coding(tag)) { + ret.total += + visit_members(item, [](auto &&...items) CONSTEXPR_INLINE_LAMBDA { + constexpr uint64_t tag = + get_parent_tag(); // to pass msvc with c++17 + return calculate_fast_varint_size(items...); + }); + } + ret += visit_members(item, [](auto &&...items) CONSTEXPR_INLINE_LAMBDA { + constexpr uint64_t tag = + get_parent_tag(); // to pass msvc with c++17 + return calculate_payload_size(items...); }); } } @@ -158,11 +178,195 @@ constexpr size_info inline calculate_one_size(const T &item) { return ret; } -template +template constexpr size_info STRUCT_PACK_INLINE calculate_payload_size(const Args &...items) { - return (calculate_one_size(items) + ...); + return (calculate_one_size(items) + ...); +} + +struct fast_varint_result {}; + +template +constexpr std::size_t STRUCT_PACK_INLINE calculate_fast_varint_count() { + if constexpr (sizeof...(Args) == 0) { + return varint_t ? 1 : 0; + } + else { + return calculate_fast_varint_count() + + (varint_t ? 1 : 0); + } +} + +template +constexpr bool STRUCT_PACK_INLINE has_signed_varint() { + if constexpr (sizeof...(Args) == 0) { + if constexpr (varint_t) { + return std::is_signed_v< + remove_cvref_t()))>>; + } + else { + return false; + } + } + else { + if constexpr (varint_t) { + return std::is_signed_v< + remove_cvref_t()))>> || + has_signed_varint(); + } + else { + return has_signed_varint(); + } + } +} + +template +constexpr bool STRUCT_PACK_INLINE has_unsigned_varint() { + if constexpr (sizeof...(Args) == 0) { + if constexpr (varint_t) { + return std::is_unsigned_v< + remove_cvref_t()))>>; + } + else { + return false; + } + } + else { + if constexpr (varint_t) { + return std::is_unsigned_v< + remove_cvref_t()))>> || + has_unsigned_varint(); + } + else { + return has_unsigned_varint(); + } + } +} + +template +constexpr bool STRUCT_PACK_INLINE has_64bits_varint() { + if constexpr (sizeof...(Args) == 0) { + if constexpr (varint_t) { + return sizeof(Arg) == 8; + } + else { + return false; + } + } + else { + if constexpr (varint_t) { + return sizeof(Arg) == 8 || has_64bits_varint(); + } + else { + return has_64bits_varint(); + } + } +} + +template +constexpr void STRUCT_PACK_INLINE get_fast_varint_width_impl( + const Arg &item, int &non_zero_cnt32, int &non_zero_cnt64, + uint64_t &unsigned_max, int64_t &signed_max) { + if constexpr (varint_t) { + if (get_varint_value(item)) { + if constexpr (sizeof(Arg) == 4) { + ++non_zero_cnt32; + } + else if constexpr (sizeof(Arg) == 8) { + ++non_zero_cnt64; + } + else { + static_assert(!sizeof(Arg), "illegal branch"); + } + if constexpr (std::is_unsigned_v>) { + unsigned_max = std::max(unsigned_max, get_varint_value(item)); + } + else { + signed_max = + std::max(signed_max, get_varint_value(item) > 0 + ? get_varint_value(item) + : -(get_varint_value(item) + 1)); + } + } + } } + +template +constexpr int STRUCT_PACK_INLINE +get_fast_varint_width_from_max(uint64_t unsigned_max, int64_t signed_max) { + int width_unsigned = 0, width_signed = 0; + if constexpr (has_unsigned_varint()) { + if SP_LIKELY (unsigned_max <= UINT8_MAX) { + width_unsigned = 0; + } + else if (unsigned_max <= UINT16_MAX) { + width_unsigned = 1; + } + else if (unsigned_max <= UINT32_MAX) { + width_unsigned = 2; + } + else { + width_unsigned = 3; + } + } + if constexpr (has_signed_varint()) { + if SP_LIKELY (signed_max <= INT8_MAX) { + width_signed = 0; + } + else if (signed_max <= INT16_MAX) { + width_signed = 1; + } + else if (signed_max <= INT32_MAX) { + width_signed = 2; + } + else { + width_signed = 3; + } + } + if constexpr (has_signed_varint() && + has_unsigned_varint()) { + return (std::max)(width_unsigned, width_signed); + } + else if constexpr (has_signed_varint()) { + return width_signed; + } + else if constexpr (has_unsigned_varint()) { + return width_unsigned; + } + else { + static_assert(sizeof...(Args), "there should has a varint"); + return 0; + } +} + +template +constexpr int STRUCT_PACK_INLINE get_fast_varint_width(const Args &...items) { + uint64_t unsigned_max = 0; + int64_t signed_max = 0; + int non_zero_cnt32 = 0, non_zero_cnt64 = 0; + (get_fast_varint_width_impl(items, non_zero_cnt32, non_zero_cnt64, + unsigned_max, signed_max), + ...); + auto width = (1 << struct_pack::detail::get_fast_varint_width_from_max< + parent_tag, Args...>(unsigned_max, signed_max)); + return width * non_zero_cnt64 + (width > 4 ? 4 : width) * non_zero_cnt32; +} + +template +constexpr std::size_t STRUCT_PACK_INLINE +calculate_fast_varint_size(const Args &...items) { + constexpr auto cnt = calculate_fast_varint_count(); + constexpr auto bitset_size = ((cnt + 2) + 7) / 8; + if constexpr (cnt == 0) { + return 0; + } + else { + auto width = get_fast_varint_width(items...); + return width + bitset_size; + } +} + template STRUCT_PACK_INLINE constexpr serialize_buffer_size get_serialize_runtime_info( const Args &...args); diff --git a/include/ylt/struct_pack/derived_helper.hpp b/include/ylt/struct_pack/derived_helper.hpp index ebc1e69a4..ab7b0a5e5 100644 --- a/include/ylt/struct_pack/derived_helper.hpp +++ b/include/ylt/struct_pack/derived_helper.hpp @@ -112,7 +112,7 @@ struct deserialize_derived_class_helper { } }; -template +template constexpr size_info inline calculate_one_size(const T &item); template diff --git a/include/ylt/struct_pack/endian_wrapper.hpp b/include/ylt/struct_pack/endian_wrapper.hpp index 7b23fa07c..0b4037cf5 100644 --- a/include/ylt/struct_pack/endian_wrapper.hpp +++ b/include/ylt/struct_pack/endian_wrapper.hpp @@ -19,6 +19,7 @@ #include #include "reflection.hpp" +#include "ylt/struct_pack/marco.h" #include "ylt/struct_pack/util.h" namespace struct_pack::detail { @@ -131,7 +132,7 @@ inline uint64_t bswap64(uint64_t raw) { }; template -void write_wrapper(writer_t& writer, const char* data) { +void write_wrapper(writer_t& writer, const char* SP_RESTRICT data) { if constexpr (is_system_little_endian || block_size == 1) { writer.write(data, block_size); } @@ -164,6 +165,34 @@ void write_bytes_array(writer_t& writer, const char* data, std::size_t length) { else writer.write(data, length); } +template +void low_bytes_write_wrapper(writer_t& writer, const T& elem) { + static_assert(sizeof(T) >= block_size); + if constexpr (is_system_little_endian) { + const char* data = (const char*)&elem; + writer.write(data, block_size); + } + else if constexpr (block_size == sizeof(T)) { + write_wrapper(writer, (const char*)&elem); + } + else { + const char* data = sizeof(T) - block_size + (const char*)&elem; + if constexpr (block_size == 1) { + writer.write(data, block_size); + } + else if constexpr (block_size == 2) { + auto tmp = bswap16(*(uint16_t*)data); + writer.write((char*)&tmp, block_size); + } + else if constexpr (block_size == 4) { + auto tmp = bswap32(*(uint32_t*)data); + writer.write((char*)&tmp, block_size); + } + else { + static_assert(!sizeof(writer), "illegal block size(should be 1,2,4)"); + } + } +} template bool read_wrapper(reader_t& reader, char* SP_RESTRICT data) { if constexpr (is_system_little_endian || block_size == 1) { @@ -198,9 +227,40 @@ bool read_wrapper(reader_t& reader, char* SP_RESTRICT data) { template bool read_bytes_array(reader_t& reader, char* SP_RESTRICT data, std::size_t length) { - if SP_UNLIKELY (length >= PTRDIFF_MAX) - unreachable(); - else - return static_cast(reader.read(data, length)); + return static_cast(reader.read(data, length)); +} +template +bool low_bytes_read_wrapper(reader_t& reader, T& elem) { + static_assert(sizeof(T) >= block_size); + if constexpr (is_system_little_endian) { + char* data = (char*)&elem; + return static_cast(reader.read(data, block_size)); + } + else if constexpr (block_size == sizeof(T)) { + return read_wrapper(reader, (char*)&elem); + } + else { + char* data = (char*)&elem + sizeof(T) - block_size; + if constexpr (block_size > 1) { + char tmp[block_size]; + bool res = static_cast(reader.read(tmp, block_size)); + if SP_UNLIKELY (!res) { + return res; + } + if constexpr (block_size == 2) { + *(uint16_t*)data = bswap16(*(uint16_t*)tmp); + } + else if constexpr (block_size == 4) { + *(uint32_t*)data = bswap32(*(uint32_t*)tmp); + } + else { + static_assert(!sizeof(reader), "illegal block size(should be 1,2,4)"); + } + return true; + } + else { + return static_cast(reader.read(data, block_size)); + } + } } }; // namespace struct_pack::detail \ No newline at end of file diff --git a/include/ylt/struct_pack/error_code.hpp b/include/ylt/struct_pack/error_code.hpp index 268cf87e0..81b67df79 100644 --- a/include/ylt/struct_pack/error_code.hpp +++ b/include/ylt/struct_pack/error_code.hpp @@ -24,6 +24,7 @@ enum class errc { invalid_buffer, hash_conflict, seek_failed, + too_width_size }; namespace detail { diff --git a/include/ylt/struct_pack/packer.hpp b/include/ylt/struct_pack/packer.hpp index 4a4e937ec..4cd02e791 100644 --- a/include/ylt/struct_pack/packer.hpp +++ b/include/ylt/struct_pack/packer.hpp @@ -14,11 +14,16 @@ * limitations under the License. */ #pragma once +#include +#include #include +#include #include "calculate_size.hpp" #include "endian_wrapper.hpp" #include "reflection.hpp" +#include "ylt/struct_pack/util.h" +#include "ylt/struct_pack/varint.hpp" namespace struct_pack::detail { template < #if __cpp_concepts >= 201907L @@ -95,23 +100,23 @@ class packer { } if constexpr (hash_head % 2) { // has more metainfo auto metainfo = info.metainfo(); + std::size_t sz = info.size(); write_wrapper(writer_, (char *)&metainfo); if constexpr (serialize_static_config::has_compatible) { - uint16_t len16; - uint32_t len32; - uint64_t len64; switch (metainfo & 0b11) { case 1: - len16 = info.size(); - write_wrapper<2>(writer_, (char *)&len16); + low_bytes_write_wrapper<2>(writer_, sz); break; case 2: - len32 = info.size(); - write_wrapper<4>(writer_, (char *)&len32); + low_bytes_write_wrapper<4>(writer_, sz); break; case 3: - len64 = info.size(); - write_wrapper<8>(writer_, (char *)&len64); + if constexpr (sizeof(std::size_t) >= 8) { + low_bytes_write_wrapper<8>(writer_, sz); + } + else { + unreachable(); + } break; default: unreachable(); @@ -127,13 +132,13 @@ class packer { } private: - template + template constexpr void STRUCT_PACK_INLINE serialize_many(const First &first_item, const Args &...items) { - serialize_one(first_item); + serialize_one(first_item); if constexpr (sizeof...(items) > 0) { - serialize_many(items...); + serialize_many(items...); } } constexpr void STRUCT_PACK_INLINE write_padding(std::size_t sz) { @@ -143,11 +148,95 @@ class packer { } } - template + template + static constexpr void STRUCT_PACK_INLINE + get_fast_varint_width_impl(char (&vec)[sz], unsigned int &i, const Arg &item, + uint64_t &unsigned_max, int64_t &signed_max) { + if constexpr (varint_t) { + if (get_varint_value(item) != 0) { + vec[i / 8] |= (0b1 << (i % 8)); + if constexpr (std::is_unsigned_v>) { + unsigned_max = + std::max(unsigned_max, get_varint_value(item)); + } + else { + signed_max = std::max(signed_max, + get_varint_value(item) > 0 + ? get_varint_value(item) + : -(get_varint_value(item) + 1)); + } + } + ++i; + } + } + + template + static constexpr int STRUCT_PACK_INLINE + get_fast_varint_width(char (&vec)[sz], const Args &...items) { + uint64_t unsigned_max = 0; + int64_t signed_max = 0; + unsigned int i = 0; + (get_fast_varint_width_impl(vec, i, items, unsigned_max, + signed_max), + ...); + return get_fast_varint_width_from_max(unsigned_max, + signed_max); + } + + template + constexpr void STRUCT_PACK_INLINE serialize_one_fast_varint(const Arg &item) { + if constexpr (varint_t) { + if (get_varint_value(item)) + low_bytes_write_wrapper<(std::min)(sz, sizeof(Arg))>( + writer_, get_varint_value(item)); + } + } + + template + constexpr void STRUCT_PACK_INLINE + serialize_fast_varint(const Args &...items) { + constexpr auto cnt = calculate_fast_varint_count(); + constexpr auto bitset_size = ((cnt + 2) + 7) / 8; + if constexpr (cnt == 0) { + return; + } + else { + char vec[bitset_size]{}; + auto width = get_fast_varint_width(vec, items...); + vec[cnt / 8] |= (width & 0b1) << (cnt % 8); + vec[(cnt + 1) / 8] |= (!!(width & 0b10)) << ((cnt + 1) % 8); + write_bytes_array(writer_, vec, bitset_size); + switch (width) { + case 0: + (serialize_one_fast_varint(items), ...); + break; + case 1: + (serialize_one_fast_varint(items), ...); + break; + case 2: + (serialize_one_fast_varint(items), ...); + break; + case 3: + if constexpr (has_64bits_varint()) { + (serialize_one_fast_varint(items), ...); + } + else { + unreachable(); + } + break; + default: + unreachable(); + } + } + } + + template constexpr void inline serialize_one(const T &item) { using type = remove_cvref_t; static_assert(!std::is_pointer_v); - constexpr auto id = get_type_id(); + constexpr auto id = get_type_id(); if constexpr (is_trivial_view_v) { return serialize_one(item.get()); } @@ -158,6 +247,14 @@ class packer { else if constexpr (std::is_same_v) { // do nothing } + else if constexpr (detail::varint_t) { + if constexpr (is_enable_fast_varint_coding(parent_tag)) { + // do nothing + } + else { + detail::serialize_varint(writer_, item); + } + } else if constexpr (std::is_fundamental_v || std::is_enum_v || id == type_id::int128_t || id == type_id::uint128_t) { write_wrapper(writer_, (char *)&item); @@ -170,7 +267,7 @@ class packer { write_wrapper(writer_, (char *)&has_value); if (has_value) { if constexpr (is_base_class) { - bool is_ok; + bool is_ok{}; uint32_t id = item->get_struct_pack_id(); auto index = search_type_by_md5( item->get_struct_pack_id(), is_ok); @@ -187,9 +284,6 @@ class packer { } } } - else if constexpr (detail::varint_t) { - detail::serialize_varint(writer_, item); - } else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value && is_little_endian_copyable) { @@ -202,43 +296,45 @@ class packer { } } else if constexpr (map_container || container) { + auto size = item.size(); if constexpr (size_type == 1) { - uint8_t size = item.size(); - write_wrapper(writer_, (char *)&size); + low_bytes_write_wrapper(writer_, size); } #ifdef STRUCT_PACK_OPTIMIZE else if constexpr (size_type == 2) { - uint16_t size = item.size(); - write_wrapper(writer_, (char *)&size); + low_bytes_write_wrapper(writer_, size); } else if constexpr (size_type == 4) { - uint32_t size = item.size(); - write_wrapper(writer_, (char *)&size); + low_bytes_write_wrapper(writer_, size); } else if constexpr (size_type == 8) { - uint64_t size = item.size(); - write_wrapper(writer_, (char *)&size); + 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."); } #else else { - uint16_t size16; - uint32_t size32; - uint64_t size64; + auto size = item.size(); switch ((info.metainfo() & 0b11000) >> 3) { case 1: - size16 = item.size(); - write_wrapper<2>(writer_, (char *)&size16); + low_bytes_write_wrapper<2>(writer_, size); break; case 2: - size32 = item.size(); - write_wrapper<4>(writer_, (char *)&size32); + low_bytes_write_wrapper<4>(writer_, size); break; case 3: - size64 = item.size(); - write_wrapper<8>(writer_, (char *)&size64); + if constexpr (sizeof(std::size_t) >= 8) { + low_bytes_write_wrapper<8>(writer_, size); + } + else { + unreachable(); + } break; default: unreachable(); @@ -314,7 +410,7 @@ class packer { else if constexpr ((is_trivial_serializable::value && !is_little_endian_copyable) || is_trivial_serializable::value) { - visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA { + visit_members(item, [this](auto &&...items) CONSTEXPR_INLINE_LAMBDA { int i = 1; ((serialize_one(items), write_padding(align::padding_size[i++])), @@ -322,8 +418,19 @@ class packer { }); } else { - visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA { - serialize_many(items...); + constexpr uint64_t tag = get_parent_tag(); + if constexpr (is_enable_fast_varint_coding(tag)) { + visit_members( + item, [this](auto &&...items) CONSTEXPR_INLINE_LAMBDA { + constexpr uint64_t tag = + get_parent_tag(); // to pass msvc with c++17 + serialize_fast_varint(items...); + }); + } + visit_members(item, [this](auto &&...items) CONSTEXPR_INLINE_LAMBDA { + constexpr uint64_t tag = + get_parent_tag(); // to pass msvc with c++17 + serialize_many(items...); }); } } @@ -344,7 +451,7 @@ class packer { else if constexpr (unique_ptr) { if (item != nullptr) { if constexpr (is_base_class) { - bool is_ok; + bool is_ok{}; auto index = search_type_by_md5( item->get_struct_pack_id(), is_ok); assert(is_ok); @@ -440,7 +547,12 @@ STRUCT_PACK_MAY_INLINE void serialize_to(Writer &writer, o.template serialize(args...); break; case 3: - o.template serialize(args...); + if constexpr (sizeof(std::size_t) >= 8) { + o.template serialize(args...); + } + else { + unreachable(); + } break; #else case 1: diff --git a/include/ylt/struct_pack/reflection.hpp b/include/ylt/struct_pack/reflection.hpp index f130c9f14..f92779654 100644 --- a/include/ylt/struct_pack/reflection.hpp +++ b/include/ylt/struct_pack/reflection.hpp @@ -38,11 +38,13 @@ namespace struct_pack { -enum sp_config { +enum sp_config : uint64_t { DEFAULT = 0, DISABLE_TYPE_INFO = 0b1, ENABLE_TYPE_INFO = 0b10, - DISABLE_ALL_META_INFO = 0b11 + DISABLE_ALL_META_INFO = 0b11, + ENCODING_WITH_VARINT = 0b100, + USE_FAST_VARINT = 0b1000 }; namespace detail { @@ -544,16 +546,33 @@ template #if __cpp_concepts >= 201907L template - concept user_defined_config = std::is_same_v())),struct_pack::sp_config>; + concept user_defined_config_by_ADL = std::is_same_v())),struct_pack::sp_config>; #else template - struct user_defined_config_impl : std::false_type {}; + struct user_defined_config_by_ADL_impl : std::false_type {}; template - struct user_defined_config_impl())),struct_pack::sp_config>>>> : std::true_type {}; + template + constexpr bool user_defined_config_by_ADL = user_defined_config_by_ADL_impl::value; +#endif + +#if __cpp_concepts >= 201907L + template + concept user_defined_config = requires { + Type::struct_pack_config; + }; +#else + template + struct user_defined_config_impl : std::false_type {}; + + template + struct user_defined_config_impl> + : std::true_type {}; + template constexpr bool user_defined_config = user_defined_config_impl::value; #endif @@ -735,19 +754,22 @@ template constexpr bool sintable_t = std::is_same_v> || std::is_same_v>; - template - constexpr bool varint_t = varintable_t || sintable_t; + template + constexpr bool varint_t = varintable_t || sintable_t || ((parent_tag&struct_pack::ENCODING_WITH_VARINT) && (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v)); template constexpr inline bool is_trivial_view_v = false; - template + template + constexpr uint64_t get_parent_tag(); + + template struct is_trivial_serializable { private: - template + template static constexpr bool class_visit_helper(std::index_sequence) { return (is_trivial_serializable, - ignore_compatible_field>::value && + ignore_compatible_field,parent_tag_>::value && ...); } static constexpr bool solve() { @@ -757,6 +779,9 @@ template else if constexpr (std::is_abstract_v) { return false; } + else if constexpr (varint_t) { + return false; + } else if constexpr (is_compatible_v || is_trivial_view_v) { return ignore_compatible_field; } @@ -782,8 +807,7 @@ template return false; } else if constexpr (container || optional || is_variant_v || - unique_ptr || expected || container_adapter || - varint_t) { + unique_ptr || expected || container_adapter) { return false; } else if constexpr (pair) { @@ -796,8 +820,9 @@ template return class_visit_helper(std::make_index_sequence>{}); } else if constexpr (std::is_class_v) { + constexpr auto tag = get_parent_tag(); using U = decltype(get_types()); - return class_visit_helper(std::make_index_sequence>{}); + return class_visit_helper(std::make_index_sequence>{}); } else return false; diff --git a/include/ylt/struct_pack/type_calculate.hpp b/include/ylt/struct_pack/type_calculate.hpp index 365283830..86c93d23c 100644 --- a/include/ylt/struct_pack/type_calculate.hpp +++ b/include/ylt/struct_pack/type_calculate.hpp @@ -153,6 +153,29 @@ constexpr decltype(auto) get_type_end_flag() { } } +template +constexpr uint64_t get_parent_tag_impl() { + if constexpr (user_defined_config_by_ADL) { + return static_cast(set_sp_config((Arg *)nullptr)); + } + else if constexpr (user_defined_config) { + return static_cast(Arg::struct_pack_config); + } + else { + return 0; + } +} + +template +constexpr uint64_t get_parent_tag() { + if constexpr (sizeof...(Args) == 0) { + return 0; + } + else { + return get_parent_tag_impl(); + } +} + template constexpr decltype(auto) get_type_literal(std::index_sequence); @@ -173,7 +196,8 @@ constexpr decltype(auto) get_type_literal() { get_size_literal(); } else { - constexpr auto id = get_type_id(); + constexpr auto parent_tag = get_parent_tag(); + constexpr auto id = get_type_id(); constexpr auto begin = string_literal{{static_cast(id)}}; if constexpr (id == type_id::struct_t) { using Args = decltype(get_types()); @@ -659,7 +683,7 @@ template constexpr bool check_if_add_type_literal() { constexpr auto config = conf & 0b11; if constexpr (config == sp_config::DEFAULT) { - if constexpr (struct_pack::detail::user_defined_config) { + if constexpr (struct_pack::detail::user_defined_config_by_ADL) { constexpr auto config = set_sp_config((T *)nullptr) & 0b11; if constexpr (config == sp_config::DEFAULT) { return serialize_static_config::has_type_literal; @@ -668,6 +692,15 @@ constexpr bool check_if_add_type_literal() { return config == sp_config::ENABLE_TYPE_INFO; } } + else if constexpr (struct_pack::detail::user_defined_config) { + constexpr auto config = T::struct_pack_config & 0b11; + if constexpr (config == sp_config::DEFAULT) { + return serialize_static_config::has_type_literal; + } + else { + return config == sp_config::ENABLE_TYPE_INFO; + } + } else { return serialize_static_config::has_type_literal; } @@ -760,12 +793,18 @@ template constexpr bool check_if_disable_hash_head_impl() { constexpr auto config = conf & 0b11; if constexpr (config != sp_config::DISABLE_ALL_META_INFO) { - if constexpr (struct_pack::detail::user_defined_config) { + if constexpr (struct_pack::detail::user_defined_config_by_ADL) { constexpr auto config = set_sp_config((T *)nullptr) & 0b11; if constexpr (config == sp_config::DISABLE_ALL_META_INFO) { return true; } } + else if constexpr (struct_pack::detail::user_defined_config) { + constexpr auto config = T::struct_pack_config & 0b11; + if constexpr (config == sp_config::DISABLE_ALL_META_INFO) { + return true; + } + } return false; } return true; diff --git a/include/ylt/struct_pack/type_id.hpp b/include/ylt/struct_pack/type_id.hpp index dba4aeccb..a25701774 100644 --- a/include/ylt/struct_pack/type_id.hpp +++ b/include/ylt/struct_pack/type_id.hpp @@ -47,10 +47,14 @@ enum class type_id { float32_t, float64_t, float128_t, - v_int32_t, // variable size int - v_int64_t, // variable size int - v_uint32_t, // variable size unsigned int - v_uint64_t, // variable size unsigned int + vint32_t, // variable size int + vint64_t, // variable size int + vuint32_t, // variable size unsigned int + vuint64_t, // variable size unsigned int + fast_vint32_t, // variable size int with fast encoding + fast_vint64_t, // variable size int with fast encoding + fast_vuint32_t, // variable size unsigned int with fast encoding + fast_vuint64_t, // variable size unsigned int with fast encoding // template type string_t = 128, array_t, @@ -74,22 +78,49 @@ enum class type_id { type_end_flag = 255, }; -template +template constexpr type_id get_varint_type() { - if constexpr (std::is_same_v) { - return type_id::v_int32_t; - } - else if constexpr (std::is_same_v) { - return type_id::v_int64_t; - } - else if constexpr (std::is_same_v) { - return type_id::v_uint32_t; - } - else if constexpr (std::is_same_v) { - return type_id::v_uint64_t; + if constexpr (is_enable_fast_varint_coding(parent_tag)) { + if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::fast_vint32_t; + } + else if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::fast_vint64_t; + } + else if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::fast_vuint32_t; + } + else if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::fast_vuint64_t; + } + else { + static_assert(!sizeof(T), "unsupported varint type!"); + } } else { - static_assert(!std::is_same_v, "unsupported varint type!"); + if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::vint32_t; + } + else if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::vint64_t; + } + else if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::vuint32_t; + } + else if constexpr (std::is_same_v || + std::is_same_v) { + return type_id::vuint64_t; + } + else { + static_assert(!sizeof(T), "unsupported varint type!"); + } } } @@ -239,13 +270,16 @@ constexpr type_id get_floating_point_type() { } } -template +template constexpr type_id get_type_id() { static_assert(CHAR_BIT == 8); // compatible member, which should be ignored in MD5 calculated. if constexpr (optional && is_compatible_v) { return type_id::compatible_t; } + else if constexpr (detail::varint_t) { + return get_varint_type(); + } else if constexpr (std::is_enum_v) { return get_integral_type>(); } @@ -260,9 +294,6 @@ constexpr type_id get_type_id() { else if constexpr (std::is_floating_point_v) { return get_floating_point_type(); } - else if constexpr (detail::varint_t) { - return get_varint_type(); - } else if constexpr (std::is_same_v || std::is_same_v || std::is_abstract_v) { return type_id::monostate_t; diff --git a/include/ylt/struct_pack/unpacker.hpp b/include/ylt/struct_pack/unpacker.hpp index 1ecf9615a..112655af5 100644 --- a/include/ylt/struct_pack/unpacker.hpp +++ b/include/ylt/struct_pack/unpacker.hpp @@ -129,12 +129,21 @@ class unpacker { err_code = deserialize_many<4, UINT64_MAX, true>(t, args...); break; case 3: - err_code = deserialize_many<8, UINT64_MAX, true>(t, args...); + if constexpr (sizeof(std::size_t) >= 8) { + err_code = deserialize_many<8, UINT64_MAX, true>(t, args...); + } + else { + return struct_pack::errc::too_width_size; + } break; #else - case 1: - case 2: + case 3: + if constexpr (sizeof(std::size_t) < 8) { + return struct_pack::errc::too_width_size; + } + case 2: + case 1: err_code = deserialize_many<2, UINT64_MAX, true>(t, args...); break; #endif @@ -181,12 +190,20 @@ class unpacker { err_code = deserialize_many<4, UINT64_MAX, true>(t, args...); break; case 3: - err_code = deserialize_many<8, UINT64_MAX, true>(t, args...); + if constexpr (sizeof(std::size_t) >= 8) { + err_code = deserialize_many<8, UINT64_MAX, true>(t, args...); + } + else { + return struct_pack::errc::too_width_size; + } break; #else - case 1: - case 2: case 3: + if constexpr (sizeof(std::size_t) < 8) { + return struct_pack::errc::too_width_size; + } + case 2: + case 1: err_code = deserialize_many<2, UINT64_MAX, true>(t, args...); break; #endif @@ -235,12 +252,20 @@ class unpacker { err_code = get_field_impl<4, UINT64_MAX, U, I>(field); break; case 3: - err_code = get_field_impl<8, UINT64_MAX, U, I>(field); + if constexpr (sizeof(std::size_t) >= 8) { + err_code = get_field_impl<8, UINT64_MAX, U, I>(field); + } + else { + return struct_pack::errc::too_width_size; + } break; #else - case 1: - case 2: case 3: + if constexpr (sizeof(std::size_t) < 8) { + return struct_pack::errc::too_width_size; + } + case 2: + case 1: err_code = get_field_impl<2, UINT64_MAX, U, I>(field); break; #endif @@ -294,18 +319,27 @@ class unpacker { ...); break; case 3: - ([&] { - err_code = - deserialize_many<8, compatible_version_number[I], true>( - t, args...); - return err_code == errc::ok; - }() && - ...); + if constexpr (sizeof(std::size_t) >= 8) { + ([&] { + err_code = + deserialize_many<8, compatible_version_number[I], true>( + t, args...); + return err_code == errc::ok; + }() && + ...); + } + else { + return struct_pack::errc::too_width_size; + } + break; #else - case 1: - case 2: case 3: + if constexpr (sizeof(std::size_t) < 8) { + return struct_pack::errc::too_width_size; + } + case 2: + case 1: ([&] { err_code = deserialize_many<2, compatible_version_number[I], true>( @@ -363,18 +397,26 @@ class unpacker { ...); break; case 3: - ([&] { - err_code = - get_field_impl<8, compatible_version_number[Is], U, I>( - field); - return err_code == errc::ok; - }() && - ...); + if constexpr (sizeof(std::size_t) >= 8) { + ([&] { + err_code = + get_field_impl<8, compatible_version_number[Is], U, I>( + field); + return err_code == errc::ok; + }() && + ...); + } + else { + return errc::too_width_size; + } break; #else - case 1: - case 2: case 3: + if constexpr (sizeof(std::size_t) < 8) { + return errc::too_width_size; + } + case 2: + case 1: ([&] { err_code = get_field_impl<2, compatible_version_number[Is], U, I>( @@ -389,7 +431,7 @@ class unpacker { } if (size_type_ == UCHAR_MAX) { // reuse size_type_ as a tag that the buffer miss some - // compatible field, whic is legal. + // compatible field, which is legal. err_code = {}; } return err_code; @@ -447,24 +489,27 @@ class unpacker { deserialize_compatible(unsigned compatible_sz_len) { constexpr std::size_t sz[] = {0, 2, 4, 8}; auto len_sz = sz[compatible_sz_len]; - uint64_t data_len64; - uint32_t data_len32; - uint16_t data_len16; + std::size_t data_len = 0; bool result; switch (compatible_sz_len) { case 1: - if SP_LIKELY (read_wrapper<2>(reader_, (char *)&data_len16)) { - return {errc{}, data_len16}; + if SP_LIKELY (low_bytes_read_wrapper<2>(reader_, data_len)) { + return {errc{}, data_len}; } break; case 2: - if SP_LIKELY (read_wrapper<4>(reader_, (char *)&data_len32)) { - return {errc{}, data_len32}; + if SP_LIKELY (low_bytes_read_wrapper<4>(reader_, data_len)) { + return {errc{}, data_len}; } break; case 3: - if SP_LIKELY (read_wrapper<8>(reader_, (char *)&data_len64)) { - return {errc{}, data_len64}; + if constexpr (sizeof(std::size_t) >= 8) { + if SP_LIKELY (low_bytes_read_wrapper<8>(reader_, data_len)) { + return {errc{}, data_len}; + } + } + else { + return {errc::too_width_size, 0}; } break; default: @@ -575,15 +620,22 @@ class unpacker { constexpr struct_pack::errc STRUCT_PACK_INLINE deserialize_many() { return {}; } - template + template constexpr struct_pack::errc STRUCT_PACK_INLINE deserialize_many(First &&first_item, Args &&...items) { - auto code = deserialize_one(first_item); + auto code = + deserialize_one(first_item); if SP_UNLIKELY (code != struct_pack::errc{}) { return code; } - return deserialize_many(items...); + if constexpr (sizeof...(items) > 0) { + return deserialize_many( + items...); + } + else { + return code; + } } constexpr struct_pack::errc STRUCT_PACK_INLINE @@ -596,12 +648,118 @@ class unpacker { } } - template + template + constexpr struct_pack::errc STRUCT_PACK_INLINE deserialize_one_fast_varint( + char (&vec)[bitset_width], unsigned int &i, Arg &item) { + if constexpr (varint_t) { + constexpr auto real_width = (std::min)(width, sizeof(Arg)); + auto index = i++; + if (!(vec[index / 8] & (0b1 << (index % 8)))) + return {}; + if constexpr (!no_skip) { + reader_.ignore(real_width) ? errc{} : errc::no_buffer_space; + } + else { + bool ec{}; + if constexpr (std::is_unsigned_v>) { + get_varint_value(item) = 0; + bool ec = low_bytes_read_wrapper(reader_, + get_varint_value(item)); + if SP_UNLIKELY (!ec) { + return errc::no_buffer_space; + } + } + else { + typename int_t::type target; + ec = read_wrapper(reader_, (char *)&target); + if SP_UNLIKELY (!ec) { + return errc::no_buffer_space; + } + item = target; + } + return {}; + } + } + else { + return {}; + } + } + template + constexpr struct_pack::errc STRUCT_PACK_INLINE deserialize_fast_varint_helper( + char (&vec)[bitset_width], unsigned int &i, Arg &item, Args &...items) { + auto ec = + deserialize_one_fast_varint(vec, i, item); + if constexpr (sizeof...(items) > 0) { + if SP_UNLIKELY (ec != errc{}) { + return ec; + } + else { + return deserialize_fast_varint_helper( + vec, i, items...); + } + } + else { + return ec; + } + } + + template + constexpr struct_pack::errc STRUCT_PACK_INLINE + deserialize_fast_varint(Args &...items) { + constexpr auto cnt = calculate_fast_varint_count(); + constexpr auto bitset_size = ((cnt + 2) + 7) / 8; + if constexpr (cnt == 0) { + static_assert(cnt != 0, "ilegal branch"); + return {}; + } + else { + char vec[bitset_size]; + if (auto ec = read_bytes_array(reader_, vec, bitset_size); !ec) { + return errc::no_buffer_space; + } + int width = !!(vec[cnt / 8] & (0b1 << (cnt % 8))) + + !!(vec[(cnt + 1) / 8] & (0b1 << ((cnt + 1) % 8))) * 2; + unsigned int i = 0; + struct_pack::errc ec{}; + switch (width) { + case 0: + ec = deserialize_fast_varint_helper(vec, i, + items...); + break; + case 1: + ec = deserialize_fast_varint_helper(vec, i, + items...); + break; + case 2: + ec = deserialize_fast_varint_helper(vec, i, + items...); + break; + case 3: + if constexpr (has_64bits_varint()) { + ec = deserialize_fast_varint_helper( + vec, i, items...); + } + else { + return struct_pack::errc::invalid_buffer; + } + break; + default: + unreachable(); + } + return ec; + } + } + + template constexpr struct_pack::errc inline deserialize_one(T &item) { struct_pack::errc code{}; using type = remove_cvref_t; static_assert(!std::is_pointer_v); - constexpr auto id = get_type_id(); + constexpr auto id = get_type_id(); if constexpr (is_trivial_view_v) { static_assert(view_reader_t, "The Reader isn't a view_reader, can't deserialize " @@ -626,6 +784,14 @@ class unpacker { else if constexpr (std::is_same_v) { // do nothing } + else if constexpr (detail::varint_t) { + if constexpr (is_enable_fast_varint_coding(parent_tag)) { + // do nothing, we have deserialized it in parent. + } + else { + code = detail::deserialize_varint(reader_, item); + } + } else if constexpr (std::is_fundamental_v || std::is_enum_v || id == type_id::int128_t || id == type_id::uint128_t) { if constexpr (NotSkip) { @@ -658,9 +824,9 @@ class unpacker { return {}; } if constexpr (is_base_class) { - uint32_t id; + uint32_t id{}; read_wrapper(reader_, (char *)&id); - bool ok; + bool ok{}; auto index = search_type_by_md5(id, ok); if SP_UNLIKELY (!ok) { return errc::invalid_buffer; @@ -679,9 +845,6 @@ class unpacker { deserialize_one(*item); } } - else if constexpr (detail::varint_t) { - code = detail::deserialize_varint(reader_, item); - } else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value && is_little_endian_copyable) { @@ -706,33 +869,33 @@ class unpacker { } } else if constexpr (container) { - uint16_t size16; - uint32_t size32; - uint64_t size64; - bool result; + std::size_t size64 = 0; + bool result{}; if constexpr (size_type == 1) { - uint8_t size8; - if SP_UNLIKELY (!read_wrapper(reader_, (char *)&size8)) { + if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, size64)) { return struct_pack::errc::no_buffer_space; } - size64 = size8; } #ifdef STRUCT_PACK_OPTIMIZE else if constexpr (size_type == 2) { - if SP_UNLIKELY (!read_wrapper(reader_, (char *)&size16)) { + if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, size64)) { return struct_pack::errc::no_buffer_space; } - size64 = size16; } else if constexpr (size_type == 4) { - if SP_UNLIKELY (!read_wrapper(reader_, (char *)&size32)) { + if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, size64)) { return struct_pack::errc::no_buffer_space; } - size64 = size32; } else if constexpr (size_type == 8) { - if SP_UNLIKELY (!read_wrapper(reader_, (char *)&size64)) { - return struct_pack::errc::no_buffer_space; + if constexpr (sizeof(std::size_t) >= 8) { + if SP_UNLIKELY (!low_bytes_read_wrapper(reader_, + size64)) { + return struct_pack::errc::no_buffer_space; + } + } + else { + static_assert("illegal branch"); } } else { @@ -742,20 +905,23 @@ class unpacker { else { switch (size_type_) { case 1: - if SP_UNLIKELY (!read_wrapper<2>(reader_, (char *)&size16)) { + if SP_UNLIKELY (!low_bytes_read_wrapper<2>(reader_, size64)) { return struct_pack::errc::no_buffer_space; } - size64 = size16; break; case 2: - if SP_UNLIKELY (!read_wrapper<4>(reader_, (char *)&size32)) { + if SP_UNLIKELY (!low_bytes_read_wrapper<4>(reader_, size64)) { return struct_pack::errc::no_buffer_space; } - size64 = size32; break; case 3: - if SP_UNLIKELY (!read_wrapper<8>(reader_, (char *)&size64)) { - return struct_pack::errc::no_buffer_space; + if constexpr (sizeof(std::size_t) >= 8) { + if SP_UNLIKELY (!low_bytes_read_wrapper<8>(reader_, size64)) { + return struct_pack::errc::no_buffer_space; + } + } + else { + unreachable(); } break; default: @@ -763,7 +929,7 @@ class unpacker { } } #endif - if SP_UNLIKELY (size64 == 0) { + if (size64 == 0) { return {}; } if constexpr (map_container) { @@ -829,15 +995,11 @@ class unpacker { } else if constexpr (is_little_endian_copyable) { - if SP_UNLIKELY (mem_sz >= PTRDIFF_MAX) - unreachable(); - else { - item.resize(size64); - if SP_UNLIKELY (!read_bytes_array( - reader_, (char *)item.data(), - size64 * sizeof(value_type))) { - return struct_pack::errc::no_buffer_space; - } + item.resize(size64); + if SP_UNLIKELY (!read_bytes_array( + reader_, (char *)item.data(), + size64 * sizeof(value_type))) { + return struct_pack::errc::no_buffer_space; } } else { @@ -972,9 +1134,25 @@ class unpacker { }); } else { - visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA { - code = deserialize_many(items...); - }); + constexpr uint64_t tag = get_parent_tag(); + if constexpr (is_enable_fast_varint_coding(tag)) { + code = visit_members( + item, [this](auto &&...items) CONSTEXPR_INLINE_LAMBDA { + constexpr uint64_t tag = + get_parent_tag(); // to pass msvc with c++17 + return deserialize_fast_varint(items...); + }); + if SP_UNLIKELY (code != errc::ok) { + return code; + } + } + code = visit_members( + item, [this](auto &&...items) CONSTEXPR_INLINE_LAMBDA { + constexpr uint64_t tag = + get_parent_tag(); // to pass msvc with c++17 + return deserialize_many( + items...); + }); } } else { @@ -1012,7 +1190,7 @@ class unpacker { } if constexpr (is_base_class) { uint32_t id = item->get_struct_pack_id(); - bool ok; + bool ok{}; auto index = search_type_by_md5(id, ok); assert(ok); return template_switch +struct int_t; + +template <> +struct int_t<1> { + using type = int8_t; +}; + +template <> +struct int_t<2> { + using type = int16_t; +}; + +template <> +struct int_t<4> { + using type = int32_t; +}; + +template <> +struct int_t<8> { + using type = int64_t; +}; + template class varint { public: @@ -202,7 +229,7 @@ STRUCT_PACK_INLINE void serialize_varint(writer& writer_, const T& t) { #endif uint64_t v; if constexpr (sintable_t) { - v = encode_zigzag(t.get()); + v = encode_zigzag(get_varint_value(t)); } else { v = t; @@ -277,6 +304,27 @@ template +const auto& get_varint_value(const T& v) { + if constexpr (varint_t) { + return v.get(); + } + else { + return v; + } +} + +template +auto& get_varint_value(T& v) { + if constexpr (varint_t) { + return v.get(); + } + else { + return v; + } +} + } // namespace detail using var_int32_t = detail::sint; using var_int64_t = detail::sint; diff --git a/src/struct_pack/tests/CMakeLists.txt b/src/struct_pack/tests/CMakeLists.txt index fa4c284c0..6bded7ebf 100644 --- a/src/struct_pack/tests/CMakeLists.txt +++ b/src/struct_pack/tests/CMakeLists.txt @@ -1,29 +1,17 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) -add_executable(struct_pack_test - test_serialize.cpp - test_compile_time_calculate.cpp - test_data_struct.cpp - test_data_struct2.cpp - test_tuplet.cpp - test_alignas.cpp - test_pragma_pack.cpp - test_pragma_pack_and_alignas_mix.cpp - test_varint.cpp - test_stream.cpp - test_compatible.cpp - test_non_aggregated_type.cpp - test_derived.cpp - test_cross_platform.cpp - test_disable_meta_info.cpp - main.cpp - ) +file(GLOB SRCS_PATHS ./*.cpp) +add_executable(struct_pack_test ${SRCS_PATHS}) +add_executable(struct_pack_test_with_optimize ${SRCS_PATHS}) add_test(NAME struct_pack_test COMMAND struct_pack_test) +add_test(NAME struct_pack_test_with_optimize COMMAND struct_pack_test_with_optimize) include (TestBigEndian) TEST_BIG_ENDIAN(IS_BIG_ENDIAN) if(NOT IS_BIG_ENDIAN) target_compile_definitions(struct_pack_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE TEST_IN_LITTLE_ENDIAN) +target_compile_definitions(struct_pack_test_with_optimize PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE STRUCT_PACK_OPTIMIZE TEST_IN_LITTLE_ENDIAN ) else() -target_compile_definitions(struct_pack_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) +target_compile_definitions(struct_pack_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE ) +target_compile_definitions(struct_pack_test_with_optimize PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE STRUCT_PACK_OPTIMIZE) endif() add_custom_command( TARGET struct_pack_test PRE_BUILD @@ -34,4 +22,4 @@ add_custom_command( TARGET struct_pack_test PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/binary_data - ${CMAKE_BINARY_DIR}/src/struct_pack/tests/binary_data) + ${CMAKE_BINARY_DIR}/src/struct_pack/tests/binary_data) \ No newline at end of file diff --git a/src/struct_pack/tests/test_compile_time_calculate.cpp b/src/struct_pack/tests/test_compile_time_calculate.cpp index e949f3b24..e83956053 100644 --- a/src/struct_pack/tests/test_compile_time_calculate.cpp +++ b/src/struct_pack/tests/test_compile_time_calculate.cpp @@ -5,6 +5,7 @@ #include "doctest.h" #include "test_struct.hpp" +#include "ylt/struct_pack/reflection.hpp" #include "ylt/struct_pack/type_calculate.hpp" #if __cplusplus >= 202002L #include @@ -474,4 +475,65 @@ TEST_CASE("test has container") { test_has_container>>()); static_assert(struct_pack::detail::check_if_has_container< test_has_container>>()); +} + +struct fast_varint_example_ct1 { + var_int32_t a; + var_int64_t b; + var_uint32_t c; + var_uint64_t d; + static constexpr struct_pack::sp_config struct_pack_config = USE_FAST_VARINT; +}; + +struct fast_varint_example_ct2 { + var_int32_t a; + var_int64_t b; + var_uint32_t c; + var_uint64_t d; +}; +constexpr auto set_sp_config(fast_varint_example_ct2*) { + return struct_pack::sp_config::USE_FAST_VARINT; +} +struct fast_varint_example_ct3 { + int32_t a; + int64_t b; + uint32_t c; + uint64_t d; + static constexpr auto struct_pack_config = + USE_FAST_VARINT | ENCODING_WITH_VARINT; +}; + +struct fast_varint_example_ct4 { + int32_t a; + int64_t b; + uint32_t c; + uint64_t d; + static constexpr auto struct_pack_config = ENCODING_WITH_VARINT; +}; + +TEST_CASE("test fast varint tag") { + using namespace struct_pack::detail; + static_assert(user_defined_config); + static_assert(user_defined_config_by_ADL); + static_assert(user_defined_config); + constexpr auto type_info1 = + struct_pack::get_type_literal(); + constexpr auto type_info2 = + struct_pack::get_type_literal(); + constexpr auto type_info3 = + struct_pack::get_type_literal(); + constexpr auto type_info4 = + struct_pack::get_type_literal(); + constexpr auto fast_varint_info = struct_pack::string_literal{ + {(char)type_id::struct_t, (char)type_id::fast_vint32_t, + (char)type_id::fast_vint64_t, (char)type_id::fast_vuint32_t, + (char)type_id::fast_vuint64_t, (char)type_id::type_end_flag}}; + constexpr auto varint_info = struct_pack::string_literal{ + {(char)type_id::struct_t, (char)type_id::vint32_t, + (char)type_id::vint64_t, (char)type_id::vuint32_t, + (char)type_id::vuint64_t, (char)type_id::type_end_flag}}; + static_assert(type_info1 == type_info2); + static_assert(type_info1 == type_info3); + static_assert(type_info3 != type_info4); + static_assert(type_info1 == fast_varint_info); } \ No newline at end of file diff --git a/src/struct_pack/tests/test_fast_varint.cpp b/src/struct_pack/tests/test_fast_varint.cpp new file mode 100644 index 000000000..1858e5d58 --- /dev/null +++ b/src/struct_pack/tests/test_fast_varint.cpp @@ -0,0 +1,1190 @@ +#include +#include + +#include "doctest.h" +#include "test_struct.hpp" +#include "ylt/struct_pack/reflection.hpp" +#include "ylt/struct_pack/varint.hpp" + +using namespace struct_pack; + +struct fast_varint_example_1 { + var_int32_t a; + var_int32_t b; + var_int32_t c; + var_int32_t d; + bool operator==(const fast_varint_example_1& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } +}; + +struct fast_varint_example_2 { + var_int32_t a; + var_int64_t b; + var_int32_t c; + var_int64_t d; + bool operator==(const fast_varint_example_2& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } +}; + +struct fast_varint_example_3 { + var_int64_t a; + var_int64_t b; + var_int64_t c; + var_int64_t d; + bool operator==(const fast_varint_example_3& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } +}; + +struct fast_varint_example_4 { + var_int64_t a; + var_int64_t b; + var_int64_t c; + var_int64_t d; + var_int64_t e; + var_int64_t f; + var_int64_t g; + bool operator==(const fast_varint_example_4& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && + f == o.f && g == o.g; + } +}; + +struct fast_varint_example_5 { + var_int64_t a; + var_int64_t b; + var_int64_t c; + var_int64_t d; + var_int64_t e; + var_int64_t f; + bool operator==(const fast_varint_example_5& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && f == o.f; + } +}; + +struct fast_varuint_example_1 { + var_uint64_t a; + var_uint64_t b; + var_uint64_t c; + var_uint64_t d; + bool operator==(const fast_varuint_example_1& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } +}; + +struct fast_varuint_example_2 { + var_uint32_t a; + var_uint32_t b; + var_uint32_t c; + var_uint32_t d; + bool operator==(const fast_varuint_example_2& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } +}; + +struct fast_varuint_example_3 { + var_uint32_t a; + var_uint64_t b; + var_uint32_t c; + var_uint64_t d; + bool operator==(const fast_varuint_example_3& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } +}; + +struct fast_varmixedint_example_1 { + var_int32_t a; + var_uint64_t b; + var_uint32_t c; + var_int64_t d; + bool operator==(const fast_varmixedint_example_1& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } + static constexpr auto struct_pack_config = + struct_pack::sp_config::USE_FAST_VARINT; +}; + +constexpr sp_config set_sp_config(fast_varint_example_1*) { + return USE_FAST_VARINT; +} + +constexpr sp_config set_sp_config(fast_varint_example_2*) { + return USE_FAST_VARINT; +} + +constexpr sp_config set_sp_config(fast_varint_example_3*) { + return USE_FAST_VARINT; +} + +constexpr sp_config set_sp_config(fast_varint_example_4*) { + return USE_FAST_VARINT; +} + +constexpr sp_config set_sp_config(fast_varint_example_5*) { + return USE_FAST_VARINT; +} + +constexpr sp_config set_sp_config(fast_varuint_example_1*) { + return USE_FAST_VARINT; +} + +constexpr sp_config set_sp_config(fast_varuint_example_2*) { + return USE_FAST_VARINT; +} + +constexpr sp_config set_sp_config(fast_varuint_example_3*) { + return USE_FAST_VARINT; +} + +TEST_CASE("fast varint test 0") { + fast_varint_example_1 o{0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 1); + return; +} + +TEST_CASE("fast varint test 1") { + fast_varint_example_1 o{1, -1, 127, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varint test 2") { + fast_varint_example_1 o{1, -127, 127, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varint test 3") { + fast_varint_example_1 o{1, -127, 0, 127}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varint test 4") { + fast_varint_example_1 o{1, 0, -127, 127}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varint test 5") { + fast_varint_example_1 o{0, 1, -127, 127}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varint test 6") { + fast_varint_example_1 o{1, -1, 128, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 7); + return; +} + +TEST_CASE("fast varint test 7") { + fast_varint_example_1 o{1, -128, 1, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varint test 8") { + fast_varint_example_1 o{32767, -32767, 1123, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 7); + return; +} + +TEST_CASE("fast varint test 8") { + fast_varint_example_1 o{0, 0, 1123, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 3); + return; +} + +TEST_CASE("fast varint test 9") { + fast_varint_example_1 o{32768, 1, 0, 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result->a == o.a); + CHECK(result->b == o.b); + CHECK(result->c == o.c); + CHECK(result->d == o.d); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varint test 10") { + fast_varint_example_1 o{-32768, 1, 0, 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 7); + return; +} + +TEST_CASE("fast varint test 12") { + fast_varint_example_1 o{-83219132, 114514, 0, 2123321213}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varint test 12") { + fast_varint_example_1 o{INT_MIN, INT_MAX, 0, INT_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varint2 test 1") { + fast_varint_example_2 o{0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 1); + return; +} + +TEST_CASE("fast varint2 test 2") { + fast_varint_example_2 o{0, 1, -1, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 3); + return; +} + +TEST_CASE("fast varint2 test 2") { + fast_varint_example_2 o{0, 127, -128, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 3); + return; +} + +TEST_CASE("fast varint2 test 3") { + fast_varint_example_2 o{0, 128, -127, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 5); + return; +} + +TEST_CASE("fast varint2 test 4") { + fast_varint_example_2 o{0, INT16_MAX, INT16_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 5); + return; +} + +TEST_CASE("fast varint2 test 5") { + fast_varint_example_2 o{0, INT16_MAX + 1, INT16_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varint2 test 6") { + fast_varint_example_2 o{0, INT16_MAX, INT16_MIN - 1, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varint2 test 7") { + fast_varint_example_2 o{0, INT32_MAX, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varint2 test 8") { + fast_varint_example_2 o{0, INT32_MAX, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varint2 test 9") { + fast_varint_example_2 o{0, INT32_MAX + 1ll, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varint2 test 10") { + fast_varint_example_2 o{0, INT32_MIN - 1ll, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varint2 test 11") { + fast_varint_example_2 o{0, INT64_MIN, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varint2 test 12") { + fast_varint_example_2 o{0, INT64_MAX, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varint3 test 1") { + fast_varint_example_3 o{0, INT64_MAX, 0, INT64_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} + +TEST_CASE("fast varint3 test 2") { + fast_varint_example_3 o{0, INT32_MAX, 0, INT32_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varint4 test 1") { + fast_varint_example_4 o{0, 0, 0, 0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 2); + return; +} + +TEST_CASE("fast varint4 test 2") { + fast_varint_example_4 o{0, 0, 0, 0, 0, 0, 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 3); + return; +} + +TEST_CASE("fast varint4 test 3") { + fast_varint_example_4 o{0, 0, 0, 0, 0, 0, INT16_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varint4 test 4") { + fast_varint_example_4 o{0, 0, 0, 0, 0, 0, INT32_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 6); + return; +} + +TEST_CASE("fast varint4 test 5") { + fast_varint_example_4 o{0, 0, 0, 0, 0, 0, INT64_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 10); + return; +} + +TEST_CASE("fast varint5 test 1") { + fast_varint_example_5 o{0, 0, 0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 1); + return; +} + +TEST_CASE("fast varint5 test 2") { + fast_varint_example_5 o{0, 0, 0, 0, 0, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 2); + return; +} + +TEST_CASE("fast varint5 test 3") { + fast_varint_example_5 o{0, 0, 0, 0, 0, INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 3); + return; +} + +TEST_CASE("fast varint5 test 4") { + fast_varint_example_5 o{0, 0, 0, 0, 0, INT32_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 5); + return; +} + +TEST_CASE("fast varint5 test 5") { + fast_varint_example_5 o{0, 0, 0, 0, 0, INT64_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varuint1 test 1") { + fast_varuint_example_1 o{0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 1); + return; +} + +TEST_CASE("fast varuint1 test 2") { + fast_varuint_example_1 o{UINT8_MAX, UINT8_MAX, 0, UINT8_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 4); + return; +} + +TEST_CASE("fast varuint1 test 3") { + fast_varuint_example_1 o{UINT8_MAX, UINT8_MAX + 1, 0, UINT8_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 7); + return; +} + +TEST_CASE("fast varuint1 test 4") { + fast_varuint_example_1 o{UINT16_MAX, UINT16_MAX, 0, UINT16_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 7); + return; +} + +TEST_CASE("fast varuint1 test 5") { + fast_varuint_example_1 o{UINT16_MAX, UINT16_MAX, 0, UINT16_MAX + 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varuint1 test 6") { + fast_varuint_example_1 o{UINT32_MAX, UINT32_MAX, 0, UINT32_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varuint1 test 7") { + fast_varuint_example_1 o{UINT32_MAX + 1ull, UINT32_MAX, 0, UINT32_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} + +TEST_CASE("fast varuint1 test 8") { + fast_varuint_example_1 o{UINT64_MAX, UINT64_MAX, 0, UINT64_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} + +TEST_CASE("fast varuint2 test 1") { + fast_varuint_example_2 o{0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 1); + return; +} + +TEST_CASE("fast varuint2 test 2") { + fast_varuint_example_2 o{0, 0, 0, UINT8_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 2); + return; +} + +TEST_CASE("fast varuint2 test 3") { + fast_varuint_example_2 o{0, 0, 0, UINT8_MAX + 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 3); + return; +} + +TEST_CASE("fast varuint2 test 4") { + fast_varuint_example_2 o{0, 0, 0, UINT16_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 3); + return; +} + +TEST_CASE("fast varuint2 test 5") { + fast_varuint_example_2 o{0, 0, 0, UINT16_MAX + 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 5); + return; +} +TEST_CASE("fast varuint2 test 6") { + fast_varuint_example_2 o{0, 0, 0, UINT32_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 5); + return; +} + +TEST_CASE("fast varuint3 test 1") { + fast_varuint_example_3 o{0, 0, UINT32_MAX, UINT32_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varuint3 test 2") { + fast_varuint_example_3 o{0, 0, UINT32_MAX, UINT32_MAX + 1ull}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varuint3 test 3") { + fast_varuint_example_3 o{0, 0, UINT32_MAX, UINT64_MAX}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 13); + return; +} + +TEST_CASE("fast varmixedint1 test 1") { + fast_varmixedint_example_1 o{0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 1); + return; +} + +TEST_CASE("fast varmixedint1 test 2") { + fast_varmixedint_example_1 o{INT8_MIN, UINT8_MAX, UINT8_MAX, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 5); + return; +} + +TEST_CASE("fast varmixedint1 test 3") { + fast_varmixedint_example_1 o{INT8_MIN - 1, UINT8_MAX, UINT8_MAX, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varmixedint1 test 5") { + fast_varmixedint_example_1 o{INT8_MIN, UINT8_MAX + 1, UINT8_MAX, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint1 test 6") { + fast_varmixedint_example_1 o{INT8_MIN, UINT8_MAX, UINT8_MAX + 1, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint1 test 7") { + fast_varmixedint_example_1 o{INT8_MIN, UINT8_MAX, UINT8_MAX, INT8_MIN - 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint1 test 8") { + fast_varmixedint_example_1 o{INT16_MIN, UINT16_MAX, UINT16_MAX, INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint1 test 9") { + fast_varmixedint_example_1 o{INT16_MIN - 1, UINT16_MAX, UINT16_MAX, + INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} +TEST_CASE("fast varmixedint1 test 10") { + fast_varmixedint_example_1 o{INT16_MIN, UINT16_MAX + 1, UINT16_MAX, + INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} +TEST_CASE("fast varmixedint1 test 11") { + fast_varmixedint_example_1 o{INT16_MIN, UINT16_MAX, UINT16_MAX + 1, + INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} + +TEST_CASE("fast varmixedint1 test 12") { + fast_varmixedint_example_1 o{INT16_MIN, UINT16_MAX, UINT16_MAX, + INT16_MIN - 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} + +TEST_CASE("fast varmixedint1 test 13") { + fast_varmixedint_example_1 o{INT32_MIN, UINT32_MAX, UINT32_MAX, INT32_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} + +TEST_CASE("fast varmixedint1 test 14") { + fast_varmixedint_example_1 o{INT32_MIN, UINT32_MAX + 1ull, UINT32_MAX, + INT32_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} +TEST_CASE("fast varmixedint1 test 15") { + fast_varmixedint_example_1 o{INT32_MIN, UINT32_MAX, UINT32_MAX, + INT32_MIN - 1ll}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} +TEST_CASE("fast varmixedint1 test 16") { + fast_varmixedint_example_1 o{INT32_MIN, UINT64_MAX, UINT32_MAX, INT64_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} + +struct varmixedint2 { + int32_t a; + uint64_t b; + uint32_t c; + int64_t d; + bool operator==(const varmixedint2& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } + static constexpr auto struct_pack_config = struct_pack::ENCODING_WITH_VARINT | + struct_pack::USE_FAST_VARINT | + struct_pack::DISABLE_ALL_META_INFO; +}; + +TEST_CASE("fast varmixedint2 test 1") { + varmixedint2 o{0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 1); + return; +} + +TEST_CASE("fast varmixedint2 test 2") { + varmixedint2 o{INT8_MIN, UINT8_MAX, UINT8_MAX, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 5); + return; +} + +TEST_CASE("fast varmixedint2 test 3") { + varmixedint2 o{INT8_MIN - 1, UINT8_MAX, UINT8_MAX, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} + +TEST_CASE("fast varmixedint2 test 5") { + varmixedint2 o{INT8_MIN, UINT8_MAX + 1, UINT8_MAX, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint2 test 6") { + varmixedint2 o{INT8_MIN, UINT8_MAX, UINT8_MAX + 1, INT8_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint2 test 7") { + varmixedint2 o{INT8_MIN, UINT8_MAX, UINT8_MAX, INT8_MIN - 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint2 test 8") { + varmixedint2 o{INT16_MIN, UINT16_MAX, UINT16_MAX, INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 9); + return; +} +TEST_CASE("fast varmixedint2 test 9") { + varmixedint2 o{INT16_MIN - 1, UINT16_MAX, UINT16_MAX, INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} +TEST_CASE("fast varmixedint2 test 10") { + varmixedint2 o{INT16_MIN, UINT16_MAX + 1, UINT16_MAX, INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} +TEST_CASE("fast varmixedint2 test 11") { + varmixedint2 o{INT16_MIN, UINT16_MAX, UINT16_MAX + 1, INT16_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} + +TEST_CASE("fast varmixedint2 test 12") { + varmixedint2 o{INT16_MIN, UINT16_MAX, UINT16_MAX, INT16_MIN - 1}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} + +TEST_CASE("fast varmixedint2 test 13") { + varmixedint2 o{INT32_MIN, UINT32_MAX, UINT32_MAX, INT32_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 17); + return; +} + +TEST_CASE("fast varmixedint2 test 14") { + varmixedint2 o{INT32_MIN, UINT32_MAX + 1ull, UINT32_MAX, INT32_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} +TEST_CASE("fast varmixedint2 test 15") { + varmixedint2 o{INT32_MIN, UINT32_MAX, UINT32_MAX, INT32_MIN - 1ll}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} +TEST_CASE("fast varmixedint2 test 16") { + varmixedint2 o{INT32_MIN, UINT64_MAX, UINT32_MAX, INT64_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 25); + return; +} + +struct six_int { + int a, b, c, d, e, f; + bool operator==(const six_int& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && f == o.f; + } + static constexpr auto struct_pack_config = + struct_pack::sp_config::USE_FAST_VARINT | + struct_pack::sp_config::ENCODING_WITH_VARINT; +}; + +TEST_CASE("six int test") { + six_int o{INT32_MIN, 2435, INT32_MAX, 0, 0, INT32_MIN}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + return; +} + +struct seven_int { + int a, b, c, d, e, f, g; + bool operator==(const seven_int& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && + f == o.f && g == o.g; + } + static constexpr auto struct_pack_config = + struct_pack::sp_config::USE_FAST_VARINT | + struct_pack::sp_config::ENCODING_WITH_VARINT; +}; + +TEST_CASE("seven int test") { + seven_int o{INT32_MIN, 21314, INT32_MAX, 0, 0, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + return; +} + +struct eight_int { + int a, b, c, d, e, f, g, h; + bool operator==(const eight_int& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && + f == o.f && g == o.g && h == o.h; + } + static constexpr auto struct_pack_config = + struct_pack::sp_config::USE_FAST_VARINT | + struct_pack::sp_config::ENCODING_WITH_VARINT; +}; + +TEST_CASE("eight int test") { + eight_int o{INT32_MIN, 521, INT32_MAX, 0, 0, INT32_MIN, 0, 2123}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + return; +} + +TEST_CASE("vector test") { + eight_int o{INT32_MIN, 521, INT32_MAX, 0, 0, INT32_MIN, 0, 2123}; + std::vector vec; + vec.resize(100, o); + auto buffer = struct_pack::serialize(vec); + auto result = struct_pack::deserialize>(buffer); + REQUIRE(result.has_value()); + CHECK(result == vec); + return; +} + +struct mixed_fast_varint { + int a, b, c; + std::string d; + int e, f, g, h; + bool operator==(const mixed_fast_varint& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && + f == o.f && g == o.g && h == o.h; + } + static constexpr auto struct_pack_config = + struct_pack::sp_config::USE_FAST_VARINT | + struct_pack::sp_config::ENCODING_WITH_VARINT; +}; + +TEST_CASE("test mixed 1") { + mixed_fast_varint o{INT32_MIN, 521, INT32_MAX, "Hello", 0, 0, INT32_MIN, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + return; +} + +struct mixed_fast_varint2 { + struct_pack::var_int64_t a, b, c; + std::string d; + int e, f, g, h; + bool operator==(const mixed_fast_varint2& o) const { + return a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && + f == o.f && g == o.g && h == o.h; + } + static constexpr auto struct_pack_config = + struct_pack::sp_config::USE_FAST_VARINT | + struct_pack::sp_config::DISABLE_ALL_META_INFO; +}; + +TEST_CASE("test mixed 2") { + mixed_fast_varint2 o{0, 0, 0, "Hello", 0, 0, 0, 0}; + auto buffer = struct_pack::serialize(o); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == o); + CHECK(buffer.size() == 24); + return; +} diff --git a/src/struct_pack/tests/test_serialize.cpp b/src/struct_pack/tests/test_serialize.cpp index dabdfc420..9bf0de4c3 100644 --- a/src/struct_pack/tests/test_serialize.cpp +++ b/src/struct_pack/tests/test_serialize.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,9 @@ #include #include +#include "ylt/struct_pack/compatible.hpp" +#include "ylt/struct_pack/endian_wrapper.hpp" + #define private public #include #undef private @@ -1278,4 +1282,68 @@ TEST_CASE("test dynamic span") { } } } -#endif \ No newline at end of file +#endif + +TEST_CASE("test width too big") { + SUBCASE("1") { + std::string buffer; + buffer.push_back(0b11000); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value() == false); + if constexpr (sizeof(std::size_t) < 8) { + CHECK(result.error() == struct_pack::errc::too_width_size); + } + else { + CHECK(result.error() == struct_pack::errc::no_buffer_space); + } + } + SUBCASE("2") { + std::string buffer; + buffer.push_back(0b11000); + std::size_t len = 0; + auto result = struct_pack::deserialize(buffer, len); + REQUIRE(result.has_value() == false); + if constexpr (sizeof(std::size_t) < 8) { + CHECK(result.error() == struct_pack::errc::too_width_size); + } + else { + CHECK(result.error() == struct_pack::errc::no_buffer_space); + } + } + SUBCASE("3") { + std::string buffer; + buffer.push_back(0b11000); + auto result = + struct_pack::get_field, 0, + struct_pack::DISABLE_ALL_META_INFO>(buffer); + REQUIRE(result.has_value() == false); + if constexpr (sizeof(std::size_t) < 8) { + CHECK(result.error() == struct_pack::errc::too_width_size); + } + else { + CHECK(result.error() == struct_pack::errc::no_buffer_space); + } + } + SUBCASE("4") { + std::string buffer; + using T = std::pair>; + auto code = struct_pack::get_type_code() + 1; + buffer.resize(4); + if constexpr (!struct_pack::detail::is_system_little_endian) { + code = struct_pack::detail::bswap32(code); + } + memcpy(buffer.data(), &code, sizeof(code)); + buffer.push_back(0b11); + auto result = struct_pack::deserialize< + std::pair>>(buffer); + REQUIRE(result.has_value() == false); + if constexpr (sizeof(std::size_t) < 8) { + CHECK(result.error() == struct_pack::errc::too_width_size); + } + else { + CHECK(result.error() == struct_pack::errc::no_buffer_space); + } + } +} \ No newline at end of file diff --git a/src/struct_pack/tests/test_varint.cpp b/src/struct_pack/tests/test_varint.cpp index f40198bb5..18666cee0 100644 --- a/src/struct_pack/tests/test_varint.cpp +++ b/src/struct_pack/tests/test_varint.cpp @@ -3,6 +3,7 @@ #include "doctest.h" #include "test_struct.hpp" +#include "ylt/struct_pack/reflection.hpp" using namespace struct_pack; TEST_CASE("test uint32") { @@ -842,4 +843,26 @@ TEST_CASE("test varint zip size") { CHECK(detail::calculate_payload_size(vec).total == 100); CHECK(detail::calculate_payload_size(vec).size_cnt == 1); } +} + +struct var_int_struct { + int32_t a; + uint32_t b; + int64_t c; + uint64_t d; + bool operator==(const var_int_struct &o) const { + return a == o.a && b == o.b && c == o.c && d == o.d; + } + static constexpr auto struct_pack_config = + struct_pack::sp_config::ENCODING_WITH_VARINT; +}; + +TEST_CASE("test varint by normal int") { + var_int_struct v{0, 1, 2, 3}; + auto buffer = struct_pack::serialize(v); + auto result = struct_pack::deserialize(buffer); + REQUIRE(result.has_value()); + CHECK(result == v); + CHECK(buffer.size() == 4); } \ No newline at end of file