Skip to content

Commit a5bdcdb

Browse files
committed
Merge rust-bitcoin#5039: Clean up encoders unit tests and fix empty SliceEncoder
526e1ad consensus_encoding: add slice encode unit tests (Nick Johnson) 5ad7d4e consensus_encoding: fix zero element SliceEncoder (Nick Johnson) 544d979 consensus_encoding: update encoder unit tests (Nick Johnson) Pull request description: Cleaning up the `encoders` unit tests and adding some helper types for further testing. But posting now because adding a few SliceEncoder tests like `encode_empty_slice` exposed a bug in the `SliceEncoder::advance` method. For an empty slice, the compact size is never cleared. I added a fix in the second commit and tests in the third, but I know a lot of iterations were just spent on this so don't have full confidence it didn't hurt some other goal (at one point branch prediction was brought up). Separately, the `encode_slice_with_zero_sized_arrays` test is kinda weird, but I think logically checks out...just don't think we will ever be sending zero-byte arrays in real life. ACKs for top commit: tcharding: ACK 526e1ad apoelstra: ACK 526e1ad; successfully ran local tests Tree-SHA512: b6582c368502ccad0a8e07661a65e3b87d36a622bd4724114dea67e741d5200eeb1d706d959f354163d58fce44de2cf27966590512766024509e023adb6dc07a
2 parents 9b90283 + 526e1ad commit a5bdcdb

File tree

1 file changed

+126
-40
lines changed

1 file changed

+126
-40
lines changed

consensus_encoding/src/encode/encoders.rs

Lines changed: 126 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,24 @@ impl<'e, T: Encodable> Encoder for SliceEncoder<'e, T> {
111111
}
112112

