Skip to main content

bitcoin_consensus_encoding/encode/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3#[cfg(feature = "alloc")]
4#[cfg(feature = "hex")]
5use alloc::string::String;
6#[cfg(feature = "alloc")]
7use alloc::vec::Vec;
8
9pub mod encoders;
10pub mod iter;
11
12/// A Bitcoin object which can be consensus encoded.
13///
14/// To encode something, use the [`Self::encoder`] method to obtain a [`Self::Encoder`], which will
15/// behave like an iterator yielding byte slices.
16///
17/// # Examples
18///
19/// ```
20/// # #[cfg(feature = "alloc")] {
21/// use bitcoin_consensus_encoding::{encoder_newtype, encode_to_vec, Encode, ArrayEncoder};
22///
23/// struct Foo([u8; 4]);
24///
25/// encoder_newtype! {
26///     pub struct FooEncoder<'e>(ArrayEncoder<4>);
27/// }
28///
29/// impl Encode for Foo {
30///     type Encoder<'e> = FooEncoder<'e> where Self: 'e;
31///
32///     fn encoder(&self) -> Self::Encoder<'_> {
33///         FooEncoder::new(ArrayEncoder::without_length_prefix(self.0))
34///     }
35/// }
36///
37/// let foo = Foo([0xde, 0xad, 0xbe, 0xef]);
38/// assert_eq!(encode_to_vec(&foo), vec![0xde, 0xad, 0xbe, 0xef]);
39/// # }
40/// ```
41pub trait Encode {
42    /// The encoder associated with this type. Conceptually, the encoder is like
43    /// an iterator which yields byte slices.
44    type Encoder<'e>: Encoder
45    where
46        Self: 'e;
47
48    /// Constructs a "default encoder" for the type.
49    fn encoder(&self) -> Self::Encoder<'_>;
50}
51
52/// A pull based encoder that yields bytes in chunks.
53///
54/// The consumers of a type implementing this encoder trait should generally use it in a loop like
55/// this:
56///
57/// ```no-compile
58/// loop {
59///     process_current_chunk(encoder.current_chunk());
60///     if encoder.advance().is_finished() {
61///         break
62///     }
63/// }
64/// // do NOT use encoder after this point
65/// ```
66///
67/// Processing the chunks in an equivalent state machine (presumably future) is also permissible.
68///
69/// It is crucial that the callers use the methods in that order: obtain the slice via
70/// `current_chunk`, write it somewhere and, once fully written, try to advance the encoder.
71/// Attempting to call any method after [`advance`](Self::advance) returned
72/// `EncoderStatus::Finished` or calling `advance` before fully processing the chunks will lead to
73/// unspecified buggy behavior.
74///
75/// The callers MUST NOT assume that the encoder returns any particular size of the chunks. The
76/// implementors are allowed to change the sizes of the chunks as long as the concatenation of all
77/// the bytes returned stays the same.
78pub trait Encoder {
79    /// Yields the current encoded byte slice.
80    ///
81    /// Will always return the same value until [`Self::advance`] is called.
82    /// May return an empty slice, however implementors should avoid returning empty slices unless
83    /// the encoded type is truly empty.
84    fn current_chunk(&self) -> &[u8];
85
86    /// Moves the encoder to its next state.
87    ///
88    /// Does not need to be called when the encoder is first created. (In fact, if it
89    /// is called, this will discard the first chunk of encoded data.)
90    ///
91    /// # Returns
92    ///
93    /// - `EncoderStatus::HasMore` if the encoder has advanced to a new state and [`Self::current_chunk`] will return new data.
94    /// - `EncoderStatus::Finished` if the encoder is exhausted and has no more states.
95    ///
96    /// # Important
97    ///
98    /// After `EncoderStatus::Finished` was returned the encoder is in unspecified state. Calling
99    /// any of its methods in such state is a bug (but not UB) unless the specific encoder documents
100    /// otherwise. While usually the encoder simply stays in the last possible state this MUST NOT
101    /// be relied upon by the callers.
102    fn advance(&mut self) -> EncoderStatus;
103}
104
105/// Indicates whether the encoder still has bytes available or it is finished.
106///
107/// This is returned from the [`Encoder::advance`] method to indicate whether encoding should stop
108/// or continue.
109#[derive(Debug, Copy, Clone, Eq, PartialEq)]
110#[must_use = "encoding has to stop when Finished is returned"]
111pub enum EncoderStatus {
112    /// The encoder has more bytes available (not yet finished).
113    ///
114    /// The [`current_chunk`](Encoder::current_chunk) method should be called to obtain them and
115    /// write them out after which [`advance`](Encoder::advance) should be called again to obtain
116    /// the next chunk (if any).
117    HasMore,
118
119    /// The encoding has ended, no more bytes are available.
120    ///
121    /// No encoder methods (other than drop) may be called after this variant is returned.
122    Finished,
123}
124
125impl EncoderStatus {
126    /// Returns `true` if `self` is `HasMore`, `false` otherwise.
127    pub fn has_more(&self) -> bool { matches!(self, Self::HasMore) }
128
129    /// Returns `true` if `self` is `Finished`, `false` otherwise.
130    pub fn has_finished(&self) -> bool { matches!(self, Self::Finished) }
131}
132
133/// Implements a newtype around an encoder.
134///
135/// The new type will implement the [`Encoder`] trait by forwarding to the wrapped encoder. If your
136/// type has a known size consider using [`crate::encoder_newtype_exact`] instead.
137///
138/// # Examples
139/// ```
140/// use bitcoin_consensus_encoding::{encoder_newtype, BytesEncoder};
141///
142/// encoder_newtype! {
143///     /// The encoder for the [`Foo`] type.
144///     pub struct FooEncoder<'e>(BytesEncoder<'e>);
145/// }
146/// ```
147///
148/// For a full example see `./examples/encoder.rs`.
149#[macro_export]
150macro_rules! encoder_newtype {
151    (
152        $(#[$($struct_attr:tt)*])*
153        $vis:vis struct $name:ident<$lt:lifetime>($encoder:ty);
154    ) => {
155        $(#[$($struct_attr)*])*
156        $vis struct $name<$lt>($encoder, core::marker::PhantomData<&$lt $encoder>);
157
158        #[allow(clippy::type_complexity)]
159        impl<$lt> $name<$lt> {
160            /// Constructs a new instance of the newtype encoder.
161            pub(crate) const fn new(encoder: $encoder) -> $name<$lt> {
162                $name(encoder, core::marker::PhantomData)
163            }
164        }
165
166        impl<$lt> $crate::Encoder for $name<$lt> {
167            #[inline]
168            fn current_chunk(&self) -> &[u8] { self.0.current_chunk() }
169
170            #[inline]
171            fn advance(&mut self) -> $crate::EncoderStatus { self.0.advance() }
172        }
173    }
174}
175
176/// Implements a newtype around an exact-size encoder.
177///
178/// The new type will implement both the [`Encoder`] and [`ExactSizeEncoder`] traits
179/// by forwarding to the wrapped encoder.
180///
181/// # Examples
182/// ```
183/// use bitcoin_consensus_encoding::{encoder_newtype_exact, ArrayEncoder};
184///
185/// encoder_newtype_exact! {
186///     /// The encoder for the [`Bar`] type.
187///     pub struct BarEncoder<'e>(ArrayEncoder<32>);
188/// }
189/// ```
190///
191/// For a full example see `./examples/encoder.rs`.
192#[macro_export]
193macro_rules! encoder_newtype_exact {
194    (
195        $(#[$($struct_attr:tt)*])*
196        $vis:vis struct $name:ident<$lt:lifetime>($encoder:ty);
197    ) => {
198        $crate::encoder_newtype! {
199            $(#[$($struct_attr)*])*
200            $vis struct $name<$lt>($encoder);
201        }
202
203        impl<$lt> $crate::ExactSizeEncoder for $name<$lt> {
204            #[inline]
205            fn len(&self) -> usize { self.0.len() }
206        }
207    }
208}
209
210/// Yields bytes from any [`Encoder`] instance.
211///
212/// **Important** this iterator is **not** fused! Call `fuse` if you need it to be fused.
213#[derive(Debug, Clone)]
214pub struct EncoderByteIter<T: Encoder> {
215    enc: T,
216    position: usize,
217}
218
219impl<T: Encoder> EncoderByteIter<T> {
220    /// Constructs a new byte iterator around a provided encoder.
221    pub fn new(encoder: T) -> Self { Self { enc: encoder, position: 0 } }
222
223    /// Returns the remaining bytes in the next non-empty chunk.
224    ///
225    /// The returned value is either a non-empty chunk of bytes that were not yielded yet,
226    /// immediately following the already-yielded bytes or empty slice if the encoder finished.
227    ///
228    /// This call can be paired with `nth` to mark bytes as processed.
229    ///
230    /// Just like with encoders or this iterator, attempting to use this type after this method
231    /// returned an empty slice will lead to unspecified behavior and is considered a bug in the
232    /// caller.
233    pub fn peek_chunk(&mut self) -> &[u8] {
234        // Can't use `.get(self.position..)` due to borrowck bug.
235        if self.position < self.enc.current_chunk().len() {
236            &self.enc.current_chunk()[self.position..]
237        } else {
238            loop {
239                if self.enc.advance().has_finished() {
240                    return &[];
241                }
242                if !self.enc.current_chunk().is_empty() {
243                    self.position = 0;
244                    return self.enc.current_chunk();
245                }
246            }
247        }
248    }
249}
250
251impl<T: Encoder> Iterator for EncoderByteIter<T> {
252    type Item = u8;
253
254    fn next(&mut self) -> Option<Self::Item> {
255        loop {
256            if let Some(b) = self.enc.current_chunk().get(self.position) {
257                // length of slice is guaranteed to be at most `isize::MAX` thus is `n` so this cannot
258                // overflow.
259                self.position += 1;
260                return Some(*b);
261            } else if self.enc.advance().has_finished() {
262                return None;
263            }
264            self.position = 0;
265        }
266    }
267
268    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
269        // This could be in a loop but we intentionally unroll one iteration so that addition is
270        // only required at the beginning.
271        if let Some(b) =
272            self.position.checked_add(n).and_then(|pos| self.enc.current_chunk().get(pos))
273        {
274            // length of slice is guaranteed to be at most `isize::MAX` thus is `n` so this cannot
275            // overflow.
276            self.position += n + 1;
277            return Some(*b);
278        }
279        n -= self.enc.current_chunk().len() - self.position;
280        if self.enc.advance().has_finished() {
281            return None;
282        }
283        loop {
284            if let Some(b) = self.enc.current_chunk().get(n) {
285                self.position = n + 1;
286                return Some(*b);
287            }
288            n -= self.enc.current_chunk().len();
289            if self.enc.advance().has_finished() {
290                return None;
291            }
292        }
293    }
294}
295
296impl<T> ExactSizeIterator for EncoderByteIter<T>
297where
298    T: Encoder + ExactSizeEncoder,
299{
300    fn len(&self) -> usize { self.enc.len() - self.position }
301}
302
303/// An encoder with a known size.
304pub trait ExactSizeEncoder: Encoder {
305    /// The number of bytes *remaining* that the encoder will yield.
306    ///
307    /// There is no requirement for this function to be constant time.
308    ///
309    /// **Important**: returns an unspecified value if [`Encoder::advance`] has returned
310    /// `EncoderStatus::Finished`.
311    fn len(&self) -> usize;
312
313    /// Returns whether the encoder would yield an empty response.
314    ///
315    /// **Important**: returns an unspecified value if [`Encoder::advance`] has returned
316    /// `EncoderStatus::Finished`.
317    fn is_empty(&self) -> bool { self.len() == 0 }
318}
319
320/// Encodes a consensus encodable type into a vector.
321#[cfg(feature = "alloc")]
322pub fn encode_to_vec<T>(object: &T) -> Vec<u8>
323where
324    T: Encode + ?Sized,
325{
326    let mut encoder = object.encoder();
327    drain_to_vec(&mut encoder)
328}
329
330/// Drains the output of an [`Encoder`] into a vector.
331#[cfg(feature = "alloc")]
332pub fn drain_to_vec<T>(encoder: &mut T) -> Vec<u8>
333where
334    T: Encoder + ?Sized,
335{
336    let mut vec = Vec::new();
337    loop {
338        vec.extend_from_slice(encoder.current_chunk());
339        if encoder.advance().has_finished() {
340            break;
341        }
342    }
343    vec
344}
345
346/// Encodes a consensus encodable type into a hex string.
347#[cfg(feature = "alloc")]
348#[cfg(feature = "hex")]
349pub fn encode_to_hex<T>(object: &T, case: hex::Case) -> String
350where
351    T: Encode + ?Sized,
352{
353    drain_to_hex(object.encoder(), case)
354}
355
356/// Drains the output of an [`Encoder`] into a hex string.
357#[cfg(feature = "alloc")]
358#[cfg(feature = "hex")]
359pub fn drain_to_hex<T>(encoder: T, case: hex::Case) -> String
360where
361    T: Encoder,
362{
363    let iter = EncoderByteIter::new(encoder);
364    let hex_iter = hex::BytesToHexIter::new(iter, case);
365    hex_iter.flatten().map(char::from).collect()
366}
367
368/// Encodes a consensus encodable type to a standard I/O writer.
369///
370/// # Performance
371///
372/// This method writes data in potentially small chunks based on the encoder's internal chunking
373/// strategy. For optimal performance with unbuffered writers (like [`std::fs::File`] or
374/// [`std::net::TcpStream`]), consider wrapping your writer with [`std::io::BufWriter`].
375///
376/// # Errors
377///
378/// Returns any I/O error encountered while writing to the writer.
379#[cfg(feature = "std")]
380pub fn encode_to_writer<T, W>(object: &T, writer: W) -> Result<(), std::io::Error>
381where
382    T: Encode + ?Sized,
383    W: std::io::Write,
384{
385    let mut encoder = object.encoder();
386    drain_to_writer(&mut encoder, writer)
387}
388
389/// Drains the output of an [`Encoder`] to a standard I/O writer.
390///
391/// See [`encode_to_writer`] for more information.
392///
393/// # Errors
394///
395/// Returns any I/O error encountered while writing to the writer.
396#[cfg(feature = "std")]
397pub fn drain_to_writer<T, W>(encoder: &mut T, mut writer: W) -> Result<(), std::io::Error>
398where
399    T: Encoder + ?Sized,
400    W: std::io::Write,
401{
402    loop {
403        writer.write_all(encoder.current_chunk())?;
404        if encoder.advance().has_finished() {
405            break;
406        }
407    }
408    Ok(())
409}
410
411/// Checks that a consensus encodable `value` encodes to `expected`, panicking if it doesn't.
412///
413/// Note that the function does not impose any requirements on chunking - whether the encoded bytes
414/// are returned as a few large chunks or they are many smaller chunks makes no difference (other
415/// than potentially performance difference), as long as the bytes yielded are what is expected, in
416/// the correct order.
417///
418/// This is intended for tests only.
419///
420/// # Panics
421///
422/// If the bytes yielded from the encoder of `value` don't match the bytes in `expected`.
423#[track_caller]
424pub fn check_encode<T: Encode + ?Sized>(value: &T, expected: &[u8]) {
425    check_encoder(&mut value.encoder(), expected);
426}
427
428/// Checks that the given `encoder` yields `expected`, panicking if it doesn't.
429///
430/// Note that the function does not impose any requirements on chunking - whether the encoded bytes
431/// are returned as a few large chunks or they are many smaller chunks makes no difference (other
432/// than potentially performance difference), as long as the bytes yielded are what is expected, in
433/// the correct order.
434///
435/// This is intended for tests only.
436///
437/// # Panics
438///
439/// If the bytes yielded from the encoder don't match the bytes in `expected`.
440#[track_caller]
441pub fn check_encoder<T: Encoder + ?Sized>(encoder: &mut T, mut expected: &[u8]) {
442    let orig_expected_len = expected.len();
443    let mut chunk_number = 0usize;
444    let mut bytes_processed = 0usize;
445
446    loop {
447        let chunk = encoder.current_chunk();
448        assert!(
449            chunk.len() <= expected.len(),
450            "encoder yielded more bytes ({}) than expected ({})",
451            bytes_processed + chunk.len(),
452            orig_expected_len
453        );
454        if let Some((i, _)) =
455            chunk.iter().zip(&expected[..chunk.len()]).enumerate().find(|&(_, (a, b))| a != b)
456        {
457            panic!(
458                "encoder did not yield expected bytes - difference in chunk #{}, after {} bytes",
459                chunk_number,
460                bytes_processed + i
461            );
462        }
463        bytes_processed += chunk.len();
464        expected = &expected[chunk.len()..];
465        chunk_number += 1;
466        if encoder.advance().has_finished() {
467            break;
468        }
469    }
470    assert!(
471        expected.is_empty(),
472        "encoder did not yield enough bytes - {} more expected",
473        expected.len()
474    );
475}
476
477impl<T: Encoder> Encoder for Option<T> {
478    fn current_chunk(&self) -> &[u8] { self.as_ref().map_or(&[], Encoder::current_chunk) }
479
480    fn advance(&mut self) -> EncoderStatus {
481        self.as_mut().map_or(EncoderStatus::Finished, Encoder::advance)
482    }
483}
484
485impl<T: ExactSizeEncoder> ExactSizeEncoder for Option<T> {
486    fn len(&self) -> usize { self.as_ref().map_or(0, T::len) }
487}