Skip to content

Commit c21a112

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

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

@@ -350,6 +351,14 @@ generate_integer_equal!(u64, i64, 64);
350351
generate_integer_equal!(u128, i128, 128);
351352
generate_integer_equal!(usize, isize, ::core::mem::size_of::<usize>() * 8);
352353

354+
/// `Ordering` is `#[repr(i32)] making it possible to leverage `i32::ct_eq`.
355+
impl ConstantTimeEq for cmp::Ordering {
356+
#[inline]
357+
fn ct_eq(&self, other: &Self) -> Choice {
358+
(*self as i32).ct_eq(&(*other as i32))
359+
}
360+
}
361+
353362
/// A type which can be conditionally selected in constant time.
354363
///
355364
/// This trait also provides generic implementations of conditional
@@ -511,6 +520,26 @@ generate_integer_conditional_select!( u64 i64);
511520
#[cfg(feature = "i128")]
512521
generate_integer_conditional_select!(u128 i128);
513522

523+
/// `Ordering` is `#[repr(i32)]` where:
524+
///
525+
/// - `Less` => -1
526+
/// - `Equal` => 0
527+
/// - `Greater` => 1
528+
///
529+
/// Given this, it's possible to operate on orderings as if they're integers,
530+
/// which allows leveraging conditional masking for predication.
531+
impl ConditionallySelectable for cmp::Ordering {
532+
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
533+
let a = *a as i32;
534+
let b = *b as i32;
535+
let ret = i32::conditional_select(&a, &b, choice);
536+
537+
// SAFETY: `Ordering` is `#[repr(i32)]` and `ret` has been assigned to
538+
// a value which was originally a valid `Ordering` then cast to `i32`
539+
unsafe { *((&ret as *const _) as *const cmp::Ordering) }
540+
}
541+
}
542+
514543
impl ConditionallySelectable for Choice {
515544
#[inline]
516545
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
@@ -811,6 +840,16 @@ generate_unsigned_integer_greater!(u64, 64);
811840
#[cfg(feature = "i128")]
812841
generate_unsigned_integer_greater!(u128, 128);
813842

843+
impl ConstantTimeGreater for cmp::Ordering {
844+
#[inline]
845+
fn ct_gt(&self, other: &Self) -> Choice {
846+
// No impl of `ConstantTimeGreater` for `i32`, so use `u8`
847+
let a = (*self as i32) + 1;
848+
let b = (*other as i32) + 1;
849+
(a as u8).ct_gt(&(b as u8))
850+
}
851+
}
852+
814853
/// A type which can be compared in some manner and be determined to be less
815854
/// than another of the same type.
816855
pub trait ConstantTimeLess: ConstantTimeEq + ConstantTimeGreater {
@@ -862,3 +901,13 @@ impl ConstantTimeLess for u32 {}
862901
impl ConstantTimeLess for u64 {}
863902
#[cfg(feature = "i128")]
864903
impl ConstantTimeLess for u128 {}
904+
905+
impl ConstantTimeLess for cmp::Ordering {
906+
#[inline]
907+
fn ct_lt(&self, other: &Self) -> Choice {
908+
// No impl of `ConstantTimeLess` for `i32`, so use `u8`
909+
let a = (*self as i32) + 1;
910+
let b = (*other as i32) + 1;
911+
(a as u8).ct_lt(&(b as u8))
912+
}
913+
}

tests/mod.rs

+37
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
@@ -147,6 +162,16 @@ fn choice_equal() {
147162
assert!(Choice::from(1).ct_eq(&Choice::from(1)).unwrap_u8() == 1);
148163
}
149164

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

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

0 commit comments

Comments
 (0)