Skip to content

Commit 077102a

Browse files
Implement int_format_into feature
1 parent d00435f commit 077102a

File tree

3 files changed

+268
-43
lines changed

3 files changed

+268
-43
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: 205 additions & 43 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,18 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199200
6061626364656667686970717273747576777879\
200201
8081828384858687888990919293949596979899";
201202

203+
unsafe fn slice_buffer_to_str(buf: &[MaybeUninit<u8>], offset: usize) -> &str {
204+
// SAFETY: All buf content since offset is set.
205+
let written = unsafe { buf.get_unchecked(offset..) };
206+
// SAFETY: Writes use ASCII from the lookup table exclusively.
207+
unsafe {
208+
str::from_utf8_unchecked(slice::from_raw_parts(
209+
MaybeUninit::slice_as_ptr(written),
210+
written.len(),
211+
))
212+
}
213+
}
214+
202215
macro_rules! impl_Display {
203216
($($signed:ident, $unsigned:ident,)* ; as $u:ident via $conv_fn:ident named $gen_name:ident) => {
204217

@@ -248,6 +261,12 @@ macro_rules! impl_Display {
248261
issue = "none"
249262
)]
250263
pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::<u8>]) -> &'a str {
264+
let offset = self._fmt_inner(buf);
265+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
266+
unsafe { slice_buffer_to_str(buf, offset) }
267+
}
268+
269+
fn _fmt_inner(self, buf: &mut [MaybeUninit::<u8>]) -> usize {
251270
// Count the number of bytes in buf that are not initialized.
252271
let mut offset = buf.len();
253272
// Consume the least-significant decimals from a working copy.
@@ -309,24 +328,99 @@ macro_rules! impl_Display {
309328
// not used: remain = 0;
310329
}
311330

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

324421
#[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);
422+
fn _inner_slow_integer_to_str(mut n: $u, buf: &mut [MaybeUninit::<u8>]) -> usize {
423+
let mut curr = buf.len();
330424

331425
// SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
332426
// `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
@@ -336,21 +430,28 @@ macro_rules! impl_Display {
336430
unsafe {
337431
loop {
338432
curr -= 1;
339-
buf_ptr.add(curr).write((n % 10) as u8 + b'0');
433+
buf[curr].write((n % 10) as u8 + b'0');
340434
n /= 10;
341435

342436
if n == 0 {
343437
break;
344438
}
345439
}
346440
}
441+
cur
442+
}
347443

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-
};
353-
f.pad_integral(is_nonnegative, "", buf_slice)
444+
#[cfg(feature = "optimize_for_size")]
445+
fn $gen_name(n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446+
const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
447+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
448+
449+
let offset = _inner_slow_integer_to_str(n, &mut buf);
450+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
451+
unsafe {
452+
let buf_slice = slice_buffer_to_str(&buf, offset);
453+
f.pad_integral(is_nonnegative, "", buf_slice)
454+
}
354455
}
355456
};
356457
}
@@ -566,7 +667,7 @@ mod imp {
566667
impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
567668

568669
/// 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) {
670+
fn parse_u64_into(mut n: u64, buf: &mut [MaybeUninit<u8>], curr: &mut usize) {
570671
let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf);
571672
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
572673
assert!(*curr > 19);
@@ -673,40 +774,98 @@ impl fmt::Display for i128 {
673774
}
674775
}
675776

