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};
use std::{
fs,
io::{self, IoSlice, IoSliceMut, Read, Seek, Write},
};
use system_interface::fs::FileIoExt;
use unsafe_io::{FromUnsafeFile, IntoUnsafeFile, OwnsRaw, UnsafeFile};
#[cfg(windows)]
use {
std::os::windows::io::{AsRawHandle, RawHandle},
unsafe_io::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket},
};
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 Array {
fn metadata(&self) -> io::Result<Metadata>;
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()>;
}
pub trait ReadAt: Array {
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: Array {
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 {}
impl<T: ReadAt + WriteAt> EditAt for T {}
#[derive(Debug)]
pub struct ArrayReader {
file: fs::File,
}
#[derive(Debug)]
pub struct ArrayWriter {
file: fs::File,
}
#[derive(Debug)]
pub struct ArrayEditor {
file: fs::File,
}
impl ArrayReader {
#[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()?;
let file = unsafe { fs::File::from_unsafe_file(unsafe_file) };
file.write_all(bytes)?;
Ok(Self { file })
}
}
impl ArrayWriter {
#[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),
"ArrayWriter 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,
"ArrayWriter doesn't support files opened with FILE_APPEND_DATA"
);
}
Self { file }
}
}
impl ArrayEditor {
#[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 Array for ArrayReader {
#[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 Array for ArrayWriter {
#[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 Array for ArrayEditor {
#[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 ArrayReader {
#[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 ArrayEditor {
#[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 ArrayWriter {
#[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 ArrayEditor {
#[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 Array 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 Array 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 Array 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 Array 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 ArrayReader {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
#[cfg(windows)]
impl AsRawHandle for ArrayReader {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
#[cfg(windows)]
impl AsRawHandleOrSocket for ArrayReader {
#[inline]
fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
self.file.as_raw_handle_or_socket()
}
}
#[cfg(not(windows))]
impl AsRawFd for ArrayWriter {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
#[cfg(windows)]
impl AsRawHandle for ArrayWriter {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
#[cfg(windows)]
impl AsRawHandleOrSocket for ArrayWriter {
#[inline]
fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
self.file.as_raw_handle_or_socket()
}
}
#[cfg(not(windows))]
impl AsRawFd for ArrayEditor {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
#[cfg(windows)]
impl AsRawHandle for ArrayEditor {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
#[cfg(windows)]
impl AsRawHandleOrSocket for ArrayEditor {
#[inline]
fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
self.file.as_raw_handle_or_socket()
}
}
unsafe impl OwnsRaw for ArrayReader {}
unsafe impl OwnsRaw for ArrayWriter {}
unsafe impl OwnsRaw for ArrayEditor {}
#[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_arrays 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
}