Skip to content

Commit 91b782a

Browse files
Destructured code for reusability
1 parent 040cedb commit 91b782a

File tree

1 file changed

+120
-171
lines changed

1 file changed

+120
-171
lines changed

library/core/src/num/int_format.rs

Lines changed: 120 additions & 171 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,6 +21,109 @@ impl<const BUF_SIZE: usize> NumBuffer<BUF_SIZE> {
2121
}
2222
}
2323

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_digits_into<T, const BUF_SIZE: usize>(var: T, buf: &mut NumBuffer<BUF_SIZE>, &mut offset: usize) {
35+
// Consume the least-significant decimals from a working copy.
36+
let mut remain = var;
37+
38+
// Format per four digits from the lookup table.
39+
// Four digits need a 16-bit $unsigned or wider.
40+
while size_of::<T>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") {
41+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
42+
// and the while condition ensures at least 4 more decimals.
43+
unsafe { core::hint::assert_unchecked(offset >= 4) }
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+
offset -= 4;
48+
49+
// pull two pairs
50+
let scale: T = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
51+
let quad = remain % scale;
52+
remain /= scale;
53+
let pair1 = (quad / 100) as usize;
54+
let pair2 = (quad % 100) as usize;
55+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]);
56+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]);
57+
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]);
58+
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]);
59+
}
60+
61+
// Format per two digits from the lookup table.
62+
if remain > 9 {
63+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
64+
// and the while condition ensures at least 2 more decimals.
65+
unsafe { core::hint::assert_unchecked(offset >= 2) }
66+
// SAFETY: The offset counts down from its initial value BUF_SIZE
67+
// without underflow due to the previous precondition.
68+
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
69+
offset -= 2;
70+
71+
let pair = (remain % 100) as usize;
72+
remain /= 100;
73+
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]);
74+
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]);
75+
}
76+
77+
// Format the last remaining digit, if any.
78+
if remain != 0 || var == 0 {
79+
// SAFETY: All of the decimals fit in buf, since it now is size-checked
80+
// and the if condition ensures (at least) 1 more decimals.
81+
unsafe { core::hint::assert_unchecked(offset >= 1) }
82+
// SAFETY: The offset counts down from its initial value BUF_SIZE
83+
// without underflow due to the previous precondition.
84+
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
85+
offset -= 1;
86+
87+
// Either the compiler sees that remain < 10, or it prevents
88+
// a boundary check up next.
89+
let last = (remain & 15) as usize;
90+
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]);
91+
// not used: remain = 0;
92+
}
93+
}
94+
95+
#[inline]
96+
fn raw_write_sign_into<const BUF_SIZE: usize>(buf: &mut NumBuffer<BUF_SIZE>, offset: &mut usize) {
97+
// SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked
98+
// and the if condition ensures (at least) that the sign can be added.
99+
unsafe { core::hint::assert_unchecked(offset >= 1) }
100+
101+
// SAFETY: The offset counts down from its initial value BUF_SIZE
102+
// without underflow due to the previous precondition.
103+
unsafe { core::hint::assert_unchecked(offset <= BUF_SIZE) }
104+
105+
// Setting sign for the negative number
106+
offset -= 1;
107+
buf.contents[offset].write(NEGATIVE_SIGN[0]);
108+
}
109+
110+
#[inline]
111+
fn raw_extract_as_str<const BUF_SIZE: usize>(buf: &NumBuffer<BUF_SIZE>, offset: usize) -> &str {
112+
// SAFETY: All contents of `buf` since `offset` is set.
113+
let written = unsafe { buf.contents.get_unchecked(offset..) };
114+
115+
// SAFETY: Writes use ASCII from the lookup table
116+
// (and `NEGATIVE_SIGN` in case of negative numbers) exclusively.
117+
let as_str = unsafe {
118+
str::from_utf8_unchecked(slice::from_raw_parts(
119+
MaybeUninit::slice_as_ptr(written),
120+
written.len(),
121+
))
122+
};
123+
124+
as_str
125+
}
126+
24127
macro_rules! int_impl_format_into {
25128
($($T:ident)*) => {
26129
$(
@@ -44,19 +147,16 @@ macro_rules! int_impl_format_into {
44147
/// ```
45148
///
46149
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-
57150
// 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;
151+
let decimal_string_size: usize = if self == $T::MIN {
152+
$T::MAX.ilog(10) as usize + 1 + 1
153+
} else if self < 0 {
154+
self.abs().ilog(10) as usize + 1 + 1
155+
} else if self == 0 {
156+
1
157+
} else {
158+
self.ilog(10) as usize + 1
159+
};
60160

61161
// `buf` must have minimum size to store the decimal string version.
62162
// BUF_SIZE is the size of the buffer.
@@ -66,91 +166,14 @@ macro_rules! int_impl_format_into {
66166

67167
// Count the number of bytes in `buf` that are not initialized.
68168
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-
}
94169

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-
}
170+
raw_write_digits_into(self, &mut buf, &mut offset);
127171

128172
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]);
173+
raw_write_sign_into(&mut buf, &mut offset);
140174
}
141175

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
176+
raw_extract_as_str(&buf, offset)
154177
}
155178
}
156179
)*
@@ -180,16 +203,8 @@ macro_rules! uint_impl_format_into {
180203
/// ```
181204
///
182205
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-
191206
// counting space for negative sign too, if `self` is negative
192-
let decimal_string_size: usize = self.ilog(10) as usize + 1;
207+
let decimal_string_size: usize = if self == 0 { 1 } else { self.ilog(10) as usize + 1 };
193208

194209
// `buf` must have minimum size to store the decimal string version.
195210
// BUF_SIZE is the size of the buffer.
@@ -199,76 +214,10 @@ macro_rules! uint_impl_format_into {
199214

200215
// Count the number of bytes in `buf` that are not initialized.
201216
let mut offset = BUF_SIZE;
202-
// Consume the least-significant decimals from a working copy.
203-
let mut remain = self;
204217

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;
218+
raw_write_digits_into(self, &mut buf, &mut offset);
215219

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-
}
227-
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-
}
260-
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
220+
raw_extract_as_str(&buf, offset)
272221
}
273222
}
274223
)*

0 commit comments

Comments
 (0)