use crate::{
errors::{EmptyPacket, EncodeError, InsufficientCapacity},
utils,
};
use arrayvec::ArrayVec;
use byteorder::{ByteOrder, LE};
#[cfg(feature = "std")]
use std::io::{self, Write};
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Packet {
id: u8,
buffer: ArrayVec<[u8; Packet::MAX_PACKET_SIZE + 1]>,
}
impl Packet {
pub const MAX_PACKET_SIZE: usize = 255;
pub fn new(id: u8) -> Packet {
Packet {
id,
..Default::default()
}
}
pub fn with_data(
id: u8,
content: &[u8],
) -> Result<Packet, InsufficientCapacity> {
let mut pkt = Packet::new(id);
pkt.push_data(content)?;
Ok(pkt)
}
#[inline]
pub fn id(&self) -> u8 { self.id }
#[inline]
pub fn contents(&self) -> &[u8] { &self.buffer }
#[inline]
pub fn len(&self) -> usize { self.buffer.len() }
#[inline]
pub fn is_empty(&self) -> bool { self.buffer.is_empty() }
#[inline]
pub fn remaining_capacity(&self) -> usize {
self.buffer.capacity() - self.buffer.len() - 1
}
pub(crate) fn header(&self) -> Header {
Header {
id: self.id,
len: self.contents().len() as u8,
crc: utils::calculate_crc16(self.contents()),
}
}
#[inline]
pub fn total_length(&self) -> usize { self.contents().len() + Header::LEN }
pub fn write_to_buffer(
&self,
buffer: &mut [u8],
) -> Result<usize, EncodeError> {
if self.is_empty() {
return Err(EncodeError::EmptyPacket(EmptyPacket));
}
let header = self.header().to_bytes();
let body = self.contents();
if buffer.len() < self.total_length() {
let err = InsufficientCapacity {
required: self.total_length(),
actual: buffer.len(),
};
return Err(EncodeError::InsufficientCapacity(err));
}
buffer[..header.len()].copy_from_slice(&header);
let rest = &mut buffer[header.len()..];
rest[..body.len()].copy_from_slice(&body);
Ok(self.total_length())
}
#[cfg(feature = "std")]
pub fn write_to<W: Write>(&self, mut writer: W) -> io::Result<()> {
let header = self.header().to_bytes();
writer.write(&header)?;
writer.write(self.contents())?;
Ok(())
}
pub fn push_data(
&mut self,
data: &[u8],
) -> Result<(), InsufficientCapacity> {
if self.remaining_capacity() < data.len() {
Err(InsufficientCapacity {
required: data.len(),
actual: self.remaining_capacity(),
})
} else {
for &byte in data {
self.buffer.push(byte);
}
Ok(())
}
}
}
#[cfg(feature = "std")]
impl Write for Packet {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
match self.push_data(data) {
Ok(_) => Ok(data.len()),
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
}
}
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct Header {
pub id: u8,
pub len: u8,
pub crc: u16,
}
impl Header {
pub const LEN: usize = 5;
pub fn from_bytes(data: &[u8]) -> Option<Header> {
if data.len() < Self::LEN {
return None;
}
let crc = LE::read_u16(&data[3..]);
Some(Header {
id: data[1],
len: data[2],
crc,
})
}
pub fn to_bytes(self) -> [u8; Self::LEN] {
let mut bytes = [0; Self::LEN];
bytes[1] = self.id;
bytes[2] = self.len;
LE::write_u16(&mut bytes[3..5], self.crc);
bytes[0] = utils::calculate_header_lrc(&bytes[1..]);
bytes
}
pub fn is_valid(header: &[u8]) -> bool {
if header.len() != Self::LEN {
return false;
}
let lrc = header[0];
let rest = &header[1..];
let lrc_should_be = utils::calculate_header_lrc(rest);
lrc == lrc_should_be
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_some_data_to_the_packet() {
let mut pkt = Packet::new(5);
assert_eq!(pkt.len(), 0);
let random_data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
pkt.push_data(random_data).unwrap();
assert_eq!(pkt.len(), random_data.len());
}
#[test]
fn encoding_empty_packets_is_an_error() {
let pkt = Packet::new(5);
let mut buffer = vec![0; pkt.total_length()];
let err = pkt.write_to_buffer(&mut buffer[..]).unwrap_err();
assert_eq!(err, EncodeError::EmptyPacket(EmptyPacket));
}
#[test]
fn calculate_a_header() {
let mut pkt = Packet::new(42);
let random_data = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let crc = utils::calculate_crc16(random_data);
pkt.push_data(random_data).unwrap();
let got = pkt.header();
assert_eq!(got.id, 42);
assert_eq!(got.len, random_data.len() as u8);
assert_eq!(got.crc, crc);
}
#[test]
fn parse_a_header_from_the_c_implementation() {
let header_from_c_implementation = [0x56, 0x2a, 0xe, 0xb, 0x67];
let crc_should_be = 0x670b;
let decoded =
Header::from_bytes(&header_from_c_implementation).unwrap();
let encoded = decoded.to_bytes();
assert_eq!(encoded, header_from_c_implementation);
assert_eq!(decoded.crc, crc_should_be);
}
#[test]
fn push_with_overflow() {
let mut pkt = Packet::new(42);
let data = [0; Packet::MAX_PACKET_SIZE + 1];
let should_be = InsufficientCapacity {
required: data.len(),
actual: Packet::MAX_PACKET_SIZE,
};
let err = pkt.push_data(&data).unwrap_err();
assert_eq!(err, should_be);
assert_eq!(pkt.len(), 0, "Nothing should have been written");
}
#[test]
#[cfg(feature = "std")]
fn write_to_and_write_to_buffer_are_identical() {
let pkt = Packet::with_data(42, b"Something really important").unwrap();
let mut first = vec![0; pkt.total_length()];
let mut second = Vec::new();
pkt.write_to_buffer(&mut first).unwrap();
pkt.write_to(&mut second).unwrap();
assert_eq!(first, second);
}
}