internet 0.1.0

Network library for rust
Documentation
//! Stateful buffer traversal and byte-level manipulation.
//!
//! Dealing with raw offsets and tracking bounds manually leads to unreadable
//! and error-prone code. This module provides the `Cursor`, a stateful wrapper
//! around any `Buf` or `BufMut` that automatically manages the internal position.
//!
//! The [`Cursor`] allows for safe sequential reading and writing, advancing the internal
//! offset, or performing in-place mutations without ever losing track of protocol
//! boundaries or manually calculating slice indexes.

use crate::Buf;
use crate::BufError;
use crate::BufMut;
use crate::BufResult;

/// A stateful wrapper for sequential reading and writing in a buffer.
///
/// The `Cursor` keeps track of the current position within the underlying buffer `B`,
/// ensuring that all read and write operations automatically advance the position
/// and respect the buffer's boundaries.
#[derive(Debug, Clone, Copy)]
pub struct Cursor<B: Buf> {
    /// The underlying buffer.
    buffer: B,
    /// The current byte offset within the buffer.
    position: usize,
}

impl<B: Buf> Cursor<B> {
    /// Creates a new `Cursor` initialized at position `0`.
    #[inline(always)]
    pub fn new(buffer: B) -> Self {
        Self {
            buffer,
            position: 0,
        }
    }

    /// Consumes the `Cursor` and returns the underlying buffer.
    #[inline(always)]
    pub fn into_inner(self) -> B {
        self.buffer
    }

    /// Returns the current byte offset within the buffer.
    #[inline(always)]
    pub fn position(&self) -> usize {
        self.position
    }
}

impl<B: Buf> Cursor<B> {
    /// Returns the number of bytes remaining until the end of the buffer.
    pub fn remaining(&self) -> usize {
        self.buffer.length().saturating_sub(self.position)
    }

    /// Returns `true` if the cursor has reached the buffer length.
    pub fn is_eof(&self) -> bool {
        self.position >= self.buffer.length()
    }

    /// Advances the cursor position by `amount` bytes.
    ///
    /// # Errors
    ///
    /// Returns [`BufError::UnexpectedEof`] if `amount` exceeds the remaining bytes.
    pub fn advance(&mut self, amount: usize) -> BufResult<()> {
        if self.remaining() < amount {
            return Err(BufError::UnexpectedEof);
        }
        self.position += amount;
        Ok(())
    }

    /// Moves the cursor position backward by `amount` bytes.
    ///
    /// # Errors
    ///
    /// Returns [`BufError::OutOfBounds`] if `amount` exceeds the current position.
    pub fn rewind(&mut self, amount: usize) -> BufResult<()> {
        if self.position < amount {
            return Err(BufError::OutOfBounds);
        }
        self.position -= amount;
        Ok(())
    }

    /// Sets the cursor position to an absolute `position`.
    ///
    /// # Errors
    ///
    /// Returns [`BufError::OutOfBounds`] if `position` exceeds the buffer's length.
    pub fn seek_to(&mut self, position: usize) -> BufResult<()> {
        if position > self.buffer.length() {
            return Err(BufError::OutOfBounds);
        }
        self.position = position;
        Ok(())
    }

    /// Sets the cursor position relative to the end of the buffer.
    ///
    /// # Errors
    ///
    /// Returns [`BufError::OutOfBounds`] if `offset_from_end` exceeds the buffer'
    pub fn seek_from_end(&mut self, offset_from_end: usize) -> BufResult<()> {
        if offset_from_end > self.buffer.length() {
            return Err(BufError::OutOfBounds);
        }
        self.position = self.buffer.length() - offset_from_end;
        Ok(())
    }

    /// Peeks at a single byte at the current position without advancing the cursor.
    pub fn peek_u8(&self) -> BufResult<u8> {
        self.buffer.get_u8(self.position)
    }

    /// Peeks at a 16-bit big-endian integer at the current position without advancing.
    pub fn peek_u16_be(&self) -> BufResult<u16> {
        self.buffer.get_u16_be(self.position)
    }

    /// Peeks at a 32-bit big-endian integer at the current position without advancing.
    pub fn peek_u32_be(&self) -> BufResult<u32> {
        self.buffer.get_u32_be(self.position)
    }

    /// Peeks at a fixed-size byte array at the current position without advancing.
    pub fn peek_array<const N: usize>(&self) -> BufResult<[u8; N]> {
        self.buffer.get_array::<N>(self.position)
    }

    /// Reads a single byte and advances the cursor by 1.
    pub fn read_u8(&mut self) -> BufResult<u8> {
        let val = self.peek_u8()?;
        self.advance(1)?;
        Ok(val)
    }

    /// Reads a 16-bit big-endian integer and advances the cursor by 2.
    pub fn read_u16_be(&mut self) -> BufResult<u16> {
        let val = self.peek_u16_be()?;
        self.advance(2)?;
        Ok(val)
    }

    /// Reads a 32-bit big-endian integer and advances the cursor by 4.
    pub fn read_u32_be(&mut self) -> BufResult<u32> {
        let val = self.peek_u32_be()?;
        self.advance(4)?;
        Ok(val)
    }

    /// Reads a fixed-size byte array and advances the cursor by `N`.
    pub fn read_array<const N: usize>(&mut self) -> BufResult<[u8; N]> {
        let val = self.peek_array::<N>()?;
        self.advance(N)?;
        Ok(val)
    }

    /// Reads bytes into the provided `destination` and advances the cursor.
    ///
    /// # Errors
    ///
    /// Returns [`BufError::UnexpectedEof`] if the destination slice length exceeds
    /// the remaining bytes in the buffer.
    pub fn read_into(&mut self, destination: &mut [u8]) -> BufResult<()> {
        self.buffer.read_into(self.position, destination)?;
        self.advance(destination.len())?;
        Ok(())
    }
}

impl<B: BufMut> Cursor<B> {
    /// Writes a single byte at the current position without advancing the cursor.
    pub fn poke_u8(&mut self, val: u8) -> BufResult<()> {
        self.buffer.set_u8(self.position, val)
    }

    /// Writes a 16-bit big-endian integer at the current position without advancing.
    pub fn poke_u16_be(&mut self, val: u16) -> BufResult<()> {
        self.buffer.set_u16_be(self.position, val)
    }

    /// Writes a 32-bit big-endian integer at the current position without advancing.
    pub fn poke_u32_be(&mut self, val: u32) -> BufResult<()> {
        self.buffer.set_u32_be(self.position, val)
    }

    /// Writes a fixed-size byte array at the current position without advancing.
    pub fn poke_array<const N: usize>(&mut self, val: &[u8; N]) -> BufResult<()> {
        self.buffer.set_array(self.position, val)
    }

    /// Writes a byte slice at the current position without advancing the cursor.
    pub fn poke_slice(&mut self, src: &[u8]) -> BufResult<()> {
        self.buffer.set_slice(self.position, src)
    }

    /// Writes a single byte and advances the cursor by 1.
    pub fn write_u8(&mut self, val: u8) -> BufResult<()> {
        self.poke_u8(val)?;
        self.advance(1)?;
        Ok(())
    }

    /// Writes a 16-bit big-endian integer and advances the cursor by 2.
    pub fn write_u16_be(&mut self, val: u16) -> BufResult<()> {
        self.poke_u16_be(val)?;
        self.advance(2)?;
        Ok(())
    }

    /// Writes a 32-bit big-endian integer and advances the cursor by 4.
    pub fn write_u32_be(&mut self, val: u32) -> BufResult<()> {
        self.poke_u32_be(val)?;
        self.advance(4)?;
        Ok(())
    }

    /// Writes a fixed-size byte array and advances the cursor by `N`.
    pub fn write_array<const N: usize>(&mut self, val: &[u8; N]) -> BufResult<()> {
        self.poke_array(val)?;
        self.advance(N)?;
        Ok(())
    }

    /// Writes a byte slice and advances the cursor by the slice's length.
    ///
    /// # Errors
    ///
    /// Returns [`BufError::OutOfBounds`] or [`BufError::UnexpectedEof`] depending
    /// on the underlying [`BufMut`] implementation if the slice exceeds buffer capacity.
    pub fn write_slice(&mut self, src: &[u8]) -> BufResult<()> {
        self.poke_slice(src)?;
        self.advance(src.len())?;
        Ok(())
    }
}