Skip to content

Commit 3435404

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

File tree

5 files changed

+210
-299
lines changed

5 files changed

+210
-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;
@@ -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: 183 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,45 @@ 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 = unsafe { buf.contents.get_unchecked(offset..) };
213+
let as_str = unsafe {
214+
str::from_utf8_unchecked(slice::from_raw_parts(
215+
MaybeUninit::slice_as_ptr(written),
216+
written.len(),
217+
))
218+
};
219+
220+
as_str
221+
}
222+
223+
// Unsafe because
224+
// 1. Offset is not bound checked
225+
unsafe fn _add_negative_sign(
226+
is_nonnegative: bool,
227+
buf: &mut NumBuffer,
228+
start_offset: usize
229+
) -> usize {
230+
if is_nonnegative {
231+
return start_offset;
232+
}
233+
234+
let offset = start_offset - 1;
235+
236+
// Setting sign for the negative number
237+
buf.contents[offset].write(NEGATIVE_SIGN[0]);
238+
239+
offset
240+
}
241+
202242
macro_rules! impl_Display {
203243
($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
204244

@@ -212,7 +252,9 @@ macro_rules! impl_Display {
212252
}
213253
#[cfg(feature = "optimize_for_size")]
214254
{
215-
$gen_name(self.$conv_fn(), true, f)
255+
let mut buf = NumBuffer::new();
256+
let as_str = $gen_name(self.$conv_fn(), true, &mut buf);
257+
f.pad_integral(true, "", as_str)
216258
}
217259
}
218260
}
@@ -226,31 +268,122 @@ macro_rules! impl_Display {
226268
}
227269
#[cfg(feature = "optimize_for_size")]
228270
{
229-
return $gen_name(self.unsigned_abs().$conv_fn(), *self >= 0, f);
271+
let mut buf = NumBuffer::new();
272+
273+
// not setting the sign here, hence sending in `true`
274+
let as_str = $gen_name(self.unsigned_abs().$conv_fn(), true, &mut buf);
275+
f.pad_integral(*self >= 0, "", as_str)
230276
}
231277
}
232278
}
233279

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

245377
// Format per four digits from the lookup table.
246378
// Four digits need a 16-bit $unsigned or wider.
247379
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
380+
// SAFETY: All of the decimals fit in buf since buf is large enough to
381+
// accommodate the largest representation of a number possible (that of i128::MIN)
249382
// and the while condition ensures at least 4 more decimals.
250383
unsafe { core::hint::assert_unchecked(offset >= 4) }
251-
// SAFETY: The offset counts down from its initial buf.len()
384+
// SAFETY: The offset counts down from its initial size
252385
// without underflow due to the previous precondition.
253-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
386+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
254387
offset -= 4;
255388

256389
// pull two pairs
@@ -259,26 +392,27 @@ macro_rules! impl_Display {
259392
remain /= scale;
260393
let pair1 = (quad / 100) as usize;
261394
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]);
395+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
396+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
397+
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
398+
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
266399
}
267400

268401
// Format per two digits from the lookup table.
269402
if remain > 9 {
270-
// SAFETY: All of the decimals fit in buf due to MAX_DEC_N
403+
// SAFETY: All of the decimals fit in buf since buf is large enough to
404+
// accommodate the largest representation of a number possilble (that of i128::MIN)
271405
// and the while condition ensures at least 2 more decimals.
272406
unsafe { core::hint::assert_unchecked(offset >= 2) }
273-
// SAFETY: The offset counts down from its initial buf.len()
407+
// SAFETY: The offset counts down from its initial size
274408
// without underflow due to the previous precondition.
275-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
409+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
276410
offset -= 2;
277411

278412
let pair = (remain % 100) as usize;
279413
remain /= 100;
280-
buf[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
281-
buf[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
414+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
415+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
282416
}
283417

284418
// Format the last remaining digit, if any.
@@ -288,40 +422,43 @@ macro_rules! impl_Display {
288422
unsafe { core::hint::assert_unchecked(offset >= 1) }
289423
// SAFETY: The offset counts down from its initial buf.len()
290424
// without underflow due to the previous precondition.
291-
unsafe { core::hint::assert_unchecked(offset <= buf.len()) }
425+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
292426
offset -= 1;
293427

294428
// Either the compiler sees that remain < 10, or it prevents
295429
// a boundary check up next.
296430
let last = (remain & 15) as usize;
297-
buf[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
431+
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
298432
// not used: remain = 0;
299433
}
300434

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

314453
#[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);
454+
fn $gen_name(mut n: $u, is_nonnegative: bool, buf: &mut NumBuffer) -> &str {
455+
let mut curr = buf.contents.len();
456+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf.contents);
320457

321458
// 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
459+
// `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41`, and at
323460
// 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]`
461+
// non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325462
// is safe to access.
326463
unsafe {
327464
loop {
@@ -335,12 +472,18 @@ macro_rules! impl_Display {
335472
}
336473
}
337474

338-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
475+
// SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
476+
// `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
477+
// is safe to access.
478+
unsafe { _add_negative_sign(is_nonnegative, buf, curr); }
479+
480+
// SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339481
let buf_slice = unsafe {
340482
str::from_utf8_unchecked(
341483
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
342484
};
343-
f.pad_integral(is_nonnegative, "", buf_slice)
485+
486+
buf_slice
344487
}
345488
};
346489
}
@@ -529,7 +672,7 @@ mod imp {
529672
i32, u32,
530673
i64, u64,
531674
isize, usize,
532-
; as u64 via to_u64 named fmt_u64
675+
; as u64 via to_u64 named stringify_u64
533676
);
534677
impl_Exp!(
535678
i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
@@ -545,10 +688,10 @@ mod imp {
545688
i16, u16,
546689
i32, u32,
547690
isize, usize,
548-
; as u32 via to_u32 named fmt_u32);
691+
; as u32 via to_u32 named stringify_u32);
549692
impl_Display!(
550693
i64, u64,
551-
; as u64 via to_u64 named fmt_u64);
694+
; as u64 via to_u64 named stringify_u64);
552695

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

0 commit comments

Comments
 (0)