Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a8968ea

Browse files
committedMar 11, 2025
Added functionality for int_format_into
1 parent 2c6a12e commit a8968ea

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed
 

‎library/core/src/num/int_format.rs

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
use crate::mem::MaybeUninit;
2+
3+
/// A minimal buffer implementation containing elements of type
4+
/// `MaybeUninit::<u8>`.
5+
pub struct NumBuffer<const N: usize> {
6+
pub contents: [MaybeUninit::<u8>; N]
7+
}
8+
9+
impl<const N: usize> NumBuffer<N> {
10+
fn new() -> NumBuffer<N> {
11+
NumBuffer {
12+
contents: [MaybeUninit::<u8>::uninit(); N]
13+
}
14+
}
15+
}
16+
17+
// 2 digit decimal look up table
18+
const DEC_DIGITS_LUT: &[u8; 200] = b"\
19+
0001020304050607080910111213141516171819\
20+
2021222324252627282930313233343536373839\
21+
4041424344454647484950515253545556575859\
22+
6061626364656667686970717273747576777879\
23+
8081828384858687888990919293949596979899";
24+
25+
const NEGATIVE_SIGN: &[u8; 1] = b"-";
26+
27+
macro_rules! int_impl_format_into {
28+
($($T:ident)*) => {
29+
$(
30+
#[unstable(feature = "int_format_into", issue = "138215")]
31+
impl $T {
32+
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of
33+
/// type [`NumBuffer`] that is passed by the caller by mutable reference.
34+
///
35+
/// This function panics if `buf` does not have enough size to store
36+
/// the signed decimal version of the number.
37+
///
38+
/// # Examples
39+
/// ```
40+
#[doc = concat!("let n = 32", stringify!($T), ";")]
41+
/// let mut buf = NumBuffer::<3>::new();
42+
///
43+
/// assert_eq!(n.format_into(&mut buf), "32");
44+
/// ```
45+
///
46+
fn format_into(self, buf: &mut $crate::NumBuffer) -> &crate::primitive::str {
47+
// counting space for negative sign too, if `self` is negative
48+
let sign_offset = if self < 0 {1} else {0};
49+
let decimal_string_size: usize = self.ilog(10) as usize + 1 + sign_offset;
50+
51+
// `buf` must have minimum size to store the decimal string version.
52+
if buf.contents.len() < decimal_string_size {
53+
panic!("Not enough buffer size to format into!");
54+
}
55+
56+
// Count the number of bytes in `buf` that are not initialized.
57+
let mut offset = buf.contents.len();
58+
// Consume the least-significant decimals from a working copy.
59+
let mut remain = self;
60+
61+
// Format per four digits from the lookup table.
62+
// Four digits need a 16-bit $unsigned or wider.
63+
while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") {
64+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
65+
// and the while condition ensures at least 4 more decimals.
66+
unsafe { core::hint::assert_unchecked(offset >= 4) }
67+
// SAFETY: The offset counts down from its initial buf.contents.len()
68+
// without underflow due to the previous precondition.
69+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
70+
offset -= 4;
71+
72+
// pull two pairs
73+
let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
74+
let quad = remain % scale;
75+
remain /= scale;
76+
let pair1 = (quad / 100) as usize;
77+
let pair2 = (quad % 100) as usize;
78+
buf.contents[offset + 0].write($crate::DEC_DIGITS_LUT[pair1 * 2 + 0]);
79+
buf.contents[offset + 1].write($crate::DEC_DIGITS_LUT[pair1 * 2 + 1]);
80+
buf.contents[offset + 2].write($crate::DEC_DIGITS_LUT[pair2 * 2 + 0]);
81+
buf.contents[offset + 3].write($crate::DEC_DIGITS_LUT[pair2 * 2 + 1]);
82+
}
83+
84+
// Format per two digits from the lookup table.
85+
if remain > 9 {
86+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
87+
// and the while condition ensures at least 2 more decimals.
88+
unsafe { core::hint::assert_unchecked(offset >= 2) }
89+
// SAFETY: The offset counts down from its initial buf.contents.len()
90+
// without underflow due to the previous precondition.
91+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
92+
offset -= 2;
93+
94+
let pair = (remain % 100) as usize;
95+
remain /= 100;
96+
buf.contents[offset + 0].write($crate::DEC_DIGITS_LUT[pair * 2 + 0]);
97+
buf.contents[offset + 1].write($crate::DEC_DIGITS_LUT[pair * 2 + 1]);
98+
}
99+
100+
// Format the last remaining digit, if any.
101+
if remain != 0 || self == 0 {
102+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
103+
// and the if condition ensures (at least) 1 more decimals.
104+
unsafe { core::hint::assert_unchecked(offset >= 1) }
105+
// SAFETY: The offset counts down from its initial buf.contents.len()
106+
// without underflow due to the previous precondition.
107+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
108+
offset -= 1;
109+
110+
// Either the compiler sees that remain < 10, or it prevents
111+
// a boundary check up next.
112+
let last = (remain & 15) as usize;
113+
buf.contents[offset].write($crate::DEC_DIGITS_LUT[last * 2 + 1]);
114+
// not used: remain = 0;
115+
}
116+
117+
if self < 0 {
118+
// SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked
119+
// and the if condition ensures (at least) that the sign can be added.
120+
unsafe { core::hint::assert_unchecked(offset >= 1) }
121+
122+
// SAFETY: The offset counts down from its initial buf.contents.len()
123+
// without underflow due to the previous precondition.
124+
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) }
125+
126+
// Setting sign for the negative number
127+
offset -= 1;
128+
buf.contents[offset].write($crate::NEGATIVE_SIGN[0])
129+
}
130+
131+
// SAFETY: All buf content since offset is set.
132+
let written = unsafe { buf.contents.get_unchecked(offset..) };
133+
134+
// SAFETY: Writes use ASCII from the lookup table
135+
// (and `NEGATIVE_SIGN` in case of negative numbers) exclusively.
136+
let as_str = unsafe {
137+
crate::primitive::str::from_utf8_unchecked(crate::slice::from_raw_parts(
138+
crate::mem::MaybeUninit::slice_as_ptr(written),
139+
written.len(),
140+
))
141+
};
142+
as_str
143+
}
144+
}
145+
)*
146+
};
147+
}

‎library/core/src/num/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ pub mod fmt;
4242
mod int_macros; // import int_impl!
4343
#[macro_use]
4444
mod uint_macros; // import uint_impl!
45+
#[macro_use]
46+
mod int_format; // import int_impl_format_into!
4547

4648
mod error;
4749
mod int_log10;
@@ -1602,3 +1604,6 @@ macro_rules! from_str_int_impl {
16021604

16031605
from_str_int_impl! { signed isize i8 i16 i32 i64 i128 }
16041606
from_str_int_impl! { unsigned usize u8 u16 u32 u64 u128 }
1607+
1608+
int_impl_format_into! { i8 i16 i32 i64 i128 isize }
1609+
int_impl_format_into! { u8 u16 u32 u64 u128 usize }

0 commit comments

Comments
 (0)
Failed to load comments.