Skip to content

Commit 1ac233f

Browse files
committed
Merge remote-tracking branch 'tarcieri/ordering-trait-impls' into develop
2 parents 01f8d48 + c319c36 commit 1ac233f

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

src/lib.rs

+49
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
@@ -529,6 +538,26 @@ generate_integer_conditional_select!( u64 i64);
529538
#[cfg(feature = "i128")]
530539
generate_integer_conditional_select!(u128 i128);
531540

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

875+
impl ConstantTimeGreater for cmp::Ordering {
876+
#[inline]
877+
fn ct_gt(&self, other: &Self) -> Choice {
878+
// No impl of `ConstantTimeGreater` for `i8`, so use `u8`
879+
let a = (*self as i8) + 1;
880+
let b = (*other as i8) + 1;
881+
(a as u8).ct_gt(&(b as u8))
882+
}
883+
}
884+
846885
/// A type which can be compared in some manner and be determined to be less
847886
/// than another of the same type.
848887
pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
@@ -893,3 +932,13 @@ impl ConstantTimeLess for u32 {}
893932
impl ConstantTimeLess for u64 {}
894933
#[cfg(feature = "i128")]
895934
impl ConstantTimeLess for u128 {}
935+
936+
impl ConstantTimeLess for cmp::Ordering {
937+
#[inline]
938+
fn ct_lt(&self, other: &Self) -> Choice {
939+
// No impl of `ConstantTimeLess` for `i8`, so use `u8`
940+
let a = (*self as i8) + 1;
941+
let b = (*other as i8) + 1;
942+
(a as u8).ct_lt(&(b as u8))
943+
}
944+
}

tests/mod.rs

+37
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::cmp;
2+
13
use rand::rngs::OsRng;
24
use rand::RngCore;
35

@@ -93,6 +95,19 @@ fn custom_conditional_select_i16() {
9395
assert_eq!(i16::conditional_select(&x, &y, 1.into()), 514);
9496
}
9597

98+
#[test]
99+
fn ordering_conditional_select() {
100+
assert_eq!(
101+
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 0.into()),
102+
cmp::Ordering::Less
103+
);
104+
105+
assert_eq!(
106+
cmp::Ordering::conditional_select(&cmp::Ordering::Less, &cmp::Ordering::Greater, 1.into()),
107+
cmp::Ordering::Greater
108+
);
109+
}
110+
96111
macro_rules! generate_integer_equal_tests {
97112
($($t:ty),*) => ($(
98113
let y: $t = 0; // all 0 bits
@@ -146,6 +161,16 @@ fn choice_equal() {
146161
assert!(Choice::from(1).ct_eq(&Choice::from(1)).unwrap_u8() == 1);
147162
}
148163

164+
#[test]
165+
fn ordering_equal() {
166+
let a = cmp::Ordering::Equal;
167+
let b = cmp::Ordering::Greater;
168+
let c = a;
169+
170+
assert_eq!(a.ct_eq(&b).unwrap_u8(), 0);
171+
assert_eq!(a.ct_eq(&c).unwrap_u8(), 1);
172+
}
173+
149174
#[test]
150175
fn test_ctoption() {
151176
let a = CtOption::new(10, Choice::from(1));
@@ -331,6 +356,12 @@ fn greater_than_u128() {
331356
generate_greater_than_test!(u128);
332357
}
333358

359+
#[test]
360+
fn greater_than_ordering() {
361+
assert_eq!(cmp::Ordering::Less.ct_gt(&cmp::Ordering::Greater).unwrap_u8(), 0);
362+
assert_eq!(cmp::Ordering::Greater.ct_gt(&cmp::Ordering::Less).unwrap_u8(), 1);
363+
}
364+
334365
#[test]
335366
/// Test that the two's compliment min and max, i.e. 0000...0001 < 1111...1110,
336367
/// gives the correct result. (This fails using the bit-twiddling algorithm that
@@ -386,3 +417,9 @@ fn less_than_u64() {
386417
fn less_than_u128() {
387418
generate_less_than_test!(u128);
388419
}
420+
421+
#[test]
422+
fn less_than_ordering() {
423+
assert_eq!(cmp::Ordering::Greater.ct_lt(&cmp::Ordering::Less).unwrap_u8(), 0);
424+
assert_eq!(cmp::Ordering::Less.ct_lt(&cmp::Ordering::Greater).unwrap_u8(), 1);
425+
}

0 commit comments

Comments
 (0)