bitcoin_consensus_encoding/encode/
mod.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Consensus Encoding Traits
4
5#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7
8pub mod encoders;
9
10/// A Bitcoin object which can be consensus-encoded.
11///
12/// To encode something, use the [`Self::encoder`] method to obtain a
13/// [`Self::Encoder`], which will behave like an iterator yielding
14/// byte slices.
15pub trait Encodable {
16    /// The encoder associated with this type. Conceptually, the encoder is like
17    /// an iterator which yields byte slices.
18    type Encoder<'s>: Encoder
19    where
20        Self: 's;
21
22    /// Constructs a "default encoder" for the type.
23    fn encoder(&self) -> Self::Encoder<'_>;
24}
25
26/// An encoder for a consensus-encodable object.
27pub trait Encoder {
28    /// Yields the current encoded byteslice.
29    ///
30    /// Will always return the same value until [`Self::advance`] is called. May return an empty
31    /// list.
32    fn current_chunk(&self) -> &[u8];
33
34    /// Moves the encoder to its next state.
35    ///
36    /// Does not need to be called when the encoder is first created. (In fact, if it
37    /// is called, this will discard the first chunk of encoded data.)
38    ///
39    /// # Returns
40    ///
41    /// - `true` if the encoder has advanced to a new state and [`Self::current_chunk`] will return new data.
42    /// - `false` if the encoder is exhausted and has no more states.
43    fn advance(&mut self) -> bool;
44}
45
46/// Implements a newtype around an encoder which implements the
47/// [`Encoder`] trait by forwarding to the wrapped encoder.
48#[macro_export]
49macro_rules! encoder_newtype{
50    (
51        $(#[$($struct_attr:tt)*])*
52        pub struct $name:ident$(<$lt:lifetime>)?($encoder:ty);
53    ) => {
54        $(#[$($struct_attr)*])*
55        pub struct $name$(<$lt>)?($encoder);
56
57        impl$(<$lt>)? $crate::Encoder for $name$(<$lt>)? {
58            #[inline]
59            fn current_chunk(&self) -> &[u8] { self.0.current_chunk() }
60
61            #[inline]
62            fn advance(&mut self) -> bool { self.0.advance() }
63        }
64    }
65}
66
67/// Encodes an object into a vector.
68#[cfg(feature = "alloc")]
69pub fn encode_to_vec<T>(object: &T) -> Vec<u8>
70where
71    T: Encodable + ?Sized,
72{
73    let mut encoder = object.encoder();
74    let mut vec = Vec::new();
75    loop {
76        vec.extend_from_slice(encoder.current_chunk());
77        if !encoder.advance() {
78            break;
79        }
80    }
81    vec
82}
83
84/// Encodes an object to a standard I/O writer.
85///
86/// # Performance
87///
88/// This method writes data in potentially small chunks based on the encoder's
89/// internal chunking strategy. For optimal performance with unbuffered writers
90/// (like [`std::fs::File`] or [`std::net::TcpStream`]), consider wrapping your
91/// writer with [`std::io::BufWriter`].
92///
93/// # Errors
94///
95/// Returns any I/O error encountered while writing to the writer.
96#[cfg(feature = "std")]
97pub fn encode_to_writer<T, W>(object: &T, mut writer: W) -> Result<(), std::io::Error>
98where
99    T: Encodable + ?Sized,
100    W: std::io::Write,
101{
102    let mut encoder = object.encoder();
103    loop {
104        writer.write_all(encoder.current_chunk())?;
105        if !encoder.advance() {
106            break;
107        }
108    }
109    Ok(())
110}
111
112impl<T: Encoder> Encoder for Option<T> {
113    fn current_chunk(&self) -> &[u8] {
114        match self {
115            Some(encoder) => encoder.current_chunk(),
116            None => &[],
117        }
118    }
119
120    fn advance(&mut self) -> bool {
121        match self {
122            Some(encoder) => encoder.advance(),
123            None => false,
124        }
125    }
126}