use crate::num::conversion::traits::{FromStringBase, WrappingFrom};
pub const fn digit_from_display_byte(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'z' => Some(b - b'a' + 10),
b'A'..=b'Z' => Some(b - b'A' + 10),
_ => None,
}
}
pub const fn digit_from_display_byte_large(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'A'..=b'Z' => Some(b - b'A' + 10),
b'a'..=b'z' => Some(b - b'a' + 36),
_ => None,
}
}
macro_rules! impl_from_string_base_unsigned {
($t:ident) => {
impl FromStringBase for $t {
fn from_string_base(base: u8, s: &str) -> Option<Self> {
assert!((2..=62).contains(&base), "base out of range");
if base <= 36 {
return $t::from_str_radix(s, u32::from(base)).ok();
}
let s = s.strip_prefix('+').unwrap_or(s);
if s.is_empty() {
return None;
}
let t_base = $t::wrapping_from(base);
let mut x: $t = 0;
for b in s.bytes() {
let digit = digit_from_display_byte_large(b)?;
if digit >= base {
return None;
}
x = x
.checked_mul(t_base)?
.checked_add($t::wrapping_from(digit))?;
}
Some(x)
}
}
};
}
apply_to_unsigneds!(impl_from_string_base_unsigned);
macro_rules! impl_from_string_base_signed {
($t:ident) => {
impl FromStringBase for $t {
fn from_string_base(base: u8, s: &str) -> Option<Self> {
assert!((2..=62).contains(&base), "base out of range");
if base <= 36 {
return $t::from_str_radix(s, u32::from(base)).ok();
}
let (neg, s) = if let Some(r) = s.strip_prefix('-') {
(true, r)
} else {
(false, s.strip_prefix('+').unwrap_or(s))
};
if s.is_empty() {
return None;
}
let t_base = $t::wrapping_from(base);
let mut x: $t = 0;
for b in s.bytes() {
let digit = digit_from_display_byte_large(b)?;
if digit >= base {
return None;
}
let t_digit = $t::wrapping_from(digit);
x = x.checked_mul(t_base)?;
x = if neg {
x.checked_sub(t_digit)?
} else {
x.checked_add(t_digit)?
};
}
Some(x)
}
}
};
}
apply_to_signeds!(impl_from_string_base_signed);