Skip to content

Commit cb48673

Browse files
Implement int_format_into feature
1 parent d00435f commit cb48673

File tree

3 files changed

+263
-42
lines changed

3 files changed

+263
-42
lines changed

library/core/src/fmt/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod float;
1515
#[cfg(no_fp_fmt_parse)]
1616
mod nofloat;
1717
mod num;
18+
mod num_buffer;
1819
mod rt;
1920

2021
#[stable(feature = "fmt_flags_align", since = "1.28.0")]
@@ -33,6 +34,9 @@ pub enum Alignment {
3334
Center,
3435
}
3536

37+
#[unstable(feature = "int_format_into", issue = "138215")]
38+
pub use num_buffer::{NumBuffer, NumBufferTrait};
39+
3640
#[stable(feature = "debug_builders", since = "1.2.0")]
3741
pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
3842
#[unstable(feature = "debug_closure_helpers", issue = "117729")]

library/core/src/fmt/num.rs

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

203+
/// This function converts a slice of ascii characters into a `&str` starting from `offset`.
204+
///
205+
/// Safety notes: `buf` content starting from `offset` index MUST BE initialized.
206+
unsafe fn slice_buffer_to_str(buf: &[MaybeUninit<u8>], offset: usize) -> &str {
207+
// SAFETY: All buf content since offset is set.
208+
let written = unsafe { buf.get_unchecked(offset..) };
209+
// SAFETY: Writes use ASCII from the lookup table exclusively.
210+
unsafe { str::from_utf8_unchecked(written.assume_init_ref()) }
211+
}
212+
202213
macro_rules! impl_Display {
203214
($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
204215

@@ -248,6 +259,12 @@ macro_rules! impl_Display {
248259
issue = "none"
249260
)]
250261
pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::<u8>]) -> &'a str {
262+
let offset = self._fmt_inner(buf);
263+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
264+
unsafe { slice_buffer_to_str(buf, offset) }
265+
}
266+
267+
fn _fmt_inner(self, buf: &mut [MaybeUninit::<u8>]) -> usize {
251268
// Count the number of bytes in buf that are not initialized.
252269
let mut offset = buf.len();
253270
// Consume the least-significant decimals from a working copy.
@@ -309,24 +326,99 @@ macro_rules! impl_Display {
309326
// not used: remain = 0;
310327
}
311328

312-
// SAFETY: All buf content since offset is set.
313-
let written = unsafe { buf.get_unchecked(offset..) };
314-
// SAFETY: Writes use ASCII from the lookup table exclusively.
315-
unsafe {
316-
str::from_utf8_unchecked(slice::from_raw_parts(
317-
MaybeUninit::slice_as_ptr(written),
318-
written.len(),
319-
))
329+
offset
330+
}
331+
}
332+
333+
impl $signed {
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+
/// ```
340+
/// #![feature(int_format_into)]
341+
/// use core::fmt::NumBuffer;
342+
///
343+
#[doc = concat!("let n = 0", stringify!($signed), ";")]
344+
/// let mut buf = NumBuffer::new();
345+
/// assert_eq!(n.format_into(&mut buf), "0");
346+
///
347+
#[doc = concat!("let n1 = 32", stringify!($unsigned), ";")]
348+
/// let mut buf1 = NumBuffer::new();
349+
/// assert_eq!(n1.format_into(&mut buf1), "32");
350+
///
351+
#[doc = concat!("let n2 = ", stringify!($unsigned::MAX), ";")]
352+
/// let mut buf2 = NumBuffer::new();
353+
#[doc = concat!("assert_eq!(n2.format_into(&mut buf2), ", stringify!($unsigned::MAX), ".to_string());")]
354+
/// ```
355+
#[unstable(feature = "int_format_into", issue = "138215")]
356+
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
357+
let mut offset;
358+
359+
#[cfg(not(feature = "optimize_for_size"))]
360+
{
361+
offset = self.unsigned_abs()._fmt_inner(&mut buf.buf);
320362
}
363+
#[cfg(feature = "optimize_for_size")]
364+
{
365+
offset = _inner_slow_integer_to_str(self.unsigned_abs().$conv_fn(), &mut buf.buf);
366+
}
367+
// Only difference between signed and unsigned are these 4 lines.
368+
if self < 0 {
369+
offset -= 1;
370+
buf.buf[offset].write(b'-');
371+
}
372+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
373+
unsafe { slice_buffer_to_str(&buf.buf, offset) }
321374
}
322-
})*
375+
}
376+
377+
impl $unsigned {
378+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
379+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
380+
///
381+
/// # Examples
382+
///
383+
/// ```
384+
/// #![feature(int_format_into)]
385+
/// use core::fmt::NumBuffer;
386+
///
387+
#[doc = concat!("let n = 0", stringify!($signed), ";")]
388+
/// let mut buf = NumBuffer::new();
389+
/// assert_eq!(n.format_into(&mut buf), "0");
390+
///
391+
#[doc = concat!("let n1 = 32", stringify!($unsigned), ";")]
392+
/// let mut buf1 = NumBuffer::new();
393+
/// assert_eq!(n1.format_into(&mut buf1), "32");
394+
///
395+
#[doc = concat!("let n2 = ", stringify!($unsigned::MAX), ";")]
396+
/// let mut buf2 = NumBuffer::new();
397+
#[doc = concat!("assert_eq!(n2.format_into(&mut buf2), ", stringify!($unsigned::MAX), ".to_string());")]
398+
/// ```
399+
#[unstable(feature = "int_format_into", issue = "138215")]
400+
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
401+
let offset;
402+
403+
#[cfg(not(feature = "optimize_for_size"))]
404+
{
405+
offset = self._fmt_inner(&mut buf.buf);
406+
}
407+
#[cfg(feature = "optimize_for_size")]
408+
{
409+
offset = _inner_slow_integer_to_str(self.$conv_fn(), &mut buf.buf);
410+
}
411+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
412+
unsafe { slice_buffer_to_str(&buf.buf, offset) }
413+
}
414+
}
415+
416+
417+
)*
323418

