consensus_encoding: Change EncodableByteIter to EncoderByteIter
What changed, and why it matters
This is a routine internal refactoring of a Rust Bitcoin library type. It renames EncodableByteIter to EncoderByteIter and changes it to wrap an encoder directly rather than an encodable value. There is no security fix or vulnerability indicated in the commit.
No security action required. Treat as a normal API refactoring; downstream users should update any references from EncodableByteIter to EncoderByteIter when upgrading.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors the byte iterator in bitcoin-consensus-encoding from being generic over Encodable (with an associated Encoder<’e> lifetime) to being generic over Encoder directly. This simplifies trait bounds, removes a manual Clone impl in favor of a derived one, and updates call sites in tests and primitives to pass .encoder() explicitly. The change is API-shaping but does not alter encoding semantics, fix a bug, or address a security issue.
Changed components
consensus_encoding/src/encode/mod.rsconsensus_encoding/src/lib.rsconsensus_encoding/tests/api.rsconsensus_encoding/tests/encode.rsconsensus_encoding/tests/iter.rsprimitives/src/hex_codec.rsprimitives/src/script/borrowed.rsInspect captured patch +33 / −43
diff --git a/consensus_encoding/src/encode/mod.rs b/consensus_encoding/src/encode/mod.rs
index d8ac721a..f80ea15b 100644
--- a/consensus_encoding/src/encode/mod.rs
+++ b/consensus_encoding/src/encode/mod.rs
@@ -172,18 +172,18 @@ macro_rules! encoder_newtype_exact {
}
}
-/// Yields bytes from any [`Encodable`] instance.
+/// Yields bytes from any [`Encoder`] instance.
///
/// **Important** this iterator is **not** fused! Call `fuse` if you need it to be fused.
-#[derive(Debug)]
-pub struct EncodableByteIter<'e, T: Encodable + ?Sized + 'e> {
- enc: T::Encoder<'e>,
+#[derive(Debug, Clone)]
+pub struct EncoderByteIter<T: Encoder> {
+ enc: T,
position: usize,
}
-impl<'e, T: Encodable + ?Sized + 'e> EncodableByteIter<'e, T> {
- /// Constructs a new byte iterator around a provided encodable.
- pub fn new(encodable: &'e T) -> Self { Self { enc: encodable.encoder(), position: 0 } }
+impl<T: Encoder> EncoderByteIter<T> {
+ /// Constructs a new byte iterator around a provided encoder.
+ pub fn new(encoder: T) -> Self { Self { enc: encoder, position: 0 } }
/// Returns the remaining bytes in the next non-empty chunk.
///
@@ -213,16 +213,7 @@ impl<'e, T: Encodable + ?Sized + 'e> EncodableByteIter<'e, T> {
}
}
-// Manual impl rather than #[derive(Clone)] because derive would constrain `where T: Clone`,
-// but `T` itself is never cloned, only the associated type `T::Encoder<'e>`.
-impl<'e, T: Encodable + ?Sized + 'e> Clone for EncodableByteIter<'e, T>
-where
- T::Encoder<'e>: Clone,
-{
- fn clone(&self) -> Self { Self { enc: self.enc.clone(), position: self.position } }
-}
-
-impl<'e, T: Encodable + ?Sized + 'e> Iterator for EncodableByteIter<'e, T> {
+impl<T: Encoder> Iterator for EncoderByteIter<T> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
@@ -265,10 +256,9 @@ impl<'e, T: Encodable + ?Sized + 'e> Iterator for EncodableByteIter<'e, T> {
}
}
-impl<'e, T> ExactSizeIterator for EncodableByteIter<'e, T>
+impl<T> ExactSizeIterator for EncoderByteIter<T>
where
- T: Encodable + ?Sized + 'e,
- T::Encoder<'e>: ExactSizeEncoder,
+ T: Encoder + ExactSizeEncoder,
{
fn len(&self) -> usize { self.enc.len() - self.position }
}
diff --git a/consensus_encoding/src/lib.rs b/consensus_encoding/src/lib.rs
index bcfecbf6..2e2361a4 100644
--- a/consensus_encoding/src/lib.rs
+++ b/consensus_encoding/src/lib.rs
@@ -96,7 +96,7 @@ pub use self::encode::{encode_to_vec, flush_to_vec};
#[doc(inline)]
pub use self::encode::{encode_to_writer, flush_to_writer};
#[doc(inline)]
-pub use self::encode::{Encodable, EncodableByteIter, Encoder, ExactSizeEncoder};
+pub use self::encode::{Encodable, Encoder, EncoderByteIter, ExactSizeEncoder};
#[cfg(feature = "alloc")]
#[doc(no_inline)]
pub use self::error::LengthPrefixExceedsMaxError;
diff --git a/consensus_encoding/tests/api.rs b/consensus_encoding/tests/api.rs
index 3cb9dfb9..16b8f4b7 100644
--- a/consensus_encoding/tests/api.rs
+++ b/consensus_encoding/tests/api.rs
@@ -14,7 +14,7 @@ use core::fmt;
use bitcoin_consensus_encoding::{
self as encoding, encoder_newtype, ArrayDecoder, ArrayEncoder, ArrayRefEncoder, BytesEncoder,
CompactSizeDecoder, CompactSizeDecoderError, CompactSizeEncoder, CompactSizeU64Decoder,
- Decodable, Decoder, Decoder2, Decoder3, Decoder4, Decoder6, Encodable, EncodableByteIter,
+ Decodable, Decoder, Decoder2, Decoder3, Decoder4, Decoder6, Encodable, EncoderByteIter,
SliceEncoder, UnexpectedEofError,
};
use encoding::error::{DecodeError, UnconsumedError};
@@ -43,7 +43,7 @@ struct Structs {
j: Decoder3<D, D, D>,
k: Decoder4<D, D, D, D>,
l: Decoder6<D, D, D, D, D, D>,
- m: EncodableByteIter<'static, Foo>,
+ m: EncoderByteIter<FooEncoder<'static>>,
n: SliceEncoder<'static, Foo>,
#[cfg(feature = "alloc")]
o: VecDecoder<Foo>,
@@ -112,7 +112,7 @@ struct Clone {
// j: Decoder3<D, D, D>,
// k: Decoder4<D, D, D, D>,
// l: Decoder6<D, D, D, D, D, D>,
- m: EncodableByteIter<'static, Foo>,
+ m: EncoderByteIter<FooEncoder<'static>>,
n: SliceEncoder<'static, Foo>,
#[cfg(feature = "alloc")]
o: VecDecoder<Foo>,
@@ -202,7 +202,7 @@ fn api_all_non_error_types_have_non_empty_debug() {
let debug = format!("{:?}", Decoder6::new(d(), d(), d(), d(), d(), d()));
assert!(!debug.is_empty());
- let debug = format!("{:?}", EncodableByteIter::new(&Foo::dummy()));
+ let debug = format!("{:?}", EncoderByteIter::new(Foo::dummy().encoder()));
assert!(!debug.is_empty());
let debug = format!("{:?}", SliceEncoder::without_length_prefix(&[Foo::dummy()]));
assert!(!debug.is_empty());
diff --git a/consensus_encoding/tests/encode.rs b/consensus_encoding/tests/encode.rs
index 79c809ef..8cd9ef11 100644
--- a/consensus_encoding/tests/encode.rs
+++ b/consensus_encoding/tests/encode.rs
@@ -6,8 +6,8 @@
use std::io::{Cursor, Write};
use bitcoin_consensus_encoding::{
- ArrayEncoder, ArrayRefEncoder, BytesEncoder, CompactSizeEncoder, Encodable, EncodableByteIter,
- Encoder, Encoder2, Encoder3, Encoder4, Encoder6, ExactSizeEncoder, SliceEncoder,
+ ArrayEncoder, ArrayRefEncoder, BytesEncoder, CompactSizeEncoder, Encodable, Encoder, Encoder2,
+ Encoder3, Encoder4, Encoder6, EncoderByteIter, ExactSizeEncoder, SliceEncoder,
};
struct TestBytes<'a>(&'a [u8]);
@@ -617,7 +617,7 @@ fn encode_compact_size() {
#[test]
fn iter_encoder() {
let test_array = TestArray([1u8, 2, 3, 4]);
- let mut iter = EncodableByteIter::new(&test_array);
+ let mut iter = EncoderByteIter::new(test_array.encoder());
assert_eq!(iter.len(), 4);
diff --git a/consensus_encoding/tests/iter.rs b/consensus_encoding/tests/iter.rs
index ff4fff4b..5c07b1de 100644
--- a/consensus_encoding/tests/iter.rs
+++ b/consensus_encoding/tests/iter.rs
@@ -1,4 +1,4 @@
-use bitcoin_consensus_encoding::{ArrayEncoder, Encodable, EncodableByteIter, Encoder2, Encoder3};
+use bitcoin_consensus_encoding::{ArrayEncoder, Encodable, Encoder2, Encoder3, EncoderByteIter};
use hex::BytesToHexIter;
struct TestArray<const N: usize>([u8; N]);
@@ -48,7 +48,7 @@ impl<const N: usize, const M: usize, const L: usize> Encodable for TestCatArray3
#[test]
fn hex_iter() {
let data = TestArray([255u8, 240, 9, 135]);
- let byte_iter = EncodableByteIter::new(&data);
+ let byte_iter = EncoderByteIter::new(data.encoder());
let mut iter = BytesToHexIter::new(byte_iter, hex::Case::Upper);
let expect_str = "FFF00987";
@@ -63,7 +63,7 @@ fn hex_iter() {
#[test]
fn hex_iter_cat_encoder() {
let data = TestCatArray([222u8, 173], [190u8, 239]);
- let byte_iter = EncodableByteIter::new(&data);
+ let byte_iter = EncoderByteIter::new(data.encoder());
let mut iter = BytesToHexIter::new(byte_iter, hex::Case::Lower);
let expect_str = "deadbeef";
@@ -80,19 +80,19 @@ fn hex_iter_cat_encoder() {
#[test]
fn nth() {
let data = TestArray([255u8, 240, 9, 135]);
- let mut byte_iter = EncodableByteIter::new(&data);
+ let mut byte_iter = EncoderByteIter::new(data.encoder());
assert_eq!(byte_iter.nth(2).unwrap(), 9);
assert_eq!(byte_iter.nth(0).unwrap(), 135);
assert!(byte_iter.nth(42).is_none());
let data = TestCatArray([222u8, 173], [190u8, 239]);
- let mut byte_iter = EncodableByteIter::new(&data);
+ let mut byte_iter = EncoderByteIter::new(data.encoder());
assert_eq!(byte_iter.nth(1).unwrap(), 173);
assert_eq!(byte_iter.nth(1).unwrap(), 239);
assert!(byte_iter.nth(42).is_none());
let data = TestCatArray3([0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]);
- let mut byte_iter = EncodableByteIter::new(&data);
+ let mut byte_iter = EncoderByteIter::new(data.encoder());
assert_eq!(byte_iter.nth(5).unwrap(), 5);
assert_eq!(byte_iter.peek_chunk(), [6, 7]);
}
@@ -100,13 +100,13 @@ fn nth() {
#[test]
fn peek_chunk() {
let data = TestArray([255u8, 240, 9, 135]);
- let mut byte_iter = EncodableByteIter::new(&data);
+ let mut byte_iter = EncoderByteIter::new(data.encoder());
assert_eq!(byte_iter.peek_chunk(), [255u8, 240, 9, 135]);
assert_eq!(byte_iter.next().unwrap(), 255);
assert_eq!(byte_iter.peek_chunk(), [240, 9, 135]);
let data = TestCatArray([222u8, 173], [190u8, 239]);
- let mut byte_iter = EncodableByteIter::new(&data);
+ let mut byte_iter = EncoderByteIter::new(data.encoder());
assert_eq!(byte_iter.peek_chunk(), [222u8, 173]);
assert_eq!(byte_iter.next().unwrap(), 222);
assert_eq!(byte_iter.peek_chunk(), [173]);
@@ -114,7 +114,7 @@ fn peek_chunk() {
assert_eq!(byte_iter.peek_chunk(), [190, 239]);
let data = TestCatArray([], [21u8, 42]);
- let mut byte_iter = EncodableByteIter::new(&data);
+ let mut byte_iter = EncoderByteIter::new(data.encoder());
assert_eq!(byte_iter.peek_chunk(), [21, 42]);
assert_eq!(byte_iter.next().unwrap(), 21);
assert_eq!(byte_iter.peek_chunk(), [42]);
diff --git a/primitives/src/hex_codec.rs b/primitives/src/hex_codec.rs
index 787bfd4e..5df446da 100644
--- a/primitives/src/hex_codec.rs
+++ b/primitives/src/hex_codec.rs
@@ -12,7 +12,7 @@ use core::convert::Infallible;
use core::fmt;
use core::fmt::Write as _;
-use encoding::{Decodable, Decoder, Encodable, EncodableByteIter};
+use encoding::{Decodable, Decoder, Encodable, EncoderByteIter};
use hex_unstable::{BytesToHexIter, Case};
use internals::write_err;
@@ -24,9 +24,9 @@ pub(crate) struct HexPrimitive<'a, T>(pub(crate) &'a T);
impl<'a, T: Encodable + Decodable> IntoIterator for &HexPrimitive<'a, T> {
type Item = u8;
- type IntoIter = EncodableByteIter<'a, T>;
+ type IntoIter = EncoderByteIter<T::Encoder<'a>>;
- fn into_iter(self) -> Self::IntoIter { EncodableByteIter::new(self.0) }
+ fn into_iter(self) -> Self::IntoIter { EncoderByteIter::new(self.0.encoder()) }
}
impl<T: Decodable> HexPrimitive<'_, T> {
@@ -84,8 +84,8 @@ impl<T: Encodable> HexPrimitive<'_, T> {
};
// Count hex chars
- let len = EncodableByteIter::new(self.0).count() * 2;
- let iter = BytesToHexIter::new(EncodableByteIter::new(self.0), case);
+ let len = EncoderByteIter::new(self.0.encoder()).count() * 2;
+ let iter = BytesToHexIter::new(EncoderByteIter::new(self.0.encoder()), case);
let extra_len = if f.alternate() { 2 } else { 0 };
let total_len = len + extra_len;
diff --git a/primitives/src/script/borrowed.rs b/primitives/src/script/borrowed.rs
index 3b7d3f47..5dad3669 100644
--- a/primitives/src/script/borrowed.rs
+++ b/primitives/src/script/borrowed.rs
@@ -134,7 +134,7 @@ impl<T> Script<T> {
pub fn to_hex_string_prefixed(&self) -> String {
use hex_unstable::{BytesToHexIter, Case};
- let iter = encoding::EncodableByteIter::new(self);
+ let iter = encoding::EncoderByteIter::new(self.encoder());
BytesToHexIter::new(iter, Case::Lower).collect()
}
Why this scored 18/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.