diff options
Diffstat (limited to 'ctr-std/src/io/buffered.rs')
| -rw-r--r-- | ctr-std/src/io/buffered.rs | 281 |
1 files changed, 227 insertions, 54 deletions
diff --git a/ctr-std/src/io/buffered.rs b/ctr-std/src/io/buffered.rs index 44dd4e9..4e7db5f 100644 --- a/ctr-std/src/io/buffered.rs +++ b/ctr-std/src/io/buffered.rs @@ -15,18 +15,18 @@ 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. /// /// It can be excessively inefficient to work directly with a [`Read`] instance. -/// For example, every call to [`read`] on [`TcpStream`] results in a system call. -/// A `BufReader` performs large, infrequent reads on the underlying [`Read`] -/// and maintains an in-memory buffer of the results. +/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] +/// results in a system call. A `BufReader` performs large, infrequent reads on +/// the underlying [`Read`] and maintains an in-memory buffer of the results. /// /// [`Read`]: ../../std/io/trait.Read.html -/// [`read`]: ../../std/net/struct.TcpStream.html#method.read +/// [`TcpStream::read`]: ../../std/net/struct.TcpStream.html#method.read /// [`TcpStream`]: ../../std/net/struct.TcpStream.html /// /// # Examples @@ -37,11 +37,11 @@ use memchr; /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { -/// let mut f = try!(File::open("log.txt")); +/// let f = File::open("log.txt")?; /// let mut reader = BufReader::new(f); /// /// let mut line = String::new(); -/// let len = try!(reader.read_line(&mut line)); +/// let len = reader.read_line(&mut line)?; /// println!("First line is {} bytes long", len); /// # Ok(()) /// # } @@ -64,8 +64,8 @@ impl<R: Read> BufReader<R> { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::open("log.txt")); - /// let mut reader = BufReader::new(f); + /// let f = File::open("log.txt")?; + /// let reader = BufReader::new(f); /// # Ok(()) /// # } /// ``` @@ -85,18 +85,23 @@ impl<R: Read> BufReader<R> { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f = try!(File::open("log.txt")); - /// let mut reader = BufReader::with_capacity(10, f); + /// let f = File::open("log.txt")?; + /// let reader = BufReader::with_capacity(10, f); /// # Ok(()) /// # } /// ``` #[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, + buf: buffer.into_boxed_slice(), + pos: 0, + cap: 0, + } } } @@ -111,8 +116,8 @@ impl<R: Read> BufReader<R> { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f1 = try!(File::open("log.txt")); - /// let mut reader = BufReader::new(f1); + /// let f1 = File::open("log.txt")?; + /// let reader = BufReader::new(f1); /// /// let f2 = reader.get_ref(); /// # Ok(()) @@ -132,7 +137,7 @@ impl<R: Read> BufReader<R> { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f1 = try!(File::open("log.txt")); + /// let f1 = File::open("log.txt")?; /// let mut reader = BufReader::new(f1); /// /// let f2 = reader.get_mut(); @@ -142,6 +147,31 @@ impl<R: Read> BufReader<R> { #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self) -> &mut R { &mut self.inner } + /// Returns `true` if there are no bytes in the internal buffer. + /// + /// # Examples + /// ``` + /// # #![feature(bufreader_is_empty)] + /// use std::io::BufReader; + /// use std::io::BufRead; + /// use std::fs::File; + /// + /// # fn foo() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let mut reader = BufReader::new(f1); + /// assert!(reader.is_empty()); + /// + /// if reader.fill_buf()?.len() > 0 { + /// assert!(!reader.is_empty()); + /// } + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "bufreader_is_empty", issue = "45323", reason = "recently added")] + pub fn is_empty(&self) -> bool { + self.pos == self.cap + } + /// Unwraps this `BufReader`, returning the underlying reader. /// /// Note that any leftover data in the internal buffer is lost. @@ -153,8 +183,8 @@ impl<R: Read> BufReader<R> { /// use std::fs::File; /// /// # fn foo() -> std::io::Result<()> { - /// let mut f1 = try!(File::open("log.txt")); - /// let mut reader = BufReader::new(f1); + /// let f1 = File::open("log.txt")?; + /// let reader = BufReader::new(f1); /// /// let f2 = reader.into_inner(); /// # Ok(()) @@ -164,6 +194,31 @@ impl<R: Read> BufReader<R> { pub fn into_inner(self) -> R { self.inner } } +impl<R: 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 + /// must track this information themselves if it is required. + #[unstable(feature = "bufreader_seek_relative", issue = "31100")] + pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + let pos = self.pos as u64; + if offset < 0 { + if let Some(new_pos) = pos.checked_sub((-offset) as u64) { + self.pos = new_pos as usize; + return Ok(()) + } + } else { + if let Some(new_pos) = pos.checked_add(offset as u64) { + if new_pos <= self.cap as u64 { + self.pos = new_pos as usize; + return Ok(()) + } + } + } + self.seek(SeekFrom::Current(offset)).map(|_|()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<R: Read> Read for BufReader<R> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { @@ -180,6 +235,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")] @@ -187,7 +247,10 @@ impl<R: Read> BufRead for BufReader<R> { fn fill_buf(&mut self) -> io::Result<&[u8]> { // If we've reached the end of our internal buffer then we need to fetch // some more data from the underlying reader. - if self.pos == self.cap { + // Branch using `>=` instead of the more correct `==` + // to tell the compiler that the pos..cap slice is always valid. + if self.pos >= self.cap { + debug_assert!(self.pos == self.cap); self.cap = self.inner.read(&mut self.buf)?; self.pos = 0; } @@ -222,13 +285,17 @@ impl<R: Seek> Seek for BufReader<R> { /// `.into_inner()` immediately after a seek yields the underlying reader /// at the same position. /// + /// To seek without discarding the internal buffer, use [`seek_relative`]. + /// /// See `std::io::Seek` for more details. /// /// Note: In the edge case where you're seeking with `SeekFrom::Current(n)` - /// where `n` minus the internal buffer length underflows an `i64`, two + /// where `n` minus the internal buffer length overflows an `i64`, two /// seeks will be performed instead of one. If the second seek returns /// `Err`, the underlying reader will be left at the same position it would /// have if you seeked to `SeekFrom::Current(0)`. + /// + /// [`seek_relative`]: #method.seek_relative fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { let result: u64; if let SeekFrom::Current(n) = pos { @@ -258,11 +325,15 @@ impl<R: Seek> Seek for BufReader<R> { /// Wraps a writer and buffers its output. /// /// It can be excessively inefficient to work directly with something that -/// implements [`Write`]. For example, every call to [`write`] on [`TcpStream`] -/// results in a system call. A `BufWriter` keeps an in-memory buffer of data -/// and writes it to an underlying writer in large, infrequent batches. +/// implements [`Write`]. For example, every call to +/// [`write`][`Tcpstream::write`] on [`TcpStream`] results in a system call. A +/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying +/// writer in large, infrequent batches. /// -/// The buffer will be written out when the writer is dropped. +/// When the `BufWriter` is dropped, the contents of its buffer will be written +/// out. However, any errors that happen in the process of flushing the buffer +/// when the writer is dropped will be ignored. Code that wishes to handle such +/// errors must manually call [`flush`] before the writer is dropped. /// /// # Examples /// @@ -274,8 +345,8 @@ impl<R: Seek> Seek for BufReader<R> { /// /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); /// -/// for i in 1..10 { -/// stream.write(&[i]).unwrap(); +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); /// } /// ``` /// @@ -290,8 +361,8 @@ impl<R: Seek> Seek for BufReader<R> { /// /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); /// -/// for i in 1..10 { -/// stream.write(&[i]).unwrap(); +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); /// } /// ``` /// @@ -300,8 +371,9 @@ impl<R: Seek> Seek for BufReader<R> { /// the `stream` is dropped. /// /// [`Write`]: ../../std/io/trait.Write.html -/// [`write`]: ../../std/net/struct.TcpStream.html#method.write +/// [`Tcpstream::write`]: ../../std/net/struct.TcpStream.html#method.write /// [`TcpStream`]: ../../std/net/struct.TcpStream.html +/// [`flush`]: #method.flush #[stable(feature = "rust1", since = "1.0.0")] pub struct BufWriter<W: Write> { inner: Option<W>, @@ -443,6 +515,10 @@ impl<W: Write> BufWriter<W> { /// /// 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 @@ -607,6 +683,9 @@ impl<W> fmt::Display for IntoInnerError<W> { /// completed, rather than the entire buffer at once. Enter `LineWriter`. It /// does exactly that. /// +/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the +/// `LineWriter` goes out of scope or when its internal buffer is full. +/// /// [bufwriter]: struct.BufWriter.html /// /// If there's still a partial line in the buffer when the `LineWriter` is @@ -629,7 +708,7 @@ impl<W> fmt::Display for IntoInnerError<W> { /// I took the one less traveled by, /// And that has made all the difference."; /// -/// let file = try!(File::create("poem.txt")); +/// let file = File::create("poem.txt")?; /// let mut file = LineWriter::new(file); /// /// for &byte in road_not_taken.iter() { @@ -637,10 +716,10 @@ impl<W> fmt::Display for IntoInnerError<W> { /// } /// /// // let's check we did the right thing. -/// let mut file = try!(File::open("poem.txt")); +/// let mut file = File::open("poem.txt")?; /// let mut contents = String::new(); /// -/// try!(file.read_to_string(&mut contents)); +/// file.read_to_string(&mut contents)?; /// /// assert_eq!(contents.as_bytes(), &road_not_taken[..]); /// # Ok(()) @@ -649,6 +728,7 @@ impl<W> fmt::Display for IntoInnerError<W> { #[stable(feature = "rust1", since = "1.0.0")] pub struct LineWriter<W: Write> { inner: BufWriter<W>, + need_flush: bool, } impl<W: Write> LineWriter<W> { @@ -661,7 +741,7 @@ impl<W: Write> LineWriter<W> { /// use std::io::LineWriter; /// /// # fn foo() -> std::io::Result<()> { - /// let file = try!(File::create("poem.txt")); + /// let file = File::create("poem.txt")?; /// let file = LineWriter::new(file); /// # Ok(()) /// # } @@ -682,14 +762,17 @@ impl<W: Write> LineWriter<W> { /// use std::io::LineWriter; /// /// # fn foo() -> std::io::Result<()> { - /// let file = try!(File::create("poem.txt")); + /// let file = File::create("poem.txt")?; /// let file = LineWriter::with_capacity(100, file); /// # Ok(()) /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn with_capacity(cap: usize, inner: W) -> LineWriter<W> { - LineWriter { inner: BufWriter::with_capacity(cap, inner) } + LineWriter { + inner: BufWriter::with_capacity(cap, inner), + need_flush: false, + } } /// Gets a reference to the underlying writer. @@ -701,7 +784,7 @@ impl<W: Write> LineWriter<W> { /// use std::io::LineWriter; /// /// # fn foo() -> std::io::Result<()> { - /// let file = try!(File::create("poem.txt")); + /// let file = File::create("poem.txt")?; /// let file = LineWriter::new(file); /// /// let reference = file.get_ref(); @@ -723,7 +806,7 @@ impl<W: Write> LineWriter<W> { /// use std::io::LineWriter; /// /// # fn foo() -> std::io::Result<()> { - /// let file = try!(File::create("poem.txt")); + /// let file = File::create("poem.txt")?; /// let mut file = LineWriter::new(file); /// /// // we can use reference just like file @@ -738,6 +821,10 @@ impl<W: Write> LineWriter<W> { /// /// The internal buffer is written out before returning the writer. /// + // # Errors + /// + /// An `Err` will be returned if an error occurs while flushing the buffer. + /// /// # Examples /// /// ``` @@ -745,18 +832,21 @@ impl<W: Write> LineWriter<W> { /// use std::io::LineWriter; /// /// # fn foo() -> std::io::Result<()> { - /// let file = try!(File::create("poem.txt")); + /// let file = File::create("poem.txt")?; /// /// let writer: LineWriter<File> = LineWriter::new(file); /// - /// let file: File = try!(writer.into_inner()); + /// let file: File = writer.into_inner()?; /// # Ok(()) /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> { self.inner.into_inner().map_err(|IntoInnerError(buf, e)| { - IntoInnerError(LineWriter { inner: buf }, e) + IntoInnerError(LineWriter { + inner: buf, + need_flush: false, + }, e) }) } } @@ -764,20 +854,46 @@ impl<W: Write> LineWriter<W> { #[stable(feature = "rust1", since = "1.0.0")] impl<W: Write> Write for LineWriter<W> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { - match memchr::memrchr(b'\n', buf) { - Some(i) => { - let n = self.inner.write(&buf[..i + 1])?; - if n != i + 1 || self.inner.flush().is_err() { - // Do not return errors on partial writes. - return Ok(n); - } - self.inner.write(&buf[i + 1..]).map(|i| n + i) - } - None => self.inner.write(buf), + if self.need_flush { + self.flush()?; + } + + // Find the last newline character in the buffer provided. If found then + // we're going to write all the data up to that point and then flush, + // otherewise we just write the whole block to the underlying writer. + let i = match memchr::memrchr(b'\n', buf) { + Some(i) => i, + None => return self.inner.write(buf), + }; + + + // Ok, we're going to write a partial amount of the data given first + // followed by flushing the newline. After we've successfully written + // some data then we *must* report that we wrote that data, so future + // errors are ignored. We set our internal `need_flush` flag, though, in + // case flushing fails and we need to try it first next time. + let n = self.inner.write(&buf[..i + 1])?; + self.need_flush = true; + if self.flush().is_err() || n != i + 1 { + return Ok(n) + } + + // At this point we successfully wrote `i + 1` bytes and flushed it out, + // meaning that the entire line is now flushed out on the screen. While + // we can attempt to finish writing the rest of the data provided. + // Remember though that we ignore errors here as we've successfully + // written data, so we need to report that. + match self.inner.write(&buf[i + 1..]) { + Ok(i) => Ok(n + i), + Err(_) => Ok(n), } } - fn flush(&mut self) -> io::Result<()> { self.inner.flush() } + fn flush(&mut self) -> io::Result<()> { + self.inner.flush()?; + self.need_flush = false; + Ok(()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -867,6 +983,23 @@ mod tests { } #[test] + fn test_buffered_reader_seek_relative() { + let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4]; + let mut reader = BufReader::with_capacity(2, io::Cursor::new(inner)); + + assert!(reader.seek_relative(3).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(0).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[1][..])); + assert!(reader.seek_relative(-1).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[0, 1][..])); + assert!(reader.seek_relative(2).is_ok()); + assert_eq!(reader.fill_buf().ok(), Some(&[2, 3][..])); + } + + #[test] fn test_buffered_reader_seek_underflow() { // gimmick reader that yields its position modulo 256 for each byte struct PositionReader { @@ -1150,4 +1283,44 @@ mod tests { BufWriter::new(io::sink()) }); } + + struct AcceptOneThenFail { + written: bool, + flushed: bool, + } + + impl Write for AcceptOneThenFail { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + if !self.written { + assert_eq!(data, b"a\nb\n"); + self.written = true; + Ok(data.len()) + } else { + Err(io::Error::new(io::ErrorKind::NotFound, "test")) + } + } + + fn flush(&mut self) -> io::Result<()> { + assert!(self.written); + assert!(!self.flushed); + self.flushed = true; + Err(io::Error::new(io::ErrorKind::Other, "test")) + } + } + + #[test] + fn erroneous_flush_retried() { + let a = AcceptOneThenFail { + written: false, + flushed: false, + }; + + let mut l = LineWriter::new(a); + assert_eq!(l.write(b"a\nb\na").unwrap(), 4); + assert!(l.get_ref().written); + assert!(l.get_ref().flushed); + l.get_mut().flushed = false; + + assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other) + } } |