Skip to content

Commit a6e14a3

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

File tree

5 files changed

+218
-301
lines changed

5 files changed

+218
-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: 190 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,49 @@ 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+
// 3. `buffer.contents` being initialized from `offset` onwards till the end
209+
unsafe fn _extract_str_from_buf(buf: &NumBuffer, offset: usize) -> &str {
210+
// SAFETY: safety is ensured by the caller about:
211+
// 1. `offset` being bound checked
212+
// 2. `buffer.contents` being initialized from `offset` onwards
213+
let written = unsafe { buf.contents.get_unchecked(offset..) };
214+
215+
// SAFETY: safety is ensured by the caller about:
216+
// 1. The contents of `buf` containing only ASCII characters
217+
let as_str = unsafe {
218+
str::from_utf8_unchecked(slice::from_raw_parts(
219+
MaybeUninit::slice_as_ptr(written),
220+
written.len(),
221+
))
222+
};
223+
224+
as_str
225+
}
226+
227+
// SAFETY: safety is ensured by the caller about:
228+
// 1. `start_offset` being bound checked
229+
unsafe fn _add_negative_sign(
230+
is_nonnegative: bool,
231+
buf: &mut NumBuffer,
232+
start_offset: usize,
233+
) -> usize {
234+
if is_nonnegative {
235+
return start_offset;
236+
}
237+
238+
let offset = start_offset - 1;
239+
240+
// Setting sign for the negative number
241+
buf.contents[offset].write(NEGATIVE_SIGN[0]);
242+
243+
offset
244+
}
245+
202246
macro_rules! impl_Display {
203247
($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
204248

@@ -212,7 +256,9 @@ macro_rules! impl_Display {
212256
}
213257
#[cfg(feature = "optimize_for_size")]
214258
{
215-
$gen_name(self.$conv_fn(), true, f)
259+
let mut buf = NumBuffer::new();
260+
let as_str = $gen_name(self.$conv_fn(), true, &mut buf);
261+
f.pad_integral(true, "", as_str)
216262
}
217263
}
218264
}
@@ -226,31 +272,122 @@ macro_rules! impl_Display {
226272
}
227273
#[cfg(feature = "optimize_for_size")]
228274
{
229-
return $gen_name(self.unsigned_abs().$conv_fn(), *self >= 0, f);
275+
let mut buf = NumBuffer::new();
276+
277+
// not setting the sign here, hence sending in `true`
278+
let as_str = $gen_name(self.unsigned_abs().$conv_fn(), true, &mut buf);
279+
f.pad_integral(*self >= 0, "", as_str)
230280
}
231281
}
232282
}
233283

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

245381
// Format per four digits from the lookup table.
246382
// Four digits need a 16-bit $unsigned or wider.
247383
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
384+
// SAFETY: All of the decimals fit in `buf` since `buf` is large enough to
385+
// accommodate the largest representation of a number possible (that of i128::MIN)
249386
// and the while condition ensures at least 4 more decimals.
250387
unsafe { core::hint::assert_unchecked(offset >= 4) }
251-
// SAFETY: The offset counts down from its initial buf.len()
388+
// SAFETY: The offset counts down from its initial size
252389
// without underflow due to the previous precondition.
253-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
390+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
254391
offset -= 4;
255392

256393
// pull two pairs
@@ -259,69 +396,74 @@ macro_rules! impl_Display {
259396
remain /= scale;
260397
let pair1 = (quad / 100) as usize;
261398
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]);
399+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
400+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
401+
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
402+
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
266403
}
267404

268405
// Format per two digits from the lookup table.
269406
if remain > 9 {
270-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
407+
// SAFETY: All of the decimals fit in `buf` since `buf` is large enough to
408+
// accommodate the largest representation of a number possilble (that of i128::MIN)
271409
// and the while condition ensures at least 2 more decimals.
272410
unsafe { core::hint::assert_unchecked(offset >= 2) }
273-
// SAFETY: The offset counts down from its initial buf.len()
411+
// SAFETY: The offset counts down from its initial size
274412
// without underflow due to the previous precondition.
275-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
413+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
276414
offset -= 2;
277415

278416
let pair = (remain % 100) as usize;
279417
remain /= 100;
280-
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
281-
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
418+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
419+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
282420
}
283421

284422
// Format the last remaining digit, if any.
285423
if remain != 0 || self == 0 {
286-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
424+
// SAFETY: All of the decimals fit in `buf` since `buf` is large enough to
425+
// accommodate the largest representation of a number possilble (that of i128::MIN)
287426
// and the if condition ensures (at least) 1 more decimals.
288427
unsafe { core::hint::assert_unchecked(offset >= 1) }
289-
// SAFETY: The offset counts down from its initial buf.len()
428+
// SAFETY: The offset counts down from its initial size
290429
// without underflow due to the previous precondition.
291-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
430+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
292431
offset -= 1;
293432

294433
// Either the compiler sees that remain < 10, or it prevents
295434
// a boundary check up next.
296435
let last = (remain & 15) as usize;
297-
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
436+
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
298437
// not used: remain = 0;
299438
}
300439

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

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

321463
// 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
464+
// `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41`, and at
323465
// 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]`
466+
// non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325467
// is safe to access.
326468
unsafe {
327469
loop {
@@ -335,12 +477,18 @@ macro_rules! impl_Display {
335477
}
336478
}
337479

338-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
480+
// SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
481+
// `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
482+
// is safe to access.
483+
unsafe { _add_negative_sign(is_nonnegative, buf, curr); }
484+
485+
// SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339486
let buf_slice = unsafe {
340487
str::from_utf8_unchecked(
341488
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
342489
};
343-
f.pad_integral(is_nonnegative, "", buf_slice)
490+
491+
buf_slice
344492
}
345493
};
346494
}
@@ -529,7 +677,7 @@ mod imp {
529677
i32, u32,
530678
i64, u64,
531679
isize, usize,
532-
; as u64 via to_u64 named fmt_u64
680+
; as u64 via to_u64 named stringify_u64
533681
);
534682
impl_Exp!(
535683
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
@@ -545,10 +693,10 @@ mod imp {
545693
i16, u16,
546694
i32, u32,
547695
isize, usize,
548-
; as u32 via to_u32 named fmt_u32);
696+
; as u32 via to_u32 named stringify_u32);
549697
impl_Display!(
550698
i64, u64,
551-
; as u64 via to_u64 named fmt_u64);
699+
; as u64 via to_u64 named stringify_u64);
552700

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

0 commit comments

Comments
 (0)