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/// Yields bytes from any [`Encodable`] instance.
68pub struct EncodableByteIter<'s, T: Encodable + 's> {
69 enc: T::Encoder<'s>,
70 position: usize,
71}
72
73impl<'s, T: Encodable + 's> EncodableByteIter<'s, T> {
74 /// Constructs a new byte iterator around a provided encodable.
75 pub fn new(encodable: &'s T) -> Self { Self { enc: encodable.encoder(), position: 0 } }
76}
77
78impl<'s, T: Encodable + 's> Iterator for EncodableByteIter<'s, T> {
79 type Item = u8;
80
81 fn next(&mut self) -> Option<Self::Item> {
82 loop {
83 if let Some(b) = self.enc.current_chunk().get(self.position) {
84 self.position += 1;
85 return Some(*b);
86 } else if !self.enc.advance() {
87 return None;
88 }
89 self.position = 0;
90 }
91 }
92}
93
94/// Encodes an object into a vector.
95#[cfg(feature = "alloc")]
96pub fn encode_to_vec<T>(object: &T) -> Vec<u8>
97where
98 T: Encodable + ?Sized,
99{
100 let mut encoder = object.encoder();
101 let mut vec = Vec::new();
102 loop {
103 vec.extend_from_slice(encoder.current_chunk());
104 if !encoder.advance() {
105 break;
106 }
107 }
108 vec
109}
110
111/// Encodes an object to a standard I/O writer.
112///
113/// # Performance
114///
115/// This method writes data in potentially small chunks based on the encoder's
116/// internal chunking strategy. For optimal performance with unbuffered writers
117/// (like [`std::fs::File`] or [`std::net::TcpStream`]), consider wrapping your
118/// writer with [`std::io::BufWriter`].
119///
120/// # Errors
121///
122/// Returns any I/O error encountered while writing to the writer.
123#[cfg(feature = "std")]
124pub fn encode_to_writer<T, W>(object: &T, mut writer: W) -> Result<(), std::io::Error>
125where
126 T: Encodable + ?Sized,
127 W: std::io::Write,
128{
129 let mut encoder = object.encoder();
130 loop {
131 writer.write_all(encoder.current_chunk())?;
132 if !encoder.advance() {
133 break;
134 }
135 }
136 Ok(())
137}
138
139impl<T: Encoder> Encoder for Option<T> {
140 fn current_chunk(&self) -> &[u8] {
141 match self {
142 Some(encoder) => encoder.current_chunk(),
143 None => &[],
144 }
145 }
146
147 fn advance(&mut self) -> bool {
148 match self {
149 Some(encoder) => encoder.advance(),
150 None => false,
151 }
152 }
153}