Skip to content

Commit 8e9e528

Browse files
authored
ctutils: alloc feature (#1367)
Adds impls of `CtAssign`, `CtEq`, `CtFind`, `CtLookup`, and `CtSelect` to `Box<[T]>` and `Vec<T>`
1 parent 2dab4bc commit 8e9e528

File tree

8 files changed

+260
-37
lines changed

8 files changed

+260
-37
lines changed

.github/workflows/ctutils.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
toolchain: ${{ matrix.rust }}
4545
targets: ${{ matrix.target }}
4646
- run: cargo build --target ${{ matrix.target }}
47+
- run: cargo build --target ${{ matrix.target }} --features alloc
4748

4849
# Disabled until there's a stable `cmov` v0.5.0 release
4950
# minimal-versions:
@@ -65,6 +66,7 @@ jobs:
6566
with:
6667
toolchain: ${{ matrix.rust }}
6768
- run: cargo test
69+
- run: cargo test --features alloc
6870
- run: cargo test --features subtle
6971
- run: cargo test --all-features
7072
- run: cargo test --all-features --release

ctutils/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,9 @@ subtle = { version = "2", optional = true, default-features = false }
2525
[dev-dependencies]
2626
proptest = "1.9"
2727

28+
[features]
29+
alloc = []
30+
subtle = ["dep:subtle"]
31+
2832
[package.metadata.docs.rs]
2933
all-features = true

ctutils/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@
8686
//! This makes it possible to use `ctutils` in a codebase where other dependencies are using
8787
//! `subtle`.
8888
89+
#[cfg(feature = "alloc")]
90+
extern crate alloc;
91+
8992
mod bytes;
9093
mod choice;
9194
mod ct_option;

ctutils/src/traits/ct_assign.rs

Lines changed: 84 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ use crate::{Choice, CtSelect};
22
use cmov::Cmov;
33
use core::cmp;
44

5+
#[cfg(feature = "alloc")]
6+
use alloc::{boxed::Box, vec::Vec};
7+
58
/// Constant-time conditional assignment: assign a given value to another based on a [`Choice`].
6-
pub trait CtAssign {
7-
/// Conditionally assign `other` to `self` if `choice` is [`Choice::TRUE`].
8-
fn ct_assign(&mut self, other: &Self, choice: Choice);
9+
pub trait CtAssign<Rhs: ?Sized = Self> {
10+
/// Conditionally assign `rhs` to `self` if `choice` is [`Choice::TRUE`].
11+
fn ct_assign(&mut self, rhs: &Rhs, choice: Choice);
912
}
1013

1114
/// Impl `CtAssign` using the `CtSelect` trait.
@@ -18,8 +21,8 @@ macro_rules! impl_ct_assign_with_ct_select {
1821
$(
1922
impl CtAssign for $ty {
2023
#[inline]
21-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
22-
*self = Self::ct_select(self, other, choice);
24+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
25+
*self = Self::ct_select(self, rhs, choice);
2326
}
2427
}
2528
)+
@@ -34,8 +37,8 @@ macro_rules! impl_ct_assign_with_cmov {
3437
$(
3538
impl CtAssign for $ty {
3639
#[inline]
37-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
38-
self.cmovnz(other, choice.into());
40+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
41+
self.cmovnz(rhs, choice.into());
3942
}
4043
}
4144
)+
@@ -46,33 +49,17 @@ impl_ct_assign_with_cmov!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
4649

4750
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
4851
impl CtAssign for isize {
49-
#[cfg(target_pointer_width = "32")]
50-
#[inline]
51-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
52-
*self = Self::ct_select(self, other, choice);
53-
}
54-
55-
#[cfg(target_pointer_width = "64")]
56-
#[allow(clippy::cast_possible_truncation)]
5752
#[inline]
58-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
59-
*self = Self::ct_select(self, other, choice);
53+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
54+
*self = Self::ct_select(self, rhs, choice);
6055
}
6156
}
6257

6358
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
6459
impl CtAssign for usize {
65-
#[cfg(target_pointer_width = "32")]
66-
#[inline]
67-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
68-
*self = Self::ct_select(self, other, choice);
69-
}
70-
71-
#[cfg(target_pointer_width = "64")]
72-
#[allow(clippy::cast_possible_truncation)]
7360
#[inline]
74-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
75-
*self = Self::ct_select(self, other, choice);
61+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
62+
*self = Self::ct_select(self, rhs, choice);
7663
}
7764
}
7865

@@ -82,7 +69,7 @@ where
8269
{
8370
#[inline]
8471
#[track_caller]
85-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
72+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
8673
const {
8774
assert!(
8875
size_of::<T>() != 1,
@@ -92,13 +79,13 @@ where
9279

9380
assert_eq!(
9481
self.len(),
95-
other.len(),
82+
rhs.len(),
9683
"source slice length ({}) does not match destination slice length ({})",
97-
other.len(),
84+
rhs.len(),
9885
self.len()
9986
);
10087

101-
for (a, b) in self.iter_mut().zip(other) {
88+
for (a, b) in self.iter_mut().zip(rhs) {
10289
a.ct_assign(b, choice)
10390
}
10491
}
@@ -109,16 +96,76 @@ where
10996
T: CtAssign,
11097
{
11198
#[inline]
112-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
113-
self.as_mut_slice().ct_assign(other, choice);
99+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
100+
self.as_mut_slice().ct_assign(rhs, choice);
101+
}
102+
}
103+
104+
#[cfg(feature = "alloc")]
105+
impl<T> CtAssign for Box<T>
106+
where
107+
T: CtAssign,
108+
{
109+
#[inline]
110+
#[track_caller]
111+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
112+
(**self).ct_assign(rhs, choice);
113+
}
114+
}
115+
116+
#[cfg(feature = "alloc")]
117+
impl<T> CtAssign for Box<[T]>
118+
where
119+
T: CtAssign,
120+
{
121+
#[inline]
122+
#[track_caller]
123+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
124+
self.ct_assign(&**rhs, choice);
125+
}
126+
}
127+
128+
#[cfg(feature = "alloc")]
129+
impl<T> CtAssign<[T]> for Box<[T]>
130+
where
131+
T: CtAssign,
132+
{
133+
#[inline]
134+
#[track_caller]
135+
fn ct_assign(&mut self, rhs: &[T], choice: Choice) {
136+
(**self).ct_assign(rhs, choice);
137+
}
138+
}
139+
140+
#[cfg(feature = "alloc")]
141+
impl<T> CtAssign for Vec<T>
142+
where
143+
T: CtAssign,
144+
{
145+
#[inline]
146+
#[track_caller]
147+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
148+
self.ct_assign(rhs.as_slice(), choice);
149+
}
150+
}
151+
152+
#[cfg(feature = "alloc")]
153+
impl<T> CtAssign<[T]> for Vec<T>
154+
where
155+
T: CtAssign,
156+
{
157+
#[inline]
158+
#[track_caller]
159+
fn ct_assign(&mut self, rhs: &[T], choice: Choice) {
160+
self.as_mut_slice().ct_assign(rhs, choice);
114161
}
115162
}
116163

117164
#[cfg(feature = "subtle")]
118165
impl CtAssign for subtle::Choice {
119166
#[inline]
120-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
121-
*self = Self::ct_select(self, other, choice);
167+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
168+
*self = Self::ct_select(self, rhs, choice);
122169
}
123170
}
124171

@@ -128,8 +175,8 @@ where
128175
T: Default + subtle::ConditionallySelectable,
129176
{
130177
#[inline]
131-
fn ct_assign(&mut self, other: &Self, choice: Choice) {
178+
fn ct_assign(&mut self, rhs: &Self, choice: Choice) {
132179
use subtle::ConditionallySelectable as _;
133-
self.conditional_assign(other, choice.into());
180+
self.conditional_assign(rhs, choice.into());
134181
}
135182
}

ctutils/src/traits/ct_eq.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use core::cmp;
44

55
#[cfg(feature = "subtle")]
66
use crate::CtOption;
7+
#[cfg(feature = "alloc")]
8+
use alloc::{boxed::Box, vec::Vec};
79

810
/// Constant-time equality: like `(Partial)Eq` with [`Choice`] instead of [`bool`].
911
///
@@ -111,6 +113,66 @@ impl<T: CtEq, const N: usize> CtEq for [T; N] {
111113
}
112114
}
113115

116+
#[cfg(feature = "alloc")]
117+
impl<T> CtEq for Box<T>
118+
where
119+
T: CtEq,
120+
{
121+
#[inline]
122+
#[track_caller]
123+
fn ct_eq(&self, rhs: &Self) -> Choice {
124+
(**self).ct_eq(rhs)
125+
}
126+
}
127+
128+
#[cfg(feature = "alloc")]
129+
impl<T> CtEq for Box<[T]>
130+
where
131+
T: CtEq,
132+
{
133+
#[inline]
134+
#[track_caller]
135+
fn ct_eq(&self, rhs: &Self) -> Choice {
136+
self.ct_eq(&**rhs)
137+
}
138+
}
139+
140+
#[cfg(feature = "alloc")]
141+
impl<T> CtEq<[T]> for Box<[T]>
142+
where
143+
T: CtEq,
144+
{
145+
#[inline]
146+
#[track_caller]
147+
fn ct_eq(&self, rhs: &[T]) -> Choice {
148+
(**self).ct_eq(rhs)
149+
}
150+
}
151+
152+
#[cfg(feature = "alloc")]
153+
impl<T> CtEq for Vec<T>
154+
where
155+
T: CtEq,
156+
{
157+
#[inline]
158+
#[track_caller]
159+
fn ct_eq(&self, rhs: &Self) -> Choice {
160+
self.ct_eq(rhs.as_slice())
161+
}
162+
}
163+
164+
#[cfg(feature = "alloc")]
165+
impl<T> CtEq<[T]> for Vec<T>
166+
where
167+
T: CtEq,
168+
{
169+
#[inline]
170+
#[track_caller]
171+
fn ct_eq(&self, rhs: &[T]) -> Choice {
172+
self.as_slice().ct_eq(rhs)
173+
}
174+
}
175+
114176
#[cfg(feature = "subtle")]
115177
impl CtEq for subtle::Choice {
116178
#[inline]

ctutils/src/traits/ct_find.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use crate::{Choice, CtOption, CtSelect};
22

3+
#[cfg(feature = "alloc")]
4+
use alloc::{boxed::Box, vec::Vec};
5+
36
#[cfg(doc)]
47
use core::iter::Iterator;
58

@@ -21,6 +24,7 @@ impl<T> CtFind<T> for [T]
2124
where
2225
T: CtSelect + Default,
2326
{
27+
#[inline]
2428
fn ct_find<P>(&self, predicate: P) -> CtOption<T>
2529
where
2630
P: Fn(&T) -> Choice,
@@ -39,6 +43,35 @@ impl<T, const N: usize> CtFind<T> for [T; N]
3943
where
4044
T: CtSelect + Default,
4145
{
46+
#[inline]
47+
fn ct_find<P>(&self, predicate: P) -> CtOption<T>
48+
where
49+
P: Fn(&T) -> Choice,
50+
{
51+
self.as_slice().ct_find(predicate)
52+
}
53+
}
54+
55+
#[cfg(feature = "alloc")]
56+
impl<T> CtFind<T> for Box<[T]>
57+
where
58+
T: CtSelect + Default,
59+
{
60+
#[inline]
61+
fn ct_find<P>(&self, predicate: P) -> CtOption<T>
62+
where
63+
P: Fn(&T) -> Choice,
64+
{
65+
(**self).ct_find(predicate)
66+
}
67+
}
68+
69+
#[cfg(feature = "alloc")]
70+
impl<T> CtFind<T> for Vec<T>
71+
where
72+
T: CtSelect + Default,
73+
{
74+
#[inline]
4275
fn ct_find<P>(&self, predicate: P) -> CtOption<T>
4376
where
4477
P: Fn(&T) -> Choice,

0 commit comments

Comments
 (0)