diff --git a/util/src/build.rs b/util/src/build.rs index e5bf5be..de74d5d 100644 --- a/util/src/build.rs +++ b/util/src/build.rs @@ -78,8 +78,8 @@ pub trait Build: 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 /// diff --git a/util/src/controlflow.rs b/util/src/controlflow.rs index e00aa8e..3d0fd37 100644 --- a/util/src/controlflow.rs +++ b/util/src/controlflow.rs @@ -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); diff --git a/util/src/fd.rs b/util/src/fd.rs index 05b1abb..f6966e9 100644 --- a/util/src/fd.rs +++ b/util/src/fd.rs @@ -89,6 +89,30 @@ pub fn claim_fd_inplace(fd: RawFd) -> rustix::io::Result { /// /// 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; - /// Alias for [socket_domain] + /// Alias for [Self::socket_domain] fn socket_address_family(&self) -> Result { 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; /// 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, 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 { 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(()), diff --git a/util/src/io.rs b/util/src/io.rs index 60747ea..1208dd9 100644 --- a/util/src/io.rs +++ b/util/src/io.rs @@ -244,7 +244,6 @@ use std::{borrow::Borrow, io}; use anyhow::ensure; -use zerocopy::AsBytes; /// Generic trait for accessing [std::io::Error::kind] /// diff --git a/util/src/mem.rs b/util/src/mem.rs index 16f290f..f3f45f5 100644 --- a/util/src/mem.rs +++ b/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 + ?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 + ?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 + ?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 { value: Option, } impl Forgetting { - /// Creates a new `Forgetting` 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 Drop for Forgetting { } /// 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 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 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 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 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 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); + 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); + } +} diff --git a/util/src/result.rs b/util/src/result.rs index 3c72ed9..5cc6282 100644 --- a/util/src/result.rs +++ b/util/src/result.rs @@ -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 = 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 = 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: Sized { /// Wraps a value in a Result::Ok variant fn ok(self) -> Result; @@ -26,6 +57,11 @@ impl OkExt 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; diff --git a/util/src/typenum.rs b/util/src/typenum.rs index 73ccfff..5558f70 100644 --- a/util/src/typenum.rs +++ b/util/src/typenum.rs @@ -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 = >::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 { - /// 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::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::Type>>( *const T, *const Param, ); #[allow(dead_code)] -struct ConstLshift, const SHIFT: i32>(*const T, *const Param); // impl IntoConst +/// 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, const SHIFT: i32>(*const T, *const Param); #[allow(dead_code)] -struct ConstAdd, Rhs: IntoConst>(*const T, *const Lhs, *const Rhs); // impl IntoConst +/// 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, Rhs: IntoConst>(*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> 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 IntoConst for UInt where Rest: IntoConst, @@ -133,26 +204,28 @@ where ConstLshift: IntoConst, ConstAdd, Bit>: IntoConst, { + /// Converts an unsigned [`UInt`] typenum into its corresponding constant integer by + /// decomposing it into shifts and additions on its subparts. const VALUE: Ret = , Bit> as IntoConst>::VALUE; } -// Signed type numbers with positive sign to const integers impl IntoConst for PInt where Ret: AssociatedUnsigned, Unsigned: markers::Unsigned + markers::NonZero + IntoConst<::Type>, ConstApplyPosSign: IntoConst, { + /// Converts a positive signed [`PInt`] typenum into its corresponding constant integer. const VALUE: Ret = as IntoConst>::VALUE; } -// Signed type numbers with negative sign to const integers impl IntoConst for NInt where Ret: AssociatedUnsigned, Unsigned: markers::Unsigned + markers::NonZero + IntoConst<::Type>, ConstApplyNegSign: IntoConst, { + /// Converts a negative signed [`NInt`] typenum into its corresponding constant integer. const VALUE: Ret = as IntoConst>::VALUE; } diff --git a/util/src/zeroize/mod.rs b/util/src/zeroize/mod.rs index f1d37be..e3e9f09 100644 --- a/util/src/zeroize/mod.rs +++ b/util/src/zeroize/mod.rs @@ -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::*;