Skip to content

Commit 0fbfc3e

Browse files
committed
Auto merge of rust-lang#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: rust-lang#89517
2 parents 70adb4e + 92f35b3 commit 0fbfc3e

File tree

4 files changed

+276
-3
lines changed

4 files changed

+276
-3
lines changed

library/std/src/os/unix/fs.rs

+30
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ use crate::sealed::Sealed;
1717
#[allow(unused_imports)]
1818
use io::{Read, Write};
1919

20+
// Tests for this module
21+
#[cfg(test)]
22+
mod tests;
23+
2024
/// Unix-specific extensions to [`fs::File`].
2125
#[stable(feature = "file_offset", since = "1.15.0")]
2226
pub trait FileExt {
@@ -54,6 +58,16 @@ pub trait FileExt {
5458
#[stable(feature = "file_offset", since = "1.15.0")]
5559
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
5660

61+
/// Like `read_at`, except that it reads into a slice of buffers.
62+
///
63+
/// Data is copied to fill each buffer in order, with the final buffer
64+
/// written to possibly being only partially filled. This method must behave
65+
/// equivalently to a single call to read with concatenated buffers.
66+
#[unstable(feature = "unix_file_vectored_at", issue = "89517")]
67+
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
68+
io::default_read_vectored(|b| self.read_at(b, offset), bufs)
69+
}
70+
5771
/// Reads the exact number of byte required to fill `buf` from the given offset.
5872
///
5973
/// The offset is relative to the start of the file and thus independent
@@ -155,6 +169,16 @@ pub trait FileExt {
155169
#[stable(feature = "file_offset", since = "1.15.0")]
156170
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
157171

172+
/// Like `write_at`, except that it writes from a slice of buffers.
173+
///
174+
/// Data is copied from each buffer in order, with the final buffer read
175+
/// from possibly being only partially consumed. This method must behave as
176+
/// a call to `write_at` with the buffers concatenated would.
177+
#[unstable(feature = "unix_file_vectored_at", issue = "89517")]
178+
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
179+
io::default_write_vectored(|b| self.write_at(b, offset), bufs)
180+
}
181+
158182
/// Attempts to write an entire buffer starting from a given offset.
159183
///
160184
/// The offset is relative to the start of the file and thus independent
@@ -218,9 +242,15 @@ impl FileExt for fs::File {
218242
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
219243
self.as_inner().read_at(buf, offset)
220244
}
245+
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
246+
self.as_inner().read_vectored_at(bufs, offset)
247+
}
221248
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
222249
self.as_inner().write_at(buf, offset)
223250
}
251+
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
252+
self.as_inner().write_vectored_at(bufs, offset)
253+
}
224254
}
225255

226256
/// Unix-specific extensions to [`fs::Permissions`].

library/std/src/os/unix/fs/tests.rs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use super::*;
2+
3+
#[test]
4+
fn read_vectored_at() {
5+
let msg = b"preadv is working!";
6+
let dir = crate::sys_common::io::test::tmpdir();
7+
8+
let filename = dir.join("preadv.txt");
9+
{
10+
let mut file = fs::File::create(&filename).unwrap();
11+
file.write_all(msg).unwrap();
12+
}
13+
{
14+
let file = fs::File::open(&filename).unwrap();
15+
let mut buf0 = [0; 4];
16+
let mut buf1 = [0; 3];
17+
18+
let mut iovec = [io::IoSliceMut::new(&mut buf0), io::IoSliceMut::new(&mut buf1)];
19+
20+
let n = file.read_vectored_at(&mut iovec, 4).unwrap();
21+
22+
assert!(n == 4 || n == 7);
23+
assert_eq!(&buf0, b"dv i");
24+
25+
if n == 7 {
26+
assert_eq!(&buf1, b"s w");
27+
}
28+
}
29+
}
30+
31+
#[test]
32+
fn write_vectored_at() {
33+
let msg = b"pwritev is not working!";
34+
let dir = crate::sys_common::io::test::tmpdir();
35+
36+
let filename = dir.join("preadv.txt");
37+
{
38+
let mut file = fs::File::create(&filename).unwrap();
39+
file.write_all(msg).unwrap();
40+
}
41+
let expected = {
42+
let file = fs::File::options().write(true).open(&filename).unwrap();
43+
let buf0 = b" ";
44+
let buf1 = b"great ";
45+
46+
let iovec = [io::IoSlice::new(buf0), io::IoSlice::new(buf1)];
47+
48+
let n = file.write_vectored_at(&iovec, 11).unwrap();
49+
50+
assert!(n == 4 || n == 11);
51+
52+
if n == 4 { b"pwritev is working!" } else { b"pwritev is great !" }
53+
};
54+
55+
let content = fs::read(&filename).unwrap();
56+
assert_eq!(&content, expected);
57+
}

