pigeon 0.1.0

Utilities for efficient packing and unpacking of big-endian binary data
Documentation
/// Types which can get written to a `FrameWriter`.
pub trait Dump {
    /// Dump this type into the `FrameWriter`.
    fn dump_to<T: FrameTarget>(&self, writer: &mut FrameWriter<T>);

    /// Calculate the size of the data.
    fn size(&self) -> usize {
        let mut writer = FrameWriter::new(());
        self.dump_to(&mut writer);
        writer.position()
    }
}

/// Dump the type to a buffer directly.
///
/// # Panics
///
/// This function will panic if the buffer doesn't have enough remaining space to fit the
/// argument.
///
/// # Examples
///
/// ```rust
/// let mut buf = [0; 6];
///
/// let written = pigeon::dump_to_buffer(&12345678u32, &mut buf[..]);
///
/// assert_eq!(written, 4);
/// assert_eq!(&buf, &[0, 188, 97, 78, 0, 0]);
/// assert_eq!(&buf[0..written], &[0, 188, 97, 78]);
/// ```
pub fn dump_to_buffer<D: Dump>(data: &D, buffer: &mut [u8]) -> usize {
    let mut writer = FrameWriter::new(buffer);
    data.dump_to(&mut writer);
    writer.position()
}

/// A type which can be written to by a `FrameWriter`.
pub trait FrameTarget {
    /// Write these bytes at a specific position.
    fn write_bytes_at(&mut self, pos: usize, bytes: &[u8]);

    /// Get the size of the buffer.
    fn size(&self) -> usize;
}

impl FrameTarget for () {
    #[inline(always)]
    fn write_bytes_at(&mut self, _pos: usize, _bytes: &[u8]) {}

    fn size(&self) -> usize {
        usize::MAX
    }
}

impl<'a> FrameTarget for &'a mut [u8] {
    fn write_bytes_at(&mut self, pos: usize, bytes: &[u8]) {
        let len = bytes.len();
        self[pos..pos + len].copy_from_slice(bytes);
    }

    fn size(&self) -> usize {
        self.len()
    }
}

/// A type which can be used for writing to a type that implements `FrameTarget`.
#[derive(Debug)]
pub struct FrameWriter<T: FrameTarget> {
    idx: usize,
    buf: T,
}

impl<T: FrameTarget> FrameWriter<T> {
    /// Create a new `FrameWriter` from a type implementing `FrameTarget`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #     pigeon::FrameWriter,
    /// #     std::convert::TryInto,
    /// # };
    /// let mut buf = [0; 8];
    /// let mut writer = FrameWriter::new(&mut buf[..]);
    ///
    /// writer.write(12u8);
    /// writer.write(24u8);
    /// writer.write(554u16);
    /// writer.write(12345678u32);
    ///
    /// assert_eq!(buf[0], 12);
    /// assert_eq!(buf[1], 24);
    /// assert_eq!(u16::from_be_bytes(buf[2..4].try_into().unwrap()), 554);
    /// assert_eq!(u32::from_be_bytes(buf[4..8].try_into().unwrap()), 12345678);
    /// ```
    pub fn new(buf: T) -> FrameWriter<T> {
        FrameWriter { idx: 0, buf }
    }

    /// Get the position of the `FrameWriter` in its buffer.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #     pigeon::FrameWriter,
    /// # };
    /// let mut buf = [0; 9];
    /// let mut writer = FrameWriter::new(&mut buf[..]);
    ///
    /// assert_eq!(writer.position(), 0);
    ///
    /// writer.write(0u8);
    ///
    /// assert_eq!(writer.position(), 1);
    ///
    /// writer.write(0u16);
    ///
    /// assert_eq!(writer.position(), 3);
    ///
    /// writer.write_bytes(b"coucou");
    ///
    /// assert_eq!(writer.position(), 9);
    /// ```
    pub fn position(&self) -> usize {
        self.idx
    }

