From d3155faedee97cb916735573fbf067d6305ee730 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 28 Jun 2013 14:05:10 -0700 Subject: [PATCH] Specialize to_str_common for floats/integers in strconv This allows the integral paths to avoid allocations on the heap Closes #4424, #4423 --- src/libextra/terminfo/parm.rs | 16 ++- src/libstd/char.rs | 2 +- src/libstd/num/f32.rs | 22 ++-- src/libstd/num/f64.rs | 22 ++-- src/libstd/num/float.rs | 22 ++-- src/libstd/num/int_macros.rs | 27 +++-- src/libstd/num/strconv.rs | 196 ++++++++++++++++++++-------------- src/libstd/num/uint_macros.rs | 27 +++-- src/libstd/repr.rs | 78 +++++--------- src/libstd/str.rs | 35 +++++- 10 files changed, 250 insertions(+), 197 deletions(-) diff --git a/src/libextra/terminfo/parm.rs b/src/libextra/terminfo/parm.rs index f3edd81f9ac..b7d21ea0ee3 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 101ea67cf59..6a9555f4efc 100644 --- a/src/libstd/char.rs +++ b/src/libstd/char.rs @@ -10,8 +10,8 @@ //! Utilities for manipulating the char type -use container::Container; use option::{None, Option, Some}; +use int; use str::StrSlice; use unicode::{derived_property, general_category}; diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 117a474ffd7..0b6eb766b29 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 e13dff1e623..c39c7a3a57d 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 c583aeacf16..7a6e3042e7b 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 845152f8552..f152d60cb7a 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 a062838aacf..1b1b82190d5 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 0dabe7fafa8..25e338fcd0f 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 a96949d84c9..fdda65d3e95 100644 --- a/src/libstd/repr.rs +++ b/src/libstd/repr.rs @@ -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 { +macro_rules! int_repr(($ty:ident) => (impl Repr for $ty { 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 u16 { - 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); - } -} +})) -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 8e0b3b6ad35..4115cad6559 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 }