library/std/src/sys/unix/fd.rs

+181-3
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl FileDesc {
9898
let ret = cvt(unsafe {
9999
libc::readv(
100100
self.as_raw_fd(),
101-
bufs.as_ptr() as *const libc::iovec,
101+
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
102102
cmp::min(bufs.len(), max_iov()) as libc::c_int,
103103
)
104104
})?;
@@ -107,7 +107,7 @@ impl FileDesc {
107107

108108
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
109109
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
110-
return crate::io::default_read_vectored(|b| self.read(b), bufs);
110+
io::default_read_vectored(|b| self.read(b), bufs)
111111
}
112112

113113
#[inline]
@@ -153,6 +153,95 @@ impl FileDesc {
153153
Ok(())
154154
}
155155

156+
#[cfg(any(
157+
target_os = "emscripten",
158+
target_os = "freebsd",
159+
target_os = "fuchsia",
160+
target_os = "illumos",
161+
target_os = "linux",
162+
target_os = "netbsd",
163+
))]
164+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
165+
let ret = cvt(unsafe {
166+
libc::preadv(
167+
self.as_raw_fd(),
168+
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
169+
cmp::min(bufs.len(), max_iov()) as libc::c_int,
170+
offset as _,
171+
)
172+
})?;
173+
Ok(ret as usize)
174+
}
175+
176+
#[cfg(not(any(
177+
target_os = "android",
178+
target_os = "emscripten",
179+
target_os = "freebsd",
180+
target_os = "fuchsia",
181+
target_os = "illumos",
182+
target_os = "ios",
183+
target_os = "linux",
184+
target_os = "macos",
185+
target_os = "netbsd",
186+
)))]
187+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
188+
io::default_read_vectored(|b| self.read_at(b, offset), bufs)
189+
}
190+
191+
// We support some old Android versions that do not have `preadv` in libc,
192+
// so we use weak linkage and fallback to a direct syscall if not available.
193+
//
194+
// On 32-bit targets, we don't want to deal with weird ABI issues around
195+
// passing 64-bits parameters to syscalls, so we fallback to the default
196+
// implementation if `preadv` is not available.
197+
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
198+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
199+
super::weak::syscall! {
200+
fn preadv(
201+
fd: libc::c_int,
202+
iovec: *const libc::iovec,
203+
n_iovec: libc::c_int,
204+
offset: off64_t
205+
) -> isize
206+
}
207+
208+
let ret = cvt(unsafe {
209+
preadv(
210+
self.as_raw_fd(),
211+
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
212+
cmp::min(bufs.len(), max_iov()) as libc::c_int,
213+
offset as _,
214+
)
215+
})?;
216+
Ok(ret as usize)
217+
}
218+
219+
// We support old MacOS and iOS versions that do not have `preadv`. There is
220+
// no `syscall` possible in these platform.
221+
#[cfg(any(
222+
all(target_os = "android", target_pointer_width = "32"),
223+
target_os = "ios",
224+
target_os = "macos",
225+
))]
226+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
227+
super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
228+
229+
match preadv64.get() {
230+
Some(preadv) => {
231+
let ret = cvt(unsafe {
232+
preadv(
233+
self.as_raw_fd(),
234+
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
235+
cmp::min(bufs.len(), max_iov()) as libc::c_int,
236+
offset as _,
237+
)
238+
})?;
239+
Ok(ret as usize)
240+
}
241+
None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
242+
}
243+
}
244+
156245
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
157246
let ret = cvt(unsafe {
158247
libc::write(
@@ -178,7 +267,7 @@ impl FileDesc {
178267

179268
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
180269
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
181-
return crate::io::default_write_vectored(|b| self.write(b), bufs);
270+
io::default_write_vectored(|b| self.write(b), bufs)
182271
}
183272

184273
#[inline]
@@ -203,6 +292,95 @@ impl FileDesc {
203292
}
204293
}
205294

295+
#[cfg(any(
296+
target_os = "emscripten",
297+
target_os = "freebsd",
298+
target_os = "fuchsia",
299+
target_os = "illumos",
300+
target_os = "linux",
301+
target_os = "netbsd",
302+
))]
303+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
304+
let ret = cvt(unsafe {
305+
libc::pwritev(
306+
self.as_raw_fd(),
307+
bufs.as_ptr() as *const libc::iovec,
308+
cmp::min(bufs.len(), max_iov()) as libc::c_int,
309+
offset as _,
310+
)
311+
})?;
312+
Ok(ret as usize)
313+
}
314+
315+
#[cfg(not(any(
316+
target_os = "android",
317+
target_os = "emscripten",
318+
target_os = "freebsd",
319+
target_os = "fuchsia",
320+
target_os = "illumos",
321+
target_os = "ios",
322+
target_os = "linux",
323+
target_os = "macos",
324+
target_os = "netbsd",
325+
)))]
326+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
327+
io::default_write_vectored(|b| self.write_at(b, offset), bufs)
328+
}
329+
330+
// We support some old Android versions that do not have `pwritev` in libc,
331+
// so we use weak linkage and fallback to a direct syscall if not available.
332+
//
333+
// On 32-bit targets, we don't want to deal with weird ABI issues around
334+
// passing 64-bits parameters to syscalls, so we fallback to the default
335+
// implementation if `pwritev` is not available.
336+
#[cfg(all(target_os = "android", target_pointer_width = "64"))]
337+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
338+
super::weak::syscall! {
339+
fn pwritev(
340+
fd: libc::c_int,
341+
iovec: *const libc::iovec,
342+
n_iovec: libc::c_int,
343+
offset: off64_t
344+
) -> isize
345+
}
346+
347+
let ret = cvt(unsafe {
348+
pwritev(
349+
self.as_raw_fd(),
350+
bufs.as_ptr() as *const libc::iovec,
351+
cmp::min(bufs.len(), max_iov()) as libc::c_int,
352+
offset as _,
353+
)
354+
})?;
355+
Ok(ret as usize)
356+
}
357+
358+
// We support old MacOS and iOS versions that do not have `pwritev`. There is
359+
// no `syscall` possible in these platform.
360+
#[cfg(any(
361+
all(target_os = "android", target_pointer_width = "32"),
362+
target_os = "ios",
363+
target_os = "macos",
364+
))]
365+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
366+
super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
367+
368+
match pwritev64.get() {
369+
Some(pwritev) => {
370+
let ret = cvt(unsafe {
371+
pwritev(
372+
self.as_raw_fd(),
373+
bufs.as_ptr() as *const libc::iovec,
374+
cmp::min(bufs.len(), max_iov()) as libc::c_int,
375+
offset as _,
376+
)
377+
})?;
378+
Ok(ret as usize)
379+
}
380+
None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
381+
}
382+
}
383+
206384
#[cfg(not(any(
207385
target_env = "newlib",
208386
target_os = "solaris",

library/std/src/sys/unix/fs.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,10 @@ impl File {
11321132
self.0.read_buf(cursor)
11331133
}
11341134

1135+
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
1136+
self.0.read_vectored_at(bufs, offset)
1137+
}
1138+
11351139
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
11361140
self.0.write(buf)
11371141
}
@@ -1149,6 +1153,10 @@ impl File {
11491153
self.0.write_at(buf, offset)
11501154
}
11511155

1156+
pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
1157+
self.0.write_vectored_at(bufs, offset)
1158+
}
1159+
11521160
pub fn flush(&self) -> io::Result<()> {
11531161
Ok(())
11541162
}

0 commit comments

Comments
 (0)