const VALID_NAME_CHARS: [char; 37] = [ '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]; pub fn encode_base37>(input: T) -> u64 { let mut combined = input.as_ref().chars().fold(0u64, |acc, c| { let acc = acc.wrapping_mul(37); match c { 'A'..='Z' => acc + (c as u64 - 65) + 1, 'a'..='z' => acc + (c as u64 - 97) + 1, '0'..='9' => acc + (c as u64 - 48) + 27, _ => acc } }); while combined % 37 == 0 && combined != 0 { combined = combined.wrapping_div(37); } combined } pub fn decode_base37(mut input: u64) -> anyhow::Result { if input == 0 || input >= 0x5B5B57F8A98A5DD1 || input % 37 == 0 { return Err(anyhow::anyhow!("invalid name")); } let mut result = ['\0'; 12]; let mut index = 0; while input != 0 { let local_input = input; input /= 37; index += 1; result[11 - index] = (VALID_NAME_CHARS[(local_input - input * 37) as usize]); } Ok(result.iter().filter(|c| **c != '\0').collect::()) } #[cfg(test)] mod tests { use super::*; #[test] pub fn test_encode_base37() { assert_eq!(encode_base37("smrkn"), 36292611); assert_eq!(encode_base37("csh"), 4818); } #[test] pub fn test_decode_base37() { assert_eq!(decode_base37(36292611).unwrap(), String::from("smrkn")); assert_eq!(decode_base37(4818).unwrap(), String::from("csh")); } }