|
1 | 1 | //! Integer and floating-point number formatting
|
2 | 2 |
|
3 |
| -// ignore-tidy-undocumented-unsafe |
4 |
| - |
5 |
| - |
6 | 3 | use crate::fmt;
|
7 | 4 | use crate::ops::{Div, Rem, Sub};
|
8 | 5 | use crate::str;
|
@@ -83,6 +80,7 @@ trait GenericRadix {
|
83 | 80 | }
|
84 | 81 | }
|
85 | 82 | let buf = &buf[curr..];
|
| 83 | + // SAFETY: only chars in buf are created by Self::digit which are asuumed to be valid utf8 |
86 | 84 | let buf = unsafe { str::from_utf8_unchecked(slice::from_raw_parts(
|
87 | 85 | MaybeUninit::first_ptr(buf),
|
88 | 86 | buf.len()
|
@@ -191,49 +189,62 @@ static DEC_DIGITS_LUT: &[u8; 200] =
|
191 | 189 | macro_rules! impl_Display {
|
192 | 190 | ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
|
193 | 191 | fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
| 192 | + // 2^128 is is about 3*10^38, so 39 gives an extra byte of space |
194 | 193 | let mut buf = [MaybeUninit::<u8>::uninit(); 39];
|
195 | 194 | let mut curr = buf.len() as isize;
|
196 | 195 | let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf);
|
197 | 196 | let lut_ptr = DEC_DIGITS_LUT.as_ptr();
|
198 | 197 |
|
199 |
| - unsafe { |
200 |
| - // need at least 16 bits for the 4-characters-at-a-time to work. |
201 |
| - assert!(crate::mem::size_of::<$u>() >= 2); |
| 198 | + // need at least 16 bits for the 4-characters-at-a-time to work. |
| 199 | + assert!(crate::mem::size_of::<$u>() >= 2); |
202 | 200 |
|
203 |
| - // eagerly decode 4 characters at a time |
204 |
| - while n >= 10000 { |
205 |
| - let rem = (n % 10000) as isize; |
206 |
| - n /= 10000; |
| 201 | + // eagerly decode 4 characters at a time |
| 202 | + while n >= 10000 { |
| 203 | + let rem = (n % 10000) as isize; |
| 204 | + n /= 10000; |
207 | 205 |
|
208 |
| - let d1 = (rem / 100) << 1; |
209 |
| - let d2 = (rem % 100) << 1; |
210 |
| - curr -= 4; |
| 206 | + let d1 = (rem / 100) << 1; |
| 207 | + let d2 = (rem % 100) << 1; |
| 208 | + curr -= 4; |
| 209 | + // SAFETY: d1, d2 are each max 198, so buf_ptr[d1..d1 + 1] is safe to access |
| 210 | + unsafe { |
211 | 211 | ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
|
212 | 212 | ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2);
|
213 | 213 | }
|
| 214 | + } |
214 | 215 |
|
215 |
| - // if we reach here numbers are <= 9999, so at most 4 chars long |
216 |
| - let mut n = n as isize; // possibly reduce 64bit math |
| 216 | + // if we reach here numbers are <= 9999, so at most 4 chars long |
| 217 | + let mut n = n as isize; // possibly reduce 64bit math |
217 | 218 |
|
218 |
| - // decode 2 more chars, if > 2 chars |
219 |
| - if n >= 100 { |
220 |
| - let d1 = (n % 100) << 1; |
221 |
| - n /= 100; |
222 |
| - curr -= 2; |
| 219 | + // decode 2 more chars, if > 2 chars |
| 220 | + if n >= 100 { |
| 221 | + let d1 = (n % 100) << 1; |
| 222 | + n /= 100; |
| 223 | + curr -= 2; |
| 224 | + // SAFETY: d1 is max 198, so buf_ptr[d1..d1 + 1] is safe to access |
| 225 | + unsafe { |
223 | 226 | ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
|
224 | 227 | }
|
| 228 | + } |
225 | 229 |
|
226 |
| - // decode last 1 or 2 chars |
227 |
| - if n < 10 { |
228 |
| - curr -= 1; |
| 230 | + // decode last 1 or 2 chars |
| 231 | + if n < 10 { |
| 232 | + curr -= 1; |
| 233 | + // SAFETY: curr is still less than buf.len() and since n < 10, n + '0' is valid utf8 |
| 234 | + unsafe { |
229 | 235 | *buf_ptr.offset(curr) = (n as u8) + b'0';
|
230 |
| - } else { |
231 |
| - let d1 = n << 1; |
232 |
| - curr -= 2; |
| 236 | + } |
| 237 | + } else { |
| 238 | + let d1 = n << 1; |
| 239 | + curr -= 2; |
| 240 | + // SAFETY: d1 is max 18, so buf_ptr[d1..d1 + 1] is safe to access |
| 241 | + unsafe { |
233 | 242 | ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
|
234 | 243 | }
|
235 | 244 | }
|
236 | 245 |
|
| 246 | + // SAFETY: curr > 0 (since we made buf large enough), and all the chars are valid utf8 |
| 247 | + // since DEC_DIGITS_LUT is |
237 | 248 | let buf_slice = unsafe {
|
238 | 249 | str::from_utf8_unchecked(
|
239 | 250 | slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize))
|
|
0 commit comments