use std::io::{IoSlice, Result};
use std::pin::Pin;
use std::task::{Context, Poll, ready};
use tokio::io::AsyncWrite;
use crate::shared::block::{BLOCK_SIZE, Block, Header};
use crate::shared::buffer::{ReadableRegion, WritableRegion};
use crate::shared::slices::{IterBuffers, Slices, Split};
use crate::shared::state::State;
use crate::{Archive, Entry, TRACING_ENABLED};
mod error;
pub use self::error::WriteError;
impl<W: AsyncWrite> Archive<W> {
pub(super) fn poll_write_header(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
header: &Header,
) -> Poll<Result<()>> {
loop {
if TRACING_ENABLED {
eprintln!(" |whead: {:?}", self.state);
}
match self.state {
State::ExpectingHeader => {
let buf = header.as_bytes();
let n = ready!(self.as_mut().poll_write_data(cx, buf, Some(header)))?;
if n == 0 {
return WriteError::WriteZero.into();
}
continue;
}
State::ReceivingHeader(rem, false) => {
let pos = BLOCK_SIZE - rem;
let buf = &header.as_bytes()[pos..];
let n = ready!(self.as_mut().poll_write_data(cx, buf, Some(header)))?;
if n == 0 && rem > 0 {
return WriteError::WriteZero.into();
}
continue;
}
State::ReceivedHeader => {
self.project().state.take_marker(Some(header))?;
return Poll::Ready(Ok(()));
}
State::ReceivingData(_)
| State::ReceivedData
| State::AligningData(_)
| State::AlignedData => {
return WriteError::OverlappingEntry.into();
}
State::ReceivingHeader(_, true) | State::ReceivingEof(_) | State::ReceivedEof => {
panic!("cannot write header; invalid state: {:?}", self.state)
}
}
}
}
fn poll_write_entry(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
header: &Header,
) -> Poll<Result<usize>> {
if TRACING_ENABLED {
eprintln!(" |write: {:?}", self.state);
}
match self.state {
State::ReceivingData(rem) => {
let n = ready!(self.as_mut().poll_write_vectored(
cx,
bufs,
rem as usize,
Some(header)
))?;
if n as u64 == rem {
debug_assert_eq!(bufs.bytes_len(), n);
debug_assert_eq!(self.state, State::ReceivedData);
let res = ready!(self.poll_finish_entry(cx, header));
debug_assert!(res.is_ok());
}
Poll::Ready(Ok(n))
}
State::ExpectingHeader
| State::ReceivingHeader(_, _)
| State::ReceivedHeader
| State::ReceivedData
| State::AligningData(_)
| State::AlignedData
| State::ReceivingEof(_)
| State::ReceivedEof => {
panic!("cannot write entry; invalid state: {:?}", self.state)
}
}
}
fn poll_finish_entry(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
header: &Header,
) -> Poll<Result<()>> {
loop {
if TRACING_ENABLED {
eprintln!(" | fini: {:?}", self.state);
}
match self.state {
State::ReceivedData => {
self.as_mut().project().state.take_marker(Some(header))?;
continue;
}
State::AligningData(rem) => {
let buf = &Block::empty().as_bytes()[..rem];
ready!(self.as_mut().poll_write_data(cx, buf, Some(header)))?;
continue;
}
State::AlignedData => {
self.project().state.take_marker(None)?;
return Poll::Ready(Ok(()));
}
State::ExpectingHeader => {
return Poll::Ready(Ok(()));
}
State::ReceivingHeader(_, _)
| State::ReceivedHeader
| State::ReceivingData(_)
| State::ReceivingEof(_)
| State::ReceivedEof => {
panic!("cannot finish entry; invalid state: {:?}", self.state)
}
}
}
}
pub(super) fn poll_finish(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
loop {
match self.state {
State::ExpectingHeader => {
let buf = Block::empty().as_bytes();
ready!(self.as_mut().poll_write_data(cx, buf, None))?;
continue;
}
State::ReceivingHeader(rem, true) | State::ReceivingEof(rem) => {
let buf = &Block::empty().as_bytes()[..rem];
ready!(self.as_mut().poll_write_data(cx, buf, None))?;
continue;
}
State::ReceivedEof => {
ready!(self.as_mut().poll_flush_buffered(cx))?;
return self.project().io.poll_shutdown(cx);
}
State::ReceivingHeader(_, false)
| State::ReceivedHeader
| State::ReceivingData(_)
| State::ReceivedData
| State::AligningData(_)
| State::AlignedData => {
panic!("cannot finish archive; invalid state: {:?}", self.state)
}
}
}
}
fn poll_flush_buffered(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
let mut this = self.project();
let mut buf = this.buf.buffered();
while !buf.is_empty() {
let bytes = buf.bytes();
let bytes_written = ready!(this.io.as_mut().poll_write(cx, bytes))?;
if bytes_written == 0 {
return WriteError::WriteZero.into();
}
buf.commit(bytes_written);
}
this.buf.clear();
Poll::Ready(Ok(()))
}
fn poll_write_data(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
header: Option<&Header>,
) -> Poll<Result<usize>> {
let max = buf.len();
let slice = [IoSlice::new(buf)];
self.poll_write_vectored(cx, &slice, max, header)
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
max: usize,
header: Option<&Header>,
) -> Poll<Result<usize>> {
let prefix = bufs.take_prefix(max);
let prefix_len = prefix.bytes_len();
let next = {
let this = self.as_mut().project();
this.state.take_slices(prefix.iter_buffers(), header)?
};
assert_eq!(next.1, prefix_len);
let can_pass_through = {
let this = self.as_mut().project();
prefix_len >= this.buf.capacity() && this.io.is_write_vectored()
};
if can_pass_through || prefix_len > self.as_mut().project().buf.available().remaining() {
ready!(self.as_mut().poll_flush_buffered(cx))?;
}
let mut this = self.as_mut().project();
let bytes_written = if can_pass_through {
let slices_len = prefix_len - prefix.remainder().len();
match ready!(this.io.as_mut().poll_write_vectored(cx, prefix.slices()))? {
n if n == slices_len => match this.io.as_mut().poll_write(cx, prefix.remainder()) {
Poll::Ready(Ok(w)) => n + w,
Poll::Ready(Err(_)) => n,
Poll::Pending => n,
},
n => n,
}
} else {
this.buf.available().fill_from_slices(prefix.iter_buffers())
};
*this.state = if bytes_written == prefix_len {
next.0
} else {
let prefix = prefix.take_prefix(bytes_written);
let next = this
.state
.take_slices(prefix.iter_buffers(), header)
.expect("this slice should have already been checked");
assert_eq!(next.1, bytes_written);
next.0
};
Poll::Ready(Ok(bytes_written))
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
ready!(self.as_mut().poll_flush_buffered(cx))?;
self.project().io.poll_flush(cx)
}
fn is_write_vectored(&self) -> bool {
true
}
}
impl<W: AsyncWrite> AsyncWrite for Entry<'_, W> {
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
let slice = [IoSlice::new(buf)];
self.poll_write_vectored(cx, &slice)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<Result<usize>> {
if TRACING_ENABLED {
eprintln!("write: '{}', size = {}", self.path_lossy(), self.size());
}
let this = self.project();
let header = this.header;
this.archive.as_mut().poll_write_entry(cx, bufs, header)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.project().archive.as_mut().poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
if TRACING_ENABLED {
eprintln!("finsh: '{}', size = {}", self.path_lossy(), self.size());
}
let this = self.project();
let header = this.header;
ready!(this.archive.as_mut().poll_finish_entry(cx, header))?;
this.archive.as_mut().poll_flush(cx)
}
fn is_write_vectored(&self) -> bool {
self.archive.is_write_vectored()
}
}
#[cfg(test)]
mod tests;