use std::io;
use tokio::io::{AsyncRead, AsyncReadExt};
use crate::Schema;
use crate::io::Len;
use crate::wire::io::framing::{body_len, decode_frame};
const STEP: usize = 64 << 10;
#[derive(Debug, Default)]
pub struct FrameReader {
header: [u8; size_of::<Len>()],
header_len: usize,
expect: Option<usize>,
body: Vec<u8>,
filled: usize,
}
impl FrameReader {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub async fn read_bytes(
&mut self,
r: &mut (impl AsyncRead + Unpin),
) -> io::Result<Option<&[u8]>> {
let len = match self.expect {
Some(len) => len,
None => match self.read_header(r).await? {
Some(len) => len,
None => return Ok(None),
},
};
self.read_body(r, len).await?;
self.header_len = 0;
self.expect = None;
self.filled = 0;
Ok(Some(&self.body[..len]))
}
pub async fn read<T: Schema>(
&mut self,
r: &mut (impl AsyncRead + Unpin),
) -> io::Result<Option<T>> {
self.read_bytes(r).await?.map(decode_frame).transpose()
}
pub(super) async fn read_owned(
mut self,
r: &mut (impl AsyncRead + Unpin),
) -> io::Result<Option<Vec<u8>>> {
let Some(len) = self.read_bytes(r).await?.map(<[u8]>::len) else {
return Ok(None);
};
self.body.truncate(len);
Ok(Some(self.body))
}
async fn read_header(&mut self, r: &mut (impl AsyncRead + Unpin)) -> io::Result<Option<usize>> {
while self.header_len < self.header.len() {
let n = r.read(&mut self.header[self.header_len..]).await?;
if n == 0 {
return if self.header_len == 0 {
Ok(None)
} else {
Err(io::ErrorKind::UnexpectedEof.into())
};
}
self.header_len += n;
}
let len = body_len(Len::from_le_bytes(self.header))?;
self.expect = Some(len);
Ok(Some(len))
}
async fn read_body(&mut self, r: &mut (impl AsyncRead + Unpin), len: usize) -> io::Result<()> {
while self.filled < len {
let target = len.min(self.filled + STEP.max(self.filled));
if self.body.len() < target {
self.body.resize(target, 0);
}
let end = self.body.len().min(len);
let n = r.read(&mut self.body[self.filled..end]).await?;
if n == 0 {
return Err(io::ErrorKind::UnexpectedEof.into());
}
self.filled += n;
}
Ok(())
}
}