internet 0.0.3

Network library for rust
Documentation
//! Abstract memory representations for zero-cost network operations.
//!
//! The `buffer` module provides the foundational [`Buf`] and [`BufMut`] traits.
//! Instead of tying protocol logic to specific data structures (like [`[u8]`], [`Vec<u8>`],
//! or custom ring buffers), these traits create an agnostic memory interface.
//!
//! This ensures the entire stack remains `no_std` compatible and completely
//! decoupled from memory allocation strategies. It gives you absolute control
//! over how and where your network packets are stored, enabling seamless integration
//! with anything from embedded static arrays to asynchronous `io_uring` buffers.

/// Errors that can occur during buffer read or write operations.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BufError {
    /// Requested offset or length is out of buffer bounds.
    OutOfBounds,
    /// Unexpected end of input while parsing.
    UnexpectedEof,
    /// Encoded data value is invalid for the target type.
    UnexpectedValue,
    /// Destination buffer is too small to complete the write.
    BufferTooSmall,
    /// Invalid length specified for a buffer operation.
    InvalidLength,
    /// Protocol-level error with a descriptive message.
    ProtocolError(&'static str),
}

impl core::error::Error for BufError {}

impl core::fmt::Display for BufError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            BufError::OutOfBounds => write!(f, "Buffer offset out of bounds"),
            BufError::UnexpectedEof => write!(f, "Unexpected end of buffer"),
            BufError::UnexpectedValue => write!(f, "Unexpected value in buffer"),
            BufError::BufferTooSmall => write!(f, "Buffer is too small"),
            BufError::InvalidLength => write!(f, "Invalid length"),
            BufError::ProtocolError(msg) => write!(f, "Protocol error: {msg}"),
        }
    }
}

/// Specialized [`Result`] type for buffer operations.
pub type BufResult<T> = core::result::Result<T, BufError>;

/// Read-only memory abstraction supporting random-access reads.
pub trait Buf {
    /// Returns the total size of the buffer in bytes.
    fn length(&self) -> usize;

    /// Returns `true` if the buffer contains no bytes.
    fn is_empty(&self) -> bool {
        self.length() == 0
    }

    /// Reads a single byte at the specified `offset`.
    fn get_u8(&self, offset: usize) -> BufResult<u8>;

    /// Reads an unsigned 16-bit integer in Big Endian format.
    fn get_u16_be(&self, offset: usize) -> BufResult<u16> {
        Ok(u16::from_be_bytes(self.get_array(offset)?))
    }

    /// Reads an unsigned 16-bit integer in Little Endian format.
    fn get_u16_le(&self, offset: usize) -> BufResult<u16> {
        Ok(u16::from_le_bytes(self.get_array(offset)?))
    }

    /// Reads an unsigned 32-bit integer in Big Endian format.
    fn get_u32_be(&self, offset: usize) -> BufResult<u32> {
        Ok(u32::from_be_bytes(self.get_array(offset)?))
    }

    /// Reads an unsigned 32-bit integer in Little Endian format.
    fn get_u32_le(&self, offset: usize) -> BufResult<u32> {
        Ok(u32::from_le_bytes(self.get_array(offset)?))
    }

    /// Reads an unsigned 64-bit integer in Big Endian format.
    fn get_u64_be(&self, offset: usize) -> BufResult<u64> {
        Ok(u64::from_be_bytes(self.get_array(offset)?))
    }

    /// Reads an unsigned 64-bit integer in Little Endian format.
    fn get_u64_le(&self, offset: usize) -> BufResult<u64> {
        Ok(u64::from_le_bytes(self.get_array(offset)?))
    }

    /// Reads an unsigned 128-bit integer in Big Endian format.
    fn get_u128_be(&self, offset: usize) -> BufResult<u128> {
        Ok(u128::from_be_bytes(self.get_array(offset)?))
    }

    /// Reads an unsigned 128-bit integer in Little Endian format.
    fn get_u128_le(&self, offset: usize) -> BufResult<u128> {
        Ok(u128::from_le_bytes(self.get_array(offset)?))
    }

    /// Reads an architecture-sized unsigned integer in Big Endian format.
    fn get_usize_be(&self, offset: usize) -> BufResult<usize> {
        let bytes: [u8; core::mem::size_of::<usize>()] = self.get_array(offset)?;
        Ok(usize::from_be_bytes(bytes))
    }

    /// Reads an architecture-sized unsigned integer in Little Endian format.
    fn get_usize_le(&self, offset: usize) -> BufResult<usize> {
        let bytes: [u8; core::mem::size_of::<usize>()] = self.get_array(offset)?;
        Ok(usize::from_le_bytes(bytes))
    }

    /// Reads a fixed-size byte array `[u8; N]` at the specified `offset`.
    fn get_array<const N: usize>(&self, offset: usize) -> BufResult<[u8; N]> {
        let mut arr = [0u8; N];
        self.read_into(offset, &mut arr)?;
        Ok(arr)
    }

