Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3b60335

Browse files
authoredJun 7, 2024
Rollup merge of rust-lang#125606 - diondokter:opt-size-int-fmt, r=cuviper
Size optimize int formatting Let's use the new feature flag! This uses a simpler algorithm to format integers. It is slower, but also smaller. It also saves having to import the 200 byte rodata lookup table. In a test of mine this saves ~300 bytes total of a cortex-m binary that does integer formatting. For a 16KB device, that's almost 2%. Note though that for opt-level 3 the text size actually grows by 116 bytes. Still a win in total. I'm not sure why the generated code is bigger than the more fancy algo. Maybe the smaller algo lends itself more to inlining and duplicating?
2 parents fa66a61 + 1e8098b commit 3b60335

File tree

1 file changed

+33
-0
lines changed

1 file changed

+33
-0
lines changed
 

‎core/src/fmt/num.rs

+33
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
212212

213213
macro_rules! impl_Display {
214214
($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
215+
#[cfg(not(feature = "optimize_for_size"))]
215216
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216217
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
217218
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
@@ -277,6 +278,38 @@ macro_rules! impl_Display {
277278
f.pad_integral(is_nonnegative, "", buf_slice)
278279
}
279280

281+
#[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
284+
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
285+
let mut curr = buf.len();
286+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
287+
288+
// SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
289+
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
290+
// each step this is kept the same as `n` is divided. Since `n` is always
291+
// non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
292+
// is safe to access.
293+
unsafe {
294+
loop {
295+
curr -= 1;
296+
buf_ptr.add(curr).write((n % 10) as u8 + b'0');
297+
n /= 10;
298+
299+
if n == 0 {
300+
break;
301+
}
302+
}
303+
}
304+
305+
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
306+
let buf_slice = unsafe {
307+
str::from_utf8_unchecked(
308+
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
309+
};
310+
f.pad_integral(is_nonnegative, "", buf_slice)
311+
}
312+
280313
$(#[stable(feature = "rust1", since = "1.0.0")]
281314
impl fmt::Display for $t {
282315
#[allow(unused_comparisons)]

0 commit comments

Comments
 (0)
Failed to load comments.