Skip to content

Commit 888b4af

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

File tree

5 files changed

+216
-301
lines changed

5 files changed

+216
-301
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: 4 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,9 @@ impl From<rt::Alignment> for Option<Alignment> {
4647
}
4748
}
4849

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

library/core/src/fmt/num.rs

Lines changed: 188 additions & 42 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,47 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199200
6061626364656667686970717273747576777879\
200201
8081828384858687888990919293949596979899";
201202

203+
static NEGATIVE_SIGN: &[u8; 1] = b"-";
204+
205+
// SAFETY: safety is ensured by the caller about:
206+
// 1. The contents of `buf` containing only ASCII characters
207+
// 2. `offset` being bound checked
208+
unsafe fn _extract_str_from_buf(buf: &NumBuffer, offset: usize) -> &str {
209+
// SAFETY: safety is ensured by the caller about:
210+
// 1. `offset` being bound checked
211+
let written = unsafe { buf.contents.get_unchecked(offset..) };
212+
213+
// SAFETY: safety is ensured by the caller about:
214+
// 1. The contents of `buf` containing only ASCII characters
215+
let as_str = unsafe {
216+
str::from_utf8_unchecked(slice::from_raw_parts(
217+
MaybeUninit::slice_as_ptr(written),
218+
written.len(),
219+
))
220+
};
221+
222+
as_str
223+
}
224+
225+
// SAFETY: safety is ensured by the caller about:
226+
// 1. `start_offset` being bound checked
227+
unsafe fn _add_negative_sign(
228+
is_nonnegative: bool,
229+
buf: &mut NumBuffer,
230+
start_offset: usize,
231+
) -> usize {
232+
if is_nonnegative {
233+
return start_offset;
234+
}
235+
236+
let offset = start_offset - 1;
237+
238+
// Setting sign for the negative number
239+
buf.contents[offset].write(NEGATIVE_SIGN[0]);
240+
241+
offset
242+
}
243+
202244
macro_rules! impl_Display {
203245
($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
204246

@@ -212,7 +254,9 @@ macro_rules! impl_Display {
212254
}
213255
#[cfg(feature = "optimize_for_size")]
214256
{
215-
$gen_name(self.$conv_fn(), true, f)
257+
let mut buf = NumBuffer::new();
258+
let as_str = $gen_name(self.$conv_fn(), true, &mut buf);
259+
f.pad_integral(true, "", as_str)
216260
}
217261
}
218262
}
@@ -226,31 +270,122 @@ macro_rules! impl_Display {
226270
}
227271
#[cfg(feature = "optimize_for_size")]
228272
{
229-
return $gen_name(self.unsigned_abs().$conv_fn(), *self >= 0, f);
273+
let mut buf = NumBuffer::new();
274+
275+
// not setting the sign here, hence sending in `true`
276+
let as_str = $gen_name(self.unsigned_abs().$conv_fn(), true, &mut buf);
277+
f.pad_integral(*self >= 0, "", as_str)
230278
}
231279
}
232280
}
233281

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

245379
// Format per four digits from the lookup table.
246380
// Four digits need a 16-bit $unsigned or wider.
247381
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
382+
// SAFETY: All of the decimals fit in `buf` since `buf` is large enough to
383+
// accommodate the largest representation of a number possible (that of i128::MIN)
249384
// and the while condition ensures at least 4 more decimals.
250385
unsafe { core::hint::assert_unchecked(offset >= 4) }
251-
// SAFETY: The offset counts down from its initial buf.len()
386+
// SAFETY: The offset counts down from its initial size
252387
// without underflow due to the previous precondition.
253-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
388+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
254389
offset -= 4;
255390

256391
// pull two pairs
@@ -259,69 +394,74 @@ macro_rules! impl_Display {
259394
remain /= scale;
260395
let pair1 = (quad / 100) as usize;
261396
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]);
397+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
398+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
399+
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
400+
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
266401
}
267402

