Skip to content

Commit 3a34533

Browse files
committed
Merge rust-bitcoin/rust-bitcoin#1035: Take Writer/Reader by &mut in consensus en/decoding
1fea098 Support unsized `R` and `W` in consensus encode/decode (Dawid Ciężarkiewicz) a24a3b0 Forward `consensus_decode` to `consensus_decode_from_finite_reader` (Dawid Ciężarkiewicz) 9c754ca Take Writer/Reader by `&mut` in consensus en/decoding (Dawid Ciężarkiewicz) Pull request description: Fix #1020 (see more relevant discussion there) This definitely makes the amount of generics compiler has to generate by avoding generating the same functions for `R`, `&mut R`, `&mut &mut R` and so on. old: ``` > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266 > strip target/release/deps/bitcoin-07a9dabf1f3e0266 > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266 ``` new: ``` > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266 > strip target/release/deps/bitcoin-07a9dabf1f3e0266 > ls -al target/release/deps/bitcoin-07a9dabf1f3e0266 -rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266 ``` In the unit-test binary itself, it saves ~100KB of data. I did not expect much performance gains, but turn out I was wrong(*): old: ``` test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871) test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833) test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732) test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519) test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3) test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8) test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0) ``` new: ``` test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910) test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363) test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695) test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694) test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2) test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0) test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0) ``` (*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think at least it doesn't make anything slower. While doing all this manual labor that will probably generate conflicts, I took a liberty of changing generic type names and variable names to `r` and `R` (reader) and `w` and `W` for writer. ACKs for top commit: RCasatta: ACK 1fea098 tested in downstream lib, space saving in compiled code confirmed apoelstra: ACK 1fea098 Tree-SHA512: bc11994791dc97cc468dc9d411b9abf52ad475f23adf5c43d563f323bae0da180c8f57f2f17c1bb7b9bdcf523584b0943763742b81362880206779872ad7489f
2 parents 54479e6 + 2c21b0e commit 3a34533

20 files changed

+336
-347
lines changed

src/blockdata/script.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
//! This module provides the structures and functions needed to support scripts.
2424
//!
2525
26-
use crate::consensus::encode::MAX_VEC_SIZE;
2726
use crate::prelude::*;
2827

2928
use crate::io;
@@ -1093,20 +1092,15 @@ impl serde::Serialize for Script {
10931092

10941093
impl Encodable for Script {
10951094
#[inline]
1096-
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, io::Error> {
1097-
self.0.consensus_encode(s)
1095+
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
1096+
self.0.consensus_encode(w)
10981097
}
10991098
}
11001099

11011100
impl Decodable for Script {
11021101
#[inline]
1103-
fn consensus_decode_from_finite_reader<D: io::Read>(d: D) -> Result<Self, encode::Error> {
1104-
Ok(Script(Decodable::consensus_decode_from_finite_reader(d)?))
1105-
}
1106-
1107-
#[inline]
1108-
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
1109-
Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64))
1102+
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
1103+
Ok(Script(Decodable::consensus_decode_from_finite_reader(r)?))
11101104
}
11111105
}
11121106

src/blockdata/transaction.rs

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
3737
use crate::blockdata::script::Script;
3838
use crate::blockdata::witness::Witness;
3939
use crate::consensus::{encode, Decodable, Encodable};
40-
use crate::consensus::encode::MAX_VEC_SIZE;
4140
use crate::hash_types::{Sighash, Txid, Wtxid};
4241
use crate::VarInt;
4342
use crate::util::sighash::UINT256_ONE;
@@ -731,50 +730,45 @@ impl Transaction {
731730
impl_consensus_encoding!(TxOut, value, script_pubkey);
732731

733732
impl Encodable for OutPoint {
734-
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, io::Error> {
735-
let len = self.txid.consensus_encode(&mut s)?;
736-
Ok(len + self.vout.consensus_encode(s)?)
733+
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
734+
let len = self.txid.consensus_encode(w)?;
735+
Ok(len + self.vout.consensus_encode(w)?)
737736
}
738737
}
739738
impl Decodable for OutPoint {
740-
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
739+
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
741740
Ok(OutPoint {
742-
txid: Decodable::consensus_decode(&mut d)?,
743-
vout: Decodable::consensus_decode(d)?,
741+
txid: Decodable::consensus_decode(r)?,
742+
vout: Decodable::consensus_decode(r)?,
744743
})
745744
}
746745
}
747746

