Skip to content

Commit a22d2b4

Browse files
committed
Add read_buf equivalents for positioned reads
Adds the following items under the `read_buf_at` (#140771) feature: - `std::os::unix::FileExt::read_buf_at` - `std::os::unix::FileExt::read_buf_exact_at` - `std::os::windows::FileExt::seek_read_buf`
1 parent 420ca71 commit a22d2b4

File tree

7 files changed

+194
-21
lines changed

7 files changed

+194
-21
lines changed

library/std/src/fs/tests.rs

+76
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,47 @@ fn file_test_io_read_write_at() {
468468
check!(fs::remove_file(&filename));
469469
}
470470

471+
#[test]
472+
#[cfg(unix)]
473+
fn test_read_buf_at() {
474+
use crate::os::unix::fs::FileExt;
475+
476+
let tmpdir = tmpdir();
477+
let filename = tmpdir.join("file_rt_io_file_test_read_buf_at.txt");
478+
{
479+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
480+
let mut file = check!(oo.open(&filename));
481+
check!(file.write_all(b"0123456789"));
482+
}
483+
{
484+
let mut file = check!(File::open(&filename));
485+
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
486+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
487+
488+
check!(file.read_buf_exact_at(buf.unfilled(), 2));
489+
assert_eq!(buf.filled(), b"23456");
490+
491+
// Already full
492+
check!(file.read_buf_exact_at(buf.unfilled(), 3));
493+
check!(file.read_buf_exact_at(buf.unfilled(), 10));
494+
assert_eq!(buf.filled(), b"23456");
495+
assert_eq!(check!(file.stream_position()), 0);
496+
497+
// Exact read past eof fails
498+
let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err();
499+
assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
500+
assert_eq!(check!(file.stream_position()), 0);
501+
502+
// Read past eof is noop
503+
check!(file.read_buf_at(buf.clear().unfilled(), 10));
504+
assert_eq!(buf.filled(), b"");
505+
check!(file.read_buf_at(buf.clear().unfilled(), 11));
506+
assert_eq!(buf.filled(), b"");
507+
assert_eq!(check!(file.stream_position()), 0);
508+
}
509+
check!(fs::remove_file(&filename));
510+
}
511+
471512
#[test]
472513
#[cfg(unix)]
473514
fn set_get_unix_permissions() {
@@ -544,6 +585,41 @@ fn file_test_io_seek_read_write() {
544585
check!(fs::remove_file(&filename));
545586
}
546587

588+
#[test]
589+
#[cfg(windows)]
590+
fn test_seek_read_buf() {
591+
use crate::os::windows::fs::FileExt;
592+
593+
let tmpdir = tmpdir();
594+
let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt");
595+
{
596+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
597+
let mut file = check!(oo.open(&filename));
598+
check!(file.write_all(b"0123456789"));
599+
}
600+
{
601+
let mut file = check!(File::open(&filename));
602+
let mut buf: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()];
603+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
604+
605+
// Seek read
606+
check!(buf.seek_read(buf.unfilled(), 8));
607+
assert_eq!(buf.filled(), b"8");
608+
assert_eq!(check!(file.stream_position()), 9);
609+
610+
// Empty seek read
611+
check!(buf.seek_read(buf.unfilled(), 0));
612+
assert_eq!(buf.filled(), b"8");
613+
assert_eq!(check!(file.stream_position()), 9);
614+
615+
// Seek read past eof
616+
check(buf.seek_read(buf.clear().unfilled(), 10));
617+
assert_eq!(buf.filled(), b"");
618+
assert_eq!(check!(file.stream_position()), 10);
619+
}
620+
check!(fs::remove_file(&filename));
621+
}
622+
547623
#[test]
548624
fn file_test_read_buf() {
549625
let tmpdir = tmpdir();

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

+40
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use super::platform::fs::MetadataExt as _;
1111
// Used for `File::read` on intra-doc links
1212
use crate::ffi::OsStr;
1313
use crate::fs::{self, OpenOptions, Permissions};
14+
use crate::io::BorrowedCursor;
1415
use crate::os::unix::io::{AsFd, AsRawFd};
1516
use crate::path::Path;
1617
use crate::sealed::Sealed;
@@ -130,6 +131,42 @@ pub trait FileExt {
130131
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
131132
}
132133

134+
/// Reads some bytes starting from a given offset into the buffer.
135+
///
136+
/// This equivalent to the [`read_at`](FileExt::read_at) method,
137+
/// except that it is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow
138+
/// use with uninitialized buffers. The new data will be appended to any
139+
/// existing contents of `buf`.
140+
#[unstable(feature = "read_buf_at", issue = "140771")]
141+
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
142+
io::default_read_buf(|b| self.read_at(b, offset), buf)
143+
}
144+
145+
/// Reads the exact number of bytes required to fill the buffer from a given
146+
/// offset.
147+
///
148+
/// This is equivalent to the [`read_exact_at`](FileExt::read_exact_at) method,
149+
/// except that it is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow
150+
/// use with uninitialized buffers. The new data will be appended to any
151+
/// existing contents of `buf`.
152+
#[unstable(feature = "read_buf_at", issue = "140771")]
153+
fn read_buf_exact_at(&self, mut buf: BorrowedCursor<'_>, mut offset: u64) -> io::Result<()> {
154+
while buf.capacity() > 0 {
155+
let prev_written = buf.written();
156+
match self.read_buf_at(buf.reborrow(), offset) {
157+
Ok(()) => {}
158+
Err(e) if e.is_interrupted() => {}
159+
Err(e) => return Err(e),
160+
}
161+
let n = buf.written() - prev_written;
162+
offset += n as u64;
163+
if n == 0 {
164+
return Err(io::Error::READ_EXACT_EOF);
165+
}
166+
}
167+
Ok(())
168+
}
169+
133170
/// Writes a number of bytes starting from a given offset.
134171
///
135172
/// Returns the number of bytes written.
@@ -264,6 +301,9 @@ impl FileExt for fs::File {
264301
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
265302
self.as_inner().read_at(buf, offset)
266303
}
304+
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
305+
self.as_inner().read_buf_at(buf, offset)
306+
}
267307
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
268308
self.as_inner().read_vectored_at(bufs, offset)
269309
}

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

