Skip to content

Commit fd7fbf0

Browse files
Moved functionality to fmt
1 parent 8860091 commit fd7fbf0

File tree

5 files changed

+207
-299
lines changed

5 files changed

+207
-299
lines changed

library/core/src/fmt/int_format.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use crate::mem::MaybeUninit;
2+
use crate::slice;
3+
4+
/// 41 is chosen as the buffer length, as it is larger
5+
/// than that required to accommodate i128::MIN
6+
/// (39 decimal digits, 1 for negative sign).
7+
const BUF_SIZE = 41;
8+
9+
/// A minimal buffer implementation containing elements of type
10+
/// `MaybeUninit<u8>`.
11+
#[unstable(feature = "int_format_into", issue = "138215")]
12+
#[derive(Debug)]
13+
pub struct NumBuffer {
14+
/// An array of elements of type `MaybeUninit<u8>`.
15+
pub contents: [MaybeUninit<u8>; BUF_SIZE],
16+
}
17+
18+
#[unstable(feature = "int_format_into", issue = "138215")]
19+
impl NumBuffer {
20+
/// Initializes `contents` as an uninitialized array of `MaybeUninit<u8>`.
21+
#[unstable(feature = "int_format_into", issue = "138215")]
22+
pub fn new() -> Self {
23+
NumBuffer { contents: [MaybeUninit::<u8>::uninit(); BUF_SIZE] }
24+
}
25+
}

library/core/src/fmt/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::{iter, mem, result, str};
1212
mod builders;
1313
#[cfg(not(no_fp_fmt_parse))]
1414
mod float;
15+
mod int_format;
1516
#[cfg(no_fp_fmt_parse)]
1617
mod nofloat;
1718
mod num;
@@ -50,6 +51,8 @@ impl From<rt::Alignment> for Option<Alignment> {
5051
pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
5152
#[unstable(feature = "debug_closure_helpers", issue = "117729")]
5253
pub use self::builders::{FromFn, from_fn};
54+
#[unstable(feature = "int_format_into", issue = "138215")]
55+
pub use int_format::NumBuffer;
5356

5457
/// The type returned by formatter methods.
5558
///

library/core/src/fmt/num.rs

Lines changed: 179 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::mem::MaybeUninit;
44
use crate::num::fmt as numfmt;
55
use crate::ops::{Div, Rem, Sub};
66
use crate::{fmt, ptr, slice, str};
7+
use crate::fmt::NumBuffer;
78

89
#[doc(hidden)]
910
trait DisplayInt:
@@ -199,6 +200,42 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199200
6061626364656667686970717273747576777879\
200201
8081828384858687888990919293949596979899";
201202

203+
static NEGATIVE_SIGN: &[u8; 1] = b"-";
204+
205+
// Unsafe because
206+
// 1. The contents of `buf` is not validated in the function
207+
// 2. offset is not bound checked
208+
unsafe fn _extract_str_from_buf(
209+
buf: &NumBuffer,
210+
offset: usize
211+
) -> &str {
212+
let written = buf.contents.get_unchecked(offset..);
213+
let as_str = str::from_utf8_unchecked(slice::from_raw_parts(
214+
MaybeUninit::slice_as_ptr(written),
215+
written.len(),
216+
));
217+
as_str
218+
}
219+
220+
// Unsafe because
221+
// 1. Offset is not bound checked
222+
unsafe fn _add_negative_sign(
223+
is_nonnegative: bool,
224+
buf: &mut NumBuffer,
225+
start_offset: usize
226+
) -> usize {
227+
if is_nonnegative {
228+
return start_offset;
229+
}
230+
231+
let mut offset = start_offset - 1;
232+
233+
// Setting sign for the negative number
234+
buf.contents[offset].write(NEGATIVE_SIGN[0]);
235+
236+
offset
237+
}
238+
202239
macro_rules! impl_Display {
203240
($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
204241

@@ -212,7 +249,9 @@ macro_rules! impl_Display {
212249
}
213250
#[cfg(feature = "optimize_for_size")]
214251
{
215-
$gen_name(self.$conv_fn(), true, f)
252+
let mut buf = NumBuffer::new();
253+
let as_str = $gen_name(self.$conv_fn(), true, &mut buf);
254+
f.pad_integral(true, "", as_str)
216255
}
217256
}
218257
}
@@ -226,31 +265,121 @@ macro_rules! impl_Display {
226265
}
227266
#[cfg(feature = "optimize_for_size")]
228267
{
229-
return $gen_name(self.unsigned_abs().$conv_fn(), *self >= 0, f);
268+
let mut buf = NumBuffer::new();
269+
270+
// not setting the sign here, hence sending in `true`
271+
let as_str = $gen_name(self.unsigned_abs().$conv_fn(), true, &mut buf);
272+
f.pad_integral(*self >= 0, "", as_str)
230273
}
231274
}
232275
}
233276

