auto merge of #12382 : bjz/rust/fmt-int, r=alexcrichton

This is PR is the beginning of a complete rewrite and ultimate removal of the `std::num::strconv` module (see #6220), and the removal of the `ToStrRadix` trait in favour of using the `std::fmt` functionality directly. This should make for a cleaner API, encourage less allocation, and make the implementation more comprehensible .

The `Formatter::{pad_integral, with_padding}` methods have also been refactored make things easier to understand.

The formatting tests for integers have been moved out of `run-pass/ifmt.rs` in order to provide more immediate feedback when building using `make check-stage2-std NO_REBUILD=1`.

Arbitrary radixes are now easier to use in format strings. For example:

~~~rust
assert_eq!(format!("{:04}", radix(3, 2)), ~"0011");
~~~

The benchmarks have been standardised between `std::num::strconv` and `std::num::fmt` to make it easier to compare the performance of the different implementations.

~~~
 type | radix | std::num::strconv      | std::num::fmt
======|=======|========================|======================
 int  | bin   | 1748 ns/iter (+/- 150) | 321 ns/iter (+/- 25)
 int  | oct   |  706 ns/iter (+/- 53)  | 179 ns/iter (+/- 22)
 int  | dec   |  640 ns/iter (+/- 59)  | 207 ns/iter (+/- 10)
 int  | hex   |  637 ns/iter (+/- 77)  | 205 ns/iter (+/- 19)
 int  | 36    |  446 ns/iter (+/- 30)  | 309 ns/iter (+/- 20)
------|-------|------------------------|----------------------
 uint | bin   | 1724 ns/iter (+/- 159) | 322 ns/iter (+/- 13)
 uint | oct   |  663 ns/iter (+/- 25)  | 175 ns/iter (+/- 7)
 uint | dec   |  613 ns/iter (+/- 30)  | 186 ns/iter (+/- 6)
 uint | hex   |  519 ns/iter (+/- 44)  | 207 ns/iter (+/- 20)
 uint | 36    |  418 ns/iter (+/- 16)  | 308 ns/iter (+/- 32)
~~~
This commit is contained in:
bors 2014-02-21 16:36:52 -08:00
commit d2f73abf10
15 changed files with 672 additions and 341 deletions

View File

@ -22,13 +22,14 @@ let y: int = x.unwrap();
**Int to string, in non-base-10**
Use [`ToStrRadix`](http://static.rust-lang.org/doc/master/std/num/trait.ToStrRadix.html).
Use the `format!` syntax extension.
~~~
use std::num::ToStrRadix;
let x: int = 42;
let y: ~str = x.to_str_radix(16);
let y: ~str = format!("{:t}", x); // binary
let y: ~str = format!("{:o}", x); // octal
let y: ~str = format!("{:x}", x); // lowercase hexadecimal
let y: ~str = format!("{:X}", x); // uppercase hexidecimal
~~~
**String to int, in non-base-10**

View File

@ -2024,7 +2024,7 @@ impl ops::Sub<TypeContents,TypeContents> for TypeContents {
impl ToStr for TypeContents {
fn to_str(&self) -> ~str {
format!("TypeContents({})", self.bits.to_str_radix(2))
format!("TypeContents({:t})", self.bits)
}
}

View File

@ -70,8 +70,7 @@ impl<V:Vid + ToStr,T:InferStr> InferStr for VarValue<V, T> {
fn inf_str(&self, cx: &InferCtxt) -> ~str {
match *self {
Redirect(ref vid) => format!("Redirect({})", vid.to_str()),
Root(ref pt, rk) => format!("Root({}, {})", pt.inf_str(cx),
rk.to_str_radix(10u))
Root(ref pt, rk) => format!("Root({}, {})", pt.inf_str(cx), rk)
}
}
}

View File

@ -215,7 +215,7 @@ impl fmt::Binary for Vector2D {
// for details, and the function `pad` can be used to pad strings.
let decimals = f.precision.unwrap_or(3);
let string = f64::to_str_exact(magnitude, decimals);
f.pad_integral(string.as_bytes(), "", true)
f.pad_integral(true, "", string.as_bytes())
}
}
@ -493,6 +493,11 @@ use str;
use vec::ImmutableVector;
use vec;
pub use self::num::radix;
pub use self::num::Radix;
pub use self::num::RadixFmt;
mod num;
pub mod parse;
pub mod rt;
@ -891,58 +896,60 @@ impl<'a> Formatter<'a> {
///
/// # Arguments
///
/// * s - the byte array that the number has been formatted into
/// * alternate_prefix - if the '#' character (FlagAlternate) is
/// provided, this is the prefix to put in front of the number.
/// Currently this is 0x/0o/0b/etc.
/// * positive - whether the original integer was positive or not.
/// * is_positive - whether the original integer was positive or not.
/// * prefix - if the '#' character (FlagAlternate) is provided, this
/// is the prefix to put in front of the number.
/// * buf - the byte array that the number has been formatted into
///
/// This function will correctly account for the flags provided as well as
/// the minimum width. It will not take precision into account.
pub fn pad_integral(&mut self, s: &[u8], alternate_prefix: &str,
positive: bool) -> Result {
pub fn pad_integral(&mut self, is_positive: bool, prefix: &str, buf: &[u8]) -> Result {
use fmt::parse::{FlagAlternate, FlagSignPlus, FlagSignAwareZeroPad};
let mut actual_len = s.len();
if self.flags & 1 << (FlagAlternate as uint) != 0 {
actual_len += alternate_prefix.len();
}
if self.flags & 1 << (FlagSignPlus as uint) != 0 {
actual_len += 1;
} else if !positive {
actual_len += 1;
let mut width = buf.len();
let mut sign = None;
if !is_positive {
sign = Some('-'); width += 1;
} else if self.flags & (1 << (FlagSignPlus as uint)) != 0 {
sign = Some('+'); width += 1;
}
let mut signprinted = false;
let sign = |this: &mut Formatter| {
if !signprinted {
if this.flags & 1 << (FlagSignPlus as uint) != 0 && positive {
try!(this.buf.write(['+' as u8]));
} else if !positive {
try!(this.buf.write(['-' as u8]));
}
if this.flags & 1 << (FlagAlternate as uint) != 0 {
try!(this.buf.write(alternate_prefix.as_bytes()));
}
signprinted = true;
}
Ok(())
};
let emit = |this: &mut Formatter| {
sign(this).and_then(|()| this.buf.write(s))
let mut prefixed = false;
if self.flags & (1 << (FlagAlternate as uint)) != 0 {
prefixed = true; width += prefix.len();
}
// Writes the sign if it exists, and then the prefix if it was requested
let write_prefix = |f: &mut Formatter| {
for c in sign.move_iter() { try!(f.buf.write_char(c)); }
if prefixed { f.buf.write_str(prefix) }
else { Ok(()) }
};
// The `width` field is more of a `min-width` parameter at this point.
match self.width {
None => emit(self),
Some(min) if actual_len >= min => emit(self),
// If there's no minimum length requirements then we can just
// write the bytes.
None => {
try!(write_prefix(self)); self.buf.write(buf)
}
// Check if we're over the minimum width, if so then we can also
// just write the bytes.
Some(min) if width >= min => {
try!(write_prefix(self)); self.buf.write(buf)
}
// The sign and prefix goes before the padding if the fill character
// is zero
Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => {
self.fill = '0';
try!(write_prefix(self));
self.with_padding(min - width, parse::AlignRight, |f| f.buf.write(buf))
}
// Otherwise, the sign and prefix goes after the padding
Some(min) => {
if self.flags & 1 << (FlagSignAwareZeroPad as uint) != 0 {
self.fill = '0';
try!(sign(self));
}
self.with_padding(min - actual_len, parse::AlignRight, |me| {
emit(me)
self.with_padding(min - width, parse::AlignRight, |f| {
try!(write_prefix(f)); f.buf.write(buf)
})
}
}
@ -979,19 +986,16 @@ impl<'a> Formatter<'a> {
}
None => {}
}
// The `width` field is more of a `min-width` parameter at this point.
match self.width {
// If we're under the maximum length, and there's no minimum length
// requirements, then we can just emit the string
None => self.buf.write(s.as_bytes()),
// If we're under the maximum width, check if we're over the minimum
// width, if so it's as easy as just emitting the string.
Some(width) if s.char_len() >= width => {
self.buf.write(s.as_bytes())
}
// If we're under both the maximum and the minimum width, then fill
// up the minimum width with the specified string + some alignment.
Some(width) => {
@ -1002,6 +1006,8 @@ impl<'a> Formatter<'a> {
}
}
/// Runs a callback, emitting the correct padding either before or
/// afterwards depending on whether right or left alingment is requested.
fn with_padding(&mut self,
padding: uint,
default: parse::Alignment,
@ -1075,67 +1081,6 @@ impl Char for char {
}
}
macro_rules! int_base(($ty:ident, $into:ident, $base:expr,
$name:ident, $prefix:expr) => {
impl $name for $ty {
fn fmt(&self, f: &mut Formatter) -> Result {
::$into::to_str_bytes(*self as $into, $base, |buf| {
f.pad_integral(buf, $prefix, true)
})
}
}
})
macro_rules! upper_hex(($ty:ident, $into:ident) => {
impl UpperHex for $ty {
fn fmt(&self, f: &mut Formatter) -> Result {
::$into::to_str_bytes(*self as $into, 16, |buf| {
upperhex(buf, f)
})
}
}
})
// Not sure why, but this causes an "unresolved enum variant, struct or const"
// when inlined into the above macro...
#[doc(hidden)]
pub fn upperhex(buf: &[u8], f: &mut Formatter) -> Result {
let mut local = [0u8, ..16];
for i in ::iter::range(0, buf.len()) {
local[i] = match buf[i] as char {
'a' .. 'f' => (buf[i] - 'a' as u8) + 'A' as u8,
c => c as u8,
}
}
f.pad_integral(local.slice_to(buf.len()), "0x", true)
}
macro_rules! integer(($signed:ident, $unsigned:ident) => {
// Signed is special because it actuall emits the negative sign,
// nothing else should do that, however.
impl Signed for $signed {
fn fmt(&self, f: &mut Formatter) -> Result {
::$unsigned::to_str_bytes(self.abs() as $unsigned, 10, |buf| {
f.pad_integral(buf, "", *self >= 0)
})
}
}
int_base!($signed, $unsigned, 2, Binary, "0b")
int_base!($signed, $unsigned, 8, Octal, "0o")
int_base!($signed, $unsigned, 16, LowerHex, "0x")
upper_hex!($signed, $unsigned)
int_base!($unsigned, $unsigned, 2, Binary, "0b")
int_base!($unsigned, $unsigned, 8, Octal, "0o")
int_base!($unsigned, $unsigned, 10, Unsigned, "")
int_base!($unsigned, $unsigned, 16, LowerHex, "0x")
upper_hex!($unsigned, $unsigned)
})
integer!(int, uint)
integer!(i8, u8)
integer!(i16, u16)
integer!(i32, u32)
integer!(i64, u64)
macro_rules! floating(($ty:ident) => {
impl Float for $ty {
fn fmt(&self, fmt: &mut Formatter) -> Result {
@ -1144,7 +1089,7 @@ macro_rules! floating(($ty:ident) => {
Some(i) => ::$ty::to_str_exact(self.abs(), i),
None => ::$ty::to_str_digits(self.abs(), 6)
};
fmt.pad_integral(s.as_bytes(), "", *self >= 0.0)
fmt.pad_integral(*self >= 0.0, "", s.as_bytes())
}
}
@ -1155,7 +1100,7 @@ macro_rules! floating(($ty:ident) => {
Some(i) => ::$ty::to_str_exp_exact(self.abs(), i, false),
None => ::$ty::to_str_exp_digits(self.abs(), 6, false)
};
fmt.pad_integral(s.as_bytes(), "", *self >= 0.0)
fmt.pad_integral(*self >= 0.0, "", s.as_bytes())
}
}
@ -1166,7 +1111,7 @@ macro_rules! floating(($ty:ident) => {
Some(i) => ::$ty::to_str_exp_exact(self.abs(), i, true),
None => ::$ty::to_str_exp_digits(self.abs(), 6, true)
};
fmt.pad_integral(s.as_bytes(), "", *self >= 0.0)
fmt.pad_integral(*self >= 0.0, "", s.as_bytes())
}
}
})
@ -1193,9 +1138,7 @@ impl<T> Poly for T {
impl<T> Pointer for *T {
fn fmt(&self, f: &mut Formatter) -> Result {
f.flags |= 1 << (parse::FlagAlternate as uint);
::uint::to_str_bytes(*self as uint, 16, |buf| {
f.pad_integral(buf, "0x", true)
})
secret_lower_hex::<uint>(&(*self as uint), f)
}
}
impl<T> Pointer for *mut T {
@ -1223,16 +1166,6 @@ macro_rules! delegate(($ty:ty to $other:ident) => {
}
}
})
delegate!(int to signed)
delegate!( i8 to signed)
delegate!(i16 to signed)
delegate!(i32 to signed)
delegate!(i64 to signed)
delegate!(uint to unsigned)
delegate!( u8 to unsigned)
delegate!( u16 to unsigned)
delegate!( u32 to unsigned)
delegate!( u64 to unsigned)
delegate!(~str to string)
delegate!(&'a str to string)
delegate!(bool to bool)

467
src/libstd/fmt/num.rs Normal file
View File

@ -0,0 +1,467 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Integer and floating-point number formatting
// FIXME: #6220 Implement floating point formatting
use container::Container;
use fmt;
use iter::{Iterator, DoubleEndedIterator};
use num::{Int, cast, zero};
use option::{Some, None};
use vec::{ImmutableVector, MutableVector};
/// A type that represents a specific radix
trait GenericRadix {
/// The number of digits.
fn base(&self) -> u8;
/// A radix-specific prefix string.
fn prefix(&self) -> &'static str { "" }
/// Converts an integer to corresponding radix digit.
fn digit(&self, x: u8) -> u8;
/// Format an integer using the radix using a formatter.
fn fmt_int<T: Int>(&self, mut x: T, f: &mut fmt::Formatter) -> fmt::Result {
// The radix can be as low as 2, so we need a buffer of at least 64
// characters for a base 2 number.
let mut buf = [0u8, ..64];
let base = cast(self.base()).unwrap();
let mut curr = buf.len();
let is_positive = x >= zero();
if is_positive {
// Accumulate each digit of the number from the least significant
// to the most significant figure.
for byte in buf.mut_iter().rev() {
let n = x % base; // Get the current place value.
x = x / base; // Deaccumulate the number.
*byte = self.digit(cast(n).unwrap()); // Store the digit in the buffer.
curr -= 1;
if x == zero() { break; } // No more digits left to accumulate.
}
} else {
// Do the same as above, but accounting for two's complement.
for byte in buf.mut_iter().rev() {
let n = -(x % base); // Get the current place value.
x = x / base; // Deaccumulate the number.
*byte = self.digit(cast(n).unwrap()); // Store the digit in the buffer.
curr -= 1;
if x == zero() { break; } // No more digits left to accumulate.
}
}
f.pad_integral(is_positive, self.prefix(), buf.slice_from(curr))
}
}
/// A binary (base 2) radix
#[deriving(Clone, Eq)]
struct Binary;
/// An octal (base 8) radix
#[deriving(Clone, Eq)]
struct Octal;
/// A decimal (base 10) radix
#[deriving(Clone, Eq)]
struct Decimal;
/// A hexidecimal (base 16) radix, formatted with lower-case characters
#[deriving(Clone, Eq)]
struct LowerHex;
/// A hexidecimal (base 16) radix, formatted with upper-case characters
#[deriving(Clone, Eq)]
pub struct UpperHex;
macro_rules! radix {
($T:ident, $base:expr, $prefix:expr, $($x:pat => $conv:expr),+) => {
impl GenericRadix for $T {
fn base(&self) -> u8 { $base }
fn prefix(&self) -> &'static str { $prefix }
fn digit(&self, x: u8) -> u8 {
match x {
$($x => $conv,)+
x => fail!("number not in the range 0..{}: {}", self.base() - 1, x),
}
}
}
}
}
radix!(Binary, 2, "0b", x @ 0 .. 2 => '0' as u8 + x)
radix!(Octal, 8, "0o", x @ 0 .. 7 => '0' as u8 + x)
radix!(Decimal, 10, "", x @ 0 .. 9 => '0' as u8 + x)
radix!(LowerHex, 16, "0x", x @ 0 .. 9 => '0' as u8 + x,
x @ 10 ..15 => 'a' as u8 + (x - 10))
radix!(UpperHex, 16, "0x", x @ 0 .. 9 => '0' as u8 + x,
x @ 10 ..15 => 'A' as u8 + (x - 10))
/// A radix with in the range of `2..36`.
#[deriving(Clone, Eq)]
pub struct Radix {
priv base: u8,
}
impl Radix {
fn new(base: u8) -> Radix {
assert!(2 <= base && base <= 36, "the base must be in the range of 0..36: {}", base);
Radix { base: base }
}
}
impl GenericRadix for Radix {
fn base(&self) -> u8 { self.base }
fn digit(&self, x: u8) -> u8 {
match x {
x @ 0 ..9 => '0' as u8 + x,
x if x < self.base() => 'a' as u8 + (x - 10),
x => fail!("number not in the range 0..{}: {}", self.base() - 1, x),
}
}
}
/// A helper type for formatting radixes.
pub struct RadixFmt<T, R>(T, R);
/// Constructs a radix formatter in the range of `2..36`.
///
/// # Example
///
/// ~~~
/// use std::fmt::radix;
/// assert_eq!(format!("{}", radix(55, 36)), ~"1j");
/// ~~~
pub fn radix<T>(x: T, base: u8) -> RadixFmt<T, Radix> {
RadixFmt(x, Radix::new(base))
}
macro_rules! radix_fmt {
($T:ty as $U:ty, $fmt:ident) => {
impl fmt::Show for RadixFmt<$T, Radix> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { RadixFmt(ref x, radix) => radix.$fmt(*x as $U, f) }
}
}
}
}
macro_rules! int_base {
($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => {
impl fmt::$Trait for $T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
$Radix.fmt_int(*self as $U, f)
}
}
}
}
macro_rules! integer {
($Int:ident, $Uint:ident) => {
int_base!(Show for $Int as $Int -> Decimal)
int_base!(Signed for $Int as $Int -> Decimal)
int_base!(Binary for $Int as $Uint -> Binary)
int_base!(Octal for $Int as $Uint -> Octal)
int_base!(LowerHex for $Int as $Uint -> LowerHex)
int_base!(UpperHex for $Int as $Uint -> UpperHex)
radix_fmt!($Int as $Uint, fmt_int)
int_base!(Show for $Uint as $Uint -> Decimal)
int_base!(Unsigned for $Uint as $Uint -> Decimal)
int_base!(Binary for $Uint as $Uint -> Binary)
int_base!(Octal for $Uint as $Uint -> Octal)
int_base!(LowerHex for $Uint as $Uint -> LowerHex)
int_base!(UpperHex for $Uint as $Uint -> UpperHex)
radix_fmt!($Uint as $Uint, fmt_int)
}
}
integer!(int, uint)
integer!(i8, u8)
integer!(i16, u16)
integer!(i32, u32)
integer!(i64, u64)
#[cfg(test)]
mod tests {
use fmt::radix;
use super::{Binary, Octal, Decimal, LowerHex, UpperHex};
use super::{GenericRadix, Radix};
#[test]
fn test_radix_base() {
assert_eq!(Binary.base(), 2);
assert_eq!(Octal.base(), 8);
assert_eq!(Decimal.base(), 10);
assert_eq!(LowerHex.base(), 16);
assert_eq!(UpperHex.base(), 16);
assert_eq!(Radix { base: 36 }.base(), 36);
}
#[test]
fn test_radix_prefix() {
assert_eq!(Binary.prefix(), "0b");
assert_eq!(Octal.prefix(), "0o");
assert_eq!(Decimal.prefix(), "");
assert_eq!(LowerHex.prefix(), "0x");
assert_eq!(UpperHex.prefix(), "0x");
assert_eq!(Radix { base: 36 }.prefix(), "");
}
#[test]
fn test_radix_digit() {
assert_eq!(Binary.digit(0), '0' as u8);
assert_eq!(Binary.digit(2), '2' as u8);
assert_eq!(Octal.digit(0), '0' as u8);
assert_eq!(Octal.digit(7), '7' as u8);
assert_eq!(Decimal.digit(0), '0' as u8);
assert_eq!(Decimal.digit(9), '9' as u8);
assert_eq!(LowerHex.digit(0), '0' as u8);
assert_eq!(LowerHex.digit(10), 'a' as u8);
assert_eq!(LowerHex.digit(15), 'f' as u8);
assert_eq!(UpperHex.digit(0), '0' as u8);
assert_eq!(UpperHex.digit(10), 'A' as u8);
assert_eq!(UpperHex.digit(15), 'F' as u8);
assert_eq!(Radix { base: 36 }.digit(0), '0' as u8);
assert_eq!(Radix { base: 36 }.digit(15), 'f' as u8);
assert_eq!(Radix { base: 36 }.digit(35), 'z' as u8);
}
#[test]
#[should_fail]
fn test_hex_radix_digit_overflow() {
let _ = LowerHex.digit(16);
}
#[test]
fn test_format_int() {
// Formatting integers should select the right implementation based off
// the type of the argument. Also, hex/octal/binary should be defined
// for integers, but they shouldn't emit the negative sign.
assert_eq!(format!("{}", 1i), ~"1");
assert_eq!(format!("{}", 1i8), ~"1");
assert_eq!(format!("{}", 1i16), ~"1");
assert_eq!(format!("{}", 1i32), ~"1");
assert_eq!(format!("{}", 1i64), ~"1");
assert_eq!(format!("{:d}", -1i), ~"-1");
assert_eq!(format!("{:d}", -1i8), ~"-1");
assert_eq!(format!("{:d}", -1i16), ~"-1");
assert_eq!(format!("{:d}", -1i32), ~"-1");
assert_eq!(format!("{:d}", -1i64), ~"-1");
assert_eq!(format!("{:t}", 1i), ~"1");
assert_eq!(format!("{:t}", 1i8), ~"1");
assert_eq!(format!("{:t}", 1i16), ~"1");
assert_eq!(format!("{:t}", 1i32), ~"1");
assert_eq!(format!("{:t}", 1i64), ~"1");
assert_eq!(format!("{:x}", 1i), ~"1");
assert_eq!(format!("{:x}", 1i8), ~"1");
assert_eq!(format!("{:x}", 1i16), ~"1");
assert_eq!(format!("{:x}", 1i32), ~"1");
assert_eq!(format!("{:x}", 1i64), ~"1");
assert_eq!(format!("{:X}", 1i), ~"1");
assert_eq!(format!("{:X}", 1i8), ~"1");
assert_eq!(format!("{:X}", 1i16), ~"1");
assert_eq!(format!("{:X}", 1i32), ~"1");
assert_eq!(format!("{:X}", 1i64), ~"1");
assert_eq!(format!("{:o}", 1i), ~"1");
assert_eq!(format!("{:o}", 1i8), ~"1");
assert_eq!(format!("{:o}", 1i16), ~"1");
assert_eq!(format!("{:o}", 1i32), ~"1");
assert_eq!(format!("{:o}", 1i64), ~"1");
assert_eq!(format!("{}", 1u), ~"1");
assert_eq!(format!("{}", 1u8), ~"1");
assert_eq!(format!("{}", 1u16), ~"1");
assert_eq!(format!("{}", 1u32), ~"1");
assert_eq!(format!("{}", 1u64), ~"1");
assert_eq!(format!("{:u}", 1u), ~"1");
assert_eq!(format!("{:u}", 1u8), ~"1");
assert_eq!(format!("{:u}", 1u16), ~"1");
assert_eq!(format!("{:u}", 1u32), ~"1");
assert_eq!(format!("{:u}", 1u64), ~"1");
assert_eq!(format!("{:t}", 1u), ~"1");
assert_eq!(format!("{:t}", 1u8), ~"1");
assert_eq!(format!("{:t}", 1u16), ~"1");
assert_eq!(format!("{:t}", 1u32), ~"1");
assert_eq!(format!("{:t}", 1u64), ~"1");
assert_eq!(format!("{:x}", 1u), ~"1");
assert_eq!(format!("{:x}", 1u8), ~"1");
assert_eq!(format!("{:x}", 1u16), ~"1");
assert_eq!(format!("{:x}", 1u32), ~"1");
assert_eq!(format!("{:x}", 1u64), ~"1");
assert_eq!(format!("{:X}", 1u), ~"1");
assert_eq!(format!("{:X}", 1u8), ~"1");
assert_eq!(format!("{:X}", 1u16), ~"1");
assert_eq!(format!("{:X}", 1u32), ~"1");
assert_eq!(format!("{:X}", 1u64), ~"1");
assert_eq!(format!("{:o}", 1u), ~"1");
assert_eq!(format!("{:o}", 1u8), ~"1");
assert_eq!(format!("{:o}", 1u16), ~"1");
assert_eq!(format!("{:o}", 1u32), ~"1");
assert_eq!(format!("{:o}", 1u64), ~"1");
// Test a larger number
assert_eq!(format!("{:t}", 55), ~"110111");
assert_eq!(format!("{:o}", 55), ~"67");
assert_eq!(format!("{:d}", 55), ~"55");
assert_eq!(format!("{:x}", 55), ~"37");
assert_eq!(format!("{:X}", 55), ~"37");
}
#[test]
fn test_format_int_zero() {
assert_eq!(format!("{}", 0i), ~"0");
assert_eq!(format!("{:d}", 0i), ~"0");
assert_eq!(format!("{:t}", 0i), ~"0");
assert_eq!(format!("{:o}", 0i), ~"0");
assert_eq!(format!("{:x}", 0i), ~"0");
assert_eq!(format!("{:X}", 0i), ~"0");
assert_eq!(format!("{}", 0u), ~"0");
assert_eq!(format!("{:u}", 0u), ~"0");
assert_eq!(format!("{:t}", 0u), ~"0");
assert_eq!(format!("{:o}", 0u), ~"0");
assert_eq!(format!("{:x}", 0u), ~"0");
assert_eq!(format!("{:X}", 0u), ~"0");
}
#[test]
fn test_format_int_flags() {
assert_eq!(format!("{:3d}", 1), ~" 1");
assert_eq!(format!("{:>3d}", 1), ~" 1");
assert_eq!(format!("{:>+3d}", 1), ~" +1");
assert_eq!(format!("{:<3d}", 1), ~"1 ");
assert_eq!(format!("{:#d}", 1), ~"1");
assert_eq!(format!("{:#x}", 10), ~"0xa");
assert_eq!(format!("{:#X}", 10), ~"0xA");
assert_eq!(format!("{:#5x}", 10), ~" 0xa");
assert_eq!(format!("{:#o}", 10), ~"0o12");
assert_eq!(format!("{:08x}", 10), ~"0000000a");
assert_eq!(format!("{:8x}", 10), ~" a");
assert_eq!(format!("{:<8x}", 10), ~"a ");
assert_eq!(format!("{:>8x}", 10), ~" a");
assert_eq!(format!("{:#08x}", 10), ~"0x00000a");
assert_eq!(format!("{:08d}", -10), ~"-0000010");
assert_eq!(format!("{:x}", -1u8), ~"ff");
assert_eq!(format!("{:X}", -1u8), ~"FF");
assert_eq!(format!("{:t}", -1u8), ~"11111111");
assert_eq!(format!("{:o}", -1u8), ~"377");
assert_eq!(format!("{:#x}", -1u8), ~"0xff");
assert_eq!(format!("{:#X}", -1u8), ~"0xFF");
assert_eq!(format!("{:#t}", -1u8), ~"0b11111111");
assert_eq!(format!("{:#o}", -1u8), ~"0o377");
}
#[test]
fn test_format_int_sign_padding() {
assert_eq!(format!("{:+5d}", 1), ~" +1");
assert_eq!(format!("{:+5d}", -1), ~" -1");
assert_eq!(format!("{:05d}", 1), ~"00001");
assert_eq!(format!("{:05d}", -1), ~"-0001");
assert_eq!(format!("{:+05d}", 1), ~"+0001");
assert_eq!(format!("{:+05d}", -1), ~"-0001");
}
#[test]
fn test_format_int_twos_complement() {
use {i8, i16, i32, i64};
assert_eq!(format!("{}", i8::MIN), ~"-128");
assert_eq!(format!("{}", i16::MIN), ~"-32768");
assert_eq!(format!("{}", i32::MIN), ~"-2147483648");
assert_eq!(format!("{}", i64::MIN), ~"-9223372036854775808");
}
#[test]
fn test_format_radix() {
assert_eq!(format!("{:04}", radix(3, 2)), ~"0011");
assert_eq!(format!("{}", radix(55, 36)), ~"1j");
}
#[test]
#[should_fail]
fn test_radix_base_too_large() {
let _ = radix(55, 37);
}
}
#[cfg(test)]
mod bench {
extern crate test;
mod uint {
use super::test::BenchHarness;
use fmt::radix;
use rand::{XorShiftRng, Rng};
#[bench]
fn format_bin(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:t}", rng.gen::<uint>()); })
}
#[bench]
fn format_oct(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:o}", rng.gen::<uint>()); })
}
#[bench]
fn format_dec(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:u}", rng.gen::<uint>()); })
}
#[bench]
fn format_hex(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:x}", rng.gen::<uint>()); })
}
#[bench]
fn format_base_36(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{}", radix(rng.gen::<uint>(), 36)); })
}
}
mod int {
use super::test::BenchHarness;
use fmt::radix;
use rand::{XorShiftRng, Rng};
#[bench]
fn format_bin(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:t}", rng.gen::<int>()); })
}
#[bench]
fn format_oct(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:o}", rng.gen::<int>()); })
}
#[bench]
fn format_dec(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:d}", rng.gen::<int>()); })
}
#[bench]
fn format_hex(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{:x}", rng.gen::<int>()); })
}
#[bench]
fn format_base_36(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { format!("{}", radix(rng.gen::<int>(), 36)); })
}
}
}

