Skip to content

Commit 9cdfd5d

Browse files
committed
Auto merge of rust-lang#137762 - thaliaarchi:io-optional-methods/write-fmt, r=<try>
Reserve before `write_fmt` `fmt::Arguments::estimated_capacity()` is currently only used for `fmt::format()` to reserve the initial string capacity. Also use it for `impl Write` for `Vec<u8>`, `VecDeque<u8>`, `Cursor<&mut Vec<u8>>`, and `Cursor<Vec<u8>>`. This may be worth checking perf.
2 parents f45d4ac + 0e82180 commit 9cdfd5d

File tree

3 files changed

+65
-36
lines changed

3 files changed

+65
-36
lines changed

library/std/src/io/cursor.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
mod tests;
33

44
use crate::alloc::Allocator;
5-
use crate::cmp;
65
use crate::io::prelude::*;
76
use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
7+
use crate::{cmp, fmt};
88

99
/// A `Cursor` wraps an in-memory buffer and provides it with a
1010
/// [`Seek`] implementation.
@@ -603,6 +603,11 @@ where
603603
true
604604
}
605605

606+
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
607+
self.inner.reserve(fmt.estimated_capacity());
608+
io::default_write_fmt(self, fmt)
609+
}
610+
606611
#[inline]
607612
fn flush(&mut self) -> io::Result<()> {
608613
Ok(())
@@ -627,6 +632,11 @@ where
627632
true
628633
}
629634

635+
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
636+
self.inner.reserve(fmt.estimated_capacity());
637+
io::default_write_fmt(self, fmt)
638+
}
639+
630640
#[inline]
631641
fn flush(&mut self) -> io::Result<()> {
632642
Ok(())

library/std/src/io/impls.rs

+12
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,12 @@ impl<A: Allocator> Write for Vec<u8, A> {
495495
Ok(())
496496
}
497497

498+
#[inline]
499+
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
500+
self.reserve(fmt.estimated_capacity());
501+
io::default_write_fmt(self, fmt)
502+
}
503+
498504
#[inline]
499505
fn flush(&mut self) -> io::Result<()> {
500506
Ok(())
@@ -638,6 +644,12 @@ impl<A: Allocator> Write for VecDeque<u8, A> {
638644
Ok(())
639645
}
640646

647+
#[inline]
648+
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
649+
self.reserve(fmt.estimated_capacity());
650+
io::default_write_fmt(self, fmt)
651+
}
652+
641653
#[inline]
642654
fn flush(&mut self) -> io::Result<()> {
643655
Ok(())

library/std/src/io/mod.rs

+42-35
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,47 @@ pub(crate) fn default_read_buf_exact<R: Read + ?Sized>(
612612
Ok(())
613613
}
614614

615+
pub(crate) fn default_write_fmt<W: Write + ?Sized>(
616+
this: &mut W,
617+
fmt: fmt::Arguments<'_>,
618+
) -> Result<()> {
619+
// Create a shim which translates a `Write` to a `fmt::Write` and saves off
620+
// I/O errors, instead of discarding them.
621+
struct Adapter<'a, T: ?Sized + 'a> {
622+
inner: &'a mut T,
623+
error: Result<()>,
624+
}
625+
626+
impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
627+
fn write_str(&mut self, s: &str) -> fmt::Result {
628+
match self.inner.write_all(s.as_bytes()) {
629+
Ok(()) => Ok(()),
630+
Err(e) => {
631+
self.error = Err(e);
632+
Err(fmt::Error)
633+
}
634+
}
635+
}
636+
}
637+
638+
let mut output = Adapter { inner: this, error: Ok(()) };
639+
match fmt::write(&mut output, fmt) {
640+
Ok(()) => Ok(()),
641+
Err(..) => {
642+
// Check whether the error came from the underlying `Write`.
643+
if output.error.is_err() {
644+
output.error
645+
} else {
646+
// This shouldn't happen: the underlying stream did not error,
647+
// but somehow the formatter still errored?
648+
panic!(
649+
"a formatting trait implementation returned an error when the underlying stream did not"
650+
);
651+
}
652+
}
653+
}
654+
}
655+
615656
/// The `Read` trait allows for reading bytes from a source.
616657
///
617658
/// Implementors of the `Read` trait are called 'readers'.
@@ -1867,41 +1908,7 @@ pub trait Write {
18671908
/// ```
18681909
#[stable(feature = "rust1", since = "1.0.0")]
18691910
fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> {
1870-
// Create a shim which translates a Write to a fmt::Write and saves
1871-
// off I/O errors. instead of discarding them
1872-
struct Adapter<'a, T: ?Sized + 'a> {
1873-
inner: &'a mut T,
1874-
error: Result<()>,
1875-
}
1876-
1877-
impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
1878-
fn write_str(&mut self, s: &str) -> fmt::Result {
1879-
match self.inner.write_all(s.as_bytes()) {
1880-
Ok(()) => Ok(()),
1881-
Err(e) => {
1882-
self.error = Err(e);
1883-
Err(fmt::Error)
1884-
}
1885-
}
1886-
}
1887-
}
1888-
1889-
let mut output = Adapter { inner: self, error: Ok(()) };
1890-
match fmt::write(&mut output, fmt) {
1891-
Ok(()) => Ok(()),
1892-
Err(..) => {
1893-
// check if the error came from the underlying `Write` or not
1894-
if output.error.is_err() {
1895-
output.error
1896-
} else {
1897-
// This shouldn't happen: the underlying stream did not error, but somehow
1898-
// the formatter still errored?
1899-
panic!(
1900-
"a formatting trait implementation returned an error when the underlying stream did not"
1901-
);
1902-
}
1903-
}
1904-
}
1911+
default_write_fmt(self, fmt)
19051912
}
19061913

19071914
/// Creates a "by reference" adapter for this instance of `Write`.

0 commit comments

Comments
 (0)