Skip to content

Instantly share code, notes, and snippets.

@csh
Created May 16, 2020 11:42
Show Gist options
  • Save csh/2e05fcf5091f01900136461a5dae0db9 to your computer and use it in GitHub Desktop.
Save csh/2e05fcf5091f01900136461a5dae0db9 to your computer and use it in GitHub Desktop.
Base37 encoder/decoder written in Rust
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<T: AsRef<str>>(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<String> {
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::<String>())
}
#[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"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment