SpanReader can never write data. Also, the code should be minimally faster, because it avoids a full redundant copy of the whole vector of bytes.
refactor: Use SpanReader over DataStream #34483
pull maflcko wants to merge 6 commits into bitcoin:master from maflcko:2602-span-reader changing 30 files +49 −71-
maflcko commented at 3:54 pm on February 2, 2026: memberThis changes all places, where possible, to use SpanReader over DataStream. This makes the code easier to read and reason about, because
-
fa879db735
test: Read debug log for self-checking comment
The DataStream comment was a bit stale, because it was using CDataStream. Fix it by using assert_debug_log for a self-documenting and self-checking test code.
-
DrahtBot renamed this:
refactor: Use SpanReader over DataStream
refactor: Use SpanReader over DataStream
on Feb 2, 2026 -
DrahtBot added the label Refactoring on Feb 2, 2026
-
DrahtBot commented at 3:55 pm on February 2, 2026: contributor
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviews
See the guideline for information on the review process.
Type Reviewers ACK janb84, sipa, stickies-v, achow101 Concept ACK davidgumberg Stale ACK darosior If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.
Conflicts
Reviewers, this pull request conflicts with the following ones:
- #34132 (coins: fail fast on database read deserialization errors by l0rinc)
- #32554 (bench: replace embedded raw block with configurable block generator by l0rinc)
- #31682 ([IBD] specialize CheckBlock’s input & coinbase checks by l0rinc)
- #31449 (coins,refactor: Reduce
getblockstatsRPC UTXO overhead estimation by l0rinc)
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
-
in src/streams.h:103 in fa498e4512 outdated
99@@ -100,6 +100,7 @@ class SpanReader 100 101 size_t size() const { return m_data.size(); } 102 bool empty() const { return m_data.empty(); } 103+ bool eof() const { return m_data.empty(); }
darosior commented at 4:06 pm on February 2, 2026:Is it not less confusing to change the one call site (that you are already touching) to useempty()rather than introducing aneofmethod to a class that is not reading from a file?
maflcko commented at 4:14 pm on February 2, 2026:Then, I’d have to addempty()to the “streams interface”, at which point theeof()alias can be removed. Let me push that …
maflcko commented at 6:36 pm on February 2, 2026:(done)darosior commented at 4:07 pm on February 2, 2026: memberCode looks good to me, just got one question.stickies-v commented at 4:38 pm on February 2, 2026: contributorConcept ACKfa20bc2ec2refactor: Use empty() over eof() in the streams interface
End-of-file does not really make sense for streams that wrap buffers. So replace it by the equivalent empty() checks.
DrahtBot added the label CI failed on Feb 2, 2026DrahtBot commented at 5:09 pm on February 2, 2026: contributor🚧 At least one of the CI tasks failed. Task
fuzzer,address,undefined,integer: https://github.com/bitcoin/bitcoin/actions/runs/21597138806/job/62232912845 LLM reason (✨ experimental): AddressSanitizer heap-buffer-overflow in fuzz target during seed corpus processing.Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:
-
Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.
-
A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.
-
An intermittent issue.
Leave a comment here, if you need help tracking down a confusing failure.
fabd4d2e2erefactor: Avoid UB in SpanReader::ignore
Currently std::span::subspan is called without checking the size first. This is UB, unless the std lib is hardened. With a hardened stdlib, the program aborts: > include/c++/v1/span:512: libc++ Hardening assertion __offset <= size() > failed: span<T>::subspan(offset, count): offset out of range Fix the UB and the abort by using the implementation from DataStream, which throws when hitting end-of-data. This commit should not change any behavior, because the UB is currently unreachable. Also, the newly added throw should properly be caught by any code that calls any streams function.
refactor: [qt] Use SpanReader to avoid two vector copies fa06e26764fad3eb3956refactor: Use SpanReader over DataStream
The mutable temporary strValue can be re-used to apply the obfuscation, which allows to avoid a redundant copy of the value.
maflcko force-pushed on Feb 2, 2026maflcko force-pushed on Feb 2, 2026maflcko commented at 5:51 pm on February 2, 2026: memberHmm, I guess there is some unrelated pre-existing UB. This isn’t remote-triggerable, so I fixed it in a “refactor” commit.
introduced in commit 82a379eca82, which I reviewed :(
godbolt: https://godbolt.org/z/YKsavqGqx
janb84 commented at 7:54 pm on February 2, 2026: contributorConcept ACK fa52b210e03879386e5582114cfefa51ec61396d
Did an extensive search, most are converted. Listed below are some leftovers, maybe intentional not converted?
Possible NIT;
/src/rpc/txoutproof.cpp: L147-149from:
0DataStream ssMB{ParseHexV(request.params[0], "proof")}; 1CMerkleBlock merkleBlock; 2ssMB >> merkleBlock;0CMerkleBlock merkleBlock; 1SpanReader{ParseHexV(request.params[0], "proof")} >> merkleBlock;or
src/wallet/rpc/backup.cpp:L60-62from:
0 DataStream ssMB{ParseHexV(request.params[1], "proof")}; 1 CMerkleBlock merkleBlock; 2 ssMB >> merkleBlock;to:
0 CMerkleBlock merkleBlock; 1 SpanReader{ParseHexV(request.params[1], "proof")} >> merkleBlock;DrahtBot removed the label CI failed on Feb 2, 2026maflcko force-pushed on Feb 2, 2026sipa commented at 8:19 pm on February 2, 2026: memberConcept ACKDrahtBot added the label CI failed on Feb 2, 2026maflcko closed this on Feb 3, 2026
maflcko reopened this on Feb 3, 2026
DrahtBot removed the label CI failed on Feb 3, 2026davidgumberg commented at 8:19 am on February 3, 2026: contributorConcept ACK. I think this should bring some measurable performance improvements becauseSpanReaderdoes not use the zero after free allocator thatDataStreamdoes, which zero’s memory and prevents compiler optimizations in order to guarantee that memory is zeroed. I came to the conclusion in #30987 at the time that there was nowhereDataStreamwas used at that time where one should be worried about data being left in memory after destruction.maflcko commented at 8:32 am on February 3, 2026: memberJust to clarify: This should have no impact on IBD time, because only the dbwrapper is changed, but I don’t expect that to give any end-to-end performance improvement.
When i tested this on btck_block_create last month, it gave a 1%-5% speed-up. I guess for all fuzz targets, the speedup is also shadowed by the remainder of the fuzz target. That is I don’t expect the
blockfuzz target to be end-to-end faster at all.This is mostly a style cleanup and the UB fix doesn’t seem too bad either.
l0rinc commented at 11:58 am on February 3, 2026: contributorthis should bring some measurable performance improvements because SpanReader does not use the zero after free allocator that DataStream does
I have measured something similar in https://github.com/l0rinc/bitcoin/pull/31 but couldn’t find any difference in higher level benchmarks. The difference is only really measurable in debug mode and in the narrow micro-bench of not zeroing DataStream bytes on destruction, but it’s probably optimized away on higher level.
darosior approveddarosior commented at 10:17 pm on February 3, 2026: memberutACK fae650c2950f3b6d99eb7c062389de0bf6a64ac9DrahtBot requested review from stickies-v on Feb 3, 2026DrahtBot requested review from davidgumberg on Feb 3, 2026DrahtBot requested review from janb84 on Feb 3, 2026DrahtBot requested review from sipa on Feb 3, 2026in src/rpc/blockchain.cpp:835 in fae650c295
831@@ -832,7 +832,7 @@ static RPCHelpMan getblock() 832 return HexStr(block_data); 833 } 834 835- DataStream block_stream{block_data}; 836+ SpanReader block_stream{block_data};
stickies-v commented at 1:37 pm on February 5, 2026:nit: a bunch more of these can be inlined, avoiding the variables named “stream” etc:
0diff --git a/src/bench/readwriteblock.cpp b/src/bench/readwriteblock.cpp 1index 690818f659..b8e226c6eb 100644 2--- a/src/bench/readwriteblock.cpp 3+++ b/src/bench/readwriteblock.cpp 4@@ -21,9 +21,8 @@ 5 6 static CBlock CreateTestBlock() 7 { 8- SpanReader stream{benchmark::data::block413567}; 9 CBlock block; 10- stream >> TX_WITH_WITNESS(block); 11+ SpanReader{benchmark::data::block413567} >> TX_WITH_WITNESS(block); 12 return block; 13 } 14 15diff --git a/src/core_io.cpp b/src/core_io.cpp 16index 094f1ec8cc..ec446debee 100644 17--- a/src/core_io.cpp 18+++ b/src/core_io.cpp 19@@ -239,9 +239,8 @@ bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header) 20 if (!IsHex(hex_header)) return false; 21 22 const std::vector<unsigned char> header_data{ParseHex(hex_header)}; 23- SpanReader ser_header{header_data}; 24 try { 25- ser_header >> header; 26+ SpanReader{header_data} >> header; 27 } catch (const std::exception&) { 28 return false; 29 } 30@@ -254,9 +253,8 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) 31 return false; 32 33 std::vector<unsigned char> blockData(ParseHex(strHexBlk)); 34- SpanReader ssBlock{blockData}; 35 try { 36- ssBlock >> TX_WITH_WITNESS(block); 37+ SpanReader{blockData} >> TX_WITH_WITNESS(block); 38 } 39 catch (const std::exception&) { 40 return false; 41diff --git a/src/rest.cpp b/src/rest.cpp 42index 4ecd0f451e..9d73c1e604 100644 43--- a/src/rest.cpp 44+++ b/src/rest.cpp 45@@ -451,8 +451,7 @@ static bool rest_block(const std::any& context, 46 case RESTResponseFormat::JSON: { 47 if (tx_verbosity) { 48 CBlock block{}; 49- SpanReader block_stream{*block_data}; 50- block_stream >> TX_WITH_WITNESS(block); 51+ SpanReader{*block_data} >> TX_WITH_WITNESS(block); 52 UniValue objBlock = blockToJSON(chainman.m_blockman, block, *tip, *pblockindex, *tx_verbosity, chainman.GetConsensus().powLimit); 53 std::string strJSON = objBlock.write() + "\n"; 54 req->WriteHeader("Content-Type", "application/json"); 55diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp 56index 6d4ced7364..d5c9987697 100644 57--- a/src/rpc/blockchain.cpp 58+++ b/src/rpc/blockchain.cpp 59@@ -832,9 +832,8 @@ static RPCHelpMan getblock() 60 return HexStr(block_data); 61 } 62 63- SpanReader block_stream{block_data}; 64 CBlock block{}; 65- block_stream >> TX_WITH_WITNESS(block); 66+ SpanReader{block_data} >> TX_WITH_WITNESS(block); 67 68 TxVerbosity tx_verbosity; 69 if (verbosity == 1) { 70diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp 71index 6e45cc251f..7b87442985 100644 72--- a/src/test/fuzz/block.cpp 73+++ b/src/test/fuzz/block.cpp 74@@ -24,10 +24,9 @@ void initialize_block() 75 76 FUZZ_TARGET(block, .init = initialize_block) 77 { 78- SpanReader ds{buffer}; 79 CBlock block; 80 try { 81- ds >> TX_WITH_WITNESS(block); 82+ SpanReader{buffer} >> TX_WITH_WITNESS(block); 83 } catch (const std::ios_base::failure&) { 84 return; 85 } 86diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp 87index 820538a54b..70cad07d69 100644 88--- a/src/test/fuzz/deserialize.cpp 89+++ b/src/test/fuzz/deserialize.cpp 90@@ -81,9 +81,8 @@ T Deserialize(DataStream&& ds, const P& params) 91 template <typename T, typename P> 92 void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj, const P& params) 93 { 94- SpanReader ds{buffer}; 95 try { 96- ds >> params(obj); 97+ SpanReader{buffer} >> params(obj); 98 } catch (const std::ios_base::failure&) { 99 throw invalid_fuzzing_input_exception(); 100 } 101@@ -109,9 +108,8 @@ T Deserialize(DataStream ds) 102 template <typename T> 103 void DeserializeFromFuzzingInput(FuzzBufferType buffer, T&& obj) 104 { 105- SpanReader ds{buffer}; 106 try { 107- ds >> obj; 108+ SpanReader{buffer} >> obj; 109 } catch (const std::ios_base::failure&) { 110 throw invalid_fuzzing_input_exception(); 111 } 112diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp 113index 224cd9d9b7..8f9c942666 100644 114--- a/src/test/fuzz/transaction.cpp 115+++ b/src/test/fuzz/transaction.cpp 116@@ -41,10 +41,9 @@ FUZZ_TARGET(transaction, .init = initialize_transaction) 117 } 118 }(); 119 bool valid_mutable_tx = true; 120- SpanReader ds_mtx{buffer}; 121 CMutableTransaction mutable_tx; 122 try { 123- ds_mtx >> TX_WITH_WITNESS(mutable_tx); 124+ SpanReader{buffer} >> TX_WITH_WITNESS(mutable_tx); 125 } catch (const std::ios_base::failure&) { 126 valid_mutable_tx = false; 127 } 128diff --git a/src/test/fuzz/tx_in.cpp b/src/test/fuzz/tx_in.cpp 129index 4146d96162..10618d60e0 100644 130--- a/src/test/fuzz/tx_in.cpp 131+++ b/src/test/fuzz/tx_in.cpp 132@@ -13,10 +13,9 @@ 133 134 FUZZ_TARGET(tx_in) 135 { 136- SpanReader ds{buffer}; 137 CTxIn tx_in; 138 try { 139- ds >> tx_in; 140+ SpanReader{buffer} >> tx_in; 141 } catch (const std::ios_base::failure&) { 142 return; 143 } 144diff --git a/src/test/fuzz/tx_out.cpp b/src/test/fuzz/tx_out.cpp 145index 1c1016eb92..ce39d14f1a 100644 146--- a/src/test/fuzz/tx_out.cpp 147+++ b/src/test/fuzz/tx_out.cpp 148@@ -12,10 +12,9 @@ 149 150 FUZZ_TARGET(tx_out) 151 { 152- SpanReader ds{buffer}; 153 CTxOut tx_out; 154 try { 155- ds >> tx_out; 156+ SpanReader{buffer} >> tx_out; 157 } catch (const std::ios_base::failure&) { 158 return; 159 } 160diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp 161index e55e1e6f03..136728a043 100644 162--- a/src/test/net_tests.cpp 163+++ b/src/test/net_tests.cpp 164@@ -872,10 +872,9 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message) 165 std::span<const unsigned char> data, 166 bool is_incoming) -> void { 167 if (!is_incoming && msg_type == "addr") { 168- SpanReader s{data}; 169 std::vector<CAddress> addresses; 170 171- s >> CAddress::V1_NETWORK(addresses); 172+ SpanReader{data} >> CAddress::V1_NETWORK(addresses); 173 174 for (const auto& addr : addresses) { 175 if (addr == expected) {
maflcko commented at 6:23 pm on February 5, 2026:sure, and thx, pushed your diff as-isstickies-v approvedstickies-v commented at 2:53 pm on February 5, 2026: contributorACK fae650c2950f3b6d99eb7c062389de0bf6a64ac9
Found a few more instances that could be replaced:
0diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp 1index 938aa233ed..8e02cfd08c 100644 2--- a/src/test/bloom_tests.cpp 3+++ b/src/test/bloom_tests.cpp 4@@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(bloom_match) 5 // and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436) 6 unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00}; 7 std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1); 8- DataStream spendStream{vch}; 9+ SpanReader spendStream{vch}; 10 CTransaction spendingTx(deserialize, TX_WITH_WITNESS, spendStream); 11 12 CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); 13diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp 14index 4d6ee6613e..8c0756d852 100644 15--- a/src/test/coins_tests.cpp 16+++ b/src/test/coins_tests.cpp 17@@ -517,37 +517,33 @@ BOOST_FIXTURE_TEST_CASE(updatecoins_simulation_test, UpdateTest) 18 BOOST_AUTO_TEST_CASE(ccoins_serialization) 19 { 20 // Good example 21- DataStream ss1{"97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"_hex}; 22 Coin cc1; 23- ss1 >> cc1; 24+ SpanReader{"97f23c835800816115944e077fe7c803cfa57f29b36bf87c1d35"_hex} >> cc1; 25 BOOST_CHECK_EQUAL(cc1.fCoinBase, false); 26 BOOST_CHECK_EQUAL(cc1.nHeight, 203998U); 27 BOOST_CHECK_EQUAL(cc1.out.nValue, CAmount{60000000000}); 28 BOOST_CHECK_EQUAL(HexStr(cc1.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160("816115944e077fe7c803cfa57f29b36bf87c1d35"_hex_u8))))); 29 30 // Good example 31- DataStream ss2{"8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex}; 32 Coin cc2; 33- ss2 >> cc2; 34+ SpanReader{"8ddf77bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex} >> cc2; 35 BOOST_CHECK_EQUAL(cc2.fCoinBase, true); 36 BOOST_CHECK_EQUAL(cc2.nHeight, 120891U); 37 BOOST_CHECK_EQUAL(cc2.out.nValue, 110397); 38 BOOST_CHECK_EQUAL(HexStr(cc2.out.scriptPubKey), HexStr(GetScriptForDestination(PKHash(uint160("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"_hex_u8))))); 39 40 // Smallest possible example 41- DataStream ss3{"000006"_hex}; 42 Coin cc3; 43- ss3 >> cc3; 44+ SpanReader{"000006"_hex} >> cc3; 45 BOOST_CHECK_EQUAL(cc3.fCoinBase, false); 46 BOOST_CHECK_EQUAL(cc3.nHeight, 0U); 47 BOOST_CHECK_EQUAL(cc3.out.nValue, 0); 48 BOOST_CHECK_EQUAL(cc3.out.scriptPubKey.size(), 0U); 49 50 // scriptPubKey that ends beyond the end of the stream 51- DataStream ss4{"000007"_hex}; 52 try { 53 Coin cc4; 54- ss4 >> cc4; 55+ SpanReader{"000007"_hex} >> cc4; 56 BOOST_CHECK_MESSAGE(false, "We should have thrown"); 57 } catch (const std::ios_base::failure&) { 58 } 59@@ -557,10 +553,9 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) 60 uint64_t x = 3000000000ULL; 61 tmp << VARINT(x); 62 BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00"); 63- DataStream ss5{"00008a95c0bb00"_hex}; 64 try { 65 Coin cc5; 66- ss5 >> cc5; 67+ SpanReader{"00008a95c0bb00"_hex} >> cc5; 68 BOOST_CHECK_MESSAGE(false, "We should have thrown"); 69 } catch (const std::ios_base::failure&) { 70 } 71diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp 72index 88260b272f..c9ab49078a 100644 73--- a/src/test/fuzz/addrman.cpp 74+++ b/src/test/fuzz/addrman.cpp 75@@ -9,6 +9,7 @@ 76 #include <common/args.h> 77 #include <merkleblock.h> 78 #include <random.h> 79+#include <streams.h> 80 #include <test/fuzz/FuzzedDataProvider.h> 81 #include <test/fuzz/fuzz.h> 82 #include <test/fuzz/util.h> 83@@ -121,9 +122,8 @@ FUZZ_TARGET(addrman, .init = initialize_addrman) 84 auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio()); 85 if (fuzzed_data_provider.ConsumeBool()) { 86 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 87- DataStream ds{serialized_data}; 88 try { 89- ds >> *addr_man_ptr; 90+ SpanReader{serialized_data} >> *addr_man_ptr; 91 } catch (const std::ios_base::failure&) { 92 addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio()); 93 } 94diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp 95index 4a8b7b2155..933baa4810 100644 96--- a/src/test/fuzz/connman.cpp 97+++ b/src/test/fuzz/connman.cpp 98@@ -9,6 +9,7 @@ 99 #include <net_processing.h> 100 #include <netaddress.h> 101 #include <protocol.h> 102+#include <streams.h> 103 #include <test/fuzz/FuzzedDataProvider.h> 104 #include <test/fuzz/fuzz.h> 105 #include <test/fuzz/util.h> 106@@ -45,9 +46,8 @@ FUZZ_TARGET(connman, .init = initialize_connman) 107 auto addr_man_ptr{std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio())}; 108 if (fuzzed_data_provider.ConsumeBool()) { 109 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 110- DataStream ds{serialized_data}; 111 try { 112- ds >> *addr_man_ptr; 113+ SpanReader{serialized_data} >> *addr_man_ptr; 114 } catch (const std::ios_base::failure&) { 115 addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio()); 116 } 117diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp 118index 722ba2eb69..f765c57f04 100644 119--- a/src/test/netbase_tests.cpp 120+++ b/src/test/netbase_tests.cpp 121@@ -575,10 +575,9 @@ BOOST_AUTO_TEST_CASE(caddress_serialize_v1) 122 123 BOOST_AUTO_TEST_CASE(caddress_unserialize_v1) 124 { 125- DataStream s{ParseHex(stream_addrv1_hex)}; 126 std::vector<CAddress> addresses_unserialized; 127 128- s >> CAddress::V1_NETWORK(addresses_unserialized); 129+ SpanReader{ParseHex(stream_addrv1_hex)} >> CAddress::V1_NETWORK(addresses_unserialized); 130 BOOST_CHECK(fixture_addresses == addresses_unserialized); 131 } 132 133@@ -592,10 +591,9 @@ BOOST_AUTO_TEST_CASE(caddress_serialize_v2) 134 135 BOOST_AUTO_TEST_CASE(caddress_unserialize_v2) 136 { 137- DataStream s{ParseHex(stream_addrv2_hex)}; 138 std::vector<CAddress> addresses_unserialized; 139 140- s >> CAddress::V2_NETWORK(addresses_unserialized); 141+ SpanReader{ParseHex(stream_addrv2_hex)} >> CAddress::V2_NETWORK(addresses_unserialized); 142 BOOST_CHECK(fixture_addresses == addresses_unserialized); 143 } 144 145diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp 146index 4272821640..5d65c35a8c 100644 147--- a/src/test/sighash_tests.cpp 148+++ b/src/test/sighash_tests.cpp 149@@ -189,8 +189,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) 150 nHashType = test[3].getInt<int>(); 151 sigHashHex = test[4].get_str(); 152 153- DataStream stream(ParseHex(raw_tx)); 154- stream >> TX_WITH_WITNESS(tx); 155+ SpanReader{ParseHex(raw_tx)} >> TX_WITH_WITNESS(tx); 156 157 TxValidationState state; 158 BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest); 159diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp 160index 3919f226d6..d0be15a00d 100644 161--- a/src/test/transaction_tests.cpp 162+++ b/src/test/transaction_tests.cpp 163@@ -377,9 +377,8 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) 164 // Random real transaction (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436) 165 unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00}; 166 std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1); 167- DataStream stream(vch); 168 CMutableTransaction tx; 169- stream >> TX_WITH_WITNESS(tx); 170+ SpanReader{vch} >> TX_WITH_WITNESS(tx); 171 TxValidationState state; 172 BOOST_CHECK_MESSAGE(CheckTransaction(CTransaction(tx), state) && state.IsValid(), "Simple deserialized transaction should be valid."); 173maflcko force-pushed on Feb 5, 2026stickies-v commented at 8:38 pm on February 5, 2026: contributorre-ACK faaf01fbd87644d597cb691b535ba5c24544efa5DrahtBot requested review from darosior on Feb 5, 2026fa0677d131refactor: Use SpanReader over DataStream
This refactor does not change behavior. However, it avoids a vector copy, which can lead to a minimal speed-up of 1%-5%, depending on the call-site. This is mostly relevant for the fuzz tests and utils that read large blobs of data (like a full block).
maflcko force-pushed on Feb 6, 2026maflcko commented at 7:30 am on February 6, 2026: membersry, didn’t realize there were two diffs. I pushed the other one as well. However addrman has DataStream in it’s interface, so this replacement is more involved and this diff does not compile as-is:
0diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp 1index 88260b272f..0d77ddfb3a 100644 2--- a/src/test/fuzz/addrman.cpp 3+++ b/src/test/fuzz/addrman.cpp 4@@ -11,2 +11,3 @@ 5 #include <random.h> 6+#include <streams.h> 7 #include <test/fuzz/FuzzedDataProvider.h> 8@@ -123,5 +124,4 @@ FUZZ_TARGET(addrman, .init = initialize_addrman) 9 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 10- DataStream ds{serialized_data}; 11 try { 12- ds >> *addr_man_ptr; 13+ DataStream{serialized_data} >> *addr_man_ptr; 14 } catch (const std::ios_base::failure&) { 15diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp 16index 4a8b7b2155..90a798501f 100644 17--- a/src/test/fuzz/connman.cpp 18+++ b/src/test/fuzz/connman.cpp 19@@ -11,2 +11,3 @@ 20 #include <protocol.h> 21+#include <streams.h> 22 #include <test/fuzz/FuzzedDataProvider.h> 23@@ -47,5 +48,4 @@ FUZZ_TARGET(connman, .init = initialize_connman) 24 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 25- DataStream ds{serialized_data}; 26 try { 27- ds >> *addr_man_ptr; 28+ DataStream{serialized_data} >> *addr_man_ptr; 29 } catch (const std::ios_base::failure&) {janb84 commented at 4:01 pm on February 6, 2026: contributorre ACK fa0677d131191d7db9868c4c1b3d780cb6991226DrahtBot requested review from stickies-v on Feb 6, 2026sipa commented at 4:42 pm on February 6, 2026: membercrACK fa0677d131191d7db9868c4c1b3d780cb6991226stickies-v commented at 11:50 pm on February 6, 2026: contributorre-ACK fa0677d131191d7db9868c4c1b3d780cb6991226
However addrman has DataStream in it’s interface, so this replacement is more involved and this diff does not compile as-is:
Apologies, wasn’t compiling the fuzz binary so I missed this, you’re right.
achow101 commented at 1:48 am on February 7, 2026: memberACK fa0677d131191d7db9868c4c1b3d780cb6991226achow101 merged this on Feb 7, 2026achow101 closed this on Feb 7, 2026
maflcko deleted the branch on Feb 9, 2026
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-02-11 21:13 UTC
More mirrored repositories can be found on mirror.b10c.me