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 5dcab78

Browse files
committedApr 30, 2023
Use ascii::Char in integer formatting internals
1 parent ab917f8 commit 5dcab78

File tree

1 file changed

+49
-40
lines changed

1 file changed

+49
-40
lines changed
 

‎library/core/src/fmt/num.rs

+49-40
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Integer and floating-point number formatting
22
3+
use crate::ascii;
34
use crate::fmt;
45
use crate::mem::MaybeUninit;
56
use crate::num::fmt as numfmt;
@@ -200,17 +201,22 @@ debug! {
200201
}
201202

202203
// 2 digit decimal look up table
203-
static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
204-
2021222324252627282930313233343536373839\
205-
4041424344454647484950515253545556575859\
206-
6061626364656667686970717273747576777879\
207-
8081828384858687888990919293949596979899";
204+
// FIXME: use `.as_ascii().unwrap()` once `slice::is_ascii` is `const fn`
205+
// SAFETY: They're all just digits, and CTFE will double-check validity too.
206+
static DEC_DIGITS_LUT: [ascii::Char; 200] = unsafe {
207+
*b"0001020304050607080910111213141516171819\
208+
2021222324252627282930313233343536373839\
209+
4041424344454647484950515253545556575859\
210+
6061626364656667686970717273747576777879\
211+
8081828384858687888990919293949596979899"
212+
.as_ascii_unchecked()
213+
};
208214

