diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index a1bf3a4d7a7..7827d8b3420 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1693,49 +1693,37 @@ impl<'a> Formatter<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn pad(&mut self, s: &str) -> Result { - // Make sure there's a fast path up front + // Make sure there's a fast path up front. if self.options.width.is_none() && self.options.precision.is_none() { return self.buf.write_str(s); } - // The `precision` field can be interpreted as a `max-width` for the + + // The `precision` field can be interpreted as a maximum width for the // string being formatted. - let s = if let Some(max) = self.options.precision { - // If our string is longer that the precision, then we must have - // truncation. However other flags like `fill`, `width` and `align` - // must act as always. - if let Some((i, _)) = s.char_indices().nth(max) { - // LLVM here can't prove that `..i` won't panic `&s[..i]`, but - // we know that it can't panic. Use `get` + `unwrap_or` to avoid - // `unsafe` and otherwise don't emit any panic-related code - // here. - s.get(..i).unwrap_or(s) - } else { - &s - } + let max_char_count = self.options.precision.unwrap_or(usize::MAX); + let mut iter = s.chars(); + let char_count = iter.by_ref().take(max_char_count).count(); + + // If our string is longer than the maximum width, truncate it and + // handle other flags in terms of the truncated string. + let byte_len = s.len() - iter.as_str().len(); + // SAFETY: The index is derived from the offset of `.chars()`, which is + // guaranteed to be in-bounds and between character boundaries. + let s = unsafe { s.get_unchecked(..byte_len) }; + + // The `width` field is more of a minimum width parameter at this point. + if let Some(width) = self.options.width + && char_count < width + { + // If we're under the minimum width, then fill up the minimum width + // with the specified string + some alignment. + let post_padding = self.padding(width - char_count, Alignment::Left)?; + self.buf.write_str(s)?; + post_padding.write(self) } else { - &s - }; - // The `width` field is more of a `min-width` parameter at this point. - match self.options.width { - // If we're under the maximum length, and there's no minimum length - // requirements, then we can just emit the string - None => self.buf.write_str(s), - Some(width) => { - let chars_count = s.chars().count(); - // If we're under the maximum width, check if we're over the minimum - // width, if so it's as easy as just emitting the string. - if chars_count >= width { - self.buf.write_str(s) - } - // If we're under both the maximum and the minimum width, then fill - // up the minimum width with the specified string + some alignment. - else { - let align = Alignment::Left; - let post_padding = self.padding(width - chars_count, align)?; - self.buf.write_str(s)?; - post_padding.write(self) - } - } + // If we're over the minimum width or there is no minimum width, we + // can just emit the string. + self.buf.write_str(s) } }