scrypt_opt/
features.rs

1/// A feature that can be checked at runtime.
2pub trait Feature {
3    /// The name of the feature.
4    fn name(&self) -> &'static str;
5
6    /// Whether the feature is required for the crate to work.
7    fn required(&self) -> bool;
8
9    /// The length of the vector in bytes.
10    fn vector_length(&self) -> usize;
11
12    /// Checks if the feature is supported.
13    fn check(&self) -> bool {
14        if self.required() {
15            return true;
16        }
17
18        self.check_volatile()
19    }
20
21    /// Checks if the feature is supported at runtime.
22    fn check_volatile(&self) -> bool;
23}
24
25/// Iterates over all features.
26#[cfg_attr(not(target_arch = "x86_64"), expect(unused))]
27pub fn iterate<F: FnMut(&dyn Feature)>(mut f: F) {
28    #[cfg(target_arch = "x86_64")]
29    {
30        f(&Sha);
31        f(&Avx2);
32        f(&Avx512F);
33        f(&Avx512VL);
34    }
35}
36
37macro_rules! define_x86_feature {
38    ($name:ident, $cpuid_name:ident, $feature:tt) => {
39        #[cfg(target_arch = "x86_64")]
40        cpufeatures::new!($cpuid_name, $feature);
41
42        #[cfg(target_arch = "x86_64")]
43        #[derive(Default)]
44        #[doc = concat!("X86 ", stringify!($feature), " feature.")]
45        pub struct $name;
46
47        #[cfg(target_arch = "x86_64")]
48        impl Feature for $name {
49            fn name(&self) -> &'static str {
50                stringify!($feature)
51            }
52
53            fn required(&self) -> bool {
54                cfg!(target_feature = $feature)
55            }
56
57            fn vector_length(&self) -> usize {
58                32
59            }
60
61            fn check_volatile(&self) -> bool {
62                $cpuid_name::get()
63            }
64        }
65    };
66}
67
68define_x86_feature!(Sha, cpuid_sha, "sha");
69define_x86_feature!(Avx2, cpuid_avx2, "avx2");
70define_x86_feature!(Avx512F, cpuid_avx512f, "avx512f");
71define_x86_feature!(Avx512VL, cpuid_avx512vl, "avx512vl");
72
73#[cfg(test)]
74mod tests {
75    #[cfg_attr(not(target_arch = "x86_64"), expect(unused_imports))]
76    use super::*;
77
78    macro_rules! write_test {
79        ($test_name:ident, $name:tt, $checker:expr) => {
80            #[test]
81            fn $test_name() {
82                if !cfg!(target_feature = $name) {
83                    assert_eq!($checker.check(), std::arch::is_x86_feature_detected!($name));
84                } else {
85                    assert!($checker.check_volatile());
86                    assert!($checker.check());
87                }
88            }
89        };
90    }
91
92    write_test!(test_sha, "sha", Sha);
93    write_test!(test_avx2, "avx2", Avx2);
94    write_test!(test_avx512f, "avx512f", Avx512F);
95    write_test!(test_avx512vl, "avx512vl", Avx512VL);
96}