Skip to content

Commit ff81920

Browse files
committed
Implement read_exact for the Read trait
This implements the proposed "read_exact" RFC (rust-lang/rfcs#980).
1 parent ef04b07 commit ff81920

File tree

4 files changed

+140
-0
lines changed

4 files changed

+140
-0
lines changed

src/libstd/io/error.rs

+9
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ pub enum ErrorKind {
147147
#[stable(feature = "rust1", since = "1.0.0")]
148148
Other,
149149

150+
/// An error returned when an operation could not be completed because an
151+
/// "end of file" was reached prematurely.
152+
///
153+
/// This typically means that an operation could only succeed if it read a
154+
/// particular number of bytes but only a smaller number of bytes could be
155+
/// read.
156+
#[unstable(feature = "read_exact", reason = "recently added")]
157+
UnexpectedEOF,
158+
150159
/// Any I/O error not part of this list.
151160
#[unstable(feature = "io_error_internals",
152161
reason = "better expressed through extensible enums that this \

src/libstd/io/impls.rs

+21
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ impl<'a, R: Read + ?Sized> Read for &'a mut R {
3838
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
3939
(**self).read_to_string(buf)
4040
}
41+
42+
#[inline]
43+
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
44+
(**self).read_exact(buf)
45+
}
4146
}
4247
#[stable(feature = "rust1", since = "1.0.0")]
4348
impl<'a, W: Write + ?Sized> Write for &'a mut W {
@@ -97,6 +102,11 @@ impl<R: Read + ?Sized> Read for Box<R> {
97102
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
98103
(**self).read_to_string(buf)
99104
}
105+
106+
#[inline]
107+
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
108+
(**self).read_exact(buf)
109+
}
100110
}
101111
#[stable(feature = "rust1", since = "1.0.0")]
102112
impl<W: Write + ?Sized> Write for Box<W> {
@@ -153,6 +163,17 @@ impl<'a> Read for &'a [u8] {
153163
*self = b;
154164
Ok(amt)
155165
}
166+
167+
#[inline]
168+
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
169+
if buf.len() > self.len() {
170+
return Err(Error::new(ErrorKind::UnexpectedEOF, "failed to fill whole buffer"));
171+
}
172+
let (a, b) = self.split_at(buf.len());
173+
slice::bytes::copy_memory(a, buf);
174+
*self = b;
175+
Ok(())
176+
}
156177
}
157178

158179
#[stable(feature = "rust1", since = "1.0.0")]

src/libstd/io/mod.rs

+107
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,72 @@ pub trait Read {
315315
append_to_string(buf, |b| read_to_end(self, b))
316316
}
317317

318+
/// Read the exact number of bytes required to fill `buf`.
319+
///
320+
/// This function reads as many bytes as necessary to completely fill the
321+
/// specified buffer `buf`.
322+
///
323+
/// No guarantees are provided about the contents of `buf` when this
324+
/// function is called, implementations cannot rely on any property of the
325+
/// contents of `buf` being true. It is recommended that implementations
326+
/// only write data to `buf` instead of reading its contents.
327+
///
328+
/// # Errors
329+
///
330+
/// If this function encounters an error of the kind
331+
/// `ErrorKind::Interrupted` then the error is ignored and the operation
332+
/// will continue.
333+
///
334+
/// If this function encounters an "end of file" before completely filling
335+
/// the buffer, it returns an error of the kind `ErrorKind::UnexpectedEOF`.
336+
/// The contents of `buf` are unspecified in this case.
337+
///
338+
/// If any other read error is encountered then this function immediately
339+
/// returns. The contents of `buf` are unspecified in this case.
340+
///
341+
/// If this function returns an error, it is unspecified how many bytes it
342+
/// has read, but it will never read more than would be necessary to
343+
/// completely fill the buffer.
344+
///
345+
/// # Examples
346+
///
347+
/// [`File`][file]s implement `Read`:
348+
///
349+
/// [file]: ../std/fs/struct.File.html
350+
///
351+
/// ```
352+
/// #![feature(read_exact)]
353+
/// use std::io;
354+
/// use std::io::prelude::*;
355+
/// use std::fs::File;
356+
///
357+
/// # fn foo() -> io::Result<()> {
358+
/// let mut f = try!(File::open("foo.txt"));
359+
/// let mut buffer = [0; 10];
360+
///
361+
/// // read exactly 10 bytes
362+
/// try!(f.read_exact(&mut buffer));
363+
/// # Ok(())
364+
/// # }
365+
/// ```
366+
#[unstable(feature = "read_exact", reason = "recently added")]
367+
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
368+
while !buf.is_empty() {
369+
match self.read(buf) {
370+
Ok(0) => break,
371+
Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; }
372+
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
373+
Err(e) => return Err(e),
374+
}
375+
}
376+
if !buf.is_empty() {
377+
Err(Error::new(ErrorKind::UnexpectedEOF,
378+
"failed to fill whole buffer"))
379+
} else {
380+
Ok(())
381+
}
382+
}
383+
318384
/// Creates a "by reference" adaptor for this instance of `Read`.
319385
///
320386
/// The returned adaptor also implements `Read` and will simply borrow this
@@ -1556,6 +1622,47 @@ mod tests {
15561622
assert!(c.read_to_string(&mut v).is_err());
15571623
}
15581624

1625+
#[test]
1626+
fn read_exact() {
1627+
let mut buf = [0; 4];
1628+
1629+
let mut c = Cursor::new(&b""[..]);
1630+
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
1631+
io::ErrorKind::UnexpectedEOF);
1632+
1633+
let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..]));
1634+
c.read_exact(&mut buf).unwrap();
1635+
assert_eq!(&buf, b"1234");
1636+
c.read_exact(&mut buf).unwrap();
1637+
assert_eq!(&buf, b"5678");
1638+
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
1639+
io::ErrorKind::UnexpectedEOF);
1640+
}
1641+
1642+
#[test]
1643+
fn read_exact_slice() {
1644+
let mut buf = [0; 4];
1645+
1646+
let mut c = &b""[..];
1647+
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
1648+
io::ErrorKind::UnexpectedEOF);
1649+
1650+
let mut c = &b"123"[..];
1651+
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(),
1652+
io::ErrorKind::UnexpectedEOF);
1653+
// make sure the optimized (early returning) method is being used
1654+
assert_eq!(&buf, &[0; 4]);
1655+
1656+
let mut c = &b"1234"[..];
1657+
c.read_exact(&mut buf).unwrap();
1658+
assert_eq!(&buf, b"1234");
1659+
1660+
let mut c = &b"56789"[..];
1661+
c.read_exact(&mut buf).unwrap();
1662+
assert_eq!(&buf, b"5678");
1663+
assert_eq!(c, b"9");
1664+
}
1665+
15591666
#[test]
15601667
fn take_eof() {
15611668
struct R;

src/libstd/io/stdio.rs

+3
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ impl Read for Stdin {
271271
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
272272
self.lock().read_to_string(buf)
273273
}
274+
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
275+
self.lock().read_exact(buf)
276+
}
274277
}
275278

276279
#[stable(feature = "rust1", since = "1.0.0")]

0 commit comments

Comments
 (0)