View File

@ -29,7 +29,6 @@
use container::Container;
use io::{Writer, IoResult};
use iter::Iterator;
use num::ToStrRadix;
use option::{Some, None};
use result::Ok;
use str::OwnedStr;
@ -281,7 +280,7 @@ impl Streaming for SipState {
let r = self.result_bytes();
let mut s = ~"";
for b in r.iter() {
s.push_str((*b as uint).to_str_radix(16u));
s.push_str(format!("{:x}", *b));
}
s
}
@ -391,7 +390,7 @@ mod tests {
fn to_hex_str(r: &[u8, ..8]) -> ~str {
let mut s = ~"";
for b in r.iter() {
s.push_str((*b as uint).to_str_radix(16u));
s.push_str(format!("{:x}", *b));
}
s
}

View File

@ -857,12 +857,12 @@ pub trait Writer {
/// Write the result of passing n through `int::to_str_bytes`.
fn write_int(&mut self, n: int) -> IoResult<()> {
int::to_str_bytes(n, 10u, |bytes| self.write(bytes))
write!(self, "{:d}", n)
}
/// Write the result of passing n through `uint::to_str_bytes`.
fn write_uint(&mut self, n: uint) -> IoResult<()> {
uint::to_str_bytes(n, 10u, |bytes| self.write(bytes))
write!(self, "{:u}", n)
}
/// Write a little-endian uint (number of bytes depends on system).

View File

@ -277,7 +277,7 @@ impl ToStr for $T {
/// Convert to a string in base 10.
#[inline]
fn to_str(&self) -> ~str {
self.to_str_radix(10)
format!("{:d}", *self)
}
}

View File

@ -17,10 +17,12 @@
use clone::{Clone, DeepClone};
use cmp::{Eq, Ord};
use kinds::Pod;
use mem::size_of;
use ops::{Add, Sub, Mul, Div, Rem, Neg};
use ops::{Not, BitAnd, BitOr, BitXor, Shl, Shr};
use option::{Option, Some, None};
use fmt::{Show, Binary, Octal, LowerHex, UpperHex};
pub mod strconv;
@ -243,7 +245,8 @@ pub trait Bitwise: Bounded
/// Specifies the available operations common to all of Rust's core numeric primitives.
/// These may not always make sense from a purely mathematical point of view, but
/// may be useful for systems programming.
pub trait Primitive: Clone
pub trait Primitive: Pod
+ Clone
+ DeepClone
+ Num
+ NumCast
@ -256,7 +259,12 @@ pub trait Int: Primitive
+ CheckedAdd
+ CheckedSub
+ CheckedMul
+ CheckedDiv {}
+ CheckedDiv
+ Show
+ Binary
+ Octal
+ LowerHex
+ UpperHex {}
/// Returns the smallest power of 2 greater than or equal to `n`.
#[inline]

View File

@ -792,24 +792,88 @@ mod test {
#[cfg(test)]
mod bench {
extern crate test;
use self::test::BenchHarness;
use rand::{XorShiftRng, Rng};
use to_str::ToStr;
use f64;
#[bench]
fn uint_to_str_rand(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| {
rng.gen::<uint>().to_str();
})
mod uint {
use super::test::BenchHarness;
use rand::{XorShiftRng, Rng};
use num::ToStrRadix;
#[bench]
fn to_str_bin(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<uint>().to_str_radix(2); })
}
#[bench]
fn to_str_oct(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<uint>().to_str_radix(8); })
}
#[bench]
fn to_str_dec(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<uint>().to_str_radix(10); })
}
#[bench]
fn to_str_hex(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<uint>().to_str_radix(16); })
}
#[bench]
fn to_str_base_36(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<uint>().to_str_radix(36); })
}
}
#[bench]
fn float_to_str_rand(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| {
f64::to_str(rng.gen());
})
mod int {
use super::test::BenchHarness;
use rand::{XorShiftRng, Rng};
use num::ToStrRadix;
#[bench]
fn to_str_bin(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<int>().to_str_radix(2); })
}
#[bench]
fn to_str_oct(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<int>().to_str_radix(8); })
}
#[bench]
fn to_str_dec(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<int>().to_str_radix(10); })
}
#[bench]
fn to_str_hex(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<int>().to_str_radix(16); })
}
#[bench]
fn to_str_base_36(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { rng.gen::<int>().to_str_radix(36); })
}
}
mod f64 {
use super::test::BenchHarness;
use rand::{XorShiftRng, Rng};
use f64;
#[bench]
fn float_to_str(bh: &mut BenchHarness) {
let mut rng = XorShiftRng::new();
bh.iter(|| { f64::to_str(rng.gen()); })
}
}
}

