2024-06-19 19:02:12 +00:00
|
|
|
//! Converts unsigned integers into a string representation with some base.
|
|
|
|
//! Bases up to and including 36 can be used for case-insensitive things.
|
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
use std::{ascii, fmt};
|
2016-11-04 21:37:42 +00:00
|
|
|
|
2019-08-01 20:57:23 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2018-01-08 11:30:52 +00:00
|
|
|
pub const MAX_BASE: usize = 64;
|
|
|
|
pub const ALPHANUMERIC_ONLY: usize = 62;
|
|
|
|
pub const CASE_INSENSITIVE: usize = 36;
|
2016-12-12 18:56:52 +00:00
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
const BASE_64: [ascii::Char; MAX_BASE] = {
|
|
|
|
let bytes = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@$";
|
|
|
|
let Some(ascii) = bytes.as_ascii() else { panic!() };
|
|
|
|
*ascii
|
|
|
|
};
|
2016-11-04 21:37:42 +00:00
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
pub struct BaseNString {
|
|
|
|
start: usize,
|
|
|
|
buf: [ascii::Char; 128],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::ops::Deref for BaseNString {
|
|
|
|
type Target = str;
|
2016-11-04 21:37:42 +00:00
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
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
|
2024-09-02 05:42:38 +00:00
|
|
|
// case-insensitive encoding. Add any impls you need.
|
2024-04-03 18:09:21 +00:00
|
|
|
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
|
|
|
|
}
|
2018-01-08 11:30:52 +00:00
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
fn to_base(self, base: usize) -> BaseNString {
|
|
|
|
let mut output = [ascii::Char::Digit0; 128];
|
2016-11-04 21:37:42 +00:00
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
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;
|
2016-11-04 21:37:42 +00:00
|
|
|
}
|
2024-04-03 18:09:21 +00:00
|
|
|
len
|
2016-11-04 21:37:42 +00:00
|
|
|
}
|
2024-04-03 18:09:21 +00:00
|
|
|
}
|
2018-08-09 15:00:14 +00:00
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
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
|
|
|
|
}
|
2016-11-04 21:37:42 +00:00
|
|
|
}
|
|
|
|
|
2024-04-03 18:09:21 +00:00
|
|
|
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
|
|
|
|
}
|
2016-11-04 21:37:42 +00:00
|
|
|
}
|