Skip to content

Commit ebf9e1a

Browse files
committed
optimize integer formatting
1 parent a27fed7 commit ebf9e1a

File tree

1 file changed

+86
-2
lines changed

1 file changed

+86
-2
lines changed

src/libcore/fmt/num.rs

+86-2
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,25 @@ use fmt;
2020
use num::Zero;
2121
use ops::{Div, Rem, Sub};
2222
use str;
23+
use slice;
24+
use ptr;
25+
use mem;
2326

2427
#[doc(hidden)]
2528
trait Int: Zero + PartialEq + PartialOrd + Div<Output=Self> + Rem<Output=Self> +
2629
Sub<Output=Self> + Copy {
2730
fn from_u8(u: u8) -> Self;
2831
fn to_u8(&self) -> u8;
32+
fn to_u32(&self) -> u32;
33+
fn to_u64(&self) -> u64;
2934
}
3035

3136
macro_rules! doit {
3237
($($t:ident)*) => ($(impl Int for $t {
3338
fn from_u8(u: u8) -> $t { u as $t }
3439
fn to_u8(&self) -> u8 { *self as u8 }
40+
fn to_u32(&self) -> u32 { *self as u32 }
41+
fn to_u64(&self) -> u64 { *self as u64 }
3542
})*)
3643
}
3744
doit! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize }
@@ -188,6 +195,7 @@ macro_rules! radix_fmt {
188195
}
189196
}
190197
}
198+
191199
macro_rules! int_base {
192200
($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => {
193201
#[stable(feature = "rust1", since = "1.0.0")]
@@ -209,17 +217,16 @@ macro_rules! debug {
209217
}
210218
}
211219
}
220+
212221
macro_rules! integer {
213222
($Int:ident, $Uint:ident) => {
214-
int_base! { Display for $Int as $Int -> Decimal }
215223
int_base! { Binary for $Int as $Uint -> Binary }
216224
int_base! { Octal for $Int as $Uint -> Octal }
217225
int_base! { LowerHex for $Int as $Uint -> LowerHex }
218226
int_base! { UpperHex for $Int as $Uint -> UpperHex }
219227
radix_fmt! { $Int as $Int, fmt_int }
220228
debug! { $Int }
221229

222-
int_base! { Display for $Uint as $Uint -> Decimal }
223230
int_base! { Binary for $Uint as $Uint -> Binary }
224231
int_base! { Octal for $Uint as $Uint -> Octal }
225232
int_base! { LowerHex for $Uint as $Uint -> LowerHex }
@@ -233,3 +240,80 @@ integer! { i8, u8 }
233240
integer! { i16, u16 }
234241
integer! { i32, u32 }
235242
integer! { i64, u64 }
243+
244+
const DEC_DIGITS_LUT: &'static[u8] =
245+
b"0001020304050607080910111213141516171819\
246+
2021222324252627282930313233343536373839\
247+
4041424344454647484950515253545556575859\
248+
6061626364656667686970717273747576777879\
249+
8081828384858687888990919293949596979899";
250+
251+
macro_rules! impl_Display {
252+
($($t:ident),*: $conv_fn:ident) => ($(
253+
impl fmt::Display for $t {
254+
#[allow(unused_comparisons)]
255+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256+
let is_positive = *self >= 0;
257+
let mut n = if is_positive {
258+
self.$conv_fn()
259+
} else {
260+
// convert the negative num to positive by summing 1 to it's 2 complement
261+
(!self.$conv_fn()).wrapping_add(1)
262+
};
263+
let mut buf: [u8; 20] = unsafe { mem::uninitialized() };
264+
let mut curr = buf.len() as isize;
265+
let buf_ptr = buf.as_mut_ptr();
266+
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
267+
268+
unsafe {
269+
// eagerly decode 4 characters at a time
270+
if <$t>::max_value() as u64 >= 10000 {
271+
while n >= 10000 {
272+
let rem = (n % 10000) as isize;
273+
n /= 10000;
274+
275+
let d1 = (rem / 100) << 1;
276+
let d2 = (rem % 100) << 1;
277+
curr -= 4;
278+
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
279+
ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
280+
}
281+
}
282+
283+
// if we reach here numbers are <= 9999, so at most 4 chars long
284+
let mut n = n as isize; // possibly reduce 64bit math
285+
286+
// decode 2 more chars, if > 2 chars
287+
if n >= 100 {
288+
let d1 = (n % 100) << 1;
289+
n /= 100;
290+
curr -= 2;
291+
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
292+
}
293+
294+
// decode last 1 or 2 chars
295+
if n < 10 {
296+
curr -= 1;
297+
*buf_ptr.offset(curr) = (n as u8) + 48;
298+
} else {
299+
let d1 = n << 1;
300+
curr -= 2;
301+
ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
302+
}
303+
}
304+
305+
let buf_slice = unsafe {
306+
str::from_utf8_unchecked(
307+
slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize))
308+
};
309+
f.pad_integral(is_positive, "", buf_slice)
310+
}
311+
})*);
312+
}
313+
314+
impl_Display!(i8, u8, i16, u16, i32, u32: to_u32);
315+
impl_Display!(i64, u64: to_u64);
316+
#[cfg(target_pointer_width = "32")]
317+
impl_Display!(isize, usize: to_u32);
318+
#[cfg(target_pointer_width = "64")]
319+
impl_Display!(isize, usize: to_u64);

0 commit comments

Comments
 (0)