Skip to main content

multi_base/
lib.rs

1// SPDX-License-Identifier: MIT
2
3//! # multibase
4//!
5//! Implementation of [multibase](https://github.com/multiformats/multibase) in Rust.
6
7#![deny(missing_docs)]
8#![deny(unsafe_code)]
9// The `build_base_enum!` macro generates doc comments containing base alphabet
10// strings (e.g. `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`)
11// and variant names like `Base256Emoji`, which clippy::doc_markdown flags. These
12// are intentional alphabet literals, not identifiers requiring backticks.
13#![allow(clippy::doc_markdown)]
14#![cfg_attr(not(feature = "std"), no_std)]
15
16#[cfg(not(feature = "std"))]
17extern crate alloc;
18
19#[cfg(not(feature = "std"))]
20use alloc::{string::String, vec::Vec};
21
22mod base;
23pub mod encoded;
24mod encoding;
25mod error;
26mod impls;
27
28pub use self::base::Base;
29pub use self::encoded::EncodedString;
30pub use self::error::{Error, Result};
31
32/// Decode the base string.
33///
34/// # Examples
35///
36/// ```
37/// use multi_base::{Base, decode};
38///
39/// assert_eq!(
40///     decode("zCn8eVZg", true).unwrap(),
41///     (Base::Base58Btc, b"hello".to_vec())
42/// );
43/// ```
44///
45/// # Errors
46///
47/// Returns an error if:
48/// - The input string is empty ([`Error::EmptyInput`])
49/// - The base code prefix is unknown ([`Error::UnknownBase`])
50/// - The encoded data is invalid for the detected base
51#[inline]
52pub fn decode<T: AsRef<str>>(input: T, strict: bool) -> Result<(Base, Vec<u8>)> {
53    let input = input.as_ref();
54    let code = input.chars().next().ok_or(Error::EmptyInput)?;
55    let base = Base::from_code(code)?;
56    let decoded = base.decode(&input[code.len_utf8()..], strict)?;
57    Ok((base, decoded))
58}
59
60/// Encode with the given byte slice to base string.
61///
62/// # Examples
63///
64/// ```
65/// use multi_base::{Base, encode};
66///
67/// assert_eq!(encode(Base::Base58Btc, b"hello"), "zCn8eVZg");
68/// ```
69///
70/// # Performance
71///
72/// This function pre-allocates the exact capacity needed and constructs the
73/// result string efficiently by prepending the base code without requiring
74/// reallocation or memory moves.
75#[inline]
76pub fn encode<T: AsRef<[u8]>>(base: Base, input: T) -> String {
77    let input = input.as_ref();
78    let encoded = base.encode(input);
79    let code = base.code();
80
81    // Pre-allocate exact size needed: code length + encoded length
82    // This is much faster than insert(0) which requires moving all bytes
83    let mut result = String::with_capacity(code.len_utf8() + encoded.len());
84    result.push(code);
85    result.push_str(&encoded);
86    result
87}
88
89/// Encode with the given byte slice to base string, writing into an existing buffer.
90///
91/// This is a zero-copy variant of [`encode`] that reuses an existing String buffer,
92/// avoiding allocations when encoding multiple values in a loop.
93///
94/// The buffer will be cleared before encoding, then filled with the base code prefix
95/// followed by the encoded data.
96///
97/// # Examples
98///
99/// ```
100/// use multi_base::{Base, encode_into};
101///
102/// let mut buffer = String::new();
103///
104/// // Encode multiple values reusing the same buffer
105/// encode_into(Base::Base58Btc, b"hello", &mut buffer);
106/// assert_eq!(buffer, "zCn8eVZg");
107///
108/// encode_into(Base::Base64, b"world", &mut buffer);
109/// assert_eq!(buffer, "md29ybGQ");
110/// ```
111///
112/// # Performance
113///
114/// When encoding many values, this function can be significantly faster than
115/// [`encode`] as it reuses the allocated buffer instead of allocating a new
116/// String for each encoding operation.
117#[inline]
118pub fn encode_into<T: AsRef<[u8]>>(base: Base, input: T, buffer: &mut String) {
119    let input = input.as_ref();
120    let encoded = base.encode(input);
121    let code = base.code();
122
123    // Clear and reserve exact capacity needed
124    buffer.clear();
125    buffer.reserve(code.len_utf8() + encoded.len());
126    buffer.push(code);
127    buffer.push_str(&encoded);
128}
129
130/// Decode the base string, writing the result into an existing buffer.
131///
132/// This is a zero-copy variant of [`decode`] that reuses an existing `Vec<u8>` buffer,
133/// avoiding allocations when decoding multiple values in a loop.
134///
135/// The buffer will be cleared before decoding, then filled with the decoded bytes.
136/// Returns the base encoding that was detected from the prefix.
137///
138/// # Examples
139///
140/// ```
141/// use multi_base::{Base, decode_into};
142///
143/// let mut buffer = Vec::new();
144///
145/// // Decode multiple values reusing the same buffer
146/// let base = decode_into("zCn8eVZg", true, &mut buffer).unwrap();
147/// assert_eq!(base, Base::Base58Btc);
148/// assert_eq!(buffer, b"hello");
149///
150/// let base = decode_into("md29ybGQ", true, &mut buffer).unwrap();
151/// assert_eq!(base, Base::Base64);
152/// assert_eq!(buffer, b"world");
153/// ```
154///
155/// # Performance
156///
157/// When decoding many values, this function can be significantly faster than
158/// [`decode`] as it reuses the allocated buffer instead of allocating a new
159/// `Vec<u8>` for each decoding operation.
160///
161/// # Errors
162///
163/// Returns an error if:
164/// - The input string is empty
165/// - The base code prefix is unknown
166/// - The encoded data is invalid for the specified base
167#[inline]
168pub fn decode_into<T: AsRef<str>>(input: T, strict: bool, buffer: &mut Vec<u8>) -> Result<Base> {
169    let input = input.as_ref();
170    let code = input.chars().next().ok_or(Error::EmptyInput)?;
171    let base = Base::from_code(code)?;
172    base.decode_into(&input[code.len_utf8()..], strict, buffer)?;
173    Ok(base)
174}
175
176/// Encode with the given byte slice and return a validated `EncodedString`.
177///
178/// This is a convenience function that combines [`encode`] with [`EncodedString::new`],
179/// providing type-level guarantees that the returned string is a valid multibase encoding.
180///
181/// # Examples
182///
183/// ```
184/// use multi_base::{Base, encode_to_validated};
185///
186/// let encoded = encode_to_validated(Base::Base58Btc, b"hello");
187/// assert_eq!(encoded.base(), Base::Base58Btc);
188/// assert_eq!(encoded.as_str(), "zCn8eVZg");
189///
190/// // Decode directly from the validated string
191/// let decoded = encoded.decode().unwrap();
192/// assert_eq!(decoded, b"hello");
193/// ```
194///
195/// # Performance
196///
197/// This function has the same performance characteristics as [`encode`].
198/// The validation overhead is negligible (just checking the base code).
199///
200/// # Panics
201///
202/// Panics if the encoded string fails validation. Since [`encode`] always
203/// produces a valid multibase string, this is unreachable in practice.
204#[inline]
205pub fn encode_to_validated<T: AsRef<[u8]>>(base: Base, input: T) -> EncodedString {
206    let encoded_str = encode(base, input);
207    // The string was just produced by `encode`, so the prefix is always valid.
208    EncodedString::new(encoded_str).expect("encoded string has a valid base prefix")
209}
210
211/// Parse a multibase string into a validated `EncodedString`.
212///
213/// This is equivalent to [`EncodedString::new`] but provides a standalone
214/// function for consistency with other API functions.
215///
216/// # Examples
217///
218/// ```
219/// use multi_base::{Base, parse_encoded};
220///
221/// let encoded = parse_encoded("zCn8eVZg").unwrap();
222/// assert_eq!(encoded.base(), Base::Base58Btc);
223/// ```
224///
225/// # Errors
226///
227/// Returns an error if:
228/// - The input string is empty
229/// - The base code prefix is unknown
230#[inline]
231pub fn parse_encoded<T: AsRef<str>>(input: T) -> Result<EncodedString> {
232    EncodedString::new(input.as_ref())
233}