1 //! Integer and floating-point number formatting
4 use crate::mem
::MaybeUninit
;
5 use crate::num
::flt2dec
;
6 use crate::ops
::{Div, Rem, Sub}
;
13 PartialEq
+ PartialOrd
+ Div
<Output
= Self> + Rem
<Output
= Self> + Sub
<Output
= Self> + Copy
16 fn from_u8(u
: u8) -> Self;
17 fn to_u8(&self) -> u8;
18 fn to_u16(&self) -> u16;
19 fn to_u32(&self) -> u32;
20 fn to_u64(&self) -> u64;
21 fn to_u128(&self) -> u128
;
25 ($
($t
:ident
)*) => ($
(impl Int
for $t
{
26 fn zero() -> Self { 0 }
27 fn from_u8(u
: u8) -> Self { u as Self }
28 fn to_u8(&self) -> u8 { *self as u8 }
29 fn to_u16(&self) -> u16 { *self as u16 }
30 fn to_u32(&self) -> u32 { *self as u32 }
31 fn to_u64(&self) -> u64 { *self as u64 }
32 fn to_u128(&self) -> u128 { *self as u128 }
35 doit
! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize }
37 /// A type that represents a specific radix
40 /// The number of digits.
43 /// A radix-specific prefix string.
44 const PREFIX
: &'
static str;
46 /// Converts an integer to corresponding radix digit.
47 fn digit(x
: u8) -> u8;
49 /// Format an integer using the radix using a formatter.
50 fn fmt_int
<T
: Int
>(&self, mut x
: T
, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
51 // The radix can be as low as 2, so we need a buffer of at least 128
52 // characters for a base 2 number.
54 let is_nonnegative
= x
>= zero
;
55 let mut buf
= [MaybeUninit
::<u8>::uninit(); 128];
56 let mut curr
= buf
.len();
57 let base
= T
::from_u8(Self::BASE
);
59 // Accumulate each digit of the number from the least significant
60 // to the most significant figure.
61 for byte
in buf
.iter_mut().rev() {
62 let n
= x
% base
; // Get the current place value.
63 x
= x
/ base
; // Deaccumulate the number.
64 byte
.write(Self::digit(n
.to_u8())); // Store the digit in the buffer.
67 // No more digits left to accumulate.
72 // Do the same as above, but accounting for two's complement.
73 for byte
in buf
.iter_mut().rev() {
74 let n
= zero
- (x
% base
); // Get the current place value.
75 x
= x
/ base
; // Deaccumulate the number.
76 byte
.write(Self::digit(n
.to_u8())); // Store the digit in the buffer.
79 // No more digits left to accumulate.
84 let buf
= &buf
[curr
..];
85 // SAFETY: The only chars in `buf` are created by `Self::digit` which are assumed to be
88 str::from_utf8_unchecked(slice
::from_raw_parts(MaybeUninit
::first_ptr(buf
), buf
.len()))
90 f
.pad_integral(is_nonnegative
, Self::PREFIX
, buf
)
94 /// A binary (base 2) radix
95 #[derive(Clone, PartialEq)]
98 /// An octal (base 8) radix
99 #[derive(Clone, PartialEq)]
102 /// A hexadecimal (base 16) radix, formatted with lower-case characters
103 #[derive(Clone, PartialEq)]
106 /// A hexadecimal (base 16) radix, formatted with upper-case characters
107 #[derive(Clone, PartialEq)]
111 ($T
:ident
, $base
:expr
, $prefix
:expr
, $
($x
:pat
=> $conv
:expr
),+) => {
112 impl GenericRadix
for $T
{
113 const BASE
: u8 = $base
;
114 const PREFIX
: &'
static str = $prefix
;
115 fn digit(x
: u8) -> u8 {
118 x
=> panic
!("number not in the range 0..={}: {}", Self::BASE
- 1, x
),
125 radix
! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x }
126 radix
! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x }
127 radix
! { LowerHex
, 16, "0x", x @
0 ..= 9 => b'
0'
+ x
,
128 x @
10 ..= 15 => b'a'
+ (x
- 10) }
129 radix
! { UpperHex
, 16, "0x", x @
0 ..= 9 => b'
0'
+ x
,
130 x @
10 ..= 15 => b'A'
+ (x
- 10) }
132 macro_rules
! int_base
{
133 ($Trait
:ident
for $T
:ident
as $U
:ident
-> $Radix
:ident
) => {
134 #[stable(feature = "rust1", since = "1.0.0")]
135 impl fmt
::$Trait
for $T
{
136 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
137 $Radix
.fmt_int(*self as $U
, f
)
145 #[stable(feature = "rust1", since = "1.0.0")]
146 impl fmt
::Debug
for $T
{
148 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
149 if f
.debug_lower_hex() {
150 fmt
::LowerHex
::fmt(self, f
)
151 } else if f
.debug_upper_hex() {
152 fmt
::UpperHex
::fmt(self, f
)
154 fmt
::Display
::fmt(self, f
)
161 macro_rules
! integer
{
162 ($Int
:ident
, $Uint
:ident
) => {
163 int_base
! { Binary for $Int as $Uint -> Binary }
164 int_base
! { Octal for $Int as $Uint -> Octal }
165 int_base
! { LowerHex for $Int as $Uint -> LowerHex }
166 int_base
! { UpperHex for $Int as $Uint -> UpperHex }
169 int_base
! { Binary for $Uint as $Uint -> Binary }
170 int_base
! { Octal for $Uint as $Uint -> Octal }
171 int_base
! { LowerHex for $Uint as $Uint -> LowerHex }
172 int_base
! { UpperHex for $Uint as $Uint -> UpperHex }
176 integer
! { isize, usize }
178 integer
! { i16, u16 }
179 integer
! { i32, u32 }
180 integer
! { i64, u64 }
181 integer
! { i128, u128 }
183 static DEC_DIGITS_LUT
: &[u8; 200] = b
"0001020304050607080910111213141516171819\
184 2021222324252627282930313233343536373839\
185 4041424344454647484950515253545556575859\
186 6061626364656667686970717273747576777879\
187 8081828384858687888990919293949596979899";
189 macro_rules
! impl_Display
{
190 ($
($t
:ident
),* as $u
:ident via $conv_fn
:ident named $name
:ident
) => {
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
193 let mut buf
= [MaybeUninit
::<u8>::uninit(); 39];
194 let mut curr
= buf
.len() as isize;
195 let buf_ptr
= MaybeUninit
::first_ptr_mut(&mut buf
);
196 let lut_ptr
= DEC_DIGITS_LUT
.as_ptr();
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.
206 // need at least 16 bits for the 4-characters-at-a-time to work.
207 assert
!(crate::mem
::size_of
::<$u
>() >= 2);
209 // eagerly decode 4 characters at a time
211 let rem
= (n
% 10000) as isize;
214 let d1
= (rem
/ 100) << 1;
215 let d2
= (rem
% 100) << 1;
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`.
221 ptr
::copy_nonoverlapping(lut_ptr
.offset(d1
), buf_ptr
.offset(curr
), 2);
222 ptr
::copy_nonoverlapping(lut_ptr
.offset(d2
), buf_ptr
.offset(curr
+ 2), 2);
225 // if we reach here numbers are <= 9999, so at most 4 chars long
226 let mut n
= n
as isize; // possibly reduce 64bit math
228 // decode 2 more chars, if > 2 chars
230 let d1
= (n
% 100) << 1;
233 ptr
::copy_nonoverlapping(lut_ptr
.offset(d1
), buf_ptr
.offset(curr
), 2);
236 // decode last 1 or 2 chars
239 *buf_ptr
.offset(curr
) = (n
as u8) + b'
0'
;
243 ptr
::copy_nonoverlapping(lut_ptr
.offset(d1
), buf_ptr
.offset(curr
), 2);
247 // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
248 // UTF-8 since `DEC_DIGITS_LUT` is
249 let buf_slice
= unsafe {
250 str::from_utf8_unchecked(
251 slice
::from_raw_parts(buf_ptr
.offset(curr
), buf
.len() - curr
as usize))
253 f
.pad_integral(is_nonnegative
, "", buf_slice
)
257 #[stable(feature = "rust1", since = "1.0.0")]
258 impl fmt
::Display
for $t
{
259 #[allow(unused_comparisons)]
260 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
261 let is_nonnegative
= *self >= 0;
262 let n
= if is_nonnegative
{
265 // convert the negative num to positive by summing 1 to it's 2 complement
266 (!self.$
conv_fn()).wrapping_add(1)
268 $
name(n
, is_nonnegative
, f
)
274 macro_rules
! impl_Exp
{
275 ($
($t
:ident
),* as $u
:ident via $conv_fn
:ident named $name
:ident
) => {
278 is_nonnegative
: bool
,
280 f
: &mut fmt
::Formatter
<'_
>
282 let (mut n
, mut exponent
, trailing_zeros
, added_precision
) = {
283 let mut exponent
= 0;
284 // count and remove trailing decimal zeroes
285 while n
% 10 == 0 && n
>= 10 {
289 let trailing_zeros
= exponent
;
291 let (added_precision
, subtracted_precision
) = match f
.precision() {
293 // number of decimal digits minus 1
300 (fmt_prec
.saturating_sub(prec
), prec
.saturating_sub(fmt_prec
))
304 for _
in 1..subtracted_precision
{
308 if subtracted_precision
!= 0 {
312 // round up last digit
317 (n
, exponent
, trailing_zeros
, added_precision
)
320 // 39 digits (worst case u128) + . = 40
321 // Since `curr` always decreases by the number of digits copied, this means
323 let mut buf
= [MaybeUninit
::<u8>::uninit(); 40];
324 let mut curr
= buf
.len() as isize; //index for buf
325 let buf_ptr
= MaybeUninit
::first_ptr_mut(&mut buf
);
326 let lut_ptr
= DEC_DIGITS_LUT
.as_ptr();
328 // decode 2 chars at a time
330 let d1
= ((n
% 100) as isize) << 1;
332 // SAFETY: `d1 <= 198`, so we can copy from `lut_ptr[d1..d1 + 2]` since
333 // `DEC_DIGITS_LUT` has a length of 200.
335 ptr
::copy_nonoverlapping(lut_ptr
.offset(d1
), buf_ptr
.offset(curr
), 2);
340 // n is <= 99, so at most 2 chars long
341 let mut n
= n
as isize; // possibly reduce 64bit math
342 // decode second-to-last character
345 // SAFETY: Safe since `40 > curr >= 0` (see comment)
347 *buf_ptr
.offset(curr
) = (n
as u8 % 10_u8) + b'
0'
;
352 // add decimal point iff >1 mantissa digit will be printed
353 if exponent
!= trailing_zeros
|| added_precision
!= 0 {
355 // SAFETY: Safe since `40 > curr >= 0`
357 *buf_ptr
.offset(curr
) = b'
.'
;
361 // SAFETY: Safe since `40 > curr >= 0`
362 let buf_slice
= unsafe {
363 // decode last character
365 *buf_ptr
.offset(curr
) = (n
as u8) + b'
0'
;
367 let len
= buf
.len() - curr
as usize;
368 slice
::from_raw_parts(buf_ptr
.offset(curr
), len
)
371 // stores 'e' (or 'E') and the up to 2-digit exponent
372 let mut exp_buf
= [MaybeUninit
::<u8>::uninit(); 3];
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`.
376 let exp_slice
= unsafe {
377 *exp_ptr
.offset(0) = if upper {b'E'}
else {b'e'}
;
378 let len
= if exponent
< 10 {
379 *exp_ptr
.offset(1) = (exponent
as u8) + b'
0'
;
382 let off
= exponent
<< 1;
383 ptr
::copy_nonoverlapping(lut_ptr
.offset(off
), exp_ptr
.offset(1), 2);
386 slice
::from_raw_parts(exp_ptr
, len
)
390 flt2dec
::Part
::Copy(buf_slice
),
391 flt2dec
::Part
::Zero(added_precision
),
392 flt2dec
::Part
::Copy(exp_slice
)
394 let sign
= if !is_nonnegative
{
396 } else if f
.sign_plus() {
401 let formatted
= flt2dec
::Formatted{sign, parts}
;
402 f
.pad_formatted_parts(&formatted
)
406 #[stable(feature = "integer_exp_format", since = "1.42.0")]
407 impl fmt
::LowerExp
for $t
{
408 #[allow(unused_comparisons)]
409 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
410 let is_nonnegative
= *self >= 0;
411 let n
= if is_nonnegative
{
414 // convert the negative num to positive by summing 1 to it's 2 complement
415 (!self.$
conv_fn()).wrapping_add(1)
417 $
name(n
, is_nonnegative
, false, f
)
421 #[stable(feature = "integer_exp_format", since = "1.42.0")]
422 impl fmt
::UpperExp
for $t
{
423 #[allow(unused_comparisons)]
424 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
425 let is_nonnegative
= *self >= 0;
426 let n
= if is_nonnegative
{
429 // convert the negative num to positive by summing 1 to it's 2 complement
430 (!self.$
conv_fn()).wrapping_add(1)
432 $
name(n
, is_nonnegative
, true, f
)
438 // Include wasm32 in here since it doesn't reflect the native pointer size, and
439 // often cares strongly about getting a smaller code size.
440 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
444 i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
445 as u64 via to_u64 named fmt_u64
448 i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
449 as u64 via to_u64 named exp_u64
453 #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
456 impl_Display
!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32
);
457 impl_Display
!(i64, u64 as u64 via to_u64 named fmt_u64
);
458 impl_Exp
!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32
);
459 impl_Exp
!(i64, u64 as u64 via to_u64 named exp_u64
);
462 impl_Display
!(i128
, u128
as u128 via to_u128 named fmt_u128
);
463 impl_Exp
!(i128
, u128
as u128 via to_u128 named exp_u128
);