1
Fork 0

faster parsing when not possible to overflow

This commit is contained in:
Giles Cope 2021-05-10 21:56:27 +01:00 committed by gilescope
parent bc881e83d1
commit 0a11090053
No known key found for this signature in database
GPG key ID: 631F6352D4A949EF

View file

@ -970,12 +970,14 @@ pub enum FpCategory {
#[doc(hidden)]
trait FromStrRadixHelper: PartialOrd + Copy {
fn min_value() -> Self;
fn max_value() -> Self;
const MIN: Self;
fn from_u32(u: u32) -> Self;
fn checked_mul(&self, other: u32) -> Option<Self>;
fn checked_sub(&self, other: u32) -> Option<Self>;
fn checked_add(&self, other: u32) -> Option<Self>;
unsafe fn unchecked_mul(&self, other: u32) -> Self;
unsafe fn unchecked_sub(&self, other: u32) -> Self;
unsafe fn unchecked_add(&self, other: u32) -> Self;
}
macro_rules! from_str_radix_int_impl {
@ -993,10 +995,7 @@ from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
macro_rules! doit {
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
#[inline]
fn min_value() -> Self { Self::MIN }
#[inline]
fn max_value() -> Self { Self::MAX }
const MIN: Self = Self::MIN;
#[inline]
fn from_u32(u: u32) -> Self { u as Self }
#[inline]
@ -1011,6 +1010,27 @@ macro_rules! doit {
fn checked_add(&self, other: u32) -> Option<Self> {
Self::checked_add(*self, other as Self)
}
#[inline]
unsafe fn unchecked_mul(&self, other: u32) -> Self {
// SAFETY: Conditions of `Self::unchecked_mul` must be upheld by the caller.
unsafe {
Self::unchecked_mul(*self, other as Self)
}
}
#[inline]
unsafe fn unchecked_sub(&self, other: u32) -> Self {
// SAFETY: Conditions of `Self::unchecked_sub` must be upheld by the caller.
unsafe {
Self::unchecked_sub(*self, other as Self)
}
}
#[inline]
unsafe fn unchecked_add(&self, other: u32) -> Self {
// SAFETY: Conditions of `Self::unchecked_add` must be upheld by the caller.
unsafe {
Self::unchecked_add(*self, other as Self)
}
}
})*)
}
doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
@ -1029,7 +1049,7 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
return Err(PIE { kind: Empty });
}
let is_signed_ty = T::from_u32(0) > T::min_value();
let is_signed_ty = T::from_u32(0) > T::MIN;
// all valid digits are ascii, so we will just iterate over the utf8 bytes
// and cast them to chars. .to_digit() will safely return None for anything
@ -1047,37 +1067,32 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
};
let mut result = T::from_u32(0);
if is_positive {
// The number is positive
for &c in digits {
let x = match (c as char).to_digit(radix) {
Some(x) => x,
None => return Err(PIE { kind: InvalidDigit }),
};
result = match result.checked_mul(radix) {
Some(result) => result,
None => return Err(PIE { kind: PosOverflow }),
};
result = match result.checked_add(x) {
Some(result) => result,
None => return Err(PIE { kind: PosOverflow }),
};
if radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize {
// SAFETY: Consider the highest radix of 16:
// `u8::MAX` is `ff` (2 characters), `u16::MAX` is `ffff` (4 characters)
// We can be sure that any src len of 2 would fit in a u8 so we don't need
// to check for overflow.
unsafe {
let unchecked_additive_op =
if is_positive { T::unchecked_add } else { T::unchecked_sub };
for &c in digits {
result = result.unchecked_mul(radix);
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
result = unchecked_additive_op(&result, x);
}
}
} else {
// The number is negative
let additive_op = if is_positive { T::checked_add } else { T::checked_sub };
let overflow_err = || PIE { kind: if is_positive { PosOverflow } else { NegOverflow } };
for &c in digits {
let x = match (c as char).to_digit(radix) {
Some(x) => x,
None => return Err(PIE { kind: InvalidDigit }),
};
result = match result.checked_mul(radix) {
Some(result) => result,
None => return Err(PIE { kind: NegOverflow }),
};
result = match result.checked_sub(x) {
Some(result) => result,
None => return Err(PIE { kind: NegOverflow }),
};
let mul = result.checked_mul(radix);
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
// multiply done early for performance reasons.
result = mul.ok_or_else(overflow_err)?;
result = additive_op(&result, x).ok_or_else(overflow_err)?;
}
}
Ok(result)