Skip to content

Commit

Permalink
Implement read_exact for the Read trait
Browse files Browse the repository at this point in the history
This implements the proposed "read_exact" RFC
(rust-lang/rfcs#980).
  • Loading branch information
cesarb committed Aug 24, 2015
1 parent ef04b07 commit ff81920
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/libstd/io/error.rs
Expand Up @@ -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 \
Expand Down
21 changes: 21 additions & 0 deletions src/libstd/io/impls.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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")]
Expand Down
107 changes: 107 additions & 0 deletions src/libstd/io/mod.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/libstd/io/stdio.rs
Expand Up @@ -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")]
Expand Down

0 comments on commit ff81920

Please sign in to comment.