use std::io::Read;
use crate::*;
#[derive(Debug, Clone, Copy)]
pub struct Header {
pub kind: FourCC,
pub size: Option<usize>,
}
impl Encode for Header {
fn encode(&self, buf: &mut BytesMut) -> Result<()> {
match self.size.map(|size| size + 8) {
Some(size) if size > u32::MAX as usize => {
1u32.encode(buf)?;
self.kind.encode(buf)?;
((size + 8) as u64).encode(buf)
}
Some(size) => {
(size as u32).encode(buf)?;
self.kind.encode(buf)
}
None => {
0u32.encode(buf)?;
self.kind.encode(buf)
}
}
}
}
impl Decode for Header {
fn decode(buf: &mut Bytes) -> Result<Self> {
let size = u32::decode(buf)?;
let kind = FourCC::decode(buf)?;
let size = match size {
0 => None,
1 => {
let size = u64::decode(buf)?;
Some(size.checked_sub(16).ok_or(Error::InvalidSize)? as usize)
}
_ => Some(size.checked_sub(8).ok_or(Error::InvalidSize)? as usize),
};
Ok(Self { kind, size })
}
}
impl ReadFrom for Header {
fn read_from<R: Read>(r: &mut R) -> Result<Self> {
<Option<Header> as ReadFrom>::read_from(r)?.ok_or(Error::UnexpectedEof)
}
}
impl ReadFrom for Option<Header> {
fn read_from<R: Read>(r: &mut R) -> Result<Self> {
let mut buf = [0u8; 8];
let n = r.read(&mut buf)?;
if n == 0 {
return Ok(None);
}
r.read_exact(&mut buf[n..])?;
let size = u32::from_be_bytes(buf[0..4].try_into().unwrap());
let kind = u32::from_be_bytes(buf[4..8].try_into().unwrap()).into();
let size = match size {
0 => None,
1 => {
r.read_exact(&mut buf)?;
let size = u64::from_be_bytes(buf);
let size = size.checked_sub(16).ok_or(Error::InvalidSize)?;
Some(size as usize)
}
_ => Some(size.checked_sub(8).ok_or(Error::InvalidSize)? as usize),
};
Ok(Some(Header { kind, size }))
}
}
#[cfg(feature = "tokio")]
impl AsyncReadFrom for Header {
async fn read_from<R: tokio::io::AsyncRead + Unpin>(r: &mut R) -> Result<Self> {
<Option<Header> as AsyncReadFrom>::read_from(r)
.await?
.ok_or(Error::UnexpectedEof)
}
}
#[cfg(feature = "tokio")]
impl AsyncReadFrom for Option<Header> {
async fn read_from<R: tokio::io::AsyncRead + Unpin>(r: &mut R) -> Result<Self> {
use tokio::io::AsyncReadExt;
let mut buf = [0u8; 8];
let n = r.read(&mut buf).await?;
if n == 0 {
return Ok(None);
}
r.read_exact(&mut buf[n..]).await?;
let size = u32::from_be_bytes(buf[0..4].try_into().unwrap());
let kind = u32::from_be_bytes(buf[4..8].try_into().unwrap()).into();
let size = match size {
0 => None,
1 => {
r.read_exact(&mut buf).await?;
let size = u64::from_be_bytes(buf);
let size = size.checked_sub(16).ok_or(Error::InvalidSize)?;
Some(size as usize)
}
_ => Some(size.checked_sub(8).ok_or(Error::InvalidSize)? as usize),
};
Ok(Some(Header { kind, size }))
}
}
impl Header {
pub(crate) fn read_body<R: Read>(&self, r: &mut R) -> Result<Bytes> {
let cap = self.size.unwrap_or(0).max(4096);
let mut buf = BytesMut::with_capacity(cap).writer();
match self.size {
Some(size) => {
let n = std::io::copy(&mut r.take(size as _), &mut buf)? as _;
if size != n {
return Err(Error::OutOfBounds);
}
}
None => {
std::io::copy(r, &mut buf)?;
}
};
Ok(buf.into_inner().freeze())
}
#[cfg(feature = "tokio")]
pub(crate) async fn read_body_tokio<R: tokio::io::AsyncRead + Unpin>(
&self,
r: &mut R,
) -> Result<Bytes> {
use tokio::io::AsyncReadExt;
let cap = self.size.unwrap_or(0).max(4096);
let mut buf = Vec::with_capacity(cap);
match self.size {
Some(size) => {
let n = tokio::io::copy(&mut r.take(size as _), &mut buf).await? as _;
if size != n {
return Err(Error::OutOfBounds);
}
}
None => {
tokio::io::copy(r, &mut buf).await?;
}
};
Ok(buf.into())
}
}