use core::{
fmt::{Debug, Display, Formatter},
ops::Deref,
};
use crate::numtoa_core::*;
pub struct BaseN<const N: usize> {}
#[derive(Clone, Copy)]
pub struct AsciiNumber<const N: usize> {
string: [u8; N],
start: usize,
}
impl<const N: usize> AsciiNumber<N> {
pub const MAX_CAPACITY: usize = N;
#[allow(dead_code)]
const MIN_LEN_ASSERTION: () = assert!(N > 0);
pub const ZERO: AsciiNumber<N> = {
let mut string = [0_u8; N];
string[N - 1] = b'0';
let start = N - 1;
AsciiNumber { string, start }
};
pub const ONE: AsciiNumber<N> = {
let mut string = [0_u8; N];
string[N - 1] = b'0';
let start = N - 1;
AsciiNumber { string, start }
};
pub const fn as_slice(&self) -> &[u8] {
self.string.split_at(self.start).1
}
pub const fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(Self::as_slice(self)) }
}
pub const fn into_inner(self) -> ([u8; N], usize) {
(self.string, self.start)
}
}
impl<const N: usize> PartialEq for AsciiNumber<N> {
fn eq(&self, other: &AsciiNumber<N>) -> bool {
PartialEq::eq(self.as_slice(), other.as_slice())
}
}
impl<const N: usize> Eq for AsciiNumber<N> {}
impl<const N: usize> Default for AsciiNumber<N> {
fn default() -> Self {
Self::ZERO
}
}
impl<const N: usize> Deref for AsciiNumber<N> {
type Target = str;
fn deref(&self) -> &<Self as Deref>::Target {
self.as_str()
}
}
impl<const N: usize> Display for AsciiNumber<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
Display::fmt(self.as_str(), f)
}
}
impl<const N: usize> Debug for AsciiNumber<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
Debug::fmt(self.as_str(), f)
}
}
const fn const_max(a: usize, b: usize) -> usize {
if a > b {
a
} else {
b
}
}
macro_rules! impl_numtoa_const_for_base_on_type {
(
$type_name:ty,
$base:expr,
$core_function_name:ident,
$base_n_function_name:ident,
$padded_function_name:ident,
$filled_function_name:ident,
$required_space_constant_name:ident,
$needed_space_bytes:expr
) => {
#[doc = concat!("the number of space in bytes required to convert an arbitrary [", stringify!($type_name), "] to its ASCII representation in base ", $base)]
pub const $required_space_constant_name: usize = $needed_space_bytes;
#[doc = concat!("converts the specified [", stringify!($type_name), "] to its ASCII representation in base ", $base)]
pub const fn $base_n_function_name(
num: $type_name,
) -> AsciiNumber<{ Self::$required_space_constant_name }> {
let mut string = [0_u8; Self::$required_space_constant_name];
let start = Self::$required_space_constant_name
- $core_function_name(num, $base, &mut string).len();
return AsciiNumber { string, start };
}
#[doc = concat!("converts the specified [", stringify!($type_name), "] to its ASCII representation in base ", $base, ", left-padded to the specified length with the provided byte")]
pub const fn $padded_function_name<const LENGTH: usize>(
num: $type_name,
padding: u8,
) -> AsciiNumber<LENGTH> {
const { assert!(LENGTH >= { Self::$required_space_constant_name }) }
let mut string = [padding; LENGTH];
let _ = $core_function_name(num, $base, &mut string);
return AsciiNumber { string, start: 0 };
}
#[doc = concat!("converts the specified [", stringify!($type_name), "] to its ASCII representation in base ", $base, ", left-filled to the specified length with the provided byte")]
pub const fn $filled_function_name<const LENGTH: usize>(
num: $type_name,
fill: u8,
) -> AsciiNumber<{ Self::$required_space_constant_name }> {
const { assert!(LENGTH <= { Self::$required_space_constant_name }) }
let mut string = [fill; Self::$required_space_constant_name];
let start = Self::$required_space_constant_name
- const_max(LENGTH, $core_function_name(num, $base, &mut string).len());
return AsciiNumber { string, start };
}
};
}
macro_rules! impl_numtoa_const_for_base_n {
($base_value:expr) => {
impl BaseN<$base_value> {
impl_numtoa_const_for_base_on_type!(
u8,
$base_value,
numtoa_u8,
u8,
u8_padded,
u8_filled,
REQUIRED_SPACE_U8,
required_space($base_value as u128, u8::MAX as u128, false).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
u16,
$base_value,
numtoa_u16,
u16,
u16_padded,
u16_filled,
REQUIRED_SPACE_U16,
required_space($base_value as u128, u16::MAX as u128, false).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
u32,
$base_value,
numtoa_u32,
u32,
u32_padded,
u32_filled,
REQUIRED_SPACE_U32,
required_space($base_value as u128, u32::MAX as u128, false).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
u64,
$base_value,
numtoa_u64,
u64,
u64_padded,
u64_filled,
REQUIRED_SPACE_U64,
required_space($base_value as u128, u64::MAX as u128, false).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
u128,
$base_value,
numtoa_u128,
u128,
u128_padded,
u128_filled,
REQUIRED_SPACE_U128,
required_space($base_value as u128, u128::MAX as u128, false).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
usize,
$base_value,
numtoa_usize,
usize,
usize_padded,
usize_filled,
REQUIRED_SPACE_USIZE,
required_space($base_value as u128, usize::MAX as u128, false).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
i8,
$base_value,
numtoa_i8,
i8,
i8_padded,
i8_filled,
REQUIRED_SPACE_I8,
required_space($base_value as u128, i8::MIN.unsigned_abs() as u128, true).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
i16,
$base_value,
numtoa_i16,
i16,
i16_padded,
i16_filled,
REQUIRED_SPACE_I16,
required_space($base_value as u128, i16::MIN.unsigned_abs() as u128, true).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
i32,
$base_value,
numtoa_i32,
i32,
i32_padded,
i32_filled,
REQUIRED_SPACE_I32,
required_space($base_value as u128, i32::MIN.unsigned_abs() as u128, true).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
i64,
$base_value,
numtoa_i64,
i64,
i64_padded,
i64_filled,
REQUIRED_SPACE_I64,
required_space($base_value as u128, i64::MIN.unsigned_abs() as u128, true).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
i128,
$base_value,
numtoa_i128,
i128,
i128_padded,
i128_filled,
REQUIRED_SPACE_I128,
required_space($base_value as u128, i128::MIN.unsigned_abs() as u128, true).expect("valid base")
);
impl_numtoa_const_for_base_on_type!(
isize,
$base_value,
numtoa_isize,
isize,
isize_padded,
isize_filled,
REQUIRED_SPACE_ISIZE,
required_space($base_value as u128, isize::MIN.unsigned_abs() as u128, true).expect("valid base")
);
}
};
}
impl_numtoa_const_for_base_n!(2);
impl_numtoa_const_for_base_n!(3);
impl_numtoa_const_for_base_n!(4);
impl_numtoa_const_for_base_n!(5);
impl_numtoa_const_for_base_n!(6);
impl_numtoa_const_for_base_n!(7);
impl_numtoa_const_for_base_n!(8);
impl_numtoa_const_for_base_n!(9);
impl_numtoa_const_for_base_n!(10);
impl_numtoa_const_for_base_n!(11);
impl_numtoa_const_for_base_n!(12);
impl_numtoa_const_for_base_n!(13);
impl_numtoa_const_for_base_n!(14);
impl_numtoa_const_for_base_n!(15);
impl_numtoa_const_for_base_n!(16);
#[test]
fn str_convenience_base2() {
assert_eq!("111110100001111011", BaseN::<2>::i32(256123).as_str());
}
#[test]
fn str_convenience_base8() {
assert_eq!("764173", BaseN::<8>::i32(256123).as_str());
}
#[test]
fn str_convenience_base10() {
assert_eq!("256123", BaseN::<10>::i32(256123).as_str());
}
#[test]
fn str_convenience_base10_padded() {
assert_eq!(
"00000000000000256123",
BaseN::<10>::i32_padded::<20>(256123, b'0').as_str()
);
}
#[test]
fn str_convenience_base10_filled() {
assert_eq!("12", BaseN::<10>::i32_filled::<1>(12, b'0').as_str());
assert_eq!("00012", BaseN::<10>::i32_filled::<5>(12, b'0').as_str());
assert_eq!(
"123456",
BaseN::<10>::i32_filled::<5>(123456, b'0').as_str()
);
}
#[test]
fn str_convenience_base16() {
assert_eq!("3E87B", BaseN::<16>::i32(256123).as_str());
}
#[test]
fn str_convenience_base16_padded() {
assert_eq!(
"0000000000000003E87B",
BaseN::<16>::i32_padded::<20>(256123, b'0').as_str()
);
}
#[test]
fn str_convenience_base16_filled() {
assert_eq!("7B", BaseN::<16>::i32_filled::<1>(123, b'0').as_str());
assert_eq!("0007B", BaseN::<16>::i32_filled::<5>(123, b'0').as_str());
assert_eq!(
"BC614E",
BaseN::<16>::i32_filled::<5>(12345678, b'0').as_str()
);
}
#[test]
fn str_convenience_wacky_padding() {
assert_eq!(
"##############-3E87B",
BaseN::<16>::i32_padded::<20>(-256123, b'#').as_str()
);
assert_eq!(
"@@@@@@@@@@@-111",
BaseN::<10>::i8_padded::<15>(-111, b'@').as_str()
);
}
#[test]
fn str_convenience_wacky_filling() {
assert_eq!(
"##-3E87B",
BaseN::<16>::i32_filled::<8>(-256123, b'#').as_str()
);
assert_eq!("@-3", BaseN::<10>::i8_filled::<3>(-3, b'@').as_str());
assert_eq!("-11", BaseN::<10>::i8_filled::<3>(-11, b'@').as_str());
}
#[test]
fn base10_i8_all_base10() {
for i in i8::MIN..i8::MAX {
let _ = BaseN::<10>::i8(i);
}
}
#[test]
fn base16_i8_all_base16() {
for i in i8::MIN..i8::MAX {
let _ = BaseN::<16>::i8(i);
}
}
#[test]
fn base10_u8_all_base10() {
for i in u8::MIN..u8::MAX {
let _ = BaseN::<10>::u8(i);
}
}
#[test]
fn base16_u8_all_base16() {
for i in u8::MIN..u8::MAX {
let _ = BaseN::<16>::u8(i);
}
}