diff --git a/include/ylt/struct_pack/endian_wrapper.hpp b/include/ylt/struct_pack/endian_wrapper.hpp new file mode 100644 index 000000000..6797ab262 --- /dev/null +++ b/include/ylt/struct_pack/endian_wrapper.hpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "reflection.hpp" +#include "ylt/struct_pack/util.h" +namespace struct_pack::detail { + +template +void write_wrapper(writer_t& writer, const char* data) { + if constexpr (is_system_little_endian || block_size == 1) { + writer.write(data, block_size); + } + else if constexpr (block_size == 2) { +#ifdef _MSC_VER + auto tmp = _byteswap_ushort(*data); + writer.write(&tmp, block_size); +#elif defined(__clang__) || defined(__GNUC__) + auto tmp = __builtin_bswap16(*data); + writer.write(&tmp, block_size); +#else + writer.write(data + 1, 1); + writer.write(data, 1); +#endif + } + else if constexpr (block_size == 4) { +#ifdef _MSC_VER + auto tmp = _byteswap_ulong(*data); + writer.write(&tmp, block_size); +#elif defined(__clang__) || defined(__GNUC__) + auto tmp = __builtin_bswap16(*data); + writer.write(&tmp, block_size); +#else + writer.write(data + 3, 1); + writer.write(data + 2, 1); + writer.write(data + 1, 1); + writer.write(data, 1); +#endif + } + else if constexpr (block_size == 8) { +#ifdef _MSC_VER + auto tmp = _byteswap_uint64(*data); + writer.write(&tmp, block_size); +#elif defined(__clang__) || defined(__GNUC__) + auto tmp = __builtin_bswap16(*data); + writer.write(&tmp, block_size); +#else + writer.write(data + 7, 1); + writer.write(data + 6, 1); + writer.write(data + 5, 1); + writer.write(data + 4, 1); + writer.write(data + 3, 1); + writer.write(data + 2, 1); + writer.write(data + 1, 1); + writer.write(data, 1); +#endif + } + else if constexpr (block_size == 16) { +#ifdef _MSC_VER + auto tmp1 = _byteswap_uint64(*data), tmp2 = _byteswap_uint64(*data + 8); + writer.write(&tmp2, block_size); + writer.write(&tmp1, block_size); +#elif defined(__clang__) || defined(__GNUC__) + auto tmp1 = __builtin_bswap64(*data), tmp2 = __builtin_bswap64(*data + 8); + writer.write(&tmp2, block_size); + writer.write(&tmp1, block_size); +#else + writer.write(data + 15, 1); + writer.write(data + 14, 1); + writer.write(data + 13, 1); + writer.write(data + 12, 1); + writer.write(data + 11, 1); + writer.write(data + 10, 1); + writer.write(data + 9, 1); + writer.write(data + 8, 1); + writer.write(data + 7, 1); + writer.write(data + 6, 1); + writer.write(data + 5, 1); + writer.write(data + 4, 1); + writer.write(data + 3, 1); + writer.write(data + 2, 1); + writer.write(data + 1, 1); + writer.write(data, 1); +#endif + } + else { + static_assert(sizeof(writer), "illegal block size(should be 1,2,4,8,16)"); + } +} +template +void write_wrapper(writer_t& writer, const char* data, + std::size_t block_count) { + if constexpr (is_system_little_endian || block_size == 1) { + auto total_sz = block_count * block_size; + if SP_UNLIKELY (total_sz >= PTRDIFF_MAX) + unreachable(); + else + writer.write(data, total_sz); + } + else { + for (std::size_t i = 0; i < block_count; ++i) { + write_wrapper(writer, data); + data += block_size; + } + } +} +template +bool read_wrapper(reader_t& reader, char* SP_RESTRICT data) { + if constexpr (is_system_little_endian || block_size == 1) { + return static_cast(reader.read(data, block_size)); + } + else { + std::array tmp; + bool res = static_cast(reader.read(&tmp, block_size)); + if SP_UNLIKELY (res) { + return res; + } + if constexpr (block_size == 2) { +#ifdef _MSC_VER + *(uint16_t*)data = _byteswap_ushort(*(uint16_t*)&tmp); +#elif defined(__clang__) || defined(__GNUC__) + *(uint16_t*)data = __builtin_bswap16(*(uint16_t*)&tmp); +#else + data[0] = tmp[1]; + data[1] = tmp[0]; +#endif + } + else if constexpr (block_size == 4) { +#ifdef _MSC_VER + *(uint32_t*)data = _byteswap_ulong(*(uint32_t*)&tmp); +#elif defined(__clang__) || defined(__GNUC__) + *(uint32_t*)data = __builtin_bswap32(*(uint32_t*)&tmp); +#else + data[0] = tmp[3]; + data[1] = tmp[2]; + data[2] = tmp[1]; + data[3] = tmp[0]; +#endif + } + else if constexpr (block_size == 8) { +#ifdef _MSC_VER + *(uint64_t*)data = _byteswap_uint64(*(uint64_t*)&tmp); +#elif defined(__clang__) || defined(__GNUC__) + *(uint64_t*)data = __builtin_bswap64(*(uint64_t*)&tmp); +#else + data[0] = tmp[7]; + data[1] = tmp[6]; + data[2] = tmp[5]; + data[3] = tmp[4]; + data[4] = tmp[3]; + data[5] = tmp[2]; + data[6] = tmp[1]; + data[7] = tmp[0]; +#endif + } + else if constexpr (block_size == 16) { +#ifdef _MSC_VER + *(uint64_t*)(data + 8) = _byteswap_uint64(*(uint64_t*)&tmp); + *(uint64_t*)data = _byteswap_uint64(*(uint64_t*)(&tmp + 8)); +#elif defined(__clang__) || defined(__GNUC__) + *(uint64_t*)(data + 8) = __builtin_bswap64(*(uint64_t*)&tmp); + *(uint64_t*)data = __builtin_bswap64(*(uint64_t*)(&tmp + 8)); +#else + data[0] = tmp[15]; + data[1] = tmp[14]; + data[2] = tmp[13]; + data[3] = tmp[12]; + data[4] = tmp[11]; + data[5] = tmp[10]; + data[6] = tmp[9]; + data[7] = tmp[8]; + data[8] = tmp[7]; + data[9] = tmp[6]; + data[10] = tmp[5]; + data[11] = tmp[4]; + data[12] = tmp[3]; + data[13] = tmp[2]; + data[14] = tmp[1]; + data[15] = tmp[0]; +#endif + } + else { + static_assert(sizeof(reader), "illegal block size(should be 1,2,4,8,16)"); + } + } +} +template +bool read_wrapper(reader_t& reader, char* SP_RESTRICT data, + std::size_t block_count) { + if constexpr (is_system_little_endian || block_size == 1) { + auto total_sz = block_count * block_size; + if SP_UNLIKELY (total_sz >= PTRDIFF_MAX) + unreachable(); + else + return static_cast(reader.read(data, total_sz)); + } + else { + for (std::size_t i = 0; i < block_count; ++i) { + if SP_UNLIKELY (!read_wrapper(reader, data)) { + return false; + } + data += block_size; + } + } +} +}; // namespace struct_pack::detail \ No newline at end of file diff --git a/include/ylt/struct_pack/marco.h b/include/ylt/struct_pack/marco.h index 7dbc0a243..546ac6e9e 100644 --- a/include/ylt/struct_pack/marco.h +++ b/include/ylt/struct_pack/marco.h @@ -60,3 +60,11 @@ #define STRUCT_PACK_RTTI_ENABLED #endif #endif + +#if defined __clang__ || __GNUC__ +#define SP_RESTRICT __restrict__ +#elif defined _MSC_VER +#define SP_RESTRICT __restrict +#else +#define SP_RESTRICT +#endif diff --git a/include/ylt/struct_pack/packer.hpp b/include/ylt/struct_pack/packer.hpp index c6103e392..eafce4ba3 100644 --- a/include/ylt/struct_pack/packer.hpp +++ b/include/ylt/struct_pack/packer.hpp @@ -14,8 +14,12 @@ * limitations under the License. */ #pragma once +#include + #include "calculate_size.hpp" #include "reflection.hpp" +#include "endian_wrapper.hpp" +#include "ylt/struct_pack/util.h" namespace struct_pack::detail { template < #if __cpp_concepts >= 201907L @@ -88,20 +92,35 @@ class packer { constexpr void STRUCT_PACK_INLINE serialize_metainfo() { constexpr auto hash_head = calculate_hash_head() | (is_default_size_type ? 0 : 1); - writer_.write((char *)&hash_head, sizeof(uint32_t)); + write_wrapper(writer_, (char *)&hash_head); if constexpr (hash_head % 2) { // has more metainfo auto metainfo = info.metainfo(); - writer_.write((char *)&metainfo, sizeof(char)); + write_wrapper(writer_, (char *)&metainfo); if constexpr (serialize_static_config::has_compatible) { - constexpr std::size_t sz[] = {0, 2, 4, 8}; - auto len_size = sz[metainfo & 0b11]; - auto len = info.size(); - writer_.write((char *)&len, len_size); + uint16_t len16; + uint32_t len32; + uint64_t len64; + switch (metainfo & 0b11) { + case 1: + len16 = info.size(); + write_wrapper<2>(writer_, (char *)&len16); + break; + case 2: + len32 = info.size(); + write_wrapper<4>(writer_, (char *)&len32); + break; + case 3: + len64 = info.size(); + write_wrapper<8>(writer_, (char *)&len64); + break; + default: + unreachable(); + } } if constexpr (check_if_add_type_literal()) { constexpr auto type_literal = struct_pack::get_type_literal(); - writer_.write(type_literal.data(), type_literal.size() + 1); + write_wrapper<1>(writer_, type_literal.data(), type_literal.size() + 1); } } } @@ -119,7 +138,7 @@ class packer { constexpr void STRUCT_PACK_INLINE write_padding(std::size_t sz) { if (sz > 0) { constexpr char buf = 0; - for (std::size_t i = 0; i < sz; ++i) writer_.write(&buf, 1); + for (std::size_t i = 0; i < sz; ++i) write_wrapper<1>(writer_, &buf); } } @@ -141,11 +160,11 @@ class packer { 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) { - writer_.write((char *)&item, sizeof(type)); + write_wrapper(writer_, (char *)&item); } else if constexpr (unique_ptr) { bool has_value = (item != nullptr); - writer_.write((char *)&has_value, sizeof(char)); + write_wrapper(writer_, (char *)&has_value); if (has_value) { if constexpr (is_base_class) { bool is_ok; @@ -153,7 +172,7 @@ class packer { auto index = search_type_by_md5( item->get_struct_pack_id(), is_ok); assert(is_ok); - writer_.write((char *)&id, sizeof(uint32_t)); + write_wrapper(writer_, (char *)&id); template_switch, std::integral_constant, @@ -170,7 +189,8 @@ class packer { } else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value) { - writer_.write((char *)&item, sizeof(type)); + write_wrapper( + writer_, (char *)&item, sizeof(type) / sizeof(decltype(item[0]))); } else { for (const auto &i : item) { @@ -179,23 +199,43 @@ class packer { } } else if constexpr (map_container || container) { - uint64_t size = item.size(); -#ifdef STRUCT_PACK_OPTIMIZE - writer_.write((char *)&size, size_type); -#else if constexpr (size_type == 1) { - writer_.write((char *)&size, size_type); + uint8_t size = item.size(); + write_wrapper(writer_, (char *)&size); + } +#ifdef STRUCT_PACK_OPTIMIZE + else if constexpr (size_type == 2) { + uint16_t size = item.size(); + write_wrapper(writer_, (char *)&size); + } + else if constexpr (size_type == 4) { + uint32_t size = item.size(); + write_wrapper(writer_, (char *)&size); } + else if constexpr (size_type == 8) { + uint64_t size = item.size(); + write_wrapper(writer_, (char *)&size); + } + else { + static_assert(!sizeof(item), "illegal size_type."); + } +#else else { + uint16_t size16; + uint32_t size32; + uint64_t size64; switch ((info.metainfo() & 0b11000) >> 3) { case 1: - writer_.write((char *)&size, 2); + size16 = item.size(); + write_wrapper<2>(writer_, (char *)&size16); break; case 2: - writer_.write((char *)&size, 4); + size32 = item.size(); + write_wrapper<4>(writer_, (char *)&size32); break; case 3: - writer_.write((char *)&size, 8); + size64 = item.size(); + write_wrapper<8>(writer_, (char *)&size64); break; default: unreachable(); @@ -204,12 +244,8 @@ class packer { #endif if constexpr (trivially_copyable_container) { using value_type = typename type::value_type; - auto container_size = 1ull * size * sizeof(value_type); - if SP_UNLIKELY (container_size >= PTRDIFF_MAX) - unreachable(); - else { - writer_.write((char *)item.data(), container_size); - } + write_wrapper(writer_, (char *)item.data(), + item.size()); return; } else { @@ -232,7 +268,7 @@ class packer { } else if constexpr (optional) { bool has_value = item.has_value(); - writer_.write((char *)&has_value, sizeof(bool)); + write_wrapper(writer_, (char *)&has_value); if (has_value) { serialize_one(*item); } @@ -241,7 +277,7 @@ class packer { static_assert(std::variant_size_v < 256, "variant's size is too large"); uint8_t index = item.index(); - writer_.write((char *)&index, sizeof(index)); + write_wrapper(writer_, (char *)&index); std::visit( [this](auto &&e) { this->serialize_one(e); @@ -250,7 +286,7 @@ class packer { } else if constexpr (expected) { bool has_value = item.has_value(); - writer_.write((char *)&has_value, sizeof(has_value)); + write_wrapper(writer_, (char *)&has_value); if (has_value) { if constexpr (!std::is_same_v) serialize_one(item.value()); @@ -266,10 +302,13 @@ class packer { std::is_aggregate_v>, "struct_pack only support aggregated type, or you should " "add macro STRUCT_PACK_REFL(Type,field1,field2...)"); - if constexpr (is_trivial_serializable::value) { - writer_.write((char *)&item, sizeof(type)); + if constexpr (is_trivial_serializable::value && + is_system_little_endian) { + write_wrapper(writer_, (char *)&item); } - else if constexpr (is_trivial_serializable::value) { + else if constexpr ((is_trivial_serializable::value && + !is_system_little_endian) || + is_trivial_serializable::value) { visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA { int i = 1; ((serialize_one(items), @@ -291,7 +330,7 @@ class packer { if constexpr (id == type_id::compatible_t) { if constexpr (version == type::version_number) { bool has_value = item.has_value(); - writer_.write((char *)&has_value, sizeof(bool)); + write_wrapper(writer_, (char *)&has_value); if (has_value) { serialize_one(*item); } diff --git a/include/ylt/struct_pack/reflection.hpp b/include/ylt/struct_pack/reflection.hpp index 292efa77f..9847fa889 100644 --- a/include/ylt/struct_pack/reflection.hpp +++ b/include/ylt/struct_pack/reflection.hpp @@ -39,6 +39,9 @@ namespace struct_pack { namespace detail { +constexpr inline bool is_system_little_endian = + static_cast(1) == 1; + template using get_args_type = remove_cvref_t>, diff --git a/include/ylt/struct_pack/unpacker.hpp b/include/ylt/struct_pack/unpacker.hpp index 83adebbbe..576ba3b47 100644 --- a/include/ylt/struct_pack/unpacker.hpp +++ b/include/ylt/struct_pack/unpacker.hpp @@ -34,6 +34,7 @@ #include "alignment.hpp" #include "calculate_size.hpp" #include "derived_helper.hpp" +#include "endian_wrapper.hpp" #include "error_code.hpp" #include "md5_constexpr.hpp" #include "packer.hpp" @@ -43,6 +44,7 @@ #include "type_id.hpp" #include "type_trait.hpp" #include "varint.hpp" +#include "ylt/struct_pack/util.h" namespace struct_pack { @@ -446,11 +448,30 @@ 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_len = 0; - if SP_UNLIKELY (!reader_.read((char *)&data_len, len_sz)) { - return {errc::no_buffer_space, 0}; + uint64_t data_len64; + uint32_t data_len32; + uint16_t data_len16; + bool result; + switch (compatible_sz_len) { + case 1: + if SP_LIKELY (read_wrapper<2>(reader_, (char *)&data_len16)) { + return {errc{}, data_len16}; + } + break; + case 2: + if SP_LIKELY (read_wrapper<4>(reader_, (char *)&data_len32)) { + return {errc{}, data_len32}; + } + break; + case 3: + if SP_LIKELY (read_wrapper<8>(reader_, (char *)&data_len64)) { + return {errc{}, data_len64}; + } + break; + default: + unreachable(); } - return {errc{}, data_len}; + return {errc::no_buffer_space, 0}; } template @@ -467,7 +488,7 @@ class unpacker { } else { char buffer[literal.size() + 1]; - if SP_UNLIKELY (!reader_.read(buffer, literal.size() + 1)) { + if SP_UNLIKELY (!read_wrapper<1>(reader_, buffer, literal.size() + 1)) { return errc::no_buffer_space; } if SP_UNLIKELY (memcmp(buffer, literal.data(), literal.size() + 1)) { @@ -486,8 +507,8 @@ class unpacker { reader_.read_head((char *)¤t_types_code); } else { - if SP_UNLIKELY (!reader_.read((char *)¤t_types_code, - sizeof(uint32_t))) { + if SP_UNLIKELY (!read_wrapper( + reader_, (char *)¤t_types_code)) { return {struct_pack::errc::no_buffer_space, 0}; } constexpr uint32_t types_code = get_types_code(); @@ -501,7 +522,8 @@ class unpacker { return {}; } unsigned char metainfo; - if SP_UNLIKELY (!reader_.read((char *)&metainfo, sizeof(unsigned char))) { + if SP_UNLIKELY (!read_wrapper(reader_, + (char *)&metainfo)) { return {struct_pack::errc::no_buffer_space, 0}; } std::pair ret; @@ -558,6 +580,9 @@ class unpacker { static_assert(view_reader_t, "The Reader isn't a view_reader, can't deserialize " "a trivial_view"); + static_assert( + is_system_little_endian || sizeof(typename type::value_type) == 1, + "get a trivial view in big-endian system is limited."); const char *view = reader_.read_view(sizeof(typename T::value_type)); if SP_LIKELY (view != nullptr) { item = *reinterpret_cast(view); @@ -578,7 +603,7 @@ class unpacker { id == type_id::int128_t || id == type_id::uint128_t || id == type_id::bitset_t) { if constexpr (NotSkip) { - if SP_UNLIKELY (!reader_.read((char *)&item, sizeof(type))) { + if SP_UNLIKELY (!read_wrapper(reader_, (char *)&item)) { return struct_pack::errc::no_buffer_space; } } @@ -588,7 +613,8 @@ class unpacker { } else if constexpr (unique_ptr) { bool has_value{}; - if SP_UNLIKELY (!reader_.read((char *)&has_value, sizeof(bool))) { + if SP_UNLIKELY (!read_wrapper(reader_, + (char *)&has_value)) { return struct_pack::errc::no_buffer_space; } if (!has_value) { @@ -596,7 +622,7 @@ class unpacker { } if constexpr (is_base_class) { uint32_t id; - reader_.read((char *)&id, sizeof(id)); + read_wrapper(reader_, (char *)&id); bool ok; auto index = search_type_by_md5(id, ok); if SP_UNLIKELY (!ok) { @@ -622,7 +648,9 @@ class unpacker { else if constexpr (id == type_id::array_t) { if constexpr (is_trivial_serializable::value) { if constexpr (NotSkip) { - if SP_UNLIKELY (!reader_.read((char *)&item, sizeof(type))) { + if SP_UNLIKELY (!read_wrapper( + reader_, (char *)&item, + sizeof(item) / sizeof(item[0]))) { return struct_pack::errc::no_buffer_space; } } @@ -641,31 +669,55 @@ class unpacker { } } else if constexpr (container) { - size_t size = 0; + uint8_t size8; + uint16_t size16; + uint32_t size32; + uint64_t size64; + bool result; + if constexpr (size_type == 1) { + if SP_UNLIKELY (!read_wrapper(reader_, (char *)&size8)) { + return struct_pack::errc::no_buffer_space; + } + size64 = size8; + } #ifdef STRUCT_PACK_OPTIMIZE - if SP_UNLIKELY (!reader_.read((char *)&size, size_type)) { - return struct_pack::errc::no_buffer_space; + else if constexpr (size_type == 2) { + if SP_UNLIKELY (!read_wrapper(reader_, (char *)&size16)) { + return struct_pack::errc::no_buffer_space; + } + size64 = size16; } -#else - if constexpr (size_type == 1) { - if SP_UNLIKELY (!reader_.read((char *)&size, size_type)) { + else if constexpr (size_type == 4) { + if SP_UNLIKELY (!read_wrapper(reader_, (char *)&size32)) { + 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; } } + else { + static_assert(!sizeof(T), "illegal size_type"); + } +#else else { switch (size_type_) { case 1: - if SP_UNLIKELY (!reader_.read((char *)&size, 2)) { + if SP_UNLIKELY (!read_wrapper<2>(reader_, (char *)&size16)) { return struct_pack::errc::no_buffer_space; } + size64 = size16; break; case 2: - if SP_UNLIKELY (!reader_.read((char *)&size, 4)) { + if SP_UNLIKELY (!read_wrapper<4>(reader_, (char *)&size32)) { return struct_pack::errc::no_buffer_space; } + size64 = size32; break; case 3: - if SP_UNLIKELY (!reader_.read((char *)&size, 8)) { + if SP_UNLIKELY (!read_wrapper<8>(reader_, (char *)&size64)) { return struct_pack::errc::no_buffer_space; } break; @@ -674,7 +726,7 @@ class unpacker { } } #endif - if SP_UNLIKELY (size == 0) { + if SP_UNLIKELY (size64 == 0) { return {}; } if constexpr (map_container) { @@ -682,11 +734,12 @@ class unpacker { value{}; if constexpr (is_trivial_serializable::value && !NotSkip) { - return reader_.ignore(size * sizeof(value)) ? errc{} - : errc::no_buffer_space; + return reader_.ignore(size64 * sizeof(value)) + ? errc{} + : errc::no_buffer_space; } else { - for (size_t i = 0; i < size; ++i) { + for (uint64_t i = 0; i < size64; ++i) { code = deserialize_one(value); if SP_UNLIKELY (code != struct_pack::errc{}) { return code; @@ -702,11 +755,12 @@ class unpacker { typename type::value_type value{}; if constexpr (is_trivial_serializable::value && !NotSkip) { - return reader_.ignore(size * sizeof(value)) ? errc{} - : errc::no_buffer_space; + return reader_.ignore(size64 * sizeof(value)) + ? errc{} + : errc::no_buffer_space; } else { - for (size_t i = 0; i < size; ++i) { + for (uint64_t i = 0; i < size64; ++i) { code = deserialize_one(value); if SP_UNLIKELY (code != struct_pack::errc{}) { return code; @@ -721,25 +775,29 @@ class unpacker { else { using value_type = typename type::value_type; if constexpr (trivially_copyable_container) { - size_t mem_sz = size * sizeof(value_type); + uint64_t mem_sz = size64 * sizeof(value_type); if constexpr (NotSkip) { if constexpr (string_view || dynamic_span) { static_assert( view_reader_t, "The Reader isn't a view_reader, can't deserialize " "a string_view/span"); + static_assert( + sizeof(value_type) == 1 || is_system_little_endian, + "zero-copy in big endian is limit."); const char *view = reader_.read_view(mem_sz); if SP_UNLIKELY (view == nullptr) { return struct_pack::errc::no_buffer_space; } - item = {(value_type *)(view), size}; + item = {(value_type *)(view), size64}; } else { if SP_UNLIKELY (mem_sz >= PTRDIFF_MAX) unreachable(); else { - item.resize(size); - if SP_UNLIKELY (!reader_.read((char *)item.data(), mem_sz)) { + item.resize(size64); + if SP_UNLIKELY (!read_wrapper( + reader_, (char *)item.data(), size64)) { return struct_pack::errc::no_buffer_space; } } @@ -757,7 +815,7 @@ class unpacker { "is a non-trival-serializable type."); } else { - item.resize(size); + item.resize(size64); for (auto &i : item) { code = deserialize_one(i); if SP_UNLIKELY (code != struct_pack::errc{}) { @@ -768,7 +826,7 @@ class unpacker { } else { value_type useless; - for (size_t i = 0; i < size; ++i) { + for (size_t i = 0; i < size64; ++i) { code = deserialize_one(useless); if SP_UNLIKELY (code != struct_pack::errc{}) { return code; @@ -792,7 +850,8 @@ class unpacker { } else if constexpr (optional || expected) { bool has_value{}; - if SP_UNLIKELY (!reader_.read((char *)&has_value, sizeof(bool))) { + if SP_UNLIKELY (!read_wrapper(reader_, + (char *)&has_value)) { return struct_pack::errc::no_buffer_space; } if SP_UNLIKELY (!has_value) { @@ -817,7 +876,7 @@ class unpacker { } else if constexpr (is_variant_v) { uint8_t index{}; - if SP_UNLIKELY (!reader_.read((char *)&index, sizeof(index))) { + if SP_UNLIKELY (!read_wrapper(reader_, (char *)&index)) { return struct_pack::errc::no_buffer_space; } if SP_UNLIKELY (index >= std::variant_size_v) { @@ -837,9 +896,11 @@ class unpacker { std::is_aggregate_v>, "struct_pack only support aggregated type, or you should " "add macro STRUCT_PACK_REFL(Type,field1,field2...)"); - if constexpr (is_trivial_serializable::value) { + if constexpr (is_trivial_serializable::value && + is_system_little_endian) { if constexpr (NotSkip) { - if SP_UNLIKELY (!reader_.read((char *)&item, sizeof(type))) { + if SP_UNLIKELY (!read_wrapper(reader_, + (char *)&item)) { return struct_pack::errc::no_buffer_space; } } @@ -848,7 +909,9 @@ class unpacker { : errc::no_buffer_space; } } - else if constexpr (is_trivial_serializable::value) { + else if constexpr ((is_trivial_serializable::value && + !is_system_little_endian) || + is_trivial_serializable::value) { visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA { int i = 1; auto f = [&](auto &&item) { @@ -883,7 +946,8 @@ class unpacker { return struct_pack::errc::no_buffer_space; } bool has_value{}; - if SP_UNLIKELY (!reader_.read((char *)&has_value, sizeof(bool))) { + if SP_UNLIKELY (!read_wrapper(reader_, + (char *)&has_value)) { return struct_pack::errc::no_buffer_space; } if (!has_value) { @@ -1062,7 +1126,8 @@ class unpacker { template struct MD5_reader_wrapper : public Reader { MD5_reader_wrapper(Reader &&reader) : Reader(std::move(reader)) { - is_failed = !Reader::read((char *)&head_chars, sizeof(head_chars)); + is_failed = + !read_wrapper(*(Reader *)this, (char *)&head_chars); } bool read_head(char *target) { if SP_UNLIKELY (is_failed) { diff --git a/include/ylt/struct_pack/varint.hpp b/include/ylt/struct_pack/varint.hpp index 799106e36..701a127d7 100644 --- a/include/ylt/struct_pack/varint.hpp +++ b/include/ylt/struct_pack/varint.hpp @@ -208,10 +208,10 @@ STRUCT_PACK_INLINE void serialize_varint(writer& writer_, const T& t) { } while (v >= 0x80) { uint8_t temp = v | 0x80u; - writer_.write((char*)&temp, sizeof(temp)); + write_wrapper(writer_, (char*)&temp); v >>= 7; } - writer_.write((char*)&v, sizeof(char)); + write_wrapper(writer_, (char*)&v); } #if __cpp_concepts >= 201907L template diff --git a/src/struct_pack/tests/binary_data/test_cross_platform.dat b/src/struct_pack/tests/binary_data/test_cross_platform.dat index 0338937e8..f5a6d0a3f 100644 Binary files a/src/struct_pack/tests/binary_data/test_cross_platform.dat and b/src/struct_pack/tests/binary_data/test_cross_platform.dat differ diff --git a/src/struct_pack/tests/binary_data/test_cross_platform_without_debug_info.dat b/src/struct_pack/tests/binary_data/test_cross_platform_without_debug_info.dat index 7134c4990..f0ed87430 100644 Binary files a/src/struct_pack/tests/binary_data/test_cross_platform_without_debug_info.dat and b/src/struct_pack/tests/binary_data/test_cross_platform_without_debug_info.dat differ diff --git a/src/struct_pack/tests/test_cross_platform.cpp b/src/struct_pack/tests/test_cross_platform.cpp index d7434de79..a320cdf8b 100644 --- a/src/struct_pack/tests/test_cross_platform.cpp +++ b/src/struct_pack/tests/test_cross_platform.cpp @@ -7,6 +7,19 @@ using namespace struct_pack; using namespace doctest; +void data_gen() { + { + std::ofstream ifs("binary_data/test_cross_platform.dat"); + auto object = create_complicated_object(); + serialize_to(ifs, object); + } + { + std::ofstream ifs("binary_data/test_cross_platform_without_debug_info.dat"); + auto object = create_complicated_object(); + serialize_to(ifs, object); + } +} + TEST_CASE("testing deserialize other platform data") { std::ifstream ifs("binary_data/test_cross_platform.dat"); if (!ifs.is_open()) { diff --git a/src/struct_pack/tests/test_struct.hpp b/src/struct_pack/tests/test_struct.hpp index 38b29d6b2..a66218a37 100644 --- a/src/struct_pack/tests/test_struct.hpp +++ b/src/struct_pack/tests/test_struct.hpp @@ -40,6 +40,15 @@ struct empty {}; enum class Color { red, black, white }; +struct trivial_one { + int a; + double b; + float c; + auto operator==(const trivial_one &rhs) const { + return a == rhs.a && b == rhs.b && c == rhs.c; + } +}; + struct complicated_object { Color color; int a; @@ -56,30 +65,34 @@ struct complicated_object { std::array m; person n[2]; std::pair o; + std::vector> p; bool operator==(const complicated_object &o) const { return color == o.color && a == o.a && b == o.b && c == o.c && d == o.d && e == o.e && f == o.f && g == o.g && h == o.h && i == o.i && j == o.j && k == o.k && m == o.m && n[0] == o.n[0] && - n[1] == o.n[1] && this->o == o.o; + n[1] == o.n[1] && this->o == o.o && p == o.p; } }; inline complicated_object create_complicated_object() { - return complicated_object{Color::red, - 42, - "hello", - {{20, "tom"}, {22, "jerry"}}, - {"hello", "world"}, - {1, 2}, - {{1, {20, "tom"}}}, - {{1, {20, "tom"}}, {1, {22, "jerry"}}}, - {"aa", "bb"}, - {1, 2}, - {{1, {20, "tom"}}}, - {{1, 2}}, - {person{20, "tom"}, {22, "jerry"}}, - {person{15, "tom"}, {31, "jerry"}}, - std::make_pair("aa", person{20, "tom"})}; + return complicated_object{ + Color::red, + 42, + "hello", + {{20, "tom"}, {22, "jerry"}}, + {"hello", "world"}, + {1, 2}, + {{1, {20, "tom"}}}, + {{1, {20, "tom"}}, {1, {22, "jerry"}}}, + {"aa", "bb"}, + {1, 2}, + {{1, {20, "tom"}}}, + {{1, 2}}, + {person{20, "tom"}, {22, "jerry"}}, + {person{15, "tom"}, {31, "jerry"}}, + std::make_pair("aa", person{20, "tom"}), + {{trivial_one{1232114, 1.7, 2.4}, trivial_one{12315, 1.4, 2.6}}, + {trivial_one{4, 0.7, 1.4}, trivial_one{1123115, 11111.4, 2213321.6}}}}; } struct nested_object {