diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index b07733d3c803c..563cce598fe80 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -449,6 +449,11 @@ impl Read for File { self.inner.read(buf) } + fn size_hint(&self) -> io::Result { + let position = self.inner.seek(SeekFrom::Current(0))?; + Ok(self.metadata()?.len().saturating_sub(position) as usize) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -473,6 +478,10 @@ impl<'a> Read for &'a File { self.inner.read(buf) } + fn size_hint(&self) -> io::Result { + (**self).size_hint() + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 6d3fbc9d26822..1f8113575b8e7 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -211,6 +211,12 @@ impl Read for BufReader { Ok(nread) } + #[inline] + fn size_hint(&self) -> io::Result { + let buffered_len = self.cap - self.pos; + Ok(buffered_len.saturating_add(self.inner.size_hint()?)) + } + // we can't skip unconditionally because of the large buffer case in read. unsafe fn initializer(&self) -> Initializer { self.inner.initializer() diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 32a92145aafed..1bb00d6e25061 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -230,6 +230,10 @@ impl Read for Cursor where T: AsRef<[u8]> { Ok(n) } + fn size_hint(&self) -> io::Result { + Ok((self.inner.as_ref().len() as u64).saturating_sub(self.pos) as usize) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index fe1179a3b4a18..ce4af0db395aa 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -23,6 +23,11 @@ impl<'a, R: Read + ?Sized> Read for &'a mut R { (**self).read(buf) } + #[inline] + fn size_hint(&self) -> io::Result { + (**self).size_hint() + } + #[inline] unsafe fn initializer(&self) -> Initializer { (**self).initializer() @@ -92,6 +97,11 @@ impl Read for Box { (**self).read(buf) } + #[inline] + fn size_hint(&self) -> io::Result { + (**self).size_hint() + } + #[inline] unsafe fn initializer(&self) -> Initializer { (**self).initializer() @@ -181,6 +191,11 @@ impl<'a> Read for &'a [u8] { Ok(amt) } + #[inline] + fn size_hint(&self) -> io::Result { + Ok(self.len()) + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index b7a3695b47096..e43f49fe337ba 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -366,7 +366,17 @@ fn append_to_string(buf: &mut String, f: F) -> Result fn read_to_end(r: &mut R, buf: &mut Vec) -> Result { let start_len = buf.len(); let mut g = Guard { len: buf.len(), buf: buf }; - let ret; + + let size_hint = r.size_hint()?; + if size_hint > 0 { + unsafe { + g.buf.reserve(size_hint.saturating_add(1)); + let capacity = g.buf.capacity(); + g.buf.set_len(capacity); + r.initializer().initialize(&mut g.buf[g.len..]); + } + } + loop { if g.len == g.buf.len() { unsafe { @@ -378,20 +388,12 @@ fn read_to_end(r: &mut R, buf: &mut Vec) -> Result } match r.read(&mut g.buf[g.len..]) { - Ok(0) => { - ret = Ok(g.len - start_len); - break; - } + Ok(0) => return Ok(g.len - start_len), Ok(n) => g.len += n, Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => { - ret = Err(e); - break; - } + Err(e) => return Err(e), } } - - ret } /// The `Read` trait allows for reading bytes from a source. @@ -553,6 +555,19 @@ pub trait Read { Initializer::zeroing() } + /// Return an estimate of how many bytes would be read from this source until EOF, + /// or zero if that is unknown. + /// + /// This is used by [`read_to_end`] and [`read_to_string`] to pre-allocate a memory buffer. + /// + /// [`read_to_end`]: #method.read_to_end + /// [`read_to_string`]: #method.read_to_string + #[unstable(feature = "read_size_hint", issue = /* FIXME */ "0")] + #[inline] + fn size_hint(&self) -> Result { + Ok(0) + } + /// Read all bytes until EOF in this source, placing them into `buf`. /// /// All bytes read from this source will be appended to the specified buffer @@ -1729,6 +1744,14 @@ impl Read for Chain { self.second.read(buf) } + fn size_hint(&self) -> Result { + if self.done_first { + self.second.size_hint() + } else { + Ok(self.first.size_hint()?.saturating_add(self.second.size_hint()?)) + } + } + unsafe fn initializer(&self) -> Initializer { let initializer = self.first.initializer(); if initializer.should_initialize() { @@ -1927,6 +1950,14 @@ impl Read for Take { Ok(n) } + fn size_hint(&self) -> Result { + if self.limit == 0 { + Ok(0) + } else { + Ok(cmp::min(self.limit, self.inner.size_hint()? as u64) as usize) + } + } + unsafe fn initializer(&self) -> Initializer { self.inner.initializer() }