#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(i128_type))]
#![cfg_attr(feature = "nightly", feature(test))]
#![cfg_attr(feature = "nightly", deny(missing_docs))]
#![cfg_attr(feature = "nightly", feature(external_doc))]
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
#![cfg_attr(feature = "nightly", feature(asm))]
#[cfg(feature = "std")]
extern crate core;
use core::ops::{BitAnd, BitOr, BitXor, Not};
#[cfg(feature = "generic-impls")]
use core::ops::Neg;
#[derive(Copy, Clone)]
pub struct Choice(u8);
impl Choice {
#[inline]
pub fn unwrap_u8(&self) -> u8 {
self.0
}
}
impl BitAnd for Choice {
type Output = Choice;
#[inline]
fn bitand(self, rhs: Choice) -> Choice {
(self.0 & rhs.0).into()
}
}
impl BitOr for Choice {
type Output = Choice;
#[inline]
fn bitor(self, rhs: Choice) -> Choice {
(self.0 | rhs.0).into()
}
}
impl BitXor for Choice {
type Output = Choice;
#[inline]
fn bitxor(self, rhs: Choice) -> Choice {
(self.0 ^ rhs.0).into()
}
}
impl Not for Choice {
type Output = Choice;
#[inline]
fn not(self) -> Choice {
(1u8 & (!self.0)).into()
}
}
#[cfg(all(feature = "nightly", not(any(target_arch = "asmjs", target_arch = "wasm32"))))]
pub fn noop(input: u8) -> u8 {
debug_assert!( input == 0u8 || input == 1u8 );
unsafe { asm!("" :: "r"(&input) :: "volatile") }
input
}
#[cfg(any(target_arch = "asmjs", target_arch = "wasm32", not(feature = "nightly")))]
#[inline(never)]
pub fn noop(input: u8) -> u8 {
debug_assert!( input == 0u8 || input == 1u8 );
input
}
impl From<u8> for Choice {
#[inline]
fn from(input: u8) -> Choice {
Choice(noop(input))
}
}
pub trait ConstantTimeEq {
#[inline]
fn ct_eq(&self, other: &Self) -> Choice;
}
impl<T: ConstantTimeEq> ConstantTimeEq for [T] {
#[inline]
fn ct_eq(&self, _rhs: &[T]) -> Choice {
let len = self.len();
if len != _rhs.len() { return Choice::from(0); }
let mut x = 1u8;
for (ai, bi) in self.iter().zip(_rhs.iter()) {
x &= ai.ct_eq(bi).unwrap_u8();
}
x.into()
}
}
macro_rules! generate_integer_equal {
($t_u:ty, $t_i:ty, $bit_width:expr) => (
impl ConstantTimeEq for $t_u {
#[inline]
fn ct_eq(&self, other: &$t_u) -> Choice {
let mut x: $t_u = !(self ^ other);
let mut shift: usize = $bit_width / 2;
while shift >= 1 {
x &= x >> shift;
shift /= 2;
}
(x as u8).into()
}
}
impl ConstantTimeEq for $t_i {
#[inline]
fn ct_eq(&self, other: &$t_i) -> Choice {
(*self as $t_u).ct_eq(&(*other as $t_u))
}
}
)
}
generate_integer_equal!( u8, i8, 8);
generate_integer_equal!( u16, i16, 16);
generate_integer_equal!( u32, i32, 32);
generate_integer_equal!( u64, i64, 64);
#[cfg(feature = "nightly")]
generate_integer_equal!(u128, i128, 128);
pub trait ConditionallyAssignable {
#[inline]
fn conditional_assign(&mut self, other: &Self, choice: Choice);
}
macro_rules! to_signed_int {
(u8) => {i8};
(u16) => {i16};
(u32) => {i32};
(u64) => {i64};
(u128) => {i128};
(i8) => {i8};
(i16) => {i16};
(i32) => {i32};
(i64) => {i64};
(i128) => {i128};
}
macro_rules! generate_integer_conditional_assign {
($($t:tt)*) => ($(
impl ConditionallyAssignable for $t {
#[inline]
fn conditional_assign(&mut self, other: &$t, choice: Choice) {
let mask = -(choice.unwrap_u8() as to_signed_int!($t)) as $t;
*self = *self ^ ((mask) & (*self ^ *other));
}
}
)*)
}
generate_integer_conditional_assign!( u8 i8);
generate_integer_conditional_assign!( u16 i16);
generate_integer_conditional_assign!( u32 i32);
generate_integer_conditional_assign!( u64 i64);
#[cfg(feature = "nightly")]
generate_integer_conditional_assign!(u128 i128);
pub trait ConditionallyNegatable {
#[inline]
fn conditional_negate(&mut self, choice: Choice);
}
#[cfg(feature = "generic-impls")]
impl<T> ConditionallyNegatable for T
where T: ConditionallyAssignable, for<'a> &'a T: Neg<Output = T>
{
#[inline]
fn conditional_negate(&mut self, choice: Choice) {
let self_neg: T = -(self as &T);
self.conditional_assign(&self_neg, choice);
}
}
pub trait ConditionallySelectable {
#[inline]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self;
}
#[cfg(feature = "generic-impls")]
impl<T> ConditionallySelectable for T
where T: Copy + ConditionallyAssignable
{
#[inline]
fn conditional_select(a: &T, b: &T, choice: Choice) -> T {
let mut tmp = *a;
tmp.conditional_assign(b, choice);
tmp
}
}
pub trait ConditionallySwappable {
#[inline]
fn conditional_swap(&mut self, other: &mut Self, choice: Choice);
}
#[cfg(feature = "generic-impls")]
impl<T> ConditionallySwappable for T
where T: ConditionallyAssignable + Copy
{
#[inline]
fn conditional_swap(&mut self, other: &mut T, choice: Choice) {
let temp: T = *self;
self.conditional_assign(&other, choice);
other.conditional_assign(&temp, choice);
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[should_panic]
fn slices_equal_different_lengths() {
let a: [u8; 3] = [0, 0, 0];
let b: [u8; 4] = [0, 0, 0, 0];
assert_eq!((&a).ct_eq(&b).unwrap_u8(), 1);
}
#[test]
fn slices_equal() {
let a: [u8; 8] = [1,2,3,4,5,6,7,8];
let b: [u8; 8] = [1,2,3,4,4,3,2,1];
let a_eq_a = (&a).ct_eq(&a);
let a_eq_b = (&a).ct_eq(&b);
assert_eq!(a_eq_a.unwrap_u8(), 1);
assert_eq!(a_eq_b.unwrap_u8(), 0);
let c: [u8; 16] = [0u8; 16];
let a_eq_c = (&a).ct_eq(&c);
assert_eq!(a_eq_c.unwrap_u8(), 0);
}
#[test]
#[cfg(feature = "generic-impls")]
fn conditional_select_i32() {
let a: i32 = 5;
let b: i32 = 13;
assert_eq!(i32::conditional_select(&a, &b, 0.into()), a);
assert_eq!(i32::conditional_select(&a, &b, 1.into()), b);
}
#[test]
#[cfg(feature = "generic-impls")]
fn conditional_select_i64() {
let c: i64 = 2343249123;
let d: i64 = 8723884895;
assert_eq!(i64::conditional_select(&c, &d, 0.into()), c);
assert_eq!(i64::conditional_select(&c, &d, 1.into()), d);
}
macro_rules! generate_integer_conditional_assign_tests {
($($t:ty)*) => ($(
let mut x: $t = 0; let y: $t = !0;
x.conditional_assign(&y, 0.into());
assert_eq!(x, 0);
x.conditional_assign(&y, 1.into());
assert_eq!(x, y);
)*)
}
#[test]
fn integer_conditional_assign() {
generate_integer_conditional_assign_tests!(u8 u16 u32 u64);
generate_integer_conditional_assign_tests!(i8 i16 i32 i64);
#[cfg(feature = "nightly")]
generate_integer_conditional_assign_tests!(u128 i128);
}
#[test]
fn custom_conditional_assign_i16() {
let mut x: i16 = 257;
let y: i16 = 514;
x.conditional_assign(&y, 0.into());
assert_eq!(x, 257);
x.conditional_assign(&y, 1.into());
assert_eq!(x, 514);
}
macro_rules! generate_integer_equal_tests {
($($t:ty),*) => ($(
let y: $t = 0; let z: $t = !0;
let x = z;
assert_eq!(x.ct_eq(&y).unwrap_u8(), 0);
assert_eq!(x.ct_eq(&z).unwrap_u8(), 1);
)*)
}
#[test]
fn integer_equal() {
generate_integer_equal_tests!(u8, u16, u32, u64);
generate_integer_equal_tests!(i8, i16, i32, i64);
#[cfg(feature = "nightly")]
generate_integer_equal_tests!(i128 u128);
}
}