Skip to content

Commit c96523c

Browse files
committed
Merge rust-bitcoin#5086: consensus_encoding: Add CompactSizeEncoder and refactor WitnessEncoder
3051b3e primitives: Refactor `WitnessEncoder` to use CompactSizeEncoder (jrakibi) 982a805 consensus_encoding: Export `CompactSizeEncoder` (jrakibi) aa00732 consensus_encoding: Add `CompactSizeEncoder` implementation (jrakibi) Pull request description: Add `CompactSizeEncoder` implementation and simplify `WitnessEncoder` by using the new `CompactSizeEncoder` + `Encoder2` composition. Discussed in: rust-bitcoin#4992 (comment) Closes: rust-bitcoin#5077 ACKs for top commit: nyonson: ACK 3051b3e apoelstra: ACK 3051b3e; successfully ran local tests tcharding: ACK 3051b3e Tree-SHA512: f74cd5aec981e902b6ae62ffd43a70145ac6dcbc48182e7062abf40010f49af4ee6313ad8ea69447a9d4b2b43b07d9d896892a22ab521847328c16d94b5f588e
2 parents eeddb0a + 3051b3e commit c96523c

File tree

3 files changed

+70
-35
lines changed

3 files changed

+70
-35
lines changed

consensus_encoding/src/encode/encoders.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//!
1313
1414
use internals::array_vec::ArrayVec;
15-
use internals::compact_size;
15+
use internals::{compact_size, ToU64};
1616

1717
use super::{Encodable, Encoder};
1818

@@ -250,6 +250,27 @@ impl<A: Encoder, B: Encoder, C: Encoder, D: Encoder, E: Encoder, F: Encoder> Enc
250250
fn advance(&mut self) -> bool { self.inner.advance() }
251251
}
252252

253+
/// Encoder for a compact size encoded integer.
254+
pub struct CompactSizeEncoder {
255+
buf: Option<ArrayVec<u8, SIZE>>,
256+
}
257+
258+
impl CompactSizeEncoder {
259+
/// Constructs a new `CompactSizeEncoder`.
260+
pub fn new(value: impl ToU64) -> Self { Self { buf: Some(compact_size::encode(value)) } }
261+
}
262+
263+
impl Encoder for CompactSizeEncoder {
264+
#[inline]
265+
fn current_chunk(&self) -> Option<&[u8]> { self.buf.as_ref().map(|b| &b[..]) }
266+
267+
#[inline]
268+
fn advance(&mut self) -> bool {
269+
self.buf = None;
270+
false
271+
}
272+
}
273+
253274
#[cfg(test)]
254275
mod tests {
255276
use super::*;
@@ -651,4 +672,42 @@ mod tests {
651672
assert!(!encoder.advance());
652673
assert_eq!(encoder.current_chunk(), None);
653674
}
675+
#[test]
676+
fn encode_compact_size() {
677+
// 1-byte
678+
let mut e = CompactSizeEncoder::new(0x10u64);
679+
assert_eq!(e.current_chunk(), Some(&[0x10][..]));
680+
assert!(!e.advance());
681+
assert_eq!(e.current_chunk(), None);
682+
683+
let mut e = CompactSizeEncoder::new(0xFCu64);
684+
assert_eq!(e.current_chunk(), Some(&[0xFC][..]));
685+
assert!(!e.advance());
686+
687+
// 0xFD + u16
688+
let mut e = CompactSizeEncoder::new(0x00FDu64);
689+
assert_eq!(e.current_chunk(), Some(&[0xFD, 0xFD, 0x00][..]));
690+
assert!(!e.advance());
691+
692+
let mut e = CompactSizeEncoder::new(0x0FFFu64);
693+
assert_eq!(e.current_chunk(), Some(&[0xFD, 0xFF, 0x0F][..]));
694+
assert!(!e.advance());
695+
696+
// 0xFE + u32
697+
let mut e = CompactSizeEncoder::new(0x0001_0000u64);
698+
assert_eq!(e.current_chunk(), Some(&[0xFE, 0x00, 0x00, 0x01, 0x00][..]));
699+
assert!(!e.advance());
700+
701+
let mut e = CompactSizeEncoder::new(0x0F0F_0F0Fu64);
702+
assert_eq!(e.current_chunk(), Some(&[0xFE, 0x0F, 0x0F, 0x0F, 0x0F][..]));
703+
assert!(!e.advance());
704+
705+
// 0xFF + u64
706+
let mut e = CompactSizeEncoder::new(0x0000_F0F0_F0F0_F0E0u64);
707+
assert_eq!(
708+
e.current_chunk(),
709+
Some(&[0xFF, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00][..])
710+
);
711+
assert!(!e.advance());
712+
}
654713
}

