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}