#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
#![deny(unsafe_code)]
#![deny(fat_ptr_transmutes)]
#![cfg_attr(feature = "nightly", feature(i128_type))]
#![cfg_attr(feature = "bench", feature(test))]
#[cfg(feature = "std")]
extern crate core;
#[cfg(feature = "std")]
extern crate num_traits;
#[cfg(feature = "std")]
use core::ops::BitAnd;
#[cfg(feature = "std")]
use core::ops::BitOr;
#[cfg(feature = "std")]
use core::ops::Not;
#[cfg(feature = "std")]
use core::ops::Sub;
use core::ops::Neg;
#[cfg(feature = "std")]
use num_traits::One;
pub type Mask = u8;
pub trait Equal {
fn ct_eq(&self, other: &Self) -> Mask;
}
macro_rules! generate_integer_equal {
($t:ty, $maxshift:expr) => (
impl Equal for $t {
#[inline(always)]
fn ct_eq(&self, other: &$t) -> Mask {
let mut x: $t = !(self ^ other);
let mut shift: $t = $maxshift;
while shift >= 1 {
x &= x >> shift;
shift /= 2;
}
x as Mask
}
}
)
}
generate_integer_equal!( u8, 4u8);
generate_integer_equal!(u16, 8u16);
generate_integer_equal!(u32, 16u32);
generate_integer_equal!(u64, 32u64);
#[cfg(feature = "nightly")]
generate_integer_equal!(u128, 64u128);
pub trait ConditionallyAssignable {
fn conditional_assign(&mut self, other: &Self, choice: Mask);
}
macro_rules! generate_integer_conditional_assign {
($($t:ty)*) => ($(
impl ConditionallyAssignable for $t {
#[inline(always)]
fn conditional_assign(&mut self, other: &$t, choice: Mask) {
let mask = -(choice as i8) as u8;
*self = *self ^ ((mask as $t) & (*self ^ *other));
}
}
)*)
}
generate_integer_conditional_assign!(u8 u16 u32 u64);
generate_integer_conditional_assign!(i8 i16 i32 i64);
#[cfg(feature = "nightly")]
generate_integer_conditional_assign!(u128 i128);
#[macro_export]
macro_rules! generate_array_conditional_assign {
($([$t:ty; $n:expr]),*) => ($(
impl ConditionallyAssignable for [$t; $n] {
#[inline(always)]
fn conditional_assign(&mut self, other: &[$t; $n], choice: Mask) {
let mask = -(choice as i8) as u8;
for i in 0 .. $n {
self[i] = self[i] ^ ((mask as $t) & (self[i] ^ other[i]));
}
}
}
)*)
}
macro_rules! generate_array_conditional_assign_1_through_32 {
($($t:ty),*) => ($(
generate_array_conditional_assign!([$t; 1], [$t; 2], [$t; 3], [$t; 4]);
generate_array_conditional_assign!([$t; 5], [$t; 6], [$t; 7], [$t; 8]);
generate_array_conditional_assign!([$t; 9], [$t; 10], [$t; 11], [$t; 12]);
generate_array_conditional_assign!([$t; 13], [$t; 14], [$t; 15], [$t; 16]);
generate_array_conditional_assign!([$t; 17], [$t; 18], [$t; 19], [$t; 20]);
generate_array_conditional_assign!([$t; 21], [$t; 22], [$t; 23], [$t; 24]);
generate_array_conditional_assign!([$t; 25], [$t; 26], [$t; 27], [$t; 28]);
generate_array_conditional_assign!([$t; 29], [$t; 30], [$t; 31], [$t; 32]);
)*)
}
generate_array_conditional_assign_1_through_32!(u8, u16, u32, u64);
#[cfg(feature = "nightly")]
generate_array_conditional_assign_1_through_32!(u128);
macro_rules! generate_arrays_equal {
($([$t:ty; $n:expr]),*) => ($(
impl Equal for [$t; $n] {
#[inline(always)]
fn ct_eq(&self, other: &[$t; $n]) -> Mask {
let mut x: $t = 0;
for i in 0 .. $n {
x |= self[i] ^ other[i];
}
x.ct_eq(&0)
}
}
)*)
}
macro_rules! generate_arrays_equal_1_through_32 {
($($t:ty),*) => ($(
generate_arrays_equal!([$t; 1], [$t; 2], [$t; 3], [$t; 4]);
generate_arrays_equal!([$t; 5], [$t; 6], [$t; 7], [$t; 8]);
generate_arrays_equal!([$t; 9], [$t; 10], [$t; 11], [$t; 12]);
generate_arrays_equal!([$t; 13], [$t; 14], [$t; 15], [$t; 16]);
generate_arrays_equal!([$t; 17], [$t; 18], [$t; 19], [$t; 20]);
generate_arrays_equal!([$t; 21], [$t; 22], [$t; 23], [$t; 24]);
generate_arrays_equal!([$t; 25], [$t; 26], [$t; 27], [$t; 28]);
generate_arrays_equal!([$t; 29], [$t; 30], [$t; 31], [$t; 32]);
)*)
}
generate_arrays_equal_1_through_32!(u8, u16, u32, u64);
#[cfg(feature = "nightly")]
generate_arrays_equal_1_through_32!(u128);
#[inline(always)]
pub fn bytes_equal(a: u8, b: u8) -> Mask {
a.ct_eq(&b)
}
pub trait ConditionallyNegatable {
fn conditional_negate(&mut self, choice: Mask);
}
impl<T> ConditionallyNegatable for T
where T: ConditionallyAssignable, for<'a> &'a T: Neg<Output = T>
{
fn conditional_negate(&mut self, choice: Mask) {
let self_neg: T = -(self as &T);
self.conditional_assign(&self_neg, choice);
}
}
#[inline(always)]
#[cfg(feature = "std")]
pub fn conditional_select<T>(a: T, b: T, choice: T) -> T
where T: PartialEq + PartialOrd + Copy +
One + Sub<T, Output = T> + Not<Output = T> +
BitAnd<T, Output = T> + BitOr<T, Output = T> {
(!(choice - T::one()) & a) | ((choice - T::one()) & b)
}
pub trait ConditionallySwappable {
fn conditional_swap(&mut self, other: &mut Self, choice: Mask);
}
impl<T> ConditionallySwappable for T
where T: ConditionallyAssignable + Copy
{
fn conditional_swap(&mut self, other: &mut T, choice: Mask) {
let temp: T = *self;
self.conditional_assign(&other, choice);
other.conditional_assign(&temp, choice);
}
}
pub trait IsNonZero {
fn is_nonzero(&self) -> Mask;
}
#[inline(always)]
pub fn byte_is_nonzero(b: u8) -> Mask {
let mut x = b;
x |= x >> 4;
x |= x >> 2;
x |= x >> 1;
(x & 1)
}
#[inline(always)]
pub fn slices_equal(a: &[u8], b: &[u8]) -> Mask {
assert_eq!(a.len(), b.len());
let mut x: u8 = 0;
let len = a.len();
let a = &a[..len];
let b = &b[..len];
for i in 0 .. len {
x |= a[i] ^ b[i];
}
bytes_equal(x, 0)
}
#[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!(slices_equal(&a, &b) == 1);
}
#[test]
#[cfg(feature = "std")]
fn conditional_select_i32() {
let a: i32 = 5;
let b: i32 = 13;
assert_eq!(conditional_select(a, b, 0), 13);
assert_eq!(conditional_select(a, b, 1), 5);
}
#[test]
#[cfg(feature = "std")]
fn conditional_select_i64() {
let c: i64 = 2343249123;
let d: i64 = 8723884895;
assert_eq!(conditional_select(c, d, 0), d);
assert_eq!(conditional_select(c, d, 1), c);
}
macro_rules! generate_integer_conditional_assign_tests {
($($t:ty)*) => ($(
let mut x: $t = 13;
let y: $t = 42;
x.conditional_assign(&y, 0);
assert_eq!(x, 13);
x.conditional_assign(&y, 1);
assert_eq!(x, 42);
)*)
}
#[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);
}
macro_rules! generate_array_conditional_assign_tests {
($([$t:ty; $n:expr]),*) => ($(
let mut x: [$t; $n] = [13; $n];
let y: [$t; $n] = [42; $n];
x.conditional_assign(&y, 0);
assert_eq!(x, [13; $n]);
x.conditional_assign(&y, 1);
assert_eq!(x, [42; $n]);
)*)
}
macro_rules! generate_array_conditional_assign_1_through_32_tests {
($($t:ty),*) => ($(
generate_array_conditional_assign_tests!([$t; 1], [$t; 2], [$t; 3], [$t; 4]);
generate_array_conditional_assign_tests!([$t; 5], [$t; 6], [$t; 7], [$t; 8]);
generate_array_conditional_assign_tests!([$t; 9], [$t; 10], [$t; 11], [$t; 12]);
generate_array_conditional_assign_tests!([$t; 13], [$t; 14], [$t; 15], [$t; 16]);
generate_array_conditional_assign_tests!([$t; 17], [$t; 18], [$t; 19], [$t; 20]);
generate_array_conditional_assign_tests!([$t; 21], [$t; 22], [$t; 23], [$t; 24]);
generate_array_conditional_assign_tests!([$t; 25], [$t; 26], [$t; 27], [$t; 28]);
generate_array_conditional_assign_tests!([$t; 29], [$t; 30], [$t; 31], [$t; 32]);
)*)
}
#[test]
fn array_conditional_assign() {
generate_array_conditional_assign_1_through_32_tests!(u8, u16, u32, u64);
#[cfg(feature = "nightly")]
generate_array_conditional_assign_1_through_32_tests!(u128);
}
macro_rules! generate_integer_equal_tests {
($($t:ty),*) => ($(
let x: $t = 13;
let y: $t = 42;
let z: $t = 13;
assert_eq!(x.ct_eq(&y), 0);
assert_eq!(x.ct_eq(&z), 1);
)*)
}
#[test]
fn integer_equal() {
generate_integer_equal_tests!(u8, u16, u32, u64);
#[cfg(feature = "nightly")]
generate_integer_equal_tests!(u128);
}
macro_rules! generate_arrays_equal_tests {
($([$t:ty; $n:expr]),*) => ($(
let x: [$t; $n] = [13; $n];
let y: [$t; $n] = [42; $n];
let z: [$t; $n] = [13; $n];
assert_eq!(x.ct_eq(&y), 0);
assert_eq!(x.ct_eq(&z), 1);
)*)
}
macro_rules! generate_arrays_equal_1_through_32_tests {
($($t:ty),*) => ($(
generate_arrays_equal_tests!([$t; 1], [$t; 2], [$t; 3], [$t; 4]);
generate_arrays_equal_tests!([$t; 5], [$t; 6], [$t; 7], [$t; 8]);
generate_arrays_equal_tests!([$t; 9], [$t; 10], [$t; 11], [$t; 12]);
generate_arrays_equal_tests!([$t; 13], [$t; 14], [$t; 15], [$t; 16]);
generate_arrays_equal_tests!([$t; 17], [$t; 18], [$t; 19], [$t; 20]);
generate_arrays_equal_tests!([$t; 21], [$t; 22], [$t; 23], [$t; 24]);
generate_arrays_equal_tests!([$t; 25], [$t; 26], [$t; 27], [$t; 28]);
generate_arrays_equal_tests!([$t; 29], [$t; 30], [$t; 31], [$t; 32]);
)*)
}
#[test]
fn arrays_equal() {
generate_arrays_equal_1_through_32_tests!(u8, u16, u32, u64);
#[cfg(feature = "nightly")]
generate_arrays_equal_1_through_32_tests!(u128);
}
}
#[cfg(all(test, feature = "bench"))]
mod bench {
extern crate test;
use self::test::Bencher;
use super::*;
#[bench]
fn slices_equal_unequal(b: &mut Bencher) {
let x: [u8; 100_000] = [13; 100_000];
let y: [u8; 100_000] = [42; 100_000];
b.iter(| | slices_equal(&x, &y));
}
#[bench]
fn slices_equal_equal(b: &mut Bencher) {
let x: [u8; 100_000] = [13; 100_000];
let y: [u8; 100_000] = [13; 100_000];
b.iter(| | slices_equal(&x, &y));
}
}