1use crate::error::{DxError, Result};
7
8const BASE62_CHARS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
10
11#[must_use]
13pub fn encode_base62(mut n: u64) -> String {
14 if n == 0 {
15 return "0".to_string();
16 }
17
18 let mut result = Vec::new();
19 while n > 0 {
20 result.push(BASE62_CHARS[(n % 62) as usize] as char);
21 n /= 62;
22 }
23 result.reverse();
24 result.into_iter().collect()
25}
26
27#[must_use = "decoding result should be used"]
29pub fn decode_base62(s: &str) -> Result<u64> {
30 let mut result = 0u64;
31
32 for (position, c) in s.chars().enumerate() {
33 let digit = match c {
34 '0'..='9' => (c as u8 - b'0') as u64,
35 'A'..='Z' => (c as u8 - b'A' + 10) as u64,
36 'a'..='z' => (c as u8 - b'a' + 36) as u64,
37 _ => {
38 return Err(DxError::Base62Error {
39 char: c,
40 position,
41 message: "expected 0-9, A-Z, or a-z".to_string(),
42 });
43 }
44 };
45
46 result = result
47 .checked_mul(62)
48 .and_then(|r| r.checked_add(digit))
49 .ok_or(DxError::IntegerOverflow)?;
50 }
51
52 Ok(result)
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn test_base62_encode() {
61 assert_eq!(encode_base62(0), "0");
62 assert_eq!(encode_base62(35), "Z"); assert_eq!(encode_base62(61), "z"); assert_eq!(encode_base62(62), "10");
65 assert_eq!(encode_base62(320), "5A"); assert_eq!(encode_base62(540), "8i"); assert_eq!(encode_base62(180), "2u"); assert_eq!(encode_base62(10000), "2bI");
69 }
70
71 #[test]
72 fn test_base62_decode() {
73 assert_eq!(decode_base62("0").unwrap(), 0);
74 assert_eq!(decode_base62("Z").unwrap(), 35);
75 assert_eq!(decode_base62("z").unwrap(), 61);
76 assert_eq!(decode_base62("10").unwrap(), 62);
77 assert_eq!(decode_base62("5A").unwrap(), 320);
78 assert_eq!(decode_base62("8i").unwrap(), 540);
79 assert_eq!(decode_base62("2u").unwrap(), 180);
80 assert_eq!(decode_base62("2bI").unwrap(), 10000);
81 }
82
83 #[test]
84 fn test_base62_round_trip() {
85 for n in [0, 1, 61, 62, 100, 320, 540, 1000, 10000, 999999] {
86 let encoded = encode_base62(n);
87 let decoded = decode_base62(&encoded).unwrap();
88 assert_eq!(decoded, n, "Failed for {}: {} -> {}", n, encoded, decoded);
89 }
90 }
91
92 #[test]
93 fn test_base62_savings() {
94 assert!(encode_base62(320).len() < "320".len()); assert!(encode_base62(10000).len() < "10000".len()); }
98}