-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Add read_buf
equivalents for positioned reads
#140459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ use super::platform::fs::MetadataExt as _; | |
// Used for `File::read` on intra-doc links | ||
use crate::ffi::OsStr; | ||
use crate::fs::{self, OpenOptions, Permissions}; | ||
use crate::io::BorrowedCursor; | ||
use crate::os::unix::io::{AsFd, AsRawFd}; | ||
use crate::path::Path; | ||
use crate::sealed::Sealed; | ||
|
@@ -130,6 +131,94 @@ pub trait FileExt { | |
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) } | ||
} | ||
|
||
/// Reads some bytes starting from a given offset into the buffer. | ||
/// | ||
/// This equivalent to the [`read_at`](FileExt::read_at) method, | ||
/// except that it is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow | ||
/// use with uninitialized buffers. The new data will be appended to any | ||
/// existing contents of `buf`. | ||
Comment on lines
+136
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Small style nit, docs can wrap at 100 chars |
||
/// | ||
/// # Examples | ||
/// | ||
/// ```no_run | ||
/// #![feature(core_io_borrowed_buf)] | ||
/// #![feature(read_buf_at)] | ||
/// | ||
/// use std::io; | ||
/// use std::io::BorrowedBuf; | ||
/// use std::fs::File; | ||
/// use std::mem::MaybeUninit; | ||
/// use std::os::unix::prelude::*; | ||
/// | ||
/// fn main() -> io::Result<()> { | ||
/// let mut file = File::open("pi.txt")?; | ||
/// | ||
/// // Read some bytes starting from offset 2 | ||
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10]; | ||
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice()); | ||
/// file.read_buf_at(buf.unfilled(), 2)?; | ||
/// | ||
/// assert!(buf.filled().starts_with(b"1")); | ||
/// | ||
/// Ok(()) | ||
/// } | ||
/// ``` | ||
#[unstable(feature = "read_buf_at", issue = "140771")] | ||
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { | ||
io::default_read_buf(|b| self.read_at(b, offset), buf) | ||
} | ||
|
||
/// Reads the exact number of bytes required to fill the buffer from a given | ||
/// offset. | ||
/// | ||
/// This is equivalent to the [`read_exact_at`](FileExt::read_exact_at) method, | ||
/// except that it is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow | ||
/// use with uninitialized buffers. The new data will be appended to any | ||
/// existing contents of `buf`. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ```no_run | ||
/// #![feature(core_io_borrowed_buf)] | ||
/// #![feature(read_buf_at)] | ||
/// | ||
/// use std::io; | ||
/// use std::io::BorrowedBuf; | ||
/// use std::fs::File; | ||
/// use std::mem::MaybeUninit; | ||
/// use std::os::unix::prelude::*; | ||
/// | ||
/// fn main() -> io::Result<()> { | ||
/// let mut file = File::open("pi.txt")?; | ||
/// | ||
/// // Read exactly 10 bytes starting from offset 2 | ||
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10]; | ||
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice()); | ||
/// file.read_buf_exact_at(buf.unfilled(), 2)?; | ||
/// | ||
/// assert_eq!(buf.filled(), b"1415926535"); | ||
/// | ||
/// Ok(()) | ||
/// } | ||
/// ``` | ||
#[unstable(feature = "read_buf_at", issue = "140771")] | ||
fn read_buf_exact_at(&self, mut buf: BorrowedCursor<'_>, mut offset: u64) -> io::Result<()> { | ||
while buf.capacity() > 0 { | ||
let prev_written = buf.written(); | ||
match self.read_buf_at(buf.reborrow(), offset) { | ||
Ok(()) => {} | ||
Err(e) if e.is_interrupted() => {} | ||
Err(e) => return Err(e), | ||
} | ||
let n = buf.written() - prev_written; | ||
offset += n as u64; | ||
if n == 0 { | ||
return Err(io::Error::READ_EXACT_EOF); | ||
} | ||
} | ||
Ok(()) | ||
} | ||
|
||
/// Writes a number of bytes starting from a given offset. | ||
/// | ||
/// Returns the number of bytes written. | ||
|
@@ -264,6 +353,9 @@ impl FileExt for fs::File { | |
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { | ||
self.as_inner().read_at(buf, offset) | ||
} | ||
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { | ||
self.as_inner().read_buf_at(buf, offset) | ||
} | ||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> { | ||
self.as_inner().read_vectored_at(bufs, offset) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -87,6 +87,19 @@ const fn max_iov() -> usize { | |
16 // The minimum value required by POSIX. | ||
} | ||
|
||
crate::cfg_select! { | ||
any( | ||
all(target_os = "linux", not(target_env = "musl")), | ||
target_os = "android", | ||
target_os = "hurd", | ||
) => { | ||
use libc::pread64; | ||
} | ||
_ => { | ||
use libc::pread as pread64; | ||
} | ||
} | ||
Comment on lines
+90
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should move to the top with the other imports (mentioned this in another comment), but actually do we need this remapping? From what I can gather at https://pubs.opengroup.org/onlinepubs/7908799/xsh/read.html and https://linux.die.net/man/2/pread64, |
||
|
||
impl FileDesc { | ||
#[inline] | ||
pub fn try_clone(&self) -> io::Result<Self> { | ||
|
@@ -146,42 +159,47 @@ impl FileDesc { | |
(&mut me).read_to_end(buf) | ||
} | ||
|
||
#[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] | ||
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { | ||
#[cfg(not(any( | ||
all(target_os = "linux", not(target_env = "musl")), | ||
target_os = "android", | ||
target_os = "hurd" | ||
)))] | ||
use libc::pread as pread64; | ||
#[cfg(any( | ||
all(target_os = "linux", not(target_env = "musl")), | ||
target_os = "android", | ||
target_os = "hurd" | ||
))] | ||
use libc::pread64; | ||
|
||
unsafe { | ||
cvt(pread64( | ||
cvt(unsafe { | ||
pread64( | ||
self.as_raw_fd(), | ||
buf.as_mut_ptr() as *mut libc::c_void, | ||
cmp::min(buf.len(), READ_LIMIT), | ||
offset as off64_t, | ||
)) | ||
.map(|n| n as usize) | ||
} | ||
) | ||
}) | ||
.map(|n| n as usize) | ||
} | ||
|
||
pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> { | ||
// SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes | ||
let ret = cvt(unsafe { | ||
libc::read( | ||
self.as_raw_fd(), | ||
cursor.as_mut().as_mut_ptr() as *mut libc::c_void, | ||
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(), | ||
cmp::min(cursor.capacity(), READ_LIMIT), | ||
) | ||
})?; | ||
|
||
// Safety: `ret` bytes were written to the initialized portion of the buffer | ||
// SAFETY: `ret` bytes were written to the initialized portion of the buffer | ||
unsafe { | ||
cursor.advance_unchecked(ret as usize); | ||
} | ||
Ok(()) | ||
} | ||
|
||
pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { | ||
// SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes | ||
let ret = cvt(unsafe { | ||
pread64( | ||
self.as_raw_fd(), | ||
cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(), | ||
cmp::min(cursor.capacity(), READ_LIMIT), | ||
offset as off64_t, | ||
) | ||
})?; | ||
|
||
// SAFETY: `ret` bytes were written to the initialized portion of the buffer | ||
unsafe { | ||
cursor.advance_unchecked(ret as usize); | ||
} | ||
|
@@ -369,7 +387,6 @@ impl FileDesc { | |
))) | ||
} | ||
|
||
#[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] | ||
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { | ||
#[cfg(not(any( | ||
all(target_os = "linux", not(target_env = "musl")), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test should get a couple of calls for
read_buf_at
(not exact) that aren't just eof. Or maybe splittest_read_buf_at
andread_buf_exact_at
?