Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/escape-simd-0.1.0/src/simd/bits.rs
Line
Count
Source
1
use super::traits::BitMask;
2
3
macro_rules! impl_bits {
4
    () => {};
5
    ($($ty:ty)*) => {
6
        $(
7
            impl BitMask for $ty {
8
                const LEN: usize = std::mem::size_of::<$ty>() * 8;
9
10
                #[inline]
11
0
                fn first_offset(&self) -> usize {
12
0
                    self.as_little_endian().trailing_zeros() as usize
13
0
                }
Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::first_offset
Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::first_offset
Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::first_offset
14
15
                #[inline]
16
0
                fn as_little_endian(&self) -> Self {
17
                    // The software bitmask already uses a canonical layout
18
                    // (lane `i` -> bit `i`), so there is no byte order to swap.
19
0
                    self.clone()
20
0
                }
Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::as_little_endian
Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::as_little_endian
Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::as_little_endian
21
22
                #[inline]
23
0
                fn all_zero(&self) -> bool {
24
0
                    *self == 0
25
0
                }
Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::all_zero
Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::all_zero
Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::all_zero
26
27
                #[inline]
28
0
                fn clear_high_bits(&self, n: usize) -> Self {
29
0
                    debug_assert!(n <= Self::LEN);
30
0
                    *self & ((u64::MAX as $ty) >> n)
31
0
                }
Unexecuted instantiation: <u16 as escape_simd::simd::traits::BitMask>::clear_high_bits
Unexecuted instantiation: <u32 as escape_simd::simd::traits::BitMask>::clear_high_bits
Unexecuted instantiation: <u64 as escape_simd::simd::traits::BitMask>::clear_high_bits
32
            }
33
        )*
34
    };
35
}
36
37
impl_bits!(u16 u32 u64);
38
39
#[cfg(target_arch = "aarch64")]
40
/// Use u64 representation the bitmask of Neon vector.
41
///         (low)
42
/// Vector: 00-ff-ff-ff-ff-00-00-00
43
/// Mask  : 0000-1111-1111-1111-1111-0000-0000-0000
44
///
45
/// first_offset() = 1
46
/// clear_high_bits(4) = Mask(0000-1111-1111-1111-[0000]-0000-0000-0000)
47
///
48
/// reference: https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
49
pub struct NeonBits(u64);
50
51
#[cfg(target_arch = "aarch64")]
52
impl NeonBits {
53
    /// Wraps the raw `u64` produced by the NEON `vshrn` bitmask extraction,
54
    /// normalizing it to the canonical `lane i -> nibble i` layout that
55
    /// `first_offset`, `clear_high_bits` and `all_zero` assume.
56
    ///
57
    /// On little-endian the extraction is already canonical. On big-endian the
58
    /// `vreinterpretq_u16_u8` + `vshrn_n_u16` step packs each lane pair with its
59
    /// two nibbles (and the byte pairs) reversed relative to lane order, so the
60
    /// whole nibble sequence ends up reversed. Reverse the 16 nibbles to restore
61
    /// `lane i -> nibble i` (a plain `swap_bytes` only fixes the byte order, not
62
    /// the nibble order within each byte).
63
    #[inline]
64
    pub fn new(u: u64) -> Self {
65
        #[cfg(target_endian = "little")]
66
        {
67
            Self(u)
68
        }
69
        #[cfg(target_endian = "big")]
70
        {
71
            let b = u.swap_bytes();
72
            Self(((b & 0x0f0f_0f0f_0f0f_0f0f) << 4) | ((b & 0xf0f0_f0f0_f0f0_f0f0) >> 4))
73
        }
74
    }
75
}
76
77
#[cfg(target_arch = "aarch64")]
78
impl BitMask for NeonBits {
79
    const LEN: usize = 16;
80
81
    #[inline]
82
    fn first_offset(&self) -> usize {
83
        (self.as_little_endian().0.trailing_zeros() as usize) >> 2
84
    }
85
86
    #[inline]
87
    fn as_little_endian(&self) -> Self {
88
        // `new` already normalized the bits to the canonical `lane i -> nibble i`
89
        // layout on every target, so there is no byte order left to swap.
90
        Self(self.0)
91
    }
92
93
    #[inline]
94
    fn all_zero(&self) -> bool {
95
        self.0 == 0
96
    }
97
98
    #[inline]
99
    fn clear_high_bits(&self, n: usize) -> Self {
100
        debug_assert!(n <= Self::LEN);
101
        Self(self.0 & u64::MAX >> (n * 4))
102
    }
103
}