eight_bytes/
mask8x8.rs

1use crate::{ALL_ONES, u8x8};
2
3/// A vector of eight `bool` values, which can have SIMD-like operations applied
4/// to them without any explicit SIMD instructions.
5///
6/// This type is really just a [`u64`], but its methods interpret it as eight
7/// [`bool`] values where the same operation is applied to all eight values at
8/// once.
9#[allow(non_camel_case_types)]
10#[repr(transparent)]
11#[derive(Clone, Copy, PartialEq, Eq)]
12pub struct mask8x8 {
13    pub(crate) n: u64,
14}
15
16impl mask8x8 {
17    /// A [`mask8x8`] value where all eight elements are set to `false`.
18    pub const ALL_FALSE: Self = Self::new(0);
19
20    /// A [`mask8x8`] value where all eight elements are set to `true`.
21    pub const ALL_TRUE: Self = Self::new(ALL_ONES);
22
23    /// Converts the given array into a [`mask8x8`].
24    #[inline(always)]
25    pub const fn from_array(a: [bool; 8]) -> Self {
26        // Safety: The Rust spec guarantees that `bool` is has size 1 and
27        // alignment 1, and that true is 0x01 while false is 0x00. The
28        // layout matches u8 and both bit patterns are valid for u8.
29        let a: [u8; 8] = unsafe { core::mem::transmute(a) };
30        Self {
31            n: u64::from_ne_bytes(a),
32        }
33    }
34
35    #[inline(always)]
36    const fn from_bitmask_raw(mask: u8) -> u64 {
37        let raw = mask as u64;
38        (((raw & 0x55) * 0x02040810204081) | ((raw & 0xaa) * 0x02040810204081)) & ALL_ONES
39    }
40
41    /// Converts the given bitmask into a [`mask8x8`] by treating a set bit
42    /// as `true` and an unset bit as `false`. The least significant bit
43    /// appears in the first element.
44    ///
45    /// ```rust
46    /// # use eight_bytes::{mask8x8};
47    /// let mask = mask8x8::from_bitmask_le(0b11001010);
48    /// assert_eq!(mask.to_array(), [false, true, false, true, false, false, true, true]);
49    /// ```
50    #[inline(always)]
51    pub const fn from_bitmask_le(mask: u8) -> Self {
52        let raw = Self::from_bitmask_raw(mask).to_le();
53        Self::new(raw)
54    }
55
56    /// Converts the given bitmask into a [`mask8x8`] by treating a set bit
57    /// as `true` and an unset bit as `false`. The least significant bit
58    /// appears in the last element.
59    ///
60    /// ```rust
61    /// # use eight_bytes::{mask8x8};
62    /// let mask = mask8x8::from_bitmask_be(0b11001010);
63    /// assert_eq!(mask.to_array(), [true, true, false, false, true, false, true, false]);
64    /// ```
65    #[inline(always)]
66    pub const fn from_bitmask_be(mask: u8) -> Self {
67        let raw = Self::from_bitmask_raw(mask).to_be();
68        Self::new(raw)
69    }
70
71    #[inline(always)]
72    pub(crate) const fn new(n: u64) -> Self {
73        Self { n }
74    }
75
76    /// Converts the vector into an array of eight `bool` values.
77    #[inline(always)]
78    pub const fn to_array(self) -> [bool; 8] {
79        // Safety: The Rust spec guarantees that `bool` is has size 1 and
80        // alignment 1, and that true is 0x01 while false is 0x00. The
81        // layout matches u8 and both bit patterns are valid for u8.
82        let u8s = self.n.to_ne_bytes();
83        unsafe { core::mem::transmute(u8s) }
84    }
85
86    #[inline(always)]
87    const fn to_bitmask_raw(raw: u64) -> u8 {
88        const MASK: u64 = 0x0102040810204080;
89        (raw.wrapping_mul(MASK) >> 56) as u8
90    }
91
92    /// Converts the vector into a bitmask where the first element is in
93    /// the least significant bit.
94    #[inline(always)]
95    pub const fn to_bitmask_le(self) -> u8 {
96        Self::to_bitmask_raw(self.n.to_le())
97    }
98
99    /// Converts the vector into a bitmask where the first element is in
100    /// the most significant bit.
101    #[inline(always)]
102    pub const fn to_bitmask_be(self) -> u8 {
103        Self::to_bitmask_raw(self.n.to_be())
104    }
105
106    /// Returns a [`u8x8`] representation of the mask where true elements
107    /// are represented as `0x01` and false elements are represented as `0x00`.
108    #[inline(always)]
109    pub fn to_u8x8(self) -> u8x8 {
110        u8x8::new(self.n)
111    }
112
113    /// Computes the complement of each element in the vector.
114    #[inline(always)]
115    pub const fn not(self) -> Self {
116        Self::new(self.n ^ 0x0101010101010101)
117    }
118
119    /// Computes a logical OR result for each element across both vectors.
120    #[inline(always)]
121    pub const fn or(self, other: Self) -> Self {
122        Self::new(self.n | other.n)
123    }
124
125    /// Computes a logical AND result for each element across both vectors.
126    #[inline(always)]
127    pub const fn and(self, other: Self) -> Self {
128        Self::new(self.n & other.n)
129    }
130
131    /// Builds a [`u8x8`] by selecting one of the two given values for each
132    /// element corresponding to the elements in the mask.
133    ///
134    /// For example, this can be useful when expanding a one-bit-per-pixel
135    /// bitmap into eight palette indices represented as `u8`, as part of
136    /// rendering an indexed-color pixmap:
137    ///
138    /// ```rust
139    /// # use eight_bytes::{u8x8, mask8x8};
140    /// let bitmap = 0b10101100;
141    /// let mask = mask8x8::from_bitmask_be(bitmap);
142    /// let fg_color = 0xff;
143    /// let bg_color = 0x01;
144    /// let pixels = mask.select(fg_color, bg_color);
145    /// assert_eq!(pixels.to_array(), [0xff, 0x01, 0xff, 0x01, 0xff, 0xff, 0x01, 0x01]);
146    /// ```
147    #[inline(always)]
148    pub const fn select(self, true_value: u8, false_value: u8) -> u8x8 {
149        let true_value = u8x8::splat(true_value).n;
150        let false_value = u8x8::splat(false_value).n;
151        let mask = self.n * 0xff;
152        u8x8::new((true_value & mask) | (false_value & !mask))
153    }
154
155    /// Returns the number of elements in the mask that are set to `true`.
156    #[inline(always)]
157    pub const fn count_true(self) -> u32 {
158        // This is the usual "tree-based" popcount, but starting partway
159        // though the process because our starting state is that we already
160        // know the popcount for each 8-bit segment, which will either be
161        // zero or one.
162        const ALT_8S: u64 = 0x00ff00ff00ff00ff;
163        const ALT_16S: u64 = 0x0000ffff0000ffff;
164        const ALT_32S: u64 = 0x00000000ffffffff;
165
166        let mut raw = self.n;
167        raw = (raw & ALT_8S) + ((raw >> 8) & ALT_8S);
168        raw = (raw & ALT_16S) + ((raw >> 16) & ALT_16S);
169        raw = (raw & ALT_32S) + ((raw >> 32) & ALT_32S);
170        raw as u32
171    }
172
173    /// Returns the number of elements in the mask that are set to `false`.
174    #[inline(always)]
175    pub const fn count_false(self) -> u32 {
176        8 - self.n.count_ones()
177    }
178}
179
180impl core::fmt::Debug for mask8x8 {
181    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182        write!(f, "mask8x8({:?})", self.to_array())
183    }
184}
185
186impl core::ops::Not for mask8x8 {
187    type Output = Self;
188
189    /// Implements the unary `!` operator using [`Self::not`].
190    #[inline(always)]
191    fn not(self) -> Self {
192        self.not()
193    }
194}
195
196impl core::ops::BitOr for mask8x8 {
197    type Output = Self;
198
199    /// Implements the `|` operator using [`Self::or`].
200    #[inline(always)]
201    fn bitor(self, rhs: Self) -> Self {
202        self.or(rhs)
203    }
204}
205
206impl core::ops::BitOrAssign for mask8x8 {
207    /// Implements the `|=` operator using [`Self::or`].
208    #[inline(always)]
209    fn bitor_assign(&mut self, rhs: Self) {
210        *self = self.or(rhs);
211    }
212}
213
214impl core::ops::BitAnd for mask8x8 {
215    type Output = Self;
216
217    /// Implements the `&` operator using [`Self::and`].
218    #[inline(always)]
219    fn bitand(self, rhs: Self) -> Self {
220        self.and(rhs)
221    }
222}
223
224impl core::ops::BitAndAssign for mask8x8 {
225    /// Implements the `&=` operator using [`Self::and`].
226    #[inline(always)]
227    fn bitand_assign(&mut self, rhs: Self) {
228        *self = self.and(rhs);
229    }
230}