consensus_encoding/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub use self::encode::encode_to_vec;
4141
#[cfg(feature = "std")]
4242
pub use self::encode::encode_to_writer;
4343
pub use self::encode::encoders::{
44-
ArrayEncoder, BytesEncoder, Encoder2, Encoder3, Encoder4, Encoder6, SliceEncoder,
44+
ArrayEncoder, BytesEncoder, CompactSizeEncoder, Encoder2, Encoder3, Encoder4, Encoder6,
45+
SliceEncoder,
4546
};
4647
pub use self::encode::{encode_to_hash_engine, Encodable, Encoder};

primitives/src/witness.rs

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ use core::ops::Index;
99

1010
#[cfg(feature = "arbitrary")]
1111
use arbitrary::{Arbitrary, Unstructured};
12-
use encoding::{Encodable, Encoder};
12+
use encoding::{BytesEncoder, CompactSizeEncoder, Encodable, Encoder, Encoder2};
1313
#[cfg(feature = "hex")]
1414
use hex::{error::HexToBytesError, FromHex};
15-
use internals::array_vec::ArrayVec;
1615
use internals::compact_size;
1716
use internals::slice::SliceExt;
1817
use internals::wrap_debug::WrapDebug;
@@ -262,19 +261,8 @@ fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<
262261
bytes.get_array::<4>(start).map(|index_bytes| u32::from_ne_bytes(*index_bytes) as usize)
263262
}
264263

265-
/// The maximum length of a compact size encoding.
266-
const SIZE: usize = compact_size::MAX_ENCODING_SIZE;
267-
268264
/// The encoder for the [`Witness`] type.
269-
// This is basically an exact copy of the `encoding::BytesEncoder` except we prefix
270-
// with the number of witness elements not the byte slice length.
271-
pub struct WitnessEncoder<'a> {
272-
/// A slice of all the elements without the initial length prefix
273-
/// but with the length prefix on each element.
274-
witness_elements: Option<&'a [u8]>,
275-
/// Encoding of the number of witness elements.
276-
num_elements: Option<ArrayVec<u8, SIZE>>,
277-
}
265+
pub struct WitnessEncoder<'a>(Encoder2<CompactSizeEncoder, BytesEncoder<'a>>);
278266

279267
impl Encodable for Witness {
280268
type Encoder<'a>
@@ -283,33 +271,20 @@ impl Encodable for Witness {
283271
Self: 'a;
284272

285273
fn encoder(&self) -> Self::Encoder<'_> {
286-
let num_elements = Some(compact_size::encode(self.len()));
287-
let witness_elements = Some(&self.content[..self.indices_start]);
274+
let num_elements = CompactSizeEncoder::new(self.len());
275+
let witness_elements =
276+
BytesEncoder::without_length_prefix(&self.content[..self.indices_start]);
288277

289-
WitnessEncoder { witness_elements, num_elements }
278+
WitnessEncoder(Encoder2::new(num_elements, witness_elements))
290279
}
291280
}
292281

293282
impl<'a> Encoder for WitnessEncoder<'a> {
294283
#[inline]
295-
fn current_chunk(&self) -> Option<&[u8]> {
296-
if let Some(num_elements) = self.num_elements.as_ref() {
297-
Some(num_elements)
298-
} else {
299-
self.witness_elements
300-
}
301-
}
284+
fn current_chunk(&self) -> Option<&[u8]> { self.0.current_chunk() }
302285

303286
#[inline]
304-
fn advance(&mut self) -> bool {
305-
if self.num_elements.is_some() {
306-
self.num_elements = None;
307-
true
308-
} else {
309-
self.witness_elements = None;
310-
false
311-
}
312-
}
287+
fn advance(&mut self) -> bool { self.0.advance() }
313288
}
314289

315290
// Note: we use `Borrow` in the following `PartialEq` impls specifically because of its additional

0 commit comments

Comments
 (0)