Auto merge of #89518 - a1phyr:unix_file_vectored_at, r=workingjubilee
Add vectored positioned I/O on Unix Add methods for vectored I/O with an offset on `File` for `unix` under `#![feature(unix_file_vectored_at)]`. The new methods are wrappers around `preadv` and `pwritev`. Tracking issue: #89517
This commit is contained in:
commit
0fbfc3e769
@ -17,6 +17,10 @@
|
||||
#[allow(unused_imports)]
|
||||
use io::{Read, Write};
|
||||
|
||||
// Tests for this module
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Unix-specific extensions to [`fs::File`].
|
||||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
pub trait FileExt {
|
||||
@ -54,6 +58,16 @@ pub trait FileExt {
|
||||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
|
||||
|
||||
/// Like `read_at`, except that it reads into a slice of buffers.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method must behave
|
||||
/// equivalently to a single call to read with concatenated buffers.
|
||||
#[unstable(feature = "unix_file_vectored_at", issue = "89517")]
|
||||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
io::default_read_vectored(|b| self.read_at(b, offset), bufs)
|
||||
}
|
||||
|
||||
/// 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
|
||||
@ -155,6 +169,16 @@ fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
|
||||
#[stable(feature = "file_offset", since = "1.15.0")]
|
||||
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
|
||||
|
||||
/// Like `write_at`, except that it writes from a slice of buffers.
|
||||
///
|
||||
/// Data is copied from each buffer in order, with the final buffer read
|
||||
/// from possibly being only partially consumed. This method must behave as
|
||||
/// a call to `write_at` with the buffers concatenated would.
|
||||
#[unstable(feature = "unix_file_vectored_at", issue = "89517")]
|
||||
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
|
||||
io::default_write_vectored(|b| self.write_at(b, offset), bufs)
|
||||
}
|
||||
|
||||
/// Attempts to write an entire buffer starting from a given offset.
|
||||
///
|
||||
/// The offset is relative to the start of the file and thus independent
|
||||
@ -218,9 +242,15 @@ impl FileExt for fs::File {
|
||||
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().read_at(buf, offset)
|
||||
}
|
||||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().read_vectored_at(bufs, offset)
|
||||
}
|
||||
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().write_at(buf, offset)
|
||||
}
|
||||
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
|
||||
self.as_inner().write_vectored_at(bufs, offset)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-specific extensions to [`fs::Permissions`].
|
||||
|
57
library/std/src/os/unix/fs/tests.rs
Normal file
57
library/std/src/os/unix/fs/tests.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn read_vectored_at() {
|
||||
let msg = b"preadv is working!";
|
||||
let dir = crate::sys_common::io::test::tmpdir();
|
||||
|
||||
let filename = dir.join("preadv.txt");
|
||||
{
|
||||
let mut file = fs::File::create(&filename).unwrap();
|
||||
file.write_all(msg).unwrap();
|
||||
}
|
||||
{
|
||||
let file = fs::File::open(&filename).unwrap();
|
||||
let mut buf0 = [0; 4];
|
||||
let mut buf1 = [0; 3];
|
||||
|
||||
let mut iovec = [io::IoSliceMut::new(&mut buf0), io::IoSliceMut::new(&mut buf1)];
|
||||
|
||||
let n = file.read_vectored_at(&mut iovec, 4).unwrap();
|
||||
|
||||
assert!(n == 4 || n == 7);
|
||||
assert_eq!(&buf0, b"dv i");
|
||||
|
||||
if n == 7 {
|
||||
assert_eq!(&buf1, b"s w");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_vectored_at() {
|
||||
let msg = b"pwritev is not working!";
|
||||
let dir = crate::sys_common::io::test::tmpdir();
|
||||
|
||||
let filename = dir.join("preadv.txt");
|
||||
{
|
||||
let mut file = fs::File::create(&filename).unwrap();
|
||||
file.write_all(msg).unwrap();
|
||||
}
|
||||
let expected = {
|
||||
let file = fs::File::options().write(true).open(&filename).unwrap();
|
||||
let buf0 = b" ";
|
||||
let buf1 = b"great ";
|
||||
|
||||
let iovec = [io::IoSlice::new(buf0), io::IoSlice::new(buf1)];
|
||||
|
||||
let n = file.write_vectored_at(&iovec, 11).unwrap();
|
||||
|
||||
assert!(n == 4 || n == 11);
|
||||
|
||||
if n == 4 { b"pwritev is working!" } else { b"pwritev is great !" }
|
||||
};
|
||||
|
||||
let content = fs::read(&filename).unwrap();
|
||||
assert_eq!(&content, expected);
|
||||
}
|
@ -98,7 +98,7 @@ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::readv(
|
||||
self.as_raw_fd(),
|
||||
bufs.as_ptr() as *const libc::iovec,
|
||||
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
|
||||
cmp::min(bufs.len(), max_iov()) as libc::c_int,
|
||||
)
|
||||
})?;
|
||||
@ -107,7 +107,7 @@ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
|
||||
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
|
||||
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
return crate::io::default_read_vectored(|b| self.read(b), bufs);
|
||||
io::default_read_vectored(|b| self.read(b), bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -153,6 +153,95 @@ pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "emscripten",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "illumos",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd",
|
||||
))]
|
||||
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::preadv(
|
||||
self.as_raw_fd(),
|
||||
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
|
||||
cmp::min(bufs.len(), max_iov()) as libc::c_int,
|
||||
offset as _,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "illumos",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
)))]
|
||||
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
io::default_read_vectored(|b| self.read_at(b, offset), bufs)
|
||||
}
|
||||
|
||||
// We support some old Android versions that do not have `preadv` in libc,
|
||||
// so we use weak linkage and fallback to a direct syscall if not available.
|
||||
//
|
||||
// On 32-bit targets, we don't want to deal with weird ABI issues around
|
||||
// passing 64-bits parameters to syscalls, so we fallback to the default
|
||||
// implementation if `preadv` is not available.
|
||||
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
|
||||
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
super::weak::syscall! {
|
||||
fn preadv(
|
||||
fd: libc::c_int,
|
||||
iovec: *const libc::iovec,
|
||||
n_iovec: libc::c_int,
|
||||
offset: off64_t
|
||||
) -> isize
|
||||
}
|
||||
|
||||
let ret = cvt(unsafe {
|
||||
preadv(
|
||||
self.as_raw_fd(),
|
||||
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
|
||||
cmp::min(bufs.len(), max_iov()) as libc::c_int,
|
||||
offset as _,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
// We support old MacOS and iOS versions that do not have `preadv`. There is
|
||||
// no `syscall` possible in these platform.
|
||||
#[cfg(any(
|
||||
all(target_os = "android", target_pointer_width = "32"),
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
))]
|
||||
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
|
||||
|
||||
match preadv64.get() {
|
||||
Some(preadv) => {
|
||||
let ret = cvt(unsafe {
|
||||
preadv(
|
||||
self.as_raw_fd(),
|
||||
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
|
||||
cmp::min(bufs.len(), max_iov()) as libc::c_int,
|
||||
offset as _,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::write(
|
||||
@ -178,7 +267,7 @@ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
|
||||
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
|
||||
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
return crate::io::default_write_vectored(|b| self.write(b), bufs);
|
||||
io::default_write_vectored(|b| self.write(b), bufs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -203,6 +292,95 @@ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "emscripten",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "illumos",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd",
|
||||
))]
|
||||
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
|
||||
let ret = cvt(unsafe {
|
||||
libc::pwritev(
|
||||
self.as_raw_fd(),
|
||||
bufs.as_ptr() as *const libc::iovec,
|
||||
cmp::min(bufs.len(), max_iov()) as libc::c_int,
|
||||
offset as _,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "android",
|
||||
target_os = "emscripten",
|
||||
target_os = "freebsd",
|
||||
target_os = "fuchsia",
|
||||
target_os = "illumos",
|
||||
target_os = "ios",
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "netbsd",
|
||||
)))]
|
||||
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
|
||||
io::default_write_vectored(|b| self.write_at(b, offset), bufs)
|
||||
}
|
||||
|
||||
// We support some old Android versions that do not have `pwritev` in libc,
|
||||
// so we use weak linkage and fallback to a direct syscall if not available.
|
||||
//
|
||||
// On 32-bit targets, we don't want to deal with weird ABI issues around
|
||||
// passing 64-bits parameters to syscalls, so we fallback to the default
|
||||
// implementation if `pwritev` is not available.
|
||||
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
|
||||
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
|
||||
super::weak::syscall! {
|
||||
fn pwritev(
|
||||
fd: libc::c_int,
|
||||
iovec: *const libc::iovec,
|
||||
n_iovec: libc::c_int,
|
||||
offset: off64_t
|
||||
) -> isize
|
||||
}
|
||||
|
||||
let ret = cvt(unsafe {
|
||||
pwritev(
|
||||
self.as_raw_fd(),
|
||||
bufs.as_ptr() as *const libc::iovec,
|
||||
cmp::min(bufs.len(), max_iov()) as libc::c_int,
|
||||
offset as _,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
|
||||
// We support old MacOS and iOS versions that do not have `pwritev`. There is
|
||||
// no `syscall` possible in these platform.
|
||||
#[cfg(any(
|
||||
all(target_os = "android", target_pointer_width = "32"),
|
||||
target_os = "ios",
|
||||
target_os = "macos",
|
||||
))]
|
||||
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
|
||||
super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
|
||||
|
||||
match pwritev64.get() {
|
||||
Some(pwritev) => {
|
||||
let ret = cvt(unsafe {
|
||||
pwritev(
|
||||
self.as_raw_fd(),
|
||||
bufs.as_ptr() as *const libc::iovec,
|
||||
cmp::min(bufs.len(), max_iov()) as libc::c_int,
|
||||
offset as _,
|
||||
)
|
||||
})?;
|
||||
Ok(ret as usize)
|
||||
}
|
||||
None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
target_env = "newlib",
|
||||
target_os = "solaris",
|
||||
|
@ -1132,6 +1132,10 @@ pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0.read_buf(cursor)
|
||||
}
|
||||
|
||||
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
|
||||
self.0.read_vectored_at(bufs, offset)
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
@ -1149,6 +1153,10 @@ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
|
||||
self.0.write_at(buf, offset)
|
||||
}
|
||||
|
||||
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
|
||||
self.0.write_vectored_at(bufs, offset)
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user