Rollup merge of #125087 - tbu-:pr_file_stream_len, r=ChrisDenton
Optimize `Seek::stream_len` impl for `File` It uses the file metadata on Unix with a fallback for files incorrectly reported as zero-sized. It uses `GetFileSizeEx` on Windows. This reduces the number of syscalls needed for determining the file size of an open file from 3 to 1.
This commit is contained in:
commit
c141cbf263
11 changed files with 90 additions and 11 deletions
|
|
@ -1311,9 +1311,39 @@ impl Write for &File {
|
|||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl Seek for &File {
|
||||
/// Seek to an offset, in bytes in a file.
|
||||
///
|
||||
/// See [`Seek::seek`] docs for more info.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `lseek64` function on Unix
|
||||
/// and the `SetFilePointerEx` function on Windows. Note that this [may
|
||||
/// change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
self.inner.seek(pos)
|
||||
}
|
||||
|
||||
/// Returns the length of this file (in bytes).
|
||||
///
|
||||
/// See [`Seek::stream_len`] docs for more info.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `statx` function on Linux
|
||||
/// (with fallbacks) and the `GetFileSizeEx` function on Windows. Note that
|
||||
/// this [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
if let Some(result) = self.inner.size() {
|
||||
return result;
|
||||
}
|
||||
io::stream_len_default(self)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
self.inner.tell()
|
||||
}
|
||||
|
|
@ -1363,6 +1393,9 @@ impl Seek for File {
|
|||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
(&*self).seek(pos)
|
||||
}
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
(&*self).stream_len()
|
||||
}
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(&*self).stream_position()
|
||||
}
|
||||
|
|
@ -1412,6 +1445,9 @@ impl Seek for Arc<File> {
|
|||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
(&**self).seek(pos)
|
||||
}
|
||||
fn stream_len(&mut self) -> io::Result<u64> {
|
||||
(&**self).stream_len()
|
||||
}
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
(&**self).stream_position()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2028,7 +2028,7 @@ pub trait Seek {
|
|||
|
||||
/// Returns the length of this stream (in bytes).
|
||||
///
|
||||
/// This method is implemented using up to three seek operations. If this
|
||||
/// The default implementation uses up to three seek operations. If this
|
||||
/// method returns successfully, the seek position is unchanged (i.e. the
|
||||
/// position before calling this method is the same as afterwards).
|
||||
/// However, if this method returns an error, the seek position is
|
||||
|
|
@ -2062,16 +2062,7 @@ pub trait Seek {
|
|||
/// ```
|
||||
#[unstable(feature = "seek_stream_len", issue = "59359")]
|
||||
fn stream_len(&mut self) -> Result<u64> {
|
||||
let old_pos = self.stream_position()?;
|
||||
let len = self.seek(SeekFrom::End(0))?;
|
||||
|
||||
// Avoid seeking a third time when we were already at the end of the
|
||||
// stream. The branch is usually way cheaper than a seek operation.
|
||||
if old_pos != len {
|
||||
self.seek(SeekFrom::Start(old_pos))?;
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
stream_len_default(self)
|
||||
}
|
||||
|
||||
/// Returns the current seek position from the start of the stream.
|
||||
|
|
@ -2132,6 +2123,19 @@ pub trait Seek {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn stream_len_default<T: Seek + ?Sized>(self_: &mut T) -> Result<u64> {
|
||||
let old_pos = self_.stream_position()?;
|
||||
let len = self_.seek(SeekFrom::End(0))?;
|
||||
|
||||
// Avoid seeking a third time when we were already at the end of the
|
||||
// stream. The branch is usually way cheaper than a seek operation.
|
||||
if old_pos != len {
|
||||
self_.seek(SeekFrom::Start(old_pos))?;
|
||||
}
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
/// Enumeration of possible methods to seek within an I/O object.
|
||||
///
|
||||
/// It is used by the [`Seek`] trait.
|
||||
|
|
|
|||
|
|
@ -422,6 +422,10 @@ impl File {
|
|||
self.0.seek(pos)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
self.0.tell()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -459,6 +459,10 @@ impl File {
|
|||
self.tell()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
unsafe {
|
||||
let mut out_offset = MaybeUninit::uninit();
|
||||
|
|
|
|||
|
|
@ -280,6 +280,10 @@ impl File {
|
|||
self.0
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
self.0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1464,6 +1464,15 @@ impl File {
|
|||
Ok(n as u64)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
match self.file_attr().map(|attr| attr.size()) {
|
||||
// Fall back to default implementation if the returned size is 0,
|
||||
// we might be in a proc mount.
|
||||
Ok(0) => None,
|
||||
result => Some(result),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
self.seek(SeekFrom::Current(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,6 +259,10 @@ impl File {
|
|||
self.0
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
self.0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -516,6 +516,10 @@ impl File {
|
|||
self.fd.seek(pos)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
self.fd.tell()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -616,6 +616,14 @@ impl File {
|
|||
Ok(newpos as u64)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Option<io::Result<u64>> {
|
||||
let mut result = 0;
|
||||
Some(
|
||||
cvt(unsafe { c::GetFileSizeEx(self.handle.as_raw_handle(), &mut result) })
|
||||
.map(|_| result as u64),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn tell(&self) -> io::Result<u64> {
|
||||
self.seek(SeekFrom::Current(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2156,6 +2156,7 @@ GetExitCodeProcess
|
|||
GetFileAttributesW
|
||||
GetFileInformationByHandle
|
||||
GetFileInformationByHandleEx
|
||||
GetFileSizeEx
|
||||
GetFileType
|
||||
GETFINALPATHNAMEBYHANDLE_FLAGS
|
||||
GetFinalPathNameByHandleW
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetExitCodeProcess(hprocess :
|
|||
windows_targets::link!("kernel32.dll" "system" fn GetFileAttributesW(lpfilename : PCWSTR) -> u32);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandle(hfile : HANDLE, lpfileinformation : *mut BY_HANDLE_FILE_INFORMATION) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetFileInformationByHandleEx(hfile : HANDLE, fileinformationclass : FILE_INFO_BY_HANDLE_CLASS, lpfileinformation : *mut core::ffi::c_void, dwbuffersize : u32) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetFileSizeEx(hfile : HANDLE, lpfilesize : *mut i64) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetFileType(hfile : HANDLE) -> FILE_TYPE);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetFinalPathNameByHandleW(hfile : HANDLE, lpszfilepath : PWSTR, cchfilepath : u32, dwflags : GETFINALPATHNAMEBYHANDLE_FLAGS) -> u32);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetFullPathNameW(lpfilename : PCWSTR, nbufferlength : u32, lpbuffer : PWSTR, lpfilepart : *mut PWSTR) -> u32);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue