use crate::{
FromDigit, FromHexDigit, FromRadix10, FromRadix10Checked, FromRadix10Signed,
FromRadix10SignedChecked, FromRadix16, FromRadix16Checked, Sign,
};
use core::cmp::min;
macro_rules! impl_traits_using_integer {
($t:ident) => {
impl FromRadix10 for $t {
#[inline]
fn from_radix_10(text: &[u8]) -> (Self, usize) {
let mut index = 0;
let mut number = 0;
while index != text.len() {
if let Some(digit) = $t::from_digit(text[index]) {
number *= 10;
number += digit;
index += 1;
} else {
break;
}
}
(number, index)
}
}
impl FromRadix10Signed for $t {
#[inline]
fn from_radix_10_signed(text: &[u8]) -> (Self, usize) {
let mut index;
let mut number = 0;
let (sign, offset) = text
.first()
.and_then(|&byte| Sign::try_from(byte))
.map(|sign| (sign, 1))
.unwrap_or((Sign::Plus, 0));
index = offset;
match sign {
Sign::Plus => {
while index != text.len() {
if let Some(digit) = $t::from_digit(text[index]) {
number *= 10;
number += digit;
index += 1;
} else {
break;
}
}
}
Sign::Minus => {
while index != text.len() {
if let Some(digit) = $t::from_digit(text[index]) {
number *= 10;
number -= digit;
index += 1;
} else {
break;
}
}
}
}
(number, index)
}
}
impl FromRadix10Checked for $t {
#[inline]
fn from_radix_10_checked(text: &[u8]) -> (Option<Self>, usize) {
let (number, mut index) = $t::from_radix_10(
&text[..min(text.len(), $t::NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10)],
);
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = $t::from_digit(text[index]) {
number = number.and_then(|n| n.checked_mul(10));
number = number.and_then(|n| n.checked_add(digit));
index += 1;
} else {
break;
}
}
(number, index)
}
}
impl FromRadix10SignedChecked for $t {
#[inline]
fn from_radix_10_signed_checked(text: &[u8]) -> (Option<Self>, usize) {
let mut index;
let mut number = 0;
let (sign, offset) = text
.first()
.and_then(|&byte| Sign::try_from(byte))
.map(|sign| (sign, 1))
.unwrap_or((Sign::Plus, 0));
index = offset;
match sign {
Sign::Plus => {
let max_safe_index = min(
text.len(),
$t::NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10 + offset,
);
while index != max_safe_index {
if let Some(digit) = $t::from_digit(text[index]) {
number *= 10;
number += digit;
index += 1;
} else {
break;
}
}
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = $t::from_digit(text[index]) {
number = number.and_then(|n| n.checked_mul(10));
number = number.and_then(|n| n.checked_add(digit));
index += 1;
} else {
break;
}
}
(number, index)
}
Sign::Minus => {
let max_safe_index = min(
text.len(),
$t::NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10 + offset,
);
while index != max_safe_index {
if let Some(digit) = $t::from_digit(text[index]) {
number *= 10;
number -= digit;
index += 1;
} else {
break;
}
}
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = $t::from_digit(text[index]) {
number = number.and_then(|n| n.checked_mul(10));
number = number.and_then(|n| n.checked_sub(digit));
index += 1;
} else {
break;
}
}
(number, index)
}
}
}
}
impl FromRadix16 for $t {
#[inline]
fn from_radix_16(text: &[u8]) -> (Self, usize) {
let mut index = 0;
let mut number = 0;
while index != text.len() {
if let Some(digit) = $t::from_hex_digit(text[index]) {
number *= 16;
number += digit;
index += 1;
} else {
break;
}
}
(number, index)
}
}
impl FromRadix16Checked for $t {
#[inline]
fn from_radix_16_checked(text: &[u8]) -> (Option<Self>, usize) {
let (number, mut index) = $t::from_radix_16(
&text[..min(text.len(), $t::NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16)],
);
let mut number = Some(number);
while index != text.len() {
if let Some(digit) = $t::from_hex_digit(text[index]) {
number = number.and_then(|n| n.checked_mul(16));
number = number.and_then(|n| n.checked_add(digit));
index += 1;
} else {
break;
}
}
(number, index)
}
}
impl FromDigit for $t {
#[inline]
fn from_digit(digit: u8) -> Option<Self> {
if matches!(digit, b'0'..=b'9') {
Some((digit - b'0') as $t)
} else {
None
}
}
}
impl FromHexDigit for $t {
#[inline]
fn from_hex_digit(digit: u8) -> Option<Self> {
let mask = 0b_1101_1111;
if matches!(digit, b'0'..=b'9') {
Some((digit - b'0') as $t)
} else if matches!(digit & mask, b'A'..=b'F') {
Some(((digit & mask) - 55) as $t)
} else {
None
}
}
}
};
}
impl_traits_using_integer!(i8);
impl_traits_using_integer!(u8);
impl_traits_using_integer!(i16);
impl_traits_using_integer!(u16);
impl_traits_using_integer!(i32);
impl_traits_using_integer!(u32);
impl_traits_using_integer!(i64);
impl_traits_using_integer!(u64);
impl_traits_using_integer!(i128);
impl_traits_using_integer!(u128);
trait SafeDigits {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize;
}
impl SafeDigits for i8 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 2;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 1;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 2;
}
impl SafeDigits for u8 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 2;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 2;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 0;
}
impl SafeDigits for i16 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 4;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 3;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 5;
}
impl SafeDigits for u16 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 4;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 4;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 0;
}
impl SafeDigits for i32 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 9;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 7;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 9;
}
impl SafeDigits for u32 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 9;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 8;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 0;
}
impl SafeDigits for i64 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 18;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 15;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 18;
}
impl SafeDigits for u64 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 19;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 16;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 0;
}
impl SafeDigits for i128 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 38;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 31;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 38;
}
impl SafeDigits for u128 {
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_10: usize = 38;
const NUM_SAFE_DIGITS_NON_NEGATIVE_RADIX_16: usize = 32;
const NUM_SAFE_DIGITS_NON_POSITIVE_RADIX_10: usize = 0;
}