From 7c09cd537cac7bccded929f3e99b3f93d88165bf Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 11 Nov 2017 09:03:21 +0100 Subject: [PATCH 1/6] Use early return in std::io::read_to_end --- src/libstd/io/mod.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index b7a3695b47096..8ba2b7da76cce 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -366,7 +366,6 @@ 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; loop { if g.len == g.buf.len() { unsafe { @@ -378,20 +377,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. From 44c93ce43685869d44f94424c5b83d99e91342de Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 11 Nov 2017 09:29:34 +0100 Subject: [PATCH 2/6] Add Read::size_hint, similar to Iterator::size_hint --- src/libstd/fs.rs | 11 +++++++++++ src/libstd/io/buffered.rs | 6 ++++++ src/libstd/io/cursor.rs | 4 ++++ src/libstd/io/impls.rs | 15 +++++++++++++++ src/libstd/io/mod.rs | 29 +++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+) diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index b07733d3c803c..da1fb8b5c4532 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -449,6 +449,13 @@ impl Read for File { self.inner.read(buf) } + fn size_hint(&self) -> usize { + match self.metadata() { + Ok(meta) => meta.len() as usize, + Err(_) => 0, + } + } + #[inline] unsafe fn initializer(&self) -> Initializer { Initializer::nop() @@ -473,6 +480,10 @@ impl<'a> Read for &'a File { self.inner.read(buf) } + fn size_hint(&self) -> usize { + (**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..c8a78e68abf1e 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) -> usize { + let buffered_len = self.cap - self.pos; + 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..5aecdd920976f 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) -> usize { + (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..120e0d1ca3c60 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) -> usize { + (**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) -> usize { + (**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) -> usize { + 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 8ba2b7da76cce..c4013de331faf 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -544,6 +544,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) -> usize { + 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 @@ -1720,6 +1733,14 @@ impl Read for Chain { self.second.read(buf) } + fn size_hint(&self) -> usize { + if self.done_first { + self.second.size_hint() + } else { + self.first.size_hint().saturating_add(self.second.size_hint()) + } + } + unsafe fn initializer(&self) -> Initializer { let initializer = self.first.initializer(); if initializer.should_initialize() { @@ -1918,6 +1939,14 @@ impl Read for Take { Ok(n) } + fn size_hint(&self) -> usize { + if self.limit == 0 { + 0 + } else { + cmp::min(self.limit, self.inner.size_hint() as u64) as usize + } + } + unsafe fn initializer(&self) -> Initializer { self.inner.initializer() } From 6adc9e6ec98b761201873cd33ead815117f0da86 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 11 Nov 2017 09:34:05 +0100 Subject: [PATCH 3/6] Pre-allocate in Read::read_to_end and read_to_string based on size_hint --- src/libstd/io/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index c4013de331faf..54cfe3fe4e514 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -366,6 +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 size_hint = r.size_hint(); + if size_hint > 0 { + unsafe { + g.buf.reserve(size_hint); + 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 { From a84c02852a83d9b6d91249afc42ab6cbb282a8c2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 28 Nov 2017 19:37:33 +0100 Subject: [PATCH 4/6] read_to_end: reserve 1 byte more than size_hint This one byte of extra capacity is necessary for the final zero-size read that indicates EOF. --- src/libstd/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 54cfe3fe4e514..37de819aa11c9 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -370,7 +370,7 @@ fn read_to_end(r: &mut R, buf: &mut Vec) -> Result let size_hint = r.size_hint(); if size_hint > 0 { unsafe { - g.buf.reserve(size_hint); + 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..]); From 1599ffc6a3e463080a3efba9c16036b92b95adfc Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 30 Nov 2017 14:17:57 +0100 Subject: [PATCH 5/6] Change Read::size_hint return io::Result --- src/libstd/fs.rs | 9 +++------ src/libstd/io/buffered.rs | 4 ++-- src/libstd/io/cursor.rs | 4 ++-- src/libstd/io/impls.rs | 8 ++++---- src/libstd/io/mod.rs | 16 ++++++++-------- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index da1fb8b5c4532..2ae83ae828418 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -449,11 +449,8 @@ impl Read for File { self.inner.read(buf) } - fn size_hint(&self) -> usize { - match self.metadata() { - Ok(meta) => meta.len() as usize, - Err(_) => 0, - } + fn size_hint(&self) -> io::Result { + Ok(self.metadata()?.len() as usize) } #[inline] @@ -480,7 +477,7 @@ impl<'a> Read for &'a File { self.inner.read(buf) } - fn size_hint(&self) -> usize { + fn size_hint(&self) -> io::Result { (**self).size_hint() } diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index c8a78e68abf1e..1f8113575b8e7 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -212,9 +212,9 @@ impl Read for BufReader { } #[inline] - fn size_hint(&self) -> usize { + fn size_hint(&self) -> io::Result { let buffered_len = self.cap - self.pos; - buffered_len.saturating_add(self.inner.size_hint()) + Ok(buffered_len.saturating_add(self.inner.size_hint()?)) } // we can't skip unconditionally because of the large buffer case in read. diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 5aecdd920976f..1bb00d6e25061 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -230,8 +230,8 @@ impl Read for Cursor where T: AsRef<[u8]> { Ok(n) } - fn size_hint(&self) -> usize { - (self.inner.as_ref().len() as u64).saturating_sub(self.pos) as usize + fn size_hint(&self) -> io::Result { + Ok((self.inner.as_ref().len() as u64).saturating_sub(self.pos) as usize) } #[inline] diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index 120e0d1ca3c60..ce4af0db395aa 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -24,7 +24,7 @@ impl<'a, R: Read + ?Sized> Read for &'a mut R { } #[inline] - fn size_hint(&self) -> usize { + fn size_hint(&self) -> io::Result { (**self).size_hint() } @@ -98,7 +98,7 @@ impl Read for Box { } #[inline] - fn size_hint(&self) -> usize { + fn size_hint(&self) -> io::Result { (**self).size_hint() } @@ -192,8 +192,8 @@ impl<'a> Read for &'a [u8] { } #[inline] - fn size_hint(&self) -> usize { - self.len() + fn size_hint(&self) -> io::Result { + Ok(self.len()) } #[inline] diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 37de819aa11c9..e43f49fe337ba 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -367,7 +367,7 @@ 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 size_hint = r.size_hint(); + let size_hint = r.size_hint()?; if size_hint > 0 { unsafe { g.buf.reserve(size_hint.saturating_add(1)); @@ -564,8 +564,8 @@ pub trait Read { /// [`read_to_string`]: #method.read_to_string #[unstable(feature = "read_size_hint", issue = /* FIXME */ "0")] #[inline] - fn size_hint(&self) -> usize { - 0 + fn size_hint(&self) -> Result { + Ok(0) } /// Read all bytes until EOF in this source, placing them into `buf`. @@ -1744,11 +1744,11 @@ impl Read for Chain { self.second.read(buf) } - fn size_hint(&self) -> usize { + fn size_hint(&self) -> Result { if self.done_first { self.second.size_hint() } else { - self.first.size_hint().saturating_add(self.second.size_hint()) + Ok(self.first.size_hint()?.saturating_add(self.second.size_hint()?)) } } @@ -1950,11 +1950,11 @@ impl Read for Take { Ok(n) } - fn size_hint(&self) -> usize { + fn size_hint(&self) -> Result { if self.limit == 0 { - 0 + Ok(0) } else { - cmp::min(self.limit, self.inner.size_hint() as u64) as usize + Ok(cmp::min(self.limit, self.inner.size_hint()? as u64) as usize) } } From c636c379855627ad73eb15dcc1f551a00519561f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 30 Nov 2017 15:08:34 +0100 Subject: [PATCH 6/6] Account for current position in `File::size_hint` --- src/libstd/fs.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 2ae83ae828418..563cce598fe80 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -450,7 +450,8 @@ impl Read for File { } fn size_hint(&self) -> io::Result { - Ok(self.metadata()?.len() as usize) + let position = self.inner.seek(SeekFrom::Current(0))?; + Ok(self.metadata()?.len().saturating_sub(position) as usize) } #[inline]