Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement read_exact for the Read trait #27588

Merged
merged 2 commits into from
Aug 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/libstd/io/error.rs
Original file line number Diff line number Diff line change
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", issue = "27585")]
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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", issue = "27585")]
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
Original file line number Diff line number Diff line change
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