748747
impl Encodable for TxIn {
749-
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, io::Error> {
748+
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
750749
let mut len = 0;
751-
len += self.previous_output.consensus_encode(&mut s)?;
752-
len += self.script_sig.consensus_encode(&mut s)?;
753-
len += self.sequence.consensus_encode(s)?;
750+
len += self.previous_output.consensus_encode(w)?;
751+
len += self.script_sig.consensus_encode(w)?;
752+
len += self.sequence.consensus_encode(w)?;
754753
Ok(len)
755754
}
756755
}
757756
impl Decodable for TxIn {
758757
#[inline]
759-
fn consensus_decode_from_finite_reader<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
758+
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
760759
Ok(TxIn {
761-
previous_output: Decodable::consensus_decode_from_finite_reader(&mut d)?,
762-
script_sig: Decodable::consensus_decode_from_finite_reader(&mut d)?,
763-
sequence: Decodable::consensus_decode_from_finite_reader(d)?,
760+
previous_output: Decodable::consensus_decode_from_finite_reader(r)?,
761+
script_sig: Decodable::consensus_decode_from_finite_reader(r)?,
762+
sequence: Decodable::consensus_decode_from_finite_reader(r)?,
764763
witness: Witness::default(),
765764
})
766765
}
767-
768-
#[inline]
769-
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
770-
Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64))
771-
}
772766
}
773767

774768
impl Encodable for Transaction {
775-
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, io::Error> {
769+
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
776770
let mut len = 0;
777-
len += self.version.consensus_encode(&mut s)?;
771+
len += self.version.consensus_encode(w)?;
778772
// To avoid serialization ambiguity, no inputs means we use BIP141 serialization (see
779773
// `Transaction` docs for full explanation).
780774
let mut have_witness = self.input.is_empty();
@@ -785,36 +779,36 @@ impl Encodable for Transaction {
785779
}
786780
}
787781
if !have_witness {
788-
len += self.input.consensus_encode(&mut s)?;
789-
len += self.output.consensus_encode(&mut s)?;
782+
len += self.input.consensus_encode(w)?;
783+
len += self.output.consensus_encode(w)?;
790784
} else {
791-
len += 0u8.consensus_encode(&mut s)?;
792-
len += 1u8.consensus_encode(&mut s)?;
793-
len += self.input.consensus_encode(&mut s)?;
794-
len += self.output.consensus_encode(&mut s)?;
785+
len += 0u8.consensus_encode(w)?;
786+
len += 1u8.consensus_encode(w)?;
787+
len += self.input.consensus_encode(w)?;
788+
len += self.output.consensus_encode(w)?;
795789
for input in &self.input {
796-
len += input.witness.consensus_encode(&mut s)?;
790+
len += input.witness.consensus_encode(w)?;
797791
}
798792
}
799-
len += self.lock_time.consensus_encode(s)?;
793+
len += self.lock_time.consensus_encode(w)?;
800794
Ok(len)
801795
}
802796
}
803797

804798
impl Decodable for Transaction {
805-
fn consensus_decode_from_finite_reader<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
806-
let version = i32::consensus_decode_from_finite_reader(&mut d)?;
807-
let input = Vec::<TxIn>::consensus_decode_from_finite_reader(&mut d)?;
799+
fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
800+
let version = i32::consensus_decode_from_finite_reader(r)?;
801+
let input = Vec::<TxIn>::consensus_decode_from_finite_reader(r)?;
808802
// segwit
809803
if input.is_empty() {
810-
let segwit_flag = u8::consensus_decode_from_finite_reader(&mut d)?;
804+
let segwit_flag = u8::consensus_decode_from_finite_reader(r)?;
811805
match segwit_flag {
812806
// BIP144 input witnesses
813807
1 => {
814-
let mut input = Vec::<TxIn>::consensus_decode_from_finite_reader(&mut d)?;
815-
let output = Vec::<TxOut>::consensus_decode_from_finite_reader(&mut d)?;
808+
let mut input = Vec::<TxIn>::consensus_decode_from_finite_reader(r)?;
809+
let output = Vec::<TxOut>::consensus_decode_from_finite_reader(r)?;
816810
for txin in input.iter_mut() {
817-
txin.witness = Decodable::consensus_decode_from_finite_reader(&mut d)?;
811+
txin.witness = Decodable::consensus_decode_from_finite_reader(r)?;
818812
}
819813
if !input.is_empty() && input.iter().all(|input| input.witness.is_empty()) {
820814
Err(encode::Error::ParseFailed("witness flag set but no witnesses present"))
@@ -823,7 +817,7 @@ impl Decodable for Transaction {
823817
version,
824818
input,
825819
output,
826-
lock_time: Decodable::consensus_decode_from_finite_reader(d)?,
820+
lock_time: Decodable::consensus_decode_from_finite_reader(r)?,
827821
})
828822
}
829823
}
@@ -835,15 +829,11 @@ impl Decodable for Transaction {
835829
Ok(Transaction {
836830
version,
837831
input,
838-
output: Decodable::consensus_decode_from_finite_reader(&mut d)?,
839-
lock_time: Decodable::consensus_decode_from_finite_reader(d)?,
832+
output: Decodable::consensus_decode_from_finite_reader(r)?,
833+
lock_time: Decodable::consensus_decode_from_finite_reader(r)?,
840834
})
841835
}
842836
}
843-
844-
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
845-
Self::consensus_decode_from_finite_reader(d.take(MAX_VEC_SIZE as u64))
846-
}
847837
}
848838

