5
5
use crate :: ascii;
6
6
use crate :: intrinsics;
7
7
use crate :: mem;
8
+ use crate :: ops:: { Add , Mul , Sub } ;
8
9
use crate :: str:: FromStr ;
9
10
10
11
// Used because the `?` operator is not allowed in a const context.
@@ -954,9 +955,10 @@ pub enum FpCategory {
954
955
}
955
956
956
957
#[ doc( hidden) ]
957
- trait FromStrRadixHelper : PartialOrd + Copy {
958
- fn min_value ( ) -> Self ;
959
- fn max_value ( ) -> Self ;
958
+ trait FromStrRadixHelper :
959
+ PartialOrd + Copy + Add < Output = Self > + Sub < Output = Self > + Mul < Output = Self >
960
+ {
961
+ const MIN : Self ;
960
962
fn from_u32 ( u : u32 ) -> Self ;
961
963
fn checked_mul ( & self , other : u32 ) -> Option < Self > ;
962
964
fn checked_sub ( & self , other : u32 ) -> Option < Self > ;
@@ -976,12 +978,9 @@ macro_rules! from_str_radix_int_impl {
976
978
}
977
979
from_str_radix_int_impl ! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 }
978
980
979
- macro_rules! doit {
981
+ macro_rules! impl_helper_for {
980
982
( $( $t: ty) * ) => ( $( impl FromStrRadixHelper for $t {
981
- #[ inline]
982
- fn min_value( ) -> Self { Self :: MIN }
983
- #[ inline]
984
- fn max_value( ) -> Self { Self :: MAX }
983
+ const MIN : Self = Self :: MIN ;
985
984
#[ inline]
986
985
fn from_u32( u: u32 ) -> Self { u as Self }
987
986
#[ inline]
@@ -998,7 +997,18 @@ macro_rules! doit {
998
997
}
999
998
} ) * )
1000
999
}
1001
- doit ! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
1000
+ impl_helper_for ! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
1001
+
1002
+ /// Determins if a string of text of that length of that radix could be guaranteed to be
1003
+ /// stored in the given type T.
1004
+ /// Note that if the radix is known to the compiler, it is just the check of digits.len that
1005
+ /// is done at runtime.
1006
+ #[ doc( hidden) ]
1007
+ #[ inline( always) ]
1008
+ #[ unstable( issue = "none" , feature = "std_internals" ) ]
1009
+ pub fn can_not_overflow < T > ( radix : u32 , is_signed_ty : bool , digits : & [ u8 ] ) -> bool {
1010
+ radix <= 16 && digits. len ( ) <= mem:: size_of :: < T > ( ) * 2 - is_signed_ty as usize
1011
+ }
1002
1012
1003
1013
fn from_str_radix < T : FromStrRadixHelper > ( src : & str , radix : u32 ) -> Result < T , ParseIntError > {
1004
1014
use self :: IntErrorKind :: * ;
@@ -1014,7 +1024,7 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
1014
1024
return Err ( PIE { kind : Empty } ) ;
1015
1025
}
1016
1026
1017
- let is_signed_ty = T :: from_u32 ( 0 ) > T :: min_value ( ) ;
1027
+ let is_signed_ty = T :: from_u32 ( 0 ) > T :: MIN ;
1018
1028
1019
1029
// all valid digits are ascii, so we will just iterate over the utf8 bytes
1020
1030
// and cast them to chars. .to_digit() will safely return None for anything
@@ -1032,38 +1042,56 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
1032
1042
} ;
1033
1043
1034
1044
let mut result = T :: from_u32 ( 0 ) ;
1035
- if is_positive {
1036
- // The number is positive
1037
- for & c in digits {
1038
- let x = match ( c as char ) . to_digit ( radix) {
1039
- Some ( x) => x,
1040
- None => return Err ( PIE { kind : InvalidDigit } ) ,
1041
- } ;
1042
- result = match result. checked_mul ( radix) {
1043
- Some ( result) => result,
1044
- None => return Err ( PIE { kind : PosOverflow } ) ,
1045
- } ;
1046
- result = match result. checked_add ( x) {
1047
- Some ( result) => result,
1048
- None => return Err ( PIE { kind : PosOverflow } ) ,
1045
+
1046
+ if can_not_overflow :: < T > ( radix, is_signed_ty, digits) {
1047
+ // If the len of the str is short compared to the range of the type
1048
+ // we are parsing into, then we can be certain that an overflow will not occur.
1049
+ // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition
1050
+ // above is a faster (conservative) approximation of this.
1051
+ //
1052
+ // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest:
1053
+ // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow.
1054
+ // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow.
1055
+ macro_rules! run_unchecked_loop {
1056
+ ( $unchecked_additive_op: expr) => {
1057
+ for & c in digits {
1058
+ result = result * T :: from_u32( radix) ;
1059
+ let x = ( c as char ) . to_digit( radix) . ok_or( PIE { kind: InvalidDigit } ) ?;
1060
+ result = $unchecked_additive_op( result, T :: from_u32( x) ) ;
1061
+ }
1049
1062
} ;
1050
1063
}
1064
+ if is_positive {
1065
+ run_unchecked_loop ! ( <T as core:: ops:: Add >:: add)
1066
+ } else {
1067
+ run_unchecked_loop ! ( <T as core:: ops:: Sub >:: sub)
1068
+ } ;
1051
1069
} else {
1052
- // The number is negative
1053
- for & c in digits {
1054
- let x = match ( c as char ) . to_digit ( radix) {
1055
- Some ( x) => x,
1056
- None => return Err ( PIE { kind : InvalidDigit } ) ,
1057
- } ;
1058
- result = match result. checked_mul ( radix) {
1059
- Some ( result) => result,
1060
- None => return Err ( PIE { kind : NegOverflow } ) ,
1061
- } ;
1062
- result = match result. checked_sub ( x) {
1063
- Some ( result) => result,
1064
- None => return Err ( PIE { kind : NegOverflow } ) ,
1070
+ macro_rules! run_checked_loop {
1071
+ ( $checked_additive_op: ident, $overflow_err: expr) => {
1072
+ for & c in digits {
1073
+ // When `radix` is passed in as a literal, rather than doing a slow `imul`
1074
+ // the compiler can use shifts if `radix` can be expressed as a
1075
+ // sum of powers of 2 (x*10 can be written as x*8 + x*2).
1076
+ // When the compiler can't use these optimisations,
1077
+ // the latency of the multiplication can be hidden by issuing it
1078
+ // before the result is needed to improve performance on
1079
+ // modern out-of-order CPU as multiplication here is slower
1080
+ // than the other instructions, we can get the end result faster
1081
+ // doing multiplication first and let the CPU spends other cycles
1082
+ // doing other computation and get multiplication result later.
1083
+ let mul = result. checked_mul( radix) ;
1084
+ let x = ( c as char ) . to_digit( radix) . ok_or( PIE { kind: InvalidDigit } ) ?;
1085
+ result = mul. ok_or_else( $overflow_err) ?;
1086
+ result = T :: $checked_additive_op( & result, x) . ok_or_else( $overflow_err) ?;
1087
+ }
1065
1088
} ;
1066
1089
}
1090
+ if is_positive {
1091
+ run_checked_loop ! ( checked_add, || PIE { kind: PosOverflow } )
1092
+ } else {
1093
+ run_checked_loop ! ( checked_sub, || PIE { kind: NegOverflow } )
1094
+ } ;
1067
1095
}
1068
1096
Ok ( result)
1069
1097
}
0 commit comments