#![allow(dead_code)]
const ALPHABET: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
pub fn base58_encode(data: &[u8]) -> String {
let leading_zeros = data.iter().take_while(|&&b| b == 0).count();
let mut digits: Vec<u8> = Vec::new();
for &byte in data {
let mut carry = byte as u32;
for d in digits.iter_mut() {
carry += (*d as u32) << 8;
*d = (carry % 58) as u8;
carry /= 58;
}
while carry > 0 {
digits.push((carry % 58) as u8);
carry /= 58;
}
}
let mut out = String::with_capacity(leading_zeros + digits.len());
for _ in 0..leading_zeros {
out.push('1');
}
for &d in digits.iter().rev() {
out.push(ALPHABET[d as usize] as char);
}
out
}
pub fn base58_decode(s: &str) -> Result<Vec<u8>, String> {
let mut lookup = [0i16; 256];
lookup.iter_mut().for_each(|v| *v = -1);
for (i, &c) in ALPHABET.iter().enumerate() {
lookup[c as usize] = i as i16;
}
let leading_ones = s.chars().take_while(|&c| c == '1').count();
let mut bytes: Vec<u8> = Vec::new();
for ch in s.chars() {
let v = lookup[ch as usize];
if v < 0 {
return Err(format!("base58: invalid character '{}'", ch));
}
let mut carry = v as u32;
for b in bytes.iter_mut() {
carry += (*b as u32) * 58;
*b = (carry & 0xFF) as u8;
carry >>= 8;
}
while carry > 0 {
bytes.push((carry & 0xFF) as u8);
carry >>= 8;
}
}
let mut out = vec![0u8; leading_ones];
for b in bytes.iter().rev() {
out.push(*b);
}
Ok(out)
}
pub fn base58_is_valid(s: &str) -> bool {
s.chars().all(|c| ALPHABET.contains(&(c as u8)))
}
pub fn base58_encoded_len_estimate(byte_len: usize) -> usize {
(byte_len * 138 / 100) + 1
}
pub fn base58_roundtrip_ok(data: &[u8]) -> bool {
base58_decode(&base58_encode(data))
.map(|d| d == data)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_known() {
let encoded = base58_encode(&[0]);
assert_eq!(encoded, "1");
}
#[test]
fn test_roundtrip_hello() {
assert!(base58_roundtrip_ok(b"hello"));
}
#[test]
fn test_roundtrip_empty() {
assert!(base58_roundtrip_ok(&[]));
}
#[test]
fn test_roundtrip_binary() {
let data = vec![0u8, 1, 127, 255, 0, 42];
assert!(base58_roundtrip_ok(&data));
}
#[test]
fn test_is_valid_true() {
assert!(base58_is_valid("123456789ABC"));
}
#[test]
fn test_is_valid_false() {
assert!(!base58_is_valid("0"));
}
#[test]
fn test_decode_invalid_char() {
assert!(base58_decode("!invalid!").is_err());
}
#[test]
fn test_encoded_len_estimate() {
assert!(base58_encoded_len_estimate(10) > 0);
}
#[test]
fn test_leading_zeros() {
let enc = base58_encode(&[0, 0, 1]);
assert!(enc.starts_with("11"));
}
}