Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
523 changes: 336 additions & 187 deletions library/core/src/fmt/float.rs

Large diffs are not rendered by default.

176 changes: 66 additions & 110 deletions library/core/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell};
use crate::char::EscapeDebugExtArgs;
use crate::hint::assert_unchecked;
use crate::marker::{PhantomData, PointeeSized};
use crate::num::fmt as numfmt;
use crate::ops::Deref;
use crate::ptr::NonNull;
use crate::{iter, mem, result, str};
Expand Down Expand Up @@ -1777,19 +1776,12 @@ impl<'a> Formatter<'a> {
// Helper methods used for padding and processing formatting arguments that
// all formatting traits can use.

/// Performs the correct padding for an integer which has already been
/// emitted into a str. The str should *not* contain the sign for the
/// integer, that will be added by this method.
///
/// # Arguments
///
/// * is_nonnegative - whether the original integer was either positive or zero.
/// * prefix - if the '#' character (Alternate) is provided, this
/// is the prefix to put in front of the number.
/// * buf - the byte array that the number has been formatted into
///
/// This function will correctly account for the flags provided as well as
/// the minimum width. It will not take precision into account.
/// Write a formatted integer to the Formatter. The `digits` string should
/// hold the representation of the absolute value, without sign. Whether a
/// sign ("+" or "-") is written depends both on format parameters and on
/// `is_nonnegative`. The `prefix` (e.g., "0x") is written when alternate.
/// Padding may be included when width is Some. The precision is ignored
/// for integers.
///
/// # Examples
///
Expand Down Expand Up @@ -1822,8 +1814,8 @@ impl<'a> Formatter<'a> {
/// assert_eq!(format!("{:0>#8}", Foo::new(-1)), "00-Foo 1");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn pad_integral(&mut self, is_nonnegative: bool, prefix: &str, buf: &str) -> Result {
let mut width = buf.len();
pub fn pad_integral(&mut self, is_nonnegative: bool, prefix: &str, digits: &str) -> Result {
let mut width = digits.len();

let mut sign = None;
if !is_nonnegative {
Expand Down Expand Up @@ -1855,27 +1847,70 @@ impl<'a> Formatter<'a> {
if width >= usize::from(min) {
// We're over the minimum width, so then we can just write the bytes.
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)
self.buf.write_str(digits)
} else if self.sign_aware_zero_pad() {
// The sign and prefix goes before the padding if the fill character
// is zero
let old_options = self.options;
self.options.fill('0').align(Some(Alignment::Right));
write_prefix(self, sign, prefix)?;
let post_padding = self.padding(min - width as u16, Alignment::Right)?;
self.buf.write_str(buf)?;
self.buf.write_str(digits)?;
post_padding.write(self)?;
self.options = old_options;
Ok(())
} else {
// Otherwise, the sign and prefix goes after the padding
let post_padding = self.padding(min - width as u16, Alignment::Right)?;
write_prefix(self, sign, prefix)?;
self.buf.write_str(buf)?;
self.buf.write_str(digits)?;
post_padding.write(self)
}
}

/// Write a formatted number to the Formatter with a closure. The Fn should
/// write the representation of the absolute value, excluding `sign` (e.g.,
/// "+" or "-"). The implementation should follow [precision] when present.
/// The output of `write_abs` must match exactly the `write_len` amount of
/// `char`s in size. Padding may be written when [width] applies.
fn pad_number<F>(&mut self, sign: &str, write_len: usize, write_abs: F) -> Result
where
F: Fn(&mut Self) -> Result,
{
let out_len = write_len + sign.len();
// Pad when the width is higher than the output length.
let pad = self.width().unwrap_or(0).saturating_sub(out_len);

if pad == 0 || self.sign_aware_zero_pad() {
self.write_str(sign)?;
self.write_zeroes(pad)?;
return write_abs(self);
}

// Numbers align to the right by default.
let align = self.align().unwrap_or(Alignment::Right);
let (pad_before, pad_after) = match align {
Alignment::Left => (0, pad),
Alignment::Right => (pad, 0),
Alignment::Center => {
let split = pad / 2; // may round down
(split, pad - split)
}
};

// Write output with padding.
let fill = self.fill();
for _ in 0..pad_before {
self.write_char(fill)?;
}
self.write_str(sign)?;
write_abs(self)?;
for _ in 0..pad_after {
self.write_char(fill)?;
}
Ok(())
}

/// Takes a string slice and emits it to the internal buffer after applying
/// the relevant formatting flags specified.
///
Expand Down Expand Up @@ -1969,100 +2004,21 @@ impl<'a> Formatter<'a> {
Ok(PostPadding::new(fill, padding - padding_left))
}

/// Takes the formatted parts and applies the padding.
///
/// Assumes that the caller already has rendered the parts with required precision,
/// so that `self.precision` can be ignored.
///
/// # Safety
///
/// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8.
unsafe fn pad_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result {
if self.options.width == 0 {
// this is the common case and we take a shortcut
// SAFETY: Per the precondition.
unsafe { self.write_formatted_parts(formatted) }
} else {
// for the sign-aware zero padding, we render the sign first and
// behave as if we had no sign from the beginning.
let mut formatted = formatted.clone();
let mut width = self.options.width;
let old_options = self.options;
if self.sign_aware_zero_pad() {
// a sign always goes first
let sign = formatted.sign;
self.buf.write_str(sign)?;

// remove the sign from the formatted parts
formatted.sign = "";
width = width.saturating_sub(sign.len() as u16);
self.options.fill('0').align(Some(Alignment::Right));
/// Write n ASCII digits (U+0030) to the Formatter.
pub(crate) fn write_zeroes(&mut self, n: usize) -> Result {
#[cfg(feature = "optimize_for_size")]
{
for _ in 0..n {
self.write_char('0')?;
}

// remaining parts go through the ordinary padding process.
let len = formatted.len();
let ret = if usize::from(width) <= len {
// no padding
// SAFETY: Per the precondition.
unsafe { self.write_formatted_parts(&formatted) }
} else {
let post_padding = self.padding(width - len as u16, Alignment::Right)?;
// SAFETY: Per the precondition.
unsafe {
self.write_formatted_parts(&formatted)?;
}
post_padding.write(self)
};
self.options = old_options;
ret
}
}

/// # Safety
///
/// Any `numfmt::Part::Copy` parts in `formatted` must contain valid UTF-8.
unsafe fn write_formatted_parts(&mut self, formatted: &numfmt::Formatted<'_>) -> Result {
unsafe fn write_bytes(buf: &mut dyn Write, s: &[u8]) -> Result {
// SAFETY: This is used for `numfmt::Part::Num` and `numfmt::Part::Copy`.
// It's safe to use for `numfmt::Part::Num` since every char `c` is between
// `b'0'` and `b'9'`, which means `s` is valid UTF-8. It's safe to use for
// `numfmt::Part::Copy` due to this function's precondition.
buf.write_str(unsafe { str::from_utf8_unchecked(s) })
}

if !formatted.sign.is_empty() {
self.buf.write_str(formatted.sign)?;
}
for part in formatted.parts {
match *part {
numfmt::Part::Zero(mut nzeroes) => {
const ZEROES: &str = // 64 zeroes
"0000000000000000000000000000000000000000000000000000000000000000";
while nzeroes > ZEROES.len() {
self.buf.write_str(ZEROES)?;
nzeroes -= ZEROES.len();
}
if nzeroes > 0 {
self.buf.write_str(&ZEROES[..nzeroes])?;
}
}
numfmt::Part::Num(mut v) => {
let mut s = [0; 5];
let len = part.len();
for c in s[..len].iter_mut().rev() {
*c = b'0' + (v % 10) as u8;
v /= 10;
}
// SAFETY: Per the precondition.
unsafe {
write_bytes(self.buf, &s[..len])?;
}
}
// SAFETY: Per the precondition.
numfmt::Part::Copy(buf) => unsafe {
write_bytes(self.buf, buf)?;
},
#[cfg(not(feature = "optimize_for_size"))]
{
const ZEROES: &str = "0000000000000000000000000000000000000000000000000000000000000000";
for _ in 0..(n / 64) {
self.write_str(ZEROES)?;
}
self.write_str(&ZEROES[..(n % 64)])?;
}
Ok(())
}
Expand Down
19 changes: 10 additions & 9 deletions library/core/src/fmt/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
use crate::fmt::NumBuffer;
use crate::mem::MaybeUninit;
use crate::num::fmt as numfmt;
use crate::{fmt, str};

/// Formatting of integers with a non-decimal radix.
Expand Down Expand Up @@ -531,21 +530,23 @@ macro_rules! impl_Exp {
let as_str = unsafe { str::from_utf8_unchecked(text) };
f.pad_integral(is_nonnegative, "", as_str)
} else {
let parts = &[
numfmt::Part::Copy(&text[..coef_len]),
numfmt::Part::Zero(more_prec),
numfmt::Part::Copy(&text[coef_len..]),
];
let sign = if !is_nonnegative {
"-"
} else if f.sign_plus() {
"+"
} else {
""
};
// SAFETY: Text is set with ASCII exclusively: either a decimal,
// or a LETTER_E, or a dot. ASCII implies valid UTF-8.
unsafe { f.pad_formatted_parts(&numfmt::Formatted { sign, parts }) }
f.pad_number(sign, text.len() + more_prec, |w| {
// SAFETY: Text is set with ASCII exclusively.
let (coef_str, scale_str) = unsafe{(
str::from_utf8_unchecked(&text[..coef_len]),
str::from_utf8_unchecked(&text[coef_len..]),
)};
w.write_str(coef_str)?;
w.write_zeroes(more_prec)?;
w.write_str(scale_str)
})
}
}

Expand Down
Loading
Loading