Skip to content

Commit 9d4e033

Browse files
committed
Add trait impls for core::cmp::Ordering
This type is `#[repr(i8)]`, making it possible to impl the following traits for it by casting to `i8` (and back, where appropriate): - `ConditionallySelectable` - `ConstantTimeEq` - `ConstantTimeGreater` - `ConstantTimeLess`
1 parent bd282be commit 9d4e033

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

src/lib.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
#[macro_use]
8888
extern crate std;
8989

90+
use core::cmp;
9091
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not};
9192
use core::option::Option;
9293

@@ -371,6 +372,14 @@ generate_integer_equal!(u64, i64, 64);
371372
generate_integer_equal!(u128, i128, 128);
372373
generate_integer_equal!(usize, isize, ::core::mem::size_of::<usize>() * 8);
373374

375+
/// `Ordering` is `#[repr(i8)] making it possible to leverage `i8::ct_eq`.
376+
impl ConstantTimeEq for cmp::Ordering {
377+
#[inline]
378+
fn ct_eq(&self, other: &Self) -> Choice {
379+
(*self as i8).ct_eq(&(*other as i8))
380+
}
381+
}
382+
374383
/// A type which can be conditionally selected in constant time.
375384
///
376385
/// This trait also provides generic implementations of conditional
@@ -532,6 +541,26 @@ generate_integer_conditional_select!( u64 i64);
532541
#[cfg(feature = "i128")]
533542
generate_integer_conditional_select!(u128 i128);
534543

544+
/// `Ordering` is `#[repr(i8)]` where:
545+
///
546+
/// - `Less` => -1
547+
/// - `Equal` => 0
548+
/// - `Greater` => 1
549+
///
550+
/// Given this, it's possible to operate on orderings as if they're integers,
551+
/// which allows leveraging conditional masking for predication.
552+
impl ConditionallySelectable for cmp::Ordering {
553+
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
554+
let a = *a as i8;
555+
let b = *b as i8;
556+
let ret = i8::conditional_select(&a, &b, choice);
557+
558+
// SAFETY: `Ordering` is `#[repr(i8)]` and `ret` has been assigned to
559+
// a value which was originally a valid `Ordering` then cast to `i8`
560+
unsafe { *((&ret as *const _) as *const cmp::Ordering) }
561+
}
562+
}
563+
535564
impl ConditionallySelectable for Choice {
536565
#[inline]
537566
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
@@ -847,6 +876,16 @@ generate_unsigned_integer_greater!(u64, 64);
847876
#[cfg(feature = "i128")]
848877
generate_unsigned_integer_greater!(u128, 128);
849878

879+
impl ConstantTimeGreater for cmp::Ordering {
880+
#[inline]
881+
fn ct_gt(&self, other: &Self) -> Choice {
882+
// No impl of `ConstantTimeGreater` for `i8`, so use `u8`
883+
let a = (*self as i8) + 1;
884+
let b = (*other as i8) + 1;
885+
(a as u8).ct_gt(&(b as u8))
886+
}
887+
}
888+
850889
/// A type which can be compared in some manner and be determined to be less
851890
/// than another of the same type.
852891
pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
@@ -898,3 +937,13 @@ impl ConstantTimeLess for u32 {}
898937
impl ConstantTimeLess for u64 {}
899938
#[cfg(feature = "i128")]
900939
impl ConstantTimeLess for u128 {}
940+
941+
impl ConstantTimeLess for cmp::Ordering {
942+
#[inline]
943+
fn ct_lt(&self, other: &Self) -> Choice {
944+
// No impl of `ConstantTimeLess` for `i8`, so use `u8`
945+
let a = (*self as i8) + 1;
946+
let b = (*other as i8) + 1;
947+
(a as u8).ct_lt(&(b as u8))
948+
}
949+
}

tests/mod.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
extern crate rand;
22
extern crate subtle;
33

4+
use std::cmp;
5+
46
use rand::rngs::OsRng;
57
use rand::RngCore;
68

@@ -96,6 +98,19 @@ fn custom_conditional_select_i16() {
9698
assert_eq!(i16::conditional_select(&x, &y, 1.into()), 514);
9799
}
98100

101+
#[test]
102+
fn ordering_conditional_select() {
103+
assert_eq!(
104+
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 0.into()),
105+
cmp::Ordering::Less
106+
);
107+
108+
assert_eq!(
109+
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 1.into()),
110+
cmp::Ordering::Greater
111+
);
112+
}
113+
99114
macro_rules! generate_integer_equal_tests {
100115
($($t:ty),*) => ($(
101116
let y: $t = 0; // all 0 bits
@@ -149,6 +164,16 @@ fn choice_equal() {
149164
assert!(Choice::from(1).ct_eq(&Choice::from(1)).unwrap_u8() == 1);
150165
}
151166

167+
#[test]
168+
fn ordering_equal() {
169+
let a = cmp::Ordering::Equal;
170+
let b = cmp::Ordering::Greater;
171+
let c = a;
172+
173+
assert_eq!(a.ct_eq(&b).unwrap_u8(), 0);
174+
assert_eq!(a.ct_eq(&c).unwrap_u8(), 1);
175+
}
176+
152177
#[test]
153178
fn test_ctoption() {
154179
let a = CtOption::new(10, Choice::from(1));
@@ -334,6 +359,12 @@ fn greater_than_u128() {
334359
generate_greater_than_test!(u128);
335360
}
336361

362+
#[test]
363+
fn greater_than_ordering() {
364+
assert_eq!(cmp::Ordering::Less.ct_gt(&cmp::Ordering::Greater).unwrap_u8(), 0);
365+
assert_eq!(cmp::Ordering::Greater.ct_gt(&cmp::Ordering::Less).unwrap_u8(), 1);
366+
}
367+
337368
#[test]
338369
/// Test that the two's compliment min and max, i.e. 0000...0001 < 1111...1110,
339370
/// gives the correct result. (This fails using the bit-twiddling algorithm that
@@ -389,3 +420,9 @@ fn less_than_u64() {
389420
fn less_than_u128() {
390421
generate_less_than_test!(u128);
391422
}
423+
424+
#[test]
425+
fn less_than_ordering() {
426+
assert_eq!(cmp::Ordering::Greater.ct_lt(&cmp::Ordering::Less).unwrap_u8(), 0);
427+
assert_eq!(cmp::Ordering::Less.ct_lt(&cmp::Ordering::Greater).unwrap_u8(), 1);
428+
}

0 commit comments

Comments
 (0)