use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use std::task::ready;
use bytes::BytesMut;
use futures::AsyncRead;
use futures::Stream;
use pin_project_lite::pin_project;
use vortex_error::VortexResult;
use vortex_error::vortex_err;
use crate::messages::DecoderMessage;
use crate::messages::MessageDecoder;
use crate::messages::PollRead;
pin_project! {
pub struct AsyncMessageReader<R> {
#[pin]
read: R,
buffer: BytesMut,
decoder: MessageDecoder,
state: ReadState,
}
}
impl<R> AsyncMessageReader<R> {
pub fn new(read: R) -> Self {
AsyncMessageReader {
read,
buffer: BytesMut::new(),
decoder: MessageDecoder::default(),
state: ReadState::default(),
}
}
}
#[derive(Default)]
enum ReadState {
#[default]
AwaitingDecoder,
Filling {
total_bytes_read: usize,
},
}
enum FillResult {
Filled,
Pending,
Eof,
}
fn poll_fill_buffer<R: AsyncRead>(
read: Pin<&mut R>,
buffer: &mut [u8],
total_bytes_read: &mut usize,
cx: &mut Context<'_>,
) -> Poll<VortexResult<FillResult>> {
let unfilled = &mut buffer[*total_bytes_read..];
let bytes_read = ready!(read.poll_read(cx, unfilled))?;
Poll::Ready(if bytes_read == 0 {
if *total_bytes_read > 0 {
Err(vortex_err!(
"unexpected EOF during partial read: read {total_bytes_read} of {} expected bytes",
buffer.len()
))
} else {
Ok(FillResult::Eof)
}
} else {
*total_bytes_read += bytes_read;
if *total_bytes_read == buffer.len() {
Ok(FillResult::Filled)
} else {
debug_assert!(*total_bytes_read < buffer.len());
Ok(FillResult::Pending)
}
})
}
impl<R: AsyncRead> Stream for AsyncMessageReader<R> {
type Item = VortexResult<DecoderMessage>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.project();
loop {
match this.state {
ReadState::AwaitingDecoder => match this.decoder.read_next(this.buffer)? {
PollRead::Some(msg) => return Poll::Ready(Some(Ok(msg))),
PollRead::NeedMore(new_len) => {
this.buffer.resize(new_len, 0x00);
*this.state = ReadState::Filling {
total_bytes_read: 0,
};
}
},
ReadState::Filling { total_bytes_read } => {
match ready!(poll_fill_buffer(
this.read.as_mut(),
this.buffer,
total_bytes_read,
cx
)) {
Err(e) => return Poll::Ready(Some(Err(e))),
Ok(FillResult::Eof) => return Poll::Ready(None),
Ok(FillResult::Filled) => *this.state = ReadState::AwaitingDecoder,
Ok(FillResult::Pending) => {}
}
}
}
}
}
}