anpp 2.0.0

A Rust implementation of the Advanced Navigation Packet Protocol
Documentation
use crate::{
    errors::{EmptyPacket, EncodeError, InsufficientCapacity},
    utils,
};
use arrayvec::ArrayVec;
use byteorder::{ByteOrder, LE};
#[cfg(feature = "std")]
use std::io::{self, Write};

/// An arbitrary chunk of bytes with a user-defined ID.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Packet {
    id: u8,
    // We need the `+1` because `Clone` is only implemented for powers of two
    // (e.g. `ArrayVec<[u8; 256]>`).
    buffer: ArrayVec<[u8; Packet::MAX_PACKET_SIZE + 1]>,
}

impl Packet {
    /// The maximum size a packet can be.
    pub const MAX_PACKET_SIZE: usize = 255;

    /// Create a new empty `Packet`.
    pub fn new(id: u8) -> Packet {
        Packet {
            id,
            ..Default::default()
        }
    }

    /// Create a `Packet` already filled with data.
    pub fn with_data(
        id: u8,
        content: &[u8],
    ) -> Result<Packet, InsufficientCapacity> {
        let mut pkt = Packet::new(id);
        pkt.push_data(content)?;
        Ok(pkt)
    }

    /// Get the `Packet`'s identifier.
    #[inline]
    pub fn id(&self) -> u8 { self.id }

    /// Get the contents of the `Packet`.
    #[inline]
    pub fn contents(&self) -> &[u8] { &self.buffer }

    /// How long is the `Packet`'s contents?
    #[inline]
    pub fn len(&self) -> usize { self.buffer.len() }

    /// Is this `Packet` empty?
    #[inline]
    pub fn is_empty(&self) -> bool { self.buffer.is_empty() }

    /// How many more bytes can be added to this `Packet` before it is full?
    #[inline]
    pub fn remaining_capacity(&self) -> usize {
        // the `-1` is because our buffer is 1 bigger than the max packet size
        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()),
        }
    }

    /// The number of bytes this `Packet` will take up when encoded and written
    /// to a buffer.
    #[inline]
    pub fn total_length(&self) -> usize { self.contents().len() + Header::LEN }

    /// Encode a `Packet` and copy it into the provided buffer.
    ///
    /// # Note
    ///
    /// The minimum required buffer size can be determined via the
    /// `total_length()` method. If the buffer isn't big enough, *nothing will
    /// be written to the buffer* and this method will fail with an
    /// `InsufficientCapacity` error.
    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())
    }

    /// Encode the `Packet` and write it to something implementing
    /// `std::io::Write`.
    #[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(())
    }

    /// Try to append some data to the `Packet`'s contents, bailing without
    /// writing anything if there isn't enough space.
    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;

    /// Reconstruct a `Header` from its byte representation. This assumes the
    /// LRC is correct.
    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);
    }
}