Skip to main content

blvm_primitives/crypto/
hash_compare.rs

1//! SIMD-optimized hash comparison utilities
2//!
3//! Provides fast hash comparison using AVX2 SIMD instructions when available,
4//! with automatic fallback to sequential comparison for compatibility.
5
6use crate::types::Hash;
7/// Compare two 32-byte hashes for equality
8///
9/// Uses SIMD (AVX2) when available for faster comparison, otherwise falls back
10/// to standard byte-by-byte comparison.
11///
12/// # Arguments
13/// * `hash1` - First hash to compare
14/// * `hash2` - Second hash to compare
15///
16/// # Returns
17/// `true` if hashes are equal, `false` otherwise
18#[inline]
19pub fn hash_eq(hash1: &Hash, hash2: &Hash) -> bool {
20    #[cfg(all(target_arch = "x86_64", feature = "production"))]
21    {
22        // Try AVX2 SIMD comparison first
23        if is_avx2_available() {
24            unsafe { hash_eq_avx2(hash1, hash2) }
25        } else {
26            hash_eq_fallback(hash1, hash2)
27        }
28    }
29
30    #[cfg(not(all(target_arch = "x86_64", feature = "production")))]
31    {
32        hash_eq_fallback(hash1, hash2)
33    }
34}
35
36/// Fallback hash comparison (sequential byte-by-byte)
37#[inline(always)]
38fn hash_eq_fallback(hash1: &Hash, hash2: &Hash) -> bool {
39    hash1 == hash2
40}
41
42/// AVX2-optimized hash comparison
43///
44/// Compares 32 bytes in parallel using a single AVX2 operation.
45#[cfg(all(target_arch = "x86_64", feature = "production"))]
46#[target_feature(enable = "avx2")]
47unsafe fn hash_eq_avx2(hash1: &Hash, hash2: &Hash) -> bool {
48    use std::arch::x86_64::*;
49
50    unsafe {
51        // Load both hashes as 256-bit AVX2 vectors
52        let h1 = _mm256_loadu_si256(hash1.as_ptr() as *const __m256i);
53        let h2 = _mm256_loadu_si256(hash2.as_ptr() as *const __m256i);
54
55        // Compare all 32 bytes in parallel (byte-wise equality)
56        let cmp = _mm256_cmpeq_epi8(h1, h2);
57
58        // Extract comparison mask (1 bit per byte)
59        let mask = _mm256_movemask_epi8(cmp);
60
61        // All 32 bytes equal if mask == -1 (all 32 bits set)
62        mask == -1i32
63    }
64}
65
66/// Check if AVX2 is available at runtime
67#[cfg(all(target_arch = "x86_64", feature = "production"))]
68fn is_avx2_available() -> bool {
69    std::arch::is_x86_feature_detected!("avx2")
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_hash_eq_identical() {
78        let hash1 = [0u8; 32];
79        let hash2 = [0u8; 32];
80        assert!(hash_eq(&hash1, &hash2));
81    }
82
83    #[test]
84    fn test_hash_eq_different() {
85        let hash1 = [0u8; 32];
86        let mut hash2 = [0u8; 32];
87        hash2[0] = 1;
88        assert!(!hash_eq(&hash1, &hash2));
89    }
90
91    #[test]
92    fn test_hash_eq_different_last_byte() {
93        let mut hash1 = [0u8; 32];
94        let mut hash2 = [0u8; 32];
95        hash1[31] = 0;
96        hash2[31] = 1;
97        assert!(!hash_eq(&hash1, &hash2));
98    }
99
100    #[test]
101    fn test_hash_eq_random() {
102        let hash1: Hash = [
103            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
104            25, 26, 27, 28, 29, 30, 31, 32,
105        ];
106        let hash2: Hash = [
107            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
108            25, 26, 27, 28, 29, 30, 31, 33,
109        ];
110
111        // Should match standard comparison
112        assert_eq!(hash_eq(&hash1, &hash1), hash1 == hash1);
113        assert_eq!(hash_eq(&hash1, &hash2), hash1 == hash2);
114    }
115}