mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 03:47:29 -03:00
Merge bitcoin/bitcoin#27927: util: Allow std::byte and char Span serialization
fa38d86235
Use only Span{} constructor for byte-like types where possible (MarcoFalke)fa257bc831
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: utACKfa38d86235
achow101: ACKfa38d86235
ryanofsky: Code review ACKfa38d86235
. This looks great. The second commit really removes a lot of boilerplate and shows why the first commit is useful. Tree-SHA512: 788592d9ff515c3ebe73d48f9ecbb8d239f5b985af86f09974e508cafb0ca6d73a959350295246b4dfb496149bc56330a0b5d659fc434ba6723dbaba0b7a49e5
This commit is contained in:
commit
7952a5934a
11 changed files with 36 additions and 26 deletions
|
@ -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.
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue