use std::{
fmt, future,
io::{self, Read, Seek, SeekFrom, Write},
num::NonZeroUsize,
path::Path,
pin::Pin,
sync::Arc,
task::{Context, Poll, ready},
};
#[cfg(unix)]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, OwnedHandle, RawHandle};
use futures_io::{AsyncRead, AsyncSeek, AsyncWrite};
use super::{Metadata, Permissions};
use crate::{Unblock, lock::Mutex, unblock};
pub struct File {
file: Arc<std::fs::File>,
unblock: Mutex<Unblock<ArcFile>>,
read_ahead: bool,
}
impl File {
pub async fn open<P>(path: P) -> io::Result<File>
where
P: AsRef<Path>,
{
let path = path.as_ref().to_owned();
let file = unblock(move || std::fs::File::open(path)).await?;
Ok(File::from(file))
}
pub async fn create<P>(path: P) -> io::Result<File>
where
P: AsRef<Path>,
{
let path = path.as_ref().to_owned();
let file = unblock(move || std::fs::File::create(path)).await?;
Ok(File::from(file))
}
pub async fn sync_all(&self) -> io::Result<()> {
self.flush_writes().await?;
let file = self.file.clone();
unblock(move || file.sync_all()).await
}
pub async fn sync_data(&self) -> io::Result<()> {
self.flush_writes().await?;
let file = self.file.clone();
unblock(move || file.sync_data()).await
}
pub async fn set_len(&self, size: u64) -> io::Result<()> {
let mut adapter = self.unblock.lock().await;
future::poll_fn(|cx| Pin::new(&mut *adapter).poll_flush(cx)).await?;
if self.read_ahead {
let current = SeekFrom::Current(0);
let _ = future::poll_fn(|cx| Pin::new(&mut *adapter).poll_seek(cx, current)).await;
}
let file = self.file.clone();
unblock(move || file.set_len(size)).await
}
pub async fn metadata(&self) -> io::Result<Metadata> {
self.flush_writes().await?;
let file = self.file.clone();
unblock(move || file.metadata()).await
}
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
let file = self.file.clone();
unblock(move || file.set_permissions(perm)).await
}
async fn flush_writes(&self) -> io::Result<()> {
let mut unblock = self.unblock.lock().await;
future::poll_fn(|cx| Pin::new(&mut *unblock).poll_flush(cx)).await
}
fn poll_reposition(&mut self, cx: &mut Context<'_>) -> Poll<()> {
if self.read_ahead {
let _ = ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, SeekFrom::Current(0)));
self.read_ahead = false;
}
Poll::Ready(())
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&*self.file, f)
}
}
impl From<std::fs::File> for File {
fn from(file: std::fs::File) -> Self {
let file = Arc::new(file);
Self {
unblock: Mutex::new(Unblock::with_capacity(CAPACITY, ArcFile(file.clone()))),
file,
read_ahead: false,
}
}
}
#[cfg(unix)]
impl From<OwnedFd> for File {
fn from(fd: OwnedFd) -> Self {
Self::from(std::fs::File::from(fd))
}
}
#[cfg(windows)]
impl From<OwnedHandle> for File {
fn from(handle: OwnedHandle) -> Self {
Self::from(std::fs::File::from(handle))
}
}
#[cfg(unix)]
impl AsFd for File {
fn as_fd(&self) -> BorrowedFd<'_> {
self.file.as_fd()
}
}
#[cfg(unix)]
impl AsRawFd for File {
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
#[cfg(windows)]
impl AsHandle for File {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.file.as_handle()
}
}
#[cfg(windows)]
impl AsRawHandle for File {
fn as_raw_handle(&self) -> RawHandle {
self.file.as_raw_handle()
}
}
impl AsyncRead for File {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
self.read_ahead = true;
Pin::new(self.unblock.get_mut()).poll_read(cx, buf)
}
}
impl AsyncWrite for File {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
ready!(self.poll_reposition(cx));
Pin::new(self.unblock.get_mut()).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(self.unblock.get_mut()).poll_flush(cx)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(self.unblock.get_mut()).poll_close(cx)
}
}
impl AsyncSeek for File {
fn poll_seek(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
pos: SeekFrom,
) -> Poll<io::Result<u64>> {
let pos = ready!(Pin::new(self.unblock.get_mut()).poll_seek(cx, pos))?;
self.read_ahead = false;
Poll::Ready(Ok(pos))
}
}
struct ArcFile(Arc<std::fs::File>);
impl Read for ArcFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&*self.0).read(buf)
}
}
impl Write for ArcFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&*self.0).write(buf)
}
fn flush(&mut self) -> io::Result<()> {
(&*self.0).flush()
}
}
impl Seek for ArcFile {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
(&*self.0).seek(pos)
}
}
const CAPACITY: NonZeroUsize = NonZeroUsize::new(64 * 1024).unwrap();