use crate::{filelike, Advice};
#[cfg(feature = "io-streams")]
use io_streams::StreamReader;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawHandle, RawHandle};
use std::{
fs,
io::{self, IoSlice, IoSliceMut, Read, Seek, Write},
};
use system_interface::fs::FileIoExt;
use unsafe_io::{AsUnsafeFile, FromUnsafeFile, IntoUnsafeFile, UnsafeFile};
pub struct Metadata {
pub(crate) len: u64,
pub(crate) blksize: u64,
}
#[allow(clippy::len_without_is_empty)]
impl Metadata {
#[inline]
#[must_use]
pub const fn len(&self) -> u64 {
self.len
}
#[inline]
#[must_use]
pub const fn blksize(&self) -> u64 {
self.blksize
}
}
pub trait Range {
fn metadata(&self) -> io::Result<Metadata>;
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()>;
}
pub trait ReadAt: Range {
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>;
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()>;
fn is_read_vectored_at(&self) -> bool;
#[cfg(feature = "io-streams")]
fn read_via_stream_at(&self, offset: u64) -> io::Result<StreamReader>;
}
pub trait WriteAt: Range {
fn write_at(&mut self, buf: &[u8], offset: u64) -> io::Result<usize>;
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> io::Result<()>;
fn write_vectored_at(&mut self, bufs: &[IoSlice], offset: u64) -> io::Result<usize>;
fn write_all_vectored_at(&mut self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()>;
fn is_write_vectored_at(&self) -> bool;
fn copy_from<R: ReadAt>(
&mut self,
offset: u64,
input: &R,
input_offset: u64,
len: u64,
) -> io::Result<u64>;
fn set_len(&mut self, size: u64) -> io::Result<()>;
}
pub trait EditAt: ReadAt + WriteAt {}
#[derive(Debug)]
pub struct RangeReader {
file: fs::File,
}
#[derive(Debug)]
pub struct RangeWriter {
file: fs::File,
}
#[derive(Debug)]
pub struct RangeEditor {
file: fs::File,
}
impl RangeReader {
#[inline]
#[must_use]
pub fn file<Filelike: IntoUnsafeFile + Read + Seek>(filelike: Filelike) -> Self {
Self {
file: fs::File::from_filelike(filelike),
}
}
#[inline]
pub fn bytes(bytes: &[u8]) -> io::Result<Self> {
let unsafe_file = create_anonymous()?;
unsafe_file.as_file_view().write_all(bytes)?;
Ok(Self {
file: unsafe { fs::File::from_unsafe_file(unsafe_file) },
})
}
}
impl RangeWriter {
#[inline]
#[must_use]
pub fn file<Filelike: IntoUnsafeFile + Write + Seek>(filelike: Filelike) -> Self {
Self::_file(fs::File::from_filelike(filelike))
}
#[inline]
fn _file(file: fs::File) -> Self {
#[cfg(not(windows))]
{
assert!(
!posish::fs::getfl(&file)
.unwrap()
.contains(posish::fs::OFlags::APPEND),
"RangeWriter doesn't support files opened with O_APPEND"
);
}
#[cfg(windows)]
{
assert!(
(winx::file::query_access_information(file.as_raw_handle()).unwrap()
& winx::file::AccessMode::FILE_APPEND_DATA)
== winx::file::AccessMode::FILE_APPEND_DATA,
"RangeWriter doesn't support files opened with FILE_APPEND_DATA"
);
}
Self { file }
}
}
impl RangeEditor {
#[inline]
#[must_use]
pub fn file<Filelike: IntoUnsafeFile + Read + Write + Seek>(filelike: Filelike) -> Self {
Self {
file: fs::File::from_filelike(filelike),
}
}
#[inline]
pub fn anonymous() -> io::Result<Self> {
let unsafe_file = create_anonymous()?;
Ok(Self {
file: unsafe { fs::File::from_unsafe_file(unsafe_file) },
})
}
}
impl Range for RangeReader {
#[inline]
fn metadata(&self) -> io::Result<Metadata> {
filelike::metadata(self)
}
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
filelike::advise(self, offset, len, advice)
}
}
impl Range for RangeWriter {
#[inline]
fn metadata(&self) -> io::Result<Metadata> {
filelike::metadata(self)
}
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
filelike::advise(self, offset, len, advice)
}
}
impl Range for RangeEditor {
#[inline]
fn metadata(&self) -> io::Result<Metadata> {
filelike::metadata(self)
}
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
filelike::advise(self, offset, len, advice)
}
}
impl ReadAt for RangeReader {
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
filelike::read_at(self, buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
filelike::read_exact_at(self, buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
filelike::read_vectored_at(self, bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
filelike::read_exact_vectored_at(self, bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
filelike::is_read_vectored_at(self)
}
#[cfg(feature = "io-streams")]
#[inline]
fn read_via_stream_at(&self, offset: u64) -> io::Result<StreamReader> {
filelike::read_via_stream_at(self, offset)
}
}
impl ReadAt for RangeEditor {
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
filelike::read_at(self, buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
filelike::read_exact_at(self, buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
filelike::read_vectored_at(self, bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
filelike::read_exact_vectored_at(self, bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
filelike::is_read_vectored_at(self)
}
#[cfg(feature = "io-streams")]
#[inline]
fn read_via_stream_at(&self, offset: u64) -> io::Result<StreamReader> {
filelike::read_via_stream_at(self, offset)
}
}
impl WriteAt for RangeWriter {
#[inline]
fn write_at(&mut self, buf: &[u8], offset: u64) -> io::Result<usize> {
filelike::write_at(self, buf, offset)
}
#[inline]
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> io::Result<()> {
filelike::write_all_at(self, buf, offset)
}
#[inline]
fn write_vectored_at(&mut self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
filelike::write_vectored_at(self, bufs, offset)
}
#[inline]
fn write_all_vectored_at(&mut self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
filelike::write_all_vectored_at(self, bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
filelike::is_write_vectored_at(self)
}
#[inline]
fn copy_from<R: ReadAt>(
&mut self,
offset: u64,
input: &R,
input_offset: u64,
len: u64,
) -> io::Result<u64> {
filelike::copy_from(self, offset, input, input_offset, len)
}
#[inline]
fn set_len(&mut self, size: u64) -> io::Result<()> {
filelike::set_len(self, size)
}
}
impl WriteAt for RangeEditor {
#[inline]
fn write_at(&mut self, buf: &[u8], offset: u64) -> io::Result<usize> {
filelike::write_at(self, buf, offset)
}
#[inline]
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> io::Result<()> {
filelike::write_all_at(self, buf, offset)
}
#[inline]
fn write_vectored_at(&mut self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
filelike::write_vectored_at(self, bufs, offset)
}
#[inline]
fn write_all_vectored_at(&mut self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
filelike::write_all_vectored_at(self, bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
filelike::is_write_vectored_at(self)
}
#[inline]
fn copy_from<R: ReadAt>(
&mut self,
offset: u64,
input: &R,
input_offset: u64,
len: u64,
) -> io::Result<u64> {
filelike::copy_from(self, offset, input, input_offset, len)
}
#[inline]
fn set_len(&mut self, size: u64) -> io::Result<()> {
filelike::set_len(self, size)
}
}
impl Range for fs::File {
#[inline]
fn metadata(&self) -> io::Result<Metadata> {
filelike::metadata(self)
}
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
filelike::advise(self, offset, len, advice)
}
}
impl ReadAt for fs::File {
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
filelike::read_at(self, buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
filelike::read_exact_at(self, buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
filelike::read_vectored_at(self, bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
filelike::read_exact_vectored_at(self, bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
filelike::is_read_vectored_at(self)
}
#[cfg(feature = "io-streams")]
#[inline]
fn read_via_stream_at(&self, offset: u64) -> io::Result<StreamReader> {
filelike::read_via_stream_at(self, offset)
}
}
impl WriteAt for fs::File {
#[inline]
fn write_at(&mut self, buf: &[u8], offset: u64) -> io::Result<usize> {
filelike::write_at(self, buf, offset)
}
#[inline]
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> io::Result<()> {
filelike::write_all_at(self, buf, offset)
}
#[inline]
fn write_vectored_at(&mut self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
filelike::write_vectored_at(self, bufs, offset)
}
#[inline]
fn write_all_vectored_at(&mut self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
filelike::write_all_vectored_at(self, bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
filelike::is_write_vectored_at(self)
}
#[inline]
fn copy_from<R: ReadAt>(
&mut self,
offset: u64,
input: &R,
input_offset: u64,
len: u64,
) -> io::Result<u64> {
filelike::copy_from(self, offset, input, input_offset, len)
}
#[inline]
fn set_len(&mut self, size: u64) -> io::Result<()> {
filelike::set_len(self, size)
}
}
#[cfg(feature = "cap-std")]
impl Range for cap_std::fs::File {
#[inline]
fn metadata(&self) -> io::Result<Metadata> {
filelike::metadata(self)
}
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
filelike::advise(self, offset, len, advice)
}
}
#[cfg(feature = "cap-std")]
impl ReadAt for cap_std::fs::File {
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
filelike::read_at(self, buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
filelike::read_exact_at(self, buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
filelike::read_vectored_at(self, bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
filelike::read_exact_vectored_at(self, bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
filelike::is_read_vectored_at(self)
}
#[cfg(feature = "io-streams")]
#[inline]
fn read_via_stream_at(&self, offset: u64) -> io::Result<StreamReader> {
filelike::read_via_stream_at(self, offset)
}
}
#[cfg(feature = "cap-std")]
impl WriteAt for cap_std::fs::File {
#[inline]
fn write_at(&mut self, buf: &[u8], offset: u64) -> io::Result<usize> {
filelike::write_at(self, buf, offset)
}
#[inline]
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> io::Result<()> {
filelike::write_all_at(self, buf, offset)
}
#[inline]
fn write_vectored_at(&mut self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
filelike::write_vectored_at(self, bufs, offset)
}
#[inline]
fn write_all_vectored_at(&mut self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
filelike::write_all_vectored_at(self, bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
filelike::is_write_vectored_at(self)
}
#[inline]
fn copy_from<R: ReadAt>(
&mut self,
offset: u64,
input: &R,
input_offset: u64,
len: u64,
) -> io::Result<u64> {
filelike::copy_from(self, offset, input, input_offset, len)
}
#[inline]
fn set_len(&mut self, size: u64) -> io::Result<()> {
filelike::set_len(self, size)
}
}
#[cfg(feature = "cap-async-std")]
impl Range for cap_async_std::fs::File {
#[inline]
fn metadata(&self) -> io::Result<Metadata> {
filelike::metadata(self)
}
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
filelike::advise(self, offset, len, advice)
}
}
#[cfg(feature = "cap-async-std")]
impl ReadAt for cap_async_std::fs::File {
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
filelike::read_at(self, buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
filelike::read_exact_at(self, buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
filelike::read_vectored_at(self, bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
filelike::read_exact_vectored_at(self, bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
filelike::is_read_vectored_at(self)
}
#[cfg(feature = "io-streams")]
#[inline]
fn read_via_stream_at(&self, offset: u64) -> io::Result<StreamReader> {
filelike::read_via_stream_at(self, offset)
}
}
#[cfg(feature = "cap-async-std")]
impl WriteAt for cap_async_std::fs::File {
#[inline]
fn write_at(&mut self, buf: &[u8], offset: u64) -> io::Result<usize> {
filelike::write_at(self, buf, offset)
}
#[inline]
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> io::Result<()> {
filelike::write_all_at(self, buf, offset)
}
#[inline]
fn write_vectored_at(&mut self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
filelike::write_vectored_at(self, bufs, offset)
}
#[inline]
fn write_all_vectored_at(&mut self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
filelike::write_all_vectored_at(self, bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
filelike::is_write_vectored_at(self)
}
#[inline]
fn copy_from<R: ReadAt>(
&mut self,
offset: u64,
input: &R,
input_offset: u64,
len: u64,
) -> io::Result<u64> {
filelike::copy_from(self, offset, input, input_offset, len)
}
#[inline]
fn set_len(&mut self, size: u64) -> io::Result<()> {
filelike::set_len(self, size)
}
}
#[cfg(feature = "async-std")]
impl Range for async_std::fs::File {
#[inline]
fn metadata(&self) -> io::Result<Metadata> {
filelike::metadata(self)
}
#[inline]
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
filelike::advise(self, offset, len, advice)
}
}
#[cfg(feature = "async-std")]
impl ReadAt for async_std::fs::File {
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
filelike::read_at(self, buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
filelike::read_exact_at(self, buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
filelike::read_vectored_at(self, bufs, offset)
}
#[inline]
fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
filelike::read_exact_vectored_at(self, bufs, offset)
}
#[inline]
fn is_read_vectored_at(&self) -> bool {
filelike::is_read_vectored_at(self)
}
#[cfg(feature = "io-streams")]
#[inline]
fn read_via_stream_at(&self, offset: u64) -> io::Result<StreamReader> {
filelike::read_via_stream_at(self, offset)
}
}
#[cfg(feature = "async-std")]
impl WriteAt for async_std::fs::File {
#[inline]
fn write_at(&mut self, buf: &[u8], offset: u64) -> io::Result<usize> {
filelike::write_at(self, buf, offset)
}
#[inline]
fn write_all_at(&mut self, buf: &[u8], offset: u64) -> io::Result<()> {
filelike::write_all_at(self, buf, offset)
}
#[inline]
fn write_vectored_at(&mut self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
filelike::write_vectored_at(self, bufs, offset)
}
#[inline]
fn write_all_vectored_at(&mut self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
filelike::write_all_vectored_at(self, bufs, offset)
}
#[inline]
fn is_write_vectored_at(&self) -> bool {
filelike::is_write_vectored_at(self)
}
#[inline]
fn copy_from<R: ReadAt>(
&mut self,
offset: u64,
input: &R,
input_offset: u64,
len: u64,
) -> io::Result<u64> {
filelike::copy_from(self, offset, input, input_offset, len)
}
#[inline]
fn set_len(&mut self, size: u64) -> io::Result<()> {
filelike::set_len(self, size)
}
}
#[cfg(not(windows))]
impl AsRawFd for RangeReader {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
#[cfg(windows)]
impl AsRawHandle for RangeReader {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
#[cfg(not(windows))]
impl AsRawFd for RangeWriter {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
#[cfg(windows)]
impl AsRawHandle for RangeWriter {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
#[cfg(not(windows))]
impl AsRawFd for RangeEditor {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
#[cfg(windows)]
impl AsRawHandle for RangeEditor {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn create_anonymous() -> io::Result<UnsafeFile> {
let flags = libc::MFD_CLOEXEC | libc::MFD_ALLOW_SEALING;
let name = b"io_ranges anonymous file\0"
.as_ptr()
.cast::<libc::c_char>();
let fd = unsafe { memfd_create(name, flags) };
if fd == -1 {
return Err(io::Error::last_os_error());
}
Ok(UnsafeFile::from_raw_fd(fd))
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
fn create_anonymous() -> io::Result<UnsafeFile> {
let file = tempfile::tempfile()?;
Ok(file.into_unsafe_file())
}
#[cfg(any(target_os = "android", target_os = "linux"))]
unsafe fn memfd_create(name: *const libc::c_char, flags: libc::c_uint) -> libc::c_int {
libc::syscall(libc::SYS_memfd_create, name, flags) as libc::c_int
}