    /// Copies bytes starting from `offset` into the destination slice `dest`.
    fn read_into(&self, offset: usize, dest: &mut [u8]) -> BufResult<()> {
        for (i, byte) in dest.iter_mut().enumerate() {
            *byte = self.get_u8(offset + i)?;
        }
        Ok(())
    }
}

/// Mutable memory abstraction supporting random-access writes.
pub trait BufMut: Buf {
    /// Writes a single byte at the specified `offset`.
    fn set_u8(&mut self, offset: usize, val: u8) -> BufResult<()>;

    /// Writes an unsigned 16-bit integer in Big Endian format.
    fn set_u16_be(&mut self, offset: usize, val: u16) -> BufResult<()> {
        self.set_array(offset, &val.to_be_bytes())
    }

    /// Writes an unsigned 16-bit integer in Little Endian format.
    fn set_u16_le(&mut self, offset: usize, val: u16) -> BufResult<()> {
        self.set_array(offset, &val.to_le_bytes())
    }

    /// Writes an unsigned 32-bit integer in Big Endian format.
    fn set_u32_be(&mut self, offset: usize, val: u32) -> BufResult<()> {
        self.set_array(offset, &val.to_be_bytes())
    }

    /// Writes an unsigned 32-bit integer in Little Endian format.
    fn set_u32_le(&mut self, offset: usize, val: u32) -> BufResult<()> {
        self.set_array(offset, &val.to_le_bytes())
    }

    /// Writes an unsigned 64-bit integer in Big Endian format.
    fn set_u64_be(&mut self, offset: usize, val: u64) -> BufResult<()> {
        self.set_array(offset, &val.to_be_bytes())
    }

    /// Writes an unsigned 64-bit integer in Little Endian format.
    fn set_u64_le(&mut self, offset: usize, val: u64) -> BufResult<()> {
        self.set_array(offset, &val.to_le_bytes())
    }

    /// Writes an unsigned 128-bit integer in Big Endian format.
    fn set_u128_be(&mut self, offset: usize, val: u128) -> BufResult<()> {
        self.set_array(offset, &val.to_be_bytes())
    }

    /// Writes an unsigned 128-bit integer in Little Endian format.
    fn set_u128_le(&mut self, offset: usize, val: u128) -> BufResult<()> {
        self.set_array(offset, &val.to_le_bytes())
    }

    /// Writes an architecture-sized unsigned integer in Big Endian format.
    fn set_usize_be(&mut self, offset: usize, val: usize) -> BufResult<()> {
        self.set_array(offset, &val.to_be_bytes())
    }

    /// Writes an architecture-sized unsigned integer in Little Endian format.
    fn set_usize_le(&mut self, offset: usize, val: usize) -> BufResult<()> {
        self.set_array(offset, &val.to_le_bytes())
    }

    /// Writes a fixed-size byte array `[u8; N]` at the specified `offset`.
    fn set_array<const N: usize>(&mut self, offset: usize, val: &[u8; N]) -> BufResult<()> {
        self.set_slice(offset, val)
    }

    /// Copies bytes from the source slice `src` into the buffer starting at `offset`.
    fn set_slice(&mut self, offset: usize, src: &[u8]) -> BufResult<()> {
        for (i, &byte) in src.iter().enumerate() {
            self.set_u8(offset + i, byte)?;
        }
        Ok(())
    }
}

impl Buf for [u8] {
    #[inline]
    fn length(&self) -> usize {
        self.len()
    }

    #[inline]
    fn get_u8(&self, offset: usize) -> BufResult<u8> {
        self.get(offset).copied().ok_or(BufError::OutOfBounds)
    }

    #[inline]
    fn get_array<const N: usize>(&self, offset: usize) -> BufResult<[u8; N]> {
        let end = offset.checked_add(N).ok_or(BufError::OutOfBounds)?;
        let slice = self.get(offset..end).ok_or(BufError::OutOfBounds)?;
        let mut arr = [0u8; N];
        arr.copy_from_slice(slice);
        Ok(arr)
    }

    #[inline]
    fn read_into(&self, offset: usize, dest: &mut [u8]) -> BufResult<()> {
        let end = offset
            .checked_add(dest.len())
            .ok_or(BufError::OutOfBounds)?;
        let slice = self.get(offset..end).ok_or(BufError::OutOfBounds)?;
        dest.copy_from_slice(slice);
        Ok(())
    }
}

impl BufMut for [u8] {
    #[inline]
    fn set_u8(&mut self, offset: usize, val: u8) -> BufResult<()> {
        let byte = self.get_mut(offset).ok_or(BufError::OutOfBounds)?;
        *byte = val;
        Ok(())
    }

