Skip to content

Commit

Permalink
Rollup merge of rust-lang#74076 - sunfishcode:wasi-fileext-newmethods…
Browse files Browse the repository at this point in the history
…, r=alexcrichton

Add `read_exact_at` and `write_all_at` to WASI's `FileExt`

This adds `read_exact_at` and `write_all_at` to WASI's `FileExt`,
similar to the Unix versions of the same names.
  • Loading branch information
Manishearth authored Jul 10, 2020
2 parents 34e4abf + 58fc61b commit fee1d6f
Showing 1 changed file with 137 additions and 4 deletions.
141 changes: 137 additions & 4 deletions src/libstd/sys/wasi/ext/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner};
///
/// [`File`]: ../../../../std/fs/struct.File.html
pub trait FileExt {
/// Reads a number of bytes starting from a given offset.
///
/// Returns the number of bytes read.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// Note that similar to [`File::read`], it is not an error to return with a
/// short read.
///
/// [`File::read`]: ../../../../std/fs/struct.File.html#method.read
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let bufs = &mut [IoSliceMut::new(buf)];
self.read_vectored_at(bufs, offset)
}

/// Reads a number of bytes starting from a given offset.
///
/// Returns the number of bytes read.
Expand All @@ -25,7 +43,80 @@ pub trait FileExt {
/// return with a short read.
///
/// [`File::read`]: ../../../../std/fs/struct.File.html#method.read_vectored
fn read_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;

/// Reads the exact number of byte required to fill `buf` from the given offset.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
///
/// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact
/// [`read_at`]: #tymethod.read_at
///
/// # 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.
///
/// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
/// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.read_at(buf, offset) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
} else {
Ok(())
}
}

/// Writes a number of bytes starting from a given offset.
///
/// Returns the number of bytes written.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// When writing beyond the end of the file, the file is appropriately
/// extended and the intermediate bytes are initialized with the value 0.
///
/// Note that similar to [`File::write`], it is not an error to return a
/// short write.
///
/// [`File::write`]: ../../../../std/fs/struct.File.html#write.v
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let bufs = &[IoSlice::new(buf)];
self.write_vectored_at(bufs, offset)
}

/// Writes a number of bytes starting from a given offset.
///
Expand All @@ -43,7 +134,49 @@ pub trait FileExt {
/// short write.
///
/// [`File::write`]: ../../../../std/fs/struct.File.html#method.write_vectored
fn write_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;

/// Attempts to write an entire buffer starting from a given offset.
///
/// The offset is relative to the start of the file and thus independent
/// from the current cursor.
///
/// The current file cursor is not affected by this function.
///
/// This method will continuously call [`write_at`] until there is no more data
/// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
/// returned. This method will not return until the entire buffer has been
/// successfully written or such an error occurs. The first error that is
/// not of [`ErrorKind::Interrupted`] kind generated from this method will be
/// returned.
///
/// # Errors
///
/// This function will return the first error of
/// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
///
/// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
/// [`write_at`]: #tymethod.write_at
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.write_at(buf, offset) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to write whole buffer",
));
}
Ok(n) => {
buf = &buf[n..];
offset += n as u64
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}

/// Returns the current position within the file.
///
Expand Down Expand Up @@ -105,11 +238,11 @@ pub trait FileExt {
// FIXME: bind random_get maybe? - on crates.io for unix

impl FileExt for fs::File {
fn read_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
self.as_inner().fd().pread(bufs, offset)
}

fn write_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
self.as_inner().fd().pwrite(bufs, offset)
}

Expand Down

0 comments on commit fee1d6f

Please sign in to comment.