use crate::{chunk::Chunk, rt::size_commonbuf_t};
use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
use tokio::{
io::{self, AsyncRead, ReadBuf},
sync::mpsc::{channel, Receiver, Sender},
};
pub struct ChunkIter<const L: usize> {
source: Receiver<Chunk<L>>,
current: Option<Chunk<L>>,
_garbage: Vec<Chunk<L>>,
_dead: bool,
}
impl<const L: usize> ChunkIter<L> {
pub fn new() -> (Sender<Chunk<L>>, ChunkIter<L>) {
let (tx, rx) = channel(size_commonbuf_t::<L>());
(
tx,
Self {
source: rx,
current: None,
_garbage: vec![],
_dead: false,
},
)
}
async fn next_chunk(&mut self) {
let current = self.source.recv().await;
self.current = current;
if self.current.is_none() {
self._dead = true;
}
}
async fn read_current_chunk(&mut self, buf: &mut ReadBuf<'_>) {
let current = self.current.as_mut().unwrap();
let _ = current.read_to_buf(buf).await;
}
}
impl<const L: usize> AsyncRead for ChunkIter<L> {
fn poll_read(
mut self: Pin<&mut Self>,
ctx: &mut Context,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match self.current {
Some(ref mut chunk) => {
if chunk.1 < chunk.0.len() {
let chunk_read = self.read_current_chunk(buf);
tokio::pin!(chunk_read);
let _ = chunk_read.as_mut().as_mut().poll(ctx);
ctx.waker().wake_by_ref();
Poll::Pending
}
else {
let old_chunk = self.current.take().unwrap();
self._garbage.push(old_chunk);
ctx.waker().wake_by_ref();
Poll::Pending
}
}
None => {
if self._dead {
return Poll::Ready(Ok(()));
}
let max_garbage = size_commonbuf_t::<L>();
let (start, end) = match (self._garbage.len(), max_garbage) {
(len, max) if max - 32 > 0 => (max - 32, len),
(len, max) if max - 16 > 0 => (max - 16, len),
(len, max) if max - 4 > 0 => (max - 4, len),
(len, max) => (max, len),
};
if start > 0 && start < end {
for _ in start..end {
self._garbage.pop();
}
}
let source_poll = self.next_chunk();
tokio::pin!(source_poll);
let _ = source_poll.as_mut().as_mut().poll(ctx);
ctx.waker().wake_by_ref();
Poll::Pending
}
}
}
}