| //! Converts unsigned integers into a string representation with some base. | |
| //! Bases up to and including 36 can be used for case-insensitive things. | |
| use std::{ascii, fmt}; | |
| #[cfg(test)] | |
| mod tests; | |
| pub const MAX_BASE: usize = 64; | |
| pub const ALPHANUMERIC_ONLY: usize = 62; | |
| pub const CASE_INSENSITIVE: usize = 36; | |
| const BASE_64: [ascii::Char; MAX_BASE] = { | |
| let bytes = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@$"; | |
| let Some(ascii) = bytes.as_ascii() else { panic!() }; | |
| *ascii | |
| }; | |
| pub struct BaseNString { | |
| start: usize, | |
| buf: [ascii::Char; 128], | |
| } | |
| impl std::ops::Deref for BaseNString { | |
| type Target = str; | |
| fn deref(&self) -> &str { | |
| self.buf[self.start..].as_str() | |
| } | |
| } | |
| impl AsRef<str> for BaseNString { | |
| fn as_ref(&self) -> &str { | |
| self.buf[self.start..].as_str() | |
| } | |
| } | |
| impl fmt::Display for BaseNString { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.write_str(self) | |
| } | |
| } | |
| // This trait just lets us reserve the exact right amount of space when doing fixed-length | |
| // case-insensitive encoding. Add any impls you need. | |
| pub trait ToBaseN: Into<u128> { | |
| fn encoded_len(base: usize) -> usize; | |
| fn to_base_fixed_len(self, base: usize) -> BaseNString { | |
| let mut encoded = self.to_base(base); | |
| encoded.start = encoded.buf.len() - Self::encoded_len(base); | |
| encoded | |
| } | |
| fn to_base(self, base: usize) -> BaseNString { | |
| let mut output = [ascii::Char::Digit0; 128]; | |
| let mut n: u128 = self.into(); | |
| let mut index = output.len(); | |
| loop { | |
| index -= 1; | |
| output[index] = BASE_64[(n % base as u128) as usize]; | |
| n /= base as u128; | |
| if n == 0 { | |
| break; | |
| } | |
| } | |
| assert_eq!(n, 0); | |
| BaseNString { start: index, buf: output } | |
| } | |
| } | |
| impl ToBaseN for u128 { | |
| fn encoded_len(base: usize) -> usize { | |
| let mut max = u128::MAX; | |
| let mut len = 0; | |
| while max > 0 { | |
| len += 1; | |
| max /= base as u128; | |
| } | |
| len | |
| } | |
| } | |
| impl ToBaseN for u64 { | |
| fn encoded_len(base: usize) -> usize { | |
| let mut max = u64::MAX; | |
| let mut len = 0; | |
| while max > 0 { | |
| len += 1; | |
| max /= base as u64; | |
| } | |
| len | |
| } | |
| } | |
| impl ToBaseN for u32 { | |
| fn encoded_len(base: usize) -> usize { | |
| let mut max = u32::MAX; | |
| let mut len = 0; | |
| while max > 0 { | |
| len += 1; | |
| max /= base as u32; | |
| } | |
| len | |
| } | |
| } |