#![allow(unsafe_code)]
#[allow(clippy::inline_always)]
#[inline(always)]
#[must_use]
pub fn apply(lhs: u64, rhs: u64) -> (u64, u64) {
const MIX: u64 = 0xbf58_476d_1ce4_e5b9;
const SHIFT2: u64 = 0x94d0_49bb_1331_11eb;
#[cfg(target_arch = "x86_64")]
{
if std::is_x86_feature_detected!("aes") {
const RK_LO: u64 = 0xcbf2_9ce4_8422_2325;
const RK_HI: u64 = 0x1000_0000_01b3_0000;
let lo: u64;
let hi: u64;
unsafe {
use core::arch::asm;
asm!(
"movq xmm0, {lhs}",
"movq xmm1, {rhs}",
"punpcklqdq xmm0, xmm1",
"movq xmm1, {rk_lo}",
"movq xmm2, {rk_hi}",
"punpcklqdq xmm1, xmm2",
"aesenc xmm0, xmm1",
"movq {lo}, xmm0",
"pextrq {hi}, xmm0, 1",
lhs = in(reg) lhs,
rhs = in(reg) rhs,
rk_lo = in(reg) RK_LO,
rk_hi = in(reg) RK_HI,
lo = out(reg) lo,
hi = out(reg) hi,
out("xmm0") _,
out("xmm1") _,
out("xmm2") _,
options(nostack, pure, nomem),
);
}
return (lo, hi);
}
}
#[cfg(target_arch = "aarch64")]
{
#[cfg(target_vendor = "apple")]
let has_aes = true;
#[cfg(not(target_vendor = "apple"))]
let has_aes = std::arch::is_aarch64_feature_detected!("aes");
if has_aes {
const RK_LO: u64 = 0xcbf2_9ce4_8422_2325;
const RK_HI: u64 = 0x1000_0000_01b3_0000;
let lo: u64;
let hi: u64;
unsafe {
use core::arch::asm;
asm!(
"fmov d0, {lhs}",
"ins v0.d[1], {rhs}",
"fmov d1, {rk_lo}",
"ins v1.d[1], {rk_hi}",
"aese v0.16b, v1.16b",
"aesmc v0.16b, v0.16b",
"umov {lo}, v0.d[0]",
"umov {hi}, v0.d[1]",
lhs = in(reg) lhs,
rhs = in(reg) rhs,
rk_lo = in(reg) RK_LO,
rk_hi = in(reg) RK_HI,
lo = out(reg) lo,
hi = out(reg) hi,
out("v0") _,
out("v1") _,
options(nostack, pure, nomem),
);
}
return (lo, hi);
}
}
#[cfg(all(target_arch = "riscv64", target_feature = "zkn"))]
{
const RK_LO: u64 = 0xcbf2_9ce4_8422_2325; const RK_HI: u64 = 0x1000_0000_01b3_0000;
let lo: u64;
let hi: u64;
unsafe {
use core::arch::asm;
asm!(
"aes64esm {lo}, {lhs}, {rhs}",
"aes64esm {hi}, {rhs}, {lhs}",
"xor {lo}, {lo}, {rk_lo}",
"xor {hi}, {hi}, {rk_hi}",
lhs = in(reg) lhs,
rhs = in(reg) rhs,
rk_lo = in(reg) RK_LO,
rk_hi = in(reg) RK_HI,
lo = out(reg) lo,
hi = out(reg) hi,
options(nostack, pure, nomem),
);
}
return (lo, hi);
}
let mut x = lhs.wrapping_mul(MIX) ^ rhs;
let mut y = rhs.wrapping_mul(MIX) ^ lhs;
x ^= x >> 27;
y ^= y >> 27;
x = x.wrapping_mul(SHIFT2);
y = y.wrapping_mul(SHIFT2);
(x ^ (x >> 31), y ^ (y >> 31))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn distinct_inputs_produce_distinct_outputs() {
let h1 = apply(0xdead_beef, 0xcafe_f00d);
let h2 = apply(0xdead_beef, 0xcafe_f00e);
let h3 = apply(0xdead_bef0, 0xcafe_f00d);
assert_ne!(h1, h2);
assert_ne!(h1, h3);
assert_ne!(h2, h3);
}
#[test]
fn function_is_pure() {
let h1 = apply(42, 99);
let h2 = apply(42, 99);
assert_eq!(h1, h2);
}
#[test]
fn zeros_do_not_collapse_to_zero() {
let h = apply(0, 0);
assert_ne!(h, (0, 0));
}
}