diff options
| author | Steven Fackler <sfackler@gmail.com> | 2017-05-14 21:29:18 -0400 |
|---|---|---|
| committer | Steven Fackler <sfackler@gmail.com> | 2017-06-20 20:26:22 -0700 |
| commit | ecbb896b9eb2acadefde57be493e4298c1aa04a3 (patch) | |
| tree | f46da3b3294c60abfb2343638c79f51124ac95c0 /src/libstd/io | |
| parent | 445077963c55297ef1e196a3525723090fe80b22 (diff) | |
| download | rust-ecbb896b9eb2acadefde57be493e4298c1aa04a3.tar.gz rust-ecbb896b9eb2acadefde57be493e4298c1aa04a3.zip | |
Add `Read::initializer`.
This is an API that allows types to indicate that they can be passed buffers of uninitialized memory which can improve performance.
Diffstat (limited to 'src/libstd/io')
| -rw-r--r-- | src/libstd/io/buffered.rs | 22 | ||||
| -rw-r--r-- | src/libstd/io/cursor.rs | 7 | ||||
| -rw-r--r-- | src/libstd/io/impls.rs | 17 | ||||
| -rw-r--r-- | src/libstd/io/mod.rs | 128 | ||||
| -rw-r--r-- | src/libstd/io/stdio.rs | 23 | ||||
| -rw-r--r-- | src/libstd/io/util.rs | 26 |
6 files changed, 183 insertions, 40 deletions
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 3b82412716e..296ee78aadb 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -15,7 +15,7 @@ use io::prelude::*; use cmp; use error; use fmt; -use io::{self, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom}; +use io::{self, Initializer, DEFAULT_BUF_SIZE, Error, ErrorKind, SeekFrom}; use memchr; /// The `BufReader` struct adds buffering to any reader. @@ -92,11 +92,16 @@ impl<R: Read> BufReader<R> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> { - BufReader { - inner: inner, - buf: vec![0; cap].into_boxed_slice(), - pos: 0, - cap: 0, + unsafe { + let mut buffer = Vec::with_capacity(cap); + buffer.set_len(cap); + inner.initializer().initialize(&mut buffer); + BufReader { + inner: inner, + buf: buffer.into_boxed_slice(), + pos: 0, + cap: 0, + } } } @@ -180,6 +185,11 @@ impl<R: Read> Read for BufReader<R> { self.consume(nread); Ok(nread) } + + // we can't skip unconditionally because of the large buffer case in read. + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 53347eb14db..616b4f47ed3 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -12,7 +12,7 @@ use io::prelude::*; use core::convert::TryInto; use cmp; -use io::{self, SeekFrom, Error, ErrorKind}; +use io::{self, Initializer, SeekFrom, Error, ErrorKind}; /// A `Cursor` wraps another type and provides it with a /// [`Seek`] implementation. @@ -229,6 +229,11 @@ impl<T> Read for Cursor<T> where T: AsRef<[u8]> { self.pos += n as u64; Ok(n) } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index f691289811b..d6b41ceda43 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -9,7 +9,7 @@ // except according to those terms. use cmp; -use io::{self, SeekFrom, Read, Write, Seek, BufRead, Error, ErrorKind}; +use io::{self, SeekFrom, Read, Initializer, Write, Seek, BufRead, Error, ErrorKind}; use fmt; use mem; @@ -24,6 +24,11 @@ impl<'a, R: Read + ?Sized> Read for &'a mut R { } #[inline] + unsafe fn initializer(&self) -> Initializer { + (**self).initializer() + } + + #[inline] fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { (**self).read_to_end(buf) } @@ -88,6 +93,11 @@ impl<R: Read + ?Sized> Read for Box<R> { } #[inline] + unsafe fn initializer(&self) -> Initializer { + (**self).initializer() + } + + #[inline] fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { (**self).read_to_end(buf) } @@ -172,6 +182,11 @@ impl<'a> Read for &'a [u8] { } #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } + + #[inline] fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if buf.len() > self.len() { return Err(Error::new(ErrorKind::UnexpectedEof, diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 3f859c45c28..680a5f32ae2 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -275,6 +275,7 @@ use fmt; use result; use str; use memchr; +use ptr; #[stable(feature = "rust1", since = "1.0.0")] pub use self::buffered::{BufReader, BufWriter, LineWriter}; @@ -292,7 +293,7 @@ pub use self::stdio::{stdin, stdout, stderr, Stdin, Stdout, Stderr}; pub use self::stdio::{StdoutLock, StderrLock, StdinLock}; #[unstable(feature = "print_internals", issue = "0")] pub use self::stdio::{_print, _eprint}; -#[unstable(feature = "libstd_io_internals", issue = "0")] +#[unstable(feature = "libstd_io_internals", issue = "42788")] #[doc(no_inline, hidden)] pub use self::stdio::{set_panic, set_print}; @@ -307,6 +308,14 @@ mod stdio; const DEFAULT_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; +struct Guard<'a> { buf: &'a mut Vec<u8>, len: usize } + +impl<'a> Drop for Guard<'a> { + fn drop(&mut self) { + unsafe { self.buf.set_len(self.len); } + } +} + // A few methods below (read_to_string, read_line) will append data into a // `String` buffer, but we need to be pretty careful when doing this. The // implementation will just call `.as_mut_vec()` and then delegate to a @@ -328,23 +337,16 @@ const DEFAULT_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize> where F: FnOnce(&mut Vec<u8>) -> Result<usize> { - struct Guard<'a> { s: &'a mut Vec<u8>, len: usize } - impl<'a> Drop for Guard<'a> { - fn drop(&mut self) { - unsafe { self.s.set_len(self.len); } - } - } - unsafe { - let mut g = Guard { len: buf.len(), s: buf.as_mut_vec() }; - let ret = f(g.s); - if str::from_utf8(&g.s[g.len..]).is_err() { + let mut g = Guard { len: buf.len(), buf: buf.as_mut_vec() }; + let ret = f(g.buf); + if str::from_utf8(&g.buf[g.len..]).is_err() { ret.and_then(|_| { Err(Error::new(ErrorKind::InvalidData, "stream did not contain valid UTF-8")) }) } else { - g.len = g.s.len(); + g.len = g.buf.len(); ret } } @@ -356,25 +358,32 @@ fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize> // of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every // time is 4,500 times (!) slower than this if the reader has a very small // amount of data to return. +// +// Because we're extending the buffer with uninitialized data for trusted +// readers, we need to make sure to truncate that if any of this panics. fn read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> { let start_len = buf.len(); - let mut len = start_len; + let mut g = Guard { len: buf.len(), buf: buf }; let mut new_write_size = 16; let ret; loop { - if len == buf.len() { + if g.len == g.buf.len() { if new_write_size < DEFAULT_BUF_SIZE { new_write_size *= 2; } - buf.resize(len + new_write_size, 0); + unsafe { + g.buf.reserve(new_write_size); + g.buf.set_len(g.len + new_write_size); + r.initializer().initialize(&mut g.buf[g.len..]); + } } - match r.read(&mut buf[len..]) { + match r.read(&mut g.buf[g.len..]) { Ok(0) => { - ret = Ok(len - start_len); + ret = Ok(g.len - start_len); break; } - Ok(n) => len += n, + Ok(n) => g.len += n, Err(ref e) if e.kind() == ErrorKind::Interrupted => {} Err(e) => { ret = Err(e); @@ -383,7 +392,6 @@ fn read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> } } - buf.truncate(len); ret } @@ -494,6 +502,31 @@ pub trait Read { #[stable(feature = "rust1", since = "1.0.0")] fn read(&mut self, buf: &mut [u8]) -> Result<usize>; + /// Determines if this `Read`er can work with buffers of uninitialized + /// memory. + /// + /// The default implementation returns an initializer which will zero + /// buffers. + /// + /// If a `Read`er guarantees that it can work properly with uninitialized + /// memory, it should call `Initializer::nop()`. See the documentation for + /// `Initializer` for details. + /// + /// The behavior of this method must be independent of the state of the + /// `Read`er - the method only takes `&self` so that it can be used through + /// trait objects. + /// + /// # Unsafety + /// + /// This method is unsafe because a `Read`er could otherwise return a + /// non-zeroing `Initializer` from another `Read` type without an `unsafe` + /// block. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::zeroing() + } + /// 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 @@ -829,6 +862,50 @@ pub trait Read { } } +/// A type used to conditionally initialize buffers passed to `Read` methods. +#[unstable(feature = "read_initializer", issue = "42788")] +#[derive(Debug)] +pub struct Initializer(bool); + +impl Initializer { + /// Returns a new `Initializer` which will zero out buffers. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub fn zeroing() -> Initializer { + Initializer(true) + } + + /// Returns a new `Initializer` which will not zero out buffers. + /// + /// # Unsafety + /// + /// This may only be called by `Read`ers which guarantee that they will not + /// read from buffers passed to `Read` methods, and that the return value of + /// the method accurately reflects the number of bytes that have been + /// written to the head of the buffer. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub unsafe fn nop() -> Initializer { + Initializer(false) + } + + /// Indicates if a buffer should be initialized. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub fn should_initialize(&self) -> bool { + self.0 + } + + /// Initializes a buffer if necessary. + #[unstable(feature = "read_initializer", issue = "42788")] + #[inline] + pub fn initialize(&self, buf: &mut [u8]) { + if self.should_initialize() { + unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) } + } + } +} + /// A trait for objects which are byte-oriented sinks. /// /// Implementors of the `Write` trait are sometimes called 'writers'. @@ -1608,6 +1685,15 @@ impl<T: Read, U: Read> Read for Chain<T, U> { } self.second.read(buf) } + + unsafe fn initializer(&self) -> Initializer { + let initializer = self.first.initializer(); + if initializer.should_initialize() { + initializer + } else { + self.second.initializer() + } + } } #[stable(feature = "chain_bufread", since = "1.9.0")] @@ -1772,6 +1858,10 @@ impl<T: Read> Read for Take<T> { self.limit -= n as u64; Ok(n) } + + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index a8b0bf0071a..fb489bf487b 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -13,7 +13,7 @@ use io::prelude::*; use cell::RefCell; use fmt; use io::lazy::Lazy; -use io::{self, BufReader, LineWriter}; +use io::{self, Initializer, BufReader, LineWriter}; use sync::{Arc, Mutex, MutexGuard}; use sys::stdio; use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; @@ -75,8 +75,10 @@ fn stderr_raw() -> io::Result<StderrRaw> { stdio::Stderr::new().map(StderrRaw) } impl Read for StdinRaw { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - self.0.read_to_end(buf) + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() } } impl Write for StdoutRaw { @@ -116,12 +118,6 @@ impl<R: io::Read> io::Read for Maybe<R> { Maybe::Fake => Ok(0) } } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - match *self { - Maybe::Real(ref mut r) => handle_ebadf(r.read_to_end(buf), 0), - Maybe::Fake => Ok(0) - } - } } fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> { @@ -294,6 +290,10 @@ impl Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.lock().read(buf) } + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { self.lock().read_to_end(buf) } @@ -310,8 +310,9 @@ impl<'a> Read for StdinLock<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.inner.read(buf) } - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - self.inner.read_to_end(buf) + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() } } diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 078f1ad3f6c..88f4214296d 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -11,7 +11,8 @@ #![allow(missing_copy_implementations)] use fmt; -use io::{self, Read, Write, ErrorKind, BufRead}; +use io::{self, Read, Initializer, Write, ErrorKind, BufRead}; +use mem; /// Copies the entire contents of a reader into a writer. /// @@ -47,7 +48,12 @@ use io::{self, Read, Write, ErrorKind, BufRead}; pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> where R: Read, W: Write { - let mut buf = [0; super::DEFAULT_BUF_SIZE]; + let mut buf = unsafe { + let mut buf: [u8; super::DEFAULT_BUF_SIZE] = mem::uninitialized(); + reader.initializer().initialize(&mut buf); + buf + }; + let mut written = 0; loop { let len = match reader.read(&mut buf) { @@ -90,11 +96,19 @@ pub fn empty() -> Empty { Empty { _priv: () } } #[stable(feature = "rust1", since = "1.0.0")] impl Read for Empty { + #[inline] fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { Ok(0) } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } } #[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Empty { + #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(&[]) } + #[inline] fn consume(&mut self, _n: usize) {} } @@ -133,12 +147,18 @@ pub fn repeat(byte: u8) -> Repeat { Repeat { byte: byte } } #[stable(feature = "rust1", since = "1.0.0")] impl Read for Repeat { + #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { for slot in &mut *buf { *slot = self.byte; } Ok(buf.len()) } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } } #[stable(feature = "std_debug", since = "1.16.0")] @@ -176,7 +196,9 @@ pub fn sink() -> Sink { Sink { _priv: () } } #[stable(feature = "rust1", since = "1.0.0")] impl Write for Sink { + #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } } |
