Skip to content

Commit 8abc14d

Browse files
Destructured code for reusability
1 parent 040cedb commit 8abc14d

File tree

1 file changed

+145
-178
lines changed

1 file changed

+145
-178
lines changed

library/core/src/num/int_format.rs

Lines changed: 145 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use crate::mem::MaybeUninit;
2+
use crate::slice;
23

34
/// A minimal buffer implementation containing elements of type
45
/// `MaybeUninit<u8>`.
56
#[unstable(feature = "int_format_into", issue = "138215")]
67
#[derive(Debug)]
78
pub struct NumBuffer<const BUF_SIZE: usize> {
8-
/// A statically allocated array of elements of type
9-
/// `MaybeUninit::<u8>`.
9+
/// An array of elements of type `MaybeUninit<u8>`.
1010
///
1111
/// An alternative to `contents.len()` is `BUF_SIZE`.
1212
pub contents: [MaybeUninit<u8>; BUF_SIZE],
@@ -21,8 +21,131 @@ impl<const BUF_SIZE: usize> NumBuffer<BUF_SIZE> {
2121
}
2222
}
2323

24-
macro_rules! int_impl_format_into {
25-
($($T:ident)*) => {
24+
const DEC_DIGITS_LUT: &[u8; 200] = b"\
25+
0001020304050607080910111213141516171819\
26+
2021222324252627282930313233343536373839\
27+
4041424344454647484950515253545556575859\
28+
6061626364656667686970717273747576777879\
29+
8081828384858687888990919293949596979899";
30+
31+
const NEGATIVE_SIGN: &[u8; 1] = b"-";
32+
33+
#[inline]
34+
fn raw_write_sign_into<const BUF_SIZE: usize>(
35+
buf: &mut NumBuffer<BUF_SIZE>,
36+
start_offset: usize,
37+
) -> usize {
38+
let mut offset = start_offset;
39+
40+
// SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked
41+
// and the if condition ensures (at least) that the sign can be added.
42+
unsafe { core::hint::assert_unchecked(offset >= 1) }
43+
44+
// SAFETY: The offset counts down from its initial value BUF_SIZE
45+
// without underflow due to the previous precondition.
46+
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
47+
48+
// Setting sign for the negative number
49+
offset -= 1;
50+
buf.contents[offset].write(NEGATIVE_SIGN[0]);
51+
52+
offset
53+
}
54+
55+
#[inline]
56+
fn raw_extract_as_str<const BUF_SIZE: usize>(buf: &NumBuffer<BUF_SIZE>, offset: usize) -> &str {
57+
// SAFETY: All contents of `buf` since `offset` is set.
58+
let written = unsafe { buf.contents.get_unchecked(offset..) };
59+
60+
// SAFETY: Writes use ASCII from the lookup table
61+
// (and `NEGATIVE_SIGN` in case of negative numbers) exclusively.
62+
let as_str = unsafe {
63+
str::from_utf8_unchecked(slice::from_raw_parts(
64+
MaybeUninit::slice_as_ptr(written),
65+
written.len(),
66+
))
67+
};
68+
69+
as_str
70+
}
71+
72+
macro_rules! raw_write_digits_into {
73+
($T:ty, $BUF_SIZE:expr) => {
74+
|target_var: $T, buf: &mut NumBuffer<{ $BUF_SIZE }>, start_offset: usize| -> usize {
75+
let mut offset = start_offset;
76+
77+
// Consume the least-significant decimals from a working copy.
78+
let mut remain: $T = target_var;
79+
80+
// Format per four digits from the lookup table.
81+
// Four digits need a 16-bit $unsigned or wider.
82+
while size_of::<$T>() > 1
83+
&& remain
84+
> 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)")
85+
{
86+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
87+
// and the while condition ensures at least 4 more decimals.
88+
unsafe { core::hint::assert_unchecked(offset >= 4) }
89+
// SAFETY: The offset counts down from its initial value $BUF_SIZE
90+
// without underflow due to the previous precondition.
91+
unsafe { core::hint::assert_unchecked(offset <= $BUF_SIZE) }
92+
offset -= 4;
93+
94+
// pull two pairs
95+
let scale: $T = 1_00_00
96+
.try_into()
97+
.expect("branch is not hit for types that cannot fit 1E4 (u8)");
98+
99+
let quad = remain % scale;
100+
remain /= scale;
101+
let pair1 = (quad / 100) as usize;
102+
let pair2 = (quad % 100) as usize;
103+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
104+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
105+
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
106+
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
107+
}
108+
109+
// Format per two digits from the lookup table.
110+
if remain > 9 {
111+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
112+
// and the while condition ensures at least 2 more decimals.
113+
unsafe { core::hint::assert_unchecked(offset >= 2) }
114+
// SAFETY: The offset counts down from its initial value $BUF_SIZE
115+
// without underflow due to the previous precondition.
116+
unsafe { core::hint::assert_unchecked(offset <= $BUF_SIZE) }
117+
offset -= 2;
118+
119+
let pair = (remain % 100) as usize;
120+
remain /= 100;
121+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
122+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
123+
}
124+
125+
// Format the last remaining digit, if any.
126+
if remain != 0 || target_var == 0 {
127+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
128+
// and the if condition ensures (at least) 1 more decimals.
129+
unsafe { core::hint::assert_unchecked(offset >= 1) }
130+
// SAFETY: The offset counts down from its initial value $BUF_SIZE
131+
// without underflow due to the previous precondition.
132+
unsafe { core::hint::assert_unchecked(offset <= $BUF_SIZE) }
133+
offset -= 1;
134+
135+
// Either the compiler sees that remain < 10, or it prevents
136+
// a boundary check up next.
137+
let last = (remain & 15) as usize;
138+
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
139+
// not used: remain = 0;
140+
}
141+
142+
offset
143+
}
144+
};
145+
}
146+
147+
macro_rules! macro_impl_format_into {
148+
(signed; $($T:ident)*) => {
26149
$(
27150
#[unstable(feature = "int_format_into", issue = "138215")]
28151
impl $T {
@@ -44,19 +167,16 @@ macro_rules! int_impl_format_into {
44167
/// ```
45168
///
46169
pub fn format_into<const BUF_SIZE: usize>(self, buf: &mut crate::num::NumBuffer<BUF_SIZE>) -> &str {
47-
// 2 digit decimal look up table
48-
const DEC_DIGITS_LUT: &[u8; 200] = b"\
49-
0001020304050607080910111213141516171819\
50-
2021222324252627282930313233343536373839\
51-
4041424344454647484950515253545556575859\
52-
6061626364656667686970717273747576777879\
53-
8081828384858687888990919293949596979899";
54-
55-
const NEGATIVE_SIGN: &[u8; 1] = b"-";
56-
57170
// counting space for negative sign too, if `self` is negative
58-
let sign_offset = if self < 0 {1} else {0};
59-
let decimal_string_size: usize = self.ilog(10) as usize + 1 + sign_offset;
171+
let decimal_string_size: usize = if self == $T::MIN {
172+
$T::MAX.ilog(10) as usize + 1 + 1
173+
} else if self < 0 {
174+
self.abs().ilog(10) as usize + 1 + 1
175+
} else if self == 0 {
176+
1
177+
} else {
178+
self.ilog(10) as usize + 1
179+
};
60180

61181
// `buf` must have minimum size to store the decimal string version.
62182
// BUF_SIZE is the size of the buffer.
@@ -66,99 +186,20 @@ macro_rules! int_impl_format_into {
66186

67187
// Count the number of bytes in `buf` that are not initialized.
68188
let mut offset = BUF_SIZE;
69-
// Consume the least-significant decimals from a working copy.
70-
let mut remain = self;
71-
72-
// Format per four digits from the lookup table.
73-
// Four digits need a 16-bit $unsigned or wider.
74-
while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") {
75-
// SAFETY: All of the decimals fit in buf, since it now is size-checked
76-
// and the while condition ensures at least 4 more decimals.
77-
unsafe { core::hint::assert_unchecked(offset >= 4) }
78-
// SAFETY: The offset counts down from its initial value BUF_SIZE
79-
// without underflow due to the previous precondition.
80-
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
81-
offset -= 4;
82-
83-
// pull two pairs
84-
let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
85-
let quad = remain % scale;
86-
remain /= scale;
87-
let pair1 = (quad / 100) as usize;
88-
let pair2 = (quad % 100) as usize;
89-
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
90-
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
91-
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
92-
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
93-
}
94189

95-
// Format per two digits from the lookup table.
96-
if remain > 9 {
97-
// SAFETY: All of the decimals fit in buf, since it now is size-checked
98-
// and the while condition ensures at least 2 more decimals.
99-
unsafe { core::hint::assert_unchecked(offset >= 2) }
100-
// SAFETY: The offset counts down from its initial value BUF_SIZE
101-
// without underflow due to the previous precondition.
102-
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
103-
offset -= 2;
104-
105-
let pair = (remain % 100) as usize;
106-
remain /= 100;
107-
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
108-
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
109-
}
110-
111-
// Format the last remaining digit, if any.
112-
if remain != 0 || self == 0 {
113-
// SAFETY: All of the decimals fit in buf, since it now is size-checked
114-
// and the if condition ensures (at least) 1 more decimals.
115-
unsafe { core::hint::assert_unchecked(offset >= 1) }
116-
// SAFETY: The offset counts down from its initial value BUF_SIZE
117-
// without underflow due to the previous precondition.
118-
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
119-
offset -= 1;
120-
121-
// Either the compiler sees that remain < 10, or it prevents
122-
// a boundary check up next.
123-
let last = (remain & 15) as usize;
124-
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
125-
// not used: remain = 0;
126-
}
190+
offset = raw_write_digits_into!(Self, BUF_SIZE)(self, buf, offset);
127191

128192
if self < 0 {
129-
// SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked
130-
// and the if condition ensures (at least) that the sign can be added.
131-
unsafe { core::hint::assert_unchecked(offset >= 1) }
132-
133-
// SAFETY: The offset counts down from its initial value BUF_SIZE
134-
// without underflow due to the previous precondition.
135-
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
136-
137-
// Setting sign for the negative number
138-
offset -= 1;
139-
buf.contents[offset].write(NEGATIVE_SIGN[0]);
193+
offset = raw_write_sign_into(buf, offset);
140194
}
141195

142-
// SAFETY: All buf content since offset is set.
143-
let written = unsafe { buf.contents.get_unchecked(offset..) };
144-
145-
// SAFETY: Writes use ASCII from the lookup table
146-
// (and `NEGATIVE_SIGN` in case of negative numbers) exclusively.
147-
let as_str = unsafe {
148-
str::from_utf8_unchecked(crate::slice::from_raw_parts(
149-
crate::mem::MaybeUninit::slice_as_ptr(written),
150-
written.len(),
151-
))
152-
};
153-
as_str
196+
raw_extract_as_str(buf, offset)
154197
}
155198
}
156199
)*
157200
};
158-
}
159201

160-
macro_rules! uint_impl_format_into {
161-
($($T:ident)*) => {
202+
(unsigned; $($T:ident)*) => {
162203
$(
163204
#[unstable(feature = "int_format_into", issue = "138215")]
164205
impl $T {
@@ -180,16 +221,8 @@ macro_rules! uint_impl_format_into {
180221
/// ```
181222
///
182223
pub fn format_into<const BUF_SIZE: usize>(self, buf: &mut crate::num::NumBuffer<BUF_SIZE>) -> &str {
183-
// 2 digit decimal look up table
184-
const DEC_DIGITS_LUT: &[u8; 200] = b"\
185-
0001020304050607080910111213141516171819\
186-
2021222324252627282930313233343536373839\
187-
4041424344454647484950515253545556575859\
188-
6061626364656667686970717273747576777879\
189-
8081828384858687888990919293949596979899";
190-
191224
// counting space for negative sign too, if `self` is negative
192-
let decimal_string_size: usize = self.ilog(10) as usize + 1;
225+
let decimal_string_size: usize = if self == 0 { 1 } else { self.ilog(10) as usize + 1 };
193226

194227
// `buf` must have minimum size to store the decimal string version.
195228
// BUF_SIZE is the size of the buffer.
@@ -199,81 +232,15 @@ macro_rules! uint_impl_format_into {
199232

200233
// Count the number of bytes in `buf` that are not initialized.
201234
let mut offset = BUF_SIZE;
202-
// Consume the least-significant decimals from a working copy.
203-
let mut remain = self;
204-
205-
// Format per four digits from the lookup table.
206-
// Four digits need a 16-bit $unsigned or wider.
207-
while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") {
208-
// SAFETY: All of the decimals fit in buf, since it now is size-checked
209-
// and the while condition ensures at least 4 more decimals.
210-
unsafe { core::hint::assert_unchecked(offset >= 4) }
211-
// SAFETY: The offset counts down from its initial value BUF_SIZE
212-
// without underflow due to the previous precondition.
213-
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
214-
offset -= 4;
215-
216-
// pull two pairs
217-
let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
218-
let quad = remain % scale;
219-
remain /= scale;
220-
let pair1 = (quad / 100) as usize;
221-
let pair2 = (quad % 100) as usize;
222-
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
223-
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
224-
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
225-
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
226-
}
227235

228-
// Format per two digits from the lookup table.
229-
if remain > 9 {
230-
// SAFETY: All of the decimals fit in buf, since it now is size-checked
231-
// and the while condition ensures at least 2 more decimals.
232-
unsafe { core::hint::assert_unchecked(offset >= 2) }
233-
// SAFETY: The offset counts down from its initial value BUF_SIZE
234-
// without underflow due to the previous precondition.
235-
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
236-
offset -= 2;
237-
238-
let pair = (remain % 100) as usize;
239-
remain /= 100;
240-
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
241-
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
242-
}
243-
244-
// Format the last remaining digit, if any.
245-
if remain != 0 || self == 0 {
246-
// SAFETY: All of the decimals fit in buf, since it now is size-checked
247-
// and the if condition ensures (at least) 1 more decimals.
248-
unsafe { core::hint::assert_unchecked(offset >= 1) }
249-
// SAFETY: The offset counts down from its initial value BUF_SIZE
250-
// without underflow due to the previous precondition.
251-
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
252-
offset -= 1;
253-
254-
// Either the compiler sees that remain < 10, or it prevents
255-
// a boundary check up next.
256-
let last = (remain & 15) as usize;
257-
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
258-
// not used: remain = 0;
259-
}
236+
offset = raw_write_digits_into!(Self, BUF_SIZE)(self, buf, offset);
260237

261-
// SAFETY: All buf content since offset is set.
262-
let written = unsafe { buf.contents.get_unchecked(offset..) };
263-
264-
// SAFETY: Writes use ASCII from the lookup table exclusively.
265-
let as_str = unsafe {
266-
str::from_utf8_unchecked(crate::slice::from_raw_parts(
267-
crate::mem::MaybeUninit::slice_as_ptr(written),
268-
written.len(),
269-
))
270-
};
271-
as_str
238+
raw_extract_as_str(buf, offset)
272239
}
273240
}
274241
)*
275242
};
276243
}
277244

278-
int_impl_format_into! { i8 i16 i32 i64 i128 isize }
279-
uint_impl_format_into! { u8 u16 u32 u64 u128 usize }
245+
macro_impl_format_into! { signed; i8 i16 i32 i64 i128 isize }
246+
macro_impl_format_into! { unsigned; u8 u16 u32 u64 u128 usize }

0 commit comments

Comments
 (0)