use core::{
fmt,
ops::Deref,
str::{self, from_utf8_unchecked},
};
use std::{ffi::OsStr, path::Path};
pub const BASE32_LEN_U64: usize = 13;
pub const BASE32_LEN_U128: usize = 26;
pub const BASE32_LOWER_TABLE: &[u8; 32] = b"0123456789abcdefghijklmnopqrstuv";
const BASE32_DECODE_TABLE: [u8; 256] = {
let mut table = [0xFFu8; 256];
let mut i = 0usize;
while i < 10 {
table[b'0' as usize + i] = i as u8;
i += 1;
}
let mut j = 0usize;
while j < 22 {
table[b'a' as usize + j] = (10 + j) as u8;
table[b'A' as usize + j] = (10 + j) as u8;
j += 1;
}
table
};
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Base32Buf64(pub [u8; BASE32_LEN_U64]);
impl Base32Buf64 {
#[inline(always)]
pub const fn as_str(&self) -> &str {
unsafe { from_utf8_unchecked(&self.0) }
}
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8; BASE32_LEN_U64] {
&self.0
}
}
impl Deref for Base32Buf64 {
type Target = str;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<str> for Base32Buf64 {
#[inline(always)]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<Path> for Base32Buf64 {
#[inline(always)]
fn as_ref(&self) -> &Path {
Path::new(self.as_str())
}
}
impl AsRef<OsStr> for Base32Buf64 {
#[inline(always)]
fn as_ref(&self) -> &OsStr {
OsStr::new(self.as_str())
}
}
impl AsRef<[u8]> for Base32Buf64 {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl PartialEq<str> for Base32Buf64 {
#[inline(always)]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<&str> for Base32Buf64 {
#[inline(always)]
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<Base32Buf64> for str {
#[inline(always)]
fn eq(&self, other: &Base32Buf64) -> bool {
self == other.as_str()
}
}
impl PartialEq<Base32Buf64> for &str {
#[inline(always)]
fn eq(&self, other: &Base32Buf64) -> bool {
*self == other.as_str()
}
}
impl fmt::Debug for Base32Buf64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl fmt::Display for Base32Buf64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Base32Buf128(pub [u8; BASE32_LEN_U128]);
impl Base32Buf128 {
#[inline(always)]
pub const fn as_str(&self) -> &str {
unsafe { from_utf8_unchecked(&self.0) }
}
#[inline(always)]
pub const fn as_bytes(&self) -> &[u8; BASE32_LEN_U128] {
&self.0
}
}
impl Deref for Base32Buf128 {
type Target = str;
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<str> for Base32Buf128 {
#[inline(always)]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<Path> for Base32Buf128 {
#[inline(always)]
fn as_ref(&self) -> &Path {
Path::new(self.as_str())
}
}
impl AsRef<OsStr> for Base32Buf128 {
#[inline(always)]
fn as_ref(&self) -> &OsStr {
OsStr::new(self.as_str())
}
}
impl AsRef<[u8]> for Base32Buf128 {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl PartialEq<str> for Base32Buf128 {
#[inline(always)]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<&str> for Base32Buf128 {
#[inline(always)]
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<Base32Buf128> for str {
#[inline(always)]
fn eq(&self, other: &Base32Buf128) -> bool {
self == other.as_str()
}
}
impl PartialEq<Base32Buf128> for &str {
#[inline(always)]
fn eq(&self, other: &Base32Buf128) -> bool {
*self == other.as_str()
}
}
impl fmt::Debug for Base32Buf128 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl fmt::Display for Base32Buf128 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[inline]
pub const fn encode_u64(val: u64) -> Base32Buf64 {
let mut buf = [0u8; BASE32_LEN_U64];
buf[0] = BASE32_LOWER_TABLE[((val >> 60) & 0x0F) as usize];
buf[1] = BASE32_LOWER_TABLE[((val >> 55) & 0x1F) as usize];
buf[2] = BASE32_LOWER_TABLE[((val >> 50) & 0x1F) as usize];
buf[3] = BASE32_LOWER_TABLE[((val >> 45) & 0x1F) as usize];
buf[4] = BASE32_LOWER_TABLE[((val >> 40) & 0x1F) as usize];
buf[5] = BASE32_LOWER_TABLE[((val >> 35) & 0x1F) as usize];
buf[6] = BASE32_LOWER_TABLE[((val >> 30) & 0x1F) as usize];
buf[7] = BASE32_LOWER_TABLE[((val >> 25) & 0x1F) as usize];
buf[8] = BASE32_LOWER_TABLE[((val >> 20) & 0x1F) as usize];
buf[9] = BASE32_LOWER_TABLE[((val >> 15) & 0x1F) as usize];
buf[10] = BASE32_LOWER_TABLE[((val >> 10) & 0x1F) as usize];
buf[11] = BASE32_LOWER_TABLE[((val >> 5) & 0x1F) as usize];
buf[12] = BASE32_LOWER_TABLE[(val & 0x1F) as usize];
Base32Buf64(buf)
}
#[inline]
pub const fn encode_u128(val: u128) -> Base32Buf128 {
let mut buf = [0u8; BASE32_LEN_U128];
buf[0] = BASE32_LOWER_TABLE[((val >> 125) & 0x07) as usize];
let mut i = 1;
while i < BASE32_LEN_U128 {
let shift = 125 - i * 5;
buf[i] = BASE32_LOWER_TABLE[((val >> shift) & 0x1F) as usize];
i += 1;
}
Base32Buf128(buf)
}
#[inline]
pub fn push_base32_u64(val: u64, buf: &mut String) {
let encoded = encode_u64(val);
buf.push_str(encoded.as_str());
}
#[inline]
pub fn push_base32_u128(val: u128, buf: &mut String) {
let encoded = encode_u128(val);
buf.push_str(encoded.as_str());
}
#[inline]
pub fn is_base32(s: &str) -> bool {
s.as_bytes()
.iter()
.all(|&b| BASE32_DECODE_TABLE[b as usize] != 0xFF)
}
#[inline]
pub fn decode_u64(s: &str) -> Option<u64> {
if s.len() != BASE32_LEN_U64 {
return None;
}
let bytes = s.as_bytes();
let head = BASE32_DECODE_TABLE[bytes[0] as usize];
if head > 0x0F {
return None;
}
bytes[1..].iter().try_fold(head as u64, |acc, &b| {
let digit = BASE32_DECODE_TABLE[b as usize];
if digit == 0xFF {
None
} else {
Some((acc << 5) | (digit as u64))
}
})
}
#[inline]
pub fn decode_u128(s: &str) -> Option<u128> {
if s.len() != BASE32_LEN_U128 {
return None;
}
let bytes = s.as_bytes();
let head = BASE32_DECODE_TABLE[bytes[0] as usize];
if head > 0x07 {
return None;
}
bytes[1..].iter().try_fold(head as u128, |acc, &b| {
let digit = BASE32_DECODE_TABLE[b as usize];
if digit == 0xFF {
None
} else {
Some((acc << 5) | (digit as u128))
}
})
}
const _: () = {
assert!(BASE32_LEN_U64 * 5 >= 64);
assert!((BASE32_LEN_U64 - 1) * 5 < 64);
assert!(BASE32_LEN_U128 * 5 >= 128);
assert!((BASE32_LEN_U128 - 1) * 5 < 128);
let b64 = encode_u64(u64::MAX);
assert!(b64.0[0] == b'f');
assert!(b64.0[12] == b'v');
let b128 = encode_u128(u128::MAX);
assert!(b128.0[0] == b'7');
assert!(b128.0[25] == b'v');
};