Skip to main content

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<'e>: Encoder
19    where
20        Self: 'e;
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, core::marker::PhantomData<&$lt $encoder>);
56
57        impl<$lt> $name<$lt> {
58            /// Construct a new instance of the newtype encoder
59            pub fn new(encoder: $encoder) -> $name<$lt> {
60                $name(encoder, core::marker::PhantomData)
61            }
62        }
63
64        impl<$lt> $crate::Encoder for $name<$lt> {
65            #[inline]
66            fn current_chunk(&self) -> &[u8] { self.0.current_chunk() }
67
68            #[inline]
69            fn advance(&mut self) -> bool { self.0.advance() }
70        }
71    }
72}
73
74/// Implements a newtype around an exact-size encoder which
75/// implements the [`Encoder`] and [`ExactSizeEncoder`] traits
76/// by forwarding to the wrapped encoder.
77#[macro_export]
78macro_rules! encoder_newtype_exact{
79    (
80        $(#[$($struct_attr:tt)*])*
81        pub struct $name:ident<$lt:lifetime>($encoder:ty);
82    ) => {
83        $crate::encoder_newtype! {
84            $(#[$($struct_attr)*])*
85            pub struct $name<$lt>($encoder);
86        }
87
88        impl<$lt> $crate::ExactSizeEncoder for $name<$lt> {
89            #[inline]
90            fn len(&self) -> usize { self.0.len() }
91        }
92    }
93}
94
95/// Yields bytes from any [`Encodable`] instance.
96pub struct EncodableByteIter<'e, T: Encodable + 'e> {
97    enc: T::Encoder<'e>,
98    position: usize,
99}
100
101impl<'e, T: Encodable + 'e> EncodableByteIter<'e, T> {
102    /// Constructs a new byte iterator around a provided encodable.
103    pub fn new(encodable: &'e T) -> Self { Self { enc: encodable.encoder(), position: 0 } }
104}
105
106impl<'e, T: Encodable + 'e> Iterator for EncodableByteIter<'e, T> {
107    type Item = u8;
108
109    fn next(&mut self) -> Option<Self::Item> {
110        loop {
111            if let Some(b) = self.enc.current_chunk().get(self.position) {
112                self.position += 1;
113                return Some(*b);
114            } else if !self.enc.advance() {
115                return None;
116            }
117            self.position = 0;
118        }
119    }
120}
121
122impl<'e, T> ExactSizeIterator for EncodableByteIter<'e, T>
123where
124    T: Encodable + 'e,
125    T::Encoder<'e>: ExactSizeEncoder,
126{
127    fn len(&self) -> usize { self.enc.len() - self.position }
128}
129
130/// An encoder with a known size.
131pub trait ExactSizeEncoder: Encoder {
132    /// The number of bytes remaining that the encoder will yield.
133    fn len(&self) -> usize;
134
135    /// Returns whether the encoder would yield an empty response.
136    fn is_empty(&self) -> bool { self.len() == 0 }
137}
138
139/// Encodes an object into a vector.
140#[cfg(feature = "alloc")]
141pub fn encode_to_vec<T>(object: &T) -> Vec<u8>
142where
143    T: Encodable + ?Sized,
144{
145    let mut encoder = object.encoder();
146    flush_to_vec(&mut encoder)
147}
148
149/// Flushes the output of an [`Encoder`] into a vector.
150#[cfg(feature = "alloc")]
151pub fn flush_to_vec<T>(encoder: &mut T) -> Vec<u8>
152where
153    T: Encoder + ?Sized,
154{
155    let mut vec = Vec::new();
156    loop {
157        vec.extend_from_slice(encoder.current_chunk());
158        if !encoder.advance() {
159            break;
160        }
161    }
162    vec
163}
164
165/// Encodes an object to a standard I/O writer.
166///
167/// # Performance
168///
169/// This method writes data in potentially small chunks based on the encoder's
170/// internal chunking strategy. For optimal performance with unbuffered writers
171/// (like [`std::fs::File`] or [`std::net::TcpStream`]), consider wrapping your
172/// writer with [`std::io::BufWriter`].
173///
174/// # Errors
175///
176/// Returns any I/O error encountered while writing to the writer.
177#[cfg(feature = "std")]
178pub fn encode_to_writer<T, W>(object: &T, writer: W) -> Result<(), std::io::Error>
179where
180    T: Encodable + ?Sized,
181    W: std::io::Write,
182{
183    let mut encoder = object.encoder();
184    flush_to_writer(&mut encoder, writer)
185}
186
187/// Flushes the output of an [`Encoder`] to a standard I/O writer.
188///
189/// See [`encode_to_writer`] for more information.
190///
191/// # Errors
192///
193/// Returns any I/O error encountered while writing to the writer.
194#[cfg(feature = "std")]
195pub fn flush_to_writer<T, W>(encoder: &mut T, mut writer: W) -> Result<(), std::io::Error>
196where
197    T: Encoder + ?Sized,
198    W: std::io::Write,
199{
200    loop {
201        writer.write_all(encoder.current_chunk())?;
202        if !encoder.advance() {
203            break;
204        }
205    }
206    Ok(())
207}
208
209impl<T: Encoder> Encoder for Option<T> {
210    fn current_chunk(&self) -> &[u8] {
211        match self {
212            Some(encoder) => encoder.current_chunk(),
213            None => &[],
214        }
215    }
216
217    fn advance(&mut self) -> bool {
218        match self {
219            Some(encoder) => encoder.advance(),
220            None => false,
221        }
222    }
223}