Skip to content

Commit 7d910cd

Browse files
committed
Merge rust-bitcoin#5108: Remove length prefix from SliceEncoder
63bbe12 Remove length prefix from SliceEncoder (Tobin C. Harding) Pull request description: Currently we hide the length prefix inside the `SliceEncoder`. While technically correct the usage of the `SliceEncoder` can be made more clear by forcing users to combine a compact size encoder and a slice encoder using an `Encoder2` instead of the current abstraction. Original idea from jrakibi in rust-bitcoin#5104. I gave you a co-developed-by tag mate. Pushing this up because I'm itching to get `primitives 1.0.0-rc.0` out. ACKs for top commit: apoelstra: ACK 63bbe12; successfully ran local tests Tree-SHA512: befe9b25a35644195834fdf0fec137d9250278416277409828cca2cd888ec69d4d29309f4fc761fe95790046131e58d743a724075c6b1092e86a564627bc3f67
2 parents b868835 + 63bbe12 commit 7d910cd

File tree

5 files changed

+58
-64
lines changed

5 files changed

+58
-64
lines changed

consensus_encoding/examples/encoder.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Example of creating an encoder that encodes a slice of encodable objects.
44
55
use consensus_encoding as encoding;
6-
use encoding::{ArrayEncoder, BytesEncoder, Encodable, Encoder2, SliceEncoder};
6+
use encoding::{ArrayEncoder, BytesEncoder, CompactSizeEncoder, Encodable, Encoder2, SliceEncoder};
77

