1
1
//! Integer and floating-point number formatting
2
2
3
- // ignore-tidy-undocumented-unsafe
4
-
5
3
use crate :: fmt;
6
4
use crate :: mem:: MaybeUninit ;
7
5
use crate :: num:: flt2dec;
@@ -84,6 +82,8 @@ trait GenericRadix {
84
82
}
85
83
}
86
84
let buf = & buf[ curr..] ;
85
+ // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be
86
+ // valid UTF-8
87
87
let buf = unsafe {
88
88
str:: from_utf8_unchecked ( slice:: from_raw_parts ( MaybeUninit :: first_ptr ( buf) , buf. len ( ) ) )
89
89
} ;
@@ -189,11 +189,19 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\
189
189
macro_rules! impl_Display {
190
190
( $( $t: ident) ,* as $u: ident via $conv_fn: ident named $name: ident) => {
191
191
fn $name( mut n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
192
+ // 2^128 is about 3*10^38, so 39 gives an extra byte of space
192
193
let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; 39 ] ;
193
194
let mut curr = buf. len( ) as isize ;
194
195
let buf_ptr = MaybeUninit :: first_ptr_mut( & mut buf) ;
195
196
let lut_ptr = DEC_DIGITS_LUT . as_ptr( ) ;
196
197
198
+ // SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we
199
+ // can copy from `lut_ptr[d1..d1 + 1]` and `lut_ptr[d2..d2 + 1]`. To show
200
+ // that it's OK to copy into `buf_ptr`, notice that at the beginning
201
+ // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
202
+ // each step this is kept the same as `n` is divided. Since `n` is always
203
+ // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
204
+ // is safe to access.
197
205
unsafe {
198
206
// need at least 16 bits for the 4-characters-at-a-time to work.
199
207
assert!( crate :: mem:: size_of:: <$u>( ) >= 2 ) ;
@@ -206,6 +214,10 @@ macro_rules! impl_Display {
206
214
let d1 = ( rem / 100 ) << 1 ;
207
215
let d2 = ( rem % 100 ) << 1 ;
208
216
curr -= 4 ;
217
+
218
+ // We are allowed to copy to `buf_ptr[curr..curr + 3]` here since
219
+ // otherwise `curr < 0`. But then `n` was originally at least `10000^10`
220
+ // which is `10^40 > 2^128 > n`.
209
221
ptr:: copy_nonoverlapping( lut_ptr. offset( d1) , buf_ptr. offset( curr) , 2 ) ;
210
222
ptr:: copy_nonoverlapping( lut_ptr. offset( d2) , buf_ptr. offset( curr + 2 ) , 2 ) ;
211
223
}
@@ -232,6 +244,8 @@ macro_rules! impl_Display {
232
244
}
233
245
}
234
246
247
+ // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
248
+ // UTF-8 since `DEC_DIGITS_LUT` is
235
249
let buf_slice = unsafe {
236
250
str :: from_utf8_unchecked(
237
251
slice:: from_raw_parts( buf_ptr. offset( curr) , buf. len( ) - curr as usize ) )
@@ -304,6 +318,8 @@ macro_rules! impl_Exp {
304
318
} ;
305
319
306
320
// 39 digits (worst case u128) + . = 40
321
+ // Since `curr` always decreases by the number of digits copied, this means
322
+ // that `curr >= 0`.
307
323
let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; 40 ] ;
308
324
let mut curr = buf. len( ) as isize ; //index for buf
309
325
let buf_ptr = MaybeUninit :: first_ptr_mut( & mut buf) ;
@@ -313,6 +329,8 @@ macro_rules! impl_Exp {
313
329
while n >= 100 {
314
330
let d1 = ( ( n % 100 ) as isize ) << 1 ;
315
331
curr -= 2 ;
332
+ // SAFETY: `d1 <= 198`, so we can copy from `lut_ptr[d1..d1 + 2]` since
333
+ // `DEC_DIGITS_LUT` has a length of 200.
316
334
unsafe {
317
335
ptr:: copy_nonoverlapping( lut_ptr. offset( d1) , buf_ptr. offset( curr) , 2 ) ;
318
336
}
@@ -324,6 +342,7 @@ macro_rules! impl_Exp {
324
342
// decode second-to-last character
325
343
if n >= 10 {
326
344
curr -= 1 ;
345
+ // SAFETY: Safe since `40 > curr >= 0` (see comment)
327
346
unsafe {
328
347
* buf_ptr. offset( curr) = ( n as u8 % 10_u8 ) + b'0' ;
329
348
}
@@ -333,11 +352,13 @@ macro_rules! impl_Exp {
333
352
// add decimal point iff >1 mantissa digit will be printed
334
353
if exponent != trailing_zeros || added_precision != 0 {
335
354
curr -= 1 ;
355
+ // SAFETY: Safe since `40 > curr >= 0`
336
356
unsafe {
337
357
* buf_ptr. offset( curr) = b'.' ;
338
358
}
339
359
}
340
360
361
+ // SAFETY: Safe since `40 > curr >= 0`
341
362
let buf_slice = unsafe {
342
363
// decode last character
343
364
curr -= 1 ;
@@ -350,6 +371,8 @@ macro_rules! impl_Exp {
350
371
// stores 'e' (or 'E') and the up to 2-digit exponent
351
372
let mut exp_buf = [ MaybeUninit :: <u8 >:: uninit( ) ; 3 ] ;
352
373
let exp_ptr = MaybeUninit :: first_ptr_mut( & mut exp_buf) ;
374
+ // SAFETY: In either case, `exp_buf` is written within bounds and `exp_ptr[..len]`
375
+ // is contained within `exp_buf` since `len <= 3`.
353
376
let exp_slice = unsafe {
354
377
* exp_ptr. offset( 0 ) = if upper { b'E' } else { b'e' } ;
355
378
let len = if exponent < 10 {
0 commit comments