fuzz: Remove allocations in roundtrip targets
What changed, and why it matters
This commit is a performance improvement for internal fuzz testing code. It removes an unnecessary memory allocation when testing that data can be encoded and decoded correctly. There is no security vulnerability or fix here.
No security action needed. This is a normal code-quality/performance change to fuzz test infrastructure.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change modifies fuzz/src/lib.rs in the rust-bitcoin project. The roundtrip fuzz targets previously encoded values to a Vec and then decoded from that Vec. The commit replaces that with a streaming encode-to-decode path that pushes encoder chunks directly into the decoder, avoiding the intermediate Vec allocation. This is purely a fuzz harness optimization.
Changed components
fuzz/src/lib.rsInspect captured patch +25 / −7
diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs
index 3fe76625..2d9136cc 100644
--- a/fuzz/src/lib.rs
+++ b/fuzz/src/lib.rs
@@ -1,8 +1,9 @@
//! Shared utilities for fuzz targets.
use std::fmt;
+use std::ops::Deref;
-use bitcoin_consensus_encoding::{decode_from_slice, encode_to_vec, Decode, Decoder, Encode};
+use bitcoin_consensus_encoding::{decode_from_slice, Decode, Decoder, Encode, Encoder};
/// Checks roundtrip decode -> encode for a type.
///
@@ -14,8 +15,7 @@ where
<<T as Decode>::Decoder as Decoder>::Error: fmt::Debug,
{
if let Ok(base_decoded) = decode_from_slice::<T>(data) {
- let encoded = encode_to_vec(&base_decoded);
- let decoded = decode_from_slice::<T>(&encoded).unwrap();
+ let decoded = stream_encode_decode::<_, T>(&base_decoded);
assert_eq!(base_decoded, decoded);
}
}
@@ -26,13 +26,31 @@ where
/// unsized counterpart (e.g. `ScriptPubKey`), so encoding must go through the deref.
pub fn check_script_roundtrip<T>(data: &[u8])
where
- T: Decode + PartialEq + fmt::Debug + std::ops::Deref,
- <T as std::ops::Deref>::Target: Encode,
+ T: Decode + PartialEq + fmt::Debug + Deref,
+ <T as Deref>::Target: Encode,
<<T as Decode>::Decoder as Decoder>::Error: fmt::Debug,
{
if let Ok(base_decoded) = decode_from_slice::<T>(data) {
- let encoded = encode_to_vec(&*base_decoded);
- let decoded = decode_from_slice::<T>(&encoded).unwrap();
+ let decoded = stream_encode_decode::<<T as Deref>::Target, T>(&*base_decoded);
assert_eq!(base_decoded, decoded);
}
}
+
+#[inline]
+fn stream_encode_decode<E, D>(encodable: &E) -> D
+where
+ E: Encode + ?Sized,
+ D: Decode,
+ <<D as Decode>::Decoder as Decoder>::Error: fmt::Debug,
+{
+ let mut encoder = encodable.encoder();
+ let mut decoder = D::decoder();
+ loop {
+ let mut chunk = encoder.current_chunk();
+ while !chunk.is_empty() && decoder.push_bytes(&mut chunk).unwrap() {}
+ if !chunk.is_empty() || !encoder.advance() {
+ break;
+ }
+ }
+ decoder.end().unwrap()
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.