use crate::buf::owned::{IoBuf, IoBufMut, OwnedBuf};
use core::future::poll_fn;
use core::task::{Context, Poll, Waker};
use std::fs::File as StdFile;
use std::io;
use std::sync::Arc;
use super::FileIo;
use super::orphans::Orphan;
use super::work::{Counted, Ended, Work, bounce, tally_read, tally_write};
use crate::fs::call::End;
use crate::io::Handle;
use crate::io::side::After;
impl FileIo {
pub(super) fn poll_ordered(self: &Arc<Self>, cx: &mut Context<'_>) -> Poll<()> {
if !self.has_unsent() {
return Poll::Ready(());
}
self.with_state(cx, |st, io, waker, after| st.poll_writes(io, waker, after))
}
pub(super) async fn bounce_for(
&self,
len: usize,
addr: usize,
) -> (crate::buf::PoolBuf, usize, usize) {
poll_fn(|cx| match bounce(len, addr, self.direct, cx.waker()) {
Some(got) => Poll::Ready(got),
None => Poll::Pending,
})
.await
}
pub(crate) async fn read_at(self: &Arc<Self>, out: &mut [u8], at: u64) -> io::Result<usize> {
if out.is_empty() {
return Ok(0);
}
poll_fn(|cx| self.poll_ordered(cx)).await;
let (buf, from, n) = self.bounce_for(out.len(), out.as_ptr() as usize).await;
let mut job = self.submit_read(buf, from, n, at);
let pooled = job.pooled();
let result = poll_fn(|cx| job.poll(cx.waker())).await;
tally_read(pooled, &result, |buf| buf.filled().len());
Ok(result?.peek_into(out))
}
pub(crate) async fn write_at(self: &Arc<Self>, bytes: &[u8], at: u64) -> io::Result<usize> {
if bytes.is_empty() {
return Ok(0);
}
poll_fn(|cx| self.poll_ordered(cx)).await;
let (mut buf, from, n) = self.bounce_for(bytes.len(), bytes.as_ptr() as usize).await;
buf.fill_at(from, &bytes[..n]);
let (work, bytes) = self.submit_pwrite(buf, at, Waker::noop());
let mut out = Out {
io: self,
work: Some(work),
bytes,
};
poll_fn(|cx| out.poll(cx)).await.map(|()| n)
}
pub(crate) async fn read_at_owned<B>(
self: &Arc<Self>,
buf: B,
at: u64,
) -> (io::Result<usize>, B)
where
B: IoBufMut,
{
let buf = OwnedBuf::filled(buf);
if buf.len() == buf.capacity() {
return (Ok(0), buf.into_filled());
}
poll_fn(|cx| self.poll_ordered(cx)).await;
let mut job = self.submit_read_owned(&mut Some(buf), at, Waker::noop());
let pooled = job.pooled();
let (result, buf) = poll_fn(|cx| job.poll(cx.waker())).await;
tally_read(pooled, &result, |n| *n);
(result, buf.into_filled())
}
pub(crate) async fn write_at_owned<B>(
self: &Arc<Self>,
buf: B,
at: u64,
) -> (io::Result<usize>, B)
where
B: IoBuf,
{
let buf = OwnedBuf::written(buf);
if buf.is_empty() {
return (Ok(0), buf.into_written());
}
poll_fn(|cx| self.poll_ordered(cx)).await;
let len = buf.len();
let (work, bytes) = self.submit_pwrite_owned(buf, at, Waker::noop());
let mut out = Out {
io: self,
work: Some(work),
bytes,
};
let (result, buf) = poll_fn(|cx| out.poll(cx)).await;
(result.map(|()| len), buf.into_written())
}
pub(crate) fn moved_on(&self) {
self.generation
.fetch_add(1, core::sync::atomic::Ordering::Release);
}
fn orphan(self: &Arc<Self>, orphan: Orphan) {
self.orphans.push(orphan);
let mut after = After::default();
self.state.defer(
super::deferred::ORPHAN,
&mut after,
&super::FileHooks { io: self },
);
self.unsent
.store(true, core::sync::atomic::Ordering::Release);
after.run();
}
}
pub(super) struct Out<'a, T>
where
(Work<T>, Counted): Into<Orphan>,
{
pub(super) io: &'a Arc<FileIo>,
pub(super) work: Option<Work<T>>,
pub(super) bytes: Counted,
}
impl<T> Out<'_, T>
where
T: Ended + End,
(Work<T>, Counted): Into<Orphan>,
{
pub(super) async fn finish(mut self) -> T {
poll_fn(|cx| self.poll(cx)).await
}
pub(super) fn poll(&mut self, cx: &mut Context<'_>) -> Poll<T> {
let Some(work) = self.work.as_mut() else {
return Poll::Pending;
};
let end = core::task::ready!(work.poll(cx.waker()));
self.work = None;
tally_write(self.bytes, end.error().is_none());
self.io.moved_on();
Poll::Ready(end)
}
}
impl<T> Drop for Out<'_, T>
where
(Work<T>, Counted): Into<Orphan>,
{
fn drop(&mut self) {
if let Some(work) = self.work.take() {
self.io.orphan((work, self.bytes).into());
}
}
}
impl Handle<StdFile> {
pub async fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.file()?.read_at(buf, offset).await
}
pub async fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.file()?.write_at(buf, offset).await
}
pub async fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.read_at(buf, offset).await {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
));
}
Ok(n) => {
buf = &mut buf[n..];
offset += n as u64;
}
Err(err) if err.kind() == io::ErrorKind::Interrupted => {}
Err(err) => return Err(err),
}
}
Ok(())
}
pub async fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.write_at(buf, offset).await {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to write whole buffer",
));
}
Ok(n) => {
buf = &buf[n..];
offset += n as u64;
}
Err(err) if err.kind() == io::ErrorKind::Interrupted => {}
Err(err) => return Err(err),
}
}
Ok(())
}
pub async fn read_at_owned<B>(&self, buf: B, offset: u64) -> (io::Result<usize>, B)
where
B: IoBufMut,
{
match self.file() {
Ok(file) => file.read_at_owned(buf, offset).await,
Err(err) => (Err(err), buf),
}
}
pub async fn write_at_owned<B>(&self, buf: B, offset: u64) -> (io::Result<usize>, B)
where
B: IoBuf,
{
match self.file() {
Ok(file) => file.write_at_owned(buf, offset).await,
Err(err) => (Err(err), buf),
}
}
}