Skip to content

Commit e8a35d4

Browse files
committed
Add read_buf equivalents for positioned reads
Adds the following items under the `read_buf_at` feature: - `std::os::unix::fs::FileExt::read_buf_at` - `std::os::unix::fs::FileExt::read_buf_exact_at` - `std::os::windows::fs::FileExt::seek_read_buf`
1 parent 8afd710 commit e8a35d4

File tree

8 files changed

+272
-22
lines changed

8 files changed

+272
-22
lines changed

library/std/src/fs/tests.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,47 @@ fn file_test_io_read_write_at() {
490490
check!(fs::remove_file(&filename));
491491
}
492492

493+
#[test]
494+
#[cfg(unix)]
495+
fn test_read_buf_at() {
496+
use crate::os::unix::fs::FileExt;
497+
498+
let tmpdir = tmpdir();
499+
let filename = tmpdir.join("file_rt_io_file_test_read_buf_at.txt");
500+
{
501+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
502+
let mut file = check!(oo.open(&filename));
503+
check!(file.write_all(b"0123456789"));
504+
}
505+
{
506+
let mut file = check!(File::open(&filename));
507+
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
508+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
509+
510+
check!(file.read_buf_exact_at(buf.unfilled(), 2));
511+
assert_eq!(buf.filled(), b"23456");
512+
513+
// Already full
514+
check!(file.read_buf_exact_at(buf.unfilled(), 3));
515+
check!(file.read_buf_exact_at(buf.unfilled(), 10));
516+
assert_eq!(buf.filled(), b"23456");
517+
assert_eq!(check!(file.stream_position()), 0);
518+
519+
// Exact read past eof fails
520+
let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err();
521+
assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
522+
assert_eq!(check!(file.stream_position()), 0);
523+
524+
// Read past eof is noop
525+
check!(file.read_buf_at(buf.clear().unfilled(), 10));
526+
assert_eq!(buf.filled(), b"");
527+
check!(file.read_buf_at(buf.clear().unfilled(), 11));
528+
assert_eq!(buf.filled(), b"");
529+
assert_eq!(check!(file.stream_position()), 0);
530+
}
531+
check!(fs::remove_file(&filename));
532+
}
533+
493534
#[test]
494535
#[cfg(unix)]
495536
fn set_get_unix_permissions() {
@@ -566,6 +607,39 @@ fn file_test_io_seek_read_write() {
566607
check!(fs::remove_file(&filename));
567608
}
568609

610+
#[test]
611+
#[cfg(windows)]
612+
fn test_seek_read_buf() {
613+
use crate::os::windows::fs::FileExt;
614+
615+
let tmpdir = tmpdir();
616+
let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt");
617+
{
618+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
619+
let mut file = check!(oo.open(&filename));
620+
check!(file.write_all(b"0123456789"));
621+
}
622+
{
623+
let mut file = check!(File::open(&filename));
624+
let mut buf: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()];
625+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
626+
627+
// Seek read
628+
check!(file.seek_read_buf(buf.unfilled(), 8));
629+
assert_eq!(buf.filled(), b"8");
630+
assert_eq!(check!(file.stream_position()), 9);
631+
632+
// Empty seek read
633+
check!(file.seek_read_buf(buf.unfilled(), 0));
634+
assert_eq!(buf.filled(), b"8");
635+
636+
// Seek read past eof
637+
check!(file.seek_read_buf(buf.clear().unfilled(), 10));
638+
assert_eq!(buf.filled(), b"");
639+
}
640+
check!(fs::remove_file(&filename));
641+
}
642+
569643
#[test]
570644
fn file_test_read_buf() {
571645
let tmpdir = tmpdir();

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@
333333
#![feature(array_chunks)]
334334
#![feature(bstr)]
335335
#![feature(bstr_internals)]
336+
#![feature(cfg_select)]
336337
#![feature(char_internals)]
337338
#![feature(clone_to_uninit)]
338339
#![feature(core_intrinsics)]

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

Lines changed: 92 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,94 @@ 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+
///
141+
/// # Examples
142+
///
143+
/// ```no_run
144+
/// #![feature(core_io_borrowed_buf)]
145+
/// #![feature(read_buf_at)]
146+
///
147+
/// use std::io;
148+
/// use std::io::BorrowedBuf;
149+
/// use std::fs::File;
150+
/// use std::mem::MaybeUninit;
151+
/// use std::os::unix::prelude::*;
152+
///
153+
/// fn main() -> io::Result<()> {
154+
/// let mut file = File::open("pi.txt")?;
155+
///
156+
/// // Read some bytes starting from offset 2
157+
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
158+
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
159+
/// file.read_buf_at(buf.unfilled(), 2)?;
160+
///
161+
/// assert!(buf.filled().starts_with(b"1"));
162+
///
163+
/// Ok(())
164+
/// }
165+
/// ```
166+
#[unstable(feature = "read_buf_at", issue = "140771")]
167+
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
168+
io::default_read_buf(|b| self.read_at(b, offset), buf)
169+
}
170+
171+
/// Reads the exact number of bytes required to fill the buffer from a given
172+
/// offset.
173+
///
174+
/// This is equivalent to the [`read_exact_at`](FileExt::read_exact_at) method,
175+
/// except that it is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow
176+
/// use with uninitialized buffers. The new data will be appended to any
177+
/// existing contents of `buf`.
178+
///
179+
/// # Examples
180+
///
181+
/// ```no_run
182+
/// #![feature(core_io_borrowed_buf)]
183+
/// #![feature(read_buf_at)]
184+
///
185+
/// use std::io;
186+
/// use std::io::BorrowedBuf;
187+
/// use std::fs::File;
188+
/// use std::mem::MaybeUninit;
189+
/// use std::os::unix::prelude::*;
190+
///
191+
/// fn main() -> io::Result<()> {
192+
/// let mut file = File::open("pi.txt")?;
193+
///
194+
/// // Read exactly 10 bytes starting from offset 2
195+
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
196+
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
197+
/// file.read_buf_exact_at(buf.unfilled(), 2)?;
198+
///
199+
/// assert_eq!(buf.filled(), b"1415926535");
200+
///
201+
/// Ok(())
202+
/// }
203+
/// ```
204+
#[unstable(feature = "read_buf_at", issue = "140771")]
205+
fn read_buf_exact_at(&self, mut buf: BorrowedCursor<'_>, mut offset: u64) -> io::Result<()> {
206+
while buf.capacity() > 0 {
207+
let prev_written = buf.written();
208+
match self.read_buf_at(buf.reborrow(), offset) {
209+
Ok(()) => {}
210+
Err(e) if e.is_interrupted() => {}
211+
Err(e) => return Err(e),
212+
}
213+
let n = buf.written() - prev_written;
214+
offset += n as u64;
215+
if n == 0 {
216+
return Err(io::Error::READ_EXACT_EOF);
217+
}
218+
}
219+
Ok(())
220+
}
221+
133222
/// Writes a number of bytes starting from a given offset.
134223
///
135224
/// Returns the number of bytes written.
@@ -264,6 +353,9 @@ impl FileExt for fs::File {
264353
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
265354
self.as_inner().read_at(buf, offset)
266355
}
356+
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
357+
self.as_inner().read_buf_at(buf, offset)
358+
}
267359
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
268360
self.as_inner().read_vectored_at(bufs, offset)
269361
}

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

