use core::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
BufferTooSmall,
InvalidEncoding,
InvalidLength {
expected: usize,
actual: usize,
},
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::BufferTooSmall => {
f.write_str("base58 output buffer is too small")
}
Self::InvalidEncoding => f.write_str("invalid base58 encoding"),
Self::InvalidLength { expected, actual } => {
write!(f, "expected {expected} bytes, received {actual}")
}
}
}
}
impl core::error::Error for Error {}
pub const fn max_encoded_len(max_bytes: usize) -> usize {
if max_bytes == 0 {
return 0;
}
(max_bytes / 100)
.saturating_mul(138)
.saturating_add((max_bytes % 100) * 138 / 100)
.saturating_add(1)
}
pub fn encode_into(bytes: &[u8], output: &mut [u8]) -> Result<usize, Error> {
bs58::encode(bytes)
.onto(output)
.map_err(|_| Error::BufferTooSmall)
}
pub fn decode_into(text: &str, output: &mut [u8]) -> Result<usize, Error> {
if text.len() > max_encoded_len(output.len()) {
return Err(Error::BufferTooSmall);
}
bs58::decode(text)
.onto(output)
.map_err(|error| match error {
bs58::decode::Error::BufferTooSmall => Error::BufferTooSmall,
_ => Error::InvalidEncoding,
})
}
pub fn decode_fixed<const N: usize>(text: &str) -> Result<[u8; N], Error> {
let mut bytes = [0; N];
let actual = decode_into(text, &mut bytes)?;
if actual != N {
return Err(Error::InvalidLength {
expected: N,
actual,
});
}
Ok(bytes)
}
pub struct Base58<'a, const N: usize>(&'a [u8; N]);
impl<const N: usize> fmt::Display for Base58<'_, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buffer = [[0u8; 2]; N];
let buffer = buffer.as_flattened_mut();
let len = encode_into(self.0, buffer).map_err(|_| fmt::Error)?;
let text =
core::str::from_utf8(&buffer[..len]).map_err(|_| fmt::Error)?;
f.write_str(text)
}
}
impl<const N: usize> fmt::Debug for Base58<'_, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
pub fn base58_encode<const N: usize>(bytes: &[u8; N]) -> Base58<'_, N> {
Base58(bytes)
}
#[cfg(test)]
mod tests;