277+
#[unstable(feature = "int_format_into", issue = "138215")]
278+
impl $signed {
279+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
280+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
281+
///
282+
/// # Examples
283+
/// ```
284+
/// #![feature(int_format_into)]
285+
/// use core::fmt::NumBuffer;
286+
///
287+
#[doc = concat!("let n = -32", stringify!($signed), ";")]
288+
/// let mut buf = NumBuffer::new();
289+
/// assert_eq!(n.format_into(&mut buf), "-32");
290+
///
291+
#[doc = concat!("let n2 = ", stringify!($signed::MIN), ";")]
292+
/// let mut buf2 = NumBuffer::new();
293+
#[doc = concat!("assert_eq!(n2.format_into(&mut buf2), ", stringify!($signed::MIN), ".to_string());")]
294+
///
295+
#[doc = concat!("let n3 = ", stringify!($signed::MAX), ";")]
296+
/// let mut buf3 = NumBuffer::new();
297+
#[doc = concat!("assert_eq!(n3.format_into(&mut buf3), ", stringify!($signed::MAX), ".to_string());")]
298+
/// ```
299+
///
300+
pub fn format_into(self, buf: &mut NumBuffer) -> &str {
301+
#[cfg(not(feature = "optimize_for_size"))]
302+
{
303+
let offset = self.unsigned_abs()._write_into_buf(buf);
304+
305+
// SAFETY: `offset >= 2` (unchanged if `is_nonnegative` is true) and
306+
// `offset >= 1` (incase `is_nonnegative` is false) so
307+
// `buf_ptr[offset..offset + 1]` is safe to access.
308+
unsafe { _add_negative_sign(is_nonnegative, buf, offset); }
309+
310+
// SAFETY: All buf content since offset is set, and
311+
// writes use ASCII from the lookup table exclusively.
312+
let as_str = unsafe { _extract_str_from_buf(buf, offset) };
313+
314+
as_str
315+
}
316+
317+
#[cfg(feature = "optimize_for_size")]
318+
{
319+
let is_nonnegative = self > 0;
320+
$gen_name(self.unsigned_abs(), is_nonnegative, buf)
321+
}
322+
323+
}
324+
}
325+
326+
#[unstable(feature = "int_format_into", issue = "138215")]
327+
impl $unsigned {
328+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
329+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
330+
///
331+
/// # Examples
332+
/// ```
333+
/// #![feature(int_format_into)]
334+
/// use core::fmt::NumBuffer;
335+
///
336+
#[doc = concat!("let n = 32", stringify!($unsigned), ";")]
337+
/// let mut buf = NumBuffer::new();
338+
/// assert_eq!(n.format_into(&mut buf), "32");
339+
///
340+
#[doc = concat!("let n2 = ", stringify!($unsigned::MAX), ";")]
341+
/// let mut buf2 = NumBuffer::new();
342+
#[doc = concat!("assert_eq!(n2.format_into(&mut buf2), ", stringify!($unsigned::MAX), ".to_string());")]
343+
/// ```
344+
///
345+
pub fn format_into(self, buf: &mut NumBuffer) -> &str {
346+
#[cfg(not(feature = "optimize_for_size"))]
347+
{
348+
let offset = self._write_into_buf(buf);
349+
350+
// SAFETY: All contents in `buf` since offset is set, and
351+
// writes use ASCII from the lookup table exclusively.
352+
let as_str = unsafe { _extract_str_from_buf(buf, offset) };
353+
354+
as_str
355+
}
356+
357+
#[cfg(feature = "optimize_for_size")]
358+
{
359+
$gen_name(self, true, buf)
360+
}
361+
362+
}
363+
}
364+
234365
#[cfg(not(feature = "optimize_for_size"))]
235366
impl $unsigned {
236-
fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237-
const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
238-
// Buffer decimals for $unsigned with right alignment.
239-
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
367+
fn _write_into_buf(self, buf: &mut NumBuffer) -> usize {
240368
// Count the number of bytes in buf that are not initialized.
241-
let mut offset = buf.len();
369+
let mut offset = buf.contents.len();
242370
// Consume the least-significant decimals from a working copy.
243371
let mut remain = self;
244372

245373
// Format per four digits from the lookup table.
246374
// Four digits need a 16-bit $unsigned or wider.
247375
while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") {
248-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
376+
// SAFETY: All of the decimals fit in buf since buf is large enough to
377+
// accommodate the largest representation of a number possible (that of i128::MIN)
249378
// and the while condition ensures at least 4 more decimals.
250379
unsafe { core::hint::assert_unchecked(offset >= 4) }
251-
// SAFETY: The offset counts down from its initial buf.len()
380+
// SAFETY: The offset counts down from its initial size
252381
// without underflow due to the previous precondition.
253-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
382+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
254383
offset -= 4;
255384

256385
// pull two pairs
@@ -259,26 +388,27 @@ macro_rules! impl_Display {
259388
remain /= scale;
260389
let pair1 = (quad / 100) as usize;
261390
let pair2 = (quad % 100) as usize;
262-
buf[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
263-
buf[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
264-
buf[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
265-
buf[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
391+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
392+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
393+
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
394+
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
266395
}
267396

268397
// Format per two digits from the lookup table.
269398
if remain > 9 {
270-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
399+
// SAFETY: All of the decimals fit in buf since buf is large enough to
400+
// accommodate the largest representation of a number possilble (that of i128::MIN)
271401
// and the while condition ensures at least 2 more decimals.
272402
unsafe { core::hint::assert_unchecked(offset >= 2) }
273-
// SAFETY: The offset counts down from its initial buf.len()
403+
// SAFETY: The offset counts down from its initial size
274404
// without underflow due to the previous precondition.
275-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
405+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
276406
offset -= 2;
277407

278408
let pair = (remain % 100) as usize;
279409
remain /= 100;
280-
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
281-
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
410+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
411+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
282412
}
283413

284414
// Format the last remaining digit, if any.
@@ -288,40 +418,43 @@ macro_rules! impl_Display {
288418
unsafe { core::hint::assert_unchecked(offset >= 1) }
289419
// SAFETY: The offset counts down from its initial buf.len()
290420
// without underflow due to the previous precondition.
291-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
421+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
292422
offset -= 1;
293423

294424
// Either the compiler sees that remain < 10, or it prevents
295425
// a boundary check up next.
296426
let last = (remain & 15) as usize;
297-
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
427+
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
298428
// not used: remain = 0;
299429
}
300430

301-
// SAFETY: All buf content since offset is set.
302-
let written = unsafe { buf.get_unchecked(offset..) };
303-
// SAFETY: Writes use ASCII from the lookup table exclusively.
431+
offset
432+
}
433+
434+
fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435+
// Buffer decimals for $unsigned with right alignment.
436+
let mut buf = NumBuffer::new();
437+
let offset = self._write_into_buf(&mut buf);
438+
439+
// SAFETY: All buf content since offset is set, and
440+
// writes use ASCII from the lookup table exclusively.
304441
let as_str = unsafe {
305-
str::from_utf8_unchecked(slice::from_raw_parts(
306-
MaybeUninit::slice_as_ptr(written),
307-
written.len(),
308-
))
442+
_extract_str_from_buf(&buf, offset)
309443
};
444+
310445
f.pad_integral(is_nonnegative, "", as_str)
311446
}
312447
})*
313448

314449
#[cfg(feature = "optimize_for_size")]
315-
fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316-
const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
317-
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
318-
let mut curr = MAX_DEC_N;
319-
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
450+
fn $gen_name(mut n: $u, is_nonnegative: bool, buf: &mut NumBuffer) -> &str {
451+
let mut curr = buf.contents.len();
452+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf.contents);
320453

321454
// SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
322-
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
455+
// `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41`, and at
323456
// each step this is kept the same as `n` is divided. Since `n` is always
324-
// non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
457+
// non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325458
// is safe to access.
326459
unsafe {
327460
loop {
@@ -335,12 +468,18 @@ macro_rules! impl_Display {
335468
}
336469
}
337470

338-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
471+
// SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
472+
// `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
473+
// is safe to access.
474+
unsafe { _add_negative_sign(is_nonnegative, buf, curr); }
475+
476+
// SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339477
let buf_slice = unsafe {
340478
str::from_utf8_unchecked(
341479
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
342480
};
343-
f.pad_integral(is_nonnegative, "", buf_slice)
481+
482+
buf_slice
344483
}
345484
};
346485
}
@@ -529,7 +668,7 @@ mod imp {
529668
i32, u32,
530669
i64, u64,
531670
isize, usize,
532-
; as u64 via to_u64 named fmt_u64
671+
; as u64 via to_u64 named stringify_u64
533672
);
534673
impl_Exp!(
535674
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
@@ -545,10 +684,10 @@ mod imp {
545684
i16, u16,
546685
i32, u32,
547686
isize, usize,
548-
; as u32 via to_u32 named fmt_u32);
687+
; as u32 via to_u32 named stringify_u32);
549688
impl_Display!(
550689
i64, u64,
551-
; as u64 via to_u64 named fmt_u64);
690+
; as u64 via to_u64 named stringify_u64);
552691

553692
impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32);
554693
impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64);

0 commit comments

Comments
 (0)