Lines changed: 45 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,46 @@ 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+
///
63+
/// # Examples
64+
///
65+
/// ```no_run
66+
/// #![feature(core_io_borrowed_buf)]
67+
/// #![feature(read_buf_at)]
68+
///
69+
/// use std::io;
70+
/// use std::io::BorrowedBuf;
71+
/// use std::fs::File;
72+
/// use std::mem::MaybeUninit;
73+
/// use std::os::windows::prelude::*;
74+
///
75+
/// fn main() -> io::Result<()> {
76+
/// let mut file = File::open("pi.txt")?;
77+
///
78+
/// // Read some bytes starting from offset 2
79+
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
80+
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
81+
/// file.seek_read_buf(buf.unfilled(), 2)?;
82+
///
83+
/// assert!(buf.filled().starts_with(b"1"));
84+
///
85+
/// Ok(())
86+
/// }
87+
/// ```
88+
#[unstable(feature = "read_buf_at", issue = "140771")]
89+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
90+
io::default_read_buf(|b| self.seek_read(b, offset), buf)
91+
}
92+
5293
/// Seeks to a given position and writes a number of bytes.
5394
///
5495
/// Returns the number of bytes written.
@@ -89,6 +130,10 @@ impl FileExt for fs::File {
89130
self.as_inner().read_at(buf, offset)
90131
}
91132

133+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
134+
self.as_inner().read_buf_at(buf, offset)
135+
}
136+
92137
fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
93138
self.as_inner().write_at(buf, offset)
94139
}

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

Lines changed: 39 additions & 22 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+
crate::cfg_select! {
91+
any(
92+
all(target_os = "linux", not(target_env = "musl")),
93+
target_os = "android",
94+
target_os = "hurd",
95+
) => {
96+
use libc::pread64;
97+
}
98+
_ => {
99+
use libc::pread as pread64;
100+
}
101+
}
102+
90103
impl FileDesc {
91104
#[inline]
92105
pub fn try_clone(&self) -> io::Result<Self> {
@@ -146,42 +159,47 @@ 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>(),
180180
cmp::min(cursor.capacity(), READ_LIMIT),
181181
)
182182
})?;
183183

184-
// Safety: `ret` bytes were written to the initialized portion of the buffer
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>(),
197+
cmp::min(cursor.capacity(), READ_LIMIT),
198+
offset as off64_t,
199+
)
200+
})?;
201+
202+
// SAFETY: `ret` bytes were written to the initialized portion of the buffer
185203
unsafe {
186204
cursor.advance_unchecked(ret as usize);
187205
}
@@ -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
@@ -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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,10 @@ impl File {
581581
self.handle.read_buf(cursor)
582582
}
583583

584+
pub fn read_buf_at(&self, cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
585+
self.handle.read_buf_at(cursor, offset)
586+
}
587+
584588
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
585589
self.handle.write(buf)
586590
}

0 commit comments

Comments
 (0)