88
fn main() {
99
let v = vec![Inner::new(0xcafe_babe), Inner::new(0xdead_beef)];
@@ -29,7 +29,7 @@ impl Adt {
2929

3030
encoding::encoder_newtype! {
3131
/// The encoder for the [`Adt`] type.
32-
pub struct AdtEncoder<'e>(Encoder2<SliceEncoder<'e, Inner>, BytesEncoder<'e>>);
32+
pub struct AdtEncoder<'e>(Encoder2<Encoder2<CompactSizeEncoder, SliceEncoder<'e, Inner>>, BytesEncoder<'e>>);
3333
}
3434

3535
impl Encodable for Adt {
@@ -39,7 +39,10 @@ impl Encodable for Adt {
3939
Self: 'a;
4040

4141
fn encoder(&self) -> Self::Encoder<'_> {
42-
let a = SliceEncoder::with_length_prefix(&self.v);
42+
let a = Encoder2::new(
43+
CompactSizeEncoder::new(self.v.len()),
44+
SliceEncoder::without_length_prefix(&self.v),
45+
);
4346
let b = BytesEncoder::without_length_prefix(self.b.as_ref());
4447

4548
AdtEncoder(Encoder2::new(a, b))

consensus_encoding/src/encode/encoders.rs

Lines changed: 25 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -65,43 +65,32 @@ impl<const N: usize> Encoder for ArrayEncoder<N> {
6565
pub struct SliceEncoder<'e, T: Encodable> {
6666
/// The list of references to the objects we are encoding.
6767
sl: &'e [T],
68-
/// The length prefix.
69-
compact_size: Option<ArrayVec<u8, SIZE>>,
7068
/// Encoder for the current object being encoded.
7169
cur_enc: Option<T::Encoder<'e>>,
7270
}
7371

7472
impl<'e, T: Encodable> SliceEncoder<'e, T> {
75-
/// Constructs an encoder which encodes the slice with a length prefix.
76-
pub fn with_length_prefix(sl: &'e [T]) -> Self {
77-
let len = sl.len();
78-
let compact_size = Some(compact_size::encode(len));
79-
73+
/// Constructs an encoder which encodes the slice _without_ adding the length prefix.
74+
///
75+
/// To encode with a length prefix consider using the `Encoder2`.
76+
///
77+
/// E.g, `Encoder2<CompactSizeEncoder, SliceEncoder<'e, Foo>>`.
78+
pub fn without_length_prefix(sl: &'e [T]) -> Self {
8079
// In this `map` call we cannot remove the closure. Seems to be a bug in the compiler.
8180
// Perhaps https://github.com/rust-lang/rust/issues/102540 which is 3 years old with
8281
// no replies or even an acknowledgement. We will not bother filing our own issue.
83-
Self { sl, compact_size, cur_enc: sl.first().map(|x| T::encoder(x)) }
82+
Self { sl, cur_enc: sl.first().map(|x| T::encoder(x)) }
8483
}
8584
}
8685

8786
impl<T: Encodable> Encoder for SliceEncoder<'_, T> {
8887
fn current_chunk(&self) -> Option<&[u8]> {
89-
if let Some(compact_size) = self.compact_size.as_ref() {
90-
return Some(compact_size);
91-
}
92-
9388
// `advance` sets `cur_enc` to `None` once the slice encoder is completely exhausted.
9489
// `current_chunk` is required to return `None` if called after the encoder is exhausted.
9590
self.cur_enc.as_ref().and_then(T::Encoder::current_chunk)
9691
}
9792

9893
fn advance(&mut self) -> bool {
99-
// Handle compact_size first, regardless of whether we have elements.
100-
if self.compact_size.is_some() {
101-
self.compact_size = None;
102-
return self.cur_enc.is_some();
103-
}
104-
10594
let Some(cur) = self.cur_enc.as_mut() else {
10695
return false;
10796
};
@@ -329,12 +318,10 @@ mod tests {
329318

330319
#[test]
331320
fn encode_slice_with_elements() {
332-
// Should have length prefix chunk, then element chunks, then exhausted.
321+
// Should have the element chunks, then exhausted.
333322
let slice = &[TestArray([0x34, 0x12, 0x00, 0x00]), TestArray([0x78, 0x56, 0x00, 0x00])];
334-
let mut encoder = SliceEncoder::with_length_prefix(slice);
323+
let mut encoder = SliceEncoder::without_length_prefix(slice);
335324

336-
assert_eq!(encoder.current_chunk(), Some(&[2u8][..]));
337-
assert!(encoder.advance());
338325
assert_eq!(encoder.current_chunk(), Some(&[0x34, 0x12, 0x00, 0x00][..]));
339326
assert!(encoder.advance());
340327
assert_eq!(encoder.current_chunk(), Some(&[0x78, 0x56, 0x00, 0x00][..]));
@@ -344,23 +331,20 @@ mod tests {
344331

345332
#[test]
346333
fn encode_empty_slice() {
347-
// Should have only length prefix chunk (0), then exhausted.
334+
// Should immediately be exhausted.
348335
let slice: &[TestArray<4>] = &[];
349-
let mut encoder = SliceEncoder::with_length_prefix(slice);
336+
let mut encoder = SliceEncoder::without_length_prefix(slice);
350337

351-
assert_eq!(encoder.current_chunk(), Some(&[0u8][..]));
352338
assert!(!encoder.advance());
353339
assert_eq!(encoder.current_chunk(), None);
354340
}
355341

356342
#[test]
357343
fn encode_slice_with_zero_sized_arrays() {
358-
// Should have length prefix chunk, then empty array chunks, then exhausted.
344+
// Should have empty array chunks, then exhausted.
359345
let slice = &[TestArray([]), TestArray([])];
360-
let mut encoder = SliceEncoder::with_length_prefix(slice);
346+
let mut encoder = SliceEncoder::without_length_prefix(slice);
361347

362-
assert_eq!(encoder.current_chunk(), Some(&[2u8][..]));
363-
assert!(encoder.advance());
364348
assert_eq!(encoder.current_chunk(), Some(&[][..]));
365349
assert!(encoder.advance());
366350
assert_eq!(encoder.current_chunk(), Some(&[][..]));
@@ -492,14 +476,12 @@ mod tests {
492476

493477
#[test]
494478
fn encode_slice_with_array_composition() {
495-
// Should encode slice with prefix and elements, then array, then exhausted.
479+
// Should encode slice elements, then array, then exhausted.
496480
let slice = &[TestArray([0x10, 0x11]), TestArray([0x12, 0x13])];
497-
let slice_enc = SliceEncoder::with_length_prefix(slice);
481+
let slice_enc = SliceEncoder::without_length_prefix(slice);
498482
let array_enc = TestArray([0x20, 0x21]).encoder();
499483
let mut encoder = Encoder2::new(slice_enc, array_enc);
500484

501-
assert_eq!(encoder.current_chunk(), Some(&[2u8][..]));
502-
assert!(encoder.advance());
503485
assert_eq!(encoder.current_chunk(), Some(&[0x10, 0x11][..]));
504486
assert!(encoder.advance());
505487
assert_eq!(encoder.current_chunk(), Some(&[0x12, 0x13][..]));
@@ -511,16 +493,14 @@ mod tests {
511493

512494
#[test]
513495
fn encode_array_with_slice_composition() {
514-
// Should encode header array, then slice with prefix and elements, then exhausted.
496+
// Should encode header array, then slice elements, then exhausted.
515497
let header = TestArray([0xFF, 0xFE]).encoder();
516498
let slice = &[TestArray([0x01]), TestArray([0x02]), TestArray([0x03])];
517-
let slice_enc = SliceEncoder::with_length_prefix(slice);
499+
let slice_enc = SliceEncoder::without_length_prefix(slice);
518500
let mut encoder = Encoder2::new(header, slice_enc);
519501

520502
assert_eq!(encoder.current_chunk(), Some(&[0xFF, 0xFE][..]));
521503
assert!(encoder.advance());
522-
assert_eq!(encoder.current_chunk(), Some(&[3u8][..]));
523-
assert!(encoder.advance());
524504
assert_eq!(encoder.current_chunk(), Some(&[0x01][..]));
525505
assert!(encoder.advance());
526506
assert_eq!(encoder.current_chunk(), Some(&[0x02][..]));
@@ -532,25 +512,23 @@ mod tests {
532512

533513
#[test]
534514
fn encode_multiple_slices_composition() {
535-
// Should encode three slices in sequence with prefixes and elements, then exhausted.
515+
// Should encode three slices in sequence, then exhausted.
536516
let slice1 = &[TestArray([0xA1]), TestArray([0xA2])];
537517
let slice2: &[TestArray<1>] = &[];
538518
let slice3 = &[TestArray([0xC1]), TestArray([0xC2]), TestArray([0xC3])];
539519

540-
let enc1 = SliceEncoder::with_length_prefix(slice1);
541-
let enc2 = SliceEncoder::with_length_prefix(slice2);
542-
let enc3 = SliceEncoder::with_length_prefix(slice3);
520+
let enc1 = SliceEncoder::without_length_prefix(slice1);
521+
let enc2 = SliceEncoder::without_length_prefix(slice2);
522+
let enc3 = SliceEncoder::without_length_prefix(slice3);
543523
let mut encoder = Encoder3::new(enc1, enc2, enc3);
544524

545-
assert_eq!(encoder.current_chunk(), Some(&[2u8][..]));
546-
assert!(encoder.advance());
547525
assert_eq!(encoder.current_chunk(), Some(&[0xA1][..]));
548526
assert!(encoder.advance());
549527
assert_eq!(encoder.current_chunk(), Some(&[0xA2][..]));
528+
529+
// Skip the empty slice
550530
assert!(encoder.advance());
551-
assert_eq!(encoder.current_chunk(), Some(&[0u8][..]));
552-
assert!(encoder.advance());
553-
assert_eq!(encoder.current_chunk(), Some(&[3u8][..]));
531+
554532
assert!(encoder.advance());
555533
assert_eq!(encoder.current_chunk(), Some(&[0xC1][..]));
556534
assert!(encoder.advance());
@@ -566,14 +544,12 @@ mod tests {
566544
// Should encode header, slice with elements, and footer with prefix, then exhausted.
567545
let header = TestBytes(&[0xDE, 0xAD]).encoder();
568546
let data_slice = &[TestArray([0x01, 0x02]), TestArray([0x03, 0x04])];
569-
let slice_enc = SliceEncoder::with_length_prefix(data_slice);
547+
let slice_enc = SliceEncoder::without_length_prefix(data_slice);
570548
let footer = TestBytes(&[0xBE, 0xEF]).encoder();
571549
let mut encoder = Encoder3::new(header, slice_enc, footer);
572550

573551
assert_eq!(encoder.current_chunk(), Some(&[0xDE, 0xAD][..]));
574552
assert!(encoder.advance());
575-
assert_eq!(encoder.current_chunk(), Some(&[2u8][..]));
576-
assert!(encoder.advance());
577553
assert_eq!(encoder.current_chunk(), Some(&[0x01, 0x02][..]));
578554
assert!(encoder.advance());
579555
assert_eq!(encoder.current_chunk(), Some(&[0x03, 0x04][..]));

consensus_encoding/tests/wrappers.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#![cfg(feature = "std")]
44

55
use consensus_encoding as encoding;
6-
use encoding::{ArrayEncoder, BytesEncoder, Encodable, Encoder2, SliceEncoder};
6+
use encoding::{ArrayEncoder, BytesEncoder, CompactSizeEncoder, Encodable, Encoder2, SliceEncoder};
77

88
encoding::encoder_newtype! {
99
/// An encoder that uses an inner `ArrayEncoder`.
@@ -93,7 +93,7 @@ fn slice_encoder() {
9393

9494
encoding::encoder_newtype! {
9595
/// An encoder that uses an inner `SliceEncoder`.
96-
pub struct TestEncoder<'e>(SliceEncoder<'e, Inner>);
96+
pub struct TestEncoder<'e>(Encoder2<CompactSizeEncoder, SliceEncoder<'e, Inner>>);
9797
}
9898

9999
impl Encodable for Test {
@@ -103,7 +103,10 @@ fn slice_encoder() {
103103
Self: 'a;
104104

105105
fn encoder(&self) -> Self::Encoder<'_> {
106-
TestEncoder(SliceEncoder::with_length_prefix(&self.0))
106+
TestEncoder(Encoder2::new(
107+
CompactSizeEncoder::new(self.0.len()),
108+
SliceEncoder::without_length_prefix(&self.0),
109+
))
107110
}
108111
}
109112

primitives/src/block.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use core::marker::PhantomData;
1313

1414
#[cfg(feature = "arbitrary")]
1515
use arbitrary::{Arbitrary, Unstructured};
16-
use encoding::{Encodable, Encoder2, SliceEncoder};
16+
use encoding::{CompactSizeEncoder, Encodable, Encoder2, SliceEncoder};
1717
use hashes::{sha256d, HashEngine as _};
1818

1919
#[cfg(feature = "alloc")]
@@ -169,19 +169,25 @@ mod sealed {
169169
encoding::encoder_newtype! {
170170
/// The encoder for the [`Block`] type.
171171
pub struct BlockEncoder<'e>(
172-
Encoder2<HeaderEncoder, SliceEncoder<'e, Transaction>>
172+
Encoder2<HeaderEncoder, Encoder2<CompactSizeEncoder, SliceEncoder<'e, Transaction>>>
173173
);
174174
}
175175

176176
#[cfg(feature = "alloc")]
177177
impl Encodable for Block {
178178
type Encoder<'e>
179-
= Encoder2<HeaderEncoder, SliceEncoder<'e, Transaction>>
179+
= Encoder2<HeaderEncoder, Encoder2<CompactSizeEncoder, SliceEncoder<'e, Transaction>>>
180180
where
181181
Self: 'e;
182182

183183
fn encoder(&self) -> Self::Encoder<'_> {
184-
Encoder2::new(self.header.encoder(), SliceEncoder::with_length_prefix(&self.transactions))
184+
Encoder2::new(
185+
self.header.encoder(),
186+
Encoder2::new(
187+
CompactSizeEncoder::new(self.transactions.len()),
188+
SliceEncoder::without_length_prefix(&self.transactions),
189+
),
190+
)
185191
}
186192
}
187193

primitives/src/transaction.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use core::fmt;
2121
use arbitrary::{Arbitrary, Unstructured};
2222
use encoding::{ArrayEncoder, BytesEncoder, Encodable, Encoder2};
2323
#[cfg(feature = "alloc")]
24-
use encoding::{Encoder, Encoder3, Encoder6, SliceEncoder};
24+
use encoding::{CompactSizeEncoder, Encoder, Encoder3, Encoder6, SliceEncoder};
2525
#[cfg(feature = "alloc")]
2626
use hashes::sha256d;
2727
#[cfg(feature = "alloc")]
@@ -311,8 +311,8 @@ encoding::encoder_newtype! {
311311
Encoder6<
312312
VersionEncoder,
313313
Option<ArrayEncoder<2>>,
314-
SliceEncoder<'e, TxIn>,
315-
SliceEncoder<'e, TxOut>,
314+
Encoder2<CompactSizeEncoder, SliceEncoder<'e, TxIn>>,
315+
Encoder2<CompactSizeEncoder, SliceEncoder<'e, TxOut>>,
316316
Option<WitnessesEncoder<'e>>,
317317
LockTimeEncoder,
318318
>
@@ -328,8 +328,14 @@ impl Encodable for Transaction {
328328

329329
fn encoder(&self) -> Self::Encoder<'_> {
330330
let version = self.version.encoder();
331-
let inputs = SliceEncoder::with_length_prefix(self.inputs.as_ref());
332-
let outputs = SliceEncoder::with_length_prefix(self.outputs.as_ref());
331+
let inputs = Encoder2::new(
332+
CompactSizeEncoder::new(self.inputs.len() as u64),
333+
SliceEncoder::without_length_prefix(self.inputs.as_ref()),
334+
);
335+
let outputs = Encoder2::new(
336+
CompactSizeEncoder::new(self.outputs.len() as u64),
337+
SliceEncoder::without_length_prefix(self.outputs.as_ref()),
338+
);
333339
let lock_time = self.lock_time.encoder();
334340

335341
if self.uses_segwit_serialization() {

0 commit comments

Comments
 (0)