#![cfg_attr(test, allow(clippy::useless_vec))]
#![cfg_attr(test, allow(clippy::len_zero))]
#![cfg_attr(test, allow(clippy::needless_range_loop))]
mod hash;
mod histogram;
mod match_finder;
mod memops;
pub use hash::{hash4_scalar, hash4x4, hash4x8};
pub use histogram::{byte_histogram, byte_histogram_simd};
pub use match_finder::{find_match_length, find_match_length_safe};
pub use memops::{copy_match, copy_within_extend, fill_repeat};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum SimdLevel {
#[default]
None,
Sse42,
Avx2,
Avx512,
Neon,
Sve,
}
impl SimdLevel {
pub fn as_str(self) -> &'static str {
match self {
SimdLevel::None => "none",
SimdLevel::Sse42 => "sse4.2",
SimdLevel::Avx2 => "avx2",
SimdLevel::Avx512 => "avx512",
SimdLevel::Neon => "neon",
SimdLevel::Sve => "sve",
}
}
pub fn vector_width(self) -> usize {
match self {
SimdLevel::None => 1,
SimdLevel::Sse42 => 16,
SimdLevel::Avx2 => 32,
SimdLevel::Avx512 => 64,
SimdLevel::Neon => 16,
SimdLevel::Sve => 64, }
}
pub fn supports_width(self, bytes: usize) -> bool {
bytes <= self.vector_width()
}
}
pub fn detect_simd() -> SimdLevel {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx512f") {
return SimdLevel::Avx512;
}
if is_x86_feature_detected!("avx2") {
return SimdLevel::Avx2;
}
if is_x86_feature_detected!("sse4.2") {
return SimdLevel::Sse42;
}
}
#[cfg(target_arch = "aarch64")]
{
return SimdLevel::Neon;
}
SimdLevel::None
}
static SIMD_LEVEL: std::sync::OnceLock<SimdLevel> = std::sync::OnceLock::new();
pub fn simd_level() -> SimdLevel {
*SIMD_LEVEL.get_or_init(detect_simd)
}
#[inline]
pub fn has_avx2() -> bool {
simd_level() >= SimdLevel::Avx2
}
#[inline]
pub fn has_avx512() -> bool {
simd_level() >= SimdLevel::Avx512
}
#[inline]
pub fn has_neon() -> bool {
simd_level() == SimdLevel::Neon || simd_level() == SimdLevel::Sve
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detect_simd() {
let level = detect_simd();
println!("Detected SIMD level: {:?} ({})", level, level.as_str());
assert!(level.as_str().len() > 0);
}
#[test]
fn test_simd_level_ordering() {
assert!(SimdLevel::None < SimdLevel::Sse42);
assert!(SimdLevel::Sse42 < SimdLevel::Avx2);
assert!(SimdLevel::Avx2 < SimdLevel::Avx512);
}
#[test]
fn test_vector_width() {
assert_eq!(SimdLevel::None.vector_width(), 1);
assert_eq!(SimdLevel::Sse42.vector_width(), 16);
assert_eq!(SimdLevel::Avx2.vector_width(), 32);
assert_eq!(SimdLevel::Avx512.vector_width(), 64);
assert_eq!(SimdLevel::Neon.vector_width(), 16);
}
#[test]
fn test_cached_simd_level() {
let level1 = simd_level();
let level2 = simd_level();
assert_eq!(level1, level2);
}
}