    #[inline]
    fn set_array<const N: usize>(&mut self, offset: usize, val: &[u8; N]) -> BufResult<()> {
        self.set_slice(offset, val)
    }

    #[inline]
    fn set_slice(&mut self, offset: usize, src: &[u8]) -> BufResult<()> {
        let end = offset.checked_add(src.len()).ok_or(BufError::OutOfBounds)?;
        let slice = self.get_mut(offset..end).ok_or(BufError::OutOfBounds)?;
        slice.copy_from_slice(src);
        Ok(())
    }
}

impl<const N: usize> Buf for [u8; N] {
    #[inline]
    fn length(&self) -> usize {
        self.as_slice().length()
    }

    #[inline]
    fn get_u8(&self, offset: usize) -> BufResult<u8> {
        self.as_slice().get_u8(offset)
    }

    #[inline]
    fn get_array<const M: usize>(&self, offset: usize) -> BufResult<[u8; M]> {
        self.as_slice().get_array(offset)
    }

    #[inline]
    fn read_into(&self, offset: usize, dest: &mut [u8]) -> BufResult<()> {
        self.as_slice().read_into(offset, dest)
    }
}

impl<const N: usize> BufMut for [u8; N] {
    #[inline]
    fn set_u8(&mut self, offset: usize, val: u8) -> BufResult<()> {
        self.as_mut_slice().set_u8(offset, val)
    }

    #[inline]
    fn set_array<const M: usize>(&mut self, offset: usize, val: &[u8; M]) -> BufResult<()> {
        self.as_mut_slice().set_array(offset, val)
    }

    #[inline]
    fn set_slice(&mut self, offset: usize, src: &[u8]) -> BufResult<()> {
        self.as_mut_slice().set_slice(offset, src)
    }
}

impl<T: Buf + ?Sized> Buf for &T {
    #[inline]
    fn length(&self) -> usize {
        (**self).length()
    }

    #[inline]
    fn is_empty(&self) -> bool {
        (**self).is_empty()
    }

    #[inline]
    fn get_u8(&self, offset: usize) -> BufResult<u8> {
        (**self).get_u8(offset)
    }

    #[inline]
    fn get_array<const N: usize>(&self, offset: usize) -> BufResult<[u8; N]> {
        (**self).get_array(offset)
    }

    #[inline]
    fn read_into(&self, offset: usize, dest: &mut [u8]) -> BufResult<()> {
        (**self).read_into(offset, dest)
    }
}

impl<T: Buf + ?Sized> Buf for &mut T {
    #[inline]
    fn length(&self) -> usize {
        (**self).length()
    }

    #[inline]
    fn is_empty(&self) -> bool {
        (**self).is_empty()
    }

    #[inline]
    fn get_u8(&self, offset: usize) -> BufResult<u8> {
        (**self).get_u8(offset)
    }

    #[inline]
    fn get_array<const N: usize>(&self, offset: usize) -> BufResult<[u8; N]> {
        (**self).get_array(offset)
    }

    #[inline]
    fn read_into(&self, offset: usize, dest: &mut [u8]) -> BufResult<()> {
        (**self).read_into(offset, dest)
    }
}

impl<T: BufMut + ?Sized> BufMut for &mut T {
    #[inline]
    fn set_u8(&mut self, offset: usize, val: u8) -> BufResult<()> {
        (**self).set_u8(offset, val)
    }

    #[inline]
    fn set_array<const N: usize>(&mut self, offset: usize, val: &[u8; N]) -> BufResult<()> {
        (**self).set_array(offset, val)
    }

    #[inline]
    fn set_slice(&mut self, offset: usize, src: &[u8]) -> BufResult<()> {
        (**self).set_slice(offset, src)
    }
}

#[cfg(feature = "alloc")]
impl Buf for Vec<u8> {
    #[inline(always)]
    fn length(&self) -> usize {
        self.as_slice().len()
    }

    fn get_u8(&self, offset: usize) -> BufResult<u8> {
        self.as_slice().get_u8(offset)
    }

    fn get_array<const N: usize>(&self, offset: usize) -> BufResult<[u8; N]> {
        self.as_slice().get_array::<N>(offset)
    }

    fn read_into(&self, offset: usize, dest: &mut [u8]) -> BufResult<()> {
        self.as_slice().read_into(offset, dest)
    }
}

#[cfg(feature = "alloc")]
impl BufMut for Vec<u8> {
    fn set_u8(&mut self, offset: usize, val: u8) -> BufResult<()> {
        if offset >= self.len() {
            self.resize(offset + 1, 0);
        }
        self.as_mut_slice()[offset] = val;
        Ok(())
    }

    fn set_slice(&mut self, offset: usize, src: &[u8]) -> BufResult<()> {
        let end = offset + src.len();
        if end > self.len() {
            self.resize(end, 0);
        }
        self.as_mut_slice()[offset..end].copy_from_slice(src);
        Ok(())
    }
}