Skip to content

Commit 09ac65a

Browse files
committed
Add read_buf equivalents for positioned reads
Adds the following items under the `read_buf_at` (rust-lang#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 4c83e55 commit 09ac65a

File tree

7 files changed

+194
-21
lines changed

7 files changed

+194
-21
lines changed

library/std/src/fs/tests.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,47 @@ fn file_test_io_read_write_at() {
452452
check!(fs::remove_file(&filename));
453453
}
454454

455+
#[test]
456+
#[cfg(unix)]
457+
fn test_read_buf_at() {
458+
use crate::os::unix::fs::FileExt;
459+
460+
let tmpdir = tmpdir();
461+
let filename = tmpdir.join("file_rt_io_file_test_read_buf_at.txt");
462+
{
463+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
464+
let mut file = check!(oo.open(&filename));
465+
check!(file.write_all(b"0123456789"));
466+
}
467+
{
468+
let mut file = check!(File::open(&filename));
469+
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
470+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
471+
472+
check!(file.read_buf_exact_at(buf.unfilled(), 2));
473+
assert_eq!(buf.filled(), b"23456");
474+
475+
// Already full
476+
check!(file.read_buf_exact_at(buf.unfilled(), 3));
477+
check!(file.read_buf_exact_at(buf.unfilled(), 10));
478+
assert_eq!(buf.filled(), b"23456");
479+
assert_eq!(check!(file.stream_position()), 0);
480+
481+
// Exact read past eof fails
482+
let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err();
483+
assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
484+
assert_eq!(check!(file.stream_position()), 0);
485+
486+
// Read past eof is noop
487+
check!(file.read_buf_at(buf.clear().unfilled(), 10));
488+
assert_eq!(buf.filled(), b"");
489+
check!(file.read_buf_at(buf.clear().unfilled(), 11));
490+
assert_eq!(buf.filled(), b"");
491+
assert_eq!(check!(file.stream_position()), 0);
492+
}
493+
check!(fs::remove_file(&filename));
494+
}
495+
455496
#[test]
456497
#[cfg(unix)]
457498
fn set_get_unix_permissions() {
@@ -528,6 +569,41 @@ fn file_test_io_seek_read_write() {
528569
check!(fs::remove_file(&filename));
529570
}
530571

572+
#[test]
573+
#[cfg(windows)]
574+
fn test_seek_read_buf() {
575+
use crate::os::windows::fs::FileExt;
576+
577+
let tmpdir = tmpdir();
578+
let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt");
579+
{
580+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
581+
let mut file = check!(oo.open(&filename));
582+
check!(file.write_all(b"0123456789"));
583+
}
584+
{
585+
let mut file = check!(File::open(&filename));
586+
let mut buf: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()];
587+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
588+
589+
// Seek read
590+
check!(buf.seek_read(buf.unfilled(), 8));
591+
assert_eq!(buf.filled(), b"8");
592+
assert_eq!(check!(file.stream_position()), 9);
593+
594+
// Empty seek read
595+
check!(buf.seek_read(buf.unfilled(), 0));
596+
assert_rq!(buf.filled(), b"8");
597+
assert_eq!(check!(file.stream_position()), 9);
598+
599+
// Seek read past eof
600+
check(buf.seek_read(buf.clear().unfilled(), 10));
601+
assert_eq!(buf.filled(), b"");
602+
assert_eq!(check!(file.stream_position()), 9);
603+
}
604+
check!(fs::remove_file(&filename));
605+
}
606+
531607
#[test]
532608
fn file_test_read_buf() {
533609
let tmpdir = tmpdir();

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

Lines changed: 40 additions & 0 deletions
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

Lines changed: 19 additions & 0 deletions
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

Lines changed: 38 additions & 21 deletions
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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,10 @@ impl File {
14111411
self.0.read_buf(cursor)
14121412
}
14131413

1414+
pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
1415+
self.0.read_buf_at(cursor, offset)
1416+
}
1417+
14141418
pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
14151419
self.0.read_vectored_at(bufs, offset)
14161420
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,10 @@ impl File {
586586
self.handle.read_buf(cursor)
587587
}
588588

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

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

Lines changed: 13 additions & 0 deletions
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)