Skip to content

Commit 10774d9

Browse files
Small optimization for integers Display implementation
1 parent 2ccafed commit 10774d9

File tree

1 file changed

+92
-46
lines changed

1 file changed

+92
-46
lines changed

library/core/src/fmt/num.rs

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

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

250289
// if we reach here numbers are <= 9999, so at most 4 chars long
@@ -258,6 +297,8 @@ macro_rules! impl_Display {
258297
ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
259298
}
260299

300+
// if we reach here numbers are <= 100, so at most 2 chars long
301+
// The biggest it can be is 99, and 99 << 1 == 198, so a `u8` is enough.
261302
// decode last 1 or 2 chars
262303
if n < 10 {
263304
curr -= 1;
@@ -276,11 +317,10 @@ macro_rules! impl_Display {
276317
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
277318
};
278319
f.pad_integral(is_nonnegative, "", buf_slice)
279-
}
320+
})*
280321

281322
#[cfg(feature = "optimize_for_size")]
282-
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283-
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
323+
fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284324
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
285325
let mut curr = buf.len();
286326
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
@@ -309,21 +349,6 @@ macro_rules! impl_Display {
309349
};
310350
f.pad_integral(is_nonnegative, "", buf_slice)
311351
}
312-
313-
$(#[stable(feature = "rust1", since = "1.0.0")]
314-
impl fmt::Display for $t {
315-
#[allow(unused_comparisons)]
316-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317-
let is_nonnegative = *self >= 0;
318-
let n = if is_nonnegative {
319-
self.$conv_fn()
320-
} else {
321-
// convert the negative num to positive by summing 1 to it's 2 complement
322-
(!self.$conv_fn()).wrapping_add(1)
323-
};
324-
$name(n, is_nonnegative, f)
325-
}
326-
})*
327352
};
328353
}
329354

@@ -377,7 +402,6 @@ macro_rules! impl_Exp {
377402
(n, exponent, exponent, added_precision)
378403
};
379404

380-
// 39 digits (worst case u128) + . = 40
381405
// Since `curr` always decreases by the number of digits copied, this means
382406
// that `curr >= 0`.
383407
let mut buf = [MaybeUninit::<u8>::uninit(); 40];
@@ -472,7 +496,7 @@ macro_rules! impl_Exp {
472496
let n = if is_nonnegative {
473497
self.$conv_fn()
474498
} else {
475-
// convert the negative num to positive by summing 1 to it's 2 complement
499+
// convert the negative num to positive by summing 1 to its 2s complement
476500
(!self.$conv_fn()).wrapping_add(1)
477501
};
478502
$name(n, is_nonnegative, false, f)
@@ -487,7 +511,7 @@ macro_rules! impl_Exp {
487511
let n = if is_nonnegative {
488512
self.$conv_fn()
489513
} else {
490-
// convert the negative num to positive by summing 1 to it's 2 complement
514+
// convert the negative num to positive by summing 1 to its 2s complement
491515
(!self.$conv_fn()).wrapping_add(1)
492516
};
493517
$name(n, is_nonnegative, true, f)
@@ -502,8 +526,17 @@ macro_rules! impl_Exp {
502526
mod imp {
503527
use super::*;
504528
impl_Display!(
505-
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
506-
as u64 via to_u64 named fmt_u64
529+
i8 => 3 as u8 in fmt_u8 => named fmt_i8,
530+
u8 => 3 => named fmt_u8,
531+
i16 => 5 as u16 in fmt_u16 => named fmt_i16,
532+
u16 => 5 => named fmt_u16,
533+
i32 => 10 as u32 in fmt_u32 => named fmt_i32,
534+
u32 => 10 => named fmt_u32,
535+
i64 => 20 as u64 in fmt_u64 => named fmt_i64,
536+
u64 => 20 => named fmt_u64,
537+
isize => 20 as usize in fmt_usize => named fmt_isize,
538+
usize => 20 => named fmt_usize,
539+
; as u64 via to_u64 named fmt_u64
507540
);
508541
impl_Exp!(
509542
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
@@ -514,8 +547,21 @@ mod imp {
514547
#[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
515548
mod imp {
516549
use super::*;
517-
impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32);
518-
impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64);
550+
impl_Display!(
551+
i8 => 3 as u8 in fmt_u8 => named fmt_i8,
552+
u8 => 3 => named fmt_u8,
553+
i16 => 5 as u16 in fmt_u16 => named fmt_i16,
554+
u16 => 5 => named fmt_u16,
555+
i32 => 10 as u32 in fmt_u32 => named fmt_i32,
556+
u32 => 10 => named fmt_u32,
557+
isize => 10 as usize in fmt_usize => named fmt_isize,
558+
usize => 10 => named fmt_usize,
559+
; as u32 via to_u32 named fmt_u32);
560+
impl_Display!(
561+
i64 => 20 as u64 in fmt_u64 => named fmt_i64,
562+
u64 => 20 => named fmt_u64,
563+
; as u64 via to_u64 named fmt_u64);
564+
519565
impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32);
520566
impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64);
521567
}
@@ -622,7 +668,7 @@ impl fmt::Display for i128 {
622668
let n = if is_nonnegative {
623669
self.to_u128()
624670
} else {
625-
// convert the negative num to positive by summing 1 to it's 2 complement
671+
// convert the negative num to positive by summing 1 to its 2s complement
626672
(!self.to_u128()).wrapping_add(1)
627673
};
628674
fmt_u128(n, is_nonnegative, f)

0 commit comments

Comments
 (0)