mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-12 15:49:22 -08:00
@@ -1,3 +1,5 @@
|
||||
#![warn(missing_docs)]
|
||||
#![recursion_limit = "256"]
|
||||
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
|
||||
|
||||
#[cfg(doctest)]
|
||||
|
||||
@@ -5,23 +5,70 @@ use crate::CondenseBeside;
|
||||
pub struct Beside<Val, Ret>(pub Val, pub Ret);
|
||||
|
||||
impl<Val, Ret> Beside<Val, Ret> {
|
||||
/// Get an immutable reference to the destination value
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rosenpass_to::Beside;
|
||||
///
|
||||
/// let beside = Beside(1, 2);
|
||||
/// assert_eq!(beside.dest(), &1);
|
||||
/// ```
|
||||
pub fn dest(&self) -> &Val {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get an immutable reference to the return value
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rosenpass_to::Beside;
|
||||
///
|
||||
/// let beside = Beside(1, 2);
|
||||
/// assert_eq!(beside.ret(), &2);
|
||||
/// ```
|
||||
pub fn ret(&self) -> &Ret {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the destination value
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rosenpass_to::Beside;
|
||||
///
|
||||
/// let mut beside = Beside(1, 2);
|
||||
/// *beside.dest_mut() = 3;
|
||||
/// assert_eq!(beside.dest(), &3);
|
||||
/// ```
|
||||
pub fn dest_mut(&mut self) -> &mut Val {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the return value
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rosenpass_to::Beside;
|
||||
///
|
||||
/// let mut beside = Beside(1, 2);
|
||||
/// *beside.ret_mut() = 3;
|
||||
/// assert_eq!(beside.ret(), &3);
|
||||
/// ```
|
||||
pub fn ret_mut(&mut self) -> &mut Ret {
|
||||
&mut self.1
|
||||
}
|
||||
|
||||
/// Perform beside condensation. See [CondenseBeside]
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use rosenpass_to::Beside;
|
||||
/// use rosenpass_to::CondenseBeside;
|
||||
///
|
||||
/// let beside = Beside(1, ());
|
||||
/// assert_eq!(beside.condense(), 1);
|
||||
/// ```
|
||||
pub fn condense(self) -> <Ret as CondenseBeside<Val>>::Condensed
|
||||
where
|
||||
Ret: CondenseBeside<Val>,
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
/// The function [Beside::condense()](crate::Beside::condense) is a shorthand for using the
|
||||
/// condense trait.
|
||||
pub trait CondenseBeside<Val> {
|
||||
/// The type that results from condensation.
|
||||
type Condensed;
|
||||
|
||||
/// Takes ownership of `self` and condenses it with the given value.
|
||||
fn condense(self, ret: Val) -> Self::Condensed;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/// Helper performing explicit unsized coercion.
|
||||
/// Used by the [to](crate::to()) function.
|
||||
pub trait DstCoercion<Dst: ?Sized> {
|
||||
/// Performs an explicit coercion to the destination type.
|
||||
fn coerce_dest(&mut self) -> &mut Dst;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use crate::{Beside, CondenseBeside};
|
||||
use std::borrow::BorrowMut;
|
||||
|
||||
// The To trait is the core of the to crate; most functions with destinations will either return
|
||||
// an object that is an instance of this trait or they will return `-> impl To<Destination,
|
||||
// Return_value`.
|
||||
//
|
||||
// A quick way to implement a function with destination is to use the
|
||||
// [with_destination(|param: &mut Type| ...)] higher order function.
|
||||
/// The To trait is the core of the to crate; most functions with destinations will either return
|
||||
/// an object that is an instance of this trait or they will return `-> impl To<Destination,
|
||||
/// Return_value`.
|
||||
///
|
||||
/// A quick way to implement a function with destination is to use the
|
||||
/// [with_destination(|param: &mut Type| ...)] higher order function.
|
||||
pub trait To<Dst: ?Sized, Ret>: Sized {
|
||||
/// Writes self to the destination `out` and returns a value of type `Ret`.
|
||||
///
|
||||
/// This is the core method that must be implemented by all types implementing `To`.
|
||||
fn to(self, out: &mut Dst) -> Ret;
|
||||
|
||||
/// Generate a destination on the fly with a lambda.
|
||||
|
||||
@@ -1,20 +1,38 @@
|
||||
use crate::To;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A struct that wraps a closure and implements the `To` trait
|
||||
///
|
||||
/// This allows passing closures that operate on a destination type `Dst`
|
||||
/// and return `Ret`.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// * `Dst` - The destination type the closure operates on
|
||||
/// * `Ret` - The return type of the closure
|
||||
/// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret`
|
||||
struct ToClosure<Dst, Ret, Fun>
|
||||
where
|
||||
Dst: ?Sized,
|
||||
Fun: FnOnce(&mut Dst) -> Ret,
|
||||
{
|
||||
/// The function to call.
|
||||
fun: Fun,
|
||||
/// Phantom data to hold the destination type
|
||||
_val: PhantomData<Box<Dst>>,
|
||||
}
|
||||
|
||||
/// Implementation of the `To` trait for ToClosure
|
||||
///
|
||||
/// This enables calling the wrapped closure with a destination reference.
|
||||
impl<Dst, Ret, Fun> To<Dst, Ret> for ToClosure<Dst, Ret, Fun>
|
||||
where
|
||||
Dst: ?Sized,
|
||||
Fun: FnOnce(&mut Dst) -> Ret,
|
||||
{
|
||||
/// Execute the wrapped closure with the given destination
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `out` - Mutable reference to the destination
|
||||
fn to(self, out: &mut Dst) -> Ret {
|
||||
(self.fun)(out)
|
||||
}
|
||||
@@ -22,6 +40,14 @@ where
|
||||
|
||||
/// Used to create a function with destination.
|
||||
///
|
||||
/// Creates a wrapper that implements the `To` trait for a closure that
|
||||
/// operates on a destination type.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// * `Dst` - The destination type the closure operates on
|
||||
/// * `Ret` - The return type of the closure
|
||||
/// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret`
|
||||
///
|
||||
/// See the tutorial in [readme.me]..
|
||||
pub fn with_destination<Dst, Ret, Fun>(fun: Fun) -> impl To<Dst, Ret>
|
||||
where
|
||||
|
||||
@@ -116,6 +116,10 @@ pub fn clone_fd_to_cloexec<Fd: AsFd>(fd: Fd, new: &mut OwnedFd) -> rustix::io::R
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
/// Duplicate a file descriptor, setting the close on exec flag.
|
||||
///
|
||||
/// This is slightly different from [clone_fd_cloexec], as this function supports specifying an
|
||||
/// explicit destination file descriptor.
|
||||
pub fn clone_fd_to_cloexec<Fd: AsFd>(fd: Fd, new: &mut OwnedFd) -> rustix::io::Result<()> {
|
||||
use rustix::io::{dup2, fcntl_setfd, FdFlags};
|
||||
dup2(&fd, new)?;
|
||||
@@ -431,4 +435,12 @@ mod tests {
|
||||
assert!(matches!(file.write(&buf), Err(_)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nullfd_read_write() {
|
||||
let nullfd = open_nullfd().unwrap();
|
||||
let mut buf = vec![0u8; 16];
|
||||
assert_eq!(rustix::io::read(&nullfd, &mut buf).unwrap(), 0);
|
||||
assert!(rustix::io::write(&nullfd, b"test").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ impl<T: IoErrorKind> TryIoErrorKind for T {
|
||||
///
|
||||
/// See [tutorial in the module](self).
|
||||
pub trait IoResultKindHintExt<T>: Sized {
|
||||
// Error trait including the ErrorKind hint
|
||||
/// Error type including the ErrorKind hint
|
||||
type Error;
|
||||
/// Helper for accessing [std::io::Error::kind] in Results
|
||||
///
|
||||
@@ -318,7 +318,7 @@ impl<T, E: IoErrorKind> IoResultKindHintExt<T> for Result<T, E> {
|
||||
///
|
||||
/// See [tutorial in the module](self).
|
||||
pub trait TryIoResultKindHintExt<T>: Sized {
|
||||
// Error trait including the ErrorKind hint
|
||||
/// Error type including the ErrorKind hint
|
||||
type Error;
|
||||
/// Helper for accessing [std::io::Error::kind] in Results where it may not be present
|
||||
///
|
||||
|
||||
@@ -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::<u64>();
|
||||
|
||||
#[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<Buf: BorrowMut<[u8]>> {
|
||||
header: [u8; HEADER_SIZE],
|
||||
buf: Buf,
|
||||
@@ -76,25 +96,33 @@ pub struct LengthPrefixDecoder<Buf: BorrowMut<[u8]>> {
|
||||
}
|
||||
|
||||
impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
/// 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<R: io::Read>(
|
||||
&mut self,
|
||||
mut r: R,
|
||||
@@ -125,6 +153,7 @@ impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads from the given reader into the decoder's internal buffers
|
||||
pub fn read_from_stdio<R: io::Read>(
|
||||
&mut self,
|
||||
mut r: R,
|
||||
@@ -150,6 +179,7 @@ impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the next buffer slice that can be written to
|
||||
pub fn next_slice_to_write_to(&mut self) -> Result<Option<&mut [u8]>, MessageTooLargeError> {
|
||||
fn some_if_nonempty(buf: &mut [u8]) -> Option<&mut [u8]> {
|
||||
match buf.is_empty() {
|
||||
@@ -172,6 +202,7 @@ impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
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<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
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<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
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<bool, MessageTooLargeError> {
|
||||
self.ensure_sufficient_msg_buffer()?;
|
||||
let msg_size = match self.get_header() {
|
||||
@@ -244,46 +286,55 @@ impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
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<usize> {
|
||||
match self.header_buffer_offset() == HEADER_SIZE {
|
||||
false => None,
|
||||
@@ -291,19 +342,23 @@ impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the size of the message if header is available
|
||||
pub fn message_size(&self) -> Option<usize> {
|
||||
self.get_header()
|
||||
}
|
||||
|
||||
/// Returns the total size of the encoded message including header
|
||||
pub fn encoded_message_bytes(&self) -> Option<usize> {
|
||||
self.message_size().map(|sz| sz + HEADER_SIZE)
|
||||
}
|
||||
|
||||
/// Returns a slice of the message fragment if available
|
||||
pub fn message_fragment(&self) -> Result<Option<&[u8]>, 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<Option<&mut [u8]>, MessageTooLargeError> {
|
||||
self.ensure_sufficient_msg_buffer()?;
|
||||
Ok(self
|
||||
@@ -311,12 +366,14 @@ impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
.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<Option<&[u8]>, 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<Option<&mut [u8]>, MessageTooLargeError> {
|
||||
@@ -325,24 +382,28 @@ impl<Buf: BorrowMut<[u8]>> LengthPrefixDecoder<Buf> {
|
||||
.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<Option<&[u8]>, 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<Option<&mut [u8]>, 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<Option<&[u8]>, 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<Option<&mut [u8]>, MessageTooLargeError> {
|
||||
let sz = self.message_size();
|
||||
self.message_fragment_avail_mut()
|
||||
|
||||
@@ -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::<u64>();
|
||||
|
||||
#[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<PositionSanityError> 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: Borrow<[u8]>> {
|
||||
buf: Buf,
|
||||
header: [u8; HEADER_SIZE],
|
||||
@@ -99,6 +118,7 @@ pub struct LengthPrefixEncoder<Buf: Borrow<[u8]>> {
|
||||
}
|
||||
|
||||
impl<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
/// 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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
r
|
||||
}
|
||||
|
||||
/// Creates a new encoder using part of the buffer as a message
|
||||
pub fn from_short_message(msg: Buf, len: usize) -> Result<Self, MessageLenSanityError> {
|
||||
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<Self, SanityError> {
|
||||
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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
(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<W: io::Write>(&mut self, mut w: W) -> io::Result<()> {
|
||||
use io::ErrorKind as K;
|
||||
loop {
|
||||
@@ -158,6 +185,7 @@ impl<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the next chunk of data to an IO writer and returns number of bytes written and completion status
|
||||
pub fn write_to_stdio<W: io::Write>(&mut self, mut w: W) -> io::Result<WriteToIoReturn> {
|
||||
if self.exhausted() {
|
||||
return Ok(WriteToIoReturn {
|
||||
@@ -177,10 +205,12 @@ impl<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
})
|
||||
}
|
||||
|
||||
/// 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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
.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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
&[]
|
||||
}
|
||||
|
||||
/// 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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
})
|
||||
}
|
||||
|
||||
/// 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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
})
|
||||
}
|
||||
|
||||
/// 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<Buf: Borrow<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
}
|
||||
|
||||
impl<Buf: BorrowMut<[u8]>> LengthPrefixEncoder<Buf> {
|
||||
/// 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..]
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
/// Module that handles decoding functionality
|
||||
pub mod decoder;
|
||||
/// Module that handles encoding functionality
|
||||
pub mod encoder;
|
||||
|
||||
@@ -2,19 +2,37 @@
|
||||
#![warn(clippy::missing_docs_in_private_items)]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
//! Core utility functions and types used across the codebase.
|
||||
|
||||
/// Base64 encoding and decoding functionality.
|
||||
pub mod b64;
|
||||
/// Build-time utilities and macros.
|
||||
pub mod build;
|
||||
/// Control flow abstractions and utilities.
|
||||
pub mod controlflow;
|
||||
/// File descriptor utilities.
|
||||
pub mod fd;
|
||||
/// File system operations and handling.
|
||||
pub mod file;
|
||||
/// Functional programming utilities.
|
||||
pub mod functional;
|
||||
/// Input/output operations.
|
||||
pub mod io;
|
||||
/// Length prefix encoding schemes implementation.
|
||||
pub mod length_prefix_encoding;
|
||||
/// Memory manipulation and allocation utilities.
|
||||
pub mod mem;
|
||||
/// MIO integration utilities.
|
||||
pub mod mio;
|
||||
/// Extended Option type functionality.
|
||||
pub mod option;
|
||||
/// Extended Result type functionality.
|
||||
pub mod result;
|
||||
/// Time and duration utilities.
|
||||
pub mod time;
|
||||
/// Type-level numbers and arithmetic.
|
||||
pub mod typenum;
|
||||
/// Zero-copy serialization utilities.
|
||||
pub mod zerocopy;
|
||||
/// Memory wiping utilities.
|
||||
pub mod zeroize;
|
||||
|
||||
@@ -22,6 +22,7 @@ macro_rules! cat {
|
||||
}
|
||||
|
||||
// TODO: consistent inout ordering
|
||||
/// Copy all bytes from `src` to `dst`. The lengths must match.
|
||||
pub fn cpy<T: BorrowMut<[u8]> + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, dst: &mut T) {
|
||||
dst.borrow_mut().copy_from_slice(src.borrow());
|
||||
}
|
||||
@@ -41,11 +42,13 @@ pub struct Forgetting<T> {
|
||||
}
|
||||
|
||||
impl<T> Forgetting<T> {
|
||||
/// Creates a new `Forgetting<T>` instance containing the given value.
|
||||
pub fn new(value: T) -> Self {
|
||||
let value = Some(value);
|
||||
Self { value }
|
||||
}
|
||||
|
||||
/// Extracts and returns the contained value, consuming self.
|
||||
pub fn extract(mut self) -> T {
|
||||
let mut value = None;
|
||||
swap(&mut value, &mut self.value);
|
||||
@@ -93,7 +96,9 @@ impl<T> Drop for Forgetting<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that provides a method to discard a value without explicitly handling its results.
|
||||
pub trait DiscardResultExt {
|
||||
/// Consumes and discards a value without doing anything with it.
|
||||
fn discard_result(self);
|
||||
}
|
||||
|
||||
@@ -101,7 +106,9 @@ impl<T> DiscardResultExt for T {
|
||||
fn discard_result(self) {}
|
||||
}
|
||||
|
||||
/// Trait that provides a method to explicitly forget values.
|
||||
pub trait ForgetExt {
|
||||
/// Consumes and forgets a value, preventing its destructor from running.
|
||||
fn forget(self);
|
||||
}
|
||||
|
||||
@@ -111,8 +118,11 @@ impl<T> ForgetExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that provides methods for swapping values.
|
||||
pub trait SwapWithExt {
|
||||
/// Takes ownership of `other` and swaps its value with `self`, returning the original value.
|
||||
fn swap_with(&mut self, other: Self) -> Self;
|
||||
/// Swaps the values between `self` and `other` in place.
|
||||
fn swap_with_mut(&mut self, other: &mut Self);
|
||||
}
|
||||
|
||||
@@ -127,7 +137,9 @@ impl<T> SwapWithExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that provides methods for swapping values with default values.
|
||||
pub trait SwapWithDefaultExt {
|
||||
/// Takes the current value and replaces it with the default value, returning the original.
|
||||
fn swap_with_default(&mut self) -> Self;
|
||||
}
|
||||
|
||||
@@ -137,6 +149,7 @@ impl<T: Default> SwapWithDefaultExt for T {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that provides a method to explicitly move values.
|
||||
pub trait MoveExt {
|
||||
/// Deliberately move the value
|
||||
///
|
||||
|
||||
@@ -6,14 +6,23 @@ use crate::{
|
||||
result::OkExt,
|
||||
};
|
||||
|
||||
/// Module containing I/O interest flags for Unix operations
|
||||
pub mod interest {
|
||||
use mio::Interest;
|
||||
|
||||
/// Interest flag indicating readability
|
||||
pub const R: Interest = Interest::READABLE;
|
||||
|
||||
/// Interest flag indicating writability
|
||||
pub const W: Interest = Interest::WRITABLE;
|
||||
|
||||
/// Interest flag indicating both readability and writability
|
||||
pub const RW: Interest = R.add(W);
|
||||
}
|
||||
|
||||
/// Extension trait providing additional functionality for Unix listener
|
||||
pub trait UnixListenerExt: Sized {
|
||||
/// Creates a new Unix listener by claiming ownership of a raw file descriptor
|
||||
fn claim_fd(fd: RawFd) -> anyhow::Result<Self>;
|
||||
}
|
||||
|
||||
@@ -27,9 +36,15 @@ impl UnixListenerExt for UnixListener {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait providing additional functionality for Unix streams
|
||||
pub trait UnixStreamExt: Sized {
|
||||
/// Creates a new Unix stream from an owned file descriptor
|
||||
fn from_fd(fd: OwnedFd) -> anyhow::Result<Self>;
|
||||
|
||||
/// Claims ownership of a raw file descriptor and creates a new Unix stream
|
||||
fn claim_fd(fd: RawFd) -> anyhow::Result<Self>;
|
||||
|
||||
/// Claims ownership of a raw file descriptor in place and creates a new Unix stream
|
||||
fn claim_fd_inplace(fd: RawFd) -> anyhow::Result<Self>;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ use uds::UnixStreamExt as FdPassingExt;
|
||||
|
||||
use crate::fd::{claim_fd_inplace, IntoStdioErr};
|
||||
|
||||
/// A wrapper around a socket that combines reading from the socket with tracking
|
||||
/// received file descriptors. Limits the maximum number of file descriptors that
|
||||
/// can be received in a single read operation via the `MAX_FDS` parameter.
|
||||
pub struct ReadWithFileDescriptors<const MAX_FDS: usize, Sock, BorrowSock, BorrowFds>
|
||||
where
|
||||
Sock: FdPassingExt,
|
||||
@@ -27,6 +30,8 @@ where
|
||||
BorrowSock: Borrow<Sock>,
|
||||
BorrowFds: BorrowMut<VecDeque<OwnedFd>>,
|
||||
{
|
||||
/// Creates a new `ReadWithFileDescriptors` by wrapping a socket and a file
|
||||
/// descriptor queue.
|
||||
pub fn new(socket: BorrowSock, fds: BorrowFds) -> Self {
|
||||
let _sock_dummy = PhantomData;
|
||||
Self {
|
||||
@@ -36,19 +41,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the wrapper and returns the underlying socket and file
|
||||
/// descriptor queue.
|
||||
pub fn into_parts(self) -> (BorrowSock, BorrowFds) {
|
||||
let Self { socket, fds, .. } = self;
|
||||
(socket, fds)
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying socket.
|
||||
pub fn socket(&self) -> &Sock {
|
||||
self.socket.borrow()
|
||||
}
|
||||
|
||||
/// Returns a reference to the file descriptor queue.
|
||||
pub fn fds(&self) -> &VecDeque<OwnedFd> {
|
||||
self.fds.borrow()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the file descriptor queue.
|
||||
pub fn fds_mut(&mut self) -> &mut VecDeque<OwnedFd> {
|
||||
self.fds.borrow_mut()
|
||||
}
|
||||
@@ -61,6 +71,7 @@ where
|
||||
BorrowSock: BorrowMut<Sock>,
|
||||
BorrowFds: BorrowMut<VecDeque<OwnedFd>>,
|
||||
{
|
||||
/// Returns a mutable reference to the underlying socket.
|
||||
pub fn socket_mut(&mut self) -> &mut Sock {
|
||||
self.socket.borrow_mut()
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use uds::UnixStreamExt as FdPassingExt;
|
||||
|
||||
use crate::{repeat, return_if};
|
||||
|
||||
/// A structure that facilitates writing data and file descriptors to a Unix domain socket
|
||||
pub struct WriteWithFileDescriptors<Sock, Fd, BorrowSock, BorrowFds>
|
||||
where
|
||||
Sock: FdPassingExt,
|
||||
@@ -30,6 +31,7 @@ where
|
||||
BorrowSock: Borrow<Sock>,
|
||||
BorrowFds: BorrowMut<VecDeque<Fd>>,
|
||||
{
|
||||
/// Creates a new `WriteWithFileDescriptors` instance with the given socket and file descriptor queue
|
||||
pub fn new(socket: BorrowSock, fds: BorrowFds) -> Self {
|
||||
let _sock_dummy = PhantomData;
|
||||
let _fd_dummy = PhantomData;
|
||||
@@ -41,19 +43,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes this instance and returns the underlying socket and file descriptor queue
|
||||
pub fn into_parts(self) -> (BorrowSock, BorrowFds) {
|
||||
let Self { socket, fds, .. } = self;
|
||||
(socket, fds)
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying socket
|
||||
pub fn socket(&self) -> &Sock {
|
||||
self.socket.borrow()
|
||||
}
|
||||
|
||||
/// Returns a reference to the file descriptor queue
|
||||
pub fn fds(&self) -> &VecDeque<Fd> {
|
||||
self.fds.borrow()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the file descriptor queue
|
||||
pub fn fds_mut(&mut self) -> &mut VecDeque<Fd> {
|
||||
self.fds.borrow_mut()
|
||||
}
|
||||
@@ -66,6 +72,7 @@ where
|
||||
BorrowSock: BorrowMut<Sock>,
|
||||
BorrowFds: BorrowMut<VecDeque<Fd>>,
|
||||
{
|
||||
/// Returns a mutable reference to the underlying socket
|
||||
pub fn socket_mut(&mut self) -> &mut Sock {
|
||||
self.socket.borrow_mut()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
/// A helper trait for turning any type value into `Some(value)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass_util::option::SomeExt;
|
||||
///
|
||||
/// let x = 42;
|
||||
/// let y = x.some();
|
||||
///
|
||||
/// assert_eq!(y, Some(42));
|
||||
/// ```
|
||||
pub trait SomeExt: Sized {
|
||||
/// Wraps the calling value in `Some()`.
|
||||
fn some(self) -> Option<Self> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ macro_rules! attempt {
|
||||
};
|
||||
}
|
||||
|
||||
/// Trait for the ok operation, which provides a way to convert a value into a Result
|
||||
pub trait OkExt<E>: Sized {
|
||||
/// Wraps a value in a Result::Ok variant
|
||||
fn ok(self) -> Result<Self, E>;
|
||||
}
|
||||
|
||||
@@ -25,6 +27,7 @@ impl<T, E> OkExt<E> for T {
|
||||
///
|
||||
/// Implementations must not panic.
|
||||
pub trait GuaranteedValue {
|
||||
/// The value type that will be returned by guaranteed()
|
||||
type Value;
|
||||
|
||||
/// Extract the contained value while being panic-safe, like .unwrap()
|
||||
@@ -35,7 +38,11 @@ pub trait GuaranteedValue {
|
||||
fn guaranteed(self) -> Self::Value;
|
||||
}
|
||||
|
||||
/// Extension trait for adding finally operation to types
|
||||
pub trait FinallyExt {
|
||||
/// Executes a closure with mutable access to self and returns self
|
||||
///
|
||||
/// The closure is guaranteed to be executed before returning.
|
||||
fn finally<F: FnOnce(&mut Self)>(self, f: F) -> Self;
|
||||
}
|
||||
|
||||
@@ -125,6 +132,18 @@ impl<T> GuaranteedValue for Guaranteed<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks a condition is true and returns an error if not.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rosenpass_util::result::ensure_or;
|
||||
/// let result = ensure_or(5 > 3, "not greater");
|
||||
/// assert!(result.is_ok());
|
||||
///
|
||||
/// let result = ensure_or(5 < 3, "not less");
|
||||
/// assert!(result.is_err());
|
||||
/// ```
|
||||
pub fn ensure_or<E>(b: bool, err: E) -> Result<(), E> {
|
||||
match b {
|
||||
true => Ok(()),
|
||||
@@ -132,6 +151,18 @@ pub fn ensure_or<E>(b: bool, err: E) -> Result<(), E> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates to an error if the condition is true.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rosenpass_util::result::bail_if;
|
||||
/// let result = bail_if(false, "not bailed");
|
||||
/// assert!(result.is_ok());
|
||||
///
|
||||
/// let result = bail_if(true, "bailed");
|
||||
/// assert!(result.is_err());
|
||||
/// ```
|
||||
pub fn bail_if<E>(b: bool, err: E) -> Result<(), E> {
|
||||
ensure_or(!b, err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,16 @@ use std::time::Instant;
|
||||
/// This is a simple wrapper around `std::time::Instant` that provides a
|
||||
/// convenient way to get the seconds elapsed since the creation of the
|
||||
/// `Timebase` instance.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rosenpass_util::time::Timebase;
|
||||
///
|
||||
/// let timebase = Timebase::default();
|
||||
/// let now = timebase.now();
|
||||
/// assert!(now > 0.0);
|
||||
/// ```
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Timebase(Instant);
|
||||
|
||||
@@ -16,6 +16,7 @@ macro_rules! typenum2const {
|
||||
|
||||
/// Trait implemented by constant integers to facilitate conversion to constant integers
|
||||
pub trait IntoConst<T> {
|
||||
/// The constant value after conversion
|
||||
const VALUE: T;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,56 +7,68 @@ use zeroize::Zeroize;
|
||||
use crate::zeroize::ZeroizedExt;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// A convenience type for working with mutable references to a buffer and an
|
||||
/// expected target type.
|
||||
pub struct RefMaker<B: Sized, T> {
|
||||
buf: B,
|
||||
_phantom_t: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<B, T> RefMaker<B, T> {
|
||||
/// Creates a new RefMaker with the given buffer
|
||||
pub fn new(buf: B) -> Self {
|
||||
let _phantom_t = PhantomData;
|
||||
Self { buf, _phantom_t }
|
||||
}
|
||||
|
||||
/// Returns the size in bytes needed for target type T
|
||||
pub const fn target_size() -> usize {
|
||||
std::mem::size_of::<T>()
|
||||
}
|
||||
|
||||
/// Consumes this RefMaker and returns the inner buffer
|
||||
pub fn into_buf(self) -> B {
|
||||
self.buf
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner buffer
|
||||
pub fn buf(&self) -> &B {
|
||||
&self.buf
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the inner buffer
|
||||
pub fn buf_mut(&mut self) -> &mut B {
|
||||
&mut self.buf
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: ByteSlice, T> RefMaker<B, T> {
|
||||
/// Parses the buffer into a reference of type T
|
||||
pub fn parse(self) -> anyhow::Result<Ref<B, T>> {
|
||||
self.ensure_fit()?;
|
||||
Ref::<B, T>::new(self.buf).context("Parser error!")
|
||||
}
|
||||
|
||||
/// Splits the buffer into a RefMaker containing the first `target_size` bytes and the remaining tail
|
||||
pub fn from_prefix_with_tail(self) -> anyhow::Result<(Self, B)> {
|
||||
self.ensure_fit()?;
|
||||
let (head, tail) = self.buf.split_at(Self::target_size());
|
||||
Ok((Self::new(head), tail))
|
||||
}
|
||||
|
||||
/// Splits the buffer into two RefMakers, with the first containing the first `target_size` bytes
|
||||
pub fn split_prefix(self) -> anyhow::Result<(Self, Self)> {
|
||||
self.ensure_fit()?;
|
||||
let (head, tail) = self.buf.split_at(Self::target_size());
|
||||
Ok((Self::new(head), Self::new(tail)))
|
||||
}
|
||||
|
||||
/// Returns a RefMaker containing only the first `target_size` bytes
|
||||
pub fn from_prefix(self) -> anyhow::Result<Self> {
|
||||
Ok(Self::from_prefix_with_tail(self)?.0)
|
||||
}
|
||||
|
||||
/// Splits the buffer into a RefMaker containing the last `target_size` bytes and the preceding head
|
||||
pub fn from_suffix_with_head(self) -> anyhow::Result<(Self, B)> {
|
||||
self.ensure_fit()?;
|
||||
let point = self.bytes().len() - Self::target_size();
|
||||
@@ -64,6 +76,7 @@ impl<B: ByteSlice, T> RefMaker<B, T> {
|
||||
Ok((Self::new(tail), head))
|
||||
}
|
||||
|
||||
/// Splits the buffer into two RefMakers, with the second containing the last `target_size` bytes
|
||||
pub fn split_suffix(self) -> anyhow::Result<(Self, Self)> {
|
||||
self.ensure_fit()?;
|
||||
let point = self.bytes().len() - Self::target_size();
|
||||
@@ -71,14 +84,17 @@ impl<B: ByteSlice, T> RefMaker<B, T> {
|
||||
Ok((Self::new(head), Self::new(tail)))
|
||||
}
|
||||
|
||||
/// Returns a RefMaker containing only the last `target_size` bytes
|
||||
pub fn from_suffix(self) -> anyhow::Result<Self> {
|
||||
Ok(Self::from_suffix_with_head(self)?.0)
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying bytes
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
self.buf().deref()
|
||||
}
|
||||
|
||||
/// Ensures the buffer is large enough to hold type T
|
||||
pub fn ensure_fit(&self) -> anyhow::Result<()> {
|
||||
let have = self.bytes().len();
|
||||
let need = Self::target_size();
|
||||
@@ -91,10 +107,12 @@ impl<B: ByteSlice, T> RefMaker<B, T> {
|
||||
}
|
||||
|
||||
impl<B: ByteSliceMut, T> RefMaker<B, T> {
|
||||
/// Creates a zeroed reference of type T from the buffer
|
||||
pub fn make_zeroized(self) -> anyhow::Result<Ref<B, T>> {
|
||||
self.zeroized().parse()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying bytes
|
||||
pub fn bytes_mut(&mut self) -> &mut [u8] {
|
||||
self.buf_mut().deref_mut()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||
|
||||
/// A trait for converting a `Ref<B, T>` into a `Ref<&[u8], T>`.
|
||||
pub trait ZerocopyEmancipateExt<B, T> {
|
||||
/// Converts this reference into a reference backed by a byte slice.
|
||||
fn emancipate(&self) -> Ref<&[u8], T>;
|
||||
}
|
||||
|
||||
/// A trait for converting a `Ref<B, T>` into a mutable `Ref<&mut [u8], T>`.
|
||||
pub trait ZerocopyEmancipateMutExt<B, T> {
|
||||
/// Converts this reference into a mutable reference backed by a byte slice.
|
||||
fn emancipate_mut(&mut self) -> Ref<&mut [u8], T>;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,24 @@ use zerocopy::{ByteSlice, ByteSliceMut, Ref};
|
||||
|
||||
use super::RefMaker;
|
||||
|
||||
/// Extension trait for zero-copy slice operations.
|
||||
pub trait ZerocopySliceExt: Sized + ByteSlice {
|
||||
/// Creates a new `RefMaker` for the given slice.
|
||||
fn zk_ref_maker<T>(self) -> RefMaker<Self, T> {
|
||||
RefMaker::<Self, T>::new(self)
|
||||
}
|
||||
|
||||
/// Parses the slice into a zero-copy reference.
|
||||
fn zk_parse<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||
self.zk_ref_maker().parse()
|
||||
}
|
||||
|
||||
/// Parses a prefix of the slice into a zero-copy reference.
|
||||
fn zk_parse_prefix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||
self.zk_ref_maker().from_prefix()?.parse()
|
||||
}
|
||||
|
||||
/// Parses a suffix of the slice into a zero-copy reference.
|
||||
fn zk_parse_suffix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||
self.zk_ref_maker().from_suffix()?.parse()
|
||||
}
|
||||
@@ -22,15 +27,19 @@ pub trait ZerocopySliceExt: Sized + ByteSlice {
|
||||
|
||||
impl<B: ByteSlice> ZerocopySliceExt for B {}
|
||||
|
||||
/// Extension trait for zero-copy slice operations with mutable slices.
|
||||
pub trait ZerocopyMutSliceExt: ZerocopySliceExt + Sized + ByteSliceMut {
|
||||
/// Creates a new zeroed reference from the entire slice.
|
||||
fn zk_zeroized<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||
self.zk_ref_maker().make_zeroized()
|
||||
}
|
||||
|
||||
/// Creates a new zeroed reference from a prefix of the slice.
|
||||
fn zk_zeroized_from_prefix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||
self.zk_ref_maker().from_prefix()?.make_zeroized()
|
||||
}
|
||||
|
||||
/// Creates a new zeroed reference from a suffix of the slice.
|
||||
fn zk_zeroized_from_suffix<T>(self) -> anyhow::Result<Ref<Self, T>> {
|
||||
self.zk_ref_maker().from_suffix()?.make_zeroized()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// Extension trait providing a method for zeroizing a value and returning it
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use zeroize::Zeroize;
|
||||
/// use rosenpass_util::zeroize::ZeroizedExt;
|
||||
///
|
||||
/// let mut value = String::from("hello");
|
||||
/// value.zeroize();
|
||||
/// assert_eq!(value, "");
|
||||
///
|
||||
/// let value = String::from("hello").zeroized();
|
||||
/// assert_eq!(value, "");
|
||||
/// ```
|
||||
pub trait ZeroizedExt: Zeroize + Sized {
|
||||
/// Zeroizes the value in place and returns self
|
||||
fn zeroized(mut self) -> Self {
|
||||
self.zeroize();
|
||||
self
|
||||
|
||||
Reference in New Issue
Block a user