849839
/// This type is consensus valid but an input including it would prevent the transaction from
@@ -1041,6 +1031,19 @@ mod tests {
10411031
use super::EcdsaSighashType;
10421032
use crate::util::sighash::SighashCache;
10431033

1034+
const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
1035+
1036+
#[test]
1037+
fn encode_to_unsized_writer() {
1038+
let mut buf = [0u8; 1024];
1039+
let raw_tx = Vec::from_hex(SOME_TX).unwrap();
1040+
let tx: Transaction = Decodable::consensus_decode(&mut raw_tx.as_slice()).unwrap();
1041+
1042+
let size = tx.consensus_encode(&mut &mut buf[..]).unwrap();
1043+
assert_eq!(size, SOME_TX.len() / 2);
1044+
assert_eq!(raw_tx, &buf[..size]);
1045+
}
1046+
10441047
#[test]
10451048
fn test_outpoint() {
10461049
assert_eq!(OutPoint::from_str("i don't care"),

src/blockdata/witness.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ pub struct Iter<'a> {
4646
}
4747

4848
impl Decodable for Witness {
49-
fn consensus_decode<D: Read>(mut d: D) -> Result<Self, Error> {
50-
let witness_elements = VarInt::consensus_decode(&mut d)?.0 as usize;
49+
fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
50+
let witness_elements = VarInt::consensus_decode(r)?.0 as usize;
5151
if witness_elements == 0 {
5252
Ok(Witness::default())
5353
} else {
@@ -62,7 +62,7 @@ impl Decodable for Witness {
6262
for _ in 0..witness_elements {
6363
second_to_last = last;
6464
last = cursor;
65-
let element_size_varint = VarInt::consensus_decode(&mut d)?;
65+
let element_size_varint = VarInt::consensus_decode(r)?;
6666
let element_size_varint_len = element_size_varint.len();
6767
let element_size = element_size_varint.0 as usize;
6868
let required_len = cursor
@@ -86,9 +86,9 @@ impl Decodable for Witness {
8686

8787
resize_if_needed(&mut content, required_len);
8888
element_size_varint
89-
.consensus_encode(&mut content[cursor..cursor + element_size_varint_len])?;
89+
.consensus_encode(&mut &mut content[cursor..cursor + element_size_varint_len])?;
9090
cursor += element_size_varint_len;
91-
d.read_exact(&mut content[cursor..cursor + element_size])?;
91+
r.read_exact(&mut content[cursor..cursor + element_size])?;
9292
cursor += element_size;
9393
}
9494
content.truncate(cursor);
@@ -113,10 +113,10 @@ fn resize_if_needed(vec: &mut Vec<u8>, required_len: usize) {
113113
}
114114

115115
impl Encodable for Witness {
116-
fn consensus_encode<W: Write>(&self, mut writer: W) -> Result<usize, io::Error> {
116+
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
117117
let len = VarInt(self.witness_elements as u64);
118-
len.consensus_encode(&mut writer)?;
119-
writer.emit_slice(&self.content[..])?;
118+
len.consensus_encode(w)?;
119+
w.emit_slice(&self.content[..])?;
120120
Ok(self.content.len() + len.len())
121121
}
122122
}
@@ -145,7 +145,7 @@ impl Witness {
145145
last = cursor;
146146
let el_len_varint = VarInt(el.len() as u64);
147147
el_len_varint
148-
.consensus_encode(&mut content[cursor..cursor + el_len_varint.len()])
148+
.consensus_encode(&mut &mut content[cursor..cursor + el_len_varint.len()])
149149
.expect("writers on vec don't errors, space granted by content_size");
150150
cursor += el_len_varint.len();
151151
content[cursor..cursor + el.len()].copy_from_slice(&el);
@@ -208,7 +208,7 @@ impl Witness {
208208
.resize(current_content_len + element_len_varint.len() + new_element.len(), 0);
209209
let end_varint = current_content_len + element_len_varint.len();
210210
element_len_varint
211-
.consensus_encode(&mut self.content[current_content_len..end_varint])
211+
.consensus_encode(&mut &mut self.content[current_content_len..end_varint])
212212
.expect("writers on vec don't error, space granted through previous resize");
213213
self.content[end_varint..].copy_from_slice(new_element);
214214
}
@@ -225,7 +225,7 @@ impl Witness {
225225

226226

227227
fn element_at(&self, index: usize) -> Option<&[u8]> {
228-
let varint = VarInt::consensus_decode(&self.content[index..]).ok()?;
228+
let varint = VarInt::consensus_decode(&mut &self.content[index..]).ok()?;
229229
let start = index + varint.len();
230230
Some(&self.content[start..start + varint.0 as usize])
231231
}
@@ -253,7 +253,7 @@ impl<'a> Iterator for Iter<'a> {
253253
type Item = &'a [u8];
254254

255255
fn next(&mut self) -> Option<Self::Item> {
256-
let varint = VarInt::consensus_decode(self.inner.as_slice()).ok()?;
256+
let varint = VarInt::consensus_decode(&mut self.inner.as_slice()).ok()?;
257257
self.inner.nth(varint.len() - 1)?; // VarInt::len returns at least 1
258258
let len = varint.0 as usize;
259259
let slice = &self.inner.as_slice()[..len];

0 commit comments

Comments
 (0)