Skip to main content

haagenti_simd/
lib.rs

1// Test modules have minor lints that don't affect production code
2#![cfg_attr(test, allow(clippy::useless_vec))]
3#![cfg_attr(test, allow(clippy::len_zero))]
4#![cfg_attr(test, allow(clippy::needless_range_loop))]
5
6//! # Haagenti SIMD
7//!
8//! SIMD-accelerated primitives for compression algorithms.
9//!
10//! Provides optimized implementations of common compression operations
11//! using platform-specific SIMD instructions.
12//!
13//! ## Supported Architectures
14//!
15//! - **x86_64**: SSE4.2, AVX2, AVX-512
16//! - **aarch64**: NEON, SVE
17//! - **Fallback**: Scalar implementation
18//!
19//! ## Example
20//!
21//! ```ignore
22//! use haagenti_simd::{detect_simd, SimdLevel};
23//!
24//! match detect_simd() {
25//!     SimdLevel::Avx512 => println!("Using AVX-512"),
26//!     SimdLevel::Avx2 => println!("Using AVX2"),
27//!     SimdLevel::Neon => println!("Using NEON"),
28//!     SimdLevel::None => println!("Scalar fallback"),
29//! }
30//! ```
31
32mod hash;
33mod histogram;
34mod match_finder;
35mod memops;
36
37pub use hash::{hash4_scalar, hash4x4, hash4x8};
38pub use histogram::{byte_histogram, byte_histogram_simd};
39pub use match_finder::{find_match_length, find_match_length_safe};
40pub use memops::{copy_match, copy_within_extend, fill_repeat};
41
42/// SIMD feature level.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
44pub enum SimdLevel {
45    /// No SIMD available.
46    #[default]
47    None,
48    /// SSE4.2 (x86_64).
49    Sse42,
50    /// AVX2 (x86_64).
51    Avx2,
52    /// AVX-512 (x86_64).
53    Avx512,
54    /// NEON (aarch64).
55    Neon,
56    /// SVE (aarch64).
57    Sve,
58}
59
60impl SimdLevel {
61    /// Get string representation.
62    pub fn as_str(self) -> &'static str {
63        match self {
64            SimdLevel::None => "none",
65            SimdLevel::Sse42 => "sse4.2",
66            SimdLevel::Avx2 => "avx2",
67            SimdLevel::Avx512 => "avx512",
68            SimdLevel::Neon => "neon",
69            SimdLevel::Sve => "sve",
70        }
71    }
72
73    /// Get vector width in bytes.
74    pub fn vector_width(self) -> usize {
75        match self {
76            SimdLevel::None => 1,
77            SimdLevel::Sse42 => 16,
78            SimdLevel::Avx2 => 32,
79            SimdLevel::Avx512 => 64,
80            SimdLevel::Neon => 16,
81            SimdLevel::Sve => 64, // Variable, assume max
82        }
83    }
84
85    /// Check if this level supports the given operation width efficiently.
86    pub fn supports_width(self, bytes: usize) -> bool {
87        bytes <= self.vector_width()
88    }
89}
90
91/// Detect available SIMD level at runtime.
92pub fn detect_simd() -> SimdLevel {
93    #[cfg(target_arch = "x86_64")]
94    {
95        if is_x86_feature_detected!("avx512f") {
96            return SimdLevel::Avx512;
97        }
98        if is_x86_feature_detected!("avx2") {
99            return SimdLevel::Avx2;
100        }
101        if is_x86_feature_detected!("sse4.2") {
102            return SimdLevel::Sse42;
103        }
104    }
105
106    #[cfg(target_arch = "aarch64")]
107    {
108        // NEON is always available on aarch64
109        return SimdLevel::Neon;
110    }
111
112    SimdLevel::None
113}
114
115/// Global SIMD level (cached on first call).
116static SIMD_LEVEL: std::sync::OnceLock<SimdLevel> = std::sync::OnceLock::new();
117
118/// Get the current SIMD level (cached).
119pub fn simd_level() -> SimdLevel {
120    *SIMD_LEVEL.get_or_init(detect_simd)
121}
122
123/// Check if AVX2 is available.
124#[inline]
125pub fn has_avx2() -> bool {
126    simd_level() >= SimdLevel::Avx2
127}
128
129/// Check if AVX-512 is available.
130#[inline]
131pub fn has_avx512() -> bool {
132    simd_level() >= SimdLevel::Avx512
133}
134
135/// Check if NEON is available.
136#[inline]
137pub fn has_neon() -> bool {
138    simd_level() == SimdLevel::Neon || simd_level() == SimdLevel::Sve
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn test_detect_simd() {
147        let level = detect_simd();
148        println!("Detected SIMD level: {:?} ({})", level, level.as_str());
149
150        // Should always get some result
151        assert!(level.as_str().len() > 0);
152    }
153
154    #[test]
155    fn test_simd_level_ordering() {
156        assert!(SimdLevel::None < SimdLevel::Sse42);
157        assert!(SimdLevel::Sse42 < SimdLevel::Avx2);
158        assert!(SimdLevel::Avx2 < SimdLevel::Avx512);
159    }
160
161    #[test]
162    fn test_vector_width() {
163        assert_eq!(SimdLevel::None.vector_width(), 1);
164        assert_eq!(SimdLevel::Sse42.vector_width(), 16);
165        assert_eq!(SimdLevel::Avx2.vector_width(), 32);
166        assert_eq!(SimdLevel::Avx512.vector_width(), 64);
167        assert_eq!(SimdLevel::Neon.vector_width(), 16);
168    }
169
170    #[test]
171    fn test_cached_simd_level() {
172        let level1 = simd_level();
173        let level2 = simd_level();
174        assert_eq!(level1, level2);
175    }
176}