#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
use std::sync::{Mutex, PoisonError};
use std::{
fmt,
fs::{self, File},
io,
path::Path,
sync::Arc,
};
#[cfg(unix)]
use std::os::unix::prelude::*;
#[cfg(all(target_os = "wasi", target_env = "p1"))]
use std::os::wasi::prelude::*;
#[cfg(target_os = "windows")]
use std::os::windows::prelude::*;
use crate::{Adapter, Size};
use super::{ReadAt, WriteAt};
#[cfg(all(target_os = "wasi", target_env = "p1"))]
trait FileExt {
fn read_at(&self, buffer: &mut [u8], offset: u64) -> io::Result<usize>;
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
fn write_at(&self, buffer: &[u8], offset: u64) -> io::Result<usize>;
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize>;
}
#[cfg(all(target_os = "wasi", target_env = "p1"))]
impl FileExt for File {
fn read_at(&self, buffer: &mut [u8], offset: u64) -> io::Result<usize> {
unsafe {
let raw = self.as_raw_fd() as wasip1::Fd;
let iovec = [wasip1::Iovec {
buf: buffer.as_mut_ptr(),
buf_len: buffer.len(),
}];
wasip1::fd_pread(raw, &iovec, offset)
.map_err(|err| io::Error::from_raw_os_error(err.raw() as _))
}
}
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
unsafe {
let raw = self.as_raw_fd() as wasip1::Fd;
let iovec = std::mem::transmute(bufs);
wasip1::fd_pread(raw, iovec, offset)
.map_err(|err| io::Error::from_raw_os_error(err.raw() as _))
}
}
fn write_at(&self, buffer: &[u8], offset: u64) -> io::Result<usize> {
unsafe {
let raw = self.as_raw_fd() as wasip1::Fd;
let iovec = [wasip1::Ciovec {
buf: buffer.as_ptr(),
buf_len: buffer.len(),
}];
wasip1::fd_pwrite(raw, &iovec, offset)
.map_err(|err| io::Error::from_raw_os_error(err.raw() as _))
}
}
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
unsafe {
let raw = self.as_raw_fd() as wasip1::Fd;
let iovec = std::mem::transmute(bufs);
wasip1::fd_pwrite(raw, iovec, offset)
.map_err(|err| io::Error::from_raw_os_error(err.raw() as _))
}
}
}
#[cfg(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
))]
type FileRepr = File;
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
type FileRepr = Mutex<File>;
#[derive(Debug)]
pub struct RandomAccessFile(FileRepr);
impl RandomAccessFile {
#[inline]
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<RandomAccessFile> {
let f = File::open(path.as_ref())?;
Ok(RandomAccessFile::from(f))
}
#[inline]
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<RandomAccessFile> {
let f = File::create(path.as_ref())?;
Ok(RandomAccessFile::from(f))
}
#[inline]
pub(crate) fn with_file<T>(&self, f: impl FnOnce(&File) -> T) -> T {
#[cfg(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
))]
{
f(&self.0)
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
{
f(&self.0.lock().unwrap_or_else(PoisonError::into_inner))
}
}
#[inline]
pub fn sync_all(&self) -> io::Result<()> {
self.with_file(|f| f.sync_all())
}
#[inline]
pub fn sync_data(&self) -> io::Result<()> {
self.with_file(|f| f.sync_data())
}
#[inline]
pub fn set_len(&self, size: u64) -> io::Result<()> {
self.with_file(|f| f.set_len(size))
}
#[inline]
pub fn metadata(&self) -> io::Result<fs::Metadata> {
self.with_file(|f| f.metadata())
}
#[inline]
pub fn try_clone(&self) -> io::Result<RandomAccessFile> {
let file = self.with_file(|f| f.try_clone())?;
Ok(RandomAccessFile::from(file))
}
#[inline]
pub fn set_permissions(&self, perm: fs::Permissions) -> io::Result<()> {
self.with_file(|f| f.set_permissions(perm))
}
#[inline]
pub fn into_inner(self) -> File {
#[cfg(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
))]
{
self.0
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
{
self.0.into_inner().unwrap_or_else(PoisonError::into_inner)
}
}
}
impl ReadAt for RandomAccessFile {
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
{
self.0.read_at(buf, offset)
}
#[cfg(target_os = "windows")]
{
self.0.seek_read(buf, offset)
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
{
use io::{Read, Seek};
let file = &mut *self.0.lock().unwrap_or_else(PoisonError::into_inner);
file.seek(io::SeekFrom::Start(offset))?;
file.read(buf)
}
}
#[cfg(unix)]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
self.0.read_exact_at(buf, offset)
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
use io::{Read, Seek};
let file = &mut *self.0.lock().unwrap_or_else(PoisonError::into_inner);
file.seek(io::SeekFrom::Start(offset))?;
file.read_exact(buf)
}
#[cfg(all(target_os = "wasi", target_env = "p1"))]
#[inline]
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
self.0.read_vectored_at(bufs, offset)
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
use io::{Read, Seek};
let file = &mut *self.0.lock().unwrap_or_else(PoisonError::into_inner);
file.seek(io::SeekFrom::Start(offset))?;
file.read_vectored(bufs)
}
}
impl WriteAt for RandomAccessFile {
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
{
self.0.write_at(buf, offset)
}
#[cfg(target_os = "windows")]
{
self.0.seek_write(buf, offset)
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
{
use io::{Seek, Write};
let file = &mut *self.0.lock().unwrap_or_else(PoisonError::into_inner);
file.seek(io::SeekFrom::Start(offset))?;
file.write(buf)
}
}
#[cfg(unix)]
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
self.0.write_all_at(buf, offset)
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
use io::{Seek, Write};
let file = &mut *self.0.lock().unwrap_or_else(PoisonError::into_inner);
file.seek(io::SeekFrom::Start(offset))?;
file.write_all(buf)
}
#[cfg(all(target_os = "wasi", target_env = "p1"))]
#[inline]
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
self.0.write_vectored_at(bufs, offset)
}
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
use io::{Seek, Write};
let file = &mut *self.0.lock().unwrap_or_else(PoisonError::into_inner);
file.seek(io::SeekFrom::Start(offset))?;
file.write_vectored(bufs)
}
#[inline]
fn flush(&self) -> io::Result<()> {
use std::io::Write;
self.with_file(|mut f| f.flush())
}
}
impl Size for RandomAccessFile {
fn size(&self) -> io::Result<u64> {
self.with_file(|f| f.metadata().map(|m| m.len()))
}
}
impl From<File> for RandomAccessFile {
#[inline]
fn from(file: File) -> RandomAccessFile {
#[cfg(not(any(
unix,
target_os = "windows",
all(target_os = "wasi", target_env = "p1")
)))]
let file = Mutex::new(file);
RandomAccessFile(file)
}
}
impl From<RandomAccessFile> for File {
#[inline]
fn from(file: RandomAccessFile) -> File {
file.into_inner()
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl AsRawFd for RandomAccessFile {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
#[cfg(target_os = "windows")]
impl AsRawHandle for RandomAccessFile {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.0.as_raw_handle()
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl AsFd for RandomAccessFile {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
#[cfg(target_os = "windows")]
impl AsHandle for RandomAccessFile {
#[inline]
fn as_handle(&self) -> BorrowedHandle<'_> {
self.0.as_handle()
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl FromRawFd for RandomAccessFile {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> Self {
unsafe { Self::from(File::from_raw_fd(fd)) }
}
}
#[cfg(target_os = "windows")]
impl FromRawHandle for RandomAccessFile {
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
unsafe { Self::from(File::from_raw_handle(handle)) }
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl From<OwnedFd> for RandomAccessFile {
#[inline]
fn from(fd: OwnedFd) -> Self {
Self::from(File::from(fd))
}
}
#[cfg(target_os = "windows")]
impl From<OwnedHandle> for RandomAccessFile {
#[inline]
fn from(handle: OwnedHandle) -> Self {
Self::from(File::from(handle))
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl IntoRawFd for RandomAccessFile {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
#[cfg(target_os = "windows")]
impl IntoRawHandle for RandomAccessFile {
#[inline]
fn into_raw_handle(self) -> RawHandle {
self.0.into_raw_handle()
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl From<RandomAccessFile> for OwnedFd {
#[inline]
fn from(f: RandomAccessFile) -> Self {
f.0.into()
}
}
#[cfg(target_os = "windows")]
impl From<RandomAccessFile> for OwnedHandle {
#[inline]
fn from(f: RandomAccessFile) -> Self {
f.0.into()
}
}
#[derive(Clone)]
pub struct SyncFile(Adapter<Arc<RandomAccessFile>>);
impl SyncFile {
#[inline]
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<SyncFile> {
let f = File::open(path.as_ref())?;
Ok(SyncFile::from(f))
}
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<SyncFile> {
let f = File::create(path.as_ref())?;
Ok(SyncFile::from(f))
}
#[must_use]
pub fn offset(&self) -> u64 {
self.0.offset()
}
}
impl std::ops::Deref for SyncFile {
type Target = RandomAccessFile;
#[inline]
fn deref(&self) -> &RandomAccessFile {
self.0.get_ref()
}
}
impl ReadAt for SyncFile {
#[inline]
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.0.read_at(buf, offset)
}
#[inline]
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
self.0.read_exact_at(buf, offset)
}
#[inline]
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
self.0.read_vectored_at(bufs, offset)
}
}
impl WriteAt for SyncFile {
#[inline]
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.0.write_at(buf, offset)
}
#[inline]
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
self.0.write_all_at(buf, offset)
}
#[inline]
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
self.0.write_vectored_at(bufs, offset)
}
#[inline]
fn flush(&self) -> io::Result<()> {
self.0.flush()
}
}
impl Size for SyncFile {
#[inline]
fn size(&self) -> io::Result<u64> {
self.0.size()
}
}
impl io::Read for SyncFile {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.0.read_exact(buf)
}
#[inline]
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
if let Ok(size) = self.size() {
if let Some(size) = size.checked_sub(self.offset()) {
let size = size.try_into().map_err(|_| io::ErrorKind::OutOfMemory)?;
buf.try_reserve(size)?;
}
}
self.0.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
if let Ok(size) = self.size() {
if let Some(size) = size.checked_sub(self.offset()) {
let size = size.try_into().map_err(|_| io::ErrorKind::OutOfMemory)?;
buf.try_reserve(size)?;
}
}
self.0.read_to_string(buf)
}
}
impl io::Seek for SyncFile {
#[inline]
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
let pos = match pos {
io::SeekFrom::End(_) => {
let offset = self.0.get_ref().with_file(|mut f| f.seek(pos))?;
io::SeekFrom::Start(offset)
}
pos => pos,
};
self.0.seek(pos)
}
#[inline]
fn rewind(&mut self) -> io::Result<()> {
self.0.rewind()
}
#[inline]
fn stream_position(&mut self) -> io::Result<u64> {
Ok(self.offset())
}
}
impl io::Write for SyncFile {
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
#[inline]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0.write_all(buf)
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl From<File> for SyncFile {
#[inline]
fn from(file: File) -> SyncFile {
SyncFile::from(RandomAccessFile::from(file))
}
}
impl From<RandomAccessFile> for SyncFile {
#[inline]
fn from(file: RandomAccessFile) -> SyncFile {
SyncFile(Adapter::new(Arc::new(file)))
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl AsRawFd for SyncFile {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0.get_ref().as_raw_fd()
}
}
#[cfg(target_os = "windows")]
impl AsRawHandle for SyncFile {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.0.get_ref().as_raw_handle()
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl AsFd for SyncFile {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.get_ref().as_fd()
}
}
#[cfg(target_os = "windows")]
impl AsHandle for SyncFile {
#[inline]
fn as_handle(&self) -> BorrowedHandle<'_> {
self.0.get_ref().as_handle()
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl FromRawFd for SyncFile {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> Self {
unsafe { Self::from(File::from_raw_fd(fd)) }
}
}
#[cfg(target_os = "windows")]
impl FromRawHandle for SyncFile {
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
unsafe { Self::from(File::from_raw_handle(handle)) }
}
}
#[cfg(any(unix, all(target_os = "wasi", target_env = "p1")))]
impl From<OwnedFd> for SyncFile {
#[inline]
fn from(fd: OwnedFd) -> Self {
Self::from(File::from(fd))
}
}
#[cfg(target_os = "windows")]
impl From<OwnedHandle> for SyncFile {
#[inline]
fn from(handle: OwnedHandle) -> Self {
Self::from(File::from(handle))
}
}
impl fmt::Debug for SyncFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SyncFile")
.field("file", self.0.get_ref())
.field("offset", &self.offset())
.finish()
}
}