aboutsummaryrefslogtreecommitdiff
path: root/ctr-std/src/io/buffered.rs
diff options
context:
space:
mode:
authorFenrir <[email protected]>2018-01-21 14:06:28 -0700
committerFenrirWolf <[email protected]>2018-01-21 19:16:33 -0700
commit23be3f4885688e5e0011005e2295c75168854c0a (patch)
treedd0850f9c73c489e114a761d5c0757f3dbec3a65 /ctr-std/src/io/buffered.rs
parentUpdate CI for Rust nightly-2017-12-01 + other fixes (diff)
downloadarchived-ctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.tar.xz
archived-ctru-rs-23be3f4885688e5e0011005e2295c75168854c0a.zip
Recreate ctr-std from latest nightly
Diffstat (limited to 'ctr-std/src/io/buffered.rs')
-rw-r--r--ctr-std/src/io/buffered.rs281
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)
+ }
}