pigeon 0.1.0

Utilities for efficient packing and unpacking of big-endian binary data
Documentation
/// Types which can get read from a `FrameReader`.
pub trait Grab<'a>
where
    Self: Sized + 'a,
{
    /// Read an item of this type from the `FrameReader`.
    fn grab_from(reader: &mut FrameReader<'a>) -> Option<Self>;
}

/// Read a type which implement `Grab` from the buffer.
///
/// # Examples
///
/// ```rust
/// let buf = [12, 34, 56, 78];
///
/// let num: u32 = pigeon::grab_from_buffer(&buf[..]).unwrap();
///
/// assert_eq!(num, 203569230);
/// ```
pub fn grab_from_buffer<'a, G: Grab<'a>>(buffer: &'a [u8]) -> Option<G> {
    let mut reader = FrameReader::new(buffer);
    G::grab_from(&mut reader)
}

/// A type that can be used to read from a buffer.
#[derive(Debug)]
pub struct FrameReader<'a> {
    idx: usize,
    buf: &'a [u8],
}

impl<'a> FrameReader<'a> {
    /// Create a new `FrameReader` from a buffer.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use pigeon::{
    /// #     FrameReader,
    /// # };
    /// let buf = [0, 1, 2, 3];
    /// let mut reader = FrameReader::new(&buf[..]);
    /// let a = reader.read::<u8>().unwrap();
    /// let b = reader.read::<u16>().unwrap();
    /// let c = reader.read::<u8>().unwrap();
    /// assert_eq!(a, 0);
    /// assert_eq!(b, 258);
    /// assert_eq!(c, 3);
    /// assert!(reader.read::<u8>().is_none());
    /// ```
    pub fn new(buf: &'a [u8]) -> FrameReader<'a> {
        FrameReader { idx: 0, buf }
    }

    /// Get the position of the `FrameReader` in the buffer.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use pigeon::{
    /// #     FrameReader,
    /// # };
    /// let buf = [0; 16];
    /// let mut reader = FrameReader::new(&buf[..]);
    /// assert_eq!(reader.position(), 0);
    /// assert!(reader.read_bytes(2).is_some());
    /// assert_eq!(reader.position(), 2);
    /// assert!(reader.read_bytes(5).is_some());
    /// assert_eq!(reader.position(), 7);
    /// assert!(reader.read::<u32>().is_some());
    /// assert_eq!(reader.position(), 11);
    /// assert!(reader.read::<u8>().is_some());
    /// assert_eq!(reader.position(), 12);
    /// assert!(reader.read::<u32>().is_some());
    /// assert_eq!(reader.position(), 16);
    /// ```
    pub fn position(&self) -> usize {
        self.idx
    }

    /// Try to read `len` bytes from the buffer.
    ///
    /// If `len` bytes can't be read, this returns `None`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use pigeon::{
    /// #     FrameReader,
    /// # };
    /// let buf = [0, 1, 2, 3, 4, 5];
    /// let mut reader = FrameReader::new(&buf[..]);
    /// let bytes_a = reader.read_bytes(3).unwrap();
    /// assert_eq!(&bytes_a, &[0, 1, 2]);
    /// let bytes_b = reader.read_bytes(2).unwrap();
    /// assert_eq!(&bytes_b, &[3, 4]);
    /// assert!(reader.read_bytes(2).is_none());
    /// ```
    pub fn read_bytes(&mut self, len: usize) -> Option<&'a [u8]> {
        let buf_len = self.buf.len();
        if self.idx + len <= buf_len {
            let idx = self.idx;
            self.idx += len;
            Some(&self.buf[idx..idx + len])
        } else {
            None
        }
    }

    /// Try to read `buf.len()` bytes into `buf`.
    ///
    /// On failure, this returns `None`.
    ///
    /// On success, this returns `Some(())`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use pigeon::{
    /// #     FrameReader,
    /// # };
    /// let buf = [0, 1, 2, 3, 4, 5];
    /// let mut reader = FrameReader::new(&buf[..]);
    /// let mut target = [0; 4];
    /// assert!(reader.read_bytes_to(&mut target[..]).is_some());
    /// assert_eq!(&target[..4], &buf[..4]);
    /// assert!(reader.read_bytes_to(&mut target[..]).is_none());
    /// ```
    pub fn read_bytes_to(&mut self, buf: &mut [u8]) -> Option<()> {
        let bytes = self.read_bytes(buf.len())?;
        buf.copy_from_slice(bytes);
        Some(())
    }

    /// Try to read an instance of a type implementing `Grab`.
    ///
    /// On failure, this returns `None`.
    ///
    /// On success, this returns `Some` with an instance of `G` inside it.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use pigeon::{
    /// #     FrameReader,
    /// # };
    /// let buf = [0, 1, 2, 3, 4, 5, 6];
    /// let mut reader = FrameReader::new(&buf[..]);
    /// let a: u32 = reader.read().unwrap();
    /// assert_eq!(a, 66051);
    /// let b: u8 = reader.read().unwrap();
    /// assert_eq!(b, 4);
    /// assert!(reader.read::<u32>().is_none());
    /// let c: u16 = reader.read().unwrap();
    /// assert_eq!(c, 1286);
    /// assert!(reader.read::<u8>().is_none());
    /// ```
    pub fn read<G: Grab<'a>>(&mut self) -> Option<G> {
        G::grab_from(self)
    }

    /// Try to read an instance of a type implementing `Grab`, and compare it to `expected`.
    ///
    /// If the read fails or the returned instance isn't equal to the expected one, returns `None`.
    /// Else, returns `Some(())`.
    ///
    /// This will advance the position if the read succeeded but doesn't match the expected value.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use pigeon::{
    /// #     FrameReader,
    /// # };
    /// let buf = [0, 1, 2, 3, 4, 5, 6];
    /// let mut reader = FrameReader::new(&buf[..]);
    /// assert!(reader.expect(66051u32).is_some());
    /// assert!(reader.expect(4u8).is_some());
    /// assert!(reader.expect(12345678u32).is_none());
    /// assert!(reader.expect(1286u16).is_some());
    /// ```
    pub fn expect<G: Grab<'a> + PartialEq>(&mut self, expected: G) -> Option<()> {
        let data: G = self.read()?;
        if data == expected {
            Some(())
        } else {
            None
        }
    }

    /// Create a `FrameReader` on a buffer and pass it into a closure, returning what the closure
    /// returns.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use pigeon::{
    /// #     FrameReader,
    /// # };
    /// let buf = [0, 1, 2, 3, 4, 5, 6];
    /// let value = FrameReader::with(&buf[..], |reader| {
    ///     let a: u32 = reader.read()?;
    ///     let b: u16 = reader.read()?;
    ///     let c: u8 = reader.read()?;
    ///     Some((a, b, c))
    /// }).unwrap();
    /// assert_eq!(value, (66051, 1029, 6));
    /// ```
    pub fn with<T>(buf: &'a [u8], cb: impl FnOnce(&mut FrameReader<'a>) -> Option<T>) -> Option<T> {
        let mut reader = FrameReader::new(buf);
        cb(&mut reader)
    }
}