+19
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![stable(feature = "rust1", since = "1.0.0")]
66

77
use crate::fs::{self, Metadata, OpenOptions};
8+
use crate::io::BorrowedCursor;
89
use crate::path::Path;
910
use crate::sealed::Sealed;
1011
use crate::sys_common::{AsInner, AsInnerMut, IntoInner};
@@ -49,6 +50,20 @@ pub trait FileExt {
4950
#[stable(feature = "file_offset", since = "1.15.0")]
5051
fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
5152

53+
/// Seeks to a given position and reads some bytes into the buffer.
54+
///
55+
/// This is equivalent to the [`seek_read`](FileExt::seek_read) method, except
56+
/// that it is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow use
57+
/// with uninitialized buffers. The new data will be appended to any existing
58+
/// contents of `buf`.
59+
///
60+
/// Reading beyond the end of the file will always succeed without reading
61+
/// any bytes.
62+
#[unstable(feature = "read_buf_at", issue = "140771")]
63+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
64+
io::default_read_buf(|b| self.seek_read(b, offset), buf)
65+
}
66+
5267
/// Seeks to a given position and writes a number of bytes.
5368
///
5469
/// Returns the number of bytes written.
@@ -89,6 +104,10 @@ impl FileExt for fs::File {
89104
self.as_inner().read_at(buf, offset)
90105
}
91106

107+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
108+
self.as_inner().read_buf_at(buf, offset)
109+
}
110+
92111
fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
93112
self.as_inner().write_at(buf, offset)
94113
}

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

