const ALPHABET: &[u8; 62] =
b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
fn base62_decode_str(s: &str) -> Option<String> {
let digits = s.as_bytes();
let leading_zeros = digits.iter().take_while(|d| **d == b'0').count();
let mut num: Vec<u8> = Vec::new();
for digit in &digits[leading_zeros..] {
let value = ALPHABET.iter().position(|a| a == digit)? as u32;
let mut carry = value;
for byte in num.iter_mut().rev() {
let acc = *byte as u32 * 62 + carry;
*byte = (acc % 256) as u8;
carry = acc / 256;
}
while carry > 0 {
num.insert(0, (carry % 256) as u8);
carry /= 256;
}
}
let mut bytes = vec![0u8; leading_zeros];
bytes.extend(num);
String::from_utf8(bytes).ok()
}
pub fn parse_composite_laboratory_id(s: &str) -> Option<(String, String, String)> {
let mut parts = s.split('/');
let machine = parts.next()?;
let machine_state = parts.next()?;
let id = parts.next()?;
if parts.next().is_some() || machine.is_empty() {
return None;
}
Some((
machine.to_string(),
base62_decode_str(machine_state)?,
base62_decode_str(id)?,
))
}
fn fnv1a32(bytes: &[u8]) -> u32 {
let mut hash: u32 = 0x811c9dc5;
for byte in bytes {
hash ^= *byte as u32;
hash = hash.wrapping_mul(0x0100_0193);
}
hash
}
fn base62_encode_u32(mut value: u32) -> String {
let mut digits = [b'0'; 6];
for slot in digits.iter_mut().rev() {
*slot = ALPHABET[(value % 62) as usize];
value /= 62;
}
String::from_utf8(digits.to_vec()).expect("alphabet is ASCII")
}
pub fn server_name(env_value: &str) -> String {
format!("oail-{}", base62_encode_u32(fnv1a32(env_value.as_bytes())))
}