crate::ix!();
pub fn encode_base58(mut input: &[u8]) -> String {
let mut zeroes: usize = 0;
let mut length: i32 = 0;
while input.len() > 0 && input[0] == 0 {
input = &input[1..];
zeroes += 1;
}
let size: usize = input.len() * 138 / 100 + 1;
let mut b58: Vec<u8> = vec![0; size];
while input.len() > 0 {
let mut carry: i32 = input[0].into();
let mut i: i32 = 0;
for it in b58.iter_mut().rev() {
if carry == 0 && i >= length {
break;
}
carry += 256 * ((*it) as i32);
*it = (carry % 58).try_into().unwrap();
carry /= 58;
i += 1;
}
assert!(carry == 0);
length = i;
input = &input[1..];
}
let mut idx = (size as i32 - length) as usize;
while idx < b58.len() && b58[idx] == 0 {
idx += 1;
}
let mut s = String::with_capacity(zeroes + (b58.len() - idx));
s.push_str(&"1".repeat(zeroes));
let alphabet = PSZ_BASE58.as_bytes();
for &val in &b58[idx..] {
s.push(alphabet[val as usize] as char);
}
s
}
#[cfg(test)]
mod encode_spec {
use super::*;
fn parse_hex(src: &str) -> Vec<u8> {
fn nibble(b: u8) -> u8 {
match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => b - b'a' + 10,
b'A'..=b'F' => b - b'A' + 10,
_ => panic!("non‑hex digit {b:?}"),
}
}
assert!(src.len() % 2 == 0, "odd‑length hex");
src.as_bytes()
.chunks_exact(2)
.map(|c| (nibble(c[0]) << 4) | nibble(c[1]))
.collect()
}
const ENCODE_VECTORS: &[(&str, &str)] = &[
("", ""),
("00", "1"),
("61", "2g"),
("626262", "a3gV"),
("73696d706c652e", "5Nfm5YkXu7"), ("73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"),
];
#[traced_test]
fn encode_reference_vectors() {
for (idx, (hex, expected)) in ENCODE_VECTORS.iter().enumerate() {
let encoded = encode_base58(&parse_hex(hex));
debug!(idx, ?hex, ?expected, ?encoded, "verifying encode vector");
assert_eq!(
encoded, *expected,
"vector #{idx}: expected {expected}, got {encoded}"
);
}
}
#[traced_test]
fn preserves_leading_zero_bytes() {
for zeros in 0..5 {
let mut payload = vec![0u8; zeros];
payload.extend_from_slice(b"data");
let encoded = encode_base58(&payload);
info!(zeros, ?encoded, "encoded with leading zeros");
assert!(
encoded.starts_with(&"1".repeat(zeros)),
"expected {zeros} leading ‘1’ symbols"
);
}
}
}