use std::io::{Read, Seek, Write};
pub trait PipelineSource: Read {}
impl<T: Read> PipelineSource for T {}
pub trait PipelineSink: Write {}
impl<T: Write> PipelineSink for T {}
pub trait PipelineSeekSource: PipelineSource + Seek {}
impl<T: Read + Seek> PipelineSeekSource for T {}
#[cfg(feature = "async")]
pub use async_io::*;
#[cfg(feature = "async")]
mod async_io {
use std::io::{ErrorKind, Read, Write};
use futures_lite::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::{constants::SLICE_LEN, error::CarbonadoError};
pub trait AsyncPipelineSource: AsyncRead {}
impl<T: AsyncRead + ?Sized> AsyncPipelineSource for T {}
pub trait AsyncPipelineSink: AsyncWrite {}
impl<T: AsyncWrite + ?Sized> AsyncPipelineSink for T {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BoundedCopyTruncation {
EncodedBody,
FecBody,
}
impl BoundedCopyTruncation {
pub(crate) fn message(self) -> &'static str {
match self {
Self::EncodedBody => "truncated encoded body",
Self::FecBody => "truncated FEC body",
}
}
}
const COPY_BUF: usize = SLICE_LEN as usize;
pub async fn async_copy_bounded<R, W>(
reader: &mut R,
writer: &mut W,
limit: Option<u64>,
truncation: BoundedCopyTruncation,
) -> Result<u64, CarbonadoError>
where
R: AsyncRead + Unpin,
W: Write,
{
let mut buf = [0u8; COPY_BUF];
let mut total = 0u64;
let max = limit.unwrap_or(u64::MAX);
while total < max {
let cap = buf.len().min((max - total) as usize);
let n = reader
.read(&mut buf[..cap])
.await
.map_err(CarbonadoError::StdIoError)?;
if n == 0 {
if limit.is_some() {
return Err(CarbonadoError::StdIoError(std::io::Error::new(
ErrorKind::UnexpectedEof,
truncation.message(),
)));
}
break;
}
writer
.write_all(&buf[..n])
.map_err(CarbonadoError::StdIoError)?;
total += n as u64;
}
Ok(total)
}
pub async fn async_copy_all<R, W>(reader: &mut R, writer: &mut W) -> Result<u64, CarbonadoError>
where
R: Read,
W: AsyncWrite + Unpin,
{
let mut buf = [0u8; COPY_BUF];
let mut total = 0u64;
loop {
let n = reader.read(&mut buf).map_err(CarbonadoError::StdIoError)?;
if n == 0 {
break;
}
writer
.write_all(&buf[..n])
.await
.map_err(CarbonadoError::StdIoError)?;
total += n as u64;
}
writer.flush().await.map_err(CarbonadoError::StdIoError)?;
Ok(total)
}
pub async fn async_reject_trailing<R>(
reader: &mut R,
declared: u64,
) -> Result<(), CarbonadoError>
where
R: AsyncRead + Unpin,
{
let mut extra = [0u8; 1];
match reader.read(&mut extra).await {
Ok(0) => Ok(()),
Ok(_) => Err(CarbonadoError::EncodedBodyExceedsDeclaredLength { declared }),
Err(e) => Err(CarbonadoError::StdIoError(e)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bounded_copy_truncation_messages_match_sync_decode() {
assert_eq!(
BoundedCopyTruncation::EncodedBody.message(),
"truncated encoded body"
);
assert_eq!(
BoundedCopyTruncation::FecBody.message(),
"truncated FEC body"
);
}
}
}