Skip to main content

hamming_bitwise_fast/
slice.rs

1//! Variable-length slice APIs for bitwise Hamming distance.
2//!
3//! Use this module when vector sizes are determined at runtime. When the size
4//! is known at compile time, prefer the [`array`](crate::array) module for
5//! better performance (see [Choosing an API](crate#choosing-an-api)).
6
7// ============================================================================
8// Public API
9// ============================================================================
10
11/// Compute the bitwise Hamming distance between two byte slices.
12///
13/// # Panics
14///
15/// Panics if the slices have different lengths.
16///
17/// # Example
18///
19/// ```
20/// use hamming_bitwise_fast::slice;
21///
22/// let a = vec![0xFFu8; 128];
23/// let b = vec![0x00u8; 128];
24/// let distance = slice::distance(&a, &b);  // 1024
25/// ```
26#[cfg_attr(
27    all(
28        feature = "multiversion_x86",
29        any(target_arch = "x86", target_arch = "x86_64")
30    ),
31    multiversion::multiversion(targets(
32        "x86_64+avx512vpopcntdq+avx512vl+popcnt",
33        "x86_64+avx512bw+avx512vl+popcnt",
34        "x86_64+avx2+popcnt",
35        "x86_64+sse4.2+popcnt",
36        "x86+avx2+popcnt",
37        "x86+sse4.2+popcnt",
38    ))
39)]
40#[inline(always)]
41pub fn distance(a: &[u8], b: &[u8]) -> u32 {
42    assert_eq!(a.len(), b.len());
43    crate::distance_impl(a, b)
44}
45
46/// Compute Hamming distance from one source slice to many target slices
47/// (one-to-many).
48///
49/// Faster than calling [`distance`] in a loop for one-to-many comparisons.
50///
51/// # Panics
52///
53/// Panics if `out.len() != targets.len()` or any target has a different
54/// length than `source`.
55///
56/// # Example
57///
58/// ```
59/// use hamming_bitwise_fast::slice;
60///
61/// let source = vec![0u8; 128];
62/// let targets_owned: Vec<Vec<u8>> = vec![vec![1u8; 128], vec![2u8; 128]];
63/// let targets: Vec<&[u8]> = targets_owned.iter().map(|v| v.as_slice()).collect();
64/// let mut distances = vec![0u32; 2];  // pre-allocate and reuse
65///
66/// slice::batch(&source, &targets, &mut distances);
67/// ```
68#[cfg_attr(
69    all(
70        feature = "multiversion_x86",
71        any(target_arch = "x86", target_arch = "x86_64")
72    ),
73    multiversion::multiversion(targets(
74        "x86_64+avx512vpopcntdq+avx512vl+popcnt",
75        "x86_64+avx512bw+avx512vl+popcnt",
76        "x86_64+avx2+popcnt",
77        "x86_64+sse4.2+popcnt",
78        "x86+avx2+popcnt",
79        "x86+sse4.2+popcnt",
80    ))
81)]
82#[inline(always)]
83pub fn batch(source: &[u8], targets: &[&[u8]], out: &mut [u32]) {
84    assert_eq!(targets.len(), out.len());
85
86    // For slices, the data layout (&[&[u8]]) isn't contiguous, so gather
87    // instructions aren't a concern.
88    for (target, dist) in targets.iter().zip(out.iter_mut()) {
89        assert_eq!(source.len(), target.len());
90        *dist = crate::distance_impl(source, target);
91    }
92}