diff options
author | Andrew Chow <github@achow101.com> | 2023-06-28 15:01:51 -0400 |
---|---|---|
committer | Andrew Chow <github@achow101.com> | 2023-06-28 15:12:12 -0400 |
commit | 7952a5934a1e071ca24a51483d058174e7b6de43 (patch) | |
tree | 61bfba21a02058c7abb221736095932f4f2ff738 | |
parent | d6ee03507f39223889a5f039c4db7204ddfb91d5 (diff) | |
parent | fa38d862358b87219b12bf31236c52f28d9fc5d6 (diff) |
Merge bitcoin/bitcoin#27927: util: Allow std::byte and char Span serialization
fa38d862358b87219b12bf31236c52f28d9fc5d6 Use only Span{} constructor for byte-like types where possible (MarcoFalke)
fa257bc8312b91c2d281f48ca2500d9cba353cc5 util: Allow std::byte and char Span serialization (MarcoFalke)
Pull request description:
Seems odd to require developers to cast all byte-like spans passed to serialization to `unsigned char`-spans. Fix that by passing and accepting byte-like spans as-is. Finally, add tests and update the code to use just `Span` where possible.
ACKs for top commit:
sipa:
utACK fa38d862358b87219b12bf31236c52f28d9fc5d6
achow101:
ACK fa38d862358b87219b12bf31236c52f28d9fc5d6
ryanofsky:
Code review ACK fa38d862358b87219b12bf31236c52f28d9fc5d6. This looks great. The second commit really removes a lot of boilerplate and shows why the first commit is useful.
Tree-SHA512: 788592d9ff515c3ebe73d48f9ecbb8d239f5b985af86f09974e508cafb0ca6d73a959350295246b4dfb496149bc56330a0b5d659fc434ba6723dbaba0b7a49e5
-rw-r--r-- | src/bench/load_external.cpp | 2 | ||||
-rw-r--r-- | src/net.cpp | 4 | ||||
-rw-r--r-- | src/pubkey.h | 4 | ||||
-rw-r--r-- | src/serialize.h | 7 | ||||
-rw-r--r-- | src/test/fuzz/p2p_transport_serialization.cpp | 2 | ||||
-rw-r--r-- | src/test/serialize_tests.cpp | 23 | ||||
-rw-r--r-- | src/uint256.h | 2 | ||||
-rw-r--r-- | src/wallet/dump.cpp | 12 | ||||
-rw-r--r-- | src/wallet/test/db_tests.cpp | 2 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 2 | ||||
-rw-r--r-- | src/wallet/walletdb.cpp | 2 |
11 files changed, 36 insertions, 26 deletions
diff --git a/src/bench/load_external.cpp b/src/bench/load_external.cpp index 2ff72a3012..1378a7b20a 100644 --- a/src/bench/load_external.cpp +++ b/src/bench/load_external.cpp @@ -34,7 +34,7 @@ static void LoadExternalBlockFile(benchmark::Bench& bench) ss << static_cast<uint32_t>(benchmark::data::block413567.size()); // We can't use the streaming serialization (ss << benchmark::data::block413567) // because that first writes a compact size. - ss.write(MakeByteSpan(benchmark::data::block413567)); + ss << Span{benchmark::data::block413567}; // Create the test file. { diff --git a/src/net.cpp b/src/net.cpp index 0ac1586cfd..3cd06f44d6 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2933,13 +2933,13 @@ void CaptureMessageToFile(const CAddress& addr, AutoFile f{fsbridge::fopen(path, "ab")}; ser_writedata64(f, now.count()); - f.write(MakeByteSpan(msg_type)); + f << Span{msg_type}; for (auto i = msg_type.length(); i < CMessageHeader::COMMAND_SIZE; ++i) { f << uint8_t{'\0'}; } uint32_t size = data.size(); ser_writedata32(f, size); - f.write(AsBytes(data)); + f << data; } std::function<void(const CAddress& addr, diff --git a/src/pubkey.h b/src/pubkey.h index d8d5e3d85b..7d37504b01 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -142,14 +142,14 @@ public: { unsigned int len = size(); ::WriteCompactSize(s, len); - s.write(AsBytes(Span{vch, len})); + s << Span{vch, len}; } template <typename Stream> void Unserialize(Stream& s) { const unsigned int len(::ReadCompactSize(s)); if (len <= SIZE) { - s.read(AsWritableBytes(Span{vch, len})); + s >> Span{vch, len}; if (len != size()) { Invalidate(); } diff --git a/src/serialize.h b/src/serialize.h index 7bc7b10779..348a6ae4f1 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -188,6 +188,7 @@ template<typename X> const X& ReadWriteAsHelper(const X& x) { return x; } } \ FORMATTER_METHODS(cls, obj) +// clang-format off #ifndef CHAR_EQUALS_INT8 template <typename Stream> void Serialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t #endif @@ -201,8 +202,7 @@ template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_wri template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); } template<typename Stream, int N> inline void Serialize(Stream& s, const char (&a)[N]) { s.write(MakeByteSpan(a)); } template<typename Stream, int N> inline void Serialize(Stream& s, const unsigned char (&a)[N]) { s.write(MakeByteSpan(a)); } -template<typename Stream> inline void Serialize(Stream& s, const Span<const unsigned char>& span) { s.write(AsBytes(span)); } -template<typename Stream> inline void Serialize(Stream& s, const Span<unsigned char>& span) { s.write(AsBytes(span)); } +template <typename Stream, typename B> void Serialize(Stream& s, Span<B> span) { (void)/* force byte-type */UCharCast(span.data()); s.write(AsBytes(span)); } #ifndef CHAR_EQUALS_INT8 template <typename Stream> void Unserialize(Stream&, char) = delete; // char serialization forbidden. Use uint8_t or int8_t @@ -217,10 +217,11 @@ template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); } template<typename Stream, int N> inline void Unserialize(Stream& s, char (&a)[N]) { s.read(MakeWritableByteSpan(a)); } template<typename Stream, int N> inline void Unserialize(Stream& s, unsigned char (&a)[N]) { s.read(MakeWritableByteSpan(a)); } -template<typename Stream> inline void Unserialize(Stream& s, Span<unsigned char>& span) { s.read(AsWritableBytes(span)); } +template <typename Stream, typename B> void Unserialize(Stream& s, Span<B> span) { (void)/* force byte-type */UCharCast(span.data()); s.read(AsWritableBytes(span)); } template <typename Stream> inline void Serialize(Stream& s, bool a) { uint8_t f = a; ser_writedata8(s, f); } template <typename Stream> inline void Unserialize(Stream& s, bool& a) { uint8_t f = ser_readdata8(s); a = f; } +// clang-format on /** diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp index ec3cdbff5a..a6fe3037e6 100644 --- a/src/test/fuzz/p2p_transport_serialization.cpp +++ b/src/test/fuzz/p2p_transport_serialization.cpp @@ -77,7 +77,7 @@ FUZZ_TARGET_INIT(p2p_transport_serialization, initialize_p2p_transport_serializa assert(msg.m_time == m_time); std::vector<unsigned char> header; - auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_type, MakeUCharSpan(msg.m_recv)); + auto msg2 = CNetMsgMaker{msg.m_recv.GetVersion()}.Make(msg.m_type, Span{msg.m_recv}); serializer.prepareForTransport(msg2, header); } } diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 09f77d2b61..b445ff8ffc 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -186,32 +186,32 @@ BOOST_AUTO_TEST_CASE(noncanonical) std::vector<char>::size_type n; // zero encoded with three bytes: - ss.write(MakeByteSpan("\xfd\x00\x00").first(3)); + ss << Span{"\xfd\x00\x00"}.first(3); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xfc encoded with three bytes: - ss.write(MakeByteSpan("\xfd\xfc\x00").first(3)); + ss << Span{"\xfd\xfc\x00"}.first(3); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xfd encoded with three bytes is OK: - ss.write(MakeByteSpan("\xfd\xfd\x00").first(3)); + ss << Span{"\xfd\xfd\x00"}.first(3); n = ReadCompactSize(ss); BOOST_CHECK(n == 0xfd); // zero encoded with five bytes: - ss.write(MakeByteSpan("\xfe\x00\x00\x00\x00").first(5)); + ss << Span{"\xfe\x00\x00\x00\x00"}.first(5); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0xffff encoded with five bytes: - ss.write(MakeByteSpan("\xfe\xff\xff\x00\x00").first(5)); + ss << Span{"\xfe\xff\xff\x00\x00"}.first(5); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // zero encoded with nine bytes: - ss.write(MakeByteSpan("\xff\x00\x00\x00\x00\x00\x00\x00\x00").first(9)); + ss << Span{"\xff\x00\x00\x00\x00\x00\x00\x00\x00"}.first(9); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); // 0x01ffffff encoded with nine bytes: - ss.write(MakeByteSpan("\xff\xff\xff\xff\x01\x00\x00\x00\x00").first(9)); + ss << Span{"\xff\xff\xff\xff\x01\x00\x00\x00\x00"}.first(9); BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); } @@ -241,6 +241,15 @@ BOOST_AUTO_TEST_CASE(class_methods) ss2 << intval << boolval << stringval << charstrval << txval; ss2 >> methodtest3; BOOST_CHECK(methodtest3 == methodtest4); + { + DataStream ds; + const std::string in{"ab"}; + ds << Span{in}; + std::array<std::byte, 2> out; + ds >> Span{out}; + BOOST_CHECK_EQUAL(out.at(0), std::byte{'a'}); + BOOST_CHECK_EQUAL(out.at(1), std::byte{'b'}); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/uint256.h b/src/uint256.h index dc8887287a..d35b3a66fa 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -78,7 +78,7 @@ public: template<typename Stream> void Serialize(Stream& s) const { - s.write(MakeByteSpan(m_data)); + s << Span(m_data); } template<typename Stream> diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp index 44c93eed7c..3ac5cf03b1 100644 --- a/src/wallet/dump.cpp +++ b/src/wallet/dump.cpp @@ -57,12 +57,12 @@ bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error) // Write out a magic string with version std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION); dump_file.write(line.data(), line.size()); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; // Write out the file format line = strprintf("%s,%s\n", "format", db.Format()); dump_file.write(line.data(), line.size()); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; if (ret) { @@ -83,7 +83,7 @@ bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error) std::string value_str = HexStr(ss_value); line = strprintf("%s,%s\n", key_str, value_str); dump_file.write(line.data(), line.size()); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; } } @@ -160,7 +160,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs:: return false; } std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value); - hasher.write(MakeByteSpan(magic_hasher_line)); + hasher << Span{magic_hasher_line}; // Get the stored file format std::string format_key; @@ -191,7 +191,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs:: warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format)); } std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value); - hasher.write(MakeByteSpan(format_hasher_line)); + hasher << Span{format_hasher_line}; DatabaseOptions options; DatabaseStatus status; @@ -236,7 +236,7 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs:: } std::string line = strprintf("%s,%s\n", key, value); - hasher.write(MakeByteSpan(line)); + hasher << Span{line}; if (key.empty() || value.empty()) { continue; diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index e0b4a1e74c..d341e84d9b 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -196,7 +196,7 @@ BOOST_AUTO_TEST_CASE(db_cursor_prefix_byte_test) for (const auto& database : TestDatabases(m_path_root)) { std::unique_ptr<DatabaseBatch> batch = database->MakeBatch(); for (const auto& [k, v] : {e, p, ps, f, fs, ff, ffs}) { - batch->Write(MakeUCharSpan(k), MakeUCharSpan(v)); + batch->Write(Span{k}, Span{v}); } CheckPrefix(*batch, StringBytes(""), {e, p, ps, f, fs, ff, ffs}); CheckPrefix(*batch, StringBytes("prefix"), {p, ps}); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a1b26b139e..5d99fb92a2 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3878,7 +3878,7 @@ bool CWallet::MigrateToSQLite(bilingual_str& error) bool began = batch->TxnBegin(); assert(began); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution. for (const auto& [key, value] : records) { - if (!batch->Write(MakeUCharSpan(key), MakeUCharSpan(value))) { + if (!batch->Write(Span{key}, Span{value})) { batch->TxnAbort(); m_database->Close(); fs::remove(m_database->Filename()); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 2aee750ced..ff0b83dc38 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -1392,7 +1392,7 @@ bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types) } // Make a copy of key to avoid data being deleted by the following read of the type - Span<const unsigned char> key_data = MakeUCharSpan(key); + Span key_data{key}; std::string type; key >> type; |