diff --git a/src/libstd/fmt/mod.rs b/src/libstd/fmt/mod.rs index 9c70d34cc0f44..38fae798d5dde 100644 --- a/src/libstd/fmt/mod.rs +++ b/src/libstd/fmt/mod.rs @@ -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 mut prefixed = false; + if self.flags & (1 << (FlagAlternate as uint)) != 0 { + prefixed = true; width += prefix.len(); + } - let emit = |this: &mut Formatter| { - sign(this).and_then(|()| this.buf.write(s)) + // 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 Poly for T { impl 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::(&(*self as uint), f) } } impl 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) diff --git a/src/libstd/fmt/num.rs b/src/libstd/fmt/num.rs new file mode 100644 index 0000000000000..681d0678ed495 --- /dev/null +++ b/src/libstd/fmt/num.rs @@ -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 or the MIT license +// , 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(&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); + +/// 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(x: T, base: u8) -> RadixFmt { + 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::()); }) + } + + #[bench] + fn format_oct(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{:o}", rng.gen::()); }) + } + + #[bench] + fn format_dec(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{:u}", rng.gen::()); }) + } + + #[bench] + fn format_hex(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{:x}", rng.gen::()); }) + } + + #[bench] + fn format_base_36(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{}", radix(rng.gen::(), 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::()); }) + } + + #[bench] + fn format_oct(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{:o}", rng.gen::()); }) + } + + #[bench] + fn format_dec(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{:d}", rng.gen::()); }) + } + + #[bench] + fn format_hex(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{:x}", rng.gen::()); }) + } + + #[bench] + fn format_base_36(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { format!("{}", radix(rng.gen::(), 36)); }) + } + } +} diff --git a/src/libstd/num/mod.rs b/src/libstd/num/mod.rs index 767faa30f24b6..150c7bdab29b8 100644 --- a/src/libstd/num/mod.rs +++ b/src/libstd/num/mod.rs @@ -22,6 +22,7 @@ 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; @@ -278,7 +279,12 @@ pub trait Int: Integer + CheckedAdd + CheckedSub + CheckedMul - + CheckedDiv {} + + CheckedDiv + + Show + + Binary + + Octal + + LowerHex + + UpperHex {} /// Returns the smallest power of 2 greater than or equal to `n`. #[inline] diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 6be829f51d73c..c0c3074be634d 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -804,24 +804,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::().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::().to_str_radix(2); }) + } + + #[bench] + fn to_str_oct(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().to_str_radix(8); }) + } + + #[bench] + fn to_str_dec(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().to_str_radix(10); }) + } + + #[bench] + fn to_str_hex(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().to_str_radix(16); }) + } + + #[bench] + fn to_str_base_36(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().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::().to_str_radix(2); }) + } + + #[bench] + fn to_str_oct(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().to_str_radix(8); }) + } + + #[bench] + fn to_str_dec(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().to_str_radix(10); }) + } + + #[bench] + fn to_str_hex(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().to_str_radix(16); }) + } + + #[bench] + fn to_str_base_36(bh: &mut BenchHarness) { + let mut rng = XorShiftRng::new(); + bh.iter(|| { rng.gen::().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()); }) + } } } diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 09188b77ad8ae..ca798a77a4fd1 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -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");