From: Giles Cope Date: Mon, 10 May 2021 20:56:27 +0000 (+0100) Subject: faster parsing when not possible to overflow X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=0a11090053d73af94de3c1a96d125c4980a16626;p=rust.git faster parsing when not possible to overflow --- diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index dca8ffa4e2c..5fd697fd428 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -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; fn checked_sub(&self, other: u32) -> Option; fn checked_add(&self, other: u32) -> Option; + 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 @@ fn from_str(src: &str) -> Result { 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 @@ fn checked_sub(&self, other: u32) -> Option { fn checked_add(&self, other: u32) -> Option { 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(src: &str, radix: u32) -> Result 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(src: &str, radix: u32) -> Result 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::() * 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)