Skip to content

Commit 1b9b515

Browse files
authored
Rollup merge of rust-lang#136662 - thaliaarchi:formatter-pad-char-count, r=m-ou-se
Count char width at most once in `Formatter::pad` When both width and precision flags are specified, then `Formatter::pad` counts the character width twice. Instead, record the character width when truncating it to the precision, so it does not need to be recomputed. Simplify control flow so the cases are more clear. Related: - 6c9e708 (`fmt::Formatter::pad`: don't call chars().count() more than one time, 2021-09-01): Reduce counting chars from thrice to twice in worst case - ede39ae (feat: reinterpret `precision` field for strings, 2016-06-29): Change meaning of precision for strings - b820748 (Implement formatting arguments for strings and integers, 2013-08-10): Implement `Formatter::pad`
2 parents 65da1ff + 0ca1c9c commit 1b9b515

File tree

1 file changed

+29
-37
lines changed

1 file changed

+29
-37
lines changed

library/core/src/fmt/mod.rs

+29-37
Original file line numberDiff line numberDiff line change
@@ -1693,49 +1693,41 @@ impl<'a> Formatter<'a> {
16931693
/// ```
16941694
#[stable(feature = "rust1", since = "1.0.0")]
16951695
pub fn pad(&mut self, s: &str) -> Result {
1696-
// Make sure there's a fast path up front
1696+
// Make sure there's a fast path up front.
16971697
if self.options.width.is_none() && self.options.precision.is_none() {
16981698
return self.buf.write_str(s);
16991699
}
1700-
// The `precision` field can be interpreted as a `max-width` for the
1700+
1701+
// The `precision` field can be interpreted as a maximum width for the
17011702
// string being formatted.
1702-
let s = if let Some(max) = self.options.precision {
1703-
// If our string is longer that the precision, then we must have
1704-
// truncation. However other flags like `fill`, `width` and `align`
1705-
// must act as always.
1706-
if let Some((i, _)) = s.char_indices().nth(max) {
1707-
// LLVM here can't prove that `..i` won't panic `&s[..i]`, but
1708-
// we know that it can't panic. Use `get` + `unwrap_or` to avoid
1709-
// `unsafe` and otherwise don't emit any panic-related code
1710-
// here.
1711-
s.get(..i).unwrap_or(s)
1712-
} else {
1713-
&s
1714-
}
1703+
let (s, char_count) = if let Some(max_char_count) = self.options.precision {
1704+
let mut iter = s.char_indices();
1705+
let remaining = match iter.advance_by(max_char_count) {
1706+
Ok(()) => 0,
1707+
Err(remaining) => remaining.get(),
1708+
};
1709+
// SAFETY: The offset of `.char_indices()` is guaranteed to be
1710+
// in-bounds and between character boundaries.
1711+
let truncated = unsafe { s.get_unchecked(..iter.offset()) };
1712+
(truncated, max_char_count - remaining)
17151713
} else {
1716-
&s
1714+
// Use the optimized char counting algorithm for the full string.
1715+
(s, s.chars().count())
17171716
};
1718-
// The `width` field is more of a `min-width` parameter at this point.
1719-
match self.options.width {
1720-
// If we're under the maximum length, and there's no minimum length
1721-
// requirements, then we can just emit the string
1722-
None => self.buf.write_str(s),
1723-
Some(width) => {
1724-
let chars_count = s.chars().count();
1725-
// If we're under the maximum width, check if we're over the minimum
1726-
// width, if so it's as easy as just emitting the string.
1727-
if chars_count >= width {
1728-
self.buf.write_str(s)
1729-
}
1730-
// If we're under both the maximum and the minimum width, then fill
1731-
// up the minimum width with the specified string + some alignment.
1732-
else {
1733-
let align = Alignment::Left;
1734-
let post_padding = self.padding(width - chars_count, align)?;
1735-
self.buf.write_str(s)?;
1736-
post_padding.write(self)
1737-
}
1738-
}
1717+
1718+
// The `width` field is more of a minimum width parameter at this point.
1719+
if let Some(width) = self.options.width
1720+
&& char_count < width
1721+
{
1722+
// If we're under the minimum width, then fill up the minimum width
1723+
// with the specified string + some alignment.
1724+
let post_padding = self.padding(width - char_count, Alignment::Left)?;
1725+
self.buf.write_str(s)?;
1726+
post_padding.write(self)
1727+
} else {
1728+
// If we're over the minimum width or there is no minimum width, we
1729+
// can just emit the string.
1730+
self.buf.write_str(s)
17391731
}
17401732
}
17411733

0 commit comments

Comments
 (0)