Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f0baf1c

Browse files
Auto merge of #140459 - niklasf:feature/read-buf-at, r=<try>
Add `read_buf` equivalents for positioned reads try-job: `x86_64-msvc*` try-job: `test-various*` try-job: `dist-various*`
2 parents fd75a9c + c914c47 commit f0baf1c

File tree

7 files changed

+307
-23
lines changed

7 files changed

+307
-23
lines changed

‎library/std/src/fs/tests.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,85 @@ 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+
// Fill entire buffer with potentially short reads
511+
while buf.unfilled().capacity() > 0 {
512+
let len = buf.len();
513+
check!(file.read_buf_at(buf.unfilled(), 2 + len as u64));
514+
assert!(!buf.filled().is_empty());
515+
assert!(b"23456".starts_with(buf.filled()));
516+
assert_eq!(check!(file.stream_position()), 0);
517+
}
518+
assert_eq!(buf.filled(), b"23456");
519+
520+
// Already full
521+
check!(file.read_buf_at(buf.unfilled(), 3));
522+
check!(file.read_buf_at(buf.unfilled(), 10));
523+
assert_eq!(buf.filled(), b"23456");
524+
assert_eq!(check!(file.stream_position()), 0);
525+
526+
// Read past eof is noop
527+
check!(file.read_buf_at(buf.clear().unfilled(), 10));
528+
assert_eq!(buf.filled(), b"");
529+
check!(file.read_buf_at(buf.clear().unfilled(), 11));
530+
assert_eq!(buf.filled(), b"");
531+
assert_eq!(check!(file.stream_position()), 0);
532+
}
533+
check!(fs::remove_file(&filename));
534+
}
535+
536+
#[test]
537+
#[cfg(unix)]
538+
fn test_read_buf_exact_at() {
539+
use crate::os::unix::fs::FileExt;
540+
541+
let tmpdir = tmpdir();
542+
let filename = tmpdir.join("file_rt_io_file_test_read_buf_exact_at.txt");
543+
{
544+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
545+
let mut file = check!(oo.open(&filename));
546+
check!(file.write_all(b"0123456789"));
547+
}
548+
{
549+
let mut file = check!(File::open(&filename));
550+
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
551+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
552+
553+
// Exact read
554+
check!(file.read_buf_exact_at(buf.unfilled(), 2));
555+
assert_eq!(buf.filled(), b"23456");
556+
assert_eq!(check!(file.stream_position()), 0);
557+
558+
// Already full
559+
check!(file.read_buf_exact_at(buf.unfilled(), 3));
560+
check!(file.read_buf_exact_at(buf.unfilled(), 10));
561+
assert_eq!(buf.filled(), b"23456");
562+
assert_eq!(check!(file.stream_position()), 0);
563+
564+
// Non-empty exact read past eof fails
565+
let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err();
566+
assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
567+
assert_eq!(check!(file.stream_position()), 0);
568+
}
569+
check!(fs::remove_file(&filename));
570+
}
571+
493572
#[test]
494573
#[cfg(unix)]
495574
fn set_get_unix_permissions() {
@@ -566,6 +645,39 @@ fn file_test_io_seek_read_write() {
566645
check!(fs::remove_file(&filename));
567646
}
568647

648+
#[test]
649+
#[cfg(windows)]
650+
fn test_seek_read_buf() {
651+
use crate::os::windows::fs::FileExt;
652+
653+
let tmpdir = tmpdir();
654+
let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt");
655+
{
656+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
657+
let mut file = check!(oo.open(&filename));
658+
check!(file.write_all(b"0123456789"));
659+
}
660+
{
661+
let mut file = check!(File::open(&filename));
662+
let mut buf: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()];
663+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
664+
665+
// Seek read
666+
check!(file.seek_read_buf(buf.unfilled(), 8));
667+
assert_eq!(buf.filled(), b"8");
668+
assert_eq!(check!(file.stream_position()), 9);
669+
670+
// Empty seek read
671+
check!(file.seek_read_buf(buf.unfilled(), 0));
672+
assert_eq!(buf.filled(), b"8");
673+
674+
// Seek read past eof
675+
check!(file.seek_read_buf(buf.clear().unfilled(), 10));
676+
assert_eq!(buf.filled(), b"");
677+
}
678+
check!(fs::remove_file(&filename));
679+
}
680+
569681
#[test]
570682
fn file_test_read_buf() {
571683
let tmpdir = tmpdir();

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

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

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

Lines changed: 43 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,44 @@ 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 that it is passed
56+
/// a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The
57+
/// new data will be appended to any existing contents of `buf`.
58+
///
59+
/// Reading beyond the end of the file will always succeed without reading any bytes.
60+
///
61+
/// # Examples
62+
///
63+
/// ```no_run
64+
/// #![feature(core_io_borrowed_buf)]
65+
/// #![feature(read_buf_at)]
66+
///
67+
/// use std::io;
68+
/// use std::io::BorrowedBuf;
69+
/// use std::fs::File;
70+
/// use std::mem::MaybeUninit;
71+
/// use std::os::windows::prelude::*;
72+
///
73+
/// fn main() -> io::Result<()> {
74+
/// let mut file = File::open("pi.txt")?;
75+
///
76+
/// // Read some bytes starting from offset 2
77+
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
78+
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
79+
/// file.seek_read_buf(buf.unfilled(), 2)?;
80+
///
81+
/// assert!(buf.filled().starts_with(b"1"));
82+
///
83+
/// Ok(())
84+
/// }
85+
/// ```
86+
#[unstable(feature = "read_buf_at", issue = "140771")]
87+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
88+
io::default_read_buf(|b| self.seek_read(b, offset), buf)
89+
}
90+
5291
/// Seeks to a given position and writes a number of bytes.
5392
///
5493
/// Returns the number of bytes written.
@@ -89,6 +128,10 @@ impl FileExt for fs::File {
89128
self.as_inner().read_at(buf, offset)
90129
}
91130

