1#![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
6mod 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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
44pub enum SimdLevel {
45 #[default]
47 None,
48 Sse42,
50 Avx2,
52 Avx512,
54 Neon,
56 Sve,
58}
59
60impl SimdLevel {
61 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 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, }
83 }
84
85 pub fn supports_width(self, bytes: usize) -> bool {
87 bytes <= self.vector_width()
88 }
89}
90
91pub 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 return SimdLevel::Neon;
110 }
111
112 SimdLevel::None
113}
114
115static SIMD_LEVEL: std::sync::OnceLock<SimdLevel> = std::sync::OnceLock::new();
117
118pub fn simd_level() -> SimdLevel {
120 *SIMD_LEVEL.get_or_init(detect_simd)
121}
122
123#[inline]
125pub fn has_avx2() -> bool {
126 simd_level() >= SimdLevel::Avx2
127}
128
129#[inline]
131pub fn has_avx512() -> bool {
132 simd_level() >= SimdLevel::Avx512
133}
134
135#[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 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}