From 24ee7fc140fdfad8a096d9af23136b4f2a1f7970 Mon Sep 17 00:00:00 2001 From: bbgan <2893129936@qq.com> Date: Sun, 21 Jul 2024 01:48:42 +0800 Subject: [PATCH] support pb2[struct_pb] --- CMakeLists.txt | 7 ++ iguana/pb_reader.hpp | 160 +++++++++++++++++++------------ iguana/pb_util.hpp | 136 +++++++++++++++++++------- iguana/pb_writer.hpp | 95 +++++++++++------- test/proto/unittest_proto2.h | 76 +++++++++++++++ test/proto/unittest_proto2.proto | 26 +++++ test/test_proto2.cpp | 99 +++++++++++++++++++ 7 files changed, 465 insertions(+), 134 deletions(-) create mode 100644 test/proto/unittest_proto2.h create mode 100644 test/proto/unittest_proto2.proto create mode 100644 test/test_proto2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d90b17ce..77d375bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ set(TEST_XMLNOTHROW test/test_xml_nothrow.cpp) set(TEST_PB test/test_pb.cpp) set(TEST_PROTO test/test_proto3.cpp) set(PB_BENCHMARK benchmark/pb_benchmark.cpp) +set(TEST_PROTO2 test/test_proto2.cpp) add_executable(json_example ${JSON_EXAMPLE}) add_executable(json_benchmark ${JSONBENCHMARK}) @@ -127,9 +128,15 @@ if(Protobuf_FOUND) add_executable(pb_benchmark ${PB_BENCHMARK} ${PROTO_SRCS}) target_link_libraries(pb_benchmark ${Protobuf_LIBRARIES}) + add_executable(test_proto ${PROTO_SRCS} ${TEST_PROTO}) target_link_libraries(test_proto ${Protobuf_LIBRARIES}) add_test(NAME test_proto COMMAND test_proto) + + add_executable(test_proto2 ${PROTO_SRCS} ${TEST_PROTO2}) + target_link_libraries(test_proto2 ${Protobuf_LIBRARIES}) + + add_test(NAME test_proto2 COMMAND test_proto2) endif() # unit test diff --git a/iguana/pb_reader.hpp b/iguana/pb_reader.hpp index 83d2425d..253e2ead 100644 --- a/iguana/pb_reader.hpp +++ b/iguana/pb_reader.hpp @@ -23,6 +23,53 @@ IGUANA_INLINE void decode_pair_value(T& val, std::string_view& pb_str) { from_pb_impl(val, pb_str); } +template +IGUANA_INLINE void decode_sequence(T& val, std::string_view& pb_str, + uint32_t field_no) { + using item_type = typename T::value_type; + size_t pos = 0; + if constexpr (is_lenprefix_v || !is_packed_num) { + // item_type non-packed + while (!pb_str.empty()) { + item_type item{}; + from_pb_impl(item, pb_str); + val.push_back(std::move(item)); + if (pb_str.empty()) { + break; + } + uint32_t key = detail::decode_varint(pb_str, pos); + uint32_t field_number = key >> 3; + if (field_number != field_no) { + break; + } + else { + pb_str = pb_str.substr(pos); + } + } + } + else { + // item_type packed + size_t pos; + uint32_t size = detail::decode_varint(pb_str, pos); + pb_str = pb_str.substr(pos); + if (pb_str.size() < size) + IGUANA_UNLIKELY { + throw std::invalid_argument("Invalid fixed int value: too few bytes."); + } + using item_type = typename T::value_type; + size_t start = pb_str.size(); + + while (!pb_str.empty()) { + item_type item; + from_pb_impl(item, pb_str); + val.push_back(std::move(item)); + if (start - pb_str.size() == size) { + break; + } + } + } +} + template IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str, uint32_t field_no) { @@ -41,49 +88,8 @@ IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str, from_pb(val, pb_str.substr(0, size)); pb_str = pb_str.substr(size); } - else if constexpr (is_sequence_container::value) { - using item_type = typename T::value_type; - if constexpr (is_lenprefix_v) { - // item_type non-packed - while (!pb_str.empty()) { - item_type item{}; - from_pb_impl(item, pb_str); - val.push_back(std::move(item)); - if (pb_str.empty()) { - break; - } - uint32_t key = detail::decode_varint(pb_str, pos); - uint32_t field_number = key >> 3; - if (field_number != field_no) { - break; - } - else { - pb_str = pb_str.substr(pos); - } - } - } - else { - // item_type packed - size_t pos; - uint32_t size = detail::decode_varint(pb_str, pos); - pb_str = pb_str.substr(pos); - if (pb_str.size() < size) - IGUANA_UNLIKELY { - throw std::invalid_argument( - "Invalid fixed int value: too few bytes."); - } - using item_type = typename T::value_type; - size_t start = pb_str.size(); - - while (!pb_str.empty()) { - item_type item; - from_pb_impl(item, pb_str); - val.push_back(std::move(item)); - if (start - pb_str.size() == size) { - break; - } - } - } + else if constexpr (is_packed_t::value) { + decode_sequence(val.val, pb_str, field_no); } else if constexpr (is_map_container::value) { using item_type = std::pair; @@ -170,6 +176,9 @@ IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str, else if constexpr (optional_v) { from_pb_impl(val.emplace(), pb_str); } + else if constexpr (is_optional_t::value) { + from_pb_impl(val.val, pb_str); + } else { static_assert(!sizeof(T), "err"); } @@ -205,21 +214,34 @@ IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) { return; } pb_str = pb_str.substr(pos); - if (wire_type != detail::get_wire_type()) - IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); } - if constexpr (variant_v) { - detail::parse_oneof(val.value(t), val, pb_str); + if constexpr (is_sequence_container::value) { + if (wire_type != detail::get_wire_type()) { + // while parsing unpacked repeated number, the wire type is a + // numeric type so it's not equal and it should be parsed as an + // unpacked number. + detail::decode_sequence(val.value(t), pb_str, val.field_no); + } + else { + detail::decode_sequence(val.value(t), pb_str, val.field_no); + } } else { - detail::from_pb_impl(val.value(t), pb_str, val.field_no); - } - if (pb_str.empty()) { - parse_done = true; - return; + if (wire_type != detail::get_wire_type()) + IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); } + if constexpr (variant_v) { + detail::parse_oneof(val.value(t), val, pb_str); + } + else { + detail::from_pb_impl(val.value(t), pb_str, val.field_no); + } + if (pb_str.empty()) { + parse_done = true; + return; + } + key = detail::decode_varint(pb_str, pos); + wire_type = static_cast(key & 0b0111); + field_number = key >> 3; } - key = detail::decode_varint(pb_str, pos); - wire_type = static_cast(key & 0b0111); - field_number = key >> 3; }, std::make_index_sequence{}); if (parse_done) @@ -233,14 +255,30 @@ IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) { [&t, &pb_str, wire_type](auto& val) { using sub_type = typename std::decay_t::sub_type; using value_type = typename std::decay_t::value_type; - if (wire_type != detail::get_wire_type()) { - throw std::runtime_error("unmatched wire_type"); - } - if constexpr (variant_v) { - detail::parse_oneof(val.value(t), val, pb_str); + + if constexpr (is_sequence_container::value) { + using item_type = typename sub_type::value_type; + if (wire_type != detail::get_wire_type()) { + if (wire_type != detail::get_wire_type()) { + throw std::runtime_error("unmatched wire_type"); + } + detail::decode_sequence(val.value(t), pb_str, + val.field_no); + } + else { + detail::decode_sequence(val.value(t), pb_str, val.field_no); + } } else { - detail::from_pb_impl(val.value(t), pb_str, val.field_no); + if (wire_type != detail::get_wire_type()) { + throw std::runtime_error("unmatched wire_type"); + } + if constexpr (variant_v) { + detail::parse_oneof(val.value(t), val, pb_str); + } + else { + detail::from_pb_impl(val.value(t), pb_str, val.field_no); + } } }, member); diff --git a/iguana/pb_util.hpp b/iguana/pb_util.hpp index 27bc9a89..fdc8e290 100644 --- a/iguana/pb_util.hpp +++ b/iguana/pb_util.hpp @@ -19,6 +19,33 @@ namespace iguana { +template +struct optional_t { + using value_type = T; + T val; + const T default_val; + constexpr optional_t(T v = T{}) : val(v), default_val(v) {} +}; + +template <> +struct optional_t { + using value_type = std::string_view; + const std::string default_val; + value_type val; + optional_t(std::string v = "") : default_val(v), val(default_val) {} +}; + +template +struct is_optional_t : is_template_instant_of {}; + +template +struct packed_t { + std::vector val; +}; + +template +struct is_packed_t : is_template_instant_of {}; + enum class WireType : uint32_t { Varint = 0, Fixed64 = 1, @@ -61,10 +88,10 @@ constexpr inline WireType get_wire_type() { else if constexpr (std::is_same_v || std::is_same_v || is_reflection_v || is_sequence_container::value || - is_map_container::value) { + is_map_container::value || is_packed_t::value) { return WireType::LengthDelimeted; } - else if constexpr (optional_v) { + else if constexpr (optional_v || is_optional_t::value) { return get_wire_type(); } else { @@ -393,8 +420,8 @@ IGUANA_INLINE size_t str_numeric_size(Type&& t) { } } -template +template IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr); template @@ -413,7 +440,7 @@ constexpr inline size_t get_variant_index() { } } -template +template IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr& size_arr) { using T = std::decay_t; int len = 0; @@ -427,16 +454,53 @@ IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr& size_arr) { constexpr uint32_t key = ((field_no + offset) << 3) | static_cast(get_wire_type()); - len = pb_key_value_size( - std::forward(value), size_arr); + len = pb_key_value_size(std::forward(value), + size_arr); }, std::forward(t)); return len; } +template +IGUANA_INLINE size_t pb_seq_size(Type&& t, Arr& size_arr) { + using T = std::remove_const_t>; + using item_type = typename T::value_type; + size_t len = 0; + if constexpr (is_lenprefix_v || !is_packed_num) { + for (auto& item : t) { + len += pb_key_value_size(item, size_arr); + } + return len; + } + else { + for (auto& item : t) { + // here 0 to get pakced size, and item must be numeric + len += str_numeric_size<0, false>(item); + } + return (len == 0) + ? 0 + : key_size + variant_uint32_size(static_cast(len)) + + len; + } +} + +template +constexpr inline uint32_t get_sub_key() { + if constexpr (is_proto2 && is_sequence_container::value) { + // while encoding the unpacked number, the wire_type should depend on the + // value_type + return (field_no << 3) | + static_cast(get_wire_type()); + } + return (field_no << 3) | static_cast(get_wire_type()); +} + // returns size = key_size + optional(len_size) + len // when key_size == 0, return len -template +template IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) { using T = std::remove_const_t>; if constexpr (is_reflection_v || is_custom_reflection_v) { @@ -461,15 +525,14 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) { get_variant_index - 1>(); if constexpr (offset == 0) { - len += pb_oneof_size(val, size_arr); + len += pb_oneof_size(val, size_arr); } } else { constexpr uint32_t sub_key = - (value.field_no << 3) | - static_cast(get_wire_type()); + get_sub_key(); constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key); - len += pb_key_value_size(val, size_arr); + len += pb_key_value_size(val, size_arr); } }, std::make_index_sequence{}); @@ -494,33 +557,19 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) { } } else if constexpr (is_sequence_container::value) { - using item_type = typename T::value_type; - size_t len = 0; - if constexpr (is_lenprefix_v) { - for (auto& item : t) { - len += pb_key_value_size(item, size_arr); - } - return len; - } - else { - for (auto& item : t) { - // here 0 to get pakced size, and item must be numeric - len += str_numeric_size<0, false>(item); - } - if (len == 0) { - return 0; - } - else { - return key_size + variant_uint32_size(static_cast(len)) + len; - } - } + // proto2 unpacked by default, proto3 packed by default + return pb_seq_size(t, size_arr); + } + else if constexpr (is_packed_t::value) { + // packed_t type will pack the value + return pb_seq_size(t.val, size_arr); } else if constexpr (is_map_container::value) { size_t len = 0; for (auto& [k, v] : t) { // the key_size of k and v is constant 1 - auto kv_len = pb_key_value_size<1, false>(k, size_arr) + - pb_key_value_size<1, false>(v, size_arr); + auto kv_len = pb_key_value_size(k, size_arr) + + pb_key_value_size(v, size_arr); len += key_size + variant_uint32_size(static_cast(kv_len)) + kv_len; } @@ -530,7 +579,20 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) { if (!t.has_value()) { return 0; } - return pb_key_value_size(*t, size_arr); + return pb_key_value_size(*t, size_arr); + } + else if constexpr (is_optional_t::value) { + using value_type = typename T::value_type; + if constexpr (is_reflection_v || + is_custom_reflection_v) { + return pb_key_value_size(t.val, size_arr); + } + else { + if (omit_default_val && t.val == t.default_val) { + return 0; + } + return str_numeric_size(t.val); + } } else { return str_numeric_size(t); @@ -538,7 +600,7 @@ IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) { } // return the payload size -template +template IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) { using T = std::remove_const_t>; if constexpr (is_reflection_v || is_custom_reflection_v) { @@ -575,7 +637,7 @@ IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) { if (!t.has_value()) { return 0; } - return pb_value_size(*t, sz_ptr); + return pb_value_size(*t, sz_ptr); } else { return str_numeric_size<0, false>(t); diff --git a/iguana/pb_writer.hpp b/iguana/pb_writer.hpp index 219650fb..7063049d 100644 --- a/iguana/pb_writer.hpp +++ b/iguana/pb_writer.hpp @@ -25,11 +25,11 @@ IGUANA_INLINE void encode_fixed_field(V val, It&& it) { it += size; } -template +template IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr); -template +template IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size, uint32_t*& sz_ptr) { if (size == 0) @@ -40,7 +40,7 @@ IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size, serialize_varint(0, it); } else { - to_pb_impl(val, it, sz_ptr); + to_pb_impl(val, it, sz_ptr); } } @@ -78,7 +78,7 @@ IGUANA_INLINE void encode_numeric_field(T t, It&& it) { } } -template +template IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it, uint32_t*& sz_ptr) { using T = std::decay_t; std::visit( @@ -91,19 +91,42 @@ IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it, uint32_t*& sz_ptr) { constexpr uint32_t key = ((field_no + offset) << 3) | static_cast(get_wire_type()); - to_pb_impl(std::forward(value), it, sz_ptr); + to_pb_impl(std::forward(value), + it, sz_ptr); }, std::forward(t)); } +template +IGUANA_INLINE void to_pb_seq(Type&& t, It&& it, uint32_t*& sz_ptr) { + using T = std::remove_const_t>; + using item_type = typename T::value_type; + if constexpr (is_lenprefix_v || !packed) { + // non-packed + for (auto& item : t) { + to_pb_impl(item, it, sz_ptr); + } + } + else { + if (t.empty()) + IGUANA_UNLIKELY { return; } + serialize_varint_u32_constexpr(it); + serialize_varint(pb_value_size(t, sz_ptr), it); + for (auto& item : t) { + encode_numeric_field<0, false>(item, it); + } + } +} + // omit_default_val = true indicates to omit the default value in searlization -template +template IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) { using T = std::remove_const_t>; if constexpr (is_reflection_v || is_custom_reflection_v) { // can't be omitted even if values are empty if constexpr (key != 0) { - auto len = pb_value_size(t, sz_ptr); + auto len = pb_value_size(t, sz_ptr); serialize_varint_u32_constexpr(it); serialize_varint(len, it); if (len == 0) @@ -125,37 +148,24 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) { get_variant_index - 1>(); if constexpr (offset == 0) { - to_pb_oneof(val, it, sz_ptr); + to_pb_oneof(val, it, sz_ptr); } } else { constexpr uint32_t sub_key = - (value.field_no << 3) | - static_cast(get_wire_type()); - to_pb_impl(val, it, sz_ptr); + get_sub_key(); + to_pb_impl(val, it, sz_ptr); } }, std::make_index_sequence{}); } + else if constexpr (is_packed_t::value) { + to_pb_seq(t.val, it, sz_ptr); + } else if constexpr (is_sequence_container::value) { // TODO support std::array // repeated values can't be omitted even if values are empty - using item_type = typename T::value_type; - if constexpr (is_lenprefix_v) { - // non-packed - for (auto& item : t) { - to_pb_impl(item, it, sz_ptr); - } - } - else { - if (t.empty()) - IGUANA_UNLIKELY { return; } - serialize_varint_u32_constexpr(it); - serialize_varint(pb_value_size(t, sz_ptr), it); - for (auto& item : t) { - encode_numeric_field(item, it); - } - } + to_pb_seq(t, it, sz_ptr); } else if constexpr (is_map_container::value) { using first_type = typename T::key_type; @@ -171,7 +181,7 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) { serialize_varint_u32_constexpr(it); // k must be string or numeric auto k_val_len = str_numeric_size<0, false>(k); - auto v_val_len = pb_value_size(v, sz_ptr); + auto v_val_len = pb_value_size(v, sz_ptr); auto pair_len = key1_size + key2_size + k_val_len + v_val_len; if constexpr (is_lenprefix_v) { pair_len += variant_uint32_size(k_val_len); @@ -181,15 +191,28 @@ IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) { } serialize_varint(pair_len, it); // map k and v can't be omitted even if values are empty - encode_pair_value(k, it, k_val_len, sz_ptr); - encode_pair_value(v, it, v_val_len, sz_ptr); + encode_pair_value(k, it, k_val_len, sz_ptr); + encode_pair_value(v, it, v_val_len, sz_ptr); } } else if constexpr (optional_v) { if (!t.has_value()) { return; } - to_pb_impl(*t, it, sz_ptr); + to_pb_impl(*t, it, sz_ptr); + } + else if constexpr (is_optional_t::value) { + using value_type = typename T::value_type; + if constexpr (is_reflection_v || + is_custom_reflection_v) { + to_pb_impl(t.val, it, sz_ptr); + } + else { + if (omit_default_val && t.val == t.default_val) { + return; + } + to_pb_impl(t.val, it, sz_ptr); + } } else if constexpr (std::is_same_v || std::is_same_v) { @@ -449,13 +472,13 @@ IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type, #endif } // namespace detail -template -IGUANA_INLINE void to_pb(T const& t, Stream& out) { +template +IGUANA_INLINE void to_pb(const T& t, Stream& out) { std::vector size_arr; - auto byte_len = detail::pb_key_value_size<0>(t, size_arr); + auto byte_len = detail::pb_key_value_size(t, size_arr); detail::resize(out, byte_len); auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0]; - detail::to_pb_impl<0>(t, &out[0], sz_ptr); + detail::to_pb_impl(t, &out[0], sz_ptr); } #if defined(__clang__) || defined(_MSC_VER) || \ diff --git a/test/proto/unittest_proto2.h b/test/proto/unittest_proto2.h new file mode 100644 index 00000000..9ad21e32 --- /dev/null +++ b/test/proto/unittest_proto2.h @@ -0,0 +1,76 @@ +#pragma once +#include "unittest_proto2.pb.h" // protoc gen +namespace stpb2 { + +struct SimpleMsg { + iguana::optional_t optional_string{"kim"}; + iguana::optional_t optional_int32; + iguana::packed_t repeated_sint32; + std::vector repeated_int64; +}; +REFLECTION(SimpleMsg, optional_string, optional_int32, repeated_sint32, + repeated_int64); + +struct NestedMsg { + iguana::optional_t msg; + std::vector repeated_simple_msg; +}; +REFLECTION(NestedMsg, msg, repeated_simple_msg); +} // namespace stpb2 + +void CheckSimpleMsg(const stpb2::SimpleMsg& st, const pb2::SimpleMsg& msg) { + CHECK(st.optional_string.val == msg.optional_string()); + + if (msg.has_optional_int32()) { + CHECK(st.optional_int32.val == msg.optional_int32()); + } + + CHECK(st.repeated_sint32.val.size() == msg.repeated_sint32().size()); + for (size_t i = 0; i < st.repeated_sint32.val.size(); ++i) { + CHECK(st.repeated_sint32.val[i] == msg.repeated_sint32()[i]); + } + + CHECK(st.repeated_int64.size() == msg.repeated_int64().size()); + for (size_t i = 0; i < st.repeated_int64.size(); ++i) { + CHECK(st.repeated_int64[i] == msg.repeated_int64()[i]); + } +} + +void SetSimpleMsg(const stpb2::SimpleMsg& st, pb2::SimpleMsg& msg) { + msg.set_optional_string(st.optional_string.val); + msg.set_optional_int32(st.optional_int32.val); + for (const auto& value : st.repeated_sint32.val) { + msg.add_repeated_sint32(value.val); + } + + for (const auto& value : st.repeated_int64) { + msg.add_repeated_int64(value); + } +} + +void CheckNestedMsg(const stpb2::NestedMsg& st, const pb2::NestedMsg& msg) { + if (msg.has_msg()) { + CheckSimpleMsg(st.msg.val, msg.msg()); + } + + CHECK(st.repeated_simple_msg.size() == msg.repeated_simple_msg().size()); + for (size_t i = 0; i < st.repeated_simple_msg.size(); ++i) { + CheckSimpleMsg(st.repeated_simple_msg[i], msg.repeated_simple_msg()[i]); + } +} + +void SetNestedMsg(const stpb2::NestedMsg& st, pb2::NestedMsg& msg) { + SetSimpleMsg(st.msg.val, *msg.mutable_msg()); + for (const auto& simple_msg : st.repeated_simple_msg) { + SetSimpleMsg(simple_msg, *msg.add_repeated_simple_msg()); + } +} + +inline void print_hex_str(const std::string& str) { + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for (unsigned char c : str) { + oss << std::setw(2) << static_cast(c); + } + std::cout << oss.str() << std::endl; +} \ No newline at end of file diff --git a/test/proto/unittest_proto2.proto b/test/proto/unittest_proto2.proto new file mode 100644 index 00000000..40da4283 --- /dev/null +++ b/test/proto/unittest_proto2.proto @@ -0,0 +1,26 @@ +syntax = "proto2"; + +package pb2; + +// 主要测试optional和packed_t类型 + +message SimpleMsg { + // optional字段在proto2中可以明确表示这个字段是可选的。 + optional string optional_string = 1 [default = "kim"]; + + // optional字段可以没有默认值。 + optional int32 optional_int32 = 2; + + // repeated字段不能有默认值。 + repeated sint32 repeated_sint32 = 3 [packed = true]; + repeated int64 repeated_int64 = 4; +} + + +message NestedMsg { + // 嵌套的message类型字段。 + optional SimpleMsg msg = 1; + + // repeated字段表示这个字段可以包含零个或多个值。 + repeated SimpleMsg repeated_simple_msg = 2; +} diff --git a/test/test_proto2.cpp b/test/test_proto2.cpp new file mode 100644 index 00000000..5d34d205 --- /dev/null +++ b/test/test_proto2.cpp @@ -0,0 +1,99 @@ +#define DOCTEST_CONFIG_IMPLEMENT +#include "doctest.h" +#include "iguana/pb_reader.hpp" +#include "iguana/pb_writer.hpp" +#include "proto/unittest_proto2.h" // msg reflection + +TEST_CASE("test SimpleMsg") { + { + stpb2::SimpleMsg st; + st.optional_string.val = "Hello"; + st.optional_int32.val = 42; + st.repeated_sint32.val = std::vector{{1}, {2}, {3}}; + st.repeated_int64 = std::vector{100, 200, 3}; + + std::string st_ss; + iguana::to_pb(st, st_ss); + + pb2::SimpleMsg msg; + SetSimpleMsg(st, msg); + std::string pb_ss; + msg.SerializeToString(&pb_ss); + CHECK(st_ss == pb_ss); + + stpb2::SimpleMsg deserialized_st; + iguana::from_pb(deserialized_st, st_ss); + pb2::SimpleMsg deserialized_msg; + deserialized_msg.ParseFromString(pb_ss); + CheckSimpleMsg(deserialized_st, deserialized_msg); + } + { + stpb2::SimpleMsg st; + std::string st_ss; + iguana::to_pb(st, st_ss); + + pb2::SimpleMsg msg; + std::string pb_ss; + msg.SerializeToString(&pb_ss); + CHECK(st_ss == pb_ss); + + stpb2::SimpleMsg deserialized_st; + iguana::from_pb(deserialized_st, st_ss); + pb2::SimpleMsg deserialized_msg; + deserialized_msg.ParseFromString(pb_ss); + CheckSimpleMsg(deserialized_st, deserialized_msg); + } + { + stpb2::SimpleMsg st; + st.optional_string.val = "Hello"; + st.optional_int32.val = 42; + + std::string st_ss; + iguana::to_pb(st, st_ss); + + pb2::SimpleMsg msg; + SetSimpleMsg(st, msg); + std::string pb_ss; + msg.SerializeToString(&pb_ss); + CHECK(st_ss == pb_ss); + + stpb2::SimpleMsg deserialized_st; + iguana::from_pb(deserialized_st, st_ss); + pb2::SimpleMsg deserialized_msg; + deserialized_msg.ParseFromString(pb_ss); + CheckSimpleMsg(deserialized_st, deserialized_msg); + } +} + +TEST_CASE("test NestedMsg") { + { + stpb2::NestedMsg st; + auto& simple_msg = st.msg.val; + simple_msg.optional_string.val = "Hello"; + simple_msg.optional_int32.val = 42; + simple_msg.repeated_sint32.val = + std::vector{{1}, {2}, {3}}; + simple_msg.repeated_int64 = std::vector{100, 200, 3}; + + std::string st_ss; + iguana::to_pb(st, st_ss); + + pb2::NestedMsg msg; + SetNestedMsg(st, msg); + std::string pb_ss; + msg.SerializeToString(&pb_ss); + print_hex_str(st_ss); + print_hex_str(pb_ss); + CHECK(st_ss == pb_ss); + + stpb2::NestedMsg deserialized_st; + iguana::from_pb(deserialized_st, st_ss); + pb2::NestedMsg deserialized_msg; + deserialized_msg.ParseFromString(pb_ss); + CheckNestedMsg(deserialized_st, deserialized_msg); + } +} + +DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) +int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } +DOCTEST_MSVC_SUPPRESS_WARNING_POP