#[cfg(all(feature = "allocator_api", feature = "runtime"))]
use std::alloc::Allocator;
use std::{fs::Metadata, io, path::Path};
#[cfg(feature = "runtime")]
use crate::{
buf::{IntoInner, IoBuf, IoBufMut},
buf_try,
driver::AsRawFd,
op::{ReadAt, Sync, WriteAt},
task::RUNTIME,
vec_alloc, Attacher, BufResult,
};
use crate::{fs::OpenOptions, impl_raw_fd};
#[derive(Debug)]
pub struct File {
inner: std::fs::File,
#[cfg(feature = "runtime")]
attacher: Attacher,
}
#[cfg(target_os = "windows")]
fn file_with_options(
path: impl AsRef<Path>,
mut options: std::fs::OpenOptions,
) -> io::Result<std::fs::File> {
use std::os::windows::prelude::OpenOptionsExt;
use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OVERLAPPED;
options.custom_flags(FILE_FLAG_OVERLAPPED);
options.open(path)
}
#[cfg(not(target_os = "windows"))]
fn file_with_options(
path: impl AsRef<Path>,
options: std::fs::OpenOptions,
) -> io::Result<std::fs::File> {
options.open(path)
}
impl File {
pub(crate) fn with_options(path: impl AsRef<Path>, options: OpenOptions) -> io::Result<Self> {
let this = Self {
inner: file_with_options(path, options.0)?,
#[cfg(feature = "runtime")]
attacher: Attacher::new(),
};
Ok(this)
}
pub fn open(path: impl AsRef<Path>) -> io::Result<Self> {
OpenOptions::new().read(true).open(path)
}
pub fn create(path: impl AsRef<Path>) -> io::Result<Self> {
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)
}
#[cfg(feature = "runtime")]
pub(crate) fn attach(&self) -> io::Result<()> {
self.attacher.attach(self)
}
pub fn try_clone(&self) -> io::Result<Self> {
Ok(Self {
inner: self.inner.try_clone()?,
#[cfg(feature = "runtime")]
attacher: self.attacher.clone(),
})
}
pub fn metadata(&self) -> io::Result<Metadata> {
self.inner.metadata()
}
#[cfg(feature = "runtime")]
pub async fn read_at<T: IoBufMut<'static>>(
&self,
buffer: T,
pos: usize,
) -> BufResult<usize, T> {
use crate::op::UpdateBufferLen;
let ((), buffer) = buf_try!(self.attach(), buffer);
let op = ReadAt::new(self.as_raw_fd(), pos, buffer);
RUNTIME
.with(|runtime| runtime.submit(op))
.await
.into_inner()
.update_buffer_len()
}
#[cfg(feature = "runtime")]
pub async fn read_exact_at<T: IoBufMut<'static>>(
&self,
mut buffer: T,
pos: usize,
) -> BufResult<usize, T> {
let need = buffer.as_uninit_slice().len();
let mut total_read = 0;
let mut read;
while total_read < need {
(read, buffer) = buf_try!(self.read_at(buffer, pos + total_read).await);
if read == 0 {
break;
} else {
total_read += read;
}
}
let res = if total_read < need {
Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
} else {
Ok(total_read)
};
(res, buffer)
}
#[cfg(feature = "runtime")]
pub async fn read_to_end_at<
#[cfg(feature = "allocator_api")] A: Allocator + Unpin + 'static,
>(
&self,
mut buffer: vec_alloc!(u8, A),
pos: usize,
) -> BufResult<usize, vec_alloc!(u8, A)> {
let mut total_read = 0;
let mut read;
loop {
(read, buffer) = buf_try!(self.read_at(buffer, pos + total_read).await);
if read == 0 {
break;
} else {
total_read += read;
if buffer.len() == buffer.capacity() {
buffer.reserve(32);
}
}
}
(Ok(total_read), buffer)
}
#[cfg(feature = "runtime")]
pub async fn write_at<T: IoBuf<'static>>(&self, buffer: T, pos: usize) -> BufResult<usize, T> {
let ((), buffer) = buf_try!(self.attach(), buffer);
let op = WriteAt::new(self.as_raw_fd(), pos, buffer);
RUNTIME
.with(|runtime| runtime.submit(op))
.await
.into_inner()
}
#[cfg(feature = "runtime")]
pub async fn write_all_at<T: IoBuf<'static>>(
&self,
mut buffer: T,
pos: usize,
) -> BufResult<usize, T> {
let buf_len = buffer.buf_len();
let mut total_written = 0;
let mut written;
while total_written < buf_len {
(written, buffer) = buf_try!(
self.write_at(buffer.slice(total_written..), pos + total_written)
.await
.into_inner()
);
total_written += written;
}
(Ok(total_written), buffer)
}
#[cfg(feature = "runtime")]
async fn sync_impl(&self, datasync: bool) -> io::Result<()> {
self.attach()?;
let op = Sync::new(self.as_raw_fd(), datasync);
RUNTIME.with(|runtime| runtime.submit(op)).await.0?;
Ok(())
}
#[cfg(feature = "runtime")]
pub async fn sync_all(&self) -> io::Result<()> {
self.sync_impl(false).await
}
#[cfg(feature = "runtime")]
pub async fn sync_data(&self) -> io::Result<()> {
self.sync_impl(true).await
}
}
impl_raw_fd!(File, inner, attacher);