From 138b76b83a067284f25e1f8971600aaf49206816 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 17:27:11 +1100 Subject: [PATCH 1/5] Separate string->integer implementation in strconv --- src/librustc/metadata/decoder.rs | 6 +- src/librustc/metadata/tydecode.rs | 5 +- src/libstd/num/f32.rs | 6 +- src/libstd/num/f64.rs | 6 +- src/libstd/num/int_macros.rs | 94 ++++++++------------- src/libstd/num/strconv.rs | 135 +++++++++++++++--------------- src/libstd/num/uint_macros.rs | 71 +++++----------- src/test/bench/shootout-pfib.rs | 4 +- 8 files changed, 138 insertions(+), 189 deletions(-) diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 213f32a1d182c..fa8f7c9ad939c 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -36,7 +36,7 @@ use std::io::extensions::u64_from_be_bytes; use std::io; use std::collections::hashmap::HashMap; use std::rc::Rc; -use std::u64; +use std::str; use rbml::reader; use rbml; use serialize::Decodable; @@ -215,7 +215,9 @@ fn each_reexport(d: rbml::Doc, f: |rbml::Doc| -> bool) -> bool { fn variant_disr_val(d: rbml::Doc) -> Option { reader::maybe_get_doc(d, tag_disr_val).and_then(|val_doc| { - reader::with_doc_data(val_doc, |data| u64::parse_bytes(data, 10u)) + reader::with_doc_data(val_doc, |data| { + str::from_utf8(data).and_then(from_str) + }) }) } diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index a52d02ccca773..6e7a6dfa09448 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -23,7 +23,6 @@ use middle::ty; use std::rc::Rc; use std::str; use std::string::String; -use std::uint; use syntax::abi; use syntax::ast; use syntax::ast::*; @@ -615,12 +614,12 @@ pub fn parse_def_id(buf: &[u8]) -> ast::DefId { let crate_part = buf[0u..colon_idx]; let def_part = buf[colon_idx + 1u..len]; - let crate_num = match uint::parse_bytes(crate_part, 10u) { + let crate_num = match str::from_utf8(crate_part).and_then(from_str::) { Some(cn) => cn as ast::CrateNum, None => panic!("internal error: parse_def_id: crate number expected, found {}", crate_part) }; - let def_num = match uint::parse_bytes(def_part, 10u) { + let def_num = match str::from_utf8(def_part).and_then(from_str::) { Some(dn) => dn as ast::NodeId, None => panic!("internal error: parse_def_id: id expected, found {}", def_part) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 0b2f17b8f93cc..87c28c9362c63 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -361,7 +361,7 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { #[inline] pub fn from_str_hex(num: &str) -> Option { strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false, false) + strconv::ExpBin, false) } impl FromStr for f32 { @@ -393,7 +393,7 @@ impl FromStr for f32 { #[inline] fn from_str(val: &str) -> Option { strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false, false) + strconv::ExpDec, false) } } @@ -418,7 +418,7 @@ impl num::FromStrRadix for f32 { #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false, false) + strconv::ExpNone, false) } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 35555b140815a..907c860f6a39f 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -369,7 +369,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { #[inline] pub fn from_str_hex(num: &str) -> Option { strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false, false) + strconv::ExpBin, false) } impl FromStr for f64 { @@ -401,7 +401,7 @@ impl FromStr for f64 { #[inline] fn from_str(val: &str) -> Option { strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false, false) + strconv::ExpDec, false) } } @@ -426,7 +426,7 @@ impl num::FromStrRadix for f64 { #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false, false) + strconv::ExpNone, false) } } diff --git a/src/libstd/num/int_macros.rs b/src/libstd/num/int_macros.rs index ca45b40e687a1..9ae146c840ae8 100644 --- a/src/libstd/num/int_macros.rs +++ b/src/libstd/num/int_macros.rs @@ -14,31 +14,11 @@ macro_rules! int_module (($T:ty) => ( -// String conversion functions and impl str -> num - -/// Parse a byte slice as a number in the given base -/// -/// Yields an `Option` because `buf` may or may not actually be parseable. -/// -/// # Examples -/// -/// ``` -/// let num = std::i64::parse_bytes([49,50,51,52,53,54,55,56,57], 10); -/// assert!(num == Some(123456789)); -/// ``` -#[inline] -#[experimental = "might need to return Result"] -pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<$T> { - strconv::from_str_bytes_common(buf, radix, true, false, false, - strconv::ExpNone, false, false) -} - #[experimental = "might need to return Result"] impl FromStr for $T { #[inline] fn from_str(s: &str) -> Option<$T> { - strconv::from_str_common(s, 10u, true, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, 10) } } @@ -46,18 +26,14 @@ impl FromStr for $T { impl FromStrRadix for $T { #[inline] fn from_str_radix(s: &str, radix: uint) -> Option<$T> { - strconv::from_str_common(s, radix, true, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, radix) } } #[cfg(test)] mod tests { use prelude::*; - use super::*; - - use i32; - use str::StrSlice; + use num::FromStrRadix; #[test] fn test_from_str() { @@ -73,33 +49,33 @@ mod tests { assert_eq!(from_str::("-123456789"), Some(-123456789 as i32)); assert_eq!(from_str::<$T>("-00100"), Some(-100 as $T)); - assert!(from_str::<$T>(" ").is_none()); - assert!(from_str::<$T>("x").is_none()); + assert_eq!(from_str::<$T>(""), None); + assert_eq!(from_str::<$T>(" "), None); + assert_eq!(from_str::<$T>("x"), None); } #[test] - fn test_parse_bytes() { - use str::StrSlice; - assert_eq!(parse_bytes("123".as_bytes(), 10u), Some(123 as $T)); - assert_eq!(parse_bytes("1001".as_bytes(), 2u), Some(9 as $T)); - assert_eq!(parse_bytes("123".as_bytes(), 8u), Some(83 as $T)); - assert_eq!(i32::parse_bytes("123".as_bytes(), 16u), Some(291 as i32)); - assert_eq!(i32::parse_bytes("ffff".as_bytes(), 16u), Some(65535 as i32)); - assert_eq!(i32::parse_bytes("FFFF".as_bytes(), 16u), Some(65535 as i32)); - assert_eq!(parse_bytes("z".as_bytes(), 36u), Some(35 as $T)); - assert_eq!(parse_bytes("Z".as_bytes(), 36u), Some(35 as $T)); - - assert_eq!(parse_bytes("-123".as_bytes(), 10u), Some(-123 as $T)); - assert_eq!(parse_bytes("-1001".as_bytes(), 2u), Some(-9 as $T)); - assert_eq!(parse_bytes("-123".as_bytes(), 8u), Some(-83 as $T)); - assert_eq!(i32::parse_bytes("-123".as_bytes(), 16u), Some(-291 as i32)); - assert_eq!(i32::parse_bytes("-ffff".as_bytes(), 16u), Some(-65535 as i32)); - assert_eq!(i32::parse_bytes("-FFFF".as_bytes(), 16u), Some(-65535 as i32)); - assert_eq!(parse_bytes("-z".as_bytes(), 36u), Some(-35 as $T)); - assert_eq!(parse_bytes("-Z".as_bytes(), 36u), Some(-35 as $T)); - - assert!(parse_bytes("Z".as_bytes(), 35u).is_none()); - assert!(parse_bytes("-9".as_bytes(), 2u).is_none()); + fn test_from_str_radix() { + assert_eq!(FromStrRadix::from_str_radix("123", 10), Some(123 as $T)); + assert_eq!(FromStrRadix::from_str_radix("1001", 2), Some(9 as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 8), Some(83 as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 16), Some(291 as i32)); + assert_eq!(FromStrRadix::from_str_radix("ffff", 16), Some(65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("FFFF", 16), Some(65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("z", 36), Some(35 as $T)); + assert_eq!(FromStrRadix::from_str_radix("Z", 36), Some(35 as $T)); + + assert_eq!(FromStrRadix::from_str_radix("-123", 10), Some(-123 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-1001", 2), Some(-9 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-123", 8), Some(-83 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-123", 16), Some(-291 as i32)); + assert_eq!(FromStrRadix::from_str_radix("-ffff", 16), Some(-65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("-FFFF", 16), Some(-65535 as i32)); + assert_eq!(FromStrRadix::from_str_radix("-z", 36), Some(-35 as $T)); + assert_eq!(FromStrRadix::from_str_radix("-Z", 36), Some(-35 as $T)); + + assert_eq!(FromStrRadix::from_str_radix("Z", 35), None::<$T>); + assert_eq!(FromStrRadix::from_str_radix("-9", 2), None::<$T>); } #[test] @@ -133,35 +109,35 @@ mod tests { fn test_int_from_str_overflow() { let mut i8_val: i8 = 127_i8; assert_eq!(from_str::("127"), Some(i8_val)); - assert!(from_str::("128").is_none()); + assert_eq!(from_str::("128"), None); i8_val += 1 as i8; assert_eq!(from_str::("-128"), Some(i8_val)); - assert!(from_str::("-129").is_none()); + assert_eq!(from_str::("-129"), None); let mut i16_val: i16 = 32_767_i16; assert_eq!(from_str::("32767"), Some(i16_val)); - assert!(from_str::("32768").is_none()); + assert_eq!(from_str::("32768"), None); i16_val += 1 as i16; assert_eq!(from_str::("-32768"), Some(i16_val)); - assert!(from_str::("-32769").is_none()); + assert_eq!(from_str::("-32769"), None); let mut i32_val: i32 = 2_147_483_647_i32; assert_eq!(from_str::("2147483647"), Some(i32_val)); - assert!(from_str::("2147483648").is_none()); + assert_eq!(from_str::("2147483648"), None); i32_val += 1 as i32; assert_eq!(from_str::("-2147483648"), Some(i32_val)); - assert!(from_str::("-2147483649").is_none()); + assert_eq!(from_str::("-2147483649"), None); let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; assert_eq!(from_str::("9223372036854775807"), Some(i64_val)); - assert!(from_str::("9223372036854775808").is_none()); + assert_eq!(from_str::("9223372036854775808"), None); i64_val += 1 as i64; assert_eq!(from_str::("-9223372036854775808"), Some(i64_val)); - assert!(from_str::("-9223372036854775809").is_none()); + assert_eq!(from_str::("-9223372036854775809"), None); } } diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 30ecf2284df76..1e70a0e571cf9 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -13,15 +13,18 @@ #![allow(missing_docs)] use char; +use char::Char; use clone::Clone; -use num::{NumCast, Zero, One, cast, Int}; +use from_str::from_str; +use iter::Iterator; +use num::{NumCast, Zero, One, cast, Int, Bounded}; use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use num; use ops::{Add, Sub, Mul, Div, Rem, Neg}; use option::{None, Option, Some}; use slice::{ImmutableSlice, MutableSlice, CloneableVector}; use std::cmp::{PartialOrd, PartialEq}; -use str::StrSlice; +use str::{Str, StrSlice}; use string::String; use vec::Vec; @@ -106,35 +109,11 @@ macro_rules! impl_NumStrConv_Floating (($t:ty) => ( } )) -macro_rules! impl_NumStrConv_Integer (($t:ty) => ( - impl NumStrConv for $t { - #[inline] fn nan() -> Option<$t> { None } - #[inline] fn inf() -> Option<$t> { None } - #[inline] fn neg_inf() -> Option<$t> { None } - #[inline] fn neg_zero() -> Option<$t> { None } - - #[inline] fn round_to_zero(&self) -> $t { *self } - #[inline] fn fractional_part(&self) -> $t { 0 } - } -)) - // FIXME: #4955 // Replace by two generic impls for traits 'Integral' and 'Floating' impl_NumStrConv_Floating!(f32) impl_NumStrConv_Floating!(f64) -impl_NumStrConv_Integer!(int) -impl_NumStrConv_Integer!(i8) -impl_NumStrConv_Integer!(i16) -impl_NumStrConv_Integer!(i32) -impl_NumStrConv_Integer!(i64) - -impl_NumStrConv_Integer!(uint) -impl_NumStrConv_Integer!(u8) -impl_NumStrConv_Integer!(u16) -impl_NumStrConv_Integer!(u32) -impl_NumStrConv_Integer!(u64) - // Special value strings as [u8] consts. static INF_BUF: [u8, ..3] = [b'i', b'n', b'f']; @@ -526,8 +505,6 @@ static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; * `FFp128`. The exponent string itself is always base 10. * Can conflict with `radix`, see Failure. * - `empty_zero` - Whether to accept an empty `buf` as a 0 or not. - * - `ignore_underscores` - Whether all underscores within the string should - * be ignored. * * # Return value * Returns `Some(n)` if `buf` parses to a number n without overflowing, and @@ -548,7 +525,6 @@ pub fn from_str_bytes_common+ NumStrConv+Clone>( buf: &[u8], radix: uint, negative: bool, fractional: bool, special: bool, exponent: ExponentFormat, empty_zero: bool, - ignore_underscores: bool ) -> Option { match exponent { ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' @@ -646,7 +622,6 @@ pub fn from_str_bytes_common+ last_accum = accum.clone(); } None => match c { - '_' if ignore_underscores => {} 'e' | 'E' | 'p' | 'P' => { exp_found = true; break; // start of exponent @@ -690,7 +665,6 @@ pub fn from_str_bytes_common+ last_accum = accum.clone(); } None => match c { - '_' if ignore_underscores => {} 'e' | 'E' | 'p' | 'P' => { exp_found = true; break; // start of exponent @@ -726,9 +700,7 @@ pub fn from_str_bytes_common+ // parse remaining bytes as decimal integer, // skipping the exponent char - let exp: Option = from_str_bytes_common( - buf[i+1..len], 10, true, false, false, ExpNone, false, - ignore_underscores); + let exp = from_str::(String::from_utf8_lossy(buf[i+1..len]).as_slice()); match exp { Some(exp_pow) => { @@ -754,11 +726,65 @@ pub fn from_str_common+Mul Sub+Neg+Add+NumStrConv+Clone>( buf: &str, radix: uint, negative: bool, fractional: bool, special: bool, exponent: ExponentFormat, empty_zero: bool, - ignore_underscores: bool ) -> Option { from_str_bytes_common(buf.as_bytes(), radix, negative, - fractional, special, exponent, empty_zero, - ignore_underscores) + fractional, special, exponent, empty_zero) +} + +pub fn from_str_radix_int(src: &str, radix: uint) -> Option { + fn cast(x: uint) -> T { + num::cast(x).unwrap() + } + + let _0: T = num::zero(); + let _1: T = num::one(); + let is_signed = _0 > Bounded::min_value(); + + let (is_negative, src) = match src.slice_shift_char() { + (Some('-'), src) if is_signed => (true, src), + (Some(_), _) => (false, src), + (None, _) => return None, + }; + + let mut xs = src.chars().map(|c| { + c.to_digit(radix).map(cast) + }); + let radix = cast(radix); + let mut result = _0; + + if is_negative { + for x in xs { + let x = match x { + Some(x) => x, + None => return None, + }; + result = match result.checked_mul(&radix) { + Some(result) => result, + None => return None, + }; + result = match result.checked_sub(&x) { + Some(result) => result, + None => return None, + }; + } + } else { + for x in xs { + let x = match x { + Some(x) => x, + None => return None, + }; + result = match result.checked_mul(&radix) { + Some(result) => result, + None => return None, + }; + result = match result.checked_add(&x) { + Some(result) => result, + None => return None, + }; + } + } + + Some(result) } #[cfg(test)] @@ -766,45 +792,18 @@ mod test { use super::*; use option::*; - #[test] - fn from_str_ignore_underscores() { - let s : Option = from_str_common("__1__", 2, false, false, false, - ExpNone, false, true); - assert_eq!(s, Some(1u8)); - - let n : Option = from_str_common("__1__", 2, false, false, false, - ExpNone, false, false); - assert_eq!(n, None); - - let f : Option = from_str_common("_1_._5_e_1_", 10, false, true, false, - ExpDec, false, true); - assert_eq!(f, Some(1.5e1f32)); - } - - #[test] - fn from_str_issue5770() { - // try to parse 0b1_1111_1111 = 511 as a u8. Caused problems - // since 255*2+1 == 255 (mod 256) so the overflow wasn't - // detected. - let n : Option = from_str_common("111111111", 2, false, false, false, - ExpNone, false, false); - assert_eq!(n, None); - } - #[test] fn from_str_issue7588() { - let u : Option = from_str_common("1000", 10, false, false, false, - ExpNone, false, false); + let u : Option = from_str_radix_int("1000", 10); assert_eq!(u, None); - let s : Option = from_str_common("80000", 10, false, false, false, - ExpNone, false, false); + let s : Option = from_str_radix_int("80000", 10); assert_eq!(s, None); let f : Option = from_str_common( "10000000000000000000000000000000000000000", 10, false, false, false, - ExpNone, false, false); + ExpNone, false); assert_eq!(f, NumStrConv::inf()) let fe : Option = from_str_common("1e40", 10, false, false, false, - ExpDec, false, false); + ExpDec, false); assert_eq!(fe, NumStrConv::inf()) } } diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs index c69c3ffa41c0e..aa8e58bab0286 100644 --- a/src/libstd/num/uint_macros.rs +++ b/src/libstd/num/uint_macros.rs @@ -15,31 +15,11 @@ macro_rules! uint_module (($T:ty) => ( -// String conversion functions and impl str -> num - -/// Parse a byte slice as a number in the given base -/// -/// Yields an `Option` because `buf` may or may not actually be parseable. -/// -/// # Examples -/// -/// ``` -/// let num = std::uint::parse_bytes([49,50,51,52,53,54,55,56,57], 10); -/// assert!(num == Some(123456789)); -/// ``` -#[inline] -#[experimental = "might need to return Result"] -pub fn parse_bytes(buf: &[u8], radix: uint) -> Option<$T> { - strconv::from_str_bytes_common(buf, radix, false, false, false, - strconv::ExpNone, false, false) -} - #[experimental = "might need to return Result"] impl FromStr for $T { #[inline] fn from_str(s: &str) -> Option<$T> { - strconv::from_str_common(s, 10u, false, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, 10) } } @@ -47,8 +27,7 @@ impl FromStr for $T { impl FromStrRadix for $T { #[inline] fn from_str_radix(s: &str, radix: uint) -> Option<$T> { - strconv::from_str_common(s, radix, false, false, false, - strconv::ExpNone, false, false) + strconv::from_str_radix_int(s, radix) } } @@ -85,10 +64,7 @@ pub fn to_str_bytes(n: $T, radix: uint, f: |v: &[u8]| -> U) -> U { #[cfg(test)] mod tests { use prelude::*; - use super::*; - - use str::StrSlice; - use u16; + use num::FromStrRadix; #[test] pub fn test_from_str() { @@ -98,23 +74,22 @@ mod tests { assert_eq!(from_str::("123456789"), Some(123456789 as u32)); assert_eq!(from_str::<$T>("00100"), Some(100u as $T)); - assert!(from_str::<$T>("").is_none()); - assert!(from_str::<$T>(" ").is_none()); - assert!(from_str::<$T>("x").is_none()); + assert_eq!(from_str::<$T>(""), None); + assert_eq!(from_str::<$T>(" "), None); + assert_eq!(from_str::<$T>("x"), None); } #[test] pub fn test_parse_bytes() { - use str::StrSlice; - assert_eq!(parse_bytes("123".as_bytes(), 10u), Some(123u as $T)); - assert_eq!(parse_bytes("1001".as_bytes(), 2u), Some(9u as $T)); - assert_eq!(parse_bytes("123".as_bytes(), 8u), Some(83u as $T)); - assert_eq!(u16::parse_bytes("123".as_bytes(), 16u), Some(291u as u16)); - assert_eq!(u16::parse_bytes("ffff".as_bytes(), 16u), Some(65535u as u16)); - assert_eq!(parse_bytes("z".as_bytes(), 36u), Some(35u as $T)); - - assert!(parse_bytes("Z".as_bytes(), 10u).is_none()); - assert!(parse_bytes("_".as_bytes(), 2u).is_none()); + assert_eq!(FromStrRadix::from_str_radix("123", 10), Some(123u as $T)); + assert_eq!(FromStrRadix::from_str_radix("1001", 2), Some(9u as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 8), Some(83u as $T)); + assert_eq!(FromStrRadix::from_str_radix("123", 16), Some(291u as u16)); + assert_eq!(FromStrRadix::from_str_radix("ffff", 16), Some(65535u as u16)); + assert_eq!(FromStrRadix::from_str_radix("z", 36), Some(35u as $T)); + + assert_eq!(FromStrRadix::from_str_radix("Z", 10), None::<$T>); + assert_eq!(FromStrRadix::from_str_radix("_", 2), None::<$T>); } #[test] @@ -148,35 +123,35 @@ mod tests { fn test_uint_from_str_overflow() { let mut u8_val: u8 = 255_u8; assert_eq!(from_str::("255"), Some(u8_val)); - assert!(from_str::("256").is_none()); + assert_eq!(from_str::("256"), None); u8_val += 1 as u8; assert_eq!(from_str::("0"), Some(u8_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); let mut u16_val: u16 = 65_535_u16; assert_eq!(from_str::("65535"), Some(u16_val)); - assert!(from_str::("65536").is_none()); + assert_eq!(from_str::("65536"), None); u16_val += 1 as u16; assert_eq!(from_str::("0"), Some(u16_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); let mut u32_val: u32 = 4_294_967_295_u32; assert_eq!(from_str::("4294967295"), Some(u32_val)); - assert!(from_str::("4294967296").is_none()); + assert_eq!(from_str::("4294967296"), None); u32_val += 1 as u32; assert_eq!(from_str::("0"), Some(u32_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; assert_eq!(from_str::("18446744073709551615"), Some(u64_val)); - assert!(from_str::("18446744073709551616").is_none()); + assert_eq!(from_str::("18446744073709551616"), None); u64_val += 1 as u64; assert_eq!(from_str::("0"), Some(u64_val)); - assert!(from_str::("-1").is_none()); + assert_eq!(from_str::("-1"), None); } } diff --git a/src/test/bench/shootout-pfib.rs b/src/test/bench/shootout-pfib.rs index 425b2e3e7140b..7fa13d6074d43 100644 --- a/src/test/bench/shootout-pfib.rs +++ b/src/test/bench/shootout-pfib.rs @@ -24,7 +24,6 @@ extern crate time; use std::os; use std::result::{Ok, Err}; use std::task; -use std::uint; fn fib(n: int) -> int { fn pfib(tx: &Sender, n: int) { @@ -102,8 +101,7 @@ fn main() { if opts.stress { stress(2); } else { - let max = uint::parse_bytes(args[1].as_bytes(), 10u).unwrap() as - int; + let max = from_str::(args[1].as_slice()).unwrap() as int; let num_trials = 10; From 251fdc877c0b2e3fdff61cbe083d1ba513f8e11b Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 20:00:34 +1100 Subject: [PATCH 2/5] Remove unnecessary features from strconv --- src/libstd/num/f32.rs | 9 +-- src/libstd/num/f64.rs | 9 +-- src/libstd/num/strconv.rs | 136 ++++++++------------------------------ 3 files changed, 33 insertions(+), 121 deletions(-) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 87c28c9362c63..448cb2d01fe2c 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -360,8 +360,7 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false) + strconv::from_str_float(num, 16u, true, strconv::ExpBin) } impl FromStr for f32 { @@ -392,8 +391,7 @@ impl FromStr for f32 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str(val: &str) -> Option { - strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false) + strconv::from_str_float(val, 10u, true, strconv::ExpDec) } } @@ -417,8 +415,7 @@ impl num::FromStrRadix for f32 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false) + strconv::from_str_float(val, rdx, false, strconv::ExpNone) } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 907c860f6a39f..f49e14cb04bab 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -368,8 +368,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_common(num, 16u, true, true, true, - strconv::ExpBin, false) + strconv::from_str_float(num, 16u, true, strconv::ExpBin) } impl FromStr for f64 { @@ -400,8 +399,7 @@ impl FromStr for f64 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str(val: &str) -> Option { - strconv::from_str_common(val, 10u, true, true, true, - strconv::ExpDec, false) + strconv::from_str_float(val, 10u, true, strconv::ExpDec) } } @@ -425,8 +423,7 @@ impl num::FromStrRadix for f64 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_common(val, rdx, true, true, false, - strconv::ExpNone, false) + strconv::from_str_float(val, rdx, false, strconv::ExpNone) } } diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 1e70a0e571cf9..07468c3702632 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -17,13 +17,11 @@ use char::Char; use clone::Clone; use from_str::from_str; use iter::Iterator; -use num::{NumCast, Zero, One, cast, Int, Bounded}; -use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use num; -use ops::{Add, Sub, Mul, Div, Rem, Neg}; +use num::{Zero, One, cast, Int, Bounded}; +use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use option::{None, Option, Some}; use slice::{ImmutableSlice, MutableSlice, CloneableVector}; -use std::cmp::{PartialOrd, PartialEq}; use str::{Str, StrSlice}; use string::String; use vec::Vec; @@ -70,51 +68,6 @@ pub enum SignFormat { SignAll, } -/// Encompasses functions used by the string converter. -pub trait NumStrConv { - /// Returns the NaN value. - fn nan() -> Option; - - /// Returns the infinite value. - fn inf() -> Option; - - /// Returns the negative infinite value. - fn neg_inf() -> Option; - - /// Returns -0.0. - fn neg_zero() -> Option; - - /// Rounds the number toward zero. - fn round_to_zero(&self) -> Self; - - /// Returns the fractional part of the number. - fn fractional_part(&self) -> Self; -} - -macro_rules! impl_NumStrConv_Floating (($t:ty) => ( - impl NumStrConv for $t { - #[inline] - fn nan() -> Option<$t> { Some( 0.0 / 0.0) } - #[inline] - fn inf() -> Option<$t> { Some( 1.0 / 0.0) } - #[inline] - fn neg_inf() -> Option<$t> { Some(-1.0 / 0.0) } - #[inline] - fn neg_zero() -> Option<$t> { Some(-0.0 ) } - - #[inline] - fn round_to_zero(&self) -> $t { self.trunc() } - #[inline] - fn fractional_part(&self) -> $t { self.fract() } - } -)) - -// FIXME: #4955 -// Replace by two generic impls for traits 'Integral' and 'Floating' -impl_NumStrConv_Floating!(f32) -impl_NumStrConv_Floating!(f64) - - // Special value strings as [u8] consts. static INF_BUF: [u8, ..3] = [b'i', b'n', b'f']; static POS_INF_BUF: [u8, ..4] = [b'+', b'i', b'n', b'f']; @@ -234,8 +187,7 @@ fn int_to_str_bytes_common(num: T, radix: uint, sign: SignFormat, f: |u8 * - Fails if `radix` > 25 and `exp_format` is `ExpBin` due to conflict * between digit and exponent sign `'p'`. */ -pub fn float_to_str_bytes_common+Neg+Rem+Mul>( +pub fn float_to_str_bytes_common( num: T, radix: uint, negative_zero: bool, sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_upper: bool ) -> (Vec, bool) { @@ -467,8 +419,7 @@ pub fn float_to_str_bytes_common+Neg+Rem+Mul>( +pub fn float_to_str_common( num: T, radix: uint, negative_zero: bool, sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_capital: bool ) -> (String, bool) { @@ -484,15 +435,13 @@ static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; /** - * Parses a byte slice as a number. This is meant to + * Parses a string as a number. This is meant to * be a common base implementation for all numeric string conversion * functions like `from_str()` or `from_str_radix()`. * * # Arguments - * - `buf` - The byte slice to parse. + * - `src` - The string to parse. * - `radix` - Which base to parse the number as. Accepts 2-36. - * - `negative` - Whether to accept negative numbers. - * - `fractional` - Whether to accept numbers with fractional parts. * - `special` - Whether to accept special values like `inf` * and `NaN`. Can conflict with `radix`, see Failure. * - `exponent` - Which exponent format to accept. Options are: @@ -504,7 +453,6 @@ static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or * `FFp128`. The exponent string itself is always base 10. * Can conflict with `radix`, see Failure. - * - `empty_zero` - Whether to accept an empty `buf` as a 0 or not. * * # Return value * Returns `Some(n)` if `buf` parses to a number n without overflowing, and @@ -520,11 +468,8 @@ static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; * - Fails if `radix` > 18 and `special == true` due to conflict * between digit and lowest first character in `inf` and `NaN`, the `'i'`. */ -pub fn from_str_bytes_common+ - Mul+Sub+Neg+Add+ - NumStrConv+Clone>( - buf: &[u8], radix: uint, negative: bool, fractional: bool, - special: bool, exponent: ExponentFormat, empty_zero: bool, +pub fn from_str_float( + src: &str, radix: uint, special: bool, exponent: ExponentFormat, ) -> Option { match exponent { ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' @@ -548,33 +493,25 @@ pub fn from_str_bytes_common+ let _0: T = Zero::zero(); let _1: T = One::one(); let radix_gen: T = cast(radix as int).unwrap(); + let buf = src.as_bytes(); let len = buf.len(); if len == 0 { - if empty_zero { - return Some(_0); - } else { - return None; - } + return None; } if special { if buf == INF_BUF || buf == POS_INF_BUF { - return NumStrConv::inf(); + return Some(Float::infinity()); } else if buf == NEG_INF_BUF { - if negative { - return NumStrConv::neg_inf(); - } else { - return None; - } + return Some(Float::neg_infinity()); } else if buf == NAN_BUF { - return NumStrConv::nan(); + return Some(Float::nan()); } } let (start, accum_positive) = match buf[0] as char { - '-' if !negative => return None, '-' => (1u, false), '+' => (1u, true), _ => (0u, true) @@ -606,17 +543,17 @@ pub fn from_str_bytes_common+ // Detect overflow by comparing to last value, except // if we've not seen any non-zero digits. if last_accum != _0 { - if accum_positive && accum <= last_accum { return NumStrConv::inf(); } - if !accum_positive && accum >= last_accum { return NumStrConv::neg_inf(); } + if accum_positive && accum <= last_accum { return Some(Float::infinity()); } + if !accum_positive && accum >= last_accum { return Some(Float::neg_infinity()); } // Detect overflow by reversing the shift-and-add process if accum_positive && (last_accum != ((accum - cast(digit as int).unwrap())/radix_gen.clone())) { - return NumStrConv::inf(); + return Some(Float::infinity()); } if !accum_positive && (last_accum != ((accum + cast(digit as int).unwrap())/radix_gen.clone())) { - return NumStrConv::neg_inf(); + return Some(Float::neg_infinity()); } } last_accum = accum.clone(); @@ -626,7 +563,7 @@ pub fn from_str_bytes_common+ exp_found = true; break; // start of exponent } - '.' if fractional => { + '.' => { i += 1u; // skip the '.' break; // start of fractional part } @@ -660,8 +597,8 @@ pub fn from_str_bytes_common+ } // Detect overflow by comparing to last value - if accum_positive && accum < last_accum { return NumStrConv::inf(); } - if !accum_positive && accum > last_accum { return NumStrConv::neg_inf(); } + if accum_positive && accum < last_accum { return Some(Float::infinity()); } + if !accum_positive && accum > last_accum { return Some(Float::neg_infinity()); } last_accum = accum.clone(); } None => match c { @@ -680,11 +617,7 @@ pub fn from_str_bytes_common+ // Special case: buf not empty, but does not contain any digit in front // of the exponent sign -> number is empty string if i == start { - if empty_zero { - return Some(_0); - } else { - return None; - } + return None; } let mut multiplier = _1.clone(); @@ -717,20 +650,6 @@ pub fn from_str_bytes_common+ Some(accum * multiplier) } -/** - * Parses a string as a number. This is a wrapper for - * `from_str_bytes_common()`, for details see there. - */ -#[inline] -pub fn from_str_common+Mul+ - Sub+Neg+Add+NumStrConv+Clone>( - buf: &str, radix: uint, negative: bool, fractional: bool, - special: bool, exponent: ExponentFormat, empty_zero: bool, - ) -> Option { - from_str_bytes_common(buf.as_bytes(), radix, negative, - fractional, special, exponent, empty_zero) -} - pub fn from_str_radix_int(src: &str, radix: uint) -> Option { fn cast(x: uint) -> T { num::cast(x).unwrap() @@ -791,6 +710,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { mod test { use super::*; use option::*; + use num::Float; #[test] fn from_str_issue7588() { @@ -798,13 +718,11 @@ mod test { assert_eq!(u, None); let s : Option = from_str_radix_int("80000", 10); assert_eq!(s, None); - let f : Option = from_str_common( - "10000000000000000000000000000000000000000", 10, false, false, false, - ExpNone, false); - assert_eq!(f, NumStrConv::inf()) - let fe : Option = from_str_common("1e40", 10, false, false, false, - ExpDec, false); - assert_eq!(fe, NumStrConv::inf()) + let f : Option = from_str_float( + "10000000000000000000000000000000000000000", 10, false, ExpNone); + assert_eq!(f, Some(Float::infinity())) + let fe : Option = from_str_float("1e40", 10, false, ExpDec); + assert_eq!(fe, Some(Float::infinity())) } } From 84f4b58eeb12f1fcf46c67029f09a21dd6506b22 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 21:39:55 +1100 Subject: [PATCH 3/5] Clean up from_str_float and use iterators --- src/libstd/num/strconv.rs | 281 +++++++++++++++++--------------------- 1 file changed, 125 insertions(+), 156 deletions(-) diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 07468c3702632..0e8d5bc5ba22e 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -14,15 +14,14 @@ use char; use char::Char; -use clone::Clone; use from_str::from_str; use iter::Iterator; use num; -use num::{Zero, One, cast, Int, Bounded}; +use num::{Int, Bounded}; use num::{Float, FPNaN, FPInfinite, ToPrimitive}; use option::{None, Option, Some}; use slice::{ImmutableSlice, MutableSlice, CloneableVector}; -use str::{Str, StrSlice}; +use str::StrSlice; use string::String; use vec::Vec; @@ -68,12 +67,6 @@ pub enum SignFormat { SignAll, } -// Special value strings as [u8] consts. -static INF_BUF: [u8, ..3] = [b'i', b'n', b'f']; -static POS_INF_BUF: [u8, ..4] = [b'+', b'i', b'n', b'f']; -static NEG_INF_BUF: [u8, ..4] = [b'-', b'i', b'n', b'f']; -static NAN_BUF: [u8, ..3] = [b'N', b'a', b'N']; - /** * Converts an integral number to its string representation as a byte vector. * This is meant to be a common base implementation for all integral string @@ -102,10 +95,10 @@ static NAN_BUF: [u8, ..3] = [b'N', b'a', b'N']; fn int_to_str_bytes_common(num: T, radix: uint, sign: SignFormat, f: |u8|) { assert!(2 <= radix && radix <= 36); - let _0: T = Zero::zero(); + let _0: T = num::zero(); let neg = num < _0; - let radix_gen: T = cast(radix).unwrap(); + let radix_gen: T = num::cast(radix).unwrap(); let mut deccum = num; // This is just for integral types, the largest of which is a u64. The @@ -202,8 +195,8 @@ pub fn float_to_str_bytes_common( _ => () } - let _0: T = Zero::zero(); - let _1: T = One::one(); + let _0: T = num::zero(); + let _1: T = num::one(); match num.classify() { FPNaN => { return (b"NaN".to_vec(), true); } @@ -224,7 +217,7 @@ pub fn float_to_str_bytes_common( let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity()); let mut buf = Vec::new(); - let radix_gen: T = cast(radix as int).unwrap(); + let radix_gen: T = num::cast(radix as int).unwrap(); let (num, exp) = match exp_format { ExpNone => (num, 0i32), @@ -233,12 +226,12 @@ pub fn float_to_str_bytes_common( (num, 0i32) } else { let (exp, exp_base) = match exp_format { - ExpDec => (num.abs().log10().floor(), cast::(10.0f64).unwrap()), - ExpBin => (num.abs().log2().floor(), cast::(2.0f64).unwrap()), + ExpDec => (num.abs().log10().floor(), num::cast::(10.0f64).unwrap()), + ExpBin => (num.abs().log2().floor(), num::cast::(2.0f64).unwrap()), ExpNone => unreachable!() }; - (num / exp_base.powf(exp), cast::(exp).unwrap()) + (num / exp_base.powf(exp), num::cast::(exp).unwrap()) } } }; @@ -490,163 +483,139 @@ pub fn from_str_float( _ => () } - let _0: T = Zero::zero(); - let _1: T = One::one(); - let radix_gen: T = cast(radix as int).unwrap(); - let buf = src.as_bytes(); - - let len = buf.len(); - - if len == 0 { - return None; - } + let _0: T = num::zero(); + let _1: T = num::one(); + let radix_gen: T = num::cast(radix as int).unwrap(); - if special { - if buf == INF_BUF || buf == POS_INF_BUF { - return Some(Float::infinity()); - } else if buf == NEG_INF_BUF { - return Some(Float::neg_infinity()); - } else if buf == NAN_BUF { - return Some(Float::nan()); - } + match src { + "inf" => return Some(Float::infinity()), + "-inf" => return Some(Float::neg_infinity()), + "NaN" => return Some(Float::nan()), + _ => {}, } - let (start, accum_positive) = match buf[0] as char { - '-' => (1u, false), - '+' => (1u, true), - _ => (0u, true) + let (is_positive, src) = match src.slice_shift_char() { + (None, _) => return None, + (Some('-'), "") => return None, + (Some('-'), src) => (false, src), + (Some(_), _) => (true, src), }; // Initialize accumulator with signed zero for floating point parsing to // work - let mut accum = if accum_positive { _0.clone() } else { -_1 * _0}; - let mut last_accum = accum.clone(); // Necessary to detect overflow - let mut i = start; - let mut exp_found = false; + let mut accum = if is_positive { _0 } else { -_1 }; + let mut last_accum = accum; // Necessary to detect overflow + let mut cs = src.chars().enumerate(); + let mut exp = None::<(char, uint)>; // Parse integer part of number - while i < len { - let c = buf[i] as char; - - match char::to_digit(c, radix) { - Some(digit) => { - // shift accum one digit left - accum = accum * radix_gen.clone(); - - // add/subtract current digit depending on sign - if accum_positive { - accum = accum + cast(digit as int).unwrap(); - } else { - accum = accum - cast(digit as int).unwrap(); - } - - // Detect overflow by comparing to last value, except - // if we've not seen any non-zero digits. - if last_accum != _0 { - if accum_positive && accum <= last_accum { return Some(Float::infinity()); } - if !accum_positive && accum >= last_accum { return Some(Float::neg_infinity()); } - - // Detect overflow by reversing the shift-and-add process - if accum_positive && - (last_accum != ((accum - cast(digit as int).unwrap())/radix_gen.clone())) { - return Some(Float::infinity()); - } - if !accum_positive && - (last_accum != ((accum + cast(digit as int).unwrap())/radix_gen.clone())) { - return Some(Float::neg_infinity()); - } - } - last_accum = accum.clone(); - } - None => match c { - 'e' | 'E' | 'p' | 'P' => { - exp_found = true; - break; // start of exponent - } - '.' => { - i += 1u; // skip the '.' - break; // start of fractional part - } - _ => return None // invalid number - } - } - - i += 1u; - } - - // Parse fractional part of number - // Skip if already reached start of exponent - if !exp_found { - let mut power = _1.clone(); - - while i < len { - let c = buf[i] as char; - - match char::to_digit(c, radix) { + for (i, c) in cs { + match c { + 'e' | 'E' | 'p' | 'P' => { + exp = Some((c, i + 1)); + break; // start of exponent + }, + '.' => { + break; // start of fractional part + }, + c => match c.to_digit(radix) { Some(digit) => { - // Decrease power one order of magnitude - power = power / radix_gen; - - let digit_t: T = cast(digit).unwrap(); + // shift accum one digit left + accum = accum * radix_gen; // add/subtract current digit depending on sign - if accum_positive { - accum = accum + digit_t * power; + if is_positive { + accum = accum + num::cast(digit as int).unwrap(); } else { - accum = accum - digit_t * power; + accum = accum - num::cast(digit as int).unwrap(); } - // Detect overflow by comparing to last value - if accum_positive && accum < last_accum { return Some(Float::infinity()); } - if !accum_positive && accum > last_accum { return Some(Float::neg_infinity()); } - last_accum = accum.clone(); - } - None => match c { - 'e' | 'E' | 'p' | 'P' => { - exp_found = true; - break; // start of exponent + // Detect overflow by comparing to last value, except + // if we've not seen any non-zero digits. + if last_accum != _0 { + if is_positive && accum <= last_accum { return Some(Float::infinity()); } + if !is_positive && accum >= last_accum { return Some(Float::neg_infinity()); } + + // Detect overflow by reversing the shift-and-add process + if is_positive && + (last_accum != ((accum - num::cast(digit as int).unwrap()) / radix_gen)) { + return Some(Float::infinity()); + } + if !is_positive && + (last_accum != ((accum + num::cast(digit as int).unwrap()) / radix_gen)) { + return Some(Float::neg_infinity()); + } } - _ => return None // invalid number - } - } - - i += 1u; + last_accum = accum; + }, + None => { + return None; // invalid number + }, + }, } } - // Special case: buf not empty, but does not contain any digit in front - // of the exponent sign -> number is empty string - if i == start { - return None; + // Parse fractional part of number + // Skip if already reached start of exponent + if exp.is_none() { + let mut power = _1; + for (i, c) in cs { + match c { + 'e' | 'E' | 'p' | 'P' => { + exp = Some((c, i + 1)); + break; // start of exponent + }, + c => match c.to_digit(radix) { + Some(digit) => { + let digit: T = num::cast(digit).unwrap(); + + // Decrease power one order of magnitude + power = power / radix_gen; + // add/subtract current digit depending on sign + accum = if is_positive { + accum + digit * power + } else { + accum - digit * power + }; + // Detect overflow by comparing to last value + if is_positive && accum < last_accum { return Some(Float::infinity()); } + if !is_positive && accum > last_accum { return Some(Float::neg_infinity()); } + last_accum = accum; + }, + None => { + return None; // invalid number + }, + }, + } + } } - let mut multiplier = _1.clone(); - - if exp_found { - let c = buf[i] as char; - let base: T = match (c, exponent) { - // c is never _ so don't need to handle specially - ('e', ExpDec) | ('E', ExpDec) => cast(10u).unwrap(), - ('p', ExpBin) | ('P', ExpBin) => cast(2u).unwrap(), - _ => return None // char doesn't fit given exponent format - }; - - // parse remaining bytes as decimal integer, - // skipping the exponent char - let exp = from_str::(String::from_utf8_lossy(buf[i+1..len]).as_slice()); - - match exp { - Some(exp_pow) => { - multiplier = if exp_pow < 0 { + let multiplier = match exp { + None => { + _1 // no exponent + }, + Some((c, offset)) => { + let base: T = match (c, exponent) { + // c is never _ so don't need to handle specially + ('e', ExpDec) | ('E', ExpDec) => num::cast(10u).unwrap(), + ('p', ExpBin) | ('P', ExpBin) => num::cast(2u).unwrap(), + _ => return None, // char doesn't fit given exponent format + }; + // parse remaining string as decimal integer + let exp = from_str::(src[offset..]); + match exp { + Some(exp_pow) if exp_pow < 0 => { _1 / num::pow(base, (-exp_pow.to_int().unwrap()) as uint) - } else { + }, + Some(exp_pow) => { num::pow(base, exp_pow.to_int().unwrap() as uint) - } + }, + None => { + return None; // invalid exponent + }, } - None => return None // invalid exponent -> invalid number - } - } - + }, + }; Some(accum * multiplier) } @@ -659,9 +628,9 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { let _1: T = num::one(); let is_signed = _0 > Bounded::min_value(); - let (is_negative, src) = match src.slice_shift_char() { - (Some('-'), src) if is_signed => (true, src), - (Some(_), _) => (false, src), + let (is_positive, src) = match src.slice_shift_char() { + (Some('-'), src) if is_signed => (false, src), + (Some(_), _) => (true, src), (None, _) => return None, }; @@ -671,7 +640,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { let radix = cast(radix); let mut result = _0; - if is_negative { + if is_positive { for x in xs { let x = match x { Some(x) => x, @@ -681,7 +650,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { Some(result) => result, None => return None, }; - result = match result.checked_sub(&x) { + result = match result.checked_add(&x) { Some(result) => result, None => return None, }; @@ -696,7 +665,7 @@ pub fn from_str_radix_int(src: &str, radix: uint) -> Option { Some(result) => result, None => return None, }; - result = match result.checked_add(&x) { + result = match result.checked_sub(&x) { Some(result) => result, None => return None, }; From 01ded5898b1863e195856d2678323315ae224463 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 22:53:48 +1100 Subject: [PATCH 4/5] Simplify float string conversion function further We now have a really simple function signature: pub fn from_str_radix_float(src: &str, radix: uint) -> Option By removing some of the arguments, we remove the possibility of some invalid states. --- src/libstd/num/f32.rs | 12 +- src/libstd/num/f64.rs | 8 +- src/libstd/num/strconv.rs | 256 ++++++++++++++++---------------------- 3 files changed, 115 insertions(+), 161 deletions(-) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 448cb2d01fe2c..ebb1aee816e24 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -359,8 +359,8 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] -pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_float(num, 16u, true, strconv::ExpBin) +pub fn from_str_hex(src: &str) -> Option { + strconv::from_str_radix_float(src, 16u) } impl FromStr for f32 { @@ -390,8 +390,8 @@ impl FromStr for f32 { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] - fn from_str(val: &str) -> Option { - strconv::from_str_float(val, 10u, true, strconv::ExpDec) + fn from_str(src: &str) -> Option { + strconv::from_str_radix_float(src, 10u) } } @@ -414,8 +414,8 @@ impl num::FromStrRadix for f32 { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] - fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_float(val, rdx, false, strconv::ExpNone) + fn from_str_radix(src: &str, radix: uint) -> Option { + strconv::from_str_radix_float(src, radix) } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index f49e14cb04bab..e1a46f5710f50 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -368,7 +368,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { /// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_float(num, 16u, true, strconv::ExpBin) + strconv::from_str_radix_float(num, 16u) } impl FromStr for f64 { @@ -399,7 +399,7 @@ impl FromStr for f64 { /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] fn from_str(val: &str) -> Option { - strconv::from_str_float(val, 10u, true, strconv::ExpDec) + strconv::from_str_radix_float(val, 10u) } } @@ -422,8 +422,8 @@ impl num::FromStrRadix for f64 { /// `None` if the string did not represent a valid number. Otherwise, /// `Some(n)` where `n` is the floating-point number represented by `num`. #[inline] - fn from_str_radix(val: &str, rdx: uint) -> Option { - strconv::from_str_float(val, rdx, false, strconv::ExpNone) + fn from_str_radix(val: &str, radix: uint) -> Option { + strconv::from_str_radix_float(val, radix) } } diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 0e8d5bc5ba22e..7a02d8d77b0b1 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -424,69 +424,18 @@ pub fn float_to_str_common( // Some constants for from_str_bytes_common's input validation, // they define minimum radix values for which the character is a valid digit. static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u; -static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u; static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u; -/** - * Parses a string as a number. This is meant to - * be a common base implementation for all numeric string conversion - * functions like `from_str()` or `from_str_radix()`. - * - * # Arguments - * - `src` - The string to parse. - * - `radix` - Which base to parse the number as. Accepts 2-36. - * - `special` - Whether to accept special values like `inf` - * and `NaN`. Can conflict with `radix`, see Failure. - * - `exponent` - Which exponent format to accept. Options are: - * - `ExpNone`: No Exponent, accepts just plain numbers like `42` or - * `-8.2`. - * - `ExpDec`: Accepts numbers with a decimal exponent like `42e5` or - * `8.2E-2`. The exponent string itself is always base 10. - * Can conflict with `radix`, see Failure. - * - `ExpBin`: Accepts numbers with a binary exponent like `42P-8` or - * `FFp128`. The exponent string itself is always base 10. - * Can conflict with `radix`, see Failure. - * - * # Return value - * Returns `Some(n)` if `buf` parses to a number n without overflowing, and - * `None` otherwise, depending on the constraints set by the remaining - * arguments. - * - * # Failure - * - Fails if `radix` < 2 or `radix` > 36. - * - Fails if `radix` > 14 and `exponent` is `ExpDec` due to conflict - * between digit and exponent sign `'e'`. - * - Fails if `radix` > 25 and `exponent` is `ExpBin` due to conflict - * between digit and exponent sign `'p'`. - * - Fails if `radix` > 18 and `special == true` due to conflict - * between digit and lowest first character in `inf` and `NaN`, the `'i'`. - */ -pub fn from_str_float( - src: &str, radix: uint, special: bool, exponent: ExponentFormat, - ) -> Option { - match exponent { - ExpDec if radix >= DIGIT_E_RADIX // decimal exponent 'e' - => panic!("from_str_bytes_common: radix {} incompatible with \ - use of 'e' as decimal exponent", radix), - ExpBin if radix >= DIGIT_P_RADIX // binary exponent 'p' - => panic!("from_str_bytes_common: radix {} incompatible with \ - use of 'p' as binary exponent", radix), - _ if special && radix >= DIGIT_I_RADIX // first digit of 'inf' - => panic!("from_str_bytes_common: radix {} incompatible with \ - special values 'inf' and 'NaN'", radix), - _ if (radix as int) < 2 - => panic!("from_str_bytes_common: radix {} to low, \ - must lie in the range [2, 36]", radix), - _ if (radix as int) > 36 - => panic!("from_str_bytes_common: radix {} to high, \ - must lie in the range [2, 36]", radix), - _ => () - } +pub fn from_str_radix_float(src: &str, radix: uint) -> Option { + assert!(radix >= 2 && radix <= 36, + "from_str_radix_float: must lie in the range `[2, 36]` - found {}", + radix); let _0: T = num::zero(); let _1: T = num::one(); - let radix_gen: T = num::cast(radix as int).unwrap(); + let radix_t: T = num::cast(radix as int).unwrap(); + // Special values match src { "inf" => return Some(Float::infinity()), "-inf" => return Some(Float::neg_infinity()), @@ -501,88 +450,89 @@ pub fn from_str_float( (Some(_), _) => (true, src), }; - // Initialize accumulator with signed zero for floating point parsing to - // work - let mut accum = if is_positive { _0 } else { -_1 }; - let mut last_accum = accum; // Necessary to detect overflow - let mut cs = src.chars().enumerate(); - let mut exp = None::<(char, uint)>; + // The significand to accumulate + let mut sig = if is_positive { _0 } else { -_1 }; + // Necessary to detect overflow + let mut prev_sig = sig; + let mut cs = src.chars().enumerate(); + // Exponent prefix and exponent index offset + let mut exp_info = None::<(char, uint)>; - // Parse integer part of number + // Parse the integer part of the significand for (i, c) in cs { - match c { - 'e' | 'E' | 'p' | 'P' => { - exp = Some((c, i + 1)); - break; // start of exponent - }, - '.' => { - break; // start of fractional part - }, - c => match c.to_digit(radix) { - Some(digit) => { - // shift accum one digit left - accum = accum * radix_gen; - - // add/subtract current digit depending on sign - if is_positive { - accum = accum + num::cast(digit as int).unwrap(); - } else { - accum = accum - num::cast(digit as int).unwrap(); - } + match c.to_digit(radix) { + Some(digit) => { + // shift significand one digit left + sig = sig * radix_t; + + // add/subtract current digit depending on sign + if is_positive { + sig = sig + num::cast(digit as int).unwrap(); + } else { + sig = sig - num::cast(digit as int).unwrap(); + } - // Detect overflow by comparing to last value, except - // if we've not seen any non-zero digits. - if last_accum != _0 { - if is_positive && accum <= last_accum { return Some(Float::infinity()); } - if !is_positive && accum >= last_accum { return Some(Float::neg_infinity()); } - - // Detect overflow by reversing the shift-and-add process - if is_positive && - (last_accum != ((accum - num::cast(digit as int).unwrap()) / radix_gen)) { - return Some(Float::infinity()); - } - if !is_positive && - (last_accum != ((accum + num::cast(digit as int).unwrap()) / radix_gen)) { - return Some(Float::neg_infinity()); - } - } - last_accum = accum; + // Detect overflow by comparing to last value, except + // if we've not seen any non-zero digits. + if prev_sig != _0 { + if is_positive && sig <= prev_sig + { return Some(Float::infinity()); } + if !is_positive && sig >= prev_sig + { return Some(Float::neg_infinity()); } + + // Detect overflow by reversing the shift-and-add process + let digit: T = num::cast(digit as int).unwrap(); + if is_positive && (prev_sig != ((sig - digit) / radix_t)) + { return Some(Float::infinity()); } + if !is_positive && (prev_sig != ((sig + digit) / radix_t)) + { return Some(Float::neg_infinity()); } + } + prev_sig = sig; + }, + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_info = Some((c, i + 1)); + break; // start of exponent + }, + '.' => { + break; // start of fractional part }, - None => { - return None; // invalid number + _ => { + return None; }, }, } } - // Parse fractional part of number - // Skip if already reached start of exponent - if exp.is_none() { + // If we are not yet at the exponent parse the fractional + // part of the significand + if exp_info.is_none() { let mut power = _1; for (i, c) in cs { - match c { - 'e' | 'E' | 'p' | 'P' => { - exp = Some((c, i + 1)); - break; // start of exponent + match c.to_digit(radix) { + Some(digit) => { + let digit: T = num::cast(digit).unwrap(); + // Decrease power one order of magnitude + power = power / radix_t; + // add/subtract current digit depending on sign + sig = if is_positive { + sig + digit * power + } else { + sig - digit * power + }; + // Detect overflow by comparing to last value + if is_positive && sig < prev_sig + { return Some(Float::infinity()); } + if !is_positive && sig > prev_sig + { return Some(Float::neg_infinity()); } + prev_sig = sig; }, - c => match c.to_digit(radix) { - Some(digit) => { - let digit: T = num::cast(digit).unwrap(); - - // Decrease power one order of magnitude - power = power / radix_gen; - // add/subtract current digit depending on sign - accum = if is_positive { - accum + digit * power - } else { - accum - digit * power - }; - // Detect overflow by comparing to last value - if is_positive && accum < last_accum { return Some(Float::infinity()); } - if !is_positive && accum > last_accum { return Some(Float::neg_infinity()); } - last_accum = accum; + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_info = Some((c, i + 1)); + break; // start of exponent }, - None => { + _ => { return None; // invalid number }, }, @@ -590,36 +540,41 @@ pub fn from_str_float( } } - let multiplier = match exp { - None => { - _1 // no exponent - }, + // Parse and calculate the exponent + let exp = match exp_info { Some((c, offset)) => { - let base: T = match (c, exponent) { - // c is never _ so don't need to handle specially - ('e', ExpDec) | ('E', ExpDec) => num::cast(10u).unwrap(), - ('p', ExpBin) | ('P', ExpBin) => num::cast(2u).unwrap(), - _ => return None, // char doesn't fit given exponent format + let base: T = match c { + 'E' | 'e' if radix == 10 => num::cast(10u).unwrap(), + 'P' | 'p' if radix == 16 => num::cast(2u).unwrap(), + _ => return None, }; - // parse remaining string as decimal integer - let exp = from_str::(src[offset..]); - match exp { - Some(exp_pow) if exp_pow < 0 => { - _1 / num::pow(base, (-exp_pow.to_int().unwrap()) as uint) - }, - Some(exp_pow) => { - num::pow(base, exp_pow.to_int().unwrap() as uint) - }, - None => { - return None; // invalid exponent - }, + + // Parse the exponent as decimal integer + let src = src[offset..]; + let (is_positive, exp) = match src.slice_shift_char() { + (Some('-'), src) => (false, from_str::(src)), + (Some('+'), src) => (true, from_str::(src)), + (Some(_), _) => (true, from_str::(src)), + (None, _) => return None, + }; + + match (is_positive, exp) { + (true, Some(exp)) => num::pow(base, exp), + (false, Some(exp)) => _1 / num::pow(base, exp), + (_, None) => return None, } }, + None => _1, // no exponent }; - Some(accum * multiplier) + + Some(sig * exp) } pub fn from_str_radix_int(src: &str, radix: uint) -> Option { + assert!(radix >= 2 && radix <= 36, + "from_str_radix_int: must lie in the range `[2, 36]` - found {}", + radix); + fn cast(x: uint) -> T { num::cast(x).unwrap() } @@ -687,10 +642,9 @@ mod test { assert_eq!(u, None); let s : Option = from_str_radix_int("80000", 10); assert_eq!(s, None); - let f : Option = from_str_float( - "10000000000000000000000000000000000000000", 10, false, ExpNone); + let f : Option = from_str_radix_float("10000000000000000000000000000000000000000", 10); assert_eq!(f, Some(Float::infinity())) - let fe : Option = from_str_float("1e40", 10, false, ExpDec); + let fe : Option = from_str_radix_float("1e40", 10); assert_eq!(fe, Some(Float::infinity())) } } From 8bd37e672411bf004a4ad64bb7d2160e455a72b0 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 2 Nov 2014 22:58:46 +1100 Subject: [PATCH 5/5] Deprecate {f32, f64}::from_str_hex This is now covered by `FromStrRadix::from_str_radix` --- src/libstd/num/f32.rs | 44 +++++++----------------------- src/libstd/num/f64.rs | 63 ++++++++++++------------------------------- 2 files changed, 27 insertions(+), 80 deletions(-) diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index ebb1aee816e24..63c3956ef2412 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -333,34 +333,10 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { r } -/// Convert a string in base 16 to a float. -/// Accepts an optional binary exponent. -/// -/// This function accepts strings such as -/// -/// * 'a4.fe' -/// * '+a4.fe', equivalent to 'a4.fe' -/// * '-a4.fe' -/// * '2b.aP128', or equivalently, '2b.ap128' -/// * '2b.aP-128' -/// * '.' (understood as 0) -/// * 'c.' -/// * '.c', or, equivalently, '0.c' -/// * '+inf', 'inf', '-inf', 'NaN' -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// -/// # Return value -/// -/// `None` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] +#[deprecated="Use `FromStrRadix::from_str_radix(src, 16)`"] pub fn from_str_hex(src: &str) -> Option { - strconv::from_str_radix_float(src, 16u) + strconv::from_str_radix_float(src, 16) } impl FromStr for f32 { @@ -383,12 +359,12 @@ impl FromStr for f32 { /// /// # Arguments /// - /// * num - A string + /// * src - A string /// /// # Return value /// /// `None` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] fn from_str(src: &str) -> Option { strconv::from_str_radix_float(src, 10u) @@ -406,13 +382,13 @@ impl num::FromStrRadix for f32 { /// /// # Arguments /// - /// * num - A string + /// * src - A string /// * radix - The base to use. Must lie in the range [2 .. 36] /// /// # Return value /// /// `None` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] fn from_str_radix(src: &str, radix: uint) -> Option { strconv::from_str_radix_float(src, radix) @@ -707,8 +683,8 @@ mod tests { fn test_ldexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f32 = from_str_hex("1p-123").unwrap(); - let f2: f32 = from_str_hex("1p-111").unwrap(); + let f1: f32 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f32 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); assert_eq!(FloatMath::ldexp(1f32, -123), f1); assert_eq!(FloatMath::ldexp(1f32, -111), f2); @@ -727,8 +703,8 @@ mod tests { fn test_frexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f32 = from_str_hex("1p-123").unwrap(); - let f2: f32 = from_str_hex("1p-111").unwrap(); + let f1: f32 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f32 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); let (x1, exp1) = f1.frexp(); let (x2, exp2) = f2.frexp(); assert_eq!((x1, exp1), (0.5f32, -122)); diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index e1a46f5710f50..6e8e92eb91d03 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -341,89 +341,60 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { r } -/// Convert a string in base 16 to a float. -/// Accepts an optional binary exponent. -/// -/// This function accepts strings such as -/// -/// * 'a4.fe' -/// * '+a4.fe', equivalent to 'a4.fe' -/// * '-a4.fe' -/// * '2b.aP128', or equivalently, '2b.ap128' -/// * '2b.aP-128' -/// * '.' (understood as 0) -/// * 'c.' -/// * '.c', or, equivalently, '0.c' -/// * '+inf', 'inf', '-inf', 'NaN' -/// -/// Leading and trailing whitespace represent an error. -/// -/// # Arguments -/// -/// * num - A string -/// -/// # Return value -/// -/// `None` if the string did not represent a valid number. Otherwise, -/// `Some(n)` where `n` is the floating-point number represented by `[num]`. #[inline] -pub fn from_str_hex(num: &str) -> Option { - strconv::from_str_radix_float(num, 16u) +#[deprecated="Use `FromStrRadix::from_str_radix(src, 16)`"] +pub fn from_str_hex(src: &str) -> Option { + strconv::from_str_radix_float(src, 16) } impl FromStr for f64 { /// Convert a string in base 10 to a float. /// Accepts an optional decimal exponent. /// - /// This function accepts strings such as + /// This function accepts strings such as: /// /// * '3.14' - /// * '+3.14', equivalent to '3.14' /// * '-3.14' /// * '2.5E10', or equivalently, '2.5e10' /// * '2.5E-10' /// * '.' (understood as 0) /// * '5.' /// * '.5', or, equivalently, '0.5' - /// * '+inf', 'inf', '-inf', 'NaN' + /// * inf', '-inf', 'NaN' /// /// Leading and trailing whitespace represent an error. /// /// # Arguments /// - /// * num - A string + /// * src - A string /// /// # Return value /// /// `none` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] - fn from_str(val: &str) -> Option { - strconv::from_str_radix_float(val, 10u) + fn from_str(src: &str) -> Option { + strconv::from_str_radix_float(src, 10u) } } impl num::FromStrRadix for f64 { /// Convert a string in a given base to a float. /// - /// Due to possible conflicts, this function does **not** accept - /// the special values `inf`, `-inf`, `+inf` and `NaN`, **nor** - /// does it recognize exponents of any kind. - /// /// Leading and trailing whitespace represent an error. /// /// # Arguments /// - /// * num - A string + /// * src - A string /// * radix - The base to use. Must lie in the range [2 .. 36] /// /// # Return value /// /// `None` if the string did not represent a valid number. Otherwise, - /// `Some(n)` where `n` is the floating-point number represented by `num`. + /// `Some(n)` where `n` is the floating-point number represented by `src`. #[inline] - fn from_str_radix(val: &str, radix: uint) -> Option { - strconv::from_str_radix_float(val, radix) + fn from_str_radix(src: &str, radix: uint) -> Option { + strconv::from_str_radix_float(src, radix) } } @@ -709,8 +680,8 @@ mod tests { fn test_ldexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f64 = from_str_hex("1p-123").unwrap(); - let f2: f64 = from_str_hex("1p-111").unwrap(); + let f1: f64 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f64 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); assert_eq!(FloatMath::ldexp(1f64, -123), f1); assert_eq!(FloatMath::ldexp(1f64, -111), f2); @@ -729,8 +700,8 @@ mod tests { fn test_frexp() { // We have to use from_str until base-2 exponents // are supported in floating-point literals - let f1: f64 = from_str_hex("1p-123").unwrap(); - let f2: f64 = from_str_hex("1p-111").unwrap(); + let f1: f64 = FromStrRadix::from_str_radix("1p-123", 16).unwrap(); + let f2: f64 = FromStrRadix::from_str_radix("1p-111", 16).unwrap(); let (x1, exp1) = f1.frexp(); let (x2, exp2) = f2.frexp(); assert_eq!((x1, exp1), (0.5f64, -122));