113113
fn advance(&mut self) -> bool {
114+
// Handle compact_size first, regardless of whether we have elements.
115+
if self.compact_size.is_some() {
116+
self.compact_size = None;
117+
return self.cur_enc.is_some();
118+
}
119+
114120
let Some(cur) = self.cur_enc.as_mut() else {
115121
return false;
116122
};
117123

118124
loop {
119-
if self.compact_size.is_some() {
120-
// On the first call to advance(), just mark the compact_size as already
121-
// yielded and leave self.sl alone.
122-
self.compact_size = None;
123-
} else {
124-
// On subsequent calls, attempt to advance the current encoder and return
125-
// success if this succeeds.
126-
if cur.advance() {
127-
return true;
128-
}
129-
// self.sl guaranteed to be non-empty if cur is non-None.
130-
self.sl = &self.sl[1..];
125+
// On subsequent calls, attempt to advance the current encoder and return
126+
// success if this succeeds.
127+
if cur.advance() {
128+
return true;
131129
}
130+
// self.sl guaranteed to be non-empty if cur is non-None.
131+
self.sl = &self.sl[1..];
132132

133133
// If advancing the current encoder failed, attempt to move to the next encoder.
134134
if let Some(x) = self.sl.first() {
@@ -241,61 +241,147 @@ impl<A: Encoder, B: Encoder, C: Encoder, D: Encoder, E: Encoder, F: Encoder> Enc
241241
}
242242

243243
#[cfg(test)]
244-
#[cfg(feature = "alloc")]
245244
mod tests {
246-
use alloc::vec::Vec;
247-
248245
use super::*;
249246

250-
// Run the encoder i.e., use it to encode into a vector.
251-
fn run_encoder(mut encoder: impl Encoder) -> Vec<u8> {
252-
let mut vec = Vec::new();
253-
while let Some(chunk) = encoder.current_chunk() {
254-
vec.extend_from_slice(chunk);
255-
encoder.advance();
247+
struct TestBytes<'a>(&'a [u8], bool);
248+
249+
impl<'a> Encodable for TestBytes<'a> {
250+
type Encoder<'s>
251+
= BytesEncoder<'s>
252+
where
253+
Self: 's;
254+
255+
fn encoder(&self) -> Self::Encoder<'_> {
256+
if self.1 {
257+
BytesEncoder::with_length_prefix(self.0)
258+
} else {
259+
BytesEncoder::without_length_prefix(self.0)
260+
}
256261
}
257-
vec
262+
}
263+
264+
struct TestArray<const N: usize>([u8; N]);
265+
266+
impl<const N: usize> Encodable for TestArray<N> {
267+
type Encoder<'s>
268+
= ArrayEncoder<N>
269+
where
270+
Self: 's;
271+
272+
fn encoder(&self) -> Self::Encoder<'_> { ArrayEncoder::without_length_prefix(self.0) }
273+
}
274+
275+
#[test]
276+
fn encode_array_with_data() {
277+
// Should have one chunk with the array data, then exhausted.
278+
let test_array = TestArray([1u8, 2, 3, 4]);
279+
let mut encoder = test_array.encoder();
280+
assert_eq!(encoder.current_chunk(), Some(&[1u8, 2, 3, 4][..]));
281+
assert!(!encoder.advance());
282+
assert_eq!(encoder.current_chunk(), None);
283+
}
284+
285+
#[test]
286+
fn encode_empty_array() {
287+
// Empty array should have one empty chunk, then exhausted.
288+
let test_array = TestArray([]);
289+
let mut encoder = test_array.encoder();
290+
assert_eq!(encoder.current_chunk(), Some(&[][..]));
291+
assert!(!encoder.advance());
292+
assert_eq!(encoder.current_chunk(), None);
258293
}
259294

260295
#[test]
261296
fn encode_byte_slice_without_prefix() {
297+
// Should have one chunk with the byte data, then exhausted.
262298
let obj = [1u8, 2, 3];
299+
let test_bytes = TestBytes(&obj, false);
300+
let mut encoder = test_bytes.encoder();
263301

264-
let encoder = BytesEncoder::without_length_prefix(&obj);
265-
let got = run_encoder(encoder);
266-
267-
assert_eq!(got, obj);
302+
assert_eq!(encoder.current_chunk(), Some(&[1u8, 2, 3][..]));
303+
assert!(!encoder.advance());
304+
assert_eq!(encoder.current_chunk(), None);
268305
}
269306

270307
#[test]
271308
fn encode_empty_byte_slice_without_prefix() {
309+
// Should have one empty chunk, then exhausted.
272310
let obj = [];
311+
let test_bytes = TestBytes(&obj, false);
312+
let mut encoder = test_bytes.encoder();
273313

274-
let encoder = BytesEncoder::without_length_prefix(&obj);
275-
let got = run_encoder(encoder);
276-
277-
assert_eq!(got, obj);
314+
assert_eq!(encoder.current_chunk(), Some(&[][..]));
315+
assert!(!encoder.advance());
316+
assert_eq!(encoder.current_chunk(), None);
278317
}
279318

280319
#[test]
281320
fn encode_byte_slice_with_prefix() {
321+
// Should have length prefix chunk, then data chunk, then exhausted.
282322
let obj = [1u8, 2, 3];
283-
284-
let encoder = BytesEncoder::with_length_prefix(&obj);
285-
let got = run_encoder(encoder);
286-
287-
let want = [3u8, 1, 2, 3];
288-
assert_eq!(got, want);
323+
let test_bytes = TestBytes(&obj, true);
324+
let mut encoder = test_bytes.encoder();
325+
326+
assert_eq!(encoder.current_chunk(), Some(&[3u8][..]));
327+
assert!(encoder.advance());
328+
assert_eq!(encoder.current_chunk(), Some(&[1u8, 2, 3][..]));
329+
assert!(!encoder.advance());
330+
assert_eq!(encoder.current_chunk(), None);
289331
}
290332

291333
#[test]
292334
fn encode_empty_byte_slice_with_prefix() {
335+
// Should have length prefix chunk (0), then empty data chunk, then exhausted.
293336
let obj = [];
337+
let test_bytes = TestBytes(&obj, true);
338+
let mut encoder = test_bytes.encoder();
339+
340+
assert_eq!(encoder.current_chunk(), Some(&[0u8][..]));
341+
assert!(encoder.advance());
342+
assert_eq!(encoder.current_chunk(), Some(&[][..]));
343+
assert!(!encoder.advance());
344+
assert_eq!(encoder.current_chunk(), None);
345+
}
346+
347+
#[test]
348+
fn encode_slice_with_elements() {
349+
// Should have length prefix chunk, then element chunks, then exhausted.
350+
let slice = &[TestArray([0x34, 0x12, 0x00, 0x00]), TestArray([0x78, 0x56, 0x00, 0x00])];
351+
let mut encoder = SliceEncoder::with_length_prefix(slice);
352+
353+
assert_eq!(encoder.current_chunk(), Some(&[2u8][..]));
354+
assert!(encoder.advance());
355+
assert_eq!(encoder.current_chunk(), Some(&[0x34, 0x12, 0x00, 0x00][..]));
356+
assert!(encoder.advance());
357+
assert_eq!(encoder.current_chunk(), Some(&[0x78, 0x56, 0x00, 0x00][..]));
358+
assert!(!encoder.advance());
359+
assert_eq!(encoder.current_chunk(), None);
360+
}
294361

295-
let encoder = BytesEncoder::with_length_prefix(&obj);
296-
let got = run_encoder(encoder);
362+
#[test]
363+
fn encode_empty_slice() {
364+
// Should have only length prefix chunk (0), then exhausted.
365+
let slice: &[TestArray<4>] = &[];
366+
let mut encoder = SliceEncoder::with_length_prefix(slice);
367+
368+
assert_eq!(encoder.current_chunk(), Some(&[0u8][..]));
369+
assert!(!encoder.advance());
370+
assert_eq!(encoder.current_chunk(), None);
371+
}
297372

298-
let want = [0u8];
299-
assert_eq!(got, want);
373+
#[test]
374+
fn encode_slice_with_zero_sized_arrays() {
375+
// Should have length prefix chunk, then empty array chunks, then exhausted.
376+
let slice = &[TestArray([]), TestArray([])];
377+
let mut encoder = SliceEncoder::with_length_prefix(slice);
378+
379+
assert_eq!(encoder.current_chunk(), Some(&[2u8][..]));
380+
assert!(encoder.advance());
381+
assert_eq!(encoder.current_chunk(), Some(&[][..]));
382+
assert!(encoder.advance());
383+
assert_eq!(encoder.current_chunk(), Some(&[][..]));
384+
assert!(!encoder.advance());
385+
assert_eq!(encoder.current_chunk(), None);
300386
}
301387
}

0 commit comments

Comments
 (0)