Files
cryptonum/src/unsigned/shifts.rs

152 lines
4.4 KiB
Rust

pub(crate) fn shiftl(res: &mut [u64], copy: &[u64], amt: usize) {
let digits = amt / 64;
let bits = amt % 64;
let mut carry = 0;
let shift = 64 - bits;
for i in 0..res.len() {
let base = if i >= digits { copy[i-digits] } else { 0 };
let new_carry = if shift == 64 { 0 } else { base >> shift };
res[i] = (base << bits) | carry;
carry = new_carry;
}
}
pub(crate) fn shiftr(res: &mut [u64], copy: &[u64], amt: usize) {
let digits = amt / 64;
let bits = amt % 64;
let mut carry = 0;
let mask = !(0xFFFFFFFFFFFFFFFF << bits);
let shift = (64 - bits) as u32;
for (idx, val) in res.iter_mut().enumerate().rev() {
let target = idx + digits;
let base = if target >= copy.len() { 0 } else { copy[target] };
let (new_carry, _) = (base & mask).overflowing_shl(shift);
*val = (base >> bits) | carry;
carry = new_carry;
}
}
macro_rules! shift_impls
{
($name: ident, $size: expr) => {
impl ShlAssign<usize> for $name {
fn shl_assign(&mut self, amt: usize) {
let copy = self.value.clone();
shiftl(&mut self.value, &copy, amt);
}
}
impl Shl<usize> for $name {
type Output = $name;
fn shl(mut self, amt: usize) -> $name {
let copy = self.value.clone();
shiftl(&mut self.value, &copy, amt);
self
}
}
impl<'a> Shl<usize> for &'a $name {
type Output = $name;
fn shl(self, amt: usize) -> $name {
let mut res = $name{ value: self.value.clone() };
shiftl(&mut res.value, &self.value, amt);
res
}
}
impl ShrAssign<usize> for $name {
fn shr_assign(&mut self, amt: usize) {
let copy = self.value.clone();
shiftr(&mut self.value, &copy, amt);
}
}
impl Shr<usize> for $name {
type Output = $name;
fn shr(mut self, amt: usize) -> $name {
let copy = self.value.clone();
shiftr(&mut self.value, &copy, amt);
self
}
}
impl<'a> Shr<usize> for &'a $name {
type Output = $name;
fn shr(self, amt: usize) -> $name {
let mut res = $name{ value: self.value.clone() };
shiftr(&mut res.value, &self.value, amt);
res
}
}
}
}
#[cfg(test)]
macro_rules! generate_shiftl_tests {
($name: ident, $lname: ident) => {
#[test]
fn $lname() {
generate_shiftl_tests!(body $name, $lname);
}
};
(ignore $name: ident, $lname: ident) => {
#[test]
#[ignore]
fn $lname() {
generate_shiftl_tests!(body $name, $lname);
}
};
(body $name: ident, $lname: ident) => {
let fname = build_test_path("shiftl", stringify!($name));
run_test(fname.to_string(), 3, |case| {
let (neg0, abytes) = case.get("a").unwrap();
let (neg1, lbytes) = case.get("l").unwrap();
let (neg2, rbytes) = case.get("r").unwrap();
assert!(!neg0 && !neg1 && !neg2);
let a = $name::from_bytes(abytes);
let l = $name::from_bytes(lbytes);
let r = $name::from_bytes(rbytes);
assert_eq!(r, a << usize::from(l));
});
};
}
#[cfg(test)]
macro_rules! generate_shiftr_tests {
($name: ident, $lname: ident) => {
#[test]
fn $lname() {
generate_shiftr_tests!(body $name, $lname);
}
};
(ignore $name: ident, $lname: ident) => {
#[test]
#[ignore]
fn $lname() {
generate_shiftr_tests!(body $name, $lname);
}
};
(body $name: ident, $lname: ident) => {
let fname = build_test_path("shiftr", stringify!($name));
run_test(fname.to_string(), 3, |case| {
let (neg0, abytes) = case.get("a").unwrap();
let (neg1, lbytes) = case.get("l").unwrap();
let (neg2, rbytes) = case.get("r").unwrap();
assert!(!neg0 && !neg1 && !neg2);
let a = $name::from_bytes(abytes);
let l = $name::from_bytes(lbytes);
let r = $name::from_bytes(rbytes);
assert_eq!(r, a >> usize::from(l));
});
};
}