209215
macro_rules! impl_Display {
210216
($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
211217
fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212218
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
213-
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
219+
let mut buf = [MaybeUninit::<ascii::Char>::uninit(); 39];
214220
let mut curr = buf.len();
215221
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
216222
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
@@ -254,23 +260,21 @@ macro_rules! impl_Display {
254260
}
255261

256262
// decode last 1 or 2 chars
257-
if n < 10 {
263+
if let Some(d) = ascii::Char::digit(n as u8) {
258264
curr -= 1;
259-
*buf_ptr.add(curr) = (n as u8) + b'0';
265+
*buf_ptr.add(curr) = d;
260266
} else {
261267
let d1 = n << 1;
262268
curr -= 2;
263269
ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
264270
}
265271
}
266272

267-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
268-
// UTF-8 since `DEC_DIGITS_LUT` is
269-
let buf_slice = unsafe {
270-
str::from_utf8_unchecked(
271-
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
273+
// SAFETY: `curr` > 0 (since we made `buf` large enough)
274+
let buf_slice: &[ascii::Char] = unsafe {
275+
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)
272276
};
273-
f.pad_integral(is_nonnegative, "", buf_slice)
277+
f.pad_integral(is_nonnegative, "", buf_slice.as_str())
274278
}
275279

276280
$(#[stable(feature = "rust1", since = "1.0.0")]
@@ -299,7 +303,7 @@ macro_rules! impl_Exp {
299303
f: &mut fmt::Formatter<'_>
300304
) -> fmt::Result {
301305
let (mut n, mut exponent, trailing_zeros, added_precision) = {
302-
let mut exponent = 0;
306+
let mut exponent: usize = 0;
303307
// count and remove trailing decimal zeroes
304308
while n % 10 == 0 && n >= 10 {
305309
n /= 10;
@@ -338,7 +342,7 @@ macro_rules! impl_Exp {
338342
// 39 digits (worst case u128) + . = 40
339343
// Since `curr` always decreases by the number of digits copied, this means
340344
// that `curr >= 0`.
341-
let mut buf = [MaybeUninit::<u8>::uninit(); 40];
345+
let mut buf = [MaybeUninit::<ascii::Char>::uninit(); 40];
342346
let mut curr = buf.len(); //index for buf
343347
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
344348
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
@@ -362,7 +366,7 @@ macro_rules! impl_Exp {
362366
curr -= 1;
363367
// SAFETY: Safe since `40 > curr >= 0` (see comment)
364368
unsafe {
365-
*buf_ptr.add(curr) = (n as u8 % 10_u8) + b'0';
369+
*buf_ptr.add(curr) = ascii::Char::digit(n as u8 % 10_u8).unwrap()
366370
}
367371
n /= 10;
368372
exponent += 1;
@@ -372,29 +376,31 @@ macro_rules! impl_Exp {
372376
curr -= 1;
373377
// SAFETY: Safe since `40 > curr >= 0`
374378
unsafe {
375-
*buf_ptr.add(curr) = b'.';
379+
*buf_ptr.add(curr) = ascii::Char::FullStop;
376380
}
377381
}
378382

379-
// SAFETY: Safe since `40 > curr >= 0`
383+
// SAFETY: Safe since `40 > curr >= 0`.
384+
// n is <= 9, because after the loop it's <= 99, then it was
385+
// /= 10 again if it wasn't already below 10.
380386
let buf_slice = unsafe {
381387
// decode last character
382388
curr -= 1;
383-
*buf_ptr.add(curr) = (n as u8) + b'0';
389+
*buf_ptr.add(curr) = ascii::Char::digit_unchecked(n as u8);
384390

385391
let len = buf.len() - curr as usize;
386392
slice::from_raw_parts(buf_ptr.add(curr), len)
387393
};
388394

389395
// stores 'e' (or 'E') and the up to 2-digit exponent
390-
let mut exp_buf = [MaybeUninit::<u8>::uninit(); 3];
396+
let mut exp_buf = [MaybeUninit::<ascii::Char>::uninit(); 3];
391397
let exp_ptr = MaybeUninit::slice_as_mut_ptr(&mut exp_buf);
392398
// SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]`
393399
// is contained within `exp_buf` since `len <= 3`.
394400
let exp_slice = unsafe {
395-
*exp_ptr.add(0) = if upper { b'E' } else { b'e' };
396-
let len = if exponent < 10 {
397-
*exp_ptr.add(1) = (exponent as u8) + b'0';
401+
*exp_ptr.add(0) = if upper { ascii::Char::CapitalE } else { ascii::Char::SmallE };
402+
let len = if let Some(digit) = ascii::Char::digit(exponent as u8) {
403+
*exp_ptr.add(1) = digit;
398404
2
399405
} else {
400406
let off = exponent << 1;
@@ -405,9 +411,9 @@ macro_rules! impl_Exp {
405411
};
406412

407413
let parts = &[
408-
numfmt::Part::Copy(buf_slice),
414+
numfmt::Part::Copy(buf_slice.as_bytes()),
409415
numfmt::Part::Zero(added_precision),
410-
numfmt::Part::Copy(exp_slice)
416+
numfmt::Part::Copy(exp_slice.as_bytes())
411417
];
412418
let sign = if !is_nonnegative {
413419
"-"
@@ -479,7 +485,11 @@ mod imp {
479485
impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
480486

481487
/// Helper function for writing a u64 into `buf` going from last to first, with `curr`.
482-
fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], curr: &mut usize) {
488+
fn parse_u64_into<const N: usize>(
489+
mut n: u64,
490+
buf: &mut [MaybeUninit<ascii::Char>; N],
491+
curr: &mut usize,
492+
) {
483493
let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf);
484494
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
485495
assert!(*curr > 19);
@@ -553,10 +563,13 @@ fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], cu
553563
ptr::copy_nonoverlapping(lut_ptr.add(d1 as usize), buf_ptr.add(*curr), 2);
554564
}
555565

566+
// `n` < 1e2 < (1 << 8)
567+
let n = n as u8;
568+
556569
// decode last 1 or 2 chars
557-
if n < 10 {
570+
if let Some(d) = ascii::Char::digit(n) {
558571
*curr -= 1;
559-
*buf_ptr.add(*curr) = (n as u8) + b'0';
572+
*buf_ptr.add(*curr) = d;
560573
} else {
561574
let d1 = n << 1;
562575
*curr -= 2;
@@ -592,7 +605,7 @@ impl fmt::Display for i128 {
592605
/// 10^20 > 2^64 > 10^19.
593606
fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594607
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
595-
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
608+
let mut buf = [MaybeUninit::<ascii::Char>::uninit(); 39];
596609
let mut curr = buf.len();
597610

598611
let (n, rem) = udiv_1e19(n);
@@ -621,24 +634,20 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R
621634
// buf `buf` is not used in this scope so we are good.
622635
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
623636
// SAFETY: At this point we wrote at most 38 bytes, pad up to that point,
624-
// There can only be at most 1 digit remaining.
637+
// There can only be at most 1 digit remaining. (2¹²⁸ ÷ 10³⁸ ≈ 3.4)
625638
unsafe {
626639
ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
627640
curr = target - 1;
628-
*buf_ptr.add(curr) = (n as u8) + b'0';
641+
*buf_ptr.add(curr) = ascii::Char::digit_unchecked(n as u8);
629642
}
630643
}
631644
}
632645

633-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
634-
// UTF-8 since `DEC_DIGITS_LUT` is
635-
let buf_slice = unsafe {
636-
str::from_utf8_unchecked(slice::from_raw_parts(
637-
MaybeUninit::slice_as_mut_ptr(&mut buf).add(curr),
638-
buf.len() - curr,
639-
))
646+
// SAFETY: `curr` > 0 (since we made `buf` large enough)
647+
let buf_slice: &[ascii::Char] = unsafe {
648+
slice::from_raw_parts(MaybeUninit::slice_as_mut_ptr(&mut buf).add(curr), buf.len() - curr)
640649
};
641-
f.pad_integral(is_nonnegative, "", buf_slice)
650+
f.pad_integral(is_nonnegative, "", buf_slice.as_str())
642651
}
643652

644653
/// Partition of `n` into n > 1e19 and rem <= 1e19

0 commit comments

Comments
 (0)
Failed to load comments.