bs58/
encode.rs

1//! Functions for encoding into Base58 encoded strings.
2
3use core::fmt;
4
5#[cfg(feature = "alloc")]
6use alloc::{string::String, vec::Vec};
7
8use crate::Check;
9#[cfg(any(feature = "check", feature = "cb58"))]
10use crate::CHECKSUM_LEN;
11
12use crate::Alphabet;
13
14/// A builder for setting up the alphabet and output of a base58 encode.
15#[allow(missing_debug_implementations)]
16pub struct EncodeBuilder<'a, I: AsRef<[u8]>> {
17    input: I,
18    alpha: &'a Alphabet,
19    check: Check,
20}
21
22/// A specialized [`Result`](core::result::Result) type for [`bs58::encode`](module@crate::encode)
23pub type Result<T> = core::result::Result<T, Error>;
24
25/// Errors that could occur when encoding a Base58 encoded string.
26#[derive(Copy, Clone, Debug, Eq, PartialEq)]
27#[non_exhaustive]
28pub enum Error {
29    /// The output buffer was too small to contain the entire input.
30    BufferTooSmall,
31}
32
33/// Represents a buffer that can be encoded into. See [`EncodeBuilder::onto`] and the provided
34/// implementations for more details.
35pub trait EncodeTarget {
36    /// Encodes into this buffer, provides the maximum length for implementations that wish to
37    /// preallocate space, along with a function that will encode ASCII bytes into the buffer and
38    /// return the length written to it.
39    fn encode_with(
40        &mut self,
41        max_len: usize,
42        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
43    ) -> Result<usize>;
44}
45
46impl<T: EncodeTarget + ?Sized> EncodeTarget for &mut T {
47    fn encode_with(
48        &mut self,
49        max_len: usize,
50        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
51    ) -> Result<usize> {
52        T::encode_with(self, max_len, f)
53    }
54}
55
56#[cfg(feature = "alloc")]
57impl EncodeTarget for Vec<u8> {
58    fn encode_with(
59        &mut self,
60        max_len: usize,
61        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
62    ) -> Result<usize> {
63        let original = self.len();
64        self.resize(original + max_len, 0);
65        let len = f(&mut self[original..])?;
66        self.truncate(original + len);
67        Ok(len)
68    }
69}
70
71#[cfg(feature = "smallvec")]
72impl<A: smallvec::Array<Item = u8>> EncodeTarget for smallvec::SmallVec<A> {
73    /// Encodes data into a [`smallvec::SmallVec`].
74    ///
75    /// Note that even if the encoded value fits into vector’s inline buffer,
76    /// this may result in allocation if `max_len` is greater than vector’s
77    /// inline size.  To make sure that the inline buffer is enough for N-byte
78    /// buffer encoded in base58, use smallvec with ⌈N*1.5⌉-byte long inline
79    /// buffer (or ⌈(N+5)*1.5⌉ if version and checksum are included).
80    fn encode_with(
81        &mut self,
82        max_len: usize,
83        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
84    ) -> Result<usize> {
85        let original = self.len();
86        self.resize(original + max_len, 0);
87        let len = f(&mut self[original..])?;
88        self.truncate(original + len);
89        Ok(len)
90    }
91}
92
93#[cfg(feature = "tinyvec")]
94impl<A: tinyvec::Array<Item = u8>> EncodeTarget for tinyvec::ArrayVec<A> {
95    fn encode_with(
96        &mut self,
97        max_len: usize,
98        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
99    ) -> Result<usize> {
100        let _ = max_len;
101        let original = self.len();
102        let len = f(self.grab_spare_slice_mut())?;
103        self.set_len(original + len);
104        Ok(len)
105    }
106}
107
108#[cfg(feature = "tinyvec")]
109impl EncodeTarget for tinyvec::SliceVec<'_, u8> {
110    fn encode_with(
111        &mut self,
112        max_len: usize,
113        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
114    ) -> Result<usize> {
115        let _ = max_len;
116        let original = self.len();
117        let len = f(self.grab_spare_slice_mut())?;
118        self.set_len(original + len);
119        Ok(len)
120    }
121}
122
123#[cfg(all(feature = "tinyvec", feature = "alloc"))]
124impl<A: tinyvec::Array<Item = u8>> EncodeTarget for tinyvec::TinyVec<A> {
125    fn encode_with(
126        &mut self,
127        max_len: usize,
128        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
129    ) -> Result<usize> {
130        let original = self.len();
131        self.resize(original + max_len, 0);
132        let len = f(&mut self[original..])?;
133        self.truncate(original + len);
134        Ok(len)
135    }
136}
137
138#[cfg(feature = "alloc")]
139impl EncodeTarget for String {
140    fn encode_with(
141        &mut self,
142        max_len: usize,
143        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
144    ) -> Result<usize> {
145        let mut output = core::mem::take(self).into_bytes();
146        let len = output.encode_with(max_len, f)?;
147        *self = String::from_utf8(output).unwrap();
148        Ok(len)
149    }
150}
151
152impl EncodeTarget for [u8] {
153    fn encode_with(
154        &mut self,
155        max_len: usize,
156        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
157    ) -> Result<usize> {
158        let _ = max_len;
159        f(&mut *self)
160    }
161}
162
163impl EncodeTarget for str {
164    fn encode_with(
165        &mut self,
166        max_len: usize,
167        f: impl for<'a> FnOnce(&'a mut [u8]) -> Result<usize>,
168    ) -> Result<usize> {
169        struct Guard<'a>(&'a mut [u8]);
170
171        impl Drop for Guard<'_> {
172            fn drop(&mut self) {
173                let mut index = 0;
174                loop {
175                    match core::str::from_utf8(&self.0[index..]) {
176                        Ok(_) => return,
177                        Err(e) => {
178                            index += e.valid_up_to();
179                            if let Some(len) = e.error_len() {
180                                for i in &mut self.0[index..index + len] {
181                                    *i = 0;
182                                }
183                                index += len;
184                            } else {
185                                for i in &mut self.0[index..] {
186                                    *i = 0;
187                                }
188                                index += self.0[index..].len();
189                            }
190                        }
191                    }
192                }
193            }
194        }
195
196        let _ = max_len;
197
198        #[allow(unsafe_code)]
199        // SAFETY: before returning the guard will be dropped and ensure the slice is valid utf-8
200        // by replacing invalid bytes with nul-bytes
201        let guard = Guard(unsafe { self.as_bytes_mut() });
202        f(&mut *guard.0)
203    }
204}
205
206impl<'a, I: AsRef<[u8]>> EncodeBuilder<'a, I> {
207    /// Setup encoder for the given string using the given alphabet.
208    /// Preferably use [`bs58::encode`](crate::encode()) instead of this
209    /// directly.
210    pub fn new(input: I, alpha: &'a Alphabet) -> EncodeBuilder<'a, I> {
211        EncodeBuilder {
212            input,
213            alpha,
214            check: Check::Disabled,
215        }
216    }
217
218    /// Setup encoder for the given string using default prepared alphabet.
219    pub(crate) fn from_input(input: I) -> EncodeBuilder<'static, I> {
220        EncodeBuilder {
221            input,
222            alpha: Alphabet::DEFAULT,
223            check: Check::Disabled,
224        }
225    }
226
227    /// Change the alphabet that will be used for encoding.
228    ///
229    /// # Examples
230    ///
231    /// ```rust
232    /// let input = [0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78];
233    /// assert_eq!(
234    ///     "he11owor1d",
235    ///     bs58::encode(input)
236    ///         .with_alphabet(bs58::Alphabet::RIPPLE)
237    ///         .into_string());
238    /// ```
239    pub fn with_alphabet(self, alpha: &'a Alphabet) -> EncodeBuilder<'a, I> {
240        EncodeBuilder { alpha, ..self }
241    }
242
243    /// Include checksum calculated using the [Base58Check][] algorithm when
244    /// encoding.
245    ///
246    /// [Base58Check]: https://en.bitcoin.it/wiki/Base58Check_encoding
247    ///
248    /// # Examples
249    ///
250    /// ```rust
251    /// let input = [0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78];
252    /// assert_eq!(
253    ///     "QuT57JNzzWTu7mW",
254    ///     bs58::encode(input)
255    ///         .with_check()
256    ///         .into_string());
257    /// ```
258    #[cfg(feature = "check")]
259    pub fn with_check(self) -> EncodeBuilder<'a, I> {
260        let check = Check::Enabled(None);
261        EncodeBuilder { check, ..self }
262    }
263
264    /// Include checksum calculated using the [Base58Check][] algorithm and
265    /// version when encoding.
266    ///
267    /// [Base58Check]: https://en.bitcoin.it/wiki/Base58Check_encoding
268    ///
269    /// # Examples
270    ///
271    /// ```rust
272    /// let input = [0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78];
273    /// assert_eq!(
274    ///     "oP8aA4HEEyFxxYhp",
275    ///     bs58::encode(input)
276    ///         .with_check_version(42)
277    ///         .into_string());
278    /// ```
279    #[cfg(feature = "check")]
280    pub fn with_check_version(self, expected_ver: u8) -> EncodeBuilder<'a, I> {
281        let check = Check::Enabled(Some(expected_ver));
282        EncodeBuilder { check, ..self }
283    }
284
285    /// Include checksum calculated using the [CB58][] algorithm and
286    /// version (if specified) when encoding.
287    ///
288    /// [CB58]: https://support.avax.network/en/articles/4587395-what-is-cb58
289    ///
290    /// # Examples
291    ///
292    /// ```rust
293    /// let input = [0x60, 0x65, 0xe7, 0x9b, 0xba, 0x2f, 0x78];
294    /// assert_eq!(
295    ///     "oP8aA4HEEyChXhM2",
296    ///     bs58::encode(input)
297    ///         .as_cb58(Some(42))
298    ///         .into_string());
299    /// ```
300    #[cfg(feature = "cb58")]
301    pub fn as_cb58(self, expected_ver: Option<u8>) -> EncodeBuilder<'a, I> {
302        let check = Check::CB58(expected_ver);
303        EncodeBuilder { check, ..self }
304    }
305
306    /// Encode into a new owned string.
307    ///
308    /// # Examples
309    ///
310    /// ```rust
311    /// let input = [0x04, 0x30, 0x5e, 0x2b, 0x24, 0x73, 0xf0, 0x58];
312    /// assert_eq!("he11owor1d", bs58::encode(input).into_string());
313    /// ```
314    #[cfg(feature = "alloc")]
315    pub fn into_string(self) -> String {
316        let mut output = String::new();
317        self.onto(&mut output).unwrap();
318        output
319    }
320
321    /// Encode into a new owned vector.
322    ///
323    /// # Examples
324    ///
325    /// ```rust
326    /// let input = [0x04, 0x30, 0x5e, 0x2b, 0x24, 0x73, 0xf0, 0x58];
327    /// assert_eq!(b"he11owor1d", &*bs58::encode(input).into_vec());
328    /// ```
329    #[cfg(feature = "alloc")]
330    pub fn into_vec(self) -> Vec<u8> {
331        let mut output = Vec::new();
332        self.onto(&mut output).unwrap();
333        output
334    }
335
336    /// Encode onto the given buffer.
337    ///
338    /// Returns the length written onto the buffer.
339    ///
340    /// If the buffer is resizeable it will be extended and the new data will be written to the end
341    /// of it, otherwise the data will be overwritten from the start.
342    ///
343    /// If the buffer is not resizeable bytes after the final character will be left alone, except
344    /// up to 3 null bytes may be written to an `&mut str` to overwrite remaining characters of a
345    /// partially overwritten multi-byte character.
346    ///
347    /// See the documentation for [`bs58::encode`](crate::encode()) for an
348    /// explanation of the errors that may occur.
349    ///
350    /// # Examples
351    ///
352    /// ## `Vec<u8>`
353    ///
354    /// ```rust
355    /// let input = [0x04, 0x30, 0x5e, 0x2b, 0x24, 0x73, 0xf0, 0x58];
356    /// let mut output = b"goodbye world ".to_vec();
357    /// bs58::encode(input).onto(&mut output)?;
358    /// assert_eq!(b"goodbye world he11owor1d", output.as_slice());
359    /// # Ok::<(), bs58::encode::Error>(())
360    /// ```
361    ///
362    /// ## `&mut [u8]`
363    ///
364    /// ```rust
365    /// let input = [0x04, 0x30, 0x5e, 0x2b, 0x24, 0x73, 0xf0, 0x58];
366    /// let mut output = b"goodbye world".to_owned();
367    /// bs58::encode(input).onto(&mut output[..])?;
368    /// assert_eq!(b"he11owor1drld", output.as_ref());
369    /// # Ok::<(), bs58::encode::Error>(())
370    /// ```
371    ///
372    /// ## `String`
373    ///
374    /// ```rust
375    /// let input = [0x04, 0x30, 0x5e, 0x2b, 0x24, 0x73, 0xf0, 0x58];
376    /// let mut output = "goodbye world ".to_owned();
377    /// bs58::encode(input).onto(&mut output)?;
378    /// assert_eq!("goodbye world he11owor1d", output);
379    /// # Ok::<(), bs58::encode::Error>(())
380    /// ```
381    ///
382    /// ## `&mut str`
383    ///
384    /// ```rust
385    /// let input = [0x04, 0x30, 0x5e, 0x2b, 0x24, 0x73, 0xf0, 0x58];
386    /// let mut output = "goodbye world".to_owned();
387    /// bs58::encode(input).onto(output.as_mut_str())?;
388    /// assert_eq!("he11owor1drld", output);
389    /// # Ok::<(), bs58::encode::Error>(())
390    /// ```
391    ///
392    /// ### Clearing partially overwritten characters
393    ///
394    /// ```rust
395    /// let input = [0x04, 0x30, 0x5e, 0x2b, 0x24, 0x73, 0xf0, 0x58];
396    /// let mut output = "goodbye w®ld".to_owned();
397    /// bs58::encode(input).onto(output.as_mut_str())?;
398    /// assert_eq!("he11owor1d\0ld", output);
399    /// # Ok::<(), bs58::encode::Error>(())
400    /// ```
401    pub fn onto(self, mut output: impl EncodeTarget) -> Result<usize> {
402        let input = self.input.as_ref();
403        match self.check {
404            Check::Disabled => output.encode_with(max_encoded_len(input.len()), |output| {
405                encode_into(input, output, self.alpha)
406            }),
407            #[cfg(feature = "check")]
408            Check::Enabled(version) => {
409                let input_len = input.len() + CHECKSUM_LEN + version.map_or(0, |_| 1);
410                output.encode_with(max_encoded_len(input_len), |output| {
411                    encode_check_into(self.input.as_ref(), output, self.alpha, version)
412                })
413            }
414            #[cfg(feature = "cb58")]
415            Check::CB58(version) => {
416                let input_len = input.len() + CHECKSUM_LEN + version.map_or(0, |_| 1);
417                output.encode_with(max_encoded_len(input_len), |output| {
418                    encode_cb58_into(self.input.as_ref(), output, self.alpha, version)
419                })
420            }
421        }
422    }
423}
424
425/// Return maximum possible encoded length of a buffer with given length.
426///
427/// Assumes that the `len` already includes version and checksum bytes if those
428/// are
429fn max_encoded_len(len: usize) -> usize {
430    // log_2(256) / log_2(58) ≈ 1.37.  Assume 1.5 for easier calculation.
431    len + (len + 1) / 2
432}
433
434fn encode_into<'a, I>(input: I, output: &mut [u8], alpha: &Alphabet) -> Result<usize>
435where
436    I: Clone + IntoIterator<Item = &'a u8>,
437{
438    let mut index = 0;
439    for &val in input.clone() {
440        let mut carry = val as usize;
441        for byte in &mut output[..index] {
442            carry += (*byte as usize) << 8;
443            *byte = (carry % 58) as u8;
444            carry /= 58;
445        }
446        while carry > 0 {
447            if index == output.len() {
448                return Err(Error::BufferTooSmall);
449            }
450            output[index] = (carry % 58) as u8;
451            index += 1;
452            carry /= 58;
453        }
454    }
455
456    for _ in input.into_iter().take_while(|v| **v == 0) {
457        if index == output.len() {
458            return Err(Error::BufferTooSmall);
459        }
460        output[index] = 0;
461        index += 1;
462    }
463
464    for val in &mut output[..index] {
465        *val = alpha.encode[*val as usize];
466    }
467
468    output[..index].reverse();
469    Ok(index)
470}
471
472#[cfg(feature = "check")]
473fn encode_check_into(
474    input: &[u8],
475    output: &mut [u8],
476    alpha: &Alphabet,
477    version: Option<u8>,
478) -> Result<usize> {
479    use sha2::{Digest, Sha256};
480
481    let mut first_hash = Sha256::new();
482    if let Some(version) = version {
483        first_hash.update([version; 1]);
484    }
485    let first_hash = first_hash.chain_update(input).finalize();
486    let second_hash = Sha256::digest(first_hash);
487
488    let checksum = &second_hash[0..CHECKSUM_LEN];
489
490    encode_into(
491        version.iter().chain(input.iter()).chain(checksum.iter()),
492        output,
493        alpha,
494    )
495}
496
497#[cfg(feature = "cb58")]
498fn encode_cb58_into(
499    input: &[u8],
500    output: &mut [u8],
501    alpha: &Alphabet,
502    version: Option<u8>,
503) -> Result<usize> {
504    use sha2::{Digest, Sha256};
505
506    let mut hash = Sha256::new();
507    if let Some(version) = version {
508        hash.update([version; 1]);
509    }
510    let hash = hash.chain_update(input).finalize();
511
512    let checksum = &hash[hash.len() - CHECKSUM_LEN..];
513
514    encode_into(
515        version.iter().chain(input.iter()).chain(checksum.iter()),
516        output,
517        alpha,
518    )
519}
520
521#[cfg(feature = "std")]
522impl std::error::Error for Error {}
523
524impl fmt::Display for Error {
525    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
526        match *self {
527            Error::BufferTooSmall => write!(
528                f,
529                "buffer provided to encode base58 string into was too small"
530            ),
531        }
532    }
533}