diff --git a/src/libextra/terminfo/parm.rs b/src/libextra/terminfo/parm.rs index f3edd81f9acd8..b7d21ea0ee3fd 100644 --- a/src/libextra/terminfo/parm.rs +++ b/src/libextra/terminfo/parm.rs @@ -11,7 +11,7 @@ //! Parameterized string expansion use std::{char, vec, util}; -use std::num::strconv::{SignNone,SignNeg,SignAll,DigAll,to_str_bytes_common}; +use std::num::strconv::{SignNone,SignNeg,SignAll,int_to_str_bytes_common}; use std::iterator::IteratorUtil; #[deriving(Eq)] @@ -469,14 +469,20 @@ priv fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> { FormatHex|FormatHEX => 16, FormatString => util::unreachable() }; - let (s,_) = match op { + let mut s = ~[]; + match op { FormatDigit => { let sign = if flags.sign { SignAll } else { SignNeg }; - to_str_bytes_common(&d, radix, false, sign, DigAll) + do int_to_str_bytes_common(d, radix, sign) |c| { + s.push(c); + } + } + _ => { + do int_to_str_bytes_common(d as uint, radix, SignNone) |c| { + s.push(c); + } } - _ => to_str_bytes_common(&(d as uint), radix, false, SignNone, DigAll) }; - let mut s = s; if flags.precision > s.len() { let mut s_ = vec::with_capacity(flags.precision); let n = flags.precision - s.len(); diff --git a/src/libstd/char.rs b/src/libstd/char.rs index 8f0870af51332..6a9555f4efcdc 100644 --- a/src/libstd/char.rs +++ b/src/libstd/char.rs @@ -10,14 +10,13 @@ //! Utilities for manipulating the char type -use container::Container; use option::{None, Option, Some}; -use str; -use str::{StrSlice, OwnedStr}; -use u32; -use uint; +use int; +use str::StrSlice; use unicode::{derived_property, general_category}; +#[cfg(test)] use str::OwnedStr; + #[cfg(not(test))] use cmp::{Eq, Ord}; #[cfg(not(test))] use num::Zero; @@ -202,21 +201,21 @@ pub fn from_digit(num: uint, radix: uint) -> Option { /// - chars in [0x100,0xffff] get 4-digit escapes: `\\uNNNN` /// - chars above 0x10000 get 8-digit escapes: `\\UNNNNNNNN` /// -pub fn escape_unicode(c: char) -> ~str { - let s = u32::to_str_radix(c as u32, 16u); - let (c, pad) = cond!( - (c <= '\xff') { ('x', 2u) } - (c <= '\uffff') { ('u', 4u) } - _ { ('U', 8u) } +pub fn escape_unicode(c: char, f: &fn(char)) { + // avoid calling str::to_str_radix because we don't really need to allocate + // here. + f('\\'); + let pad = cond!( + (c <= '\xff') { f('x'); 2 } + (c <= '\uffff') { f('u'); 4 } + _ { f('U'); 8 } ); - assert!(s.len() <= pad); - let mut out = ~"\\"; - out.push_str(str::from_char(c)); - for uint::range(s.len(), pad) |_| { - out.push_str("0"); + for int::range_step(4 * (pad - 1), -1, -4) |offset| { + match ((c as u32) >> offset) & 0xf { + i @ 0 .. 9 => { f('0' + i as char); } + i => { f('a' + (i - 10) as char); } + } } - out.push_str(s); - out } /// @@ -231,16 +230,16 @@ pub fn escape_unicode(c: char) -> ~str { /// - Any other chars in the range [0x20,0x7e] are not escaped. /// - Any other chars are given hex unicode escapes; see `escape_unicode`. /// -pub fn escape_default(c: char) -> ~str { +pub fn escape_default(c: char, f: &fn(char)) { match c { - '\t' => ~"\\t", - '\r' => ~"\\r", - '\n' => ~"\\n", - '\\' => ~"\\\\", - '\'' => ~"\\'", - '"' => ~"\\\"", - '\x20' .. '\x7e' => str::from_char(c), - _ => c.escape_unicode(), + '\t' => { f('\\'); f('t'); } + '\r' => { f('\\'); f('r'); } + '\n' => { f('\\'); f('n'); } + '\\' => { f('\\'); f('\\'); } + '\'' => { f('\\'); f('\''); } + '"' => { f('\\'); f('"'); } + '\x20' .. '\x7e' => { f(c); } + _ => c.escape_unicode(f), } } @@ -274,8 +273,8 @@ pub trait Char { fn is_digit_radix(&self, radix: uint) -> bool; fn to_digit(&self, radix: uint) -> Option; fn from_digit(num: uint, radix: uint) -> Option; - fn escape_unicode(&self) -> ~str; - fn escape_default(&self) -> ~str; + fn escape_unicode(&self, f: &fn(char)); + fn escape_default(&self, f: &fn(char)); fn len_utf8_bytes(&self) -> uint; } @@ -302,9 +301,9 @@ impl Char for char { fn from_digit(num: uint, radix: uint) -> Option { from_digit(num, radix) } - fn escape_unicode(&self) -> ~str { escape_unicode(*self) } + fn escape_unicode(&self, f: &fn(char)) { escape_unicode(*self, f) } - fn escape_default(&self) -> ~str { escape_default(*self) } + fn escape_default(&self, f: &fn(char)) { escape_default(*self, f) } fn len_utf8_bytes(&self) -> uint { len_utf8_bytes(*self) } } @@ -392,27 +391,37 @@ fn test_is_digit() { #[test] fn test_escape_default() { - assert_eq!('\n'.escape_default(), ~"\\n"); - assert_eq!('\r'.escape_default(), ~"\\r"); - assert_eq!('\''.escape_default(), ~"\\'"); - assert_eq!('"'.escape_default(), ~"\\\""); - assert_eq!(' '.escape_default(), ~" "); - assert_eq!('a'.escape_default(), ~"a"); - assert_eq!('~'.escape_default(), ~"~"); - assert_eq!('\x00'.escape_default(), ~"\\x00"); - assert_eq!('\x1f'.escape_default(), ~"\\x1f"); - assert_eq!('\x7f'.escape_default(), ~"\\x7f"); - assert_eq!('\xff'.escape_default(), ~"\\xff"); - assert_eq!('\u011b'.escape_default(), ~"\\u011b"); - assert_eq!('\U0001d4b6'.escape_default(), ~"\\U0001d4b6"); + fn string(c: char) -> ~str { + let mut result = ~""; + do escape_default(c) |c| { result.push_char(c); } + return result; + } + assert_eq!(string('\n'), ~"\\n"); + assert_eq!(string('\r'), ~"\\r"); + assert_eq!(string('\''), ~"\\'"); + assert_eq!(string('"'), ~"\\\""); + assert_eq!(string(' '), ~" "); + assert_eq!(string('a'), ~"a"); + assert_eq!(string('~'), ~"~"); + assert_eq!(string('\x00'), ~"\\x00"); + assert_eq!(string('\x1f'), ~"\\x1f"); + assert_eq!(string('\x7f'), ~"\\x7f"); + assert_eq!(string('\xff'), ~"\\xff"); + assert_eq!(string('\u011b'), ~"\\u011b"); + assert_eq!(string('\U0001d4b6'), ~"\\U0001d4b6"); } #[test] fn test_escape_unicode() { - assert_eq!('\x00'.escape_unicode(), ~"\\x00"); - assert_eq!('\n'.escape_unicode(), ~"\\x0a"); - assert_eq!(' '.escape_unicode(), ~"\\x20"); - assert_eq!('a'.escape_unicode(), ~"\\x61"); - assert_eq!('\u011b'.escape_unicode(), ~"\\u011b"); - assert_eq!('\U0001d4b6'.escape_unicode(), ~"\\U0001d4b6"); + fn string(c: char) -> ~str { + let mut result = ~""; + do escape_unicode(c) |c| { result.push_char(c); } + return result; + } + assert_eq!(string('\x00'), ~"\\x00"); + assert_eq!(string('\n'), ~"\\x0a"); + assert_eq!(string(' '), ~"\\x20"); + assert_eq!(string('a'), ~"\\x61"); + assert_eq!(string('\u011b'), ~"\\u011b"); + assert_eq!(string('\U0001d4b6'), ~"\\U0001d4b6"); } diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 117a474ffd7d3..0b6eb766b2990 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -754,8 +754,8 @@ impl Float for f32 { /// #[inline] pub fn to_str(num: f32) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigAll); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigAll); r } @@ -768,8 +768,8 @@ pub fn to_str(num: f32) -> ~str { /// #[inline] pub fn to_str_hex(num: f32) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 16u, true, strconv::SignNeg, strconv::DigAll); + let (r, _) = strconv::float_to_str_common( + num, 16u, true, strconv::SignNeg, strconv::DigAll); r } @@ -789,8 +789,8 @@ pub fn to_str_hex(num: f32) -> ~str { /// #[inline] pub fn to_str_radix(num: f32, rdx: uint) -> ~str { - let (r, special) = strconv::to_str_common( - &num, rdx, true, strconv::SignNeg, strconv::DigAll); + let (r, special) = strconv::float_to_str_common( + num, rdx, true, strconv::SignNeg, strconv::DigAll); if special { fail!("number has a special value, \ try to_str_radix_special() if those are expected") } r @@ -807,7 +807,7 @@ pub fn to_str_radix(num: f32, rdx: uint) -> ~str { /// #[inline] pub fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) { - strconv::to_str_common(&num, rdx, true, + strconv::float_to_str_common(num, rdx, true, strconv::SignNeg, strconv::DigAll) } @@ -822,8 +822,8 @@ pub fn to_str_radix_special(num: f32, rdx: uint) -> (~str, bool) { /// #[inline] pub fn to_str_exact(num: f32, dig: uint) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); r } @@ -838,8 +838,8 @@ pub fn to_str_exact(num: f32, dig: uint) -> ~str { /// #[inline] pub fn to_str_digits(num: f32, dig: uint) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); r } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index e13dff1e62357..c39c7a3a57d21 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -796,8 +796,8 @@ impl Float for f64 { /// #[inline] pub fn to_str(num: f64) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigAll); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigAll); r } @@ -810,8 +810,8 @@ pub fn to_str(num: f64) -> ~str { /// #[inline] pub fn to_str_hex(num: f64) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 16u, true, strconv::SignNeg, strconv::DigAll); + let (r, _) = strconv::float_to_str_common( + num, 16u, true, strconv::SignNeg, strconv::DigAll); r } @@ -831,8 +831,8 @@ pub fn to_str_hex(num: f64) -> ~str { /// #[inline] pub fn to_str_radix(num: f64, rdx: uint) -> ~str { - let (r, special) = strconv::to_str_common( - &num, rdx, true, strconv::SignNeg, strconv::DigAll); + let (r, special) = strconv::float_to_str_common( + num, rdx, true, strconv::SignNeg, strconv::DigAll); if special { fail!("number has a special value, \ try to_str_radix_special() if those are expected") } r @@ -849,7 +849,7 @@ pub fn to_str_radix(num: f64, rdx: uint) -> ~str { /// #[inline] pub fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) { - strconv::to_str_common(&num, rdx, true, + strconv::float_to_str_common(num, rdx, true, strconv::SignNeg, strconv::DigAll) } @@ -864,8 +864,8 @@ pub fn to_str_radix_special(num: f64, rdx: uint) -> (~str, bool) { /// #[inline] pub fn to_str_exact(num: f64, dig: uint) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigExact(dig)); r } @@ -880,8 +880,8 @@ pub fn to_str_exact(num: f64, dig: uint) -> ~str { /// #[inline] pub fn to_str_digits(num: f64, dig: uint) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigMax(dig)); r } diff --git a/src/libstd/num/float.rs b/src/libstd/num/float.rs index c583aeacf162d..7a6e3042e7b75 100644 --- a/src/libstd/num/float.rs +++ b/src/libstd/num/float.rs @@ -101,8 +101,8 @@ pub mod consts { /// #[inline] pub fn to_str(num: float) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigAll); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigAll); r } @@ -115,8 +115,8 @@ pub fn to_str(num: float) -> ~str { /// #[inline] pub fn to_str_hex(num: float) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 16u, true, strconv::SignNeg, strconv::DigAll); + let (r, _) = strconv::float_to_str_common( + num, 16u, true, strconv::SignNeg, strconv::DigAll); r } @@ -136,8 +136,8 @@ pub fn to_str_hex(num: float) -> ~str { /// #[inline] pub fn to_str_radix(num: float, radix: uint) -> ~str { - let (r, special) = strconv::to_str_common( - &num, radix, true, strconv::SignNeg, strconv::DigAll); + let (r, special) = strconv::float_to_str_common( + num, radix, true, strconv::SignNeg, strconv::DigAll); if special { fail!("number has a special value, \ try to_str_radix_special() if those are expected") } r @@ -154,7 +154,7 @@ pub fn to_str_radix(num: float, radix: uint) -> ~str { /// #[inline] pub fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) { - strconv::to_str_common(&num, radix, true, + strconv::float_to_str_common(num, radix, true, strconv::SignNeg, strconv::DigAll) } @@ -169,8 +169,8 @@ pub fn to_str_radix_special(num: float, radix: uint) -> (~str, bool) { /// #[inline] pub fn to_str_exact(num: float, digits: uint) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigExact(digits)); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigExact(digits)); r } @@ -185,8 +185,8 @@ pub fn to_str_exact(num: float, digits: uint) -> ~str { /// #[inline] pub fn to_str_digits(num: float, digits: uint) -> ~str { - let (r, _) = strconv::to_str_common( - &num, 10u, true, strconv::SignNeg, strconv::DigMax(digits)); + let (r, _) = strconv::float_to_str_common( + num, 10u, true, strconv::SignNeg, strconv::DigMax(digits)); r } diff --git a/src/libstd/num/int_macros.rs b/src/libstd/num/int_macros.rs index 845152f855251..f152d60cb7a35 100644 --- a/src/libstd/num/int_macros.rs +++ b/src/libstd/num/int_macros.rs @@ -17,6 +17,7 @@ macro_rules! int_module (($T:ty, $bits:expr) => (mod generated { use num::{ToStrRadix, FromStrRadix}; use num::{Zero, One, strconv}; use prelude::*; +use str; pub use cmp::{min, max}; @@ -529,25 +530,33 @@ impl FromStrRadix for $T { /// Convert to a string as a byte slice in a given base. #[inline] pub fn to_str_bytes(n: $T, radix: uint, f: &fn(v: &[u8]) -> U) -> U { - let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, - strconv::SignNeg, strconv::DigAll); - f(buf) + // The radix can be as low as 2, so we need at least 64 characters for a + // base 2 number, and then we need another for a possible '-' character. + let mut buf = [0u8, ..65]; + let mut cur = 0; + do strconv::int_to_str_bytes_common(n, radix, strconv::SignNeg) |i| { + buf[cur] = i; + cur += 1; + } + f(buf.slice(0, cur)) } /// Convert to a string in base 10. #[inline] pub fn to_str(num: $T) -> ~str { - let (buf, _) = strconv::to_str_common(&num, 10u, false, - strconv::SignNeg, strconv::DigAll); - buf + to_str_radix(num, 10u) } /// Convert to a string in a given base. #[inline] pub fn to_str_radix(num: $T, radix: uint) -> ~str { - let (buf, _) = strconv::to_str_common(&num, radix, false, - strconv::SignNeg, strconv::DigAll); - buf + let mut buf: ~[u8] = ~[]; + do strconv::int_to_str_bytes_common(num, radix, strconv::SignNeg) |i| { + buf.push(i); + } + // We know we generated valid utf-8, so we don't need to go through that + // check. + unsafe { str::raw::from_bytes_owned(buf) } } impl ToStr for $T { diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index a062838aacf04..1b1b82190d552 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -16,13 +16,13 @@ use ops::{Add, Sub, Mul, Div, Rem, Neg}; use option::{None, Option, Some}; use char; use str; -use str::{StrSlice}; +use str::StrSlice; use kinds::Copy; use vec; use vec::{CopyableVector, ImmutableVector}; use vec::OwnedVector; -use num::{NumCast, Zero, One, cast, pow_with_uint}; -use f64; +use num::{NumCast, Zero, One, cast, pow_with_uint, Integer}; +use num::{Round, Float, FPNaN, FPInfinite}; pub enum ExponentFormat { ExpNone, @@ -42,35 +42,6 @@ pub enum SignFormat { SignAll } -#[inline] -fn is_NaN(num: &T) -> bool { - *num != *num -} - -#[inline] -fn is_inf(num: &T) -> bool { - match NumStrConv::inf() { - None => false, - Some(n) => *num == n - } -} - -#[inline] -fn is_neg_inf(num: &T) -> bool { - match NumStrConv::neg_inf() { - None => false, - Some(n) => *num == n - } -} - -#[inline] -fn is_neg_zero>(num: &T) -> bool { - let _0: T = Zero::zero(); - let _1: T = One::one(); - - *num == _0 && is_neg_inf(&(_1 / *num)) -} - pub trait NumStrConv { fn NaN() -> Option; fn inf() -> Option; @@ -93,16 +64,9 @@ macro_rules! impl_NumStrConv_Floating (($t:ty) => ( fn neg_zero() -> Option<$t> { Some(-0.0 ) } #[inline] - fn round_to_zero(&self) -> $t { - ( if *self < 0.0 { f64::ceil(*self as f64) } - else { f64::floor(*self as f64) } - ) as $t - } - + fn round_to_zero(&self) -> $t { self.trunc() } #[inline] - fn fractional_part(&self) -> $t { - *self - self.round_to_zero() - } + fn fractional_part(&self) -> $t { self.fract() } } )) @@ -145,6 +109,87 @@ static negative_inf_buf: [u8, ..4] = ['-' as u8, 'i' as u8, 'n' as u8, 'f' as u8]; static nan_buf: [u8, ..3] = ['N' as u8, 'a' as u8, 'N' as u8]; +/** + * 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 + * conversion functions like `to_str()` or `to_str_radix()`. + * + * # Arguments + * - `num` - The number to convert. Accepts any number that + * implements the numeric traits. + * - `radix` - Base to use. Accepts only the values 2-36. + * - `sign` - How to emit the sign. Options are: + * - `SignNone`: No sign at all. Basically emits `abs(num)`. + * - `SignNeg`: Only `-` on negative values. + * - `SignAll`: Both `+` on positive, and `-` on negative numbers. + * - `f` - a callback which will be invoked for each ascii character + * which composes the string representation of this integer + * + * # Return value + * A tuple containing the byte vector, and a boolean flag indicating + * whether it represents a special value like `inf`, `-inf`, `NaN` or not. + * It returns a tuple because there can be ambiguity between a special value + * and a number representation at higher bases. + * + * # Failure + * - Fails if `radix` < 2 or `radix` > 36. + */ +pub fn int_to_str_bytes_common+Neg+Rem+Mul>( + num: T, radix: uint, sign: SignFormat, f: &fn(u8)) { + assert!(2 <= radix && radix <= 36); + + let _0: T = Zero::zero(); + + let neg = num < _0; + let radix_gen: T = cast(radix); + + let mut deccum = num; + // This is just for integral types, the largest of which is a u64. The + // smallest base that we can have is 2, so the most number of digits we're + // ever going to have is 64 + let mut buf = [0u8, ..64]; + let mut cur = 0; + + // Loop at least once to make sure at least a `0` gets emitted. + loop { + // Calculate the absolute value of each digit instead of only + // doing it once for the whole number because a + // representable negative number doesn't necessary have an + // representable additive inverse of the same type + // (See twos complement). But we assume that for the + // numbers [-35 .. 0] we always have [0 .. 35]. + let current_digit_signed = deccum % radix_gen; + let current_digit = if current_digit_signed < _0 { + -current_digit_signed + } else { + current_digit_signed + }; + buf[cur] = match current_digit.to_u8() { + i @ 0..9 => '0' as u8 + i, + i => 'a' as u8 + (i - 10), + }; + cur += 1; + + deccum = deccum / radix_gen; + // No more digits to calculate for the non-fractional part -> break + if deccum == _0 { break; } + } + + // Decide what sign to put in front + match sign { + SignNeg | SignAll if neg => { f('-' as u8); } + SignAll => { f('+' as u8); } + _ => () + } + + // We built the number in reverse order, so un-reverse it here + while cur > 0 { + cur -= 1; + f(buf[cur]); + } +} + /** * Converts a number to its string representation as a byte vector. * This is meant to be a common base implementation for all numeric string @@ -176,44 +221,39 @@ static nan_buf: [u8, ..3] = ['N' as u8, 'a' as u8, 'N' as u8]; * # Failure * - Fails if `radix` < 2 or `radix` > 36. */ -pub fn to_str_bytes_common+Neg+Rem+Mul>( - num: &T, radix: uint, negative_zero: bool, + num: T, radix: uint, negative_zero: bool, sign: SignFormat, digits: SignificantDigits) -> (~[u8], bool) { - if (radix as int) < 2 { - fail!("to_str_bytes_common: radix %? to low, must lie in the range [2, 36]", radix); - } else if radix as int > 36 { - fail!("to_str_bytes_common: radix %? to high, must lie in the range [2, 36]", radix); - } + assert!(2 <= radix && radix <= 36); let _0: T = Zero::zero(); let _1: T = One::one(); - if is_NaN(num) { - return ("NaN".as_bytes().to_owned(), true); - } - else if is_inf(num){ - return match sign { - SignAll => ("+inf".as_bytes().to_owned(), true), - _ => ("inf".as_bytes().to_owned(), true) + match num.classify() { + FPNaN => { return ("NaN".as_bytes().to_owned(), true); } + FPInfinite if num > _0 => { + return match sign { + SignAll => ("+inf".as_bytes().to_owned(), true), + _ => ("inf".as_bytes().to_owned(), true) + }; } - } - else if is_neg_inf(num) { - return match sign { - SignNone => ("inf".as_bytes().to_owned(), true), - _ => ("-inf".as_bytes().to_owned(), true), + FPInfinite if num < _0 => { + return match sign { + SignNone => ("inf".as_bytes().to_owned(), true), + _ => ("-inf".as_bytes().to_owned(), true), + }; } + _ => {} } - let neg = *num < _0 || (negative_zero && is_neg_zero(num)); + let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity()); let mut buf: ~[u8] = ~[]; let radix_gen: T = cast(radix as int); - let mut deccum; - // First emit the non-fractional part, looping at least once to make // sure at least a `0` gets emitted. - deccum = num.round_to_zero(); + let mut deccum = num.trunc(); loop { // Calculate the absolute value of each digit instead of only // doing it once for the whole number because a @@ -221,16 +261,11 @@ pub fn to_str_bytes_common 0) { buf.push('.' as u8); let mut dig = 0u; @@ -286,18 +321,13 @@ pub fn to_str_bytes_common+Neg+Rem+Mul>( - num: &T, radix: uint, negative_zero: bool, +pub fn float_to_str_common+Neg+Rem+Mul>( + num: T, radix: uint, negative_zero: bool, sign: SignFormat, digits: SignificantDigits) -> (~str, bool) { - let (bytes, special) = to_str_bytes_common(num, radix, + let (bytes, special) = float_to_str_bytes_common(num, radix, negative_zero, sign, digits); (str::from_bytes(bytes), special) } diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs index 0dabe7fafa834..25e338fcd0f64 100644 --- a/src/libstd/num/uint_macros.rs +++ b/src/libstd/num/uint_macros.rs @@ -18,6 +18,7 @@ use num::BitCount; use num::{ToStrRadix, FromStrRadix}; use num::{Zero, One, strconv}; use prelude::*; +use str; pub use cmp::{min, max}; @@ -356,25 +357,33 @@ impl FromStrRadix for $T { /// Convert to a string as a byte slice in a given base. #[inline] pub fn to_str_bytes(n: $T, radix: uint, f: &fn(v: &[u8]) -> U) -> U { - let (buf, _) = strconv::to_str_bytes_common(&n, radix, false, - strconv::SignNeg, strconv::DigAll); - f(buf) + // The radix can be as low as 2, so we need at least 64 characters for a + // base 2 number. + let mut buf = [0u8, ..64]; + let mut cur = 0; + do strconv::int_to_str_bytes_common(n, radix, strconv::SignNone) |i| { + buf[cur] = i; + cur += 1; + } + f(buf.slice(0, cur)) } /// Convert to a string in base 10. #[inline] pub fn to_str(num: $T) -> ~str { - let (buf, _) = strconv::to_str_common(&num, 10u, false, - strconv::SignNeg, strconv::DigAll); - buf + to_str_radix(num, 10u) } /// Convert to a string in a given base. #[inline] pub fn to_str_radix(num: $T, radix: uint) -> ~str { - let (buf, _) = strconv::to_str_common(&num, radix, false, - strconv::SignNeg, strconv::DigAll); - buf + let mut buf = ~[]; + do strconv::int_to_str_bytes_common(num, radix, strconv::SignNone) |i| { + buf.push(i); + } + // We know we generated valid utf-8, so we don't need to go through that + // check. + unsafe { str::raw::from_bytes_owned(buf) } } impl ToStr for $T { diff --git a/src/libstd/repr.rs b/src/libstd/repr.rs index f39b5a00ed054..fdda65d3e95b8 100644 --- a/src/libstd/repr.rs +++ b/src/libstd/repr.rs @@ -57,9 +57,9 @@ impl EscapedCharWriter for @Writer { '"' => self.write_str("\\\""), '\x20'..'\x7e' => self.write_char(ch), _ => { - // FIXME #4423: This is inefficient because it requires a - // malloc. - self.write_str(char::escape_unicode(ch)) + do char::escape_unicode(ch) |c| { + self.write_char(c); + } } } } @@ -81,65 +81,35 @@ impl Repr for bool { } } -impl Repr for int { - fn write_repr(&self, writer: @Writer) { writer.write_int(*self); } -} -impl Repr for i8 { - fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } -} -impl Repr for i16 { - fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } -} -impl Repr for i32 { - fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } -} -impl Repr for i64 { - // FIXME #4424: This can lose precision. - fn write_repr(&self, writer: @Writer) { writer.write_int(*self as int); } -} - -impl Repr for uint { - fn write_repr(&self, writer: @Writer) { writer.write_uint(*self); } -} -impl Repr for u8 { - fn write_repr(&self, writer: @Writer) { - writer.write_uint(*self as uint); - } -} -impl Repr for u16 { +macro_rules! int_repr(($ty:ident) => (impl Repr for $ty { fn write_repr(&self, writer: @Writer) { - writer.write_uint(*self as uint); - } -} -impl Repr for u32 { - fn write_repr(&self, writer: @Writer) { - writer.write_uint(*self as uint); - } -} -impl Repr for u64 { - // FIXME #4424: This can lose precision. - fn write_repr(&self, writer: @Writer) { - writer.write_uint(*self as uint); + do ::$ty::to_str_bytes(*self, 10u) |bits| { + writer.write(bits); + } } -} +})) -impl Repr for float { - // FIXME #4423: This mallocs. - fn write_repr(&self, writer: @Writer) { writer.write_str(self.to_str()); } -} -impl Repr for f32 { - // FIXME #4423 This mallocs. - fn write_repr(&self, writer: @Writer) { writer.write_str(self.to_str()); } -} -impl Repr for f64 { - // FIXME #4423: This mallocs. - fn write_repr(&self, writer: @Writer) { writer.write_str(self.to_str()); } -} +int_repr!(int) +int_repr!(i8) +int_repr!(i16) +int_repr!(i32) +int_repr!(i64) +int_repr!(uint) +int_repr!(u8) +int_repr!(u16) +int_repr!(u32) +int_repr!(u64) -impl Repr for char { - fn write_repr(&self, writer: @Writer) { writer.write_char(*self); } -} +macro_rules! num_repr(($ty:ident) => (impl Repr for $ty { + fn write_repr(&self, writer: @Writer) { + let s = self.to_str(); + writer.write(s.as_bytes()); + } +})) +num_repr!(float) +num_repr!(f32) +num_repr!(f64) // New implementation using reflect::MovePtr diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 8e0b3b6ad3552..4115cad655940 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -54,7 +54,6 @@ Section: Creating a string * * Raises the `not_utf8` condition if invalid UTF-8 */ - pub fn from_bytes(vv: &[u8]) -> ~str { use str::not_utf8::cond; @@ -68,6 +67,25 @@ pub fn from_bytes(vv: &[u8]) -> ~str { } } +/** + * Consumes a vector of bytes to create a new utf-8 string + * + * # Failure + * + * Raises the `not_utf8` condition if invalid UTF-8 + */ +pub fn from_bytes_owned(vv: ~[u8]) -> ~str { + use str::not_utf8::cond; + + if !is_utf8(vv) { + let first_bad_byte = *vv.iter().find_(|&b| !is_utf8([*b])).get(); + cond.raise(fmt!("from_bytes: input is not UTF-8; first bad byte is %u", + first_bad_byte as uint)) + } else { + return unsafe { raw::from_bytes_owned(vv) } + } +} + /** * Convert a vector of bytes to a UTF-8 string. * The vector needs to be one byte longer than the string, and end with a 0 byte. @@ -850,6 +868,13 @@ pub mod raw { } } + /// Converts an owned vector of bytes to a new owned string. This assumes + /// that the utf-8-ness of the vector has already been validated + pub unsafe fn from_bytes_owned(mut v: ~[u8]) -> ~str { + v.push(0u8); + cast::transmute(v) + } + /// Converts a vector of bytes to a string. /// The byte slice needs to contain valid utf8 and needs to be one byte longer than /// the string, if possible ending in a 0 byte. @@ -1472,7 +1497,9 @@ impl<'self> StrSlice<'self> for &'self str { let mut out: ~str = ~""; out.reserve_at_least(self.len()); for self.iter().advance |c| { - out.push_str(char::escape_default(c)); + do c.escape_default |c| { + out.push_char(c); + } } out } @@ -1482,7 +1509,9 @@ impl<'self> StrSlice<'self> for &'self str { let mut out: ~str = ~""; out.reserve_at_least(self.len()); for self.iter().advance |c| { - out.push_str(char::escape_unicode(c)); + do c.escape_unicode |c| { + out.push_char(c); + } } out } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 94147825da49c..a50fa4168320c 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -16,7 +16,6 @@ use util::interner::StrInterner; use util::interner; use std::cast; -use std::char; use std::cmp::Equiv; use std::local_data; use std::rand; @@ -166,7 +165,12 @@ pub fn to_str(in: @ident_interner, t: &Token) -> ~str { /* Literals */ LIT_INT(c, ast::ty_char) => { - ~"'" + char::escape_default(c as char) + "'" + let mut res = ~"'"; + do (c as char).escape_default |c| { + res.push_char(c); + } + res.push_char('\''); + res } LIT_INT(i, t) => { i.to_str() + ast_util::int_ty_to_str(t) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 978561eaa67c6..5e685d85f95df 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -27,7 +27,6 @@ use print::pp::{breaks, consistent, inconsistent, eof}; use print::pp; use print::pprust; -use std::char; use std::io; use std::u64; use std::uint; @@ -2016,7 +2015,12 @@ pub fn print_literal(s: @ps, lit: @ast::lit) { match lit.node { ast::lit_str(st) => print_string(s, st), ast::lit_int(ch, ast::ty_char) => { - word(s.s, ~"'" + char::escape_default(ch as char) + "'"); + let mut res = ~"'"; + do (ch as char).escape_default |c| { + res.push_char(c); + } + res.push_char('\''); + word(s.s, res); } ast::lit_int(i, t) => { if i < 0_i64 {