Compare commits

...

4 Commits

Author SHA1 Message Date
Karolin Varner
caf91c84f0 chore: Remove unused warning in api integration test 2024-12-18 13:45:38 +01:00
Karolin Varner
f5b4c17011 fix: Disable asserts that rely on timing characteristics during coverage testing 2024-12-18 13:45:38 +01:00
Karolin Varner
12506e5f95 chore: Final improvements on the to crate API doc 2024-12-18 13:45:38 +01:00
David Niehues
965600212d docs+doctest(to): Add tests, examples and documentation to the to-crate 2024-12-18 13:45:38 +01:00
14 changed files with 411 additions and 51 deletions

View File

@@ -20,3 +20,6 @@ memsec = { workspace = true }
[dev-dependencies] [dev-dependencies]
rand = "0.8.5" rand = "0.8.5"
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }

View File

@@ -113,9 +113,10 @@ mod tests {
// Pearson correlation // Pearson correlation
let correlation = cv / (sd_x * sd_y); let correlation = cv / (sd_x * sd_y);
println!("correlation: {:.6?}", correlation); println!("correlation: {:.6?}", correlation);
#[cfg(not(coverage))]
assert!( assert!(
correlation.abs() < 0.01, correlation.abs() < 0.01,
"execution time correlates with result" "execution time correlates with result"
) );
} }
} }

View File

@@ -91,3 +91,6 @@ experiment_api = [
internal_signal_handling_for_coverage_reports = ["signal-hook"] internal_signal_handling_for_coverage_reports = ["signal-hook"]
internal_testing = [] internal_testing = []
internal_bin_gen_ipc_msg_types = ["hex", "heck"] internal_bin_gen_ipc_msg_types = ["hex", "heck"]
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }

View File

@@ -39,7 +39,7 @@ impl Drop for KillChild {
// system is a bit broken; there is probably a few functions that just restart on EINTR // system is a bit broken; there is probably a few functions that just restart on EINTR
// so the signal is absorbed // so the signal is absorbed
loop { loop {
rustix::process::kill_process(pid, Term).discard_result(); kill_process(pid, Term).discard_result();
if self.0.try_wait().unwrap().is_some() { if self.0.try_wait().unwrap().is_some() {
break; break;
} }

View File

@@ -2,11 +2,9 @@
use std::{ use std::{
borrow::{Borrow, BorrowMut}, borrow::{Borrow, BorrowMut},
collections::VecDeque, collections::VecDeque,
fmt::{Debug, Write}, ops::DerefMut,
ops::{DerefMut, RangeBounds},
}; };
use rand::distributions::uniform::SampleBorrow;
use rosenpass_cipher_traits::Kem; use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem; use rosenpass_ciphers::kem::StaticKem;
use rosenpass_util::result::OkExt; use rosenpass_util::result::OkExt;
@@ -28,48 +26,53 @@ fn test_successful_exchange_with_poll() -> anyhow::Result<()> {
sim.poll_loop(150)?; // Poll 75 times sim.poll_loop(150)?; // Poll 75 times
let transcript = sim.transcript; let transcript = sim.transcript;
let completions: Vec<_> = transcript let _completions: Vec<_> = transcript
.iter() .iter()
.filter(|elm| matches!(elm, (_, TranscriptEvent::CompletedExchange(_)))) .filter(|elm| matches!(elm, (_, TranscriptEvent::CompletedExchange(_))))
.collect(); .collect();
#[cfg(not(coverage))]
assert!( assert!(
!completions.is_empty(), !_completions.is_empty(),
"\ "\
Should have performed a successful key exchanged!\n\ Should have performed a successful key exchanged!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
#[cfg(not(coverage))]
assert!( assert!(
completions[0].0 < 20.0, _completions[0].0 < 20.0,
"\ "\
First key exchange should happen in under twenty seconds!\n\ First key exchange should happen in under twenty seconds!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
#[cfg(not(coverage))]
assert!( assert!(
completions.len() >= 3, _completions.len() >= 3,
"\ "\
Should have at least two renegotiations!\n\ Should have at least two renegotiations!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
#[cfg(not(coverage))]
assert!( assert!(
(110.0..175.0).contains(&completions[1].0), (110.0..175.0).contains(&_completions[1].0),
"\ "\
First renegotiation should happen in between two and three minutes!\n\ First renegotiation should happen in between two and three minutes!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
assert!((110.0..175.0).contains(&(completions[2].0 - completions[1].0)), "\ #[cfg(not(coverage))]
assert!((110.0..175.0).contains(&(_completions[2].0 - _completions[1].0)), "\
First renegotiation should happen in between two and three minutes after the first renegotiation!\n\ First renegotiation should happen in between two and three minutes after the first renegotiation!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
"); ");
Ok(()) Ok(())
@@ -106,48 +109,53 @@ fn test_successful_exchange_under_packet_loss() -> anyhow::Result<()> {
} }
let transcript = sim.transcript; let transcript = sim.transcript;
let completions: Vec<_> = transcript let _completions: Vec<_> = transcript
.iter() .iter()
.filter(|elm| matches!(elm, (_, TranscriptEvent::CompletedExchange(_)))) .filter(|elm| matches!(elm, (_, TranscriptEvent::CompletedExchange(_))))
.collect(); .collect();
#[cfg(not(coverage))]
assert!( assert!(
!completions.is_empty(), !_completions.is_empty(),
"\ "\
Should have performed a successful key exchanged!\n\ Should have performed a successful key exchanged!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
#[cfg(not(coverage))]
assert!( assert!(
completions[0].0 < 10.0, _completions[0].0 < 10.0,
"\ "\
First key exchange should happen in under twenty seconds!\n\ First key exchange should happen in under twenty seconds!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
#[cfg(not(coverage))]
assert!( assert!(
completions.len() >= 3, _completions.len() >= 3,
"\ "\
Should have at least two renegotiations!\n\ Should have at least two renegotiations!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
#[cfg(not(coverage))]
assert!( assert!(
(110.0..175.0).contains(&completions[1].0), (110.0..175.0).contains(&_completions[1].0),
"\ "\
First renegotiation should happen in between two and three minutes!\n\ First renegotiation should happen in between two and three minutes!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
" "
); );
assert!((110.0..175.0).contains(&(completions[2].0 - completions[1].0)), "\ #[cfg(not(coverage))]
assert!((110.0..175.0).contains(&(_completions[2].0 - _completions[1].0)), "\
First renegotiation should happen in between two and three minutes after the first renegotiation!\n\ First renegotiation should happen in between two and three minutes after the first renegotiation!\n\
Transcript: {transcript:?}\n\ Transcript: {transcript:?}\n\
Completions: {completions:?}\ Completions: {_completions:?}\
"); ");
Ok(()) Ok(())

View File

@@ -17,13 +17,14 @@ use rosenpass_to::{to, with_destination, To};
use std::ops::BitXorAssign; use std::ops::BitXorAssign;
// Destination functions return some value that implements the To trait. // Destination functions return some value that implements the To trait.
// Unfortunately dealing with lifetimes is a bit more finicky than it would# // Unfortunately dealing with lifetimes is a bit more finicky than it would
// be without destination parameters // be without destination parameters
fn xor_slice<'a, T>(src: &'a [T]) -> impl To<[T], ()> + 'a fn xor_slice<'a, T>(src: &'a [T]) -> impl To<[T], ()> + 'a
where where
T: BitXorAssign + Clone, T: BitXorAssign + Clone,
{ {
// Custom implementations of the to trait can be created, but the easiest // Custom implementations of the to trait can be created, but the easiest
// way to create them is to use the provided helper functions like with_destination.
with_destination(move |dst: &mut [T]| { with_destination(move |dst: &mut [T]| {
assert!(src.len() == dst.len()); assert!(src.len() == dst.len());
for (d, s) in dst.iter_mut().zip(src.iter()) { for (d, s) in dst.iter_mut().zip(src.iter()) {

View File

@@ -1,4 +1,5 @@
//! Functions with destination copying data between slices and arrays. //! Functions that make it easy to copy data between arrays and slices using functions with
//! destinations. See the specific functions for examples and more explanations.
use crate::{with_destination, To}; use crate::{with_destination, To};
@@ -8,6 +9,17 @@ use crate::{with_destination, To};
/// # Panics /// # Panics
/// ///
/// This function will panic if the two slices have different lengths. /// This function will panic if the two slices have different lengths.
///
/// # Example
/// ```
/// use rosenpass_to::To;
/// # use crate::rosenpass_to::ops::copy_slice;
/// let to_function = copy_slice(&[0; 16]);
/// let mut dst = [255; 16];
/// to_function.to(&mut dst);
/// // After the operation `dst` will hold the same data as the original slice.
/// assert!(dst.iter().all(|b| *b == 0));
/// ```
pub fn copy_slice<T>(origin: &[T]) -> impl To<[T], ()> + '_ pub fn copy_slice<T>(origin: &[T]) -> impl To<[T], ()> + '_
where where
T: Copy, T: Copy,
@@ -23,6 +35,19 @@ where
/// # Panics /// # Panics
/// ///
/// This function will panic if destination is shorter than origin. /// This function will panic if destination is shorter than origin.
///
/// # Example
/// ```
/// use rosenpass_to::To;
/// # use crate::rosenpass_to::ops::copy_slice_least_src;
/// let to_function = copy_slice_least_src(&[0; 16]);
/// let mut dst = [255; 32];
/// to_function.to(&mut dst);
/// // After the operation the first half of `dst` will hold the same data as the original slice.
/// assert!(dst[0..16].iter().all(|b| *b == 0));
/// // The second half will have remained the same
/// assert!(dst[16..32].iter().all(|b| *b == 255));
/// ```
pub fn copy_slice_least_src<T>(origin: &[T]) -> impl To<[T], ()> + '_ pub fn copy_slice_least_src<T>(origin: &[T]) -> impl To<[T], ()> + '_
where where
T: Copy, T: Copy,
@@ -34,6 +59,18 @@ where
/// destination. /// destination.
/// ///
/// Copies as much data as is present in the shorter slice. /// Copies as much data as is present in the shorter slice.
/// # Example
/// ```
/// use rosenpass_to::To;
/// # use crate::rosenpass_to::ops::copy_slice_least;
/// let to_function = copy_slice_least(&[0; 16]);
/// let mut dst = [255; 32];
/// to_function.to(&mut dst);
/// // After the operation the first half of `dst` will hold the same data as the original slice.
/// assert!(dst[0..16].iter().all(|b| *b == 0));
/// // The second half will have remained the same.
/// assert!(dst[16..32].iter().all(|b| *b == 255));
/// ```
pub fn copy_slice_least<T>(origin: &[T]) -> impl To<[T], ()> + '_ pub fn copy_slice_least<T>(origin: &[T]) -> impl To<[T], ()> + '_
where where
T: Copy, T: Copy,
@@ -47,6 +84,24 @@ where
/// Function with destination that attempts to copy data from origin into the destination. /// Function with destination that attempts to copy data from origin into the destination.
/// ///
/// Will return None if the slices are of different lengths. /// Will return None if the slices are of different lengths.
/// # Example
/// ```
/// use rosenpass_to::To;
/// # use crate::rosenpass_to::ops::try_copy_slice;
/// let to_function = try_copy_slice(&[0; 16]);
/// let mut dst = [255; 32];
/// let result = to_function.to(&mut dst);
/// // This will return None because the slices do not have the same length.
/// assert!(result.is_none());
///
/// let to_function = try_copy_slice(&[0; 16]);
/// let mut dst = [255; 16];
/// let result = to_function.to(&mut dst);
/// // This time it works:
/// assert!(result.is_some());
/// // After the operation `dst` will hold the same data as the original slice.
/// assert!(dst.iter().all(|b| *b == 0));
/// ```
pub fn try_copy_slice<T>(origin: &[T]) -> impl To<[T], Option<()>> + '_ pub fn try_copy_slice<T>(origin: &[T]) -> impl To<[T], Option<()>> + '_
where where
T: Copy, T: Copy,
@@ -62,6 +117,26 @@ where
/// Destination may be longer than origin. /// Destination may be longer than origin.
/// ///
/// Will return None if the destination is shorter than origin. /// Will return None if the destination is shorter than origin.
/// # Example
/// ```rust
/// use rosenpass_to::To;
/// # use crate::rosenpass_to::ops::try_copy_slice_least_src;
/// let to_function = try_copy_slice_least_src(&[0; 16]);
/// let mut dst = [255; 15];
/// let result = to_function.to(&mut dst);
/// // This will return None because the destination is to short.
/// assert!(result.is_none());
///
/// let to_function = try_copy_slice_least_src(&[0; 16]);
/// let mut dst = [255; 32];
/// let result = to_function.to(&mut dst);
/// // This time it works:
/// assert!(result.is_some());
/// // After the operation, the first half of `dst` will hold the same data as the original slice.
/// assert!(dst[0..16].iter().all(|b| *b == 0));
/// // The second half will have remained the same.
/// assert!(dst[16..32].iter().all(|b| *b == 255));
/// ```
pub fn try_copy_slice_least_src<T>(origin: &[T]) -> impl To<[T], Option<()>> + '_ pub fn try_copy_slice_least_src<T>(origin: &[T]) -> impl To<[T], Option<()>> + '_
where where
T: Copy, T: Copy,
@@ -72,6 +147,18 @@ where
} }
/// Function with destination that copies all data between two array references. /// Function with destination that copies all data between two array references.
///
/// # Example
/// ```rust
/// use rosenpass_to::ops::copy_array;
/// use rosenpass_to::To;
/// let my_arr: [u8; 32] = [0; 32];
/// let to_function = copy_array(&my_arr);
/// let mut dst = [255; 32];
/// to_function.to(&mut dst);
/// // After the operation `dst` will hold the same data as the original slice.
/// assert!(dst.iter().all(|b| *b == 0));
/// ```
pub fn copy_array<T, const N: usize>(origin: &[T; N]) -> impl To<[T; N], ()> + '_ pub fn copy_array<T, const N: usize>(origin: &[T; N]) -> impl To<[T; N], ()> + '_
where where
T: Copy, T: Copy,

View File

@@ -1,6 +1,11 @@
//! This module provides the [Beside] struct. In the context of functions with targets,
//! [Beside] structures the destination value and the return value unmistakably and offers useful
//! helper functions to work with them.
use crate::CondenseBeside; use crate::CondenseBeside;
/// Named tuple holding the return value and the output from a function with destinations. /// Named tuple holding the return value and the destination from a function with destinations.
/// See the respective functions for usage examples.
#[derive(Debug, PartialEq, Eq, Default, PartialOrd, Ord, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Default, PartialOrd, Ord, Copy, Clone)]
pub struct Beside<Val, Ret>(pub Val, pub Ret); pub struct Beside<Val, Ret>(pub Val, pub Ret);
@@ -59,7 +64,7 @@ impl<Val, Ret> Beside<Val, Ret> {
&mut self.1 &mut self.1
} }
/// Perform beside condensation. See [CondenseBeside] /// Perform beside condensation. See [CondenseBeside] for more details.
/// ///
/// # Example /// # Example
/// ``` /// ```
@@ -90,3 +95,25 @@ impl<Val, Ret> From<Beside<Val, Ret>> for (Val, Ret) {
(val, ret) (val, ret)
} }
} }
#[cfg(test)]
mod tests {
use crate::Beside;
#[test]
fn from_tuple() {
let tuple = (21u8, 42u16);
let beside: Beside<u8, u16> = Beside::from(tuple);
assert_eq!(beside.dest(), &21u8);
assert_eq!(beside.ret(), &42u16);
}
#[test]
fn from_beside() {
let beside: Beside<u8, u16> = Beside(21u8, 42u16);
type U8u16 = (u8, u16);
let tuple = U8u16::from(beside);
assert_eq!(tuple.0, 21u8);
assert_eq!(tuple.1, 42u16);
}
}

View File

@@ -1,4 +1,11 @@
/// Beside condensation. //! This module provides condensation for values that stand side by side,
//! which is often useful when working with destination parameters. See [CondenseBeside]
//! for more details.
/// Condenses two values that stand beside each other into one value.
/// For example, a blanked implementation for [Result<(), Error>](Result) is provided. If
/// `condense(val)` is called on such an object, a [Result<Val, Error>](Result) will
/// be returned, if `val` is of type `Val`.
/// ///
/// This trait can be used to enable the use of [to_this(|| ...)](crate::To::to_this), /// This trait can be used to enable the use of [to_this(|| ...)](crate::To::to_this),
/// [to_value()](crate::To::to_value), and [collect::<...>()](crate::To::collect) with custom /// [to_value()](crate::To::to_value), and [collect::<...>()](crate::To::collect) with custom
@@ -6,6 +13,19 @@
/// ///
/// The function [Beside::condense()](crate::Beside::condense) is a shorthand for using the /// The function [Beside::condense()](crate::Beside::condense) is a shorthand for using the
/// condense trait. /// condense trait.
///
/// # Example
/// As an example implementation, we take a look at the blanket implementation for [Option]
/// ```ignore
/// impl<Val> CondenseBeside<Val> for Option<()> {
/// type Condensed = Option<Val>;
///
/// /// Replaces the empty tuple inside this [Option] with `ret`.
/// fn condense(self, ret: Val) -> Option<Val> {
/// self.map(|()| ret)
/// }
/// }
/// ```
pub trait CondenseBeside<Val> { pub trait CondenseBeside<Val> {
/// The type that results from condensation. /// The type that results from condensation.
type Condensed; type Condensed;
@@ -17,6 +37,7 @@ pub trait CondenseBeside<Val> {
impl<Val> CondenseBeside<Val> for () { impl<Val> CondenseBeside<Val> for () {
type Condensed = Val; type Condensed = Val;
/// Replaces this empty tuple with `ret`.
fn condense(self, ret: Val) -> Val { fn condense(self, ret: Val) -> Val {
ret ret
} }
@@ -25,6 +46,7 @@ impl<Val> CondenseBeside<Val> for () {
impl<Val, Error> CondenseBeside<Val> for Result<(), Error> { impl<Val, Error> CondenseBeside<Val> for Result<(), Error> {
type Condensed = Result<Val, Error>; type Condensed = Result<Val, Error>;
/// Replaces the empty tuple inside this [Result] with `ret`.
fn condense(self, ret: Val) -> Result<Val, Error> { fn condense(self, ret: Val) -> Result<Val, Error> {
self.map(|()| ret) self.map(|()| ret)
} }
@@ -33,6 +55,7 @@ impl<Val, Error> CondenseBeside<Val> for Result<(), Error> {
impl<Val> CondenseBeside<Val> for Option<()> { impl<Val> CondenseBeside<Val> for Option<()> {
type Condensed = Option<Val>; type Condensed = Option<Val>;
/// Replaces the empty tuple inside this [Option] with `ret`.
fn condense(self, ret: Val) -> Option<Val> { fn condense(self, ret: Val) -> Option<Val> {
self.map(|()| ret) self.map(|()| ret)
} }

View File

@@ -1,5 +1,28 @@
/// Helper performing explicit unsized coercion. //! This module provides explicit type coercion from [Sized] types to [?Sized][core::marker::Sized]
/// Used by the [to](crate::to()) function. //! types. See [DstCoercion] for more details.
/// Helper Trait for performing explicit coercion from [Sized] types to
/// [?Sized][core::marker::Sized] types. It's used by the [to](crate::to()) function.
///
/// We provide blanket implementations for any [Sized] type and for any array of [Sized] types.
///
/// # Example
/// It can be used as follows:
/// ```
/// # use rosenpass_to::DstCoercion;
/// // Consider a sized type like this example:
/// struct SizedStruct {
/// x: u32
/// }
/// // Then we can coerce it to be unsized:
/// let mut sized = SizedStruct { x: 42 };
/// assert_eq!(42, sized.coerce_dest().x);
///
/// // Analogously, we can coerce arrays to slices:
/// let mut sized_array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
/// let un_sized: &[i32] = sized_array.coerce_dest();
/// assert_eq!(un_sized, un_sized);
/// ```
pub trait DstCoercion<Dst: ?Sized> { pub trait DstCoercion<Dst: ?Sized> {
/// Performs an explicit coercion to the destination type. /// Performs an explicit coercion to the destination type.
fn coerce_dest(&mut self) -> &mut Dst; fn coerce_dest(&mut self) -> &mut Dst;

View File

@@ -6,11 +6,16 @@
//! - `Dst: ?Sized`; (e.g. [u8]) The target to write to //! - `Dst: ?Sized`; (e.g. [u8]) The target to write to
//! - `Out: Sized = &mut Dst`; (e.g. &mut [u8]) A reference to the target to write to //! - `Out: Sized = &mut Dst`; (e.g. &mut [u8]) A reference to the target to write to
//! - `Coercable: ?Sized + DstCoercion<Dst>`; (e.g. `[u8]`, `[u8; 16]`) Some value that //! - `Coercable: ?Sized + DstCoercion<Dst>`; (e.g. `[u8]`, `[u8; 16]`) Some value that
//! destination coercion can be applied to. Usually either `Dst` itself (e.g. `[u8]` or some sized variant of //! destination coercion can be applied to. Usually either `Dst` itself (e.g. `[u8]` or
//! some sized variant of
//! `Dst` (e.g. `[u8; 64]`). //! `Dst` (e.g. `[u8; 64]`).
//! - `Ret: Sized`; (anything) must be `CondenseBeside<_>` if condensing is to be applied. The ordinary return value of a function with an output //! - `Ret: Sized`; (anything) must be `CondenseBeside<_>` if condensing is to be applied. The
//! - `Val: Sized + BorrowMut<Dst>`; (e.g. [u8; 16]) Some owned storage that can be borrowed as `Dst` //! ordinary return value of a function with an output
//! - `Condensed: Sized = CondenseBeside<Val>::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>) The combiation of Val and Ret after condensing was applied (`Beside<Val, Ret>::condense()`/`Ret::condense(v)` for all `v : Val`). //! - `Val: Sized + BorrowMut<Dst>`; (e.g. [u8; 16]) Some owned storage that can be borrowed as
//! `Dst`
//! - `Condensed: Sized = CondenseBeside<Val>::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>)
//! The combiation of Val and Ret after condensing was applied
//! (`Beside<Val, Ret>::condense()`/`Ret::condense(v)` for all `v : Val`).
pub mod beside; pub mod beside;
pub mod condense; pub mod condense;

View File

@@ -1,9 +1,23 @@
//! This module provides the [To::to] function which allows to use functions with destination in
//! a manner akin to that of a variable assignment. See [To::to] for more details.
use crate::{DstCoercion, To}; use crate::{DstCoercion, To};
/// Alias for [To::to] moving the destination to the left. /// Alias for [To::to] moving the destination to the left.
/// ///
/// This provides similar haptics to the let assignment syntax is rust, which also keeps /// This provides similar haptics to the let assignment syntax is rust, which also keeps
/// the variable to assign to on the left and the generating function on the right. /// the variable to assign to on the left and the generating function on the right.
///
/// # Example
/// ```rust
/// // Using the to function to have data flowing from the right to the left,
/// // performing something akin to a variable assignment.
/// use rosenpass_to::ops::copy_slice_least;
/// # use rosenpass_to::to;
/// let mut dst = b" ".to_vec();
/// to(&mut dst[..], copy_slice_least(b"Hello World"));
/// assert_eq!(&dst[..], b"Hello World");
/// ```
pub fn to<Coercable, Src, Dst, Ret>(dst: &mut Coercable, src: Src) -> Ret pub fn to<Coercable, Src, Dst, Ret>(dst: &mut Coercable, src: Src) -> Ret
where where
Coercable: ?Sized + DstCoercion<Dst>, Coercable: ?Sized + DstCoercion<Dst>,

View File

@@ -1,12 +1,46 @@
//! Module that contains the [To] crate which is the container used to
//! implement the core functionality of this crate.
use crate::{Beside, CondenseBeside}; use crate::{Beside, CondenseBeside};
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
/// The To trait is the core of the to crate; most functions with destinations will either return /// 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, /// an object that is an instance of this trait, or they will return `-> impl To<Destination,
/// Return_value`. /// Return_value>`.
/// ///
/// A quick way to implement a function with destination is to use the /// A quick way to implement a function with destination is to use the
/// [with_destination(|param: &mut Type| ...)] higher order function. /// [with_destination(|param: &mut Type| ...)](crate::with_destination) higher order function.
///
/// # Example
/// Below, we provide a very simple example for how the Trait can be implemented. More examples for
/// how this Trait is best implemented can be found in the overall [crate documentation](crate).
/// ```
/// use rosenpass_to::To;
///
/// // This is a simple wrapper around a String that can be written into a byte array using to.
/// struct StringToBytes {
/// inner: String
/// }
///
/// impl To<[u8], Result<(), String>> for StringToBytes {
/// fn to(self, out: &mut [u8]) -> Result<(), String> {
/// let bytes = self.inner.as_bytes();
/// if bytes.len() > out.len() {
/// return Err("out is to short".to_string());
/// }
/// for i in 0..bytes.len() {
/// (*out)[i] = bytes[i];
/// }
/// Ok(())
/// }
/// }
///
/// let string_to_bytes = StringToBytes { inner: "my message".to_string() };
/// let mut buffer: [u8; 10] = [0; 10];
/// let result = string_to_bytes.to(&mut buffer);
/// assert_eq!(buffer, [109, 121, 32, 109, 101, 115, 115, 97, 103, 101]);
/// assert!(result.is_ok());
/// ```
pub trait To<Dst: ?Sized, Ret>: Sized { pub trait To<Dst: ?Sized, Ret>: Sized {
/// Writes self to the destination `out` and returns a value of type `Ret`. /// Writes self to the destination `out` and returns a value of type `Ret`.
/// ///
@@ -19,6 +53,36 @@ pub trait To<Dst: ?Sized, Ret>: Sized {
/// calls [crate::to()] to evaluate the function and finally /// calls [crate::to()] to evaluate the function and finally
/// returns a [Beside] instance containing the generated destination value and the return /// returns a [Beside] instance containing the generated destination value and the return
/// value. /// value.
///
/// # Example
/// Below, we rewrite the example for the overall [To]-Trait and simplify it by using
/// [self.to_this_beside]. We refer to the overall [crate documentation](crate)
/// for more examples and general explanations.
/// ```
/// # use rosenpass_to::To;
/// use rosenpass_to::Beside;
/// # struct StringToBytes {
/// # inner: String
/// # }
///
/// # impl To<[u8], Result<(), String>> for StringToBytes {
/// # fn to(self, out: &mut [u8]) -> Result<(), String> {
/// # let bytes = self.inner.as_bytes();
/// # if bytes.len() > out.len() {
/// # return Err("out is to short".to_string());
/// # }
/// # for i in 0..bytes.len() {
/// # (*out)[i] = bytes[i];
/// # }
/// # Ok(())
/// # }
/// # }
/// // StringToBytes is taken from the overall Trait example.
/// let string_to_bytes = StringToBytes { inner: "my message".to_string() };
/// let Beside(dst, result) = string_to_bytes.to_this_beside(|| [0; 10]);
/// assert_eq!(dst, [109, 121, 32, 109, 101, 115, 115, 97, 103, 101]);
/// assert!(result.is_ok());
/// ```
fn to_this_beside<Val, Fun>(self, fun: Fun) -> Beside<Val, Ret> fn to_this_beside<Val, Fun>(self, fun: Fun) -> Beside<Val, Ret>
where where
Val: BorrowMut<Dst>, Val: BorrowMut<Dst>,
@@ -31,10 +95,21 @@ pub trait To<Dst: ?Sized, Ret>: Sized {
/// Generate a destination on the fly using default. /// Generate a destination on the fly using default.
/// ///
/// Uses [Default] to create a value, /// Uses [Default] to create a value, calls [crate::to()] to evaluate the function and finally
/// calls [crate::to()] to evaluate the function and finally
/// returns a [Beside] instance containing the generated destination value and the return /// returns a [Beside] instance containing the generated destination value and the return
/// value. /// value.
///
/// # Example
/// Below, we provide a simple example for the usage of [to_value_beside](To::to_value_beside).
/// We refer to the overall [crate documentation](crate) for more examples and general
/// explanations.
/// ```
/// use rosenpass_to::Beside;
/// use rosenpass_to::To;
/// use rosenpass_to::ops::*;
/// let Beside(dst, ret) = copy_array(&[42u8; 16]).to_value_beside();
/// assert_eq!(dst, [42u8; 16]);
/// ```
fn to_value_beside(self) -> Beside<Dst, Ret> fn to_value_beside(self) -> Beside<Dst, Ret>
where where
Dst: Sized + Default, Dst: Sized + Default,
@@ -53,6 +128,19 @@ pub trait To<Dst: ?Sized, Ret>: Sized {
/// when the Destination is unsized. /// when the Destination is unsized.
/// ///
/// This could be the case when the destination is an `[u8]` for instance. /// This could be the case when the destination is an `[u8]` for instance.
///
/// # Example
/// Below, we provide a simple example for the usage of [collect_beside](To::collect_beside).
/// We refer to the overall [crate documentation](crate) for more examples and general
/// explanations.
/// ```
/// use rosenpass_to::Beside;
/// use rosenpass_to::To;
/// use rosenpass_to::ops::*;
///
/// let Beside(dst, ret) = copy_slice(&[42u8; 16]).collect_beside::<[u8; 16]>();
/// assert_eq!(dst, [42u8; 16]);
/// ```
fn collect_beside<Val>(self) -> Beside<Val, Ret> fn collect_beside<Val>(self) -> Beside<Val, Ret>
where where
Val: Default + BorrowMut<Dst>, Val: Default + BorrowMut<Dst>,
@@ -64,6 +152,36 @@ pub trait To<Dst: ?Sized, Ret>: Sized {
/// return value into one. /// return value into one.
/// ///
/// This is like using [Self::to_this_beside] followed by calling [Beside::condense]. /// This is like using [Self::to_this_beside] followed by calling [Beside::condense].
/// # Example
/// Below, we rewrite the example for the overall [To]-Trait and simplify it by using
/// [Self::to_this]. We refer to the overall [crate documentation](crate)
/// for more examples and general explanations.
/// ```
/// # use rosenpass_to::To;
/// use rosenpass_to::Beside;
/// # struct StringToBytes {
/// # inner: String
/// # }
///
/// # impl To<[u8], Result<(), String>> for StringToBytes {
/// # fn to(self, out: &mut [u8]) -> Result<(), String> {
/// # let bytes = self.inner.as_bytes();
/// # if bytes.len() > out.len() {
/// # return Err("out is to short".to_string());
/// # }
/// # for i in 0..bytes.len() {
/// # (*out)[i] = bytes[i];
/// # }
/// # Ok(())
/// # }
/// # }
/// // StringToBytes is taken from the overall Trait example.
/// let string_to_bytes = StringToBytes { inner: "my message".to_string() };
/// let result = string_to_bytes.to_this_beside(|| [0; 10]).condense();
/// assert!(result.is_ok());
/// assert_eq!(result.unwrap(), [109, 121, 32, 109, 101, 115, 115, 97, 103, 101]);
///
/// ```
fn to_this<Val, Fun>(self, fun: Fun) -> <Ret as CondenseBeside<Val>>::Condensed fn to_this<Val, Fun>(self, fun: Fun) -> <Ret as CondenseBeside<Val>>::Condensed
where where
Ret: CondenseBeside<Val>, Ret: CondenseBeside<Val>,
@@ -77,6 +195,18 @@ pub trait To<Dst: ?Sized, Ret>: Sized {
/// return value into one. /// return value into one.
/// ///
/// This is like using [Self::to_value_beside] followed by calling [Beside::condense]. /// This is like using [Self::to_value_beside] followed by calling [Beside::condense].
///
/// # Example
/// Below, we provide a simple example for the usage of [to_value](To::to_value).
/// We refer to the overall [crate documentation](crate) for more examples and general
/// explanations.
/// ```
/// use rosenpass_to::Beside;
/// use rosenpass_to::To;
/// use rosenpass_to::ops::*;
/// let dst = copy_array(&[42u8; 16]).to_value_beside().condense();
/// assert_eq!(dst, [42u8; 16]);
/// ```
fn to_value(self) -> <Ret as CondenseBeside<Dst>>::Condensed fn to_value(self) -> <Ret as CondenseBeside<Dst>>::Condensed
where where
Dst: Sized + Default, Dst: Sized + Default,
@@ -89,6 +219,19 @@ pub trait To<Dst: ?Sized, Ret>: Sized {
/// return value into one. /// return value into one.
/// ///
/// This is like using [Self::collect_beside] followed by calling [Beside::condense]. /// This is like using [Self::collect_beside] followed by calling [Beside::condense].
///
/// # Example
/// Below, we provide a simple example for the usage of [collect](To::collect).
/// We refer to the overall [crate documentation](crate) for more examples and general
/// explanations.
/// ```
/// use rosenpass_to::Beside;
/// use rosenpass_to::To;
/// use rosenpass_to::ops::*;
///
/// let dst = copy_slice(&[42u8; 16]).collect_beside::<[u8; 16]>().condense();
/// assert_eq!(dst, [42u8; 16]);
/// ```
fn collect<Val>(self) -> <Ret as CondenseBeside<Val>>::Condensed fn collect<Val>(self) -> <Ret as CondenseBeside<Val>>::Condensed
where where
Val: Default + BorrowMut<Dst>, Val: Default + BorrowMut<Dst>,

View File

@@ -1,15 +1,19 @@
//! The module provides the [with_destination] function, which makes it easy to create
//! a [To] from a lambda function. See [with_destination] and the [crate documentation](crate)
//! for more details and examples.
use crate::To; use crate::To;
use std::marker::PhantomData; use std::marker::PhantomData;
/// A struct that wraps a closure and implements the `To` trait /// A struct that wraps a closure and implements the `To` trait.
/// ///
/// This allows passing closures that operate on a destination type `Dst` /// This allows passing closures that operate on a destination type `Dst`
/// and return `Ret`. /// and return `Ret`. It is only internally used to implement [with_destination].
/// ///
/// # Type Parameters /// # Type Parameters
/// * `Dst` - The destination type the closure operates on /// * `Dst` - The destination type the closure operates on.
/// * `Ret` - The return type of the closure /// * `Ret` - The return type of the closure.
/// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret` /// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret`.
struct ToClosure<Dst, Ret, Fun> struct ToClosure<Dst, Ret, Fun>
where where
Dst: ?Sized, Dst: ?Sized,
@@ -17,11 +21,11 @@ where
{ {
/// The function to call. /// The function to call.
fun: Fun, fun: Fun,
/// Phantom data to hold the destination type /// Phantom data to hold the destination type.
_val: PhantomData<Box<Dst>>, _val: PhantomData<Box<Dst>>,
} }
/// Implementation of the `To` trait for ToClosure /// Implementation of the `To` trait for ToClosure.
/// ///
/// This enables calling the wrapped closure with a destination reference. /// This enables calling the wrapped closure with a destination reference.
impl<Dst, Ret, Fun> To<Dst, Ret> for ToClosure<Dst, Ret, Fun> impl<Dst, Ret, Fun> To<Dst, Ret> for ToClosure<Dst, Ret, Fun>
@@ -33,6 +37,7 @@ where
/// ///
/// # Arguments /// # Arguments
/// * `out` - Mutable reference to the destination /// * `out` - Mutable reference to the destination
/// See the tutorial in [readme.md] for examples and more explanations.
fn to(self, out: &mut Dst) -> Ret { fn to(self, out: &mut Dst) -> Ret {
(self.fun)(out) (self.fun)(out)
} }
@@ -48,7 +53,24 @@ where
/// * `Ret` - The return type of the closure /// * `Ret` - The return type of the closure
/// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret` /// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret`
/// ///
/// See the tutorial in [readme.me].. /// See the tutorial in the [crate documentation](crate) for more examples and more explanations.
/// # Example
/// ```
/// # use rosenpass_to::with_destination;
/// use crate::rosenpass_to::To;
/// let my_origin_data: [u8; 16]= [2; 16];
/// let times_two = with_destination( move |dst: &mut [u8; 16]| {
/// for (dst, org) in dst.iter_mut().zip(my_origin_data.iter()) {
/// *dst = dst.clone() * org;
/// }
/// });
/// let mut dst: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
/// times_two.to(&mut dst);
/// for i in 0..16 {
/// assert_eq!(dst[i], (2 * i) as u8);
/// }
///
/// ```
pub fn with_destination<Dst, Ret, Fun>(fun: Fun) -> impl To<Dst, Ret> pub fn with_destination<Dst, Ret, Fun>(fun: Fun) -> impl To<Dst, Ret>
where where
Dst: ?Sized, Dst: ?Sized,