131+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
132+
self.as_inner().read_buf_at(buf, offset)
133+
}
134+
92135
fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
93136
self.as_inner().write_at(buf, offset)
94137
}

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

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,21 @@ use libc::off_t as off64_t;
1818
))]
1919
use libc::off64_t;
2020

21+
cfg_select! {
22+
any(
23+
all(target_os = "linux", not(target_env = "musl")),
24+
target_os = "android",
25+
target_os = "hurd",
26+
) => {
27+
// Prefer explicit pread64 for 64-bit offset independently of libc
28+
// #[cfg(gnu_file_offset_bits64)].
29+
use libc::pread64;
30+
}
31+
_ => {
32+
use libc::pread as pread64;
33+
}
34+
}
35+
2136
use crate::cmp;
2237
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
2338
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
@@ -146,42 +161,47 @@ impl FileDesc {
146161
(&mut me).read_to_end(buf)
147162
}
148163

149-
#[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
150164
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(
165+
cvt(unsafe {
166+
pread64(
166167
self.as_raw_fd(),
167168
buf.as_mut_ptr() as *mut libc::c_void,
168169
cmp::min(buf.len(), READ_LIMIT),
169-
offset as off64_t,
170-
))
171-
.map(|n| n asusize)
172-
}
170+
offset as off64_t,// EINVAL if offset + count overflows
171+
)
172+
})
173+
.map(|n| n asusize)
173174
}
174175

175176
pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
177+
// SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
176178
let ret = cvt(unsafe {
177179
libc::read(
178180
self.as_raw_fd(),
179-
cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
181+
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
182+
cmp::min(cursor.capacity(), READ_LIMIT),
183+
)
184+
})?;
185+
186+
// SAFETY: `ret` bytes were written to the initialized portion of the buffer
187+
unsafe {
188+
cursor.advance_unchecked(ret as usize);
189+
}
190+
Ok(())
191+
}
192+
193+
pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
194+
// SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes
195+
let ret = cvt(unsafe {
196+
pread64(
197+
self.as_raw_fd(),
198+
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
180199
cmp::min(cursor.capacity(), READ_LIMIT),
200+
offset as off64_t, // EINVAL if offset + count overflows
181201
)
182202
})?;
183203

184-
// Safety: `ret` bytes were written to the initialized portion of the buffer
204+
// SAFETY: `ret` bytes were written to the initialized portion of the buffer
185205
unsafe {
186206
cursor.advance_unchecked(ret as usize);
187207
}
@@ -369,7 +389,6 @@ impl FileDesc {
369389
)))
370390
}
371391

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

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /