Skip to content

Commit a5e9b5a

Browse files
Shifted to crate fmt from num
1 parent 8860091 commit a5e9b5a

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

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;
@@ -46,6 +47,8 @@ impl From<rt::Alignment> for Option<Alignment> {
4647
}
4748
}
4849

50+
#[unstable(feature = "int_format_into", issue = "138215")]
51+
pub use int_format::NumBuffer;
4952
#[stable(feature = "debug_builders", since = "1.2.0")]
5053
pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
5154
#[unstable(feature = "debug_closure_helpers", issue = "117729")]

library/core/src/fmt/num.rs

Lines changed: 180 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Integer and floating-point number formatting
22
3+
use crate::fmt::NumBuffer;
34
use crate::mem::MaybeUninit;
45
use crate::num::fmt as numfmt;
56
use crate::ops::{Div, Rem, Sub};
@@ -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(buf: &NumBuffer, offset: usize) -> &str {
209+
let written = unsafe { buf.contents.get_unchecked(offset..) };
210+
let as_str = unsafe {
211+
str::from_utf8_unchecked(slice::from_raw_parts(
212+
MaybeUninit::slice_as_ptr(written),
213+
written.len(),
214+
))
215+
};
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 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,122 @@ 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)
273+
}
274+
}
275+
}
276+
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 is_nonnegative = self > 0;
304+
let offset = self.unsigned_abs()._write_into_buf(buf);
305+
306+
// SAFETY: `offset >= 2` (unchanged if `is_nonnegative` is true) and
307+
// `offset >= 1` (incase `is_nonnegative` is false) so
308+
// `buf_ptr[offset..offset + 1]` is safe to access.
309+
unsafe { _add_negative_sign(is_nonnegative, buf, offset); }
310+
311+
// SAFETY: All buf content since offset is set, and
312+
// writes use ASCII from the lookup table exclusively.
313+
let as_str = unsafe { _extract_str_from_buf(buf, offset) };
314+
315+
as_str
316+
}
317+
318+
#[cfg(feature = "optimize_for_size")]
319+
{
320+
let is_nonnegative = self > 0;
321+
$gen_name(self.unsigned_abs(), is_nonnegative, buf)
322+
}
323+
324+
}
325+
}
326+
327+
#[unstable(feature = "int_format_into", issue = "138215")]
328+
impl $unsigned {
329+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
330+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
331+
///
332+
/// # Examples
333+
/// ```
334+
/// #![feature(int_format_into)]
335+
/// use core::fmt::NumBuffer;
336+
///
337+
#[doc = concat!("let n = 32", stringify!($unsigned), ";")]
338+
/// let mut buf = NumBuffer::new();
339+
/// assert_eq!(n.format_into(&mut buf), "32");
340+
///
341+
#[doc = concat!("let n2 = ", stringify!($unsigned::MAX), ";")]
342+
/// let mut buf2 = NumBuffer::new();
343+
#[doc = concat!("assert_eq!(n2.format_into(&mut buf2), ", stringify!($unsigned::MAX), ".to_string());")]
344+
/// ```
345+
///
346+
pub fn format_into(self, buf: &mut NumBuffer) -> &str {
347+
#[cfg(not(feature = "optimize_for_size"))]
348+
{
349+
let offset = self._write_into_buf(buf);
350+
351+
// SAFETY: All contents in `buf` since offset is set, and
352+
// writes use ASCII from the lookup table exclusively.
353+
let as_str = unsafe { _extract_str_from_buf(buf, offset) };
354+
355+
as_str
356+
}
357+
358+
#[cfg(feature = "optimize_for_size")]
359+
{
360+
$gen_name(self, true, buf)
230361
}
362+
231363
}
232364
}
233365

234366
#[cfg(not(feature = "optimize_for_size"))]
235367
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];
368+
fn _write_into_buf(self, buf: &mut NumBuffer) -> usize {
240369
// Count the number of bytes in buf that are not initialized.
241-
let mut offset = buf.len();
370+
let mut offset = buf.contents.len();
242371
// Consume the least-significant decimals from a working copy.
243372
let mut remain = self;
244373

245374
// Format per four digits from the lookup table.
246375
// Four digits need a 16-bit $unsigned or wider.
247376
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
377+
// SAFETY: All of the decimals fit in buf since buf is large enough to
378+
// accommodate the largest representation of a number possible (that of i128::MIN)
249379
// and the while condition ensures at least 4 more decimals.
250380
unsafe { core::hint::assert_unchecked(offset >= 4) }
251-
// SAFETY: The offset counts down from its initial buf.len()
381+
// SAFETY: The offset counts down from its initial size
252382
// without underflow due to the previous precondition.
253-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
383+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
254384
offset -= 4;
255385

256386
// pull two pairs
@@ -259,26 +389,27 @@ macro_rules! impl_Display {
259389
remain /= scale;
260390
let pair1 = (quad / 100) as usize;
261391
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]);
392+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
393+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
394+
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
395+
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
266396
}
267397

268398
// Format per two digits from the lookup table.
269399
if remain > 9 {
270-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
400+
// SAFETY: All of the decimals fit in buf since buf is large enough to
401+
// accommodate the largest representation of a number possilble (that of i128::MIN)
271402
// and the while condition ensures at least 2 more decimals.
272403
unsafe { core::hint::assert_unchecked(offset >= 2) }
273-
// SAFETY: The offset counts down from its initial buf.len()
404+
// SAFETY: The offset counts down from its initial size
274405
// without underflow due to the previous precondition.
275-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
406+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
276407
offset -= 2;
277408

278409
let pair = (remain % 100) as usize;
279410
remain /= 100;
280-
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
281-
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
411+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
412+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
282413
}
283414

284415
// Format the last remaining digit, if any.
@@ -288,40 +419,43 @@ macro_rules! impl_Display {
288419
unsafe { core::hint::assert_unchecked(offset >= 1) }
289420
// SAFETY: The offset counts down from its initial buf.len()
290421
// without underflow due to the previous precondition.
291-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
422+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
292423
offset -= 1;
293424

294425
// Either the compiler sees that remain < 10, or it prevents
295426
// a boundary check up next.
296427
let last = (remain & 15) as usize;
297-
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
428+
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
298429
// not used: remain = 0;
299430
}
300431

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.
432+
offset
433+
}
434+
435+
fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436+
// Buffer decimals for $unsigned with right alignment.
437+
let mut buf = NumBuffer::new();
438+
let offset = self._write_into_buf(&mut buf);
439+
440+
// SAFETY: All buf content since offset is set, and
441+
// writes use ASCII from the lookup table exclusively.
304442
let as_str = unsafe {
305-
str::from_utf8_unchecked(slice::from_raw_parts(
306-
MaybeUninit::slice_as_ptr(written),
307-
written.len(),
308-
))
443+
_extract_str_from_buf(&buf, offset)
309444
};
445+
310446
f.pad_integral(is_nonnegative, "", as_str)
311447
}
312448
})*
313449

314450
#[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);
451+
fn $gen_name(mut n: $u, is_nonnegative: bool, buf: &mut NumBuffer) -> &str {
452+
let mut curr = buf.contents.len();
453+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf.contents);
320454

321455
// 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
456+
// `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41`, and at
323457
// 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]`
458+
// non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325459
// is safe to access.
326460
unsafe {
327461
loop {
@@ -335,12 +469,18 @@ macro_rules! impl_Display {
335469
}
336470
}
337471

338-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
472+
// SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
473+
// `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
474+
// is safe to access.
475+
unsafe { _add_negative_sign(is_nonnegative, buf, curr); }
476+
477+
// SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339478
let buf_slice = unsafe {
340479
str::from_utf8_unchecked(
341480
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
342481
};
343-
f.pad_integral(is_nonnegative, "", buf_slice)
482+
483+
buf_slice
344484
}
345485
};
346486
}
@@ -529,7 +669,7 @@ mod imp {
529669
i32, u32,
530670
i64, u64,
531671
isize, usize,
532-
; as u64 via to_u64 named fmt_u64
672+
; as u64 via to_u64 named stringify_u64
533673
);
534674
impl_Exp!(
535675
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
@@ -545,10 +685,10 @@ mod imp {
545685
i16, u16,
546686
i32, u32,
547687
isize, usize,
548-
; as u32 via to_u32 named fmt_u32);
688+
; as u32 via to_u32 named stringify_u32);
549689
impl_Display!(
550690
i64, u64,
551-
; as u64 via to_u64 named fmt_u64);
691+
; as u64 via to_u64 named stringify_u64);
552692

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

0 commit comments

Comments
 (0)