Skip to content

Commit c90dba4

Browse files
committed
Count char width at most once in Formatter::pad
When both width and precision flags are specified, then the character width is counted 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.
1 parent 942db67 commit c90dba4

File tree

1 file changed

+24
-37
lines changed

1 file changed

+24
-37
lines changed

library/core/src/fmt/mod.rs

Lines changed: 24 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,45 +1697,32 @@ impl<'a> Formatter<'a> {
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+
// The `precision` field can be interpreted as a maximum width for the
17011701
// 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-
}
1702+
let max_chars_count = self.options.precision.unwrap_or(usize::MAX);
1703+
let mut iter = s.chars();
1704+
let chars_count = iter.by_ref().take(max_chars_count).count();
1705+
1706+
// If our string is longer than the maximum width, truncate it and
1707+
// handle other flags in terms of the truncated string.
1708+
let byte_len = s.len() - iter.as_str().len();
1709+
// SAFETY: `char_indices` guarantees the index is in-bounds and between
1710+
// character boundaries.
1711+
let s = unsafe { s.get_unchecked(..byte_len) };
1712+
1713+
// The `width` field is more of a minimum width parameter at this point.
1714+
if let Some(width) = self.options.width
1715+
&& chars_count < width
1716+
{
1717+
// If we're under the minimum width, then fill up the minimum width
1718+
// with the specified string + some alignment.
1719+
let post_padding = self.padding(width - chars_count, Alignment::Left)?;
1720+
self.buf.write_str(s)?;
1721+
post_padding.write(self)
17151722
} else {
1716-
&s
1717-
};
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-
}
1723+
// If we're over the minimum width or there is no minimum width, we
1724+
// can just emit the string.
1725+
self.buf.write_str(s)
17391726
}
17401727
}
17411728

0 commit comments

Comments
 (0)