+38-21
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ const fn max_iov() -> usize {
8787
16 // The minimum value required by POSIX.
8888
}
8989

90+
#[cfg(not(any(
91+
all(target_os = "linux", not(target_env = "musl")),
92+
target_os = "android",
93+
target_os = "hurd"
94+
)))]
95+
use libc::pread as pread64;
96+
#[cfg(any(
97+
all(target_os = "linux", not(target_env = "musl")),
98+
target_os = "android",
99+
target_os = "hurd"
100+
))]
101+
use libc::pread64;
102+
90103
impl FileDesc {
91104
#[inline]
92105
pub fn try_clone(&self) -> io::Result<Self> {
@@ -146,38 +159,43 @@ impl FileDesc {
146159
(&mut me).read_to_end(buf)
147160
}
148161

149-
#[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
150162
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
151-
#[cfg(not(any(
152-
all(target_os = "linux", not(target_env = "musl")),
153-
target_os = "android",
154-
target_os = "hurd"
155-
)))]
156-
use libc::pread as pread64;
157-
#[cfg(any(
158-
all(target_os = "linux", not(target_env = "musl")),
159-
target_os = "android",
160-
target_os = "hurd"
161-
))]
162-
use libc::pread64;
163-
164-
unsafe {
165-
cvt(pread64(
163+
cvt(unsafe {
164+
pread64(
166165
self.as_raw_fd(),
167166
buf.as_mut_ptr() as *mut libc::c_void,
168167
cmp::min(buf.len(), READ_LIMIT),
169168
offset as off64_t,
170-
))
171-
.map(|n| n as usize)
172-
}
169+
)
170+
})
171+
.map(|n| n as usize)
173172
}
174173

175174
pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
175+
// Safety: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
176176
let ret = cvt(unsafe {
177177
libc::read(
178178
self.as_raw_fd(),
179-
cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
179+
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
180+
cmp::min(cursor.capacity(), READ_LIMIT),
181+
)
182+
})?;
183+
184+
// Safety: `ret` bytes were written to the initialized portion of the buffer
185+
unsafe {
186+
cursor.advance_unchecked(ret as usize);
187+
}
188+
Ok(())
189+
}
190+
191+
pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
192+
// Safety: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
193+
let ret = cvt(unsafe {
194+
pread64(
195+
self.as_raw_fd(),
196+
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
180197
cmp::min(cursor.capacity(), READ_LIMIT),
198+
offset as off64_t,
181199
)
182200
})?;
183201

@@ -369,7 +387,6 @@ impl FileDesc {
369387
)))
370388
}
371389

372-
#[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
373390
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
374391
#[cfg(not(any(
375392
all(target_os = "linux", not(target_env = "musl")),

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

+4
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,10 @@ impl File {
14221422
self.0.read_buf(cursor)
14231423
}
14241424

1425+
pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
1426+
self.0.read_buf_at(cursor, offset)
1427+
}
1428+
14251429
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
14261430
self.0.read_vectored_at(bufs, offset)
14271431
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,10 @@ impl File {
587587
self.handle.read_buf(cursor)
588588
}
589589

590+
pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
591+
self.handle.read_buf_at(cursor, offset)
592+
}
593+
590594
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
591595
self.handle.write(buf)
592596
}

library/std/src/sys/pal/windows/handle.rs

+13
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,19 @@ impl Handle {
136136
}
137137
}
138138

139+
pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
140+
// Safety: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
141+
let read = unsafe {
142+
self.synchronous_read(cursor.as_mut().as_mut_ptr(), cursor.capacity(), Some(offset))
143+
}?;
144+
145+
// Safety: `read` bytes were written to the initialized portion of the buffer
146+
unsafe {
147+
cursor.advance_unchecked(read);
148+
}
149+
Ok(())
150+
}
151+
139152
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
140153
let mut me = self;
141154

0 commit comments

Comments
 (0)