use core::task::{Poll, Waker};
use std::io;
use super::work::bounce;
use super::{FILE_SLOTS, FileIo, FileState};
use crate::buf::PoolBuf;
use crate::io::side::After;
use crate::io::source::Failure;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) struct Tag {
addr: usize,
len: usize,
}
pub(super) struct Landing {
tag: Tag,
at: u64,
n: usize,
}
pub(super) struct Answer {
tag: Tag,
result: io::Result<usize>,
buf: PoolBuf,
}
#[derive(Default)]
pub(super) struct Answers(Vec<Answer>);
impl Answers {
fn len(&self) -> usize {
self.0.len()
}
fn push(&mut self, answer: Answer) {
self.0.push(answer);
}
fn remove_oldest(&mut self) -> Answer {
self.0.remove(0)
}
fn take_where(&mut self, pred: impl FnMut(&Answer) -> bool) -> Option<Answer> {
let at = self.0.iter().position(pred)?;
Some(self.0.remove(at))
}
}
impl Drop for Answers {
fn drop(&mut self) {
for answer in &self.0 {
if let Err(err) = &answer.result {
crate::rt::counters::write_behind_lost(1);
log::warn!("bytes a file handle had accepted could not be written: {err}");
}
}
}
}
impl FileState {
pub(super) fn write_landed(
&mut self,
io: &FileIo,
bufs: &[io::IoSlice<'_>],
waker: &Waker,
after: &mut After,
) -> Poll<io::Result<usize>> {
let Some(bytes) = bufs.iter().find(|b| !b.is_empty()) else {
return Poll::Ready(Ok(0));
};
let tag = Tag {
addr: bytes.as_ptr() as usize,
len: bytes.len(),
};
if let Some(done) = self.take_answer(tag, bytes, after) {
return Poll::Ready(done);
}
if self.landing.as_ref().is_some_and(|l| l.tag == tag) {
return Poll::Pending;
}
if let Some(failure) = self.failed {
return Poll::Ready(Err(failure.error()));
}
if self.writing.is_some() || !self.orphans.is_empty() || self.behind.is_some() {
return Poll::Pending;
}
let Some((mut buf, from, n)) = bounce(bytes.len(), tag.addr, io.direct, waker) else {
return Poll::Pending;
};
buf.fill_at(from, &bytes[..n]);
self.invalidate(after);
self.writing = Some(io.submit_write_kept(buf, self.cursor, waker));
self.landing = Some(Landing {
tag,
at: self.cursor,
n,
});
self.cursor += n as u64;
let _ = self.poll_writes(io, waker, after);
self.take_answer(tag, bytes, after)
.map_or(Poll::Pending, Poll::Ready)
}
pub(super) fn landed(
&mut self,
io: &FileIo,
result: io::Result<Option<u64>>,
buf: Option<PoolBuf>,
after: &mut After,
) -> (Option<Failure>, Option<u64>) {
let (Some(landing), Some(buf)) = (self.landing.take(), buf) else {
return (result.as_ref().err().map(Failure::of), None);
};
let (result, end) = match result {
Ok(end) => (Ok(landing.n), end),
Err(err) => {
self.cursor = landing.at;
(Err(err), None)
}
};
if self.answers.len() == FILE_SLOTS {
let oldest = self.answers.remove_oldest();
self.forget(io, oldest, after);
}
self.answers.push(Answer {
tag: landing.tag,
result,
buf,
});
(None, end)
}
fn take_answer(
&mut self,
tag: Tag,
bytes: &[u8],
after: &mut After,
) -> Option<io::Result<usize>> {
let answer = self.answers.take_where(|a| {
let kept = a.buf.filled();
a.tag == tag && bytes.get(..kept.len()) == Some(kept)
})?;
after.drop_buf(answer.buf);
Some(answer.result)
}
fn forget(&mut self, io: &FileIo, answer: Answer, after: &mut After) {
if let Err(err) = &answer.result {
self.fail(io, Failure::of(err));
}
after.drop_buf(answer.buf);
}
pub(super) fn fail_answers(&mut self, io: &FileIo, after: &mut After) {
while let Some(answer) = self.answers.take_where(|a| a.result.is_err()) {
self.forget(io, answer, after);
}
}
}