Skip to content

Commit d2f73ab

Browse files
committed
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) ~~~
2 parents 87c7e15 + 6943acd commit d2f73ab

File tree

15 files changed

+667
-336
lines changed

15 files changed

+667
-336
lines changed

src/doc/complement-cheatsheet.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ let y: int = x.unwrap();
2222

2323
**Int to string, in non-base-10**
2424

25-
Use [`ToStrRadix`](http://static.rust-lang.org/doc/master/std/num/trait.ToStrRadix.html).
25+
Use the `format!` syntax extension.
2626

2727
~~~
28-
use std::num::ToStrRadix;
29-
3028
let x: int = 42;
31-
let y: ~str = x.to_str_radix(16);
29+
let y: ~str = format!("{:t}", x); // binary
30+
let y: ~str = format!("{:o}", x); // octal
31+
let y: ~str = format!("{:x}", x); // lowercase hexadecimal
32+
let y: ~str = format!("{:X}", x); // uppercase hexidecimal
3233
~~~
3334

3435
**String to int, in non-base-10**

src/librustc/middle/ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2024,7 +2024,7 @@ impl ops::Sub<TypeContents,TypeContents> for TypeContents {
20242024

20252025
impl ToStr for TypeContents {
20262026
fn to_str(&self) -> ~str {
2027-
format!("TypeContents({})", self.bits.to_str_radix(2))
2027+
format!("TypeContents({:t})", self.bits)
20282028
}
20292029
}
20302030

src/librustc/middle/typeck/infer/to_str.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ impl<V:Vid + ToStr,T:InferStr> InferStr for VarValue<V, T> {
7070
fn inf_str(&self, cx: &InferCtxt) -> ~str {
7171
match *self {
7272
Redirect(ref vid) => format!("Redirect({})", vid.to_str()),
73-
Root(ref pt, rk) => format!("Root({}, {})", pt.inf_str(cx),
74-
rk.to_str_radix(10u))
73+
Root(ref pt, rk) => format!("Root({}, {})", pt.inf_str(cx), rk)
7574
}
7675
}
7776
}

src/libstd/fmt/mod.rs

Lines changed: 54 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl fmt::Binary for Vector2D {
215215
// for details, and the function `pad` can be used to pad strings.
216216
let decimals = f.precision.unwrap_or(3);
217217
let string = f64::to_str_exact(magnitude, decimals);
218-
f.pad_integral(string.as_bytes(), "", true)
218+
f.pad_integral(true, "", string.as_bytes())
219219
}
220220
}
221221
@@ -493,6 +493,11 @@ use str;
493493
use vec::ImmutableVector;
494494
use vec;
495495

496+
pub use self::num::radix;
497+
pub use self::num::Radix;
498+
pub use self::num::RadixFmt;
499+
500+
mod num;
496501
pub mod parse;
497502
pub mod rt;
498503

@@ -891,58 +896,60 @@ impl<'a> Formatter<'a> {
891896
///
892897
/// # Arguments
893898
///
894-
/// * s - the byte array that the number has been formatted into
895-
/// * alternate_prefix - if the '#' character (FlagAlternate) is
896-
/// provided, this is the prefix to put in front of the number.
897-
/// Currently this is 0x/0o/0b/etc.
898-
/// * positive - whether the original integer was positive or not.
899+
/// * is_positive - whether the original integer was positive or not.
900+
/// * prefix - if the '#' character (FlagAlternate) is provided, this
901+
/// is the prefix to put in front of the number.
902+
/// * buf - the byte array that the number has been formatted into
899903
///
900904
/// This function will correctly account for the flags provided as well as
901905
/// the minimum width. It will not take precision into account.
902-
pub fn pad_integral(&mut self, s: &[u8], alternate_prefix: &str,
903-
positive: bool) -> Result {
906+
pub fn pad_integral(&mut self, is_positive: bool, prefix: &str, buf: &[u8]) -> Result {
904907
use fmt::parse::{FlagAlternate, FlagSignPlus, FlagSignAwareZeroPad};
905908

906-
let mut actual_len = s.len();
907-
if self.flags & 1 << (FlagAlternate as uint) != 0 {
908-
actual_len += alternate_prefix.len();
909-
}
910-
if self.flags & 1 << (FlagSignPlus as uint) != 0 {
911-
actual_len += 1;
912-
} else if !positive {
913-
actual_len += 1;
909+
let mut width = buf.len();
910+
911+
let mut sign = None;
912+
if !is_positive {
913+
sign = Some('-'); width += 1;
914+
} else if self.flags & (1 << (FlagSignPlus as uint)) != 0 {
915+
sign = Some('+'); width += 1;
914916
}
915917

916-
let mut signprinted = false;
917-
let sign = |this: &mut Formatter| {
918-
if !signprinted {
919-
if this.flags & 1 << (FlagSignPlus as uint) != 0 && positive {
920-
try!(this.buf.write(['+' as u8]));
921-
} else if !positive {
922-
try!(this.buf.write(['-' as u8]));
923-
}
924-
if this.flags & 1 << (FlagAlternate as uint) != 0 {
925-
try!(this.buf.write(alternate_prefix.as_bytes()));
926-
}
927-
signprinted = true;
928-
}
929-
Ok(())
930-
};
918+
let mut prefixed = false;
919+
if self.flags & (1 << (FlagAlternate as uint)) != 0 {
920+
prefixed = true; width += prefix.len();
921+
}
931922

932-
let emit = |this: &mut Formatter| {
933-
sign(this).and_then(|()| this.buf.write(s))
923+
// Writes the sign if it exists, and then the prefix if it was requested
924+
let write_prefix = |f: &mut Formatter| {
925+
for c in sign.move_iter() { try!(f.buf.write_char(c)); }
926+
if prefixed { f.buf.write_str(prefix) }
927+
else { Ok(()) }
934928
};
935929

930+
// The `width` field is more of a `min-width` parameter at this point.
936931
match self.width {
937-
None => emit(self),
938-
Some(min) if actual_len >= min => emit(self),
932+
// If there's no minimum length requirements then we can just
933+
// write the bytes.
934+
None => {
935+
try!(write_prefix(self)); self.buf.write(buf)
936+
}
937+
// Check if we're over the minimum width, if so then we can also
938+
// just write the bytes.
939+
Some(min) if width >= min => {
940+
try!(write_prefix(self)); self.buf.write(buf)
941+
}
942+
// The sign and prefix goes before the padding if the fill character
943+
// is zero
944+
Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => {
945+
self.fill = '0';
946+
try!(write_prefix(self));
947+
self.with_padding(min - width, parse::AlignRight, |f| f.buf.write(buf))
948+
}
949+
// Otherwise, the sign and prefix goes after the padding
939950
Some(min) => {
940-
if self.flags & 1 << (FlagSignAwareZeroPad as uint) != 0 {
941-
self.fill = '0';
942-
try!(sign(self));
943-
}
944-
self.with_padding(min - actual_len, parse::AlignRight, |me| {
945-
emit(me)
951+
self.with_padding(min - width, parse::AlignRight, |f| {
952+
try!(write_prefix(f)); f.buf.write(buf)
946953
})
947954
}
948955
}
@@ -979,19 +986,16 @@ impl<'a> Formatter<'a> {
979986
}
980987
None => {}
981988
}
982-
983989
// The `width` field is more of a `min-width` parameter at this point.
984990
match self.width {
985991
// If we're under the maximum length, and there's no minimum length
986992
// requirements, then we can just emit the string
987993
None => self.buf.write(s.as_bytes()),
988-
989994
// If we're under the maximum width, check if we're over the minimum
990995
// width, if so it's as easy as just emitting the string.
991996
Some(width) if s.char_len() >= width => {
992997
self.buf.write(s.as_bytes())
993998
}
994-
995999
// If we're under both the maximum and the minimum width, then fill
9961000
// up the minimum width with the specified string + some alignment.
9971001
Some(width) => {
@@ -1002,6 +1006,8 @@ impl<'a> Formatter<'a> {
10021006
}
10031007
}
10041008

1009+
/// Runs a callback, emitting the correct padding either before or
1010+
/// afterwards depending on whether right or left alingment is requested.
10051011
fn with_padding(&mut self,
10061012
padding: uint,
10071013
default: parse::Alignment,
@@ -1075,67 +1081,6 @@ impl Char for char {
10751081
}
10761082
}
10771083

1078-
macro_rules! int_base(($ty:ident, $into:ident, $base:expr,
1079-
$name:ident, $prefix:expr) => {
1080-
impl $name for $ty {
1081-
fn fmt(&self, f: &mut Formatter) -> Result {
1082-
::$into::to_str_bytes(*self as $into, $base, |buf| {
1083-
f.pad_integral(buf, $prefix, true)
1084-
})
1085-
}
1086-
}
1087-
})
1088-
macro_rules! upper_hex(($ty:ident, $into:ident) => {
1089-
impl UpperHex for $ty {
1090-
fn fmt(&self, f: &mut Formatter) -> Result {
1091-
::$into::to_str_bytes(*self as $into, 16, |buf| {
1092-
upperhex(buf, f)
1093-
})
1094-
}
1095-
}
1096-
})
1097-
// Not sure why, but this causes an "unresolved enum variant, struct or const"
1098-
// when inlined into the above macro...
1099-
#[doc(hidden)]
1100-
pub fn upperhex(buf: &[u8], f: &mut Formatter) -> Result {
1101-
let mut local = [0u8, ..16];
1102-
for i in ::iter::range(0, buf.len()) {
1103-
local[i] = match buf[i] as char {
1104-
'a' .. 'f' => (buf[i] - 'a' as u8) + 'A' as u8,
1105-
c => c as u8,
1106-
}
1107-
}
1108-
f.pad_integral(local.slice_to(buf.len()), "0x", true)
1109-
}
1110-
1111-
macro_rules! integer(($signed:ident, $unsigned:ident) => {
1112-
// Signed is special because it actuall emits the negative sign,
1113-
// nothing else should do that, however.
1114-
impl Signed for $signed {
1115-
fn fmt(&self, f: &mut Formatter) -> Result {
1116-
::$unsigned::to_str_bytes(self.abs() as $unsigned, 10, |buf| {
1117-
f.pad_integral(buf, "", *self >= 0)
1118-
})
1119-
}
1120-
}
1121-
int_base!($signed, $unsigned, 2, Binary, "0b")
1122-
int_base!($signed, $unsigned, 8, Octal, "0o")
1123-
int_base!($signed, $unsigned, 16, LowerHex, "0x")
1124-
upper_hex!($signed, $unsigned)
1125-
1126-
int_base!($unsigned, $unsigned, 2, Binary, "0b")
1127-
int_base!($unsigned, $unsigned, 8, Octal, "0o")
1128-
int_base!($unsigned, $unsigned, 10, Unsigned, "")
1129-
int_base!($unsigned, $unsigned, 16, LowerHex, "0x")
1130-
upper_hex!($unsigned, $unsigned)
1131-
})
1132-
1133-
integer!(int, uint)
1134-
integer!(i8, u8)
1135-
integer!(i16, u16)
1136-
integer!(i32, u32)
1137-
integer!(i64, u64)
1138-
11391084
macro_rules! floating(($ty:ident) => {
11401085
impl Float for $ty {
11411086
fn fmt(&self, fmt: &mut Formatter) -> Result {
@@ -1144,7 +1089,7 @@ macro_rules! floating(($ty:ident) => {
11441089
Some(i) => ::$ty::to_str_exact(self.abs(), i),
11451090
None => ::$ty::to_str_digits(self.abs(), 6)
11461091
};
1147-
fmt.pad_integral(s.as_bytes(), "", *self >= 0.0)
1092+
fmt.pad_integral(*self >= 0.0, "", s.as_bytes())
11481093
}
11491094
}
11501095

@@ -1155,7 +1100,7 @@ macro_rules! floating(($ty:ident) => {
11551100
Some(i) => ::$ty::to_str_exp_exact(self.abs(), i, false),
11561101
None => ::$ty::to_str_exp_digits(self.abs(), 6, false)
11571102
};
1158-
fmt.pad_integral(s.as_bytes(), "", *self >= 0.0)
1103+
fmt.pad_integral(*self >= 0.0, "", s.as_bytes())
11591104
}
11601105
}
11611106

@@ -1166,7 +1111,7 @@ macro_rules! floating(($ty:ident) => {
11661111
Some(i) => ::$ty::to_str_exp_exact(self.abs(), i, true),
11671112
None => ::$ty::to_str_exp_digits(self.abs(), 6, true)
11681113
};
1169-
fmt.pad_integral(s.as_bytes(), "", *self >= 0.0)
1114+
fmt.pad_integral(*self >= 0.0, "", s.as_bytes())
11701115
}
11711116
}
11721117
})
@@ -1193,9 +1138,7 @@ impl<T> Poly for T {
11931138
impl<T> Pointer for *T {
11941139
fn fmt(&self, f: &mut Formatter) -> Result {
11951140
f.flags |= 1 << (parse::FlagAlternate as uint);
1196-
::uint::to_str_bytes(*self as uint, 16, |buf| {
1197-
f.pad_integral(buf, "0x", true)
1198-
})
1141+
secret_lower_hex::<uint>(&(*self as uint), f)
11991142
}
12001143
}
12011144
impl<T> Pointer for *mut T {
@@ -1223,16 +1166,6 @@ macro_rules! delegate(($ty:ty to $other:ident) => {
12231166
}
12241167
}
12251168
})
1226-
delegate!(int to signed)
1227-
delegate!( i8 to signed)
1228-
delegate!(i16 to signed)
1229-
delegate!(i32 to signed)
1230-
delegate!(i64 to signed)
1231-
delegate!(uint to unsigned)
1232-
delegate!( u8 to unsigned)
1233-
delegate!( u16 to unsigned)
1234-
delegate!( u32 to unsigned)
1235-
delegate!( u64 to unsigned)
12361169
delegate!(~str to string)
12371170
delegate!(&'a str to string)
12381171
delegate!(bool to bool)

0 commit comments

Comments
 (0)