Skip to content

Commit 5703f7b

Browse files
committed
ctutils: pluggable trait impls for [T] and [T; N]
Introduces a set of helper traits for the following: - `CtAssign`: `CtAssignSlice` - `CtEq`: `CtEqSlice` - `CtSelect`: `CtSelectArray` Implementing a `*Slice` trait unlocks a blanket impl for the corresponding trait for `[T]`, e.g. impl'ing `CtAssignSlice` for `T` means `CtAssign` will now work with `[T]`. Likewise, implementing `CtSelectArray` unlocks the blanket impl of `CtSelect` for `[T; N]`, and `CtSelectArray` itself is already blanket impl'd for types which impl `Clone + CtAssignSlice + CtSelect`. All traits provide a default implementation with the option to plug in a faster one. The implementations on core integers thunk to the corresponding `Cmov`/`CmovEq` impls on slices of core integers, which in some cases have been optimized (especially `u8`).
1 parent 14621c5 commit 5703f7b

6 files changed

Lines changed: 209 additions & 266 deletions

File tree

ctutils/src/array.rs

Lines changed: 0 additions & 91 deletions
This file was deleted.

ctutils/src/lib.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,19 @@
9696
#[cfg(feature = "alloc")]
9797
extern crate alloc;
9898

99-
pub mod array;
100-
pub mod slice;
101-
10299
mod choice;
103100
mod ct_option;
104101
mod traits;
105102

106103
pub use choice::Choice;
107104
pub use ct_option::CtOption;
108105
pub use traits::{
109-
ct_assign::CtAssign, ct_eq::CtEq, ct_find::CtFind, ct_gt::CtGt, ct_lookup::CtLookup,
110-
ct_lt::CtLt, ct_neg::CtNeg, ct_select::CtSelect,
106+
ct_assign::{CtAssign, CtAssignSlice},
107+
ct_eq::{CtEq, CtEqSlice},
108+
ct_find::CtFind,
109+
ct_gt::CtGt,
110+
ct_lookup::CtLookup,
111+
ct_lt::CtLt,
112+
ct_neg::CtNeg,
113+
ct_select::{CtSelect, CtSelectArray},
111114
};

ctutils/src/slice.rs

Lines changed: 0 additions & 83 deletions
This file was deleted.

ctutils/src/traits/ct_assign.rs

Lines changed: 83 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,89 @@ use core::{
1111
#[cfg(feature = "alloc")]
1212
use alloc::{boxed::Box, vec::Vec};
1313

14+
#[cfg(doc)]
15+
use core::num::NonZero;
16+
1417
/// Constant-time conditional assignment: assign a given value to another based on a [`Choice`].
18+
///
19+
/// This crate provides built-in implementations for the following types:
20+
/// - [`i8`], [`i16`], [`i32`], [`i64`], [`i128`], [`isize`]
21+
/// - [`u8`], [`u16`], [`u32`], [`u64`], [`u128`], [`usize`]
22+
/// - [`NonZeroI8`], [`NonZeroI16`], [`NonZeroI32`], [`NonZeroI64`], [`NonZeroI128`]
23+
/// - [`NonZeroU8`], [`NonZeroU16`], [`NonZeroU32`], [`NonZeroU64`], [`NonZeroU128`]
24+
/// - [`cmp::Ordering`]
25+
/// - [`Choice`]
26+
/// - `[T]` and `[T; N]` where `T` impls [`CtAssignSlice`], which the previously mentioned
27+
/// types all do.
1528
pub trait CtAssign<Rhs: ?Sized = Self> {
16-
/// Conditionally assign `rhs` to `self` if `choice` is [`Choice::TRUE`].
17-
fn ct_assign(&mut self, rhs: &Rhs, choice: Choice);
29+
/// Conditionally assign `src` to `self` if `choice` is [`Choice::TRUE`].
30+
fn ct_assign(&mut self, src: &Rhs, choice: Choice);
31+
}
32+
33+
/// Implementing this trait enables use of the [`CtAssign`] trait for `[T]` where `T` is the
34+
/// `Self` type implementing the trait, via a blanket impl.
35+
///
36+
/// It needs to be a separate trait from [`CtAssign`] because we need to be able to impl
37+
/// [`CtAssign`] for `[T]`.
38+
pub trait CtAssignSlice: CtAssign + Sized {
39+
/// Conditionally assign `src` to `dst` if `choice` is [`Choice::TRUE`], or leave it unchanged
40+
/// for [`Choice::FALSE`].
41+
fn ct_assign_slice(dst: &mut [Self], src: &[Self], choice: Choice) {
42+
assert_eq!(
43+
dst.len(),
44+
src.len(),
45+
"source slice length ({}) does not match destination slice length ({})",
46+
src.len(),
47+
dst.len()
48+
);
49+
50+
for (a, b) in dst.iter_mut().zip(src) {
51+
a.ct_assign(b, choice);
52+
}
53+
}
54+
}
55+
56+
impl<T: CtAssignSlice> CtAssign for [T] {
57+
fn ct_assign(&mut self, src: &[T], choice: Choice) {
58+
T::ct_assign_slice(self, src, choice);
59+
}
60+
}
61+
62+
/// Impl `CtAssign` using the `cmov::Cmov` trait
63+
macro_rules! impl_ct_assign_with_cmov {
64+
( $($ty:ty),+ ) => {
65+
$(
66+
impl CtAssign for $ty {
67+
#[inline]
68+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
69+
self.cmovnz(rhs, choice.into());
70+
}
71+
}
72+
)+
73+
};
74+
}
75+
76+
/// Impl `CtAssign` and `CtAssignSlice` using the `cmov::Cmov` trait
77+
macro_rules! impl_ct_assign_slice_with_cmov {
78+
( $($ty:ty),+ ) => {
79+
$(
80+
impl_ct_assign_with_cmov!($ty);
81+
82+
impl CtAssignSlice for $ty {
83+
#[inline]
84+
fn ct_assign_slice(dst: &mut [Self], src: &[Self], choice: Choice) {
85+
dst.cmovnz(src, choice.into());
86+
}
87+
}
88+
)+
89+
};
1890
}
1991

