docs(util): add docs and examples for the remaining util crate

This commit is contained in:
Amin Faez
2024-12-16 12:09:29 +01:00
parent 9cc860fdeb
commit 2e0e2cfa0c
8 changed files with 444 additions and 41 deletions

View File

@@ -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
///

View File

@@ -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);

View File

@@ -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(()),

View File

@@ -244,7 +244,6 @@
use std::{borrow::Borrow, io};
use anyhow::ensure;
use zerocopy::AsBytes;
/// Generic trait for accessing [std::io::Error::kind]
///

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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::*;