Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 0a11090

Browse files
committed
faster parsing when not possible to overflow
1 parent bc881e8 commit 0a11090

File tree

1 file changed

+50
-35
lines changed

1 file changed

+50
-35
lines changed

library/core/src/num/mod.rs

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -970,12 +970,14 @@ pub enum FpCategory {
970970

971971
#[doc(hidden)]
972972
trait FromStrRadixHelper: PartialOrd + Copy {
973-
fn min_value() -> Self;
974-
fn max_value() -> Self;
973+
const MIN: Self;
975974
fn from_u32(u: u32) -> Self;
976975
fn checked_mul(&self, other: u32) -> Option<Self>;
977976
fn checked_sub(&self, other: u32) -> Option<Self>;
978977
fn checked_add(&self, other: u32) -> Option<Self>;
978+
unsafe fn unchecked_mul(&self, other: u32) -> Self;
979+
unsafe fn unchecked_sub(&self, other: u32) -> Self;
980+
unsafe fn unchecked_add(&self, other: u32) -> Self;
979981
}
980982

981983
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 }
993995

994996
macro_rules! doit {
995997
($($t:ty)*) => ($(impl FromStrRadixHelper for $t {
996-
#[inline]
997-
fn min_value() -> Self { Self::MIN }
998-
#[inline]
999-
fn max_value() -> Self { Self::MAX }
998+
const MIN: Self = Self::MIN;
1000999
#[inline]
10011000
fn from_u32(u: u32) -> Self { u as Self }
10021001
#[inline]
@@ -1011,6 +1010,27 @@ macro_rules! doit {
10111010
fn checked_add(&self, other: u32) -> Option<Self> {
10121011
Self::checked_add(*self, other as Self)
10131012
}
1013+
#[inline]
1014+
unsafe fn unchecked_mul(&self, other: u32) -> Self {
1015+
// SAFETY: Conditions of `Self::unchecked_mul` must be upheld by the caller.
1016+
unsafe {
1017+
Self::unchecked_mul(*self, other as Self)
1018+
}
1019+
}
1020+
#[inline]
1021+
unsafe fn unchecked_sub(&self, other: u32) -> Self {
1022+
// SAFETY: Conditions of `Self::unchecked_sub` must be upheld by the caller.
1023+
unsafe {
1024+
Self::unchecked_sub(*self, other as Self)
1025+
}
1026+
}
1027+
#[inline]
1028+
unsafe fn unchecked_add(&self, other: u32) -> Self {
1029+
// SAFETY: Conditions of `Self::unchecked_add` must be upheld by the caller.
1030+
unsafe {
1031+
Self::unchecked_add(*self, other as Self)
1032+
}
1033+
}
10141034
})*)
10151035
}
10161036
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
10291049
return Err(PIE { kind: Empty });
10301050
}
10311051

1032-
let is_signed_ty = T::from_u32(0) > T::min_value();
1052+
let is_signed_ty = T::from_u32(0) > T::MIN;
10331053

10341054
// all valid digits are ascii, so we will just iterate over the utf8 bytes
10351055
// 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
10471067
};
10481068

10491069
let mut result = T::from_u32(0);
1050-
if is_positive {
1051-
// The number is positive
1052-
for &c in digits {
1053-
let x = match (c as char).to_digit(radix) {
1054-
Some(x) => x,
1055-
None => return Err(PIE { kind: InvalidDigit }),
1056-
};
1057-
result = match result.checked_mul(radix) {
1058-
Some(result) => result,
1059-
None => return Err(PIE { kind: PosOverflow }),
1060-
};
1061-
result = match result.checked_add(x) {
1062-
Some(result) => result,
1063-
None => return Err(PIE { kind: PosOverflow }),
1064-
};
1070+
1071+
if radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize {
1072+
// SAFETY: Consider the highest radix of 16:
1073+
// `u8::MAX` is `ff` (2 characters), `u16::MAX` is `ffff` (4 characters)
1074+
// We can be sure that any src len of 2 would fit in a u8 so we don't need
1075+
// to check for overflow.
1076+
unsafe {
1077+
let unchecked_additive_op =
1078+
if is_positive { T::unchecked_add } else { T::unchecked_sub };
1079+
1080+
for &c in digits {
1081+
result = result.unchecked_mul(radix);
1082+
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
1083+
result = unchecked_additive_op(&result, x);
1084+
}
10651085
}
10661086
} else {
1067-
// The number is negative
1087+
let additive_op = if is_positive { T::checked_add } else { T::checked_sub };
1088+
let overflow_err = || PIE { kind: if is_positive { PosOverflow } else { NegOverflow } };
1089+
10681090
for &c in digits {
1069-
let x = match (c as char).to_digit(radix) {
1070-
Some(x) => x,
1071-
None => return Err(PIE { kind: InvalidDigit }),
1072-
};
1073-
result = match result.checked_mul(radix) {
1074-
Some(result) => result,
1075-
None => return Err(PIE { kind: NegOverflow }),
1076-
};
1077-
result = match result.checked_sub(x) {
1078-
Some(result) => result,
1079-
None => return Err(PIE { kind: NegOverflow }),
1080-
};
1091+
let mul = result.checked_mul(radix);
1092+
let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?;
1093+
// multiply done early for performance reasons.
1094+
result = mul.ok_or_else(overflow_err)?;
1095+
result = additive_op(&result, x).ok_or_else(overflow_err)?;
10811096
}
10821097
}
10831098
Ok(result)

0 commit comments

Comments
 (0)