1#[derive(Debug, Clone, Copy)]
2pub enum IoMode {
3 IoUring,
4 Posix,
5}
6
7#[derive(Debug, Clone)]
8pub struct HalProfile {
9 pub arch: String,
10 pub kernel_release: Option<String>,
11 pub io_mode: IoMode,
12 pub sse42: bool,
13 pub avx2: bool,
14 pub neon: bool,
15}
16
17impl HalProfile {
18 pub fn detect() -> Self {
19 let arch = std::env::consts::ARCH.to_string();
20 let kernel_release = detect_kernel_release();
21
22 #[cfg(target_arch = "x86_64")]
23 let sse42 = std::is_x86_feature_detected!("sse4.2");
24 #[cfg(not(target_arch = "x86_64"))]
25 let sse42 = false;
26
27 #[cfg(target_arch = "x86_64")]
28 let avx2 = std::is_x86_feature_detected!("avx2");
29 #[cfg(not(target_arch = "x86_64"))]
30 let avx2 = false;
31
32 #[cfg(target_arch = "aarch64")]
33 let neon = std::arch::is_aarch64_feature_detected!("neon");
34 #[cfg(not(target_arch = "aarch64"))]
35 let neon = false;
36
37 let io_mode = if supports_io_uring(kernel_release.as_deref()) {
38 IoMode::IoUring
39 } else {
40 IoMode::Posix
41 };
42
43 Self {
44 arch,
45 kernel_release,
46 io_mode,
47 sse42,
48 avx2,
49 neon,
50 }
51 }
52
53 pub fn startup_line(&self) -> String {
54 let io = match self.io_mode {
55 IoMode::IoUring => "io_uring",
56 IoMode::Posix => "epoll/preadv/pwritev fallback",
57 };
58
59 format!(
60 "HAL detected arch={} kernel={:?} io_mode={} sse4.2={} avx2={} neon={}",
61 self.arch, self.kernel_release, io, self.sse42, self.avx2, self.neon
62 )
63 }
64}
65
66fn detect_kernel_release() -> Option<String> {
67 #[cfg(target_os = "linux")]
68 {
69 std::fs::read_to_string("/proc/sys/kernel/osrelease")
70 .ok()
71 .map(|s| s.trim().to_string())
72 }
73
74 #[cfg(not(target_os = "linux"))]
75 {
76 None
77 }
78}
79
80fn supports_io_uring(kernel_release: Option<&str>) -> bool {
81 #[cfg(not(target_os = "linux"))]
82 {
83 let _ = kernel_release;
84 return false;
85 }
86
87 #[cfg(target_os = "linux")]
88 {
89 let Some(release) = kernel_release else {
90 return false;
91 };
92
93 let mut parts = release
94 .split(['.', '-'])
95 .filter_map(|p| p.parse::<u64>().ok());
96
97 let major = parts.next().unwrap_or(0);
98 let minor = parts.next().unwrap_or(0);
99
100 major > 5 || (major == 5 && minor >= 1)
101 }
102}
103
104#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
105#[multiversion::multiversion(targets("x86_64+avx2", "x86_64+sse4.2", "aarch64+neon"))]
106pub fn simd_accumulate(bytes: &[u8]) -> u64 {
107 bytes.iter().map(|b| u64::from(*b)).sum()
108}
109
110#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
111pub fn simd_accumulate(bytes: &[u8]) -> u64 {
112 bytes.iter().map(|b| u64::from(*b)).sum()
113}