777+
impl u128 {
778+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
779+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
780+
///
781+
/// # Examples
782+
///
783+
/// ```
784+
/// #![feature(int_format_into)]
785+
/// use core::fmt::NumBuffer;
786+
///
787+
/// let n = 0u128;
788+
/// let mut buf = NumBuffer::new();
789+
/// assert_eq!(n.format_into(&mut buf), "0");
790+
///
791+
/// let n1 = 32u128;
792+
/// let mut buf1 = NumBuffer::new();
793+
/// assert_eq!(n1.format_into(&mut buf1), "32");
794+
///
795+
/// let n2 = u128::MAX;
796+
/// let mut buf2 = NumBuffer::new();
797+
/// assert_eq!(n2.format_into(&mut buf2), u128::MAX.to_string());
798+
/// ```
799+
#[unstable(feature = "int_format_into", issue = "138215")]
800+
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
801+
let offset = fmt_u128_inner(self, &mut buf.buf);
802+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
803+
unsafe { slice_buffer_to_str(&buf.buf, offset) }
804+
}
805+
}
806+
807+
impl i128 {
808+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
809+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
810+
///
811+
/// # Examples
812+
///
813+
/// ```
814+
/// #![feature(int_format_into)]
815+
/// use core::fmt::NumBuffer;
816+
///
817+
/// let n = 0i128;
818+
/// let mut buf = NumBuffer::new();
819+
/// assert_eq!(n.format_into(&mut buf), "0");
820+
///
821+
/// let n1 = 32i128;
822+
/// let mut buf1 = NumBuffer::new();
823+
/// assert_eq!(n1.format_into(&mut buf1), "32");
824+
///
825+
/// let n2 = i128::MAX;
826+
/// let mut buf2 = NumBuffer::new();
827+
/// assert_eq!(n2.format_into(&mut buf2), i128::MAX.to_string());
828+
/// ```
829+
#[unstable(feature = "int_format_into", issue = "138215")]
830+
pub fn format_into(self, buf: &mut NumBuffer<Self>) -> &str {
831+
let mut offset = fmt_u128_inner(self.unsigned_abs(), &mut buf.buf);
832+
// Only difference between signed and unsigned are these 4 lines.
833+
if self < 0 {
834+
offset -= 1;
835+
buf.buf[offset].write(b'-');
836+
}
837+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
838+
unsafe { slice_buffer_to_str(&buf.buf, offset) }
839+
}
840+
}
841+
676842
/// Specialized optimization for u128. Instead of taking two items at a time, it splits
677843
/// into at most 2 u64s, and then chunks by 10e16, 10e8, 10e4, 10e2, and then 10e1.
678844
/// It also has to handle 1 last item, as 10^40 > 2^128 > 10^39, whereas
679845
/// 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];
846+
fn fmt_u128_inner(n: u128, buf: &mut [MaybeUninit<u8>]) -> usize {
683847
let mut curr = buf.len();
684-
685848
let (n, rem) = udiv_1e19(n);
686-
parse_u64_into(rem, &mut buf, &mut curr);
849+
parse_u64_into(rem, buf, &mut curr);
687850

688851
if n != 0 {
689852
// 0 pad up to point
690853
let target = buf.len() - 19;
691854
// SAFETY: Guaranteed that we wrote at most 19 bytes, and there must be space
692855
// remaining since it has length 39
693856
unsafe {
694-
ptr::write_bytes(
695-
MaybeUninit::slice_as_mut_ptr(&mut buf).add(target),
696-
b'0',
697-
curr - target,
698-
);
857+
ptr::write_bytes(MaybeUninit::slice_as_mut_ptr(buf).add(target), b'0', curr - target);
699858
}
700859
curr = target;
701860

702861
let (n, rem) = udiv_1e19(n);
703-
parse_u64_into(rem, &mut buf, &mut curr);
862+
parse_u64_into(rem, buf, &mut curr);
704863
// Should this following branch be annotated with unlikely?
705864
if n != 0 {
706865
let target = buf.len() - 38;
707866
// The raw `buf_ptr` pointer is only valid until `buf` is used the next time,
708867
// buf `buf` is not used in this scope so we are good.
709-
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
868+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf);
710869
// SAFETY: At this point we wrote at most 38 bytes, pad up to that point,
711870
// There can only be at most 1 digit remaining.
712871
unsafe {
@@ -716,16 +875,19 @@ fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::R
716875
}
717876
}
718877
}
878+
curr
879+
}
719880

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-
};
728-
f.pad_integral(is_nonnegative, "", buf_slice)
881+
fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
882+
// 2^128 is about 3*10^38, so 39 gives an extra byte of space
883+
let mut buf = [MaybeUninit::<u8>::uninit(); 39];
884+
885+
let offset = fmt_u128_inner(n, &mut buf);
886+
// SAFETY: Starting from `offset`, all elements of the slice have been set.
887+
unsafe {
888+
let buf_slice = slice_buffer_to_str(&buf, offset);
889+
f.pad_integral(is_nonnegative, "", buf_slice)
890+
}
729891
}
730892

731893
/// Partition of `n` into n > 1e19 and rem <= 1e19

0 commit comments

Comments
 (0)