Skip to content

Commit c4be3da

Browse files
Small optimization for integers Display implementation
1 parent 098ada1 commit c4be3da

File tree

1 file changed

+92
-46
lines changed

1 file changed

+92
-46
lines changed

core/src/fmt/num.rs

+92-46
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,46 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
208208
8081828384858687888990919293949596979899";
209209

210210
macro_rules! impl_Display {
211-
($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
211+
($($t:ident => $size:literal $(as $positive:ident in $other:ident)? => named $name:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
212+
213+
$(
214+
#[stable(feature = "rust1", since = "1.0.0")]
215+
impl fmt::Display for $t {
216+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217+
// If it's a signed integer.
218+
$(
219+
let is_nonnegative = *self >= 0;
220+
221+
#[cfg(not(feature = "optimize_for_size"))]
222+
{
223+
if !is_nonnegative {
224+
// convert the negative num to positive by summing 1 to its 2s complement
225+
return $other((!self as $positive).wrapping_add(1), false, f);
226+
}
227+
}
228+
#[cfg(feature = "optimize_for_size")]
229+
{
230+
if !is_nonnegative {
231+
// convert the negative num to positive by summing 1 to its 2s complement
232+
return $other((!self.$conv_fn()).wrapping_add(1), false, f);
233+
}
234+
}
235+
)?
236+
// If it's a positive integer.
237+
#[cfg(not(feature = "optimize_for_size"))]
238+
{
239+
$name(*self, true, f)
240+
}
241+
#[cfg(feature = "optimize_for_size")]
242+
{
243+
$gen_name(*self, true, f)
244+
}
245+
}
246+
}
247+
212248
#[cfg(not(feature = "optimize_for_size"))]
213-
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214-
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
215-
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
249+
fn $name(mut n: $t, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250+
let mut buf = [MaybeUninit::<u8>::uninit(); $size];
216251
let mut curr = buf.len();
217252
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
218253
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
@@ -226,22 +261,26 @@ macro_rules! impl_Display {
226261
// is safe to access.
227262
unsafe {
228263
// need at least 16 bits for the 4-characters-at-a-time to work.
229-
assert!(crate::mem::size_of::<$u>() >= 2);
230-
231-
// eagerly decode 4 characters at a time
232-
while n >= 10000 {
233-
let rem = (n % 10000) as usize;
234-
n /= 10000;
235-
236-
let d1 = (rem / 100) << 1;
237-
let d2 = (rem % 100) << 1;
238-
curr -= 4;
239-
240-
// We are allowed to copy to `buf_ptr[curr..curr + 3]` here since
241-
// otherwise `curr < 0`. But then `n` was originally at least `10000^10`
242-
// which is `10^40 > 2^128 > n`.
243-
ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
244-
ptr::copy_nonoverlapping(lut_ptr.add(d2), buf_ptr.add(curr + 2), 2);
264+
#[allow(overflowing_literals)]
265+
#[allow(unused_comparisons)]
266+
// This block will be removed for smaller types at compile time and in the worst
267+
// case, it will prevent to have the `10000` literal to overflow for `i8` and `u8`.
268+
if core::mem::size_of::<$t>() >= 2 {
269+
// eagerly decode 4 characters at a time
270+
while n >= 10000 {
271+
let rem = (n % 10000) as usize;
272+
n /= 10000;
273+
274+
let d1 = (rem / 100) << 1;
275+
let d2 = (rem % 100) << 1;
276+
curr -= 4;
277+
278+
// We are allowed to copy to `buf_ptr[curr..curr + 3]` here since
279+
// otherwise `curr < 0`. But then `n` was originally at least `10000^10`
280+
// which is `10^40 > 2^128 > n`.
281+
ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(curr), 2);
282+
ptr::copy_nonoverlapping(lut_ptr.add(d2 as usize), buf_ptr.add(curr + 2), 2);
283+
}
245284
}
246285

247286
// if we reach here numbers are <= 9999, so at most 4 chars long
@@ -255,6 +294,8 @@ macro_rules! impl_Display {
255294
ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
256295
}
257296

297+
// if we reach here numbers are <= 100, so at most 2 chars long
298+
// The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough.
258299
// decode last 1 or 2 chars
259300
if n < 10 {
260301
curr -= 1;
@@ -273,11 +314,10 @@ macro_rules! impl_Display {
273314
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
274315
};
275316
f.pad_integral(is_nonnegative, "", buf_slice)
276-
}
317+
})*
277318

278319
#[cfg(feature = "optimize_for_size")]
279-
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280-
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
320+
fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281321
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
282322
let mut curr = buf.len();
283323
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
@@ -306,21 +346,6 @@ macro_rules! impl_Display {
306346
};
307347
f.pad_integral(is_nonnegative, "", buf_slice)
308348
}
309-
310-
$(#[stable(feature = "rust1", since = "1.0.0")]
311-
impl fmt::Display for $t {
312-
#[allow(unused_comparisons)]
313-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314-
let is_nonnegative = *self >= 0;
315-
let n = if is_nonnegative {
316-
self.$conv_fn()
317-
} else {
318-
// convert the negative num to positive by summing 1 to it's 2 complement
319-
(!self.$conv_fn()).wrapping_add(1)
320-
};
321-
$name(n, is_nonnegative, f)
322-
}
323-
})*
324349
};
325350
}
326351

@@ -374,7 +399,6 @@ macro_rules! impl_Exp {
374399
(n, exponent, exponent, added_precision)
375400
};
376401

377-
// 39 digits (worst case u128) + . = 40
378402
// Since `curr` always decreases by the number of digits copied, this means
379403
// that `curr >= 0`.
380404
let mut buf = [MaybeUninit::<u8>::uninit(); 40];
@@ -469,7 +493,7 @@ macro_rules! impl_Exp {
469493
let n = if is_nonnegative {
470494
self.$conv_fn()
471495
} else {
472-
// convert the negative num to positive by summing 1 to it's 2 complement
496+
// convert the negative num to positive by summing 1 to its 2s complement
473497
(!self.$conv_fn()).wrapping_add(1)
474498
};
475499
$name(n, is_nonnegative, false, f)
@@ -484,7 +508,7 @@ macro_rules! impl_Exp {
484508
let n = if is_nonnegative {
485509
self.$conv_fn()
486510
} else {
487-
// convert the negative num to positive by summing 1 to it's 2 complement
511+
// convert the negative num to positive by summing 1 to its 2s complement
488512
(!self.$conv_fn()).wrapping_add(1)
489513
};
490514
$name(n, is_nonnegative, true, f)
@@ -499,8 +523,17 @@ macro_rules! impl_Exp {
499523
mod imp {
500524
use super::*;
501525
impl_Display!(
502-
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
503-
as u64 via to_u64 named fmt_u64
526+
i8 => 3 as u8 in fmt_u8 => named fmt_i8,
527+
u8 => 3 => named fmt_u8,
528+
i16 => 5 as u16 in fmt_u16 => named fmt_i16,
529+
u16 => 5 => named fmt_u16,
530+
i32 => 10 as u32 in fmt_u32 => named fmt_i32,
531+
u32 => 10 => named fmt_u32,
532+
i64 => 19 as u64 in fmt_u64 => named fmt_i64,
533+
u64 => 20 => named fmt_u64,
534+
isize => 19 as usize in fmt_usize => named fmt_isize,
535+
usize => 20 => named fmt_usize,
536+
; as u64 via to_u64 named fmt_u64
504537
);
505538
impl_Exp!(
506539
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
@@ -511,8 +544,21 @@ mod imp {
511544
#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
512545
mod imp {
513546
use super::*;
514-
impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32);
515-
impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64);
547+
impl_Display!(
548+
i8 => 3 as u8 in fmt_u8 => named fmt_i8,
549+
u8 => 3 => named fmt_u8,
550+
i16 => 5 as u16 in fmt_u16 => named fmt_i16,
551+
u16 => 5 => named fmt_u16,
552+
i32 => 10 as u32 in fmt_u32 => named fmt_i32,
553+
u32 => 10 => named fmt_u32,
554+
isize => 10 as usize in fmt_usize => named fmt_isize,
555+
usize => 10 => named fmt_usize,
556+
; as u32 via to_u32 named fmt_u32);
557+
impl_Display!(
558+
i64 => 19 as u64 in fmt_u64 => named fmt_i64,
559+
u64 => 20 => named fmt_u64,
560+
; as u64 via to_u64 named fmt_u64);
561+
516562
impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32);
517563
impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64);
518564
}
@@ -619,7 +665,7 @@ impl fmt::Display for i128 {
619665
let n = if is_nonnegative {
620666
self.to_u128()
621667
} else {
622-
// convert the negative num to positive by summing 1 to it's 2 complement
668+
// convert the negative num to positive by summing 1 to its 2s complement
623669
(!self.to_u128()).wrapping_add(1)
624670
};
625671
fmt_u128(n, is_nonnegative, f)

0 commit comments

Comments
 (0)