268403
// Format per two digits from the lookup table.
269404
if remain > 9 {
270-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
405+
// SAFETY: All of the decimals fit in `buf` since `buf` is large enough to
406+
// accommodate the largest representation of a number possilble (that of i128::MIN)
271407
// and the while condition ensures at least 2 more decimals.
272408
unsafe { core::hint::assert_unchecked(offset >= 2) }
273-
// SAFETY: The offset counts down from its initial buf.len()
409+
// SAFETY: The offset counts down from its initial size
274410
// without underflow due to the previous precondition.
275-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
411+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
276412
offset -= 2;
277413

278414
let pair = (remain % 100) as usize;
279415
remain /= 100;
280-
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
281-
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
416+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
417+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
282418
}
283419

284420
// Format the last remaining digit, if any.
285421
if remain != 0 || self == 0 {
286-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
422+
// SAFETY: All of the decimals fit in `buf` since `buf` is large enough to
423+
// accommodate the largest representation of a number possilble (that of i128::MIN)
287424
// and the if condition ensures (at least) 1 more decimals.
288425
unsafe { core::hint::assert_unchecked(offset >= 1) }
289-
// SAFETY: The offset counts down from its initial buf.len()
426+
// SAFETY: The offset counts down from its initial size
290427
// without underflow due to the previous precondition.
291-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
428+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
292429
offset -= 1;
293430

294431
// Either the compiler sees that remain < 10, or it prevents
295432
// a boundary check up next.
296433
let last = (remain & 15) as usize;
297-
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
434+
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
298435
// not used: remain = 0;
299436
}
300437

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.
438+
offset
439+
}
440+
441+
fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442+
// Buffer decimals for $unsigned with right alignment.
443+
let mut buf = NumBuffer::new();
444+
let offset = self._write_into_buf(&mut buf);
445+
446+
// SAFETY: All buf contents since offset is set, and
447+
// writes use ASCII from the lookup table exclusively.
304448
let as_str = unsafe {
305-
str::from_utf8_unchecked(slice::from_raw_parts(
306-
MaybeUninit::slice_as_ptr(written),
307-
written.len(),
308-
))
449+
_extract_str_from_buf(&buf, offset)
309450
};
451+
310452
f.pad_integral(is_nonnegative, "", as_str)
311453
}
312454
})*
313455

314456
#[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);
457+
fn $gen_name(mut n: $u, is_nonnegative: bool, buf: &mut NumBuffer) -> &str {
458+
let mut curr = buf.contents.len();
459+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf.contents);
320460

321461
// 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
462+
// `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41`, and at
323463
// 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]`
464+
// non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325465
// is safe to access.
326466
unsafe {
327467
loop {
@@ -335,12 +475,18 @@ macro_rules! impl_Display {
335475
}
336476
}
337477

338-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
478+
// SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
479+
// `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
480+
// is safe to access.
481+
unsafe { _add_negative_sign(is_nonnegative, buf, curr); }
482+
483+
// SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339484
let buf_slice = unsafe {
340485
str::from_utf8_unchecked(
341486
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
342487
};
343-
f.pad_integral(is_nonnegative, "", buf_slice)
488+
489+
buf_slice
344490
}
345491
};
346492
}
@@ -529,7 +675,7 @@ mod imp {
529675
i32, u32,
530676
i64, u64,
531677
isize, usize,
532-
; as u64 via to_u64 named fmt_u64
678+
; as u64 via to_u64 named stringify_u64
533679
);
534680
impl_Exp!(
535681
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
@@ -545,10 +691,10 @@ mod imp {
545691
i16, u16,
546692
i32, u32,
547693
isize, usize,
548-
; as u32 via to_u32 named fmt_u32);
694+
; as u32 via to_u32 named stringify_u32);
549695
impl_Display!(
550696
i64, u64,
551-
; as u64 via to_u64 named fmt_u64);
697+
; as u64 via to_u64 named stringify_u64);
552698

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

0 commit comments

Comments
 (0)