about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorCesar Eduardo Barros <cesarb@cesarb.eti.br>2015-07-20 00:23:37 -0300
committerCesar Eduardo Barros <cesarb@cesarb.eti.br>2015-08-24 19:10:08 -0300
commitff81920f03866674080ac63b565cc9d30f80c450 (patch)
tree9619a464863630730373297a9a35e7bccba94fcf /src/libstd
parentef04b07239d75f2b6bb66f18855f3af695d76e1c (diff)
downloadrust-ff81920f03866674080ac63b565cc9d30f80c450.tar.gz
rust-ff81920f03866674080ac63b565cc9d30f80c450.zip
Implement read_exact for the Read trait
This implements the proposed "read_exact" RFC
(https://github.com/rust-lang/rfcs/pull/980).
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io/error.rs9
-rw-r--r--src/libstd/io/impls.rs21
-rw-r--r--src/libstd/io/mod.rs107
-rw-r--r--src/libstd/io/stdio.rs3
4 files changed, 140 insertions, 0 deletions
diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs
index 3b48ff30960..d55721db126 100644
--- a/src/libstd/io/error.rs
+++ b/src/libstd/io/error.rs
@@ -147,6 +147,15 @@ pub enum ErrorKind {
     #[stable(feature = "rust1", since = "1.0.0")]
     Other,
 
+    /// An error returned when an operation could not be completed because an
+    /// "end of file" was reached prematurely.
+    ///
+    /// This typically means that an operation could only succeed if it read a
+    /// particular number of bytes but only a smaller number of bytes could be
+    /// read.
+    #[unstable(feature = "read_exact", reason = "recently added")]
+    UnexpectedEOF,
+
     /// Any I/O error not part of this list.
     #[unstable(feature = "io_error_internals",
                reason = "better expressed through extensible enums that this \
diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs
index 67bc45d3b62..4d9edfbefef 100644
--- a/src/libstd/io/impls.rs
+++ b/src/libstd/io/impls.rs
@@ -38,6 +38,11 @@ impl<'a, R: Read + ?Sized> Read for &'a mut R {
     fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
         (**self).read_to_string(buf)
     }
+
+    #[inline]
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        (**self).read_exact(buf)
+    }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, W: Write + ?Sized> Write for &'a mut W {
@@ -97,6 +102,11 @@ impl<R: Read + ?Sized> Read for Box<R> {
     fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
         (**self).read_to_string(buf)
     }
+
+    #[inline]
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        (**self).read_exact(buf)
+    }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<W: Write + ?Sized> Write for Box<W> {
@@ -153,6 +163,17 @@ impl<'a> Read for &'a [u8] {
         *self = b;
         Ok(amt)
     }
+
+    #[inline]
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        if buf.len() > self.len() {
+            return Err(Error::new(ErrorKind::UnexpectedEOF, "failed to fill whole buffer"));
+        }
+        let (a, b) = self.split_at(buf.len());
+        slice::bytes::copy_memory(a, buf);
+        *self = b;
+        Ok(())
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 24474731031..d2b8b40ca5c 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -315,6 +315,72 @@ pub trait Read {
         append_to_string(buf, |b| read_to_end(self, b))
     }
 
+    /// Read the exact number of bytes required to fill `buf`.
+    ///
+    /// This function reads as many bytes as necessary to completely fill the
+    /// specified buffer `buf`.
+    ///
+    /// No guarantees are provided about the contents of `buf` when this
+    /// function is called, implementations cannot rely on any property of the
+    /// contents of `buf` being true. It is recommended that implementations
+    /// only write data to `buf` instead of reading its contents.
+    ///
+    /// # Errors
+    ///
+    /// If this function encounters an error of the kind
+    /// `ErrorKind::Interrupted` then the error is ignored and the operation
+    /// will continue.
+    ///
+    /// If this function encounters an "end of file" before completely filling
+    /// the buffer, it returns an error of the kind `ErrorKind::UnexpectedEOF`.
+    /// The contents of `buf` are unspecified in this case.
+    ///
+    /// If any other read error is encountered then this function immediately
+    /// returns. The contents of `buf` are unspecified in this case.
+    ///
+    /// If this function returns an error, it is unspecified how many bytes it
+    /// has read, but it will never read more than would be necessary to
+    /// completely fill the buffer.
+    ///
+    /// # Examples
+    ///
+    /// [`File`][file]s implement `Read`:
+    ///
+    /// [file]: ../std/fs/struct.File.html
+    ///
+    /// ```
+    /// #![feature(read_exact)]
+    /// use std::io;
+    /// use std::io::prelude::*;
+    /// use std::fs::File;
+    ///
+    /// # fn foo() -> io::Result<()> {
+    /// let mut f = try!(File::open("foo.txt"));
+    /// let mut buffer = [0; 10];
+    ///
+    /// // read exactly 10 bytes
+    /// try!(f.read_exact(&mut buffer));
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[unstable(feature = "read_exact", reason = "recently added")]
+    fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
+        while !buf.is_empty() {
+            match self.read(buf) {
+                Ok(0) => break,
+                Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; }
+                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+                Err(e) => return Err(e),
+            }
+        }
+        if !buf.is_empty() {
+            Err(Error::new(ErrorKind::UnexpectedEOF,
+                           "failed to fill whole buffer"))
+        } else {
+            Ok(())
+        }
+    }
+
     /// Creates a "by reference" adaptor for this instance of `Read`.
     ///
     /// The returned adaptor also implements `Read` and will simply borrow this
@@ -1557,6 +1623,47 @@ mod tests {
     }
 
     #[test]
+    fn read_exact() {
+        let mut buf = [0; 4];
+
+        let mut c = Cursor::new(&b""[..]);
+        assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
+                   io::ErrorKind::UnexpectedEOF);
+
+        let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..]));
+        c.read_exact(&mut buf).unwrap();
+        assert_eq!(&buf, b"1234");
+        c.read_exact(&mut buf).unwrap();
+        assert_eq!(&buf, b"5678");
+        assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
+                   io::ErrorKind::UnexpectedEOF);
+    }
+
+    #[test]
+    fn read_exact_slice() {
+        let mut buf = [0; 4];
+
+        let mut c = &b""[..];
+        assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
+                   io::ErrorKind::UnexpectedEOF);
+
+        let mut c = &b"123"[..];
+        assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
+                   io::ErrorKind::UnexpectedEOF);
+        // make sure the optimized (early returning) method is being used
+        assert_eq!(&buf, &[0; 4]);
+
+        let mut c = &b"1234"[..];
+        c.read_exact(&mut buf).unwrap();
+        assert_eq!(&buf, b"1234");
+
+        let mut c = &b"56789"[..];
+        c.read_exact(&mut buf).unwrap();
+        assert_eq!(&buf, b"5678");
+        assert_eq!(c, b"9");
+    }
+
+    #[test]
     fn take_eof() {
         struct R;
 
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index d8b7c8a282c..88f40d764e2 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -271,6 +271,9 @@ impl Read for Stdin {
     fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
         self.lock().read_to_string(buf)
     }
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        self.lock().read_exact(buf)
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]