mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-04 19:11:52 -08:00
docs(util): add docs and examples for the remaining util crate
This commit is contained in:
@@ -78,8 +78,8 @@ pub trait Build<T>: Sized {
|
||||
|
||||
/// A type that can be incrementally built from a type that can [Build] it
|
||||
///
|
||||
/// This is similar to an option, where [Self::Void] is [std::Option::None],
|
||||
/// [Self::Product] is [std::Option::Some], except that there is a third
|
||||
/// This is similar to an option, where [Self::Void] is [std::option::Option::None],
|
||||
/// [Self::Product] is [std::option::Option::Some], except that there is a third
|
||||
/// intermediate state [Self::Builder] that represents a Some/Product value
|
||||
/// in the process of being made.
|
||||
///
|
||||
@@ -508,9 +508,9 @@ where
|
||||
matches!(self, Self::Void)
|
||||
}
|
||||
|
||||
/// Returns `true` if the construction site is [`InProgress`].
|
||||
/// Returns `true` if the construction site is in the [`Builder`] phase.
|
||||
///
|
||||
/// [`InProgress`]: ConstructionSite::InProgress
|
||||
/// [`Builder`]: ConstructionSite::Builder
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -541,9 +541,10 @@ where
|
||||
matches!(self, Self::Builder(..))
|
||||
}
|
||||
|
||||
/// Returns `true` if the construction site is [`Done`].
|
||||
/// Returns `true` if the construction site is in the [`Product`] phase and
|
||||
/// is therefore done.
|
||||
///
|
||||
/// [`Done`]: ConstructionSite::Done
|
||||
/// [`Product`]: ConstructionSite::Product
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// A collection of control flow utility macros
|
||||
//! A collection of control flow utility macros
|
||||
|
||||
#[macro_export]
|
||||
/// A simple for loop to repeat a $body a number of times
|
||||
@@ -33,7 +33,7 @@ macro_rules! repeat {
|
||||
/// 0
|
||||
/// }
|
||||
/// assert_eq!(test_fn(), 0);
|
||||
|
||||
///
|
||||
/// fn test_fn2() -> i32 {
|
||||
/// return_unless!(false, 1);
|
||||
/// 0
|
||||
@@ -65,7 +65,7 @@ macro_rules! return_unless {
|
||||
/// 0
|
||||
/// }
|
||||
/// assert_eq!(test_fn(), 1);
|
||||
|
||||
///
|
||||
/// fn test_fn2() -> i32 {
|
||||
/// return_if!(false, 1);
|
||||
/// 0
|
||||
@@ -98,7 +98,7 @@ macro_rules! return_if {
|
||||
/// sum += 1;
|
||||
/// }
|
||||
/// assert_eq!(sum, 5);
|
||||
|
||||
///
|
||||
/// let mut sum = 0;
|
||||
/// 'one: for _ in 0..10 {
|
||||
/// for j in 0..20 {
|
||||
@@ -134,7 +134,7 @@ macro_rules! break_if {
|
||||
/// sum += 1;
|
||||
/// }
|
||||
/// assert_eq!(sum, 9);
|
||||
|
||||
///
|
||||
/// let mut sum = 0;
|
||||
/// 'one: for i in 0..10 {
|
||||
/// continue_if!(i == 5, 'one);
|
||||
|
||||
141
util/src/fd.rs
141
util/src/fd.rs
@@ -89,6 +89,30 @@ pub fn claim_fd_inplace(fd: RawFd) -> rustix::io::Result<OwnedFd> {
|
||||
///
|
||||
/// Will panic if the given file descriptor is negative of or larger than
|
||||
/// the file descriptor numbers permitted by the operating system.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use std::fs::File;
|
||||
/// # use std::io::Read;
|
||||
/// # use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
/// # use std::os::fd::IntoRawFd;
|
||||
/// # use rustix::fd::AsFd;
|
||||
/// # use rosenpass_util::fd::mask_fd;
|
||||
///
|
||||
/// // Open a temporary file
|
||||
/// let fd = tempfile::tempfile().unwrap().into_raw_fd();
|
||||
/// assert!(fd >= 0);
|
||||
///
|
||||
/// // Mask the file descriptor
|
||||
/// mask_fd(fd).unwrap();
|
||||
///
|
||||
/// // Verify the file descriptor now points to `/dev/null`
|
||||
/// // Reading from `/dev/null` always returns 0 bytes
|
||||
/// let mut replaced_file = unsafe { File::from_raw_fd(fd) };
|
||||
/// let mut buffer = [0u8; 4];
|
||||
/// let bytes_read = replaced_file.read(&mut buffer).unwrap();
|
||||
/// assert_eq!(bytes_read, 0);
|
||||
/// ```
|
||||
pub fn mask_fd(fd: RawFd) -> rustix::io::Result<()> {
|
||||
// Safety: because the OwnedFd resulting from OwnedFd::from_raw_fd is wrapped in a Forgetting,
|
||||
// it never gets dropped, meaning that fd is never closed and thus outlives the OwnedFd
|
||||
@@ -286,14 +310,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Distinguish different socket address familys; e.g. IP and unix sockets
|
||||
/// Distinguish different socket address families; e.g. IP and unix sockets
|
||||
#[cfg(target_os = "linux")]
|
||||
pub trait GetSocketDomain {
|
||||
/// Error type returned by operations in this trait
|
||||
type Error;
|
||||
/// Retrieve the socket domain (address family)
|
||||
fn socket_domain(&self) -> Result<rustix::net::AddressFamily, Self::Error>;
|
||||
/// Alias for [socket_domain]
|
||||
/// Alias for [Self::socket_domain]
|
||||
fn socket_address_family(&self) -> Result<rustix::net::AddressFamily, Self::Error> {
|
||||
self.socket_domain()
|
||||
}
|
||||
@@ -320,9 +344,67 @@ where
|
||||
pub trait GetUnixSocketType {
|
||||
/// Error type returned by operations in this trait
|
||||
type Error;
|
||||
/// Check if the socket is a unix stream socket
|
||||
|
||||
/// Checks whether the socket is a Unix stream socket.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(true)` if the socket is a Unix stream socket.
|
||||
/// - `Ok(false)` if the socket is not a Unix stream socket.
|
||||
/// - `Err(Self::Error)` if there is an error while performing the check.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::fs::File;
|
||||
/// # use std::os::fd::{AsFd, BorrowedFd};
|
||||
/// # use std::os::unix::net::UnixListener;
|
||||
/// # use tempfile::NamedTempFile;
|
||||
/// # use rosenpass_util::fd::GetUnixSocketType;
|
||||
/// let f = {
|
||||
/// // Generate a temp file and take its path
|
||||
/// // Remove the temp file
|
||||
/// // Create a unix socket on the temp path that is not unused
|
||||
/// let temp_file = NamedTempFile::new().unwrap();
|
||||
/// let socket_path = temp_file.path().to_owned();
|
||||
/// std::fs::remove_file(&socket_path).unwrap();
|
||||
/// UnixListener::bind(socket_path).unwrap()
|
||||
/// };
|
||||
/// assert!(matches!(f.as_fd().is_unix_stream_socket(), Ok(true)));
|
||||
/// ```
|
||||
fn is_unix_stream_socket(&self) -> Result<bool, Self::Error>;
|
||||
/// Returns Ok(()) only if the underlying socket is a unix stream socket
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::fs::File;
|
||||
/// # use std::os::fd::{AsFd, BorrowedFd};
|
||||
/// # use std::os::unix::net::{UnixDatagram, UnixListener};
|
||||
/// # use tempfile::NamedTempFile;
|
||||
/// # use rosenpass_util::fd::GetUnixSocketType;
|
||||
/// let f = {
|
||||
/// // Generate a temp file and take its path
|
||||
/// // Remove the temp file
|
||||
/// // Create a unix socket on the temp path that is not unused
|
||||
/// let temp_file = NamedTempFile::new().unwrap();
|
||||
/// let socket_path = temp_file.path().to_owned();
|
||||
/// std::fs::remove_file(&socket_path).unwrap();
|
||||
/// UnixListener::bind(socket_path).unwrap()
|
||||
/// };
|
||||
/// assert!(matches!(f.as_fd().demand_unix_stream_socket(), Ok(())));
|
||||
/// // Error if the FD is a file
|
||||
/// let temp_file = NamedTempFile::new().unwrap();
|
||||
/// assert_eq!(temp_file.as_fd().demand_unix_stream_socket().err().unwrap().to_string(),
|
||||
/// "Socket operation on non-socket (os error 88)"
|
||||
/// );
|
||||
/// // Error if the FD is a Unix stream with a wrong mode (e.g. Datagram)
|
||||
/// let f = {
|
||||
/// let temp_file = NamedTempFile::new().unwrap();
|
||||
/// let socket_path = temp_file.path().to_owned();
|
||||
/// std::fs::remove_file(&socket_path).unwrap();
|
||||
/// UnixDatagram::bind(socket_path).unwrap()
|
||||
/// };
|
||||
/// assert_eq!(f.as_fd().demand_unix_stream_socket().err().unwrap().to_string(),
|
||||
/// "Expected unix socket in stream mode, but mode is SocketType(2)"
|
||||
/// );
|
||||
/// ```
|
||||
fn demand_unix_stream_socket(&self) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
@@ -352,16 +434,65 @@ where
|
||||
#[cfg(target_os = "linux")]
|
||||
/// Distinguish between different network socket protocols (e.g. tcp, udp)
|
||||
pub trait GetSocketProtocol {
|
||||
/// Retrieve the socket protocol
|
||||
/// Retrieves the socket's protocol.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(Some(Protocol))`: The protocol of the socket if available.
|
||||
/// - `Ok(None)`: If the protocol information is unavailable.
|
||||
/// - `Err(rustix::io::Errno)`: If an error occurs while retrieving the protocol.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::net::UdpSocket;
|
||||
/// # use std::os::fd::{AsFd, AsRawFd};
|
||||
/// # use rosenpass_util::fd::GetSocketProtocol;
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:0")?;
|
||||
/// assert_eq!(socket.as_fd().socket_protocol().unwrap().unwrap(), rustix::net::ipproto::UDP);
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
fn socket_protocol(&self) -> Result<Option<rustix::net::Protocol>, rustix::io::Errno>;
|
||||
/// Check if the socket is a udp socket
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::net::UdpSocket;
|
||||
/// # use std::net::TcpListener;
|
||||
/// # use std::os::fd::{AsFd, AsRawFd};
|
||||
/// # use rosenpass_util::fd::GetSocketProtocol;
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:0")?;
|
||||
/// assert!(socket.as_fd().is_udp_socket().unwrap());
|
||||
///
|
||||
/// let socket = TcpListener::bind("127.0.0.1:0")?;
|
||||
/// assert!(!socket.as_fd().is_udp_socket().unwrap());
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
fn is_udp_socket(&self) -> Result<bool, rustix::io::Errno> {
|
||||
self.socket_protocol()?
|
||||
.map(|p| p == rustix::net::ipproto::UDP)
|
||||
.unwrap_or(false)
|
||||
.ok()
|
||||
}
|
||||
/// Return Ok(()) only if the socket is a udp socket
|
||||
|
||||
/// Ensures that the socket is a UDP socket, returning an error otherwise.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(())` if the socket is a UDP socket.
|
||||
/// - `Err(anyhow::Error)` if the socket is not a UDP socket or if an error occurs retrieving the socket protocol.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use std::net::UdpSocket;
|
||||
/// # use std::net::TcpListener;
|
||||
/// # use std::os::fd::{AsFd, AsRawFd};
|
||||
/// # use rosenpass_util::fd::GetSocketProtocol;
|
||||
/// let socket = UdpSocket::bind("127.0.0.1:0")?;
|
||||
/// assert!(matches!(socket.as_fd().demand_udp_socket(), Ok(())));
|
||||
///
|
||||
/// let socket = TcpListener::bind("127.0.0.1:0")?;
|
||||
/// assert_eq!(socket.as_fd().demand_udp_socket().unwrap_err().to_string(),
|
||||
/// "Not a udp socket, instead socket protocol is: Protocol(6)");
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
fn demand_udp_socket(&self) -> anyhow::Result<()> {
|
||||
match self.socket_protocol() {
|
||||
Ok(Some(rustix::net::ipproto::UDP)) => Ok(()),
|
||||
|
||||
@@ -244,7 +244,6 @@
|
||||
use std::{borrow::Borrow, io};
|
||||
|
||||
use anyhow::ensure;
|
||||
use zerocopy::AsBytes;
|
||||
|
||||
/// Generic trait for accessing [std::io::Error::kind]
|
||||
///
|
||||
|
||||
166
util/src/mem.rs
166
util/src/mem.rs
@@ -1,10 +1,33 @@
|
||||
//!
|
||||
//! This module provides functions for copying data, concatenating byte arrays,
|
||||
//! and various traits and types that help manage values, including preventing
|
||||
//! drops, discarding results, and swapping values.
|
||||
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::cmp::min;
|
||||
use std::mem::{forget, swap};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Concatenate two byte arrays
|
||||
// TODO: Zeroize result?
|
||||
/// Concatenate multiple byte slices into a fixed-size byte array.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the concatenated length does not match the declared length.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_util::cat;
|
||||
/// let arr = cat!(6; b"abc", b"def");
|
||||
/// assert_eq!(&arr, b"abcdef");
|
||||
///
|
||||
/// let err = std::panic::catch_unwind(|| cat!(5; b"abc", b"def"));
|
||||
/// assert!(matches!(err, Err(_)));
|
||||
///
|
||||
/// let err = std::panic::catch_unwind(|| cat!(7; b"abc", b"def"));
|
||||
/// assert!(matches!(err, Err(_)));
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! cat {
|
||||
($len:expr; $($toks:expr),+) => {{
|
||||
@@ -22,12 +45,37 @@ macro_rules! cat {
|
||||
}
|
||||
|
||||
// TODO: consistent inout ordering
|
||||
/// Copy all bytes from `src` to `dst`. The lengths must match.
|
||||
/// Copy bytes from `src` to `dst`, requiring equal lengths.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if lengths differ.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_util::mem::cpy;
|
||||
/// let src = [1, 2, 3];
|
||||
/// let mut dst = [0; 3];
|
||||
/// cpy(&src, &mut dst);
|
||||
/// assert_eq!(dst, [1, 2, 3]);
|
||||
/// ```
|
||||
pub fn cpy<T: BorrowMut<[u8]> + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, dst: &mut T) {
|
||||
dst.borrow_mut().copy_from_slice(src.borrow());
|
||||
}
|
||||
|
||||
/// Copy from `src` to `dst`. If `src` and `dst` are not of equal length, copy as many bytes as possible.
|
||||
/// Copy from `src` to `dst`. If `src` and `dst` are not of equal length,
|
||||
/// copy as many bytes as possible.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_util::mem::cpy_min;
|
||||
/// let src = [1, 2, 3, 4];
|
||||
/// let mut dst = [0; 2];
|
||||
/// cpy_min(&src, &mut dst);
|
||||
/// assert_eq!(dst, [1, 2]);
|
||||
/// ```
|
||||
pub fn cpy_min<T: BorrowMut<[u8]> + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, dst: &mut T) {
|
||||
let src = src.borrow();
|
||||
let dst = dst.borrow_mut();
|
||||
@@ -35,20 +83,30 @@ pub fn cpy_min<T: BorrowMut<[u8]> + ?Sized, F: Borrow<[u8]> + ?Sized>(src: &F, d
|
||||
dst[..len].copy_from_slice(&src[..len]);
|
||||
}
|
||||
|
||||
/// Wrapper type to inhibit calling [std::mem::Drop] when the underlying variable is freed
|
||||
/// Wrapper type to inhibit calling [std::mem::Drop] when the underlying
|
||||
/// variable is freed
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_util::mem::Forgetting;
|
||||
/// let f = Forgetting::new(String::from("hello"));
|
||||
/// assert_eq!(&*f, "hello");
|
||||
/// let val = f.extract();
|
||||
/// assert_eq!(val, "hello");
|
||||
/// ```
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Default)]
|
||||
pub struct Forgetting<T> {
|
||||
value: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> Forgetting<T> {
|
||||
/// Creates a new `Forgetting<T>` instance containing the given value.
|
||||
/// Create a new `Forgetting` wrapping `value`.
|
||||
pub fn new(value: T) -> Self {
|
||||
let value = Some(value);
|
||||
Self { value }
|
||||
Self { value: Some(value) }
|
||||
}
|
||||
|
||||
/// Extracts and returns the contained value, consuming self.
|
||||
/// Consume and return the inner value.
|
||||
pub fn extract(mut self) -> T {
|
||||
let mut value = None;
|
||||
swap(&mut value, &mut self.value);
|
||||
@@ -97,6 +155,13 @@ impl<T> Drop for Forgetting<T> {
|
||||
}
|
||||
|
||||
/// A trait that provides a method to discard a value without explicitly handling its results.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rosenpass_util::mem::DiscardResultExt;
|
||||
/// let result: () = (|| { return 42 })().discard_result(); // Just discard
|
||||
/// ```
|
||||
pub trait DiscardResultExt {
|
||||
/// Consumes and discards a value without doing anything with it.
|
||||
fn discard_result(self);
|
||||
@@ -107,8 +172,16 @@ impl<T> DiscardResultExt for T {
|
||||
}
|
||||
|
||||
/// Trait that provides a method to explicitly forget values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rosenpass_util::mem::ForgetExt;
|
||||
/// let s = String::from("no drop");
|
||||
/// s.forget(); // destructor not run
|
||||
/// ```
|
||||
pub trait ForgetExt {
|
||||
/// Consumes and forgets a value, preventing its destructor from running.
|
||||
/// Forget the value.
|
||||
fn forget(self);
|
||||
}
|
||||
|
||||
@@ -119,10 +192,23 @@ impl<T> ForgetExt for T {
|
||||
}
|
||||
|
||||
/// Extension trait that provides methods for swapping values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_util::mem::SwapWithExt;
|
||||
/// let mut x = 10;
|
||||
/// let mut y = x.swap_with(20);
|
||||
/// assert_eq!(x, 20);
|
||||
/// assert_eq!(y, 10);
|
||||
/// y.swap_with_mut(&mut x);
|
||||
/// assert_eq!(x, 10);
|
||||
/// assert_eq!(y, 20);
|
||||
/// ```
|
||||
pub trait SwapWithExt {
|
||||
/// Takes ownership of `other` and swaps its value with `self`, returning the original value.
|
||||
/// Swap values and return the old value of `self`.
|
||||
fn swap_with(&mut self, other: Self) -> Self;
|
||||
/// Swaps the values between `self` and `other` in place.
|
||||
/// Swap values in place with another mutable reference.
|
||||
fn swap_with_mut(&mut self, other: &mut Self);
|
||||
}
|
||||
|
||||
@@ -138,8 +224,18 @@ impl<T> SwapWithExt for T {
|
||||
}
|
||||
|
||||
/// Extension trait that provides methods for swapping values with default values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rosenpass_util::mem::SwapWithDefaultExt;
|
||||
/// let mut s = String::from("abc");
|
||||
/// let old = s.swap_with_default();
|
||||
/// assert_eq!(old, "abc");
|
||||
/// assert_eq!(s, "");
|
||||
/// ```
|
||||
pub trait SwapWithDefaultExt {
|
||||
/// Takes the current value and replaces it with the default value, returning the original.
|
||||
/// Swap with `Self::default()`.
|
||||
fn swap_with_default(&mut self) -> Self;
|
||||
}
|
||||
|
||||
@@ -150,6 +246,26 @@ impl<T: Default> SwapWithDefaultExt for T {
|
||||
}
|
||||
|
||||
/// Extension trait that provides a method to explicitly move values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::rc::Rc;
|
||||
/// use rosenpass_util::mem::MoveExt;
|
||||
/// let val = 42;
|
||||
/// let another_val = val.move_here();
|
||||
/// assert_eq!(another_val, 42);
|
||||
/// // val is now inaccessible
|
||||
///
|
||||
/// let value = Rc::new(42);
|
||||
/// let clone = Rc::clone(&value);
|
||||
///
|
||||
/// assert_eq!(Rc::strong_count(&value), 2);
|
||||
///
|
||||
/// clone.move_here(); // this will drop the second reference
|
||||
///
|
||||
/// assert_eq!(Rc::strong_count(&value), 1);
|
||||
/// ```
|
||||
pub trait MoveExt {
|
||||
/// Deliberately move the value
|
||||
///
|
||||
@@ -163,3 +279,29 @@ impl<T: Sized> MoveExt for T {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_forgetting {
|
||||
use crate::mem::Forgetting;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn test_forgetting() {
|
||||
let drop_was_called = Arc::new(AtomicBool::new(false));
|
||||
struct SetFlagOnDrop(Arc<AtomicBool>);
|
||||
impl Drop for SetFlagOnDrop {
|
||||
fn drop(&mut self) {
|
||||
self.0.store(true, SeqCst);
|
||||
}
|
||||
}
|
||||
drop(SetFlagOnDrop(drop_was_called.clone()));
|
||||
assert!(drop_was_called.load(SeqCst));
|
||||
// reset flag and use Forgetting
|
||||
drop_was_called.store(false, SeqCst);
|
||||
let forgetting = Forgetting::new(SetFlagOnDrop(drop_was_called.clone()));
|
||||
drop(forgetting);
|
||||
assert_eq!(drop_was_called.load(SeqCst), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
use std::convert::Infallible;
|
||||
|
||||
/// Try block basically…returns a result and allows the use of the question mark operator inside
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use anyhow::Result;
|
||||
/// # use rosenpass_util::attempt;
|
||||
/// let result: Result<i32> = attempt!({
|
||||
/// let x = 42;
|
||||
/// Ok(x)
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(result.unwrap(), 42);
|
||||
///
|
||||
/// let error_result: Result<()> = attempt!({
|
||||
/// Err(anyhow::anyhow!("some error"))
|
||||
/// });
|
||||
///
|
||||
/// assert!(error_result.is_err());
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! attempt {
|
||||
($block:expr) => {
|
||||
@@ -9,6 +27,19 @@ macro_rules! attempt {
|
||||
}
|
||||
|
||||
/// Trait for the ok operation, which provides a way to convert a value into a Result
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// # use rosenpass_util::result::OkExt;
|
||||
/// let value: i32 = 42;
|
||||
/// let result: Result<i32, &str> = value.ok();
|
||||
///
|
||||
/// assert_eq!(result, Ok(42));
|
||||
///
|
||||
/// let value = "hello";
|
||||
/// let result: Result<&str, &str> = value.ok();
|
||||
///
|
||||
/// assert_eq!(result, Ok("hello"));
|
||||
/// ```
|
||||
pub trait OkExt<E>: Sized {
|
||||
/// Wraps a value in a Result::Ok variant
|
||||
fn ok(self) -> Result<Self, E>;
|
||||
@@ -26,6 +57,11 @@ impl<T, E> OkExt<E> for T {
|
||||
/// the function will not panic.
|
||||
///
|
||||
/// Implementations must not panic.
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use rosenpass_util::result::GuaranteedValue;
|
||||
/// let x:u32 = 10u8.try_into().guaranteed();
|
||||
/// ```
|
||||
pub trait GuaranteedValue {
|
||||
/// The value type that will be returned by guaranteed()
|
||||
type Value;
|
||||
|
||||
@@ -3,7 +3,23 @@ use typenum::int::{NInt, PInt, Z0};
|
||||
use typenum::marker_traits as markers;
|
||||
use typenum::uint::{UInt, UTerm};
|
||||
|
||||
/// Convenience macro to convert type numbers to constant integers
|
||||
/// Convenience macro to convert [`typenum`] type numbers to constant integers.
|
||||
///
|
||||
/// This macro takes a [`typenum`] type-level integer (like `U5`, `P3`, or `N7`)
|
||||
/// and converts it into its equivalent constant integer value at compile time.
|
||||
/// By default, it converts to a suitable unsigned integer type, but you can
|
||||
/// specify a target type explicitly using `typenum2const!(Type as i32)`,
|
||||
/// for example.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use typenum::consts::U10;
|
||||
/// # use rosenpass_util::typenum2const;
|
||||
///
|
||||
/// const TEN: u32 = typenum2const!(U10 as u32);
|
||||
/// assert_eq!(TEN, 10);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! typenum2const {
|
||||
($val:ty) => {
|
||||
@@ -14,35 +30,80 @@ macro_rules! typenum2const {
|
||||
};
|
||||
}
|
||||
|
||||
/// Trait implemented by constant integers to facilitate conversion to constant integers
|
||||
/// A trait implemented by type-level integers to facilitate their conversion
|
||||
/// into constant values.
|
||||
///
|
||||
/// Types from the [`typenum`] crate (like `U5`, `P3`, or `N7`) can implement
|
||||
/// `IntoConst` to produce a compile-time constant integer of the specified
|
||||
/// type. This trait is part of the underlying mechanism used by the
|
||||
/// [`crate::typenum2const`] macro.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use rosenpass_util::typenum2const;
|
||||
/// use typenum::consts::U42;
|
||||
/// use rosenpass_util::typenum::IntoConst;
|
||||
///
|
||||
/// // Directly using IntoConst:
|
||||
/// const VALUE: u64 = <U42 as IntoConst<u64>>::VALUE;
|
||||
/// assert_eq!(VALUE, 42);
|
||||
///
|
||||
/// // Or via the macro:
|
||||
/// const VALUE_MACRO: u64 = typenum2const!(U42 as u64);
|
||||
/// assert_eq!(VALUE_MACRO, 42);
|
||||
/// ```
|
||||
pub trait IntoConst<T> {
|
||||
/// The constant value after conversion
|
||||
/// The constant value after conversion.
|
||||
const VALUE: T;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Internal struct for applying a negative sign to an unsigned type-level integer during conversion.
|
||||
///
|
||||
/// This is part of the implementation detail for signed conversions. It uses
|
||||
/// [`AssociatedUnsigned`] to determine the underlying unsigned type and negates its value.
|
||||
struct ConstApplyNegSign<T: AssociatedUnsigned, Param: IntoConst<<T as AssociatedUnsigned>::Type>>(
|
||||
*const T,
|
||||
*const Param,
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Internal struct for applying a positive sign to an unsigned type-level integer during conversion.
|
||||
///
|
||||
/// This is used as part of converting a positive signed type-level integer to its runtime integer
|
||||
/// value, ensuring that the correct unsigned representation is known via [`AssociatedUnsigned`].
|
||||
struct ConstApplyPosSign<T: AssociatedUnsigned, Param: IntoConst<<T as AssociatedUnsigned>::Type>>(
|
||||
*const T,
|
||||
*const Param,
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct ConstLshift<T, Param: IntoConst<T>, const SHIFT: i32>(*const T, *const Param); // impl IntoConst<T>
|
||||
/// Internal struct representing a left-shift operation on a type-level integer.
|
||||
///
|
||||
/// Used as part of compile-time computations. `SHIFT` determines how many bits the value will be
|
||||
/// shifted to the left.
|
||||
struct ConstLshift<T, Param: IntoConst<T>, const SHIFT: i32>(*const T, *const Param);
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct ConstAdd<T, Lhs: IntoConst<T>, Rhs: IntoConst<T>>(*const T, *const Lhs, *const Rhs); // impl IntoConst<T>
|
||||
/// Internal struct representing an addition operation between two type-level integers.
|
||||
///
|
||||
/// `ConstAdd` is another building block for compile-time arithmetic on type-level integers before
|
||||
/// their conversion to runtime constants.
|
||||
struct ConstAdd<T, Lhs: IntoConst<T>, Rhs: IntoConst<T>>(*const T, *const Lhs, *const Rhs);
|
||||
|
||||
/// Assigns an unsigned type to a signed type
|
||||
/// Associates an unsigned type with a signed type, enabling conversions between signed and unsigned
|
||||
/// representations of compile-time integers.
|
||||
///
|
||||
/// This trait is used internally to facilitate the conversion of signed [`typenum`] integers by
|
||||
/// referencing their underlying unsigned representation.
|
||||
trait AssociatedUnsigned {
|
||||
/// The associated unsigned type.
|
||||
type Type;
|
||||
}
|
||||
|
||||
/// Internal macro implementing the [`IntoConst`] trait for a given mapping from a type-level integer
|
||||
/// to a concrete integer type.
|
||||
macro_rules! impl_into_const {
|
||||
( $from:ty as $to:ty := $impl:expr) => {
|
||||
impl IntoConst<$to> for $from {
|
||||
@@ -51,6 +112,10 @@ macro_rules! impl_into_const {
|
||||
};
|
||||
}
|
||||
|
||||
/// Internal macro implementing common `IntoConst` logic for various numeric types.
|
||||
///
|
||||
/// It sets up `Z0`, `B0`, `B1`, `UTerm`, and also provides default implementations for
|
||||
/// `ConstLshift` and `ConstAdd`.
|
||||
macro_rules! impl_numeric_into_const_common {
|
||||
($type:ty) => {
|
||||
impl_into_const! { Z0 as $type := 0 }
|
||||
@@ -73,6 +138,10 @@ macro_rules! impl_numeric_into_const_common {
|
||||
};
|
||||
}
|
||||
|
||||
/// Internal macro implementing `IntoConst` for unsigned integer types.
|
||||
///
|
||||
/// It sets up conversions for multiple unsigned integer target types and
|
||||
/// provides the positive sign application implementation.
|
||||
macro_rules! impl_numeric_into_const_unsigned {
|
||||
($($to_list:ty),*) => {
|
||||
$( impl_numeric_into_const_unsigned! { @impl $to_list } )*
|
||||
@@ -91,6 +160,9 @@ macro_rules! impl_numeric_into_const_unsigned {
|
||||
};
|
||||
}
|
||||
|
||||
/// Internal macro implementing `IntoConst` for signed integer types.
|
||||
///
|
||||
/// It uses their associated unsigned types to handle positive and negative conversions correctly.
|
||||
macro_rules! impl_numeric_into_const_signed {
|
||||
($($to_list:ty : $unsigned_list:ty),*) => {
|
||||
$( impl_numeric_into_const_signed! { @impl $to_list : $unsigned_list} )*
|
||||
@@ -110,9 +182,8 @@ macro_rules! impl_numeric_into_const_signed {
|
||||
impl<Param: IntoConst<$unsigned>> IntoConst<$type> for ConstApplyNegSign<$type, Param> {
|
||||
const VALUE : $type =
|
||||
if Param::VALUE == (1 as $unsigned).rotate_right(1) {
|
||||
// Handle the negative value without an associated positive value (e.g. -128
|
||||
// for i8)
|
||||
< $type >::MIN
|
||||
// Handling negative values at boundaries, such as i8::MIN
|
||||
<$type>::MIN
|
||||
} else {
|
||||
-(Param::VALUE as $type)
|
||||
};
|
||||
@@ -122,10 +193,10 @@ macro_rules! impl_numeric_into_const_signed {
|
||||
|
||||
impl_into_const! { B0 as bool := false }
|
||||
impl_into_const! { B1 as bool := true }
|
||||
|
||||
impl_numeric_into_const_unsigned! { usize, u8, u16, u32, u64, u128 }
|
||||
impl_numeric_into_const_signed! { isize : usize, i8 : u8, i16 : u16, i32 : u32, i64 : u64, i128 : u128 }
|
||||
|
||||
// Unsigned type numbers to const integers
|
||||
impl<Ret, Rest, Bit> IntoConst<Ret> for UInt<Rest, Bit>
|
||||
where
|
||||
Rest: IntoConst<Ret>,
|
||||
@@ -133,26 +204,28 @@ where
|
||||
ConstLshift<Ret, Rest, 1>: IntoConst<Ret>,
|
||||
ConstAdd<Ret, ConstLshift<Ret, Rest, 1>, Bit>: IntoConst<Ret>,
|
||||
{
|
||||
/// Converts an unsigned [`UInt`] typenum into its corresponding constant integer by
|
||||
/// decomposing it into shifts and additions on its subparts.
|
||||
const VALUE: Ret = <ConstAdd<Ret, ConstLshift<Ret, Rest, 1>, Bit> as IntoConst<Ret>>::VALUE;
|
||||
}
|
||||
|
||||
// Signed type numbers with positive sign to const integers
|
||||
impl<Ret, Unsigned> IntoConst<Ret> for PInt<Unsigned>
|
||||
where
|
||||
Ret: AssociatedUnsigned,
|
||||
Unsigned: markers::Unsigned + markers::NonZero + IntoConst<<Ret as AssociatedUnsigned>::Type>,
|
||||
ConstApplyPosSign<Ret, Unsigned>: IntoConst<Ret>,
|
||||
{
|
||||
/// Converts a positive signed [`PInt`] typenum into its corresponding constant integer.
|
||||
const VALUE: Ret = <ConstApplyPosSign<Ret, Unsigned> as IntoConst<Ret>>::VALUE;
|
||||
}
|
||||
|
||||
// Signed type numbers with negative sign to const integers
|
||||
impl<Ret, Unsigned> IntoConst<Ret> for NInt<Unsigned>
|
||||
where
|
||||
Ret: AssociatedUnsigned,
|
||||
Unsigned: markers::Unsigned + markers::NonZero + IntoConst<<Ret as AssociatedUnsigned>::Type>,
|
||||
ConstApplyNegSign<Ret, Unsigned>: IntoConst<Ret>,
|
||||
{
|
||||
/// Converts a negative signed [`NInt`] typenum into its corresponding constant integer.
|
||||
const VALUE: Ret = <ConstApplyNegSign<Ret, Unsigned> as IntoConst<Ret>>::VALUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,23 @@
|
||||
//!
|
||||
//! This module provides an extension trait,
|
||||
//! [`ZeroizedExt`](crate::zeroize::ZeroizedExt), for all types implementing the
|
||||
//! `zeroize::Zeroize` trait.
|
||||
//! It introduces the [`zeroized`](crate::zeroize::ZeroizedExt::zeroized)
|
||||
//! method, which zeroizes a value in place and returns it, making it convenient
|
||||
//! for chaining operations and ensuring sensitive data is securely erased.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! use zeroize::Zeroize;
|
||||
//! use rosenpass_util::zeroize::ZeroizedExt;
|
||||
//!
|
||||
//! let mut value = String::from("hello");
|
||||
//! value.zeroize(); // Zeroizes in place
|
||||
//! assert_eq!(value, "");
|
||||
//!
|
||||
//! assert_eq!(String::from("hello").zeroized(), "");
|
||||
//! ```
|
||||
|
||||
mod zeroized_ext;
|
||||
pub use zeroized_ext::*;
|
||||
|
||||
Reference in New Issue
Block a user