#![feature(link_llvm_intrinsics)]
#![cfg_attr(not(test), no_std)]
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
#[cfg(feature = "llvm")]
pub mod llvm;
#[inline]
pub fn clmul(a: u64, b: u64) -> u128 {
#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
target_feature = "aes"
))]
return clmul_aarch64_neon(a, b);
#[cfg(target_arch = "x86_64")]
{
if core_detect::is_x86_feature_detected!("pclmulqdq") {
return unsafe { clmul_intel(a, b) };
}
}
#[allow(unreachable_code)]
return clmul_nosimd(a, b);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "pclmulqdq")]
#[inline]
fn clmul_intel(a: u64, b: u64) -> u128 {
use core::arch::x86_64::*;
unsafe {
core::mem::transmute(_mm_clmulepi64_si128(
_mm_cvtsi64_si128(a as i64),
_mm_cvtsi64_si128(b as i64),
0,
))
}
}
#[cfg(all(
target_arch = "aarch64",
target_feature = "neon",
target_feature = "aes"
))]
#[inline]
fn clmul_aarch64_neon(a: u64, b: u64) -> u128 {
unsafe { core::arch::aarch64::vmull_p64(a, b) }
}
#[inline]
#[allow(dead_code)]
fn clmul_nosimd(a: u64, b: u64) -> u128 {
let mut tmp: u128 = b as u128;
let mut result: u128 = 0;
for i in 0..64 {
if a & (1 << i) != 0 {
result ^= tmp;
}
tmp <<= 1;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clmul_u8() {
let result = clmul(2, 2);
assert_eq!(result, 4);
}
#[test]
fn test_clmul_u32() {
let result = clmul(u32::MAX as u64, u32::MAX as u64);
assert_eq!(result, 6148914691236517205);
}
#[test]
fn test_clmul_u64() {
let result = clmul(u64::MAX as u64, u64::MAX as u64);
assert_eq!(result, 113427455640312821154458202477256070485);
}
}