From ff81920f03866674080ac63b565cc9d30f80c450 Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Mon, 20 Jul 2015 00:23:37 -0300 Subject: [PATCH] Implement read_exact for the Read trait This implements the proposed "read_exact" RFC (https://github.com/rust-lang/rfcs/pull/980). --- src/libstd/io/error.rs | 9 ++++ src/libstd/io/impls.rs | 21 ++++++++ src/libstd/io/mod.rs | 107 +++++++++++++++++++++++++++++++++++++++++ src/libstd/io/stdio.rs | 3 ++ 4 files changed, 140 insertions(+) diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 3b48ff3096043..d55721db12617 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 67bc45d3b62a1..4d9edfbefef0c 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 { (**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 Read for Box { fn read_to_string(&mut self, buf: &mut String) -> io::Result { (**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 Write for Box { @@ -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 2447473103101..d2b8b40ca5cc5 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 @@ -1556,6 +1622,47 @@ mod tests { assert!(c.read_to_string(&mut v).is_err()); } + #[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 d8b7c8a282ca2..88f40d764e23e 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 { 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")]