324419
#[cfg(feature = "optimize_for_size")]
325-
fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326-
const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
327-
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
328-
let mut curr = MAX_DEC_N;
329-
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
420+
fn _inner_slow_integer_to_str(mut n: $u, buf: &mut [MaybeUninit::<u8>]) -> usize {
421+
let mut curr = buf.len();
330422

331423
// SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
332424
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
@@ -336,20 +428,25 @@ macro_rules! impl_Display {
336428
unsafe {
337429
loop {
338430
curr -= 1;
339-
buf_ptr.add(curr).write((n % 10) as u8 + b'0');
431+
buf[curr].write((n % 10) as u8 + b'0');
340432
n /= 10;
341433

342434
if n == 0 {
343435
break;
344436
}
345437
}
346438
}
439+
cur
440+
}
347441

348-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
349-
let buf_slice = unsafe {
350-
str::from_utf8_unchecked(
351-
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
352-
};
442+
#[cfg(feature = "optimize_for_size")]
443+
fn $gen_name(n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444+
const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
445+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
446+
447+
let offset = _inner_slow_integer_to_str(n, &mut buf);
448+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
449+
let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) };
353450
f.pad_integral(is_nonnegative, "", buf_slice)
354451
}
355452
};
@@ -566,7 +663,7 @@ mod imp {
566663
impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
567664

568665
/// Helper function for writing a u64 into `buf` going from last to first, with `curr`.
569-
fn parse_u64_into<const N: usize>(mut n: u64, buf: &mut [MaybeUninit<u8>; N], curr: &mut usize) {
666+
fn parse_u64_into(mut n: u64, buf: &mut [MaybeUninit<u8>], curr: &mut usize) {
570667
let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf);
571668
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
572669
assert!(*curr > 19);
@@ -673,40 +770,100 @@ impl fmt::Display for i128 {
673770
}
674771
}
675772

773+
impl u128 {
774+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
775+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
776+
///
777+
/// # Examples
778+
///
779+
/// ```
780+
/// #![feature(int_format_into)]
781+
/// use core::fmt::NumBuffer;
782+
///
783+
/// let n = 0u128;
784+
/// let mut buf = NumBuffer::new();
785+
/// assert_eq!(n.format_into(&mut buf), "0");
786+
///
787+
/// let n1 = 32u128;
788+
/// let mut buf1 = NumBuffer::new();
789+
/// assert_eq!(n1.format_into(&mut buf1), "32");
790+
///
791+
/// let n2 = u128::MAX;
792+
/// let mut buf2 = NumBuffer::new();
793+
/// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
794+
/// ```
795+
#[unstable(feature = "int_format_into", issue = "138215")]
796+
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
797+
let offset = fmt_u128_inner(self, &mut buf.buf);
798+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
799+
unsafe { slice_buffer_to_str(&buf.buf, offset) }
800+
}
801+
}
802+
803+
impl i128 {
804+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
805+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
806+
///
807+
/// # Examples
808+
///
809+
/// ```
810+
/// #![feature(int_format_into)]
811+
/// use core::fmt::NumBuffer;
812+
///
813+
/// let n = 0i128;
814+
/// let mut buf = NumBuffer::new();
815+
/// assert_eq!(n.format_into(&mut buf), "0");
816+
///
817+
/// let n1 = 32i128;
818+
/// let mut buf1 = NumBuffer::new();
819+
/// assert_eq!(n1.format_into(&mut buf1), "32");
820+
///
821+
/// let n2 = i128::MAX;
822+
/// let mut buf2 = NumBuffer::new();
823+
/// assert_eq!(n2.format_into(&mut buf2), i128::MAX.to_string());
824+
/// ```
825+
#[unstable(feature = "int_format_into", issue = "138215")]
826+
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
827+
let mut offset = fmt_u128_inner(self.unsigned_abs(), &mut buf.buf);
828+
// Only difference between signed and unsigned are these 4 lines.
829+
if self < 0 {
830+
offset -= 1;
831+
buf.buf[offset].write(b'-');
832+
}
833+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
834+
unsafe { slice_buffer_to_str(&buf.buf, offset) }
835+
}
836+
}
837+
676838
/// Specialized optimization for u128. Instead of taking two items at a time, it splits
677839
/// into at most 2 u64s, and then chunks by 10e16, 10e8, 10e4, 10e2, and then 10e1.
678840
/// It also has to handle 1 last item, as 10^40 > 2^128 > 10^39, whereas
679841
/// 10^20 > 2^64 > 10^19.
680-
fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681-
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
682-
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
842+
///
843+
/// IMPORTANT: `buf` length MUST BE at least 39.
844+
fn fmt_u128_inner(n: u128, buf: &mut [MaybeUninit<u8>]) -> usize {
683845
let mut curr = buf.len();
684-
685846
let (n, rem) = udiv_1e19(n);
686-
parse_u64_into(rem, &mut buf, &mut curr);
847+
parse_u64_into(rem, buf, &mut curr);
687848

688849
if n != 0 {
689850
// 0 pad up to point
690851
let target = buf.len() - 19;
691852
// SAFETY: Guaranteed that we wrote at most 19 bytes, and there must be space
692-
// remaining since it has length 39
853+
// remaining since it has length of at least 39
693854
unsafe {
694-
ptr::write_bytes(
695-
MaybeUninit::slice_as_mut_ptr(&mut buf).add(target),
696-
b'0',
697-
curr - target,
698-
);
855+
ptr::write_bytes(MaybeUninit::slice_as_mut_ptr(buf).add(target), b'0', curr - target);
699856
}
700857
curr = target;
701858

702859
let (n, rem) = udiv_1e19(n);
703-
parse_u64_into(rem, &mut buf, &mut curr);
860+
parse_u64_into(rem, buf, &mut curr);
704861
// Should this following branch be annotated with unlikely?
705862
if n != 0 {
706863
let target = buf.len() - 38;
707864
// The raw `buf_ptr` pointer is only valid until `buf` is used the next time,
708865
// buf `buf` is not used in this scope so we are good.
709-
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
866+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf);
710867
// SAFETY: At this point we wrote at most 38 bytes, pad up to that point,
711868
// There can only be at most 1 digit remaining.
712869
unsafe {
@@ -716,15 +873,16 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R
716873
}
717874
}
718875
}
876+
curr
877+
}
719878

720-
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
721-
// UTF-8 since `DEC_DIGITS_LUT` is
722-
let buf_slice = unsafe {
723-
str::from_utf8_unchecked(slice::from_raw_parts(
724-
MaybeUninit::slice_as_mut_ptr(&mut buf).add(curr),
725-
buf.len() - curr,
726-
))
727-
};
879+
fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
880+
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
881+
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
882+
883+
let offset = fmt_u128_inner(n, &mut buf);
884+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
885+
let buf_slice = unsafe { slice_buffer_to_str(&buf, offset) };
728886
f.pad_integral(is_nonnegative, "", buf_slice)
729887
}
730888

0 commit comments

Comments
 (0)