1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
#[cfg(tests)] mod tests; /// Convert a number into its New Base 60 representation. /// /// For more information, see [this link](http://tantek.pbworks.com/w/page/19402946/NewBase60). /// /// # Examples /// /// Basic usage: /// ``` /// use newbase60::num_to_sxg; /// /// assert_eq!(num_to_sxg(1337), "NH".to_string()); /// ``` pub fn num_to_sxg(n: u128) -> String { // A static lookup table static DIGITS: &[u8; 60] = b"0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz"; if n == 0 { return "0".to_string(); } let mut n = n; let mut s = String::new(); while n > 0 { // Safe to convert because it is mod 60 let d = n % 60; // Safe to index because 0 <= n < 60 // Safe to convert to char because the lookup table is all chars let ch = DIGITS[d as usize] as char; s.push(ch); n = (n - d) / 60; } s.chars().rev().collect() } /// Convert a string into its New Base 60 representation, dropping invalid characters. /// /// Valid New Base 60 characters are alphanumeric or underscores (that is, they /// individually match the regex `[a-zA-Z0-9_]`). Invalid characters will be treated as if /// they did not exist. Empty strings will evaluate to 0. /// /// If the resulting value is larger than 2<sup>128</sup>, then this function will return `None`. /// /// For more information, see [this link](http://tantek.pbworks.com/w/page/19402946/NewBase60). /// /// # Examples /// /// ``` /// use newbase60::sxg_to_num; /// /// assert_eq!(sxg_to_num("NH"), Some(1337)); /// assert_eq!(sxg_to_num("N🥺H"), Some(1337)); /// assert_eq!(sxg_to_num("verylongstringthatoverflowsthemultiplicationbuffer"), None); /// ``` pub fn sxg_to_num(s: &str) -> Option<u128> { let mut n: u128 = 0; for c in s.chars() { let digit = match c { '0'..='9' => c as u8 - b'0', 'A'..='H' => c as u8 - b'A' + 10, 'J'..='N' => c as u8 - b'J' + 18, 'P'..='Z' => c as u8 - b'P' + 23, '_' => 34, 'a'..='k' => c as u8 - b'a' + 35, 'm'..='z' => c as u8 - b'm' + 46, 'I' | 'l' => 1, // typo capital I, lowercase l to 1 'O' => 0, // error correct typo capital O to 0 _ => continue, // skip invalid chars }; n = match n.checked_mul(60).and_then(|x| x.checked_add(digit as u128)) { Some(x) => x, None => return None, } } Some(n) }