@@ -4,6 +4,7 @@ use crate::mem::MaybeUninit;
44use crate :: num:: fmt as numfmt;
55use crate :: ops:: { Div , Rem , Sub } ;
66use crate :: { fmt, ptr, slice, str} ;
7+ use crate :: fmt:: NumBuffer ;
78
89#[ doc( hidden) ]
910trait DisplayInt :
@@ -199,6 +200,42 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"\
199200 6061626364656667686970717273747576777879\
200201 8081828384858687888990919293949596979899";
201202
203+ static NEGATIVE_SIGN : & [ u8 ; 1 ] = b"-" ;
204+
205+ // Unsafe because
206+ // 1. The contents of `buf` is not validated in the function
207+ // 2. offset is not bound checked
208+ unsafe fn _extract_str_from_buf (
209+ buf : & NumBuffer ,
210+ offset : usize
211+ ) -> & str {
212+ let written = buf. contents . get_unchecked ( offset..) ;
213+ let as_str = str:: from_utf8_unchecked ( slice:: from_raw_parts (
214+ MaybeUninit :: slice_as_ptr ( written) ,
215+ written. len ( ) ,
216+ ) ) ;
217+ as_str
218+ }
219+
220+ // Unsafe because
221+ // 1. Offset is not bound checked
222+ unsafe fn _add_negative_sign (
223+ is_nonnegative : bool ,
224+ buf : & mut NumBuffer ,
225+ start_offset : usize
226+ ) -> usize {
227+ if is_nonnegative {
228+ return start_offset;
229+ }
230+
231+ let mut offset = start_offset - 1 ;
232+
233+ // Setting sign for the negative number
234+ buf. contents [ offset] . write ( NEGATIVE_SIGN [ 0 ] ) ;
235+
236+ offset
237+ }
238+
202239macro_rules! impl_Display {
203240 ( $( $signed: ident, $unsigned: ident, ) * ; as $u: ident via $conv_fn: ident named $gen_name: ident) => {
204241
@@ -212,7 +249,9 @@ macro_rules! impl_Display {
212249 }
213250 #[ cfg( feature = "optimize_for_size" ) ]
214251 {
215- $gen_name( self . $conv_fn( ) , true , f)
252+ let mut buf = NumBuffer :: new( ) ;
253+ let as_str = $gen_name( self . $conv_fn( ) , true , & mut buf) ;
254+ f. pad_integral( true , "" , as_str)
216255 }
217256 }
218257 }
@@ -226,31 +265,121 @@ macro_rules! impl_Display {
226265 }
227266 #[ cfg( feature = "optimize_for_size" ) ]
228267 {
229- return $gen_name( self . unsigned_abs( ) . $conv_fn( ) , * self >= 0 , f) ;
268+ let mut buf = NumBuffer :: new( ) ;
269+
270+ // not setting the sign here, hence sending in `true`
271+ let as_str = $gen_name( self . unsigned_abs( ) . $conv_fn( ) , true , & mut buf) ;
272+ f. pad_integral( * self >= 0 , "" , as_str)
230273 }
231274 }
232275 }
233276
277+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
278+ impl $signed {
279+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
280+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
281+ ///
282+ /// # Examples
283+ /// ```
284+ /// #![feature(int_format_into)]
285+ /// use core::fmt::NumBuffer;
286+ ///
287+ #[ doc = concat!( "let n = -32" , stringify!( $signed) , ";" ) ]
288+ /// let mut buf = NumBuffer::new();
289+ /// assert_eq!(n.format_into(&mut buf), "-32");
290+ ///
291+ #[ doc = concat!( "let n2 = " , stringify!( $signed:: MIN ) , ";" ) ]
292+ /// let mut buf2 = NumBuffer::new();
293+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $signed:: MIN ) , ".to_string());" ) ]
294+ ///
295+ #[ doc = concat!( "let n3 = " , stringify!( $signed:: MAX ) , ";" ) ]
296+ /// let mut buf3 = NumBuffer::new();
297+ #[ doc = concat!( "assert_eq!(n3.format_into(&mut buf3), " , stringify!( $signed:: MAX ) , ".to_string());" ) ]
298+ /// ```
299+ ///
300+ pub fn format_into( self , buf: & mut NumBuffer ) -> & str {
301+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
302+ {
303+ let offset = self . unsigned_abs( ) . _write_into_buf( buf) ;
304+
305+ // SAFETY: `offset >= 2` (unchanged if `is_nonnegative` is true) and
306+ // `offset >= 1` (incase `is_nonnegative` is false) so
307+ // `buf_ptr[offset..offset + 1]` is safe to access.
308+ unsafe { _add_negative_sign( is_nonnegative, buf, offset) ; }
309+
310+ // SAFETY: All buf content since offset is set, and
311+ // writes use ASCII from the lookup table exclusively.
312+ let as_str = unsafe { _extract_str_from_buf( buf, offset) } ;
313+
314+ as_str
315+ }
316+
317+ #[ cfg( feature = "optimize_for_size" ) ]
318+ {
319+ let is_nonnegative = self > 0 ;
320+ $gen_name( self . unsigned_abs( ) , is_nonnegative, buf)
321+ }
322+
323+ }
324+ }
325+
326+ #[ unstable( feature = "int_format_into" , issue = "138215" ) ]
327+ impl $unsigned {
328+ /// Allows users to write an integer (in signed decimal format) into a variable `buf` of
329+ /// type [`NumBuffer`] that is passed by the caller by mutable reference.
330+ ///
331+ /// # Examples
332+ /// ```
333+ /// #![feature(int_format_into)]
334+ /// use core::fmt::NumBuffer;
335+ ///
336+ #[ doc = concat!( "let n = 32" , stringify!( $unsigned) , ";" ) ]
337+ /// let mut buf = NumBuffer::new();
338+ /// assert_eq!(n.format_into(&mut buf), "32");
339+ ///
340+ #[ doc = concat!( "let n2 = " , stringify!( $unsigned:: MAX ) , ";" ) ]
341+ /// let mut buf2 = NumBuffer::new();
342+ #[ doc = concat!( "assert_eq!(n2.format_into(&mut buf2), " , stringify!( $unsigned:: MAX ) , ".to_string());" ) ]
343+ /// ```
344+ ///
345+ pub fn format_into( self , buf: & mut NumBuffer ) -> & str {
346+ #[ cfg( not( feature = "optimize_for_size" ) ) ]
347+ {
348+ let offset = self . _write_into_buf( buf) ;
349+
350+ // SAFETY: All contents in `buf` since offset is set, and
351+ // writes use ASCII from the lookup table exclusively.
352+ let as_str = unsafe { _extract_str_from_buf( buf, offset) } ;
353+
354+ as_str
355+ }
356+
357+ #[ cfg( feature = "optimize_for_size" ) ]
358+ {
359+ $gen_name( self , true , buf)
360+ }
361+
362+ }
363+ }
364+
234365 #[ cfg( not( feature = "optimize_for_size" ) ) ]
235366 impl $unsigned {
236- fn _fmt( self , is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
237- const MAX_DEC_N : usize = $unsigned:: MAX . ilog( 10 ) as usize + 1 ;
238- // Buffer decimals for $unsigned with right alignment.
239- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
367+ fn _write_into_buf( self , buf: & mut NumBuffer ) -> usize {
240368 // Count the number of bytes in buf that are not initialized.
241- let mut offset = buf. len( ) ;
369+ let mut offset = buf. contents . len( ) ;
242370 // Consume the least-significant decimals from a working copy.
243371 let mut remain = self ;
244372
245373 // Format per four digits from the lookup table.
246374 // Four digits need a 16-bit $unsigned or wider.
247375 while size_of:: <Self >( ) > 1 && remain > 999 . try_into( ) . expect( "branch is not hit for types that cannot fit 999 (u8)" ) {
248- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
376+ // SAFETY: All of the decimals fit in buf since buf is large enough to
377+ // accommodate the largest representation of a number possible (that of i128::MIN)
249378 // and the while condition ensures at least 4 more decimals.
250379 unsafe { core:: hint:: assert_unchecked( offset >= 4 ) }
251- // SAFETY: The offset counts down from its initial buf.len()
380+ // SAFETY: The offset counts down from its initial size
252381 // without underflow due to the previous precondition.
253- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
382+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
254383 offset -= 4 ;
255384
256385 // pull two pairs
@@ -259,26 +388,27 @@ macro_rules! impl_Display {
259388 remain /= scale;
260389 let pair1 = ( quad / 100 ) as usize ;
261390 let pair2 = ( quad % 100 ) as usize ;
262- buf[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
263- buf[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
264- buf[ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
265- buf[ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
391+ buf. contents [ offset + 0 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 0 ] ) ;
392+ buf. contents [ offset + 1 ] . write( DEC_DIGITS_LUT [ pair1 * 2 + 1 ] ) ;
393+ buf. contents [ offset + 2 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 0 ] ) ;
394+ buf. contents [ offset + 3 ] . write( DEC_DIGITS_LUT [ pair2 * 2 + 1 ] ) ;
266395 }
267396
268397 // Format per two digits from the lookup table.
269398 if remain > 9 {
270- // SAFETY: All of the decimals fit in buf due to MAX_DEC_N
399+ // SAFETY: All of the decimals fit in buf since buf is large enough to
400+ // accommodate the largest representation of a number possilble (that of i128::MIN)
271401 // and the while condition ensures at least 2 more decimals.
272402 unsafe { core:: hint:: assert_unchecked( offset >= 2 ) }
273- // SAFETY: The offset counts down from its initial buf.len()
403+ // SAFETY: The offset counts down from its initial size
274404 // without underflow due to the previous precondition.
275- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
405+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
276406 offset -= 2 ;
277407
278408 let pair = ( remain % 100 ) as usize ;
279409 remain /= 100 ;
280- buf[ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
281- buf[ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
410+ buf. contents [ offset + 0 ] . write( DEC_DIGITS_LUT [ pair * 2 + 0 ] ) ;
411+ buf. contents [ offset + 1 ] . write( DEC_DIGITS_LUT [ pair * 2 + 1 ] ) ;
282412 }
283413
284414 // Format the last remaining digit, if any.
@@ -288,40 +418,43 @@ macro_rules! impl_Display {
288418 unsafe { core:: hint:: assert_unchecked( offset >= 1 ) }
289419 // SAFETY: The offset counts down from its initial buf.len()
290420 // without underflow due to the previous precondition.
291- unsafe { core:: hint:: assert_unchecked( offset <= buf. len( ) ) }
421+ unsafe { core:: hint:: assert_unchecked( offset <= buf. contents . len( ) ) }
292422 offset -= 1 ;
293423
294424 // Either the compiler sees that remain < 10, or it prevents
295425 // a boundary check up next.
296426 let last = ( remain & 15 ) as usize ;
297- buf[ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
427+ buf. contents [ offset] . write( DEC_DIGITS_LUT [ last * 2 + 1 ] ) ;
298428 // not used: remain = 0;
299429 }
300430
301- // SAFETY: All buf content since offset is set.
302- let written = unsafe { buf. get_unchecked( offset..) } ;
303- // SAFETY: Writes use ASCII from the lookup table exclusively.
431+ offset
432+ }
433+
434+ fn _fmt( self , is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
435+ // Buffer decimals for $unsigned with right alignment.
436+ let mut buf = NumBuffer :: new( ) ;
437+ let offset = self . _write_into_buf( & mut buf) ;
438+
439+ // SAFETY: All buf content since offset is set, and
440+ // writes use ASCII from the lookup table exclusively.
304441 let as_str = unsafe {
305- str :: from_utf8_unchecked( slice:: from_raw_parts(
306- MaybeUninit :: slice_as_ptr( written) ,
307- written. len( ) ,
308- ) )
442+ _extract_str_from_buf( & buf, offset)
309443 } ;
444+
310445 f. pad_integral( is_nonnegative, "" , as_str)
311446 }
312447 } ) *
313448
314449 #[ cfg( feature = "optimize_for_size" ) ]
315- fn $gen_name( mut n: $u, is_nonnegative: bool , f: & mut fmt:: Formatter <' _>) -> fmt:: Result {
316- const MAX_DEC_N : usize = $u:: MAX . ilog( 10 ) as usize + 1 ;
317- let mut buf = [ MaybeUninit :: <u8 >:: uninit( ) ; MAX_DEC_N ] ;
318- let mut curr = MAX_DEC_N ;
319- let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf) ;
450+ fn $gen_name( mut n: $u, is_nonnegative: bool , buf: & mut NumBuffer ) -> & str {
451+ let mut curr = buf. contents. len( ) ;
452+ let buf_ptr = MaybeUninit :: slice_as_mut_ptr( & mut buf. contents) ;
320453
321454 // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning
322- // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at
455+ // `curr == buf.len() == 41 > log(n)` since `n < 2^128 < 10^39 < 10^41 `, and at
323456 // each step this is kept the same as `n` is divided. Since `n` is always
324- // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]`
457+ // non-negative, this means that `curr >= (41 - 39) == 2 > 0` so `buf_ptr[curr..curr + 1]`
325458 // is safe to access.
326459 unsafe {
327460 loop {
@@ -335,12 +468,18 @@ macro_rules! impl_Display {
335468 }
336469 }
337470
338- // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8
471+ // SAFETY: `curr >= 2` (unchanged if `is_nonnegative` is true) and
472+ // `curr >= 1` (incase `is_nonnegative` is false) so `buf_ptr[curr..curr + 1]`
473+ // is safe to access.
474+ unsafe { _add_negative_sign( is_nonnegative, buf, curr) ; }
475+
476+ // SAFETY: `curr >= 1` (since we made `buf` large enough), and all the chars are valid UTF-8
339477 let buf_slice = unsafe {
340478 str :: from_utf8_unchecked(
341479 slice:: from_raw_parts( buf_ptr. add( curr) , buf. len( ) - curr) )
342480 } ;
343- f. pad_integral( is_nonnegative, "" , buf_slice)
481+
482+ buf_slice
344483 }
345484 } ;
346485}
@@ -529,7 +668,7 @@ mod imp {
529668 i32 , u32 ,
530669 i64 , u64 ,
531670 isize , usize ,
532- ; as u64 via to_u64 named fmt_u64
671+ ; as u64 via to_u64 named stringify_u64
533672 ) ;
534673 impl_Exp ! (
535674 i8 , u8 , i16 , u16 , i32 , u32 , i64 , u64 , usize , isize
@@ -545,10 +684,10 @@ mod imp {
545684 i16 , u16 ,
546685 i32 , u32 ,
547686 isize , usize ,
548- ; as u32 via to_u32 named fmt_u32 ) ;
687+ ; as u32 via to_u32 named stringify_u32 ) ;
549688 impl_Display ! (
550689 i64 , u64 ,
551- ; as u64 via to_u64 named fmt_u64 ) ;
690+ ; as u64 via to_u64 named stringify_u64 ) ;
552691
553692 impl_Exp ! ( i8 , u8 , i16 , u16 , i32 , u32 , isize , usize as u32 via to_u32 named exp_u32) ;
554693 impl_Exp ! ( i64 , u64 as u64 via to_u64 named exp_u64) ;
0 commit comments