use crate::payload::{NextChunk, PayloadHeader, SafeHeaderReader};
use crate::*;
impl ReadFrom for PayloadHeader {
fn read<T: std::io::Read>(buf: &mut T) -> Result<Self, Error> {
let mut sig_len = [0u8; 1];
buf.read_exact(&mut sig_len)?;
let sig_len = sig_len[0];
ByteBlock::is_valid_capacity(sig_len)?;
let mut sig = vec![0u8; sig_len as usize];
buf.read_exact(&mut sig)?;
let mut crc_len = [0u8; 1];
buf.read_exact(&mut crc_len)?;
let crc_len = crc_len[0];
ByteBlock::is_valid_capacity(crc_len)?;
let mut crc = vec![0u8; crc_len as usize];
buf.read_exact(&mut crc)?;
let mut len = [0u8; 4];
buf.read_exact(&mut len)?;
Ok(Self {
crc: crc.try_into()?,
len: u32::from_le_bytes(len),
sig: sig.try_into()?,
})
}
}
impl TryReadFrom for PayloadHeader {
fn try_read<T: std::io::Read + std::io::Seek>(buf: &mut T) -> Result<ReadStatus<Self>, Error> {
let mut reader = SafeHeaderReader::new(buf)?;
let sig_len = match reader.next_u8()? {
NextChunk::NotEnoughData(n) => return Ok(ReadStatus::NotEnoughData(n)),
NextChunk::U8(v) => v,
_ => return Err(Error::FailToReadPayloadHeader),
};
ByteBlock::is_valid_capacity(sig_len)?;
let sig = match reader.next_bytes(sig_len as u64)? {
NextChunk::NotEnoughData(n) => return Ok(ReadStatus::NotEnoughData(n)),
NextChunk::Bytes(v) => v,
_ => return Err(Error::FailToReadPayloadHeader),
};
let crc_len = match reader.next_u8()? {
NextChunk::NotEnoughData(n) => return Ok(ReadStatus::NotEnoughData(n)),
NextChunk::U8(v) => v,
_ => return Err(Error::FailToReadPayloadHeader),
};
ByteBlock::is_valid_capacity(crc_len)?;
let crc = match reader.next_bytes(crc_len as u64)? {
NextChunk::NotEnoughData(n) => return Ok(ReadStatus::NotEnoughData(n)),
NextChunk::Bytes(v) => v,
_ => return Err(Error::FailToReadPayloadHeader),
};
let len = match reader.next_u32()? {
NextChunk::NotEnoughData(n) => return Ok(ReadStatus::NotEnoughData(n)),
NextChunk::U32(v) => v,
_ => return Err(Error::FailToReadPayloadHeader),
};
Ok(ReadStatus::Success(Self {
crc: crc.try_into()?,
len,
sig: sig.try_into()?,
}))
}
}
impl TryReadFromBuffered for PayloadHeader {
fn try_read<T: std::io::BufRead>(reader: &mut T) -> Result<ReadStatus<Self>, Error> {
fn ensure_available(buffer: &[u8], required: usize) -> Option<ReadStatus<PayloadHeader>> {
if buffer.len() < required {
Some(ReadStatus::NotEnoughData((required - buffer.len()) as u64))
} else {
None
}
}
let buffer = reader.fill_buf()?;
let mut required = 1; if let Some(rs) = ensure_available(buffer, required) {
return Ok(rs);
}
let sig_len = buffer[required - 1];
ByteBlock::is_valid_capacity(sig_len)?;
required += sig_len as usize; if let Some(rs) = ensure_available(buffer, required) {
return Ok(rs);
}
required += 1; if let Some(rs) = ensure_available(buffer, required) {
return Ok(rs);
}
let crc_len = buffer[required - 1];
ByteBlock::is_valid_capacity(crc_len)?;
required += crc_len as usize; if let Some(rs) = ensure_available(buffer, required) {
return Ok(rs);
}
required += 4; if let Some(rs) = ensure_available(buffer, required) {
return Ok(rs);
}
let header = PayloadHeader::read(&mut std::io::Cursor::new(buffer))?;
Ok(ReadStatus::Success(header))
}
}
#[cfg(test)]
mod tests {
use crate::{
ByteBlock, Error, PayloadHeader, ReadFrom, ReadStatus, TryReadFrom, TryReadFromBuffered,
};
use std::io::{BufReader, Cursor, Seek};
fn sample_header_bytes() -> Vec<u8> {
PayloadHeader {
sig: ByteBlock::Len4(*b"ABCD"),
crc: ByteBlock::Len4([9, 8, 7, 6]),
len: 1234,
}
.as_vec()
}
#[test]
fn read_and_try_read_success() {
let bytes = sample_header_bytes();
let mut cursor = Cursor::new(bytes.clone());
let header = PayloadHeader::read(&mut cursor).expect("read header");
assert_eq!(header.payload_len(), 1234);
assert_eq!(header.sig.as_slice(), b"ABCD");
assert_eq!(header.crc.as_slice(), &[9, 8, 7, 6]);
let mut cursor = Cursor::new(bytes);
match <PayloadHeader as TryReadFrom>::try_read(&mut cursor).expect("try_read") {
ReadStatus::Success(header) => {
assert_eq!(header.payload_len(), 1234);
assert_eq!(header.sig.as_slice(), b"ABCD");
}
ReadStatus::NotEnoughData(_) => panic!("expected Success"),
}
}
#[test]
fn read_and_try_read_detect_invalid_capacity() {
let bad_sig_len = vec![3, 1, 2, 3, 4, 0, 0, 0, 0];
let mut cursor = Cursor::new(bad_sig_len.clone());
assert!(matches!(
PayloadHeader::read(&mut cursor),
Err(Error::InvalidCapacity(_, _))
));
let mut cursor = Cursor::new(bad_sig_len);
assert!(matches!(
<PayloadHeader as TryReadFrom>::try_read(&mut cursor),
Err(Error::InvalidCapacity(_, _))
));
}
#[test]
fn try_read_and_buffered_try_read_not_enough_keep_stream_position() {
let bytes = sample_header_bytes();
let short = bytes[..2].to_vec();
let mut cursor = Cursor::new(short.clone());
match <PayloadHeader as TryReadFrom>::try_read(&mut cursor).expect("try_read short") {
ReadStatus::NotEnoughData(need) => assert!(need > 0),
ReadStatus::Success(_) => panic!("expected NotEnoughData"),
}
assert_eq!(cursor.stream_position().expect("pos"), 0);
let mut reader = BufReader::new(Cursor::new(short));
match <PayloadHeader as TryReadFromBuffered>::try_read(&mut reader)
.expect("buffered try_read short")
{
ReadStatus::NotEnoughData(need) => assert!(need > 0),
ReadStatus::Success(_) => panic!("expected NotEnoughData"),
}
let mut reader = BufReader::new(Cursor::new(bytes));
match <PayloadHeader as TryReadFromBuffered>::try_read(&mut reader)
.expect("buffered try_read full")
{
ReadStatus::Success(header) => {
assert_eq!(header.payload_len(), 1234);
assert_eq!(header.sig.as_slice(), b"ABCD");
}
ReadStatus::NotEnoughData(_) => panic!("expected Success"),
}
}
}