Optimize io::Write::write_fmt for constant strings
When the formatting args to `fmt::Write::write_fmt` are a statically known string, it simplifies to only calling `write_str` without a runtime branch. Do the same in `io::Write::write_fmt` with `write_all`. Also, match the convention of `fmt::Write` for the name of `args`.
This commit is contained in:
parent
493c38ba37
commit
a0c3dd4df4
2 changed files with 48 additions and 36 deletions
|
@ -710,9 +710,10 @@ impl<'a> Arguments<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as [`Arguments::as_str`], but will only return `Some(s)` if it can be determined at compile time.
|
/// Same as [`Arguments::as_str`], but will only return `Some(s)` if it can be determined at compile time.
|
||||||
|
#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn as_statically_known_str(&self) -> Option<&'static str> {
|
pub fn as_statically_known_str(&self) -> Option<&'static str> {
|
||||||
let s = self.as_str();
|
let s = self.as_str();
|
||||||
if core::intrinsics::is_val_statically_known(s.is_some()) { s } else { None }
|
if core::intrinsics::is_val_statically_known(s.is_some()) { s } else { None }
|
||||||
}
|
}
|
||||||
|
|
|
@ -612,6 +612,47 @@ pub(crate) fn default_read_buf_exact<R: Read + ?Sized>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn default_write_fmt<W: Write + ?Sized>(
|
||||||
|
this: &mut W,
|
||||||
|
args: fmt::Arguments<'_>,
|
||||||
|
) -> Result<()> {
|
||||||
|
// Create a shim which translates a `Write` to a `fmt::Write` and saves off
|
||||||
|
// I/O errors, instead of discarding them.
|
||||||
|
struct Adapter<'a, T: ?Sized + 'a> {
|
||||||
|
inner: &'a mut T,
|
||||||
|
error: Result<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
match self.inner.write_all(s.as_bytes()) {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(e) => {
|
||||||
|
self.error = Err(e);
|
||||||
|
Err(fmt::Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = Adapter { inner: this, error: Ok(()) };
|
||||||
|
match fmt::write(&mut output, args) {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(..) => {
|
||||||
|
// Check whether the error came from the underlying `Write`.
|
||||||
|
if output.error.is_err() {
|
||||||
|
output.error
|
||||||
|
} else {
|
||||||
|
// This shouldn't happen: the underlying stream did not error,
|
||||||
|
// but somehow the formatter still errored?
|
||||||
|
panic!(
|
||||||
|
"a formatting trait implementation returned an error when the underlying stream did not"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The `Read` trait allows for reading bytes from a source.
|
/// The `Read` trait allows for reading bytes from a source.
|
||||||
///
|
///
|
||||||
/// Implementors of the `Read` trait are called 'readers'.
|
/// Implementors of the `Read` trait are called 'readers'.
|
||||||
|
@ -1866,41 +1907,11 @@ pub trait Write {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> {
|
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> {
|
||||||
// Create a shim which translates a Write to a fmt::Write and saves
|
if let Some(s) = args.as_statically_known_str() {
|
||||||
// off I/O errors. instead of discarding them
|
self.write_all(s.as_bytes())
|
||||||
struct Adapter<'a, T: ?Sized + 'a> {
|
} else {
|
||||||
inner: &'a mut T,
|
default_write_fmt(self, args)
|
||||||
error: Result<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
match self.inner.write_all(s.as_bytes()) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(e) => {
|
|
||||||
self.error = Err(e);
|
|
||||||
Err(fmt::Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut output = Adapter { inner: self, error: Ok(()) };
|
|
||||||
match fmt::write(&mut output, fmt) {
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
Err(..) => {
|
|
||||||
// check if the error came from the underlying `Write` or not
|
|
||||||
if output.error.is_err() {
|
|
||||||
output.error
|
|
||||||
} else {
|
|
||||||
// This shouldn't happen: the underlying stream did not error, but somehow
|
|
||||||
// the formatter still errored?
|
|
||||||
panic!(
|
|
||||||
"a formatting trait implementation returned an error when the underlying stream did not"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue