use crate::io::IoExt;
use io_lifetimes::AsFilelike;
#[cfg(not(any(
windows,
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
)))]
use rustix::fs::fadvise;
#[cfg(any(target_os = "ios", target_os = "macos"))]
use rustix::fs::fcntl_rdadvise;
#[cfg(not(any(
windows,
target_os = "netbsd",
target_os = "redox",
target_os = "openbsd"
)))]
use rustix::fs::{fallocate, FallocateFlags};
#[cfg(not(any(windows, target_os = "ios", target_os = "macos", target_os = "redox")))]
use rustix::io::{preadv, pwritev};
use std::io::{self, IoSlice, IoSliceMut, Seek, SeekFrom};
use std::slice;
#[cfg(windows)]
use {cap_fs_ext::Reopen, fd_lock::RwLock, std::fs, std::os::windows::fs::FileExt};
#[cfg(not(windows))]
use {rustix::fs::tell, rustix::fs::FileExt};
#[cfg(not(any(
windows,
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox"
)))]
#[derive(Debug, Eq, PartialEq, Hash)]
#[repr(i32)]
pub enum Advice {
Normal = rustix::fs::Advice::Normal as i32,
Sequential = rustix::fs::Advice::Sequential as i32,
Random = rustix::fs::Advice::Random as i32,
WillNeed = rustix::fs::Advice::WillNeed as i32,
DontNeed = rustix::fs::Advice::DontNeed as i32,
NoReuse = rustix::fs::Advice::NoReuse as i32,
}
#[cfg(any(
windows,
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox"
))]
#[derive(Debug, Eq, PartialEq, Hash)]
pub enum Advice {
Normal,
Sequential,
Random,
WillNeed,
DontNeed,
NoReuse,
}
pub trait FileIoExt: IoExt {
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()>;
fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()>;
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
let buf = bufs
.iter_mut()
.find(|b| !b.is_empty())
.map_or(&mut [][..], |b| &mut **b);
self.read_at(buf, offset)
}
fn read_exact_vectored_at(
&self,
mut bufs: &mut [IoSliceMut],
mut offset: u64,
) -> io::Result<()> {
bufs = skip_leading_empties(bufs);
while !bufs.is_empty() {
match self.read_vectored_at(bufs, offset) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
Ok(nread) => {
offset = offset
.checked_add(nread.try_into().unwrap())
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
bufs = advance_mut(bufs, nread);
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
Err(e) => return Err(e),
}
bufs = skip_leading_empties(bufs);
}
Ok(())
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
false
}
fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize>;
fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize>;
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.write_at(buf, offset) {
Ok(nwritten) => {
buf = &buf[nwritten..];
offset += nwritten as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
Err(e) => return Err(e),
}
}
Ok(())
}
fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
let buf = bufs
.iter()
.find(|b| !b.is_empty())
.map_or(&[][..], |b| &**b);
self.write_at(buf, offset)
}
fn write_all_vectored_at(&self, mut bufs: &mut [IoSlice], mut offset: u64) -> io::Result<()> {
while !bufs.is_empty() {
match self.write_vectored_at(bufs, offset) {
Ok(nwritten) => {
offset = offset
.checked_add(nwritten.try_into().unwrap())
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
bufs = advance(bufs, nwritten);
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
Err(e) => return Err(e),
}
}
Ok(())
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
false
}
fn append(&self, buf: &[u8]) -> io::Result<usize>;
fn append_all(&self, mut buf: &[u8]) -> io::Result<()> {
while !buf.is_empty() {
match self.append(buf) {
Ok(nwritten) => {
buf = &buf[nwritten..];
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
Err(e) => return Err(e),
}
}
Ok(())
}
fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
let buf = bufs
.iter()
.find(|b| !b.is_empty())
.map_or(&[][..], |b| &**b);
self.append(buf)
}
fn append_all_vectored(&self, mut bufs: &mut [IoSlice]) -> io::Result<()> {
while !bufs.is_empty() {
match self.append_vectored(bufs) {
Ok(nwritten) => {
bufs = advance(bufs, nwritten);
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
Err(e) => return Err(e),
}
}
Ok(())
}
#[inline]
fn is_append_vectored(&self) -> bool {
false
}
fn seek(&self, pos: SeekFrom) -> io::Result<u64>;
fn stream_position(&self) -> io::Result<u64>;
}
fn skip_leading_empties<'a, 'b>(mut bufs: &'b mut [IoSliceMut<'a>]) -> &'b mut [IoSliceMut<'a>] {
while !bufs.is_empty() {
if !bufs[0].is_empty() {
break;
}
bufs = &mut bufs[1..];
}
bufs
}
fn advance<'a, 'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] {
let mut remove = 0;
let mut accumulated_len = 0;
for buf in bufs.iter() {
if accumulated_len + buf.len() > n {
break;
} else {
accumulated_len += buf.len();
remove += 1;
}
}
#[allow(clippy::indexing_slicing)]
let bufs = &mut bufs[remove..];
if let Some(first) = bufs.first_mut() {
let advance_by = n - accumulated_len;
let mut ptr = first.as_ptr();
let mut len = first.len();
unsafe {
ptr = ptr.add(advance_by);
len -= advance_by;
*first = IoSlice::<'a>::new(slice::from_raw_parts::<'a>(ptr, len));
}
}
bufs
}
fn advance_mut<'a, 'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] {
let mut remove = 0;
let mut accumulated_len = 0;
for buf in bufs.iter() {
if accumulated_len + buf.len() > n {
break;
} else {
accumulated_len += buf.len();
remove += 1;
}
}
#[allow(clippy::indexing_slicing)]
let bufs = &mut bufs[remove..];
if let Some(first) = bufs.first_mut() {
let advance_by = n - accumulated_len;
let mut ptr = first.as_mut_ptr();
let mut len = first.len();
unsafe {
ptr = ptr.add(advance_by);
len -= advance_by;
*first = IoSliceMut::<'a>::new(slice::from_raw_parts_mut::<'a>(ptr, len));
}
}
bufs
}
#[cfg(not(windows))]
impl<T: AsFilelike + IoExt> FileIoExt for T {
#[cfg(not(any(
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox"
)))]
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
let advice = match advice {
Advice::WillNeed => rustix::fs::Advice::WillNeed,
Advice::Normal => rustix::fs::Advice::Normal,
Advice::Sequential => rustix::fs::Advice::Sequential,
Advice::NoReuse => rustix::fs::Advice::NoReuse,
Advice::Random => rustix::fs::Advice::Random,
Advice::DontNeed => rustix::fs::Advice::DontNeed,
};
Ok(fadvise(self, offset, len, advice)?)
}
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
match advice {
Advice::WillNeed => (),
Advice::Normal
| Advice::Sequential
| Advice::NoReuse
| Advice::Random
| Advice::DontNeed => return Ok(()),
}
Ok(fcntl_rdadvise(self, offset, len)?)
}
#[cfg(any(target_os = "netbsd", target_os = "redox", target_os = "openbsd"))]
#[inline]
fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> io::Result<()> {
Ok(())
}
#[cfg(not(any(target_os = "netbsd", target_os = "redox", target_os = "openbsd")))]
#[inline]
fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
Ok(fallocate(self, FallocateFlags::empty(), offset, len)?)
}
#[cfg(target_os = "netbsd")]
fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
todo!("NetBSD 7.0 supports posix_fallocate; add bindings for it")
}
#[cfg(target_os = "openbsd")]
fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
todo!("OpenBSD does not support posix_fallocate; figure out what to do")
}
#[cfg(target_os = "redox")]
fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
todo!("figure out what to do on redox for posix_fallocate")
}
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
FileExt::read_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
FileExt::read_exact_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
Ok(preadv(self, bufs, offset)?)
}
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
#[inline]
fn is_read_vectored_at(&self) -> bool {
true
}
#[inline]
fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
read_to_end_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[inline]
fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
read_to_string_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[inline]
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
FileExt::write_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[inline]
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
FileExt::write_all_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
#[inline]
fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
Ok(pwritev(self, bufs, offset)?)
}
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
#[inline]
fn is_write_vectored_at(&self) -> bool {
true
}
fn append(&self, buf: &[u8]) -> io::Result<usize> {
use rustix::fs::{fcntl_getfl, fcntl_setfl, seek, OFlags, SeekFrom};
use rustix::io::write;
#[cfg(any(target_os = "android", target_os = "linux"))]
{
use rustix::io::{pwritev2, Errno, ReadWriteFlags};
let iovs = [IoSlice::new(buf)];
match pwritev2(self, &iovs, 0, ReadWriteFlags::APPEND) {
Err(Errno::NOSYS) | Err(Errno::NOTSUP) => {}
otherwise => return Ok(otherwise?),
}
}
let old_flags = fcntl_getfl(self)?;
let old_pos = tell(self)?;
fcntl_setfl(self, old_flags | OFlags::APPEND)?;
let result = write(self, buf);
fcntl_setfl(self, old_flags).unwrap();
seek(self, SeekFrom::Start(old_pos)).unwrap();
Ok(result?)
}
fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
use rustix::fs::{fcntl_getfl, fcntl_setfl, seek, OFlags, SeekFrom};
use rustix::io::writev;
#[cfg(any(target_os = "android", target_os = "linux"))]
{
use rustix::io::{pwritev2, Errno, ReadWriteFlags};
match pwritev2(self, bufs, 0, ReadWriteFlags::APPEND) {
Err(Errno::NOSYS) | Err(Errno::NOTSUP) => {}
otherwise => return Ok(otherwise?),
}
}
let old_flags = fcntl_getfl(self)?;
let old_pos = tell(self)?;
fcntl_setfl(self, old_flags | OFlags::APPEND)?;
let result = writev(self, bufs);
fcntl_setfl(self, old_flags).unwrap();
seek(self, SeekFrom::Start(old_pos)).unwrap();
Ok(result?)
}
#[inline]
fn is_append_vectored(&self) -> bool {
true
}
#[inline]
fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
Seek::seek(&mut &*self.as_filelike_view::<std::fs::File>(), pos)
}
#[inline]
fn stream_position(&self) -> io::Result<u64> {
Ok(tell(self)?)
}
}
#[cfg(windows)]
impl FileIoExt for std::fs::File {
#[inline]
fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> io::Result<()> {
Ok(())
}
#[inline]
fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"file allocate is not supported on Windows",
))
}
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let reopened = reopen(self)?;
reopened.seek_read(buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
let reopened = loop {
match reopen(self) {
Ok(file) => break file,
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
};
loop {
match reopened.seek(SeekFrom::Start(offset)) {
Ok(_) => break,
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
}
reopened.read_exact(buf)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
let reopened = reopen(self)?;
reopened.seek(SeekFrom::Start(offset))?;
reopened.read_vectored(bufs)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
let reopened = loop {
match reopen(self) {
Ok(file) => break file,
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
};
loop {
match reopened.seek(SeekFrom::Start(offset)) {
Ok(_) => break,
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
}
reopened.read_exact_vectored(bufs)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
true
}
#[inline]
fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
read_to_end_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[inline]
fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
read_to_string_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
}
#[inline]
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let reopened = reopen_write(self)?;
let mut reopened = RwLock::new(reopened);
let reopened = reopened.write()?;
let mut seek_offset = offset;
let mut write_buf = buf;
let mut prepend_zeros;
let reopened_size = reopened.metadata()?.len();
let num_zeros = offset.saturating_sub(reopened_size);
let num_zeros: usize = num_zeros
.try_into()
.map_err(|_| io::Error::new(io::ErrorKind::OutOfMemory, "write_all_vectored_at"))?;
if num_zeros > 0 {
prepend_zeros = vec![0_u8; num_zeros];
prepend_zeros.extend_from_slice(buf);
seek_offset = reopened_size;
write_buf = &prepend_zeros;
}
let num_written = reopened
.seek_write(write_buf, seek_offset)?
.saturating_sub(num_zeros);
Ok(num_written)
}
#[inline]
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
let reopened = loop {
match reopen_write(self) {
Ok(file) => break file,
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
};
let mut reopened = RwLock::new(reopened);
let reopened = reopened.write()?;
let mut seek_offset = offset;
let mut write_buf = buf;
let mut prepend_zeros;
let reopened_size = reopened.metadata()?.len();
let num_zeros = offset.saturating_sub(reopened_size);
let num_zeros: usize = num_zeros
.try_into()
.map_err(|_| io::Error::new(io::ErrorKind::OutOfMemory, "write_all_vectored_at"))?;
if num_zeros > 0 {
prepend_zeros = vec![0_u8; num_zeros];
prepend_zeros.extend_from_slice(buf);
seek_offset = reopened_size;
write_buf = &prepend_zeros;
}
loop {
match reopened.seek(SeekFrom::Start(seek_offset)) {
Ok(_) => break,
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
}
reopened.write_all(write_buf)?;
Ok(())
}
#[inline]
fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
let reopened = reopen_write(self)?;
let mut reopened = RwLock::new(reopened);
let reopened = reopened.write()?;
let mut seek_offset = offset;
let mut write_bufs = bufs;
let zeros;
let mut prepend_zeros;
let reopened_size = reopened.metadata()?.len();
let num_zeros = offset.saturating_sub(reopened_size);
let num_zeros: usize = num_zeros
.try_into()
.map_err(|_| io::Error::new(io::ErrorKind::OutOfMemory, "write_vectored_at"))?;
if num_zeros > 0 {
zeros = vec![0_u8; num_zeros];
prepend_zeros = vec![IoSlice::new(&zeros)];
prepend_zeros.extend_from_slice(bufs);
seek_offset = reopened_size;
write_bufs = &prepend_zeros;
}
reopened.seek(SeekFrom::Start(seek_offset))?;
let num_written = reopened
.write_vectored(write_bufs)?
.saturating_sub(num_zeros);
Ok(num_written)
}
#[inline]
fn write_all_vectored_at(&self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
let reopened = loop {
match reopen_write(self) {
Ok(file) => break file,
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
};
let mut reopened = RwLock::new(reopened);
let reopened = reopened.write()?;
let reopened_size = reopened.metadata()?.len();
let num_zeros = offset.saturating_sub(reopened_size);
let num_zeros: usize = num_zeros
.try_into()
.map_err(|_| io::Error::new(io::ErrorKind::OutOfMemory, "write_vectored_at"))?;
if num_zeros > 0 {
let zeros = vec![0_u8; num_zeros];
let mut prepend_zeros = vec![IoSlice::new(&zeros)];
prepend_zeros.extend_from_slice(bufs);
reopened.seek(SeekFrom::Start(reopened_size))?;
reopened.write_all_vectored(&mut prepend_zeros)?;
} else {
reopened.seek(SeekFrom::Start(offset))?;
reopened.write_all_vectored(bufs)?;
}
Ok(())
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
true
}
fn append(&self, buf: &[u8]) -> io::Result<usize> {
let reopened = reopen_append(self)?;
reopened.write(buf)
}
fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
let reopened = reopen_append(self)?;
reopened.write_vectored(bufs)
}
#[inline]
fn is_append_vectored(&self) -> bool {
true
}
#[inline]
fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
Seek::seek(&mut &*self.as_filelike_view::<std::fs::File>(), pos)
}
#[inline]
fn stream_position(&self) -> io::Result<u64> {
Seek::seek(
&mut &*self.as_filelike_view::<std::fs::File>(),
SeekFrom::Current(0),
)
}
}
#[cfg(windows)]
#[cfg(feature = "cap_std_impls")]
impl FileIoExt for cap_std::fs::File {
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.advise(offset, len, advice)
}
#[inline]
fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.allocate(offset, len)
}
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_at(buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.read_exact_at(buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_vectored_at(bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.read_exact_vectored_at(bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
self.as_filelike_view::<std::fs::File>()
.is_read_vectored_at()
}
#[inline]
fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_to_end_at(buf, offset)
}
#[inline]
fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_to_string_at(buf, offset)
}
#[inline]
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.write_at(buf, offset)
}
#[inline]
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.write_all_at(buf, offset)
}
#[inline]
fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.write_vectored_at(bufs, offset)
}
#[inline]
fn write_all_vectored_at(&self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.write_all_vectored_at(bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
self.as_filelike_view::<std::fs::File>()
.is_write_vectored_at()
}
fn append(&self, buf: &[u8]) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>().append(buf)
}
fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.append_vectored(bufs)
}
#[inline]
fn is_append_vectored(&self) -> bool {
true
}
#[inline]
fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
self.as_filelike_view::<std::fs::File>().seek(pos)
}
#[inline]
fn stream_position(&self) -> io::Result<u64> {
self.as_filelike_view::<std::fs::File>().stream_position()
}
}
#[cfg(windows)]
#[cfg(feature = "cap_std_impls_fs_utf8")]
impl FileIoExt for cap_std::fs_utf8::File {
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.advise(offset, len, advice)
}
#[inline]
fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.allocate(offset, len)
}
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_at(buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.read_exact_at(buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_vectored_at(bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.read_exact_vectored_at(bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
self.as_filelike_view::<std::fs::File>()
.is_read_vectored_at()
}
#[inline]
fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_to_end_at(buf, offset)
}
#[inline]
fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.read_to_string_at(buf, offset)
}
#[inline]
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.write_at(buf, offset)
}
#[inline]
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.write_all_at(buf, offset)
}
#[inline]
fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.write_vectored_at(bufs, offset)
}
#[inline]
fn write_all_vectored_at(&self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
self.as_filelike_view::<std::fs::File>()
.write_all_vectored_at(bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
self.as_filelike_view::<std::fs::File>()
.is_write_vectored_at()
}
fn append(&self, buf: &[u8]) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>().append(buf)
}
fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
self.as_filelike_view::<std::fs::File>()
.append_vectored(bufs)
}
#[inline]
fn is_append_vectored(&self) -> bool {
true
}
#[inline]
fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
self.as_filelike_view::<std::fs::File>().seek(pos)
}
#[inline]
fn stream_position(&self) -> io::Result<u64> {
self.as_filelike_view::<std::fs::File>().stream_position()
}
}
#[cfg(windows)]
#[inline]
fn reopen<Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<fs::File> {
let file = filelike.as_filelike_view::<std::fs::File>();
unsafe { _reopen(&file) }
}
#[cfg(windows)]
unsafe fn _reopen(file: &fs::File) -> io::Result<fs::File> {
file.reopen(cap_fs_ext::OpenOptions::new().read(true))
}
#[cfg(windows)]
#[inline]
fn reopen_write<Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<fs::File> {
let file = filelike.as_filelike_view::<std::fs::File>();
unsafe { _reopen_write(&file) }
}
#[cfg(windows)]
unsafe fn _reopen_write(file: &fs::File) -> io::Result<fs::File> {
file.reopen(cap_fs_ext::OpenOptions::new().write(true))
}
#[cfg(windows)]
#[inline]
fn reopen_append<Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<fs::File> {
let file = filelike.as_filelike_view::<std::fs::File>();
unsafe { _reopen_append(&file) }
}
#[cfg(windows)]
unsafe fn _reopen_append(file: &fs::File) -> io::Result<fs::File> {
file.reopen(cap_fs_ext::OpenOptions::new().append(true))
}
fn read_to_end_at(file: &std::fs::File, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
let len = match file.metadata()?.len().checked_sub(offset) {
None => return Ok(0),
Some(len) => len,
};
buf.resize(
(buf.len() as u64)
.saturating_add(len)
.try_into()
.unwrap_or(usize::MAX),
0_u8,
);
FileIoExt::read_exact_at(file, buf, offset)?;
Ok(len as usize)
}
fn read_to_string_at(file: &std::fs::File, buf: &mut String, offset: u64) -> io::Result<usize> {
let len = match file.metadata()?.len().checked_sub(offset) {
None => return Ok(0),
Some(len) => len,
};
let mut tmp = vec![0_u8; len.try_into().unwrap_or(usize::MAX)];
FileIoExt::read_exact_at(file, &mut tmp, offset)?;
let s = String::from_utf8(tmp).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"stream did not contain valid UTF-8",
)
})?;
buf.push_str(&s);
Ok(len as usize)
}
fn _file_io_ext_can_be_trait_object(_: &dyn FileIoExt) {}