@@ -57,7 +57,7 @@ impl<'i> Decoder<'i> {
5757 Ok ( buf[ 0 ] )
5858 }
5959
60- /// Decodes a `uint32` as described in [RFC4251 § 5]:
60+ /// Decode a `uint32` as described in [RFC4251 § 5]:
6161 ///
6262 /// > Represents a 32-bit unsigned integer. Stored as four bytes in the
6363 /// > order of decreasing significance (network byte order).
@@ -113,7 +113,7 @@ impl<'i> Decoder<'i> {
113113 Ok ( result)
114114 }
115115
116- /// Decodes a `string` as described in [RFC4251 § 5]:
116+ /// Decode a `string` as described in [RFC4251 § 5]:
117117 ///
118118 /// > Arbitrary length binary string. Strings are allowed to contain
119119 /// > arbitrary binary data, including null characters and 8-bit
@@ -146,9 +146,97 @@ impl<'i> Decoder<'i> {
146146 }
147147}
148148
149+ /// Encoder trait.
150+ pub ( crate ) trait Encode : Sized {
151+ /// Get the length of this type encoded in bytes, prior to Base64 encoding.
152+ fn encoded_len ( & self ) -> Result < usize > ;
153+
154+ /// Attempt to encode a value of this type using the provided [`Encoder`].
155+ fn encode ( & self , encoder : & mut Encoder < ' _ > ) -> Result < ( ) > ;
156+ }
157+
158+ /// Stateful Base64 encoder.
159+ pub ( crate ) struct Encoder < ' o > {
160+ inner : base64ct:: Encoder < ' o , base64ct:: Base64 > ,
161+ }
162+
163+ impl < ' o > Encoder < ' o > {
164+ /// Create a new decoder for a byte slice containing contiguous
165+ /// (non-newline-delimited) Base64-encoded data.
166+ pub ( crate ) fn new ( buffer : & ' o mut [ u8 ] ) -> Result < Self > {
167+ Ok ( Self {
168+ inner : base64ct:: Encoder :: new ( buffer) ?,
169+ } )
170+ }
171+
172+ /// Encode the given byte slice as Base64.
173+ pub ( crate ) fn encode ( & mut self , bytes : & [ u8 ] ) -> Result < ( ) > {
174+ Ok ( self . inner . encode ( bytes) ?)
175+ }
176+
177+ /// Encode a `uint32` as described in [RFC4251 § 5]:
178+ ///
179+ /// > Represents a 32-bit unsigned integer. Stored as four bytes in the
180+ /// > order of decreasing significance (network byte order).
181+ /// > For example: the value 699921578 (0x29b7f4aa) is stored as 29 b7 f4 aa.
182+ ///
183+ /// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
184+ pub ( crate ) fn encode_u32 ( & mut self , num : u32 ) -> Result < ( ) > {
185+ self . encode ( & num. to_be_bytes ( ) )
186+ }
187+
188+ /// Encode a `usize` as a `uint32` as described in [RFC4251 § 5].
189+ ///
190+ /// Uses [`Encoder::encode_u32`] after converting from a `usize`, handling
191+ /// potential overflow if `usize` is bigger than `u32`.
192+ ///
193+ /// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
194+ pub ( crate ) fn encode_usize ( & mut self , num : usize ) -> Result < ( ) > {
195+ self . encode_u32 ( u32:: try_from ( num) ?)
196+ }
197+
198+ /// Encodes `[u8]` into `byte[n]` as described in [RFC4251 § 5]:
199+ ///
200+ /// > A byte represents an arbitrary 8-bit value (octet). Fixed length
201+ /// > data is sometimes represented as an array of bytes, written
202+ /// > byte[n], where n is the number of bytes in the array.
203+ ///
204+ /// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
205+ pub ( crate ) fn encode_byte_slice ( & mut self , bytes : & [ u8 ] ) -> Result < ( ) > {
206+ self . encode_usize ( bytes. len ( ) ) ?;
207+ self . encode ( bytes)
208+ }
209+
210+ /// Encode a `string` as described in [RFC4251 § 5]:
211+ ///
212+ /// > Arbitrary length binary string. Strings are allowed to contain
213+ /// > arbitrary binary data, including null characters and 8-bit
214+ /// > characters. They are stored as a uint32 containing its length
215+ /// > (number of bytes that follow) and zero (= empty string) or more
216+ /// > bytes that are the value of the string. Terminating null
217+ /// > characters are not used.
218+ /// >
219+ /// > Strings are also used to store text. In that case, US-ASCII is
220+ /// > used for internal names, and ISO-10646 UTF-8 for text that might
221+ /// > be displayed to the user. The terminating null character SHOULD
222+ /// > NOT normally be stored in the string. For example: the US-ASCII
223+ /// > string "testing" is represented as 00 00 00 07 t e s t i n g. The
224+ /// > UTF-8 mapping does not alter the encoding of US-ASCII characters.
225+ ///
226+ /// [RFC4251 § 5]: https://datatracker.ietf.org/doc/html/rfc4251#section-5
227+ pub ( crate ) fn encode_str ( & mut self , s : & str ) -> Result < ( ) > {
228+ self . encode_byte_slice ( s. as_bytes ( ) )
229+ }
230+
231+ /// Finish encoding, returning the encoded Base64 as a `str`.
232+ pub ( crate ) fn finish ( self ) -> Result < & ' o str > {
233+ Ok ( self . inner . finish ( ) ?)
234+ }
235+ }
236+
149237#[ cfg( test) ]
150238mod tests {
151- use super :: Decoder ;
239+ use super :: { Decoder , Encoder } ;
152240
153241 /// From `id_ecdsa_p256.pub`
154242 const EXAMPLE_BASE64 : & str =
@@ -168,4 +256,12 @@ mod tests {
168256 let decoded = decoder. decode_into ( & mut buf) . unwrap ( ) ;
169257 assert_eq ! ( EXAMPLE_BIN , decoded) ;
170258 }
259+
260+ #[ test]
261+ fn encode ( ) {
262+ let mut buffer = [ 0u8 ; EXAMPLE_BASE64 . len ( ) ] ;
263+ let mut encoder = Encoder :: new ( & mut buffer) . unwrap ( ) ;
264+ encoder. encode ( EXAMPLE_BIN ) . unwrap ( ) ;
265+ assert_eq ! ( EXAMPLE_BASE64 , encoder. finish( ) . unwrap( ) ) ;
266+ }
171267}
0 commit comments