View File

@ -191,7 +191,7 @@ impl ToStr for $T {
/// Convert to a string in base 10.
#[inline]
fn to_str(&self) -> ~str {
self.to_str_radix(10u)
format!("{:u}", *self)
}
}

View File

@ -60,19 +60,13 @@ impl Repr for bool {
impl Repr for int {
fn write_repr(&self, writer: &mut io::Writer) -> io::IoResult<()> {
::int::to_str_bytes(*self, 10u, |bits| {
writer.write(bits)
})
write!(writer, "{}", *self)
}
}
macro_rules! int_repr(($ty:ident, $suffix:expr) => (impl Repr for $ty {
fn write_repr(&self, writer: &mut io::Writer) -> io::IoResult<()> {
::$ty::to_str_bytes(*self, 10u, |bits| {
writer.write(bits).and_then(|()| {
writer.write(bytes!($suffix))
})
})
write!(writer, "{}{}", *self, $suffix)
}
}))

View File

@ -2267,29 +2267,14 @@ pub fn print_literal(s: &mut State, lit: &ast::Lit) -> io::IoResult<()> {
word(&mut s.s, res)
}
ast::LitInt(i, t) => {
if i < 0_i64 {
word(&mut s.s,
~"-" + (-i as u64).to_str_radix(10u)
+ ast_util::int_ty_to_str(t))
} else {
word(&mut s.s,
(i as u64).to_str_radix(10u)
+ ast_util::int_ty_to_str(t))
}
word(&mut s.s, format!("{}{}", i, ast_util::int_ty_to_str(t)))
}
ast::LitUint(u, t) => {
word(&mut s.s,
u.to_str_radix(10u)
+ ast_util::uint_ty_to_str(t))
word(&mut s.s, format!("{}{}", u, ast_util::uint_ty_to_str(t)))
}
ast::LitIntUnsuffixed(i) => {
if i < 0_i64 {
word(&mut s.s, ~"-" + (-i as u64).to_str_radix(10u))
} else {
word(&mut s.s, (i as u64).to_str_radix(10u))
}
word(&mut s.s, format!("{}", i))
}
ast::LitFloat(ref f, t) => {
word(&mut s.s, f.get() + ast_util::float_ty_to_str(t))
}

View File

@ -12,7 +12,6 @@
use std::{char, vec};
use std::mem::replace;
use std::num::strconv::{SignNone,SignNeg,SignAll,int_to_str_bytes_common};
#[deriving(Eq)]
enum States {
@ -480,68 +479,49 @@ impl FormatOp {
fn format(val: Param, op: FormatOp, flags: Flags) -> Result<~[u8],~str> {
let mut s = match val {
Number(d) => {
match op {
FormatString => {
return Err(~"non-number on stack with %s")
}
_ => {
let radix = match op {
FormatDigit => 10,
FormatOctal => 8,
FormatHex|FormatHEX => 16,
FormatString => unreachable!()
};
let mut s = ~[];
match op {
FormatDigit => {
let sign = if flags.sign { SignAll } else { SignNeg };
int_to_str_bytes_common(d, radix, sign, |c| {
s.push(c);
})
}
_ => {
int_to_str_bytes_common(d as uint, radix, SignNone, |c| {
s.push(c);
})
}
};
if flags.precision > s.len() {
let mut s_ = vec::with_capacity(flags.precision);
let n = flags.precision - s.len();
s_.grow(n, &('0' as u8));
s_.push_all_move(s);
s = s_;
}
assert!(!s.is_empty(), "string conversion produced empty result");
match op {
FormatDigit => {
if flags.space && !(s[0] == '-' as u8 || s[0] == '+' as u8) {
s.unshift(' ' as u8);
}
}
FormatOctal => {
if flags.alternate && s[0] != '0' as u8 {
s.unshift('0' as u8);
}
}
FormatHex => {
if flags.alternate {
let s_ = replace(&mut s, ~['0' as u8, 'x' as u8]);
s.push_all_move(s_);
}
}
FormatHEX => {
s = s.into_ascii().to_upper().into_bytes();
if flags.alternate {
let s_ = replace(&mut s, ~['0' as u8, 'X' as u8]);
s.push_all_move(s_);
}
}
FormatString => unreachable!()
}
s
}
let mut s = match (op, flags.sign) {
(FormatDigit, true) => format!("{:+d}", d).into_bytes(),
(FormatDigit, false) => format!("{:d}", d).into_bytes(),
(FormatOctal, _) => format!("{:o}", d).into_bytes(),
(FormatHex, _) => format!("{:x}", d).into_bytes(),
(FormatHEX, _) => format!("{:X}", d).into_bytes(),
(FormatString, _) => return Err(~"non-number on stack with %s"),
};
if flags.precision > s.len() {
let mut s_ = vec::with_capacity(flags.precision);
let n = flags.precision - s.len();
s_.grow(n, &('0' as u8));
s_.push_all_move(s);
s = s_;
}
assert!(!s.is_empty(), "string conversion produced empty result");
match op {
FormatDigit => {
if flags.space && !(s[0] == '-' as u8 || s[0] == '+' as u8) {
s.unshift(' ' as u8);
}
}
FormatOctal => {
if flags.alternate && s[0] != '0' as u8 {
s.unshift('0' as u8);
}
}
FormatHex => {
if flags.alternate {
let s_ = replace(&mut s, ~['0' as u8, 'x' as u8]);
s.push_all_move(s_);
}
}
FormatHEX => {
s = s.into_ascii().to_upper().into_bytes();
if flags.alternate {
let s_ = replace(&mut s, ~['0' as u8, 'X' as u8]);
s.push_all_move(s_);
}
}
FormatString => unreachable!()
}
s
}
String(s) => {
match op {

View File

@ -36,7 +36,6 @@ impl fmt::Signed for B {
macro_rules! t(($a:expr, $b:expr) => { assert_eq!($a, $b.to_owned()) })
pub fn main() {
// Make sure there's a poly formatter that takes anything
t!(format!("{:?}", 1), "1");
t!(format!("{:?}", A), "A");
@ -49,16 +48,6 @@ pub fn main() {
t!(format!("hello \\{"), "hello {");
// default formatters should work
t!(format!("{}", 1i), "1");
t!(format!("{}", 1i8), "1");
t!(format!("{}", 1i16), "1");
t!(format!("{}", 1i32), "1");
t!(format!("{}", 1i64), "1");
t!(format!("{}", 1u), "1");
t!(format!("{}", 1u8), "1");
t!(format!("{}", 1u16), "1");
t!(format!("{}", 1u32), "1");
t!(format!("{}", 1u64), "1");
t!(format!("{}", 1.0f32), "1");
t!(format!("{}", 1.0f64), "1");
t!(format!("{}", "a"), "a");
@ -126,94 +115,6 @@ pub fn main() {
t!(format!("{:-#s}", "a"), "a");
t!(format!("{:+#s}", "a"), "a");
// Formatting integers should select the right implementation based off the
// type of the argument. Also, hex/octal/binary should be defined for
// integers, but they shouldn't emit the negative sign.
t!(format!("{:d}", -1i), "-1");
t!(format!("{:d}", -1i8), "-1");
t!(format!("{:d}", -1i16), "-1");
t!(format!("{:d}", -1i32), "-1");
t!(format!("{:d}", -1i64), "-1");
t!(format!("{:t}", 1i), "1");
t!(format!("{:t}", 1i8), "1");
t!(format!("{:t}", 1i16), "1");
t!(format!("{:t}", 1i32), "1");
t!(format!("{:t}", 1i64), "1");
t!(format!("{:x}", 1i), "1");
t!(format!("{:x}", 1i8), "1");
t!(format!("{:x}", 1i16), "1");
t!(format!("{:x}", 1i32), "1");
t!(format!("{:x}", 1i64), "1");
t!(format!("{:X}", 1i), "1");
t!(format!("{:X}", 1i8), "1");
t!(format!("{:X}", 1i16), "1");
t!(format!("{:X}", 1i32), "1");
t!(format!("{:X}", 1i64), "1");
t!(format!("{:o}", 1i), "1");
t!(format!("{:o}", 1i8), "1");
t!(format!("{:o}", 1i16), "1");
t!(format!("{:o}", 1i32), "1");
t!(format!("{:o}", 1i64), "1");
t!(format!("{:u}", 1u), "1");
t!(format!("{:u}", 1u8), "1");
t!(format!("{:u}", 1u16), "1");
t!(format!("{:u}", 1u32), "1");
t!(format!("{:u}", 1u64), "1");
t!(format!("{:t}", 1u), "1");
t!(format!("{:t}", 1u8), "1");
t!(format!("{:t}", 1u16), "1");
t!(format!("{:t}", 1u32), "1");
t!(format!("{:t}", 1u64), "1");
t!(format!("{:x}", 1u), "1");
t!(format!("{:x}", 1u8), "1");
t!(format!("{:x}", 1u16), "1");
t!(format!("{:x}", 1u32), "1");
t!(format!("{:x}", 1u64), "1");
t!(format!("{:X}", 1u), "1");
t!(format!("{:X}", 1u8), "1");
t!(format!("{:X}", 1u16), "1");
t!(format!("{:X}", 1u32), "1");
t!(format!("{:X}", 1u64), "1");
t!(format!("{:o}", 1u), "1");
t!(format!("{:o}", 1u8), "1");
t!(format!("{:o}", 1u16), "1");
t!(format!("{:o}", 1u32), "1");
t!(format!("{:o}", 1u64), "1");
// Test the flags for formatting integers
t!(format!("{:3d}", 1), " 1");
t!(format!("{:>3d}", 1), " 1");
t!(format!("{:>+3d}", 1), " +1");
t!(format!("{:<3d}", 1), "1 ");
t!(format!("{:#d}", 1), "1");
t!(format!("{:#x}", 10), "0xa");
t!(format!("{:#X}", 10), "0xA");
t!(format!("{:#5x}", 10), " 0xa");
t!(format!("{:#o}", 10), "0o12");
t!(format!("{:08x}", 10), "0000000a");
t!(format!("{:8x}", 10), " a");
t!(format!("{:<8x}", 10), "a ");
t!(format!("{:>8x}", 10), " a");
t!(format!("{:#08x}", 10), "0x00000a");
t!(format!("{:08d}", -10), "-0000010");
t!(format!("{:x}", -1u8), "ff");
t!(format!("{:X}", -1u8), "FF");
t!(format!("{:t}", -1u8), "11111111");
t!(format!("{:o}", -1u8), "377");
t!(format!("{:#x}", -1u8), "0xff");
t!(format!("{:#X}", -1u8), "0xFF");
t!(format!("{:#t}", -1u8), "0b11111111");
t!(format!("{:#o}", -1u8), "0o377");
// Signed combinations
t!(format!("{:+5d}", 1), " +1");
t!(format!("{:+5d}", -1), " -1");
t!(format!("{:05d}", 1), "00001");
t!(format!("{:05d}", -1), "-0001");
t!(format!("{:+05d}", 1), "+0001");
t!(format!("{:+05d}", -1), "-0001");
// Some float stuff
t!(format!("{:f}", 1.0f32), "1");
t!(format!("{:f}", 1.0f64), "1");