diff options
Diffstat (limited to '')
-rw-r--r-- | src/serialization/tuple.h | 162 | ||||
-rw-r--r-- | tests/unit_tests/serialization.cpp | 92 |
2 files changed, 149 insertions, 105 deletions
diff --git a/src/serialization/tuple.h b/src/serialization/tuple.h index d9592bc96..b1ef94097 100644 --- a/src/serialization/tuple.h +++ b/src/serialization/tuple.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2023, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -29,7 +29,7 @@ // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #pragma once -#include <memory> +#include <tuple> #include "serialization.h" namespace serialization @@ -51,119 +51,71 @@ namespace serialization } } -template <template <bool> class Archive, class E0, class E1, class E2> -inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2>& p) +template <size_t I, bool BackwardsCompat, bool W, template <bool> class Archive, typename... Ts> +bool do_serialize_tuple_nth(Archive<W>& ar, std::tuple<Ts...>& v) { - size_t cnt; - ar.begin_array(cnt); - if (!ar.good()) - return false; - if (cnt != 3) - return false; + static constexpr const size_t tuple_size = std::tuple_size<std::tuple<Ts...>>(); + static_assert(I <= tuple_size, "bad call"); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; + if constexpr (I == 0) + { + // If BackwardsCompat is true, we serialize the size of 3-tuples and 4-tuples + if constexpr (BackwardsCompat && (tuple_size == 3 || tuple_size == 4)) + { + size_t cnt = tuple_size; + ar.begin_array(cnt); + if (cnt != tuple_size) + return false; + } + else + { + ar.begin_array(); + } + } + else if constexpr (I < tuple_size) + { + ar.delimit_array(); + } - ar.end_array(); - return true; + if constexpr (I == tuple_size) + { + ar.end_array(); + return ar.good(); + } + else + { + if (!::serialization::detail::serialize_tuple_element(ar, std::get<I>(v)) + || !ar.good()) + return false; + + return do_serialize_tuple_nth<I + 1, BackwardsCompat>(ar, v); + } } -template <template <bool> class Archive, class E0, class E1, class E2> -inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2>& p) +template <bool BackwardsCompat, bool W, template <bool> class Archive, typename... Ts> +bool do_serialize_tuple(Archive<W>& ar, std::tuple<Ts...>& v) { - ar.begin_array(3); - if (!ar.good()) - return false; - if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; - ar.end_array(); - return true; + return do_serialize_tuple_nth<0, BackwardsCompat>(ar, v); } -template <template <bool> class Archive, class E0, class E1, class E2, class E3> -inline bool do_serialize(Archive<false>& ar, std::tuple<E0,E1,E2,E3>& p) +template <bool W, template <bool> class Archive, typename... Ts> +bool do_serialize(Archive<W>& ar, std::tuple<Ts...>& v) { - size_t cnt; - ar.begin_array(cnt); - if (!ar.good()) - return false; - if (cnt != 4) - return false; + return do_serialize_tuple<true>(ar, v); +} - if (!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if (!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p))) - return false; - if (!ar.good()) - return false; +#define TUPLE_COMPACT_FIELDS(v) \ + do { \ + if (!do_serialize_tuple<false>(ar, v) || !ar.good()) \ + return false; \ + } while (0); - ar.end_array(); - return true; -} +#define TUPLE_COMPACT_FIELD_N(t, v) \ + do { \ + ar.tag(t); \ + TUPLE_COMPACT_FIELDS(v); \ + } while (0); -template <template <bool> class Archive, class E0, class E1, class E2, class E3> -inline bool do_serialize(Archive<true>& ar, std::tuple<E0,E1,E2,E3>& p) -{ - ar.begin_array(4); - if (!ar.good()) - return false; - if(!::serialization::detail::serialize_tuple_element(ar, std::get<0>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<1>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<2>(p))) - return false; - if (!ar.good()) - return false; - ar.delimit_array(); - if(!::serialization::detail::serialize_tuple_element(ar, std::get<3>(p))) - return false; - if (!ar.good()) - return false; - ar.end_array(); - return true; -} +#define TUPLE_COMPACT_FIELD(f) TUPLE_COMPACT_FIELD_N(#f, f) +#define TUPLE_COMPACT_FIELD_F(f) TUPLE_COMPACT_FIELD_N(#f, v.f) diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index ab81acefc..73a438e27 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -56,6 +56,11 @@ struct Struct int32_t a; int32_t b; char blob[8]; + + bool operator==(const Struct &other) const + { + return a == other.a && b == other.b && 0 == memcmp(blob, other.blob, sizeof(blob)); + } }; template <class Archive> @@ -1207,3 +1212,90 @@ TEST(Serialization, adl_free_function) const std::string expected = "{\"custom_fieldname\": " + std::to_string(msg.size()) + '"' + epee::string_tools::buff_to_hex_nodelimer(msg) + "\"}"; EXPECT_EQ(expected, ss.str()); } + +using Tuple3 = std::tuple<uint16_t, std::string, uint64_t>; +using Tuple4 = std::tuple<int32_t, std::string, uint64_t, Struct>; + +TEST(Serialization, tuple_3_4_backwards_compatibility) +{ + std::string serialized; + + //////////////////////////////////////// + + Tuple3 t3{1876, "Hullabaloo", 1963}; + EXPECT_TRUE(::serialization::dump_binary(t3, serialized)); + + EXPECT_EQ("0354070a48756c6c6162616c6f6fab0f", + epee::string_tools::buff_to_hex_nodelimer(serialized)); + + Tuple3 t3_recovered; + EXPECT_TRUE(::serialization::parse_binary(serialized, t3_recovered)); + EXPECT_EQ(t3, t3_recovered); + + ///////////////////////////////////////// + + Tuple4 t4{1999, "Caneck Caneck", (uint64_t)-1, {20229, 242, {1, 1, 2, 3, 5, 8, 13, 21}}}; + EXPECT_TRUE(::serialization::dump_binary(t4, serialized)); + + EXPECT_EQ("04cf0700000d43616e65636b2043616e65636bffffffffffffffffff01054f0000f20000000101020305080d15", + epee::string_tools::buff_to_hex_nodelimer(serialized)); + + Tuple4 t4_recovered; + EXPECT_TRUE(::serialization::parse_binary(serialized, t4_recovered)); + EXPECT_EQ(t4, t4_recovered); +} + +struct Tupler +{ + std::tuple<> t0; + std::tuple<int8_t> t1; + std::tuple<uint8_t, int16_t> t2; + Tuple3 t3_backcompat; + Tuple3 t3_compact; + Tuple4 t4_backcompat; + Tuple4 t4_compact; + std::tuple<uint32_t, std::string, bool, int64_t, Struct> t5; + + BEGIN_SERIALIZE_OBJECT() + FIELD(t0) + FIELD(t1) + FIELD(t2) + FIELD(t3_backcompat) + TUPLE_COMPACT_FIELD(t3_compact) + FIELD(t4_backcompat) + TUPLE_COMPACT_FIELD(t4_compact) + TUPLE_COMPACT_FIELD(t5) + END_SERIALIZE() +}; + +bool operator==(const Tupler &a, const Tupler &b) +{ + return a.t0 == b.t0 && a.t1 == b.t1 && a.t2 == b.t2 && a.t3_backcompat == b.t3_backcompat && + a.t3_compact == b.t3_compact && a.t4_backcompat == b.t4_backcompat && a.t5 == b.t5; +} + +TEST(Serialization, tuple_many_tuples) +{ + Tupler tupler{ + {}, + {69}, + {42, 420}, + {1876, "Hullabaloo", 1963}, + {1876, "Hullabaloo", 1963}, + {1999, "Caneck Caneck", (uint64_t)-1, {20229, 242, {1, 1, 2, 3, 5, 8, 13, 21}}}, + {1999, "Caneck Caneck", (uint64_t)-1, {20229, 242, {1, 1, 2, 3, 5, 8, 13, 21}}}, + {72982, "He is now rising from affluence to poverty.", false, 256, + { + 13, 37, { 1, 1, 1, 2, 3, 7, 11, 26 } + } + } + }; + + std::string serialized; + EXPECT_TRUE(::serialization::dump_binary(tupler, serialized)); + + Tupler tupler_recovered; + EXPECT_TRUE(::serialization::parse_binary(serialized, tupler_recovered)); + + EXPECT_EQ(tupler, tupler_recovered); +} |