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}