Skip to content

Commit 2fc06e6

Browse files
authored
ctutils: enforce Bytes* trait usage at compile-time (#1364)
The `Bytes*` traits exist because we don't have specialization[1] and the generic implementation of these traits for `[T]` and `[T; N]` will operate a byte-at-a-time on byte slices and byte arrays, when efficient implementations from the `cmov` crate can instead perform the same operations a word-at-a-time. Adds != 1 const size assertions for `T` in the impls of `CtAssign`, `CtEq` for `[T]` and `[T; N]` as well as the `CtSelect` impl for `[T; N]` which cause a compile-time error if they're requested to be used with 1-byte values: error[E0080]: evaluation panicked: use `BytesCtEq::bytes_ct_eq` when working with byte-sized values --> ctutils/src/traits/ct_eq.rs:81:13 | 81 | / assert!( 82 | | size_of::<T>() != 1, 83 | | "use `BytesCtEq::bytes_ct_eq` when working with byte-sized values" 84 | | ); | |_____________^ evaluation of `<[u8] as traits::ct_eq::CtEq>::ct_eq::{constant#0}` failed here Each of them has been configured with a message that steers the user instead towards the appropriate `BytesCt*` trait to use instead. [1]: https://rust-lang.github.io/rfcs/1210-impl-specialization.html
1 parent 450a85b commit 2fc06e6

File tree

3 files changed

+33
-0
lines changed

3 files changed

+33
-0
lines changed

ctutils/src/traits/ct_assign.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ where
7070
#[inline]
7171
#[track_caller]
7272
fn ct_assign(&mut self, other: &Self, choice: Choice) {
73+
const {
74+
assert!(
75+
size_of::<T>() != 1,
76+
"use `BytesCtAssign::bytes_ct_assign` when working with byte-sized values"
77+
);
78+
}
79+
7380
assert_eq!(
7481
self.len(),
7582
other.len(),

ctutils/src/traits/ct_eq.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,31 @@ impl CtEq for cmp::Ordering {
7777
impl<T: CtEq> CtEq for [T] {
7878
#[inline]
7979
fn ct_eq(&self, other: &[T]) -> Choice {
80+
const {
81+
assert!(
82+
size_of::<T>() != 1,
83+
"use `BytesCtEq::bytes_ct_eq` when working with byte-sized values"
84+
);
85+
}
86+
8087
let mut ret = self.len().ct_eq(&other.len());
8188
for (a, b) in self.iter().zip(other.iter()) {
8289
ret &= a.ct_eq(b);
8390
}
8491
ret
8592
}
93+
94+
#[inline]
95+
fn ct_ne(&self, other: &[T]) -> Choice {
96+
const {
97+
assert!(
98+
size_of::<T>() != 1,
99+
"use `BytesCtEq::bytes_ct_ne` when working with byte-sized values"
100+
);
101+
}
102+
103+
!self.ct_eq(other)
104+
}
86105
}
87106

88107
impl<T: CtEq, const N: usize> CtEq for [T; N] {

ctutils/src/traits/ct_select.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ where
9898
{
9999
#[inline]
100100
fn ct_select(&self, other: &Self, choice: Choice) -> Self {
101+
const {
102+
assert!(
103+
size_of::<T>() != 1,
104+
"use `BytesCtSelect::bytes_ct_select` when working with byte-sized values"
105+
);
106+
}
107+
101108
core::array::from_fn(|i| T::ct_select(&self[i], &other[i], choice))
102109
}
103110
}

0 commit comments

Comments
 (0)