    /// Write a byte slice into the `FrameWriter` at the current offset.
    ///
    /// # Panics
    ///
    /// This function will panic if the buffer doesn't have enough remaining space to fit all these
    /// bytes.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #     pigeon::FrameWriter,
    /// # };
    /// let mut buf = [0; 13];
    /// let mut writer = FrameWriter::new(&mut buf[..]);
    ///
    /// writer.write_bytes(b"Hello, world!");
    ///
    /// assert_eq!(&buf, b"Hello, world!");
    /// ```
    pub fn write_bytes(&mut self, bytes: &[u8]) {
        self.buf.write_bytes_at(self.idx, bytes);
        self.idx += bytes.len();
    }

    /// Write a type implementing `Dump` into the `FrameWriter` at the current offset.
    ///
    /// # Panics
    ///
    /// This function will panic if the buffer doesn't have enough remaining space to fit the
    /// argument.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #   pigeon::{FrameWriter, ShortStr},
    /// # };
    /// let mut buf = [0; 14];
    /// let mut writer = FrameWriter::new(&mut buf[..]);
    ///
    /// writer.write(ShortStr("Hello, world!"));
    ///
    /// assert_eq!(&buf, b"\x0DHello, world!");
    /// ```
    pub fn write<D: Dump>(&mut self, data: D) {
        data.dump_to(self);
    }

    /// Check whether a type implementing `Dump` fits into the rest of the buffer.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #     pigeon::FrameWriter,
    /// # };
    /// let mut buf = [0; 4];
    /// let mut writer = FrameWriter::new(&mut buf[..]);
    ///
    /// assert!(writer.fits(0u32));
    /// assert!(!writer.fits(0u64));
    ///
    /// writer.write(0u8);
    ///
    /// assert!(writer.fits(0u16));
    /// assert!(!writer.fits(0u32));
    ///
    /// writer.write(0u16);
    ///
    /// assert!(writer.fits(0u8));
    /// assert!(!writer.fits(0u16));
    ///
    /// writer.write(0u8);
    ///
    /// assert!(!writer.fits(0u8));
    /// ```
    pub fn fits<D: Dump>(&mut self, data: D) -> bool {
        let size = data.size();
        dbg!(size);
        dbg!(self.buf.size());
        self.idx + size <= self.buf.size()
    }

    /// Check whether a byteslice can fit into the buffer.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #     pigeon::FrameWriter,
    /// # };
    /// let mut buf = [0; 4];
    /// let mut writer = FrameWriter::new(&mut buf[..]);
    ///
    /// assert!(writer.fits_bytes(b"hewo"));
    /// assert!(!writer.fits_bytes(b"hallo, iedereen!"));
    ///
    /// writer.write_bytes(b"mew");
    ///
    /// assert!(writer.fits_bytes(&[12]));
    /// assert!(!writer.fits_bytes(b"hewo"));
    /// ```
    pub fn fits_bytes(&mut self, bytes: &[u8]) -> bool {
        self.idx + bytes.len() <= self.buf.size()
    }

    /// Create a `FrameWriter` on a buffer and pass it into a closure.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #     pigeon::{
    /// #         FrameWriter,
    /// #         ShortStr,
    /// #     },
    /// # };
    /// let mut buf = [0; 25];
    ///
    /// FrameWriter::with(&mut buf[..], |writer| {
    ///     writer.write(ShortStr("bonjour, tout le monde!"));
    ///     writer.write(12u8);
    /// });
    ///
    /// assert_eq!(&buf, b"\x17bonjour, tout le monde!\x0C");
    /// ```
    pub fn with(buf: T, cb: impl FnOnce(&mut FrameWriter<T>)) {
        let mut writer = FrameWriter::new(buf);
        cb(&mut writer);
    }
}

impl<'a> FrameWriter<&'a mut [u8]> {
    /// Get a reference to the  part of the internal buffer that was written to.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use {
    /// #     pigeon::FrameWriter,
    /// # };
    /// let mut buf = [0; 32];
    /// let mut writer = FrameWriter::new(&mut buf[..]);
    ///
    /// writer.write_bytes(b"ik hou van je!");
    ///
    /// assert_eq!(writer.as_bytes(), b"ik hou van je!");
    /// ```
    pub fn as_bytes(&self) -> &[u8] {
        &self.buf[..self.idx]
    }
}