use super::{Error, Header};
use std::io;
const MAX_MESSAGE_SIZE_IN_BYTES: usize =
u32::MAX as usize - Header::LENGTH_IN_BYTES;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Frame<T> {
encoding_type: u16,
payload: T,
}
impl<T> Frame<T>
where
T: AsRef<[u8]>,
{
pub fn new(encoding_type: u16, payload: T) -> Self {
assert!(payload.as_ref().len() <= MAX_MESSAGE_SIZE_IN_BYTES);
Frame { encoding_type, payload }
}
pub fn encoding_type(&self) -> u16 {
self.encoding_type
}
pub fn payload(&self) -> &T {
&self.payload
}
pub fn payload_mut(&mut self) -> &mut T {
&mut self.payload
}
pub fn serialize<W>(&self, writer: &mut W) -> io::Result<usize>
where
W: io::Write,
{
let nominal_message_length_in_bytes =
self.payload().as_ref().len() + Header::LENGTH_IN_BYTES;
let header = Header {
nominal_message_length_in_bytes,
encoding_type: self.encoding_type,
};
writer.write_all(&header.to_bytes())?;
writer.write_all(self.payload().as_ref())?;
Ok(nominal_message_length_in_bytes)
}
}
impl<'a> Frame<&'a [u8]> {
pub fn deserialize(data: &'a [u8]) -> Result<Self, Error> {
let header = Header::from_bytes(data)?;
Ok(Frame::new(
header.encoding_type,
&data[Header::LENGTH_IN_BYTES
..header.nominal_message_length_in_bytes],
))
}
}
#[cfg(test)]
mod test {
use super::*;
use quickcheck::QuickCheck;
#[test]
fn information_retrieval_is_consistent_with_new() {
let frame = Frame::new(0x0, b"" as &[u8]);
assert_eq!(frame.encoding_type(), 0x0);
assert_eq!(frame.payload(), &[]);
let frame = Frame::new(0x1122, b"foobar" as &[u8]);
assert_eq!(frame.encoding_type(), 0x1122);
assert_eq!(frame.payload(), &&b"foobar"[..]);
let frame = Frame::new(u16::MAX, &[0u8] as &[u8]);
assert_eq!(frame.encoding_type(), u16::MAX);
assert_eq!(frame.payload(), &[0]);
}
#[test]
#[should_panic]
fn new_with_size_u32_max() {
let data = vec![0; u32::MAX as usize];
Frame::new(0x0, &data[..]);
}
#[test]
#[should_panic]
fn new_with_size_u32_max_minus_5() {
let data = vec![0; u32::MAX as usize - 5];
Frame::new(0x0, &data[..]);
}
#[test]
fn new_with_size_u32_max_minus_6() {
let data = vec![0; u32::MAX as usize - 6];
Frame::new(0x0, &data[..]);
}
#[test]
fn new_with_size_u32_max_minus_7() {
let data = vec![0; u32::MAX as usize - 7];
Frame::new(0x0, &data[..]);
}
#[test]
fn decode_incomplete_header() {
assert!(matches!(
Frame::<&[u8]>::deserialize(&[]),
Err(Error::Incomplete { needed: 6 })
));
assert!(matches!(
Frame::<&[u8]>::deserialize(&[0, 0, 0]),
Err(Error::Incomplete { needed: 3 })
));
assert!(matches!(
Frame::<&[u8]>::deserialize(&[0, 0, 0, 0, 0]),
Err(Error::Incomplete { needed: 1 })
));
}
#[test]
fn decode_empty_message() {
let frame = Frame::<&[u8]>::deserialize(&[0, 0, 0, 6, 0, 0]).unwrap();
assert_eq!(frame.encoding_type(), 0);
assert_eq!(frame.payload(), &[]);
}
#[test]
fn encode_then_decode_should_have_no_effect() {
fn prop(encoding_type: u16, payload: Vec<u8>) -> bool {
let frame = Frame::<&[u8]>::new(encoding_type, &payload[..]);
let mut buffer = vec![];
frame.serialize(&mut buffer).unwrap();
let frame_decoded =
Frame::<&[u8]>::deserialize(&buffer[..]).unwrap();
frame_decoded.encoding_type() == encoding_type
&& frame_decoded.payload() == &&payload[..]
}
QuickCheck::new().quickcheck(prop as fn(u16, Vec<u8>) -> bool)
}
}