92+
impl_ct_assign_slice_with_cmov!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
93+
impl_ct_assign_with_cmov!(isize, usize);
94+
impl CtAssignSlice for isize {}
95+
impl CtAssignSlice for usize {}
96+
2097
/// Impl `CtAssign` using the `CtSelect` trait.
2198
///
2299
/// In cases where `CtSelect` is more straightforward to implement, but you want to use a provided
@@ -31,6 +108,8 @@ macro_rules! impl_ct_assign_with_ct_select {
31108
*self = Self::ct_select(self, rhs, choice);
32109
}
33110
}
111+
112+
impl CtAssignSlice for $ty {}
34113
)+
35114
};
36115
}
@@ -49,50 +128,6 @@ impl_ct_assign_with_ct_select!(
49128
NonZeroU128
50129
);
51130

52-
/// Impl `CtAssign` using the `cmov::Cmov` trait
53-
macro_rules! impl_ct_assign_with_cmov {
54-
( $($ty:ty),+ ) => {
55-
$(
56-
impl CtAssign for $ty {
57-
#[inline]
58-
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
59-
self.cmovnz(rhs, choice.into());
60-
}
61-
}
62-
)+
63-
};
64-
}
65-
66-
impl_ct_assign_with_cmov!(
67-
i8,
68-
i16,
69-
i32,
70-
i64,
71-
i128,
72-
u8,
73-
u16,
74-
u32,
75-
u64,
76-
u128,
77-
[i8],
78-
[i16],
79-
[i32],
80-
[i64],
81-
[i128],
82-
[u8],
83-
[u16],
84-
[u32],
85-
[u64],
86-
[u128]
87-
);
88-
89-
#[cfg(any(
90-
target_pointer_width = "16",
91-
target_pointer_width = "32",
92-
target_pointer_width = "64"
93-
))]
94-
impl_ct_assign_with_cmov!(isize, usize);
95-
96131
impl<T, const N: usize> CtAssign for [T; N]
97132
where
98133
[T]: CtAssign,
@@ -103,6 +138,8 @@ where
103138
}
104139
}
105140

141+
impl<T, const N: usize> CtAssignSlice for [T; N] where [T]: CtAssign {}
142+
106143
#[cfg(feature = "alloc")]
107144
impl<T> CtAssign for Box<T>
108145
where

0 commit comments

Comments
 (0)