summaryrefslogtreecommitdiffstats
path: root/library/std/src/io
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/io')
-rw-r--r--library/std/src/io/buffered/bufreader.rs31
-rw-r--r--library/std/src/io/buffered/bufwriter.rs140
-rw-r--r--library/std/src/io/buffered/linewriter.rs50
-rw-r--r--library/std/src/io/buffered/linewritershim.rs6
-rw-r--r--library/std/src/io/copy.rs110
-rw-r--r--library/std/src/io/copy/tests.rs108
-rw-r--r--library/std/src/io/mod.rs13
-rw-r--r--library/std/src/io/util/tests.rs61
8 files changed, 330 insertions, 189 deletions
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index 4f339a18a..7097dfef8 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -47,13 +47,13 @@ use buffer::Buffer;
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
-pub struct BufReader<R> {
- inner: R,
+pub struct BufReader<R: ?Sized> {
buf: Buffer,
+ inner: R,
}
impl<R: Read> BufReader<R> {
- /// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KB,
+ /// Creates a new `BufReader<R>` with a default buffer capacity. The default is currently 8 KiB,
/// but may change in the future.
///
/// # Examples
@@ -95,7 +95,7 @@ impl<R: Read> BufReader<R> {
}
}
-impl<R> BufReader<R> {
+impl<R: ?Sized> BufReader<R> {
/// Gets a reference to the underlying reader.
///
/// It is inadvisable to directly read from the underlying reader.
@@ -213,26 +213,29 @@ impl<R> BufReader<R> {
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn into_inner(self) -> R {
+ pub fn into_inner(self) -> R
+ where
+ R: Sized,
+ {
self.inner
}
/// Invalidates all data in the internal buffer.
#[inline]
- fn discard_buffer(&mut self) {
+ pub(in crate::io) fn discard_buffer(&mut self) {
self.buf.discard_buffer()
}
}
// This is only used by a test which asserts that the initialization-tracking is correct.
#[cfg(test)]
-impl<R> BufReader<R> {
+impl<R: ?Sized> BufReader<R> {
pub fn initialized(&self) -> usize {
self.buf.initialized()
}
}
-impl<R: Seek> BufReader<R> {
+impl<R: ?Sized + Seek> BufReader<R> {
/// Seeks relative to the current position. If the new position lies within the buffer,
/// the buffer will not be flushed, allowing for more efficient seeks.
/// This method does not return the location of the underlying reader, so the caller
@@ -257,7 +260,7 @@ impl<R: Seek> BufReader<R> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<R: Read> Read for BufReader<R> {
+impl<R: ?Sized + Read> Read for BufReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// If we don't have any buffered data and we're doing a massive read
// (larger than our internal buffer), bypass our internal buffer
@@ -371,7 +374,7 @@ impl<R: Read> Read for BufReader<R> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<R: Read> BufRead for BufReader<R> {
+impl<R: ?Sized + Read> BufRead for BufReader<R> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.buf.fill_buf(&mut self.inner)
}
@@ -384,11 +387,11 @@ impl<R: Read> BufRead for BufReader<R> {
#[stable(feature = "rust1", since = "1.0.0")]
impl<R> fmt::Debug for BufReader<R>
where
- R: fmt::Debug,
+ R: ?Sized + fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("BufReader")
- .field("reader", &self.inner)
+ .field("reader", &&self.inner)
.field(
"buffer",
&format_args!("{}/{}", self.buf.filled() - self.buf.pos(), self.capacity()),
@@ -398,7 +401,7 @@ where
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<R: Seek> Seek for BufReader<R> {
+impl<R: ?Sized + Seek> Seek for BufReader<R> {
/// Seek to an offset, in bytes, in the underlying reader.
///
/// The position used for seeking with <code>[SeekFrom::Current]\(_)</code> is the
@@ -491,7 +494,7 @@ impl<R: Seek> Seek for BufReader<R> {
}
}
-impl<T> SizeHint for BufReader<T> {
+impl<T: ?Sized> SizeHint for BufReader<T> {
#[inline]
fn lower_bound(&self) -> usize {
SizeHint::lower_bound(self.get_ref()) + self.buffer().len()
diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs
index 14c455d4f..0f04f2911 100644
--- a/library/std/src/io/buffered/bufwriter.rs
+++ b/library/std/src/io/buffered/bufwriter.rs
@@ -67,8 +67,7 @@ use crate::ptr;
/// [`TcpStream`]: crate::net::TcpStream
/// [`flush`]: BufWriter::flush
#[stable(feature = "rust1", since = "1.0.0")]
-pub struct BufWriter<W: Write> {
- inner: W,
+pub struct BufWriter<W: ?Sized + Write> {
// The buffer. Avoid using this like a normal `Vec` in common code paths.
// That is, don't use `buf.push`, `buf.extend_from_slice`, or any other
// methods that require bounds checking or the like. This makes an enormous
@@ -78,10 +77,11 @@ pub struct BufWriter<W: Write> {
// write the buffered data a second time in BufWriter's destructor. This
// flag tells the Drop impl if it should skip the flush.
panicked: bool,
+ inner: W,
}
impl<W: Write> BufWriter<W> {
- /// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KB,
+ /// Creates a new `BufWriter<W>` with a default buffer capacity. The default is currently 8 KiB,
/// but may change in the future.
///
/// # Examples
@@ -115,6 +115,69 @@ impl<W: Write> BufWriter<W> {
BufWriter { inner, buf: Vec::with_capacity(capacity), panicked: false }
}
+ /// Unwraps this `BufWriter<W>`, returning the underlying writer.
+ ///
+ /// The buffer is written out before returning the writer.
+ ///
+ /// # Errors
+ ///
+ /// An [`Err`] will be returned if an error occurs while flushing the buffer.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::io::BufWriter;
+ /// use std::net::TcpStream;
+ ///
+ /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
+ ///
+ /// // unwrap the TcpStream and flush the buffer
+ /// let stream = buffer.into_inner().unwrap();
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
+ match self.flush_buf() {
+ Err(e) => Err(IntoInnerError::new(self, e)),
+ Ok(()) => Ok(self.into_parts().0),
+ }
+ }
+
+ /// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but
+ /// unwritten data.
+ ///
+ /// If the underlying writer panicked, it is not known what portion of the data was written.
+ /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer
+ /// contents can still be recovered).
+ ///
+ /// `into_parts` makes no attempt to flush data and cannot fail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::{BufWriter, Write};
+ ///
+ /// let mut buffer = [0u8; 10];
+ /// let mut stream = BufWriter::new(buffer.as_mut());
+ /// write!(stream, "too much data").unwrap();
+ /// stream.flush().expect_err("it doesn't fit");
+ /// let (recovered_writer, buffered_data) = stream.into_parts();
+ /// assert_eq!(recovered_writer.len(), 0);
+ /// assert_eq!(&buffered_data.unwrap(), b"ata");
+ /// ```
+ #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
+ pub fn into_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) {
+ let buf = mem::take(&mut self.buf);
+ let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
+
+ // SAFETY: forget(self) prevents double dropping inner
+ let inner = unsafe { ptr::read(&self.inner) };
+ mem::forget(self);
+
+ (inner, buf)
+ }
+}
+
+impl<W: ?Sized + Write> BufWriter<W> {
/// Send data in our local buffer into the inner writer, looping as
/// necessary until either it's all been sent or an error occurs.
///
@@ -284,67 +347,6 @@ impl<W: Write> BufWriter<W> {
self.buf.capacity()
}
- /// Unwraps this `BufWriter<W>`, returning the underlying writer.
- ///
- /// The buffer is written out before returning the writer.
- ///
- /// # Errors
- ///
- /// An [`Err`] will be returned if an error occurs while flushing the buffer.
- ///
- /// # Examples
- ///
- /// ```no_run
- /// use std::io::BufWriter;
- /// use std::net::TcpStream;
- ///
- /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap());
- ///
- /// // unwrap the TcpStream and flush the buffer
- /// let stream = buffer.into_inner().unwrap();
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
- match self.flush_buf() {
- Err(e) => Err(IntoInnerError::new(self, e)),
- Ok(()) => Ok(self.into_parts().0),
- }
- }
-
- /// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but
- /// unwritten data.
- ///
- /// If the underlying writer panicked, it is not known what portion of the data was written.
- /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer
- /// contents can still be recovered).
- ///
- /// `into_parts` makes no attempt to flush data and cannot fail.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::io::{BufWriter, Write};
- ///
- /// let mut buffer = [0u8; 10];
- /// let mut stream = BufWriter::new(buffer.as_mut());
- /// write!(stream, "too much data").unwrap();
- /// stream.flush().expect_err("it doesn't fit");
- /// let (recovered_writer, buffered_data) = stream.into_parts();
- /// assert_eq!(recovered_writer.len(), 0);
- /// assert_eq!(&buffered_data.unwrap(), b"ata");
- /// ```
- #[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
- pub fn into_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) {
- let buf = mem::take(&mut self.buf);
- let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
-
- // SAFETY: forget(self) prevents double dropping inner
- let inner = unsafe { ptr::read(&self.inner) };
- mem::forget(self);
-
- (inner, buf)
- }
-
// Ensure this function does not get inlined into `write`, so that it
// remains inlineable and its common path remains as short as possible.
// If this function ends up being called frequently relative to `write`,
@@ -511,7 +513,7 @@ impl fmt::Debug for WriterPanicked {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<W: Write> Write for BufWriter<W> {
+impl<W: ?Sized + Write> Write for BufWriter<W> {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
// Use < instead of <= to avoid a needless trip through the buffer in some cases.
@@ -640,20 +642,20 @@ impl<W: Write> Write for BufWriter<W> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<W: Write> fmt::Debug for BufWriter<W>
+impl<W: ?Sized + Write> fmt::Debug for BufWriter<W>
where
W: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("BufWriter")
- .field("writer", &self.inner)
+ .field("writer", &&self.inner)
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity()))
.finish()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<W: Write + Seek> Seek for BufWriter<W> {
+impl<W: ?Sized + Write + Seek> Seek for BufWriter<W> {
/// Seek to the offset, in bytes, in the underlying writer.
///
/// Seeking always writes out the internal buffer before seeking.
@@ -664,7 +666,7 @@ impl<W: Write + Seek> Seek for BufWriter<W> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<W: Write> Drop for BufWriter<W> {
+impl<W: ?Sized + Write> Drop for BufWriter<W> {
fn drop(&mut self) {
if !self.panicked {
// dtors should not panic, so we ignore a failed flush
diff --git a/library/std/src/io/buffered/linewriter.rs b/library/std/src/io/buffered/linewriter.rs
index a26a4ab33..3d4ae7041 100644
--- a/library/std/src/io/buffered/linewriter.rs
+++ b/library/std/src/io/buffered/linewriter.rs
@@ -64,7 +64,7 @@ use crate::io::{self, buffered::LineWriterShim, BufWriter, IntoInnerError, IoSli
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
-pub struct LineWriter<W: Write> {
+pub struct LineWriter<W: ?Sized + Write> {
inner: BufWriter<W>,
}
@@ -109,27 +109,6 @@ impl<W: Write> LineWriter<W> {
LineWriter { inner: BufWriter::with_capacity(capacity, inner) }
}
- /// Gets a reference to the underlying writer.
- ///
- /// # Examples
- ///
- /// ```no_run
- /// use std::fs::File;
- /// use std::io::LineWriter;
- ///
- /// fn main() -> std::io::Result<()> {
- /// let file = File::create("poem.txt")?;
- /// let file = LineWriter::new(file);
- ///
- /// let reference = file.get_ref();
- /// Ok(())
- /// }
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn get_ref(&self) -> &W {
- self.inner.get_ref()
- }
-
/// Gets a mutable reference to the underlying writer.
///
/// Caution must be taken when calling methods on the mutable reference
@@ -184,8 +163,31 @@ impl<W: Write> LineWriter<W> {
}
}
+impl<W: ?Sized + Write> LineWriter<W> {
+ /// Gets a reference to the underlying writer.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// use std::fs::File;
+ /// use std::io::LineWriter;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let file = File::create("poem.txt")?;
+ /// let file = LineWriter::new(file);
+ ///
+ /// let reference = file.get_ref();
+ /// Ok(())
+ /// }
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn get_ref(&self) -> &W {
+ self.inner.get_ref()
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
-impl<W: Write> Write for LineWriter<W> {
+impl<W: ?Sized + Write> Write for LineWriter<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
LineWriterShim::new(&mut self.inner).write(buf)
}
@@ -216,7 +218,7 @@ impl<W: Write> Write for LineWriter<W> {
}
#[stable(feature = "rust1", since = "1.0.0")]
-impl<W: Write> fmt::Debug for LineWriter<W>
+impl<W: ?Sized + Write> fmt::Debug for LineWriter<W>
where
W: fmt::Debug,
{
diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs
index 0175d2693..f2a55da05 100644
--- a/library/std/src/io/buffered/linewritershim.rs
+++ b/library/std/src/io/buffered/linewritershim.rs
@@ -11,11 +11,11 @@ use crate::sys_common::memchr;
/// `BufWriters` to be temporarily given line-buffering logic; this is what
/// enables Stdout to be alternately in line-buffered or block-buffered mode.
#[derive(Debug)]
-pub struct LineWriterShim<'a, W: Write> {
+pub struct LineWriterShim<'a, W: ?Sized + Write> {
buffer: &'a mut BufWriter<W>,
}
-impl<'a, W: Write> LineWriterShim<'a, W> {
+impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> {
pub fn new(buffer: &'a mut BufWriter<W>) -> Self {
Self { buffer }
}
@@ -49,7 +49,7 @@ impl<'a, W: Write> LineWriterShim<'a, W> {
}
}
-impl<'a, W: Write> Write for LineWriterShim<'a, W> {
+impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> {
/// Write some data into this BufReader with line buffering. This means
/// that, if any newlines are present in the data, the data up to the last
/// newline is sent directly to the underlying writer, and data after it
diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs
index 1d9d93f5b..ef1f4031e 100644
--- a/library/std/src/io/copy.rs
+++ b/library/std/src/io/copy.rs
@@ -1,6 +1,9 @@
-use super::{BorrowedBuf, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
+use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
use crate::mem::MaybeUninit;
+#[cfg(test)]
+mod tests;
+
/// Copies the entire contents of a reader into a writer.
///
/// This function will continuously read data from `reader` and then
@@ -71,32 +74,113 @@ where
R: Read,
W: Write,
{
- BufferedCopySpec::copy_to(reader, writer)
+ let read_buf = BufferedReaderSpec::buffer_size(reader);
+ let write_buf = BufferedWriterSpec::buffer_size(writer);
+
+ if read_buf >= DEFAULT_BUF_SIZE && read_buf >= write_buf {
+ return BufferedReaderSpec::copy_to(reader, writer);
+ }
+
+ BufferedWriterSpec::copy_from(writer, reader)
+}
+
+/// Specialization of the read-write loop that reuses the internal
+/// buffer of a BufReader. If there's no buffer then the writer side
+/// should be used intead.
+trait BufferedReaderSpec {
+ fn buffer_size(&self) -> usize;
+
+ fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64>;
+}
+
+impl<T> BufferedReaderSpec for T
+where
+ Self: Read,
+ T: ?Sized,
+{
+ #[inline]
+ default fn buffer_size(&self) -> usize {
+ 0
+ }
+
+ default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> {
+ unimplemented!("only called from specializations");
+ }
+}
+
+impl<I> BufferedReaderSpec for BufReader<I>
+where
+ Self: Read,
+ I: ?Sized,
+{
+ fn buffer_size(&self) -> usize {
+ self.capacity()
+ }
+
+ fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
+ let mut len = 0;
+
+ loop {
+ // Hack: this relies on `impl Read for BufReader` always calling fill_buf
+ // if the buffer is empty, even for empty slices.
+ // It can't be called directly here since specialization prevents us
+ // from adding I: Read
+ match self.read(&mut []) {
+ Ok(_) => {}
+ Err(e) if e.kind() == ErrorKind::Interrupted => continue,
+ Err(e) => return Err(e),
+ }
+ let buf = self.buffer();
+ if self.buffer().len() == 0 {
+ return Ok(len);
+ }
+
+ // In case the writer side is a BufWriter then its write_all
+ // implements an optimization that passes through large
+ // buffers to the underlying writer. That code path is #[cold]
+ // but we're still avoiding redundant memcopies when doing
+ // a copy between buffered inputs and outputs.
+ to.write_all(buf)?;
+ len += buf.len() as u64;
+ self.discard_buffer();
+ }
+ }
}
/// Specialization of the read-write loop that either uses a stack buffer
/// or reuses the internal buffer of a BufWriter
-trait BufferedCopySpec: Write {
- fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64>;
+trait BufferedWriterSpec: Write {
+ fn buffer_size(&self) -> usize;
+
+ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64>;
}
-impl<W: Write + ?Sized> BufferedCopySpec for W {
- default fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> {
- stack_buffer_copy(reader, writer)
+impl<W: Write + ?Sized> BufferedWriterSpec for W {
+ #[inline]
+ default fn buffer_size(&self) -> usize {
+ 0
+ }
+
+ default fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
+ stack_buffer_copy(reader, self)
}
}
-impl<I: Write> BufferedCopySpec for BufWriter<I> {
- fn copy_to<R: Read + ?Sized>(reader: &mut R, writer: &mut Self) -> Result<u64> {
- if writer.capacity() < DEFAULT_BUF_SIZE {
- return stack_buffer_copy(reader, writer);
+impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
+ fn buffer_size(&self) -> usize {
+ self.capacity()
+ }
+
+ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
+ if self.capacity() < DEFAULT_BUF_SIZE {
+ return stack_buffer_copy(reader, self);
}
let mut len = 0;
let mut init = 0;
loop {
- let buf = writer.buffer_mut();
+ let buf = self.buffer_mut();
let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
unsafe {
@@ -127,7 +211,7 @@ impl<I: Write> BufferedCopySpec for BufWriter<I> {
Err(e) => return Err(e),
}
} else {
- writer.flush_buf()?;
+ self.flush_buf()?;
init = 0;
}
}
diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs
new file mode 100644
index 000000000..8c816af15
--- /dev/null
+++ b/library/std/src/io/copy/tests.rs
@@ -0,0 +1,108 @@
+use crate::cmp::{max, min};
+use crate::io::*;
+
+#[test]
+fn copy_copies() {
+ let mut r = repeat(0).take(4);
+ let mut w = sink();
+ assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
+
+ let mut r = repeat(0).take(1 << 17);
+ assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
+}
+
+struct ShortReader {
+ cap: usize,
+ read_size: usize,
+ observed_buffer: usize,
+}
+
+impl Read for ShortReader {
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+ let bytes = min(self.cap, self.read_size);
+ self.cap -= bytes;
+ self.observed_buffer = max(self.observed_buffer, buf.len());
+ Ok(bytes)
+ }
+}
+
+struct WriteObserver {
+ observed_buffer: usize,
+}
+
+impl Write for WriteObserver {
+ fn write(&mut self, buf: &[u8]) -> Result<usize> {
+ self.observed_buffer = max(self.observed_buffer, buf.len());
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> Result<()> {
+ Ok(())
+ }
+}
+
+#[test]
+fn copy_specializes_bufwriter() {
+ let cap = 117 * 1024;
+ let buf_sz = 16 * 1024;
+ let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
+ let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 });
+ assert_eq!(
+ copy(&mut r, &mut w).unwrap(),
+ cap as u64,
+ "expected the whole capacity to be copied"
+ );
+ assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader");
+ assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes");
+}
+
+#[test]
+fn copy_specializes_bufreader() {
+ let mut source = vec![0; 768 * 1024];
+ source[1] = 42;
+ let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source));
+
+ let mut sink = Vec::new();
+ assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
+ assert_eq!(source.as_slice(), sink.as_slice());
+
+ let buf_sz = 71 * 1024;
+ assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition");
+
+ let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source));
+ let mut sink = WriteObserver { observed_buffer: 0 };
+ assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
+ assert_eq!(
+ sink.observed_buffer, buf_sz,
+ "expected a large buffer to be provided to the writer"
+ );
+}
+
+#[cfg(unix)]
+mod io_benches {
+ use crate::fs::File;
+ use crate::fs::OpenOptions;
+ use crate::io::prelude::*;
+ use crate::io::BufReader;
+
+ use test::Bencher;
+
+ #[bench]
+ fn bench_copy_buf_reader(b: &mut Bencher) {
+ let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed");
+ // use dyn to avoid specializations unrelated to readbuf
+ let dyn_in = &mut file_in as &mut dyn Read;
+ let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0));
+ let mut writer =
+ OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed");
+
+ const BYTES: u64 = 1024 * 1024;
+
+ b.bytes = BYTES;
+
+ b.iter(|| {
+ reader.get_mut().set_limit(BYTES);
+ crate::io::copy(&mut reader, &mut writer).unwrap()
+ });
+ }
+}
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 8a007d095..71d91f213 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -1416,17 +1416,18 @@ pub trait Write {
///
/// This function will attempt to write the entire contents of `buf`, but
/// the entire write might not succeed, or the write may also generate an
- /// error. A call to `write` represents *at most one* attempt to write to
+ /// error. Typically, a call to `write` represents one attempt to write to
/// any wrapped object.
///
/// Calls to `write` are not guaranteed to block waiting for data to be
/// written, and a write which would otherwise block can be indicated through
/// an [`Err`] variant.
///
- /// If the return value is [`Ok(n)`] then it must be guaranteed that
- /// `n <= buf.len()`. A return value of `0` typically means that the
- /// underlying object is no longer able to accept bytes and will likely not
- /// be able to in the future as well, or that the buffer provided is empty.
+ /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`].
+ /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`.
+ /// A return value of `Ok(0)` typically means that the underlying object is
+ /// no longer able to accept bytes and will likely not be able to in the
+ /// future as well, or that the buffer provided is empty.
///
/// # Errors
///
@@ -2754,7 +2755,7 @@ trait SizeHint {
}
}
-impl<T> SizeHint for T {
+impl<T: ?Sized> SizeHint for T {
#[inline]
default fn lower_bound(&self) -> usize {
0
diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs
index ce5e2c9da..1baa94e64 100644
--- a/library/std/src/io/util/tests.rs
+++ b/library/std/src/io/util/tests.rs
@@ -1,68 +1,9 @@
-use crate::cmp::{max, min};
use crate::io::prelude::*;
-use crate::io::{
- copy, empty, repeat, sink, BorrowedBuf, BufWriter, Empty, Repeat, Result, SeekFrom, Sink,
- DEFAULT_BUF_SIZE,
-};
+use crate::io::{empty, repeat, sink, BorrowedBuf, Empty, Repeat, SeekFrom, Sink};
use crate::mem::MaybeUninit;
#[test]
-fn copy_copies() {
- let mut r = repeat(0).take(4);
- let mut w = sink();
- assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
-
- let mut r = repeat(0).take(1 << 17);
- assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
-}
-
-struct ShortReader {
- cap: usize,
- read_size: usize,
- observed_buffer: usize,
-}
-
-impl Read for ShortReader {
- fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
- let bytes = min(self.cap, self.read_size);
- self.cap -= bytes;
- self.observed_buffer = max(self.observed_buffer, buf.len());
- Ok(bytes)
- }
-}
-
-struct WriteObserver {
- observed_buffer: usize,
-}
-
-impl Write for WriteObserver {
- fn write(&mut self, buf: &[u8]) -> Result<usize> {
- self.observed_buffer = max(self.observed_buffer, buf.len());
- Ok(buf.len())
- }
-
- fn flush(&mut self) -> Result<()> {
- Ok(())
- }
-}
-
-#[test]
-fn copy_specializes_bufwriter() {
- let cap = 117 * 1024;
- let buf_sz = 16 * 1024;
- let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
- let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 });
- assert_eq!(
- copy(&mut r, &mut w).unwrap(),
- cap as u64,
- "expected the whole capacity to be copied"
- );
- assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader");
- assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes");
-}
-
-#[test]
fn sink_sinks() {
let mut s = sink();
assert_eq!(s.write(&[]).unwrap(), 0);