mod reader;
mod status;
use crate::*;
use payload::*;
pub use reader::*;
pub use status::*;
pub trait ReadFrom {
fn read<R: std::io::Read, S: ProtocolSchema>(buf: &mut R) -> Result<Self, Error>
where
Self: Sized;
}
pub trait ReadBlockFromSlice {
fn read_from_slice<'a>(buf: &'a [u8], skip_sig: bool) -> Result<Self, Error>
where
Self: 'a + Sized;
}
pub trait ReadBlockFrom {
fn read<T: std::io::Read>(buf: &mut T, skip_sig: bool) -> Result<Self, Error>
where
Self: Sized;
}
pub trait ReadPayloadFrom<
T: Sized + PayloadDecode<T> + PayloadHooks + StaticPayloadSignature + PayloadCrc,
>
{
fn read<B: std::io::Read>(
buf: &mut B,
header: &PayloadHeader,
ctx: &mut T::Context<'_>,
) -> Result<T, Error>
where
Self: Sized + PayloadDecode<Self> + PayloadHooks + StaticPayloadSignature,
{
if header.sig != T::ssig() {
return Err(Error::SignatureDismatch(Unrecognized::payload(
header.sig.as_slice().to_vec(),
)));
}
let mut bytes = vec![0u8; header.payload_len()];
buf.read_exact(&mut bytes)?;
let mut hasher = crc32fast::Hasher::new();
hasher.update(&bytes);
let crc = ByteBlock::Len4(hasher.finalize().to_le_bytes());
if header.crc != crc {
return Err(Error::CrcDismatch);
}
let value = T::decode(&bytes, ctx)?;
Ok(value)
}
}
pub trait ReadPacketFrom: ProtocolSchema {
fn read<T: std::io::Read>(
buf: &mut T,
ctx: &mut <Self as ProtocolSchema>::Context<'_>,
) -> Result<Self, Error>
where
Self: Sized;
}
pub trait ExtractPayloadFrom<T: Sized> {
fn read<B: std::io::Read>(
buf: &mut B,
header: &PayloadHeader,
ctx: &mut T::Context<'_>,
) -> Result<T, Error>
where
T: ProtocolSchema;
}
pub trait TryReadPayloadFrom<
T: Sized
+ PayloadDecode<T>
+ PayloadHooks
+ StaticPayloadSignature
+ PayloadCrc
+ ReadPayloadFrom<T>,
>
{
fn try_read<B: std::io::Read + std::io::Seek>(
buf: &mut B,
header: &PayloadHeader,
ctx: &mut T::Context<'_>,
) -> Result<ReadStatus<T>, Error> {
let start_pos = buf.stream_position()?;
let len = buf.seek(std::io::SeekFrom::End(0))? - start_pos;
buf.seek(std::io::SeekFrom::Start(start_pos))?;
if len < header.payload_len() as u64 {
return Ok(ReadStatus::NotEnoughData(header.payload_len() as u64 - len));
}
<T as ReadPayloadFrom<T>>::read(buf, header, ctx).map(ReadStatus::Success)
}
}
pub trait TryExtractPayloadFrom<T: Sized> {
fn try_read<B: std::io::Read + std::io::Seek>(
buf: &mut B,
header: &PayloadHeader,
ctx: &mut T::Context<'_>,
) -> Result<ReadStatus<T>, Error>
where
T: ProtocolSchema;
}
pub trait TryReadPacketFrom: ProtocolSchema {
fn try_read<T: std::io::Read + std::io::Seek>(
buf: &mut T,
ctx: &mut <Self as ProtocolSchema>::Context<'_>,
) -> Result<PacketReadStatus<Self>, Error>
where
Self: Sized;
}
pub trait TryReadPayloadFromBuffered<
T: Sized
+ PayloadDecode<T>
+ PayloadHooks
+ StaticPayloadSignature
+ PayloadCrc
+ ReadPayloadFrom<T>,
>
{
fn try_read<B: std::io::BufRead>(
buf: &mut B,
header: &PayloadHeader,
ctx: &mut T::Context<'_>,
) -> Result<ReadStatus<T>, Error> {
<T as ReadPayloadFrom<T>>::read(buf, header, ctx).map(ReadStatus::Success)
}
}
pub trait TryExtractPayloadFromBuffered<T: Sized> {
fn try_read<B: std::io::BufRead>(
buf: &mut B,
header: &PayloadHeader,
ctx: &mut T::Context<'_>,
) -> Result<ReadStatus<T>, Error>
where
T: ProtocolSchema;
}
pub trait TryReadPacketFromBuffered: ProtocolSchema {
fn try_read<T: std::io::BufRead>(
buf: &mut T,
ctx: &mut <Self as ProtocolSchema>::Context<'_>,
) -> Result<PacketReadStatus<Self>, Error>
where
Self: Sized;
}
pub trait TryReadFrom {
fn try_read<T: std::io::Read + std::io::Seek, S: ProtocolSchema>(
buf: &mut T,
) -> Result<ReadStatus<Self>, Error>
where
Self: Sized;
}
pub trait TryReadFromBuffered {
fn try_read<T: std::io::BufRead, S: ProtocolSchema>(
buf: &mut T,
) -> Result<ReadStatus<Self>, Error>
where
Self: Sized;
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[derive(Debug)]
struct LimitedPayload;
impl ProtocolSchema for LimitedPayload {
type Context<'a> = ();
const MAX_PAYLOAD_LEN: u32 = 2;
}
impl PayloadHooks for LimitedPayload {}
impl PayloadEncode for LimitedPayload {
fn encode(&self, _: &mut Self::Context<'_>) -> std::io::Result<Vec<u8>> {
Ok(Vec::new())
}
}
impl PayloadEncodeReferred for LimitedPayload {
fn encode(&self, _: &mut Self::Context<'_>) -> std::io::Result<Option<&[u8]>> {
Ok(Some(&[]))
}
}
impl StaticPayloadSignature for LimitedPayload {
fn ssig() -> ByteBlock {
ByteBlock::Len4(*b"LIMT")
}
}
impl PayloadCrc for LimitedPayload {}
impl PayloadDecode<LimitedPayload> for LimitedPayload {
fn decode(_: &[u8], _: &mut Self::Context<'_>) -> std::io::Result<LimitedPayload> {
Ok(LimitedPayload)
}
}
impl ReadPayloadFrom<LimitedPayload> for LimitedPayload {}
#[test]
fn payload_header_read_rejects_len_larger_than_schema_max_before_allocation() {
let header = PayloadHeader {
sig: ByteBlock::Len4(*b"LIMT"),
crc: ByteBlock::Len4([0; 4]),
len: 3,
};
let mut input = Cursor::new(header.as_vec());
let err = match PayloadHeader::read::<_, LimitedPayload>(&mut input) {
Ok(_) => panic!("payload header length must exceed schema max"),
Err(err) => err,
};
assert!(matches!(err, Error::InvalidLength));
}
}