From 43cb0c09c565d1401feb040516fce9a56d05c6e1 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Wed, 6 Nov 2024 17:37:35 +0100 Subject: [PATCH] docs(length_prefix_encoding): fix docstring warnings Signed-off-by: Paul Spooren --- util/src/length_prefix_encoding/decoder.rs | 61 ++++++++++++++++++++++ util/src/length_prefix_encoding/encoder.rs | 59 +++++++++++++++++++++ util/src/length_prefix_encoding/mod.rs | 2 + 3 files changed, 122 insertions(+) diff --git a/util/src/length_prefix_encoding/decoder.rs b/util/src/length_prefix_encoding/decoder.rs index 1769b70..f40d900 100644 --- a/util/src/length_prefix_encoding/decoder.rs +++ b/util/src/length_prefix_encoding/decoder.rs @@ -8,28 +8,37 @@ use crate::{ result::ensure_or, }; +/// Size in bytes of a message header carrying length information pub const HEADER_SIZE: usize = std::mem::size_of::(); #[derive(Error, Debug)] +/// Error enum to represent various boundary sanity check failures during buffer operations pub enum SanityError { #[error("Offset is out of read buffer bounds")] + /// Error indicating that the given offset exceeds the bounds of the read buffer OutOfBufferBounds, #[error("Offset is out of message buffer bounds")] + /// Error indicating that the given offset exceeds the bounds of the message buffer OutOfMessageBounds, } #[derive(Error, Debug)] #[error("Message too large ({msg_size} bytes) for buffer ({buf_size} bytes)")] +/// Error indicating that message exceeds available buffer space pub struct MessageTooLargeError { msg_size: usize, buf_size: usize, } impl MessageTooLargeError { + /// Creates a new MessageTooLargeError with the given message and buffer sizes pub fn new(msg_size: usize, buf_size: usize) -> Self { Self { msg_size, buf_size } } + /// Ensures that the message size fits within the buffer size + /// + /// Returns Ok(()) if the message fits, otherwise returns an error with size details pub fn ensure(msg_size: usize, buf_size: usize) -> Result<(), Self> { let err = MessageTooLargeError { msg_size, buf_size }; ensure_or(msg_size <= buf_size, err) @@ -37,12 +46,16 @@ impl MessageTooLargeError { } #[derive(Debug)] +/// Return type for ReadFromIo operations that contains the number of bytes read and an optional message slice pub struct ReadFromIoReturn<'a> { + /// Number of bytes read from the input pub bytes_read: usize, + /// Optional slice containing the complete message, if one was read pub message: Option<&'a mut [u8]>, } impl<'a> ReadFromIoReturn<'a> { + /// Creates a new ReadFromIoReturn with the given number of bytes read and optional message slice. pub fn new(bytes_read: usize, message: Option<&'a mut [u8]>) -> Self { Self { bytes_read, @@ -52,9 +65,12 @@ impl<'a> ReadFromIoReturn<'a> { } #[derive(Debug, Error)] +/// An enum representing errors that can occur during read operations from I/O pub enum ReadFromIoError { + /// Error occurred while reading from the underlying I/O stream #[error("Error reading from the underlying stream")] IoError(#[from] io::Error), + /// Error occurred because message size exceeded buffer capacity #[error("Message size out of buffer bounds")] MessageTooLargeError(#[from] MessageTooLargeError), } @@ -69,6 +85,10 @@ impl TryIoErrorKind for ReadFromIoError { } #[derive(Debug, Default, Clone)] +/// A decoder for length-prefixed messages +/// +/// This struct provides functionality to decode messages that are prefixed with their length. +/// It maintains internal state for header information, the message buffer, and current offset. pub struct LengthPrefixDecoder> { header: [u8; HEADER_SIZE], buf: Buf, @@ -76,25 +96,33 @@ pub struct LengthPrefixDecoder> { } impl> LengthPrefixDecoder { + /// Creates a new LengthPrefixDecoder with the given buffer pub fn new(buf: Buf) -> Self { let header = Default::default(); let off = 0; Self { header, buf, off } } + /// Clears and zeroes all internal state pub fn clear(&mut self) { self.zeroize() } + /// Creates a new LengthPrefixDecoder from its component parts pub fn from_parts(header: [u8; HEADER_SIZE], buf: Buf, off: usize) -> Self { Self { header, buf, off } } + /// Consumes the decoder and returns its component parts pub fn into_parts(self) -> ([u8; HEADER_SIZE], Buf, usize) { let Self { header, buf, off } = self; (header, buf, off) } + /// Reads a complete message from the given reader into the decoder. + /// + /// Retries on interrupts and returns the decoded message buffer on success. + /// Returns an error if the read fails or encounters an unexpected EOF. pub fn read_all_from_stdio( &mut self, mut r: R, @@ -125,6 +153,7 @@ impl> LengthPrefixDecoder { } } + /// Reads from the given reader into the decoder's internal buffers pub fn read_from_stdio( &mut self, mut r: R, @@ -150,6 +179,7 @@ impl> LengthPrefixDecoder { }) } + /// Gets the next buffer slice that can be written to pub fn next_slice_to_write_to(&mut self) -> Result, MessageTooLargeError> { fn some_if_nonempty(buf: &mut [u8]) -> Option<&mut [u8]> { match buf.is_empty() { @@ -172,6 +202,7 @@ impl> LengthPrefixDecoder { Ok(None) } + /// Advances the internal offset by the specified number of bytes pub fn advance(&mut self, count: usize) -> Result<(), SanityError> { let off = self.off + count; let msg_off = off.saturating_sub(HEADER_SIZE); @@ -189,6 +220,7 @@ impl> LengthPrefixDecoder { Ok(()) } + /// Ensures that the internal message buffer is large enough for the message size in the header pub fn ensure_sufficient_msg_buffer(&self) -> Result<(), MessageTooLargeError> { let buf_size = self.message_buffer().len(); let msg_size = match self.get_header() { @@ -198,43 +230,53 @@ impl> LengthPrefixDecoder { MessageTooLargeError::ensure(msg_size, buf_size) } + /// Returns a reference to the header buffer pub fn header_buffer(&self) -> &[u8] { &self.header[..] } + /// Returns a mutable reference to the header buffer pub fn header_buffer_mut(&mut self) -> &mut [u8] { &mut self.header[..] } + /// Returns a reference to the message buffer pub fn message_buffer(&self) -> &[u8] { self.buf.borrow() } + /// Returns a mutable reference to the message buffer pub fn message_buffer_mut(&mut self) -> &mut [u8] { self.buf.borrow_mut() } + /// Returns the number of bytes read so far pub fn bytes_read(&self) -> &usize { &self.off } + /// Consumes the decoder and returns just the message buffer pub fn into_message_buffer(self) -> Buf { let Self { buf, .. } = self; buf } + /// Returns the current offset into the header buffer pub fn header_buffer_offset(&self) -> usize { min(self.off, HEADER_SIZE) } + /// Returns the current offset into the message buffer pub fn message_buffer_offset(&self) -> usize { self.off.saturating_sub(HEADER_SIZE) } + /// Returns whether a complete header has been read pub fn has_header(&self) -> bool { self.header_buffer_offset() == HEADER_SIZE } + /// Returns whether a complete message has been read pub fn has_message(&self) -> Result { self.ensure_sufficient_msg_buffer()?; let msg_size = match self.get_header() { @@ -244,46 +286,55 @@ impl> LengthPrefixDecoder { Ok(self.message_buffer_avail().len() == msg_size) } + /// Returns a slice of the available data in the header buffer pub fn header_buffer_avail(&self) -> &[u8] { let off = self.header_buffer_offset(); &self.header_buffer()[..off] } + /// Returns a mutable slice of the available data in the header buffer pub fn header_buffer_avail_mut(&mut self) -> &mut [u8] { let off = self.header_buffer_offset(); &mut self.header_buffer_mut()[..off] } + /// Returns a slice of the remaining space in the header buffer pub fn header_buffer_left(&self) -> &[u8] { let off = self.header_buffer_offset(); &self.header_buffer()[off..] } + /// Returns a mutable slice of the remaining space in the header buffer pub fn header_buffer_left_mut(&mut self) -> &mut [u8] { let off = self.header_buffer_offset(); &mut self.header_buffer_mut()[off..] } + /// Returns a slice of the available data in the message buffer pub fn message_buffer_avail(&self) -> &[u8] { let off = self.message_buffer_offset(); &self.message_buffer()[..off] } + /// Returns a mutable slice of the available data in the message buffer pub fn message_buffer_avail_mut(&mut self) -> &mut [u8] { let off = self.message_buffer_offset(); &mut self.message_buffer_mut()[..off] } + /// Returns a slice of the remaining space in the message buffer pub fn message_buffer_left(&self) -> &[u8] { let off = self.message_buffer_offset(); &self.message_buffer()[off..] } + /// Returns a mutable slice of the remaining space in the message buffer pub fn message_buffer_left_mut(&mut self) -> &mut [u8] { let off = self.message_buffer_offset(); &mut self.message_buffer_mut()[off..] } + /// Returns the message size from the header if available pub fn get_header(&self) -> Option { match self.header_buffer_offset() == HEADER_SIZE { false => None, @@ -291,19 +342,23 @@ impl> LengthPrefixDecoder { } } + /// Returns the size of the message if header is available pub fn message_size(&self) -> Option { self.get_header() } + /// Returns the total size of the encoded message including header pub fn encoded_message_bytes(&self) -> Option { self.message_size().map(|sz| sz + HEADER_SIZE) } + /// Returns a slice of the message fragment if available pub fn message_fragment(&self) -> Result, MessageTooLargeError> { self.ensure_sufficient_msg_buffer()?; Ok(self.message_size().map(|sz| &self.message_buffer()[..sz])) } + /// Returns a mutable slice of the message fragment if available pub fn message_fragment_mut(&mut self) -> Result, MessageTooLargeError> { self.ensure_sufficient_msg_buffer()?; Ok(self @@ -311,12 +366,14 @@ impl> LengthPrefixDecoder { .map(|sz| &mut self.message_buffer_mut()[..sz])) } + /// Returns a slice of the available data in the message fragment pub fn message_fragment_avail(&self) -> Result, MessageTooLargeError> { let off = self.message_buffer_avail().len(); self.message_fragment() .map(|frag| frag.map(|frag| &frag[..off])) } + /// Returns a mutable slice of the available data in the message fragment pub fn message_fragment_avail_mut( &mut self, ) -> Result, MessageTooLargeError> { @@ -325,24 +382,28 @@ impl> LengthPrefixDecoder { .map(|frag| frag.map(|frag| &mut frag[..off])) } + /// Returns a slice of the remaining space in the message fragment pub fn message_fragment_left(&self) -> Result, MessageTooLargeError> { let off = self.message_buffer_avail().len(); self.message_fragment() .map(|frag| frag.map(|frag| &frag[off..])) } + /// Returns a mutable slice of the remaining space in the message fragment pub fn message_fragment_left_mut(&mut self) -> Result, MessageTooLargeError> { let off = self.message_buffer_avail().len(); self.message_fragment_mut() .map(|frag| frag.map(|frag| &mut frag[off..])) } + /// Returns a slice of the complete message if available pub fn message(&self) -> Result, MessageTooLargeError> { let sz = self.message_size(); self.message_fragment_avail() .map(|frag_opt| frag_opt.and_then(|frag| (frag.len() == sz?).then_some(frag))) } + /// Returns a mutable slice of the complete message if available pub fn message_mut(&mut self) -> Result, MessageTooLargeError> { let sz = self.message_size(); self.message_fragment_avail_mut() diff --git a/util/src/length_prefix_encoding/encoder.rs b/util/src/length_prefix_encoding/encoder.rs index a7f86b5..ccc12b1 100644 --- a/util/src/length_prefix_encoding/encoder.rs +++ b/util/src/length_prefix_encoding/encoder.rs @@ -9,46 +9,61 @@ use zeroize::Zeroize; use crate::{io::IoResultKindHintExt, result::ensure_or}; +/// Size of the length prefix header in bytes - equal to the size of a u64 pub const HEADER_SIZE: usize = std::mem::size_of::(); #[derive(Error, Debug, Clone, Copy)] #[error("Write position is out of buffer bounds")] +/// Error type indicating that a write position is beyond the boundaries of the allocated buffer pub struct PositionOutOfBufferBounds; #[derive(Error, Debug, Clone, Copy)] #[error("Write position is out of message bounds")] +/// Error type indicating that a write position is beyond the boundaries of the message pub struct PositionOutOfMessageBounds; #[derive(Error, Debug, Clone, Copy)] #[error("Write position is out of header bounds")] +/// Error type indicating that a write position is beyond the boundaries of the header pub struct PositionOutOfHeaderBounds; #[derive(Error, Debug, Clone, Copy)] #[error("Message length is bigger than buffer length")] +/// Error type indicating that the message length is larger than the available buffer space pub struct MessageTooLarge; #[derive(Error, Debug, Clone, Copy)] +/// Error type for message length sanity checks pub enum MessageLenSanityError { + /// Error indicating position is beyond message boundaries #[error("{0:?}")] PositionOutOfMessageBounds(#[from] PositionOutOfMessageBounds), + /// Error indicating message length exceeds buffer capacity #[error("{0:?}")] MessageTooLarge(#[from] MessageTooLarge), } #[derive(Error, Debug, Clone, Copy)] +/// Error type for position bounds checking pub enum PositionSanityError { + /// Error indicating position is beyond message boundaries #[error("{0:?}")] PositionOutOfMessageBounds(#[from] PositionOutOfMessageBounds), + /// Error indicating position is beyond buffer boundaries #[error("{0:?}")] PositionOutOfBufferBounds(#[from] PositionOutOfBufferBounds), } #[derive(Error, Debug, Clone, Copy)] +/// Error type combining all sanity check errors pub enum SanityError { + /// Error indicating position is beyond message boundaries #[error("{0:?}")] PositionOutOfMessageBounds(#[from] PositionOutOfMessageBounds), + /// Error indicating position is beyond buffer boundaries #[error("{0:?}")] PositionOutOfBufferBounds(#[from] PositionOutOfBufferBounds), + /// Error indicating message length exceeds buffer capacity #[error("{0:?}")] MessageTooLarge(#[from] MessageTooLarge), } @@ -86,12 +101,16 @@ impl From for SanityError { } } +/// Result of a write operation on an IO stream pub struct WriteToIoReturn { + /// Number of bytes successfully written in this operation pub bytes_written: usize, + /// Whether the write operation has completed fully pub done: bool, } #[derive(Clone, Copy, Debug)] +/// Length-prefixed encoder that adds a length header to data before writing pub struct LengthPrefixEncoder> { buf: Buf, header: [u8; HEADER_SIZE], @@ -99,6 +118,7 @@ pub struct LengthPrefixEncoder> { } impl> LengthPrefixEncoder { + /// Creates a new encoder from a buffer pub fn from_buffer(buf: Buf) -> Self { let (header, pos) = ([0u8; HEADER_SIZE], 0); let mut r = Self { buf, header, pos }; @@ -106,6 +126,7 @@ impl> LengthPrefixEncoder { r } + /// Creates a new encoder using the full buffer as a message pub fn from_message(msg: Buf) -> Self { let mut r = Self::from_buffer(msg); r.restart_write_with_new_message(r.buffer_bytes().len()) @@ -113,23 +134,27 @@ impl> LengthPrefixEncoder { r } + /// Creates a new encoder using part of the buffer as a message pub fn from_short_message(msg: Buf, len: usize) -> Result { let mut r = Self::from_message(msg); r.set_message_len(len)?; Ok(r) } + /// Creates a new encoder from buffer, message length and write position pub fn from_parts(buf: Buf, len: usize, pos: usize) -> Result { let mut r = Self::from_buffer(buf); r.set_msg_len_and_position(len, pos)?; Ok(r) } + /// Consumes the encoder and returns the underlying buffer pub fn into_buffer(self) -> Buf { let Self { buf, .. } = self; buf } + /// Consumes the encoder and returns buffer, message length and write position pub fn into_parts(self) -> (Buf, usize, usize) { let len = self.message_len(); let pos = self.writing_position(); @@ -137,11 +162,13 @@ impl> LengthPrefixEncoder { (buf, len, pos) } + /// Resets the encoder state pub fn clear(&mut self) { self.set_msg_len_and_position(0, 0).unwrap(); self.set_message_offset(0).unwrap(); } + /// Writes the full message to an IO writer, retrying on interrupts pub fn write_all_to_stdio(&mut self, mut w: W) -> io::Result<()> { use io::ErrorKind as K; loop { @@ -158,6 +185,7 @@ impl> LengthPrefixEncoder { } } + /// Writes the next chunk of data to an IO writer and returns number of bytes written and completion status pub fn write_to_stdio(&mut self, mut w: W) -> io::Result { if self.exhausted() { return Ok(WriteToIoReturn { @@ -177,10 +205,12 @@ impl> LengthPrefixEncoder { }) } + /// Resets write position to start for restarting output pub fn restart_write(&mut self) { self.set_writing_position(0).unwrap() } + /// Resets write position to start and updates message length for restarting with new data pub fn restart_write_with_new_message( &mut self, len: usize, @@ -189,6 +219,7 @@ impl> LengthPrefixEncoder { .map_err(|e| e.try_into().unwrap()) } + /// Returns the next unwritten slice of data to write from header or message pub fn next_slice_to_write(&self) -> &[u8] { let s = self.header_left(); if !s.is_empty() { @@ -203,66 +234,82 @@ impl> LengthPrefixEncoder { &[] } + /// Returns true if all data including header and message has been written pub fn exhausted(&self) -> bool { self.next_slice_to_write().is_empty() } + /// Returns slice containing full message data pub fn message(&self) -> &[u8] { &self.buffer_bytes()[..self.message_len()] } + /// Returns slice containing written portion of length header pub fn header_written(&self) -> &[u8] { &self.header()[..self.header_offset()] } + /// Returns slice containing unwritten portion of length header pub fn header_left(&self) -> &[u8] { &self.header()[self.header_offset()..] } + /// Returns slice containing written portion of message data pub fn message_written(&self) -> &[u8] { &self.message()[..self.message_offset()] } + /// Returns slice containing unwritten portion of message data pub fn message_left(&self) -> &[u8] { &self.message()[self.message_offset()..] } + /// Returns reference to underlying buffer pub fn buf(&self) -> &Buf { &self.buf } + /// Returns slice view of underlying buffer bytes pub fn buffer_bytes(&self) -> &[u8] { self.buf().borrow() } + /// Decodes and returns length header value as u64 pub fn decode_header(&self) -> u64 { u64::from_le_bytes(self.header) } + /// Returns slice containing raw length header bytes pub fn header(&self) -> &[u8; HEADER_SIZE] { &self.header } + /// Returns decoded message length from header pub fn message_len(&self) -> usize { self.decode_header() as usize } + /// Returns total encoded size including header and message bytes pub fn encoded_message_bytes(&self) -> usize { self.message_len() + HEADER_SIZE } + /// Returns current write position within header and message pub fn writing_position(&self) -> usize { self.pos } + /// Returns write offset within length header bytes pub fn header_offset(&self) -> usize { min(self.writing_position(), HEADER_SIZE) } + /// Returns write offset within message bytes pub fn message_offset(&self) -> usize { self.writing_position().saturating_sub(HEADER_SIZE) } + /// Sets new length header bytes with bounds checking pub fn set_header(&mut self, header: [u8; HEADER_SIZE]) -> Result<(), MessageLenSanityError> { self.offset_transaction(|t| { t.header = header; @@ -272,14 +319,17 @@ impl> LengthPrefixEncoder { }) } + /// Encodes and sets length header value with bounds checking pub fn encode_and_set_header(&mut self, header: u64) -> Result<(), MessageLenSanityError> { self.set_header(header.to_le_bytes()) } + /// Sets message lengthwith bounds checking pub fn set_message_len(&mut self, len: usize) -> Result<(), MessageLenSanityError> { self.encode_and_set_header(len as u64) } + /// Sets write position with message and buffer bounds checking pub fn set_writing_position(&mut self, pos: usize) -> Result<(), PositionSanityError> { self.offset_transaction(|t| { t.pos = pos; @@ -289,20 +339,24 @@ impl> LengthPrefixEncoder { }) } + /// Sets write position within header bytes with bounds checking pub fn set_header_offset(&mut self, off: usize) -> Result<(), PositionOutOfHeaderBounds> { ensure_or(off <= HEADER_SIZE, PositionOutOfHeaderBounds)?; self.set_writing_position(off).unwrap(); Ok(()) } + /// Sets write position within message bytes with bounds checking pub fn set_message_offset(&mut self, off: usize) -> Result<(), PositionSanityError> { self.set_writing_position(off + HEADER_SIZE) } + /// Advances write position by specified offset with bounds checking pub fn advance(&mut self, off: usize) -> Result<(), PositionSanityError> { self.set_writing_position(self.writing_position() + off) } + /// Sets message length and write position with bounds checking pub fn set_msg_len_and_position(&mut self, len: usize, pos: usize) -> Result<(), SanityError> { self.pos = 0; self.set_message_len(len)?; @@ -347,24 +401,29 @@ impl> LengthPrefixEncoder { } impl> LengthPrefixEncoder { + /// Gets a mutable reference to the underlying buffer pub fn buf_mut(&mut self) -> &mut Buf { &mut self.buf } + /// Gets the buffer as mutable bytes pub fn buffer_bytes_mut(&mut self) -> &mut [u8] { self.buf.borrow_mut() } + /// Gets a mutable reference to the message slice pub fn message_mut(&mut self) -> &mut [u8] { let off = self.message_len(); &mut self.buffer_bytes_mut()[..off] } + /// Gets a mutable reference to the written portion of the message pub fn message_written_mut(&mut self) -> &mut [u8] { let off = self.message_offset(); &mut self.message_mut()[..off] } + /// Gets a mutable reference to the unwritten portion of the message pub fn message_left_mut(&mut self) -> &mut [u8] { let off = self.message_offset(); &mut self.message_mut()[off..] diff --git a/util/src/length_prefix_encoding/mod.rs b/util/src/length_prefix_encoding/mod.rs index a4d5bf5..5d32cf7 100644 --- a/util/src/length_prefix_encoding/mod.rs +++ b/util/src/length_prefix_encoding/mod.rs @@ -1,2 +1,4 @@ +/// Module that handles decoding functionality pub mod decoder; +/// Module that handles encoding functionality pub mod encoder;