Skip to main content

wide/
u8x16_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="sse2")] {
5    #[derive(Default, Clone, Copy, PartialEq, Eq)]
6    #[repr(C, align(16))]
7    pub struct u8x16 { pub(crate) sse: m128i }
8  } else if #[cfg(target_feature="simd128")] {
9    use core::arch::wasm32::*;
10
11    #[derive(Clone, Copy)]
12    #[repr(transparent)]
13    pub struct u8x16 { pub(crate) simd: v128 }
14
15    impl Default for u8x16 {
16      fn default() -> Self {
17        Self::splat(0)
18      }
19    }
20
21    impl PartialEq for u8x16 {
22      fn eq(&self, other: &Self) -> bool {
23        u8x16_all_true(u8x16_eq(self.simd, other.simd))
24      }
25    }
26
27    impl Eq for u8x16 { }
28  } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
29    use core::arch::aarch64::*;
30    #[repr(C)]
31    #[derive(Copy, Clone)]
32    pub struct u8x16 { pub(crate) neon : uint8x16_t }
33
34    impl Default for u8x16 {
35      #[inline]
36      fn default() -> Self {
37        Self::splat(0)
38      }
39    }
40
41    impl PartialEq for u8x16 {
42      #[inline]
43      fn eq(&self, other: &Self) -> bool {
44        unsafe { vminvq_u8(vceqq_u8(self.neon, other.neon))==u8::MAX }
45      }
46    }
47
48    impl Eq for u8x16 { }
49  } else {
50    #[derive(Default, Clone, Copy, PartialEq, Eq)]
51    #[repr(C, align(16))]
52    pub struct u8x16 { pub(crate) arr: [u8;16] }
53  }
54}
55
56int_uint_consts!(u8, 16, u8x16, 128);
57
58unsafe impl Zeroable for u8x16 {}
59unsafe impl Pod for u8x16 {}
60
61impl AlignTo for u8x16 {
62  type Elem = u8;
63}
64
65impl Add for u8x16 {
66  type Output = Self;
67  #[inline]
68  fn add(self, rhs: Self) -> Self::Output {
69    pick! {
70      if #[cfg(target_feature="sse2")] {
71        Self { sse: add_i8_m128i(self.sse, rhs.sse) }
72      } else if #[cfg(target_feature="simd128")] {
73        Self { simd: u8x16_add(self.simd, rhs.simd) }
74      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
75        unsafe { Self { neon: vaddq_u8(self.neon, rhs.neon) } }
76      } else {
77        Self { arr: [
78          self.arr[0].wrapping_add(rhs.arr[0]),
79          self.arr[1].wrapping_add(rhs.arr[1]),
80          self.arr[2].wrapping_add(rhs.arr[2]),
81          self.arr[3].wrapping_add(rhs.arr[3]),
82          self.arr[4].wrapping_add(rhs.arr[4]),
83          self.arr[5].wrapping_add(rhs.arr[5]),
84          self.arr[6].wrapping_add(rhs.arr[6]),
85          self.arr[7].wrapping_add(rhs.arr[7]),
86          self.arr[8].wrapping_add(rhs.arr[8]),
87          self.arr[9].wrapping_add(rhs.arr[9]),
88          self.arr[10].wrapping_add(rhs.arr[10]),
89          self.arr[11].wrapping_add(rhs.arr[11]),
90          self.arr[12].wrapping_add(rhs.arr[12]),
91          self.arr[13].wrapping_add(rhs.arr[13]),
92          self.arr[14].wrapping_add(rhs.arr[14]),
93          self.arr[15].wrapping_add(rhs.arr[15]),
94        ]}
95      }
96    }
97  }
98}
99
100impl Sub for u8x16 {
101  type Output = Self;
102  #[inline]
103  fn sub(self, rhs: Self) -> Self::Output {
104    pick! {
105      if #[cfg(target_feature="sse2")] {
106        Self { sse: sub_i8_m128i(self.sse, rhs.sse) }
107      } else if #[cfg(target_feature="simd128")] {
108        Self { simd: u8x16_sub(self.simd, rhs.simd) }
109      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
110        unsafe {Self { neon: vsubq_u8(self.neon, rhs.neon) }}
111      } else {
112        Self { arr: [
113          self.arr[0].wrapping_sub(rhs.arr[0]),
114          self.arr[1].wrapping_sub(rhs.arr[1]),
115          self.arr[2].wrapping_sub(rhs.arr[2]),
116          self.arr[3].wrapping_sub(rhs.arr[3]),
117          self.arr[4].wrapping_sub(rhs.arr[4]),
118          self.arr[5].wrapping_sub(rhs.arr[5]),
119          self.arr[6].wrapping_sub(rhs.arr[6]),
120          self.arr[7].wrapping_sub(rhs.arr[7]),
121          self.arr[8].wrapping_sub(rhs.arr[8]),
122          self.arr[9].wrapping_sub(rhs.arr[9]),
123          self.arr[10].wrapping_sub(rhs.arr[10]),
124          self.arr[11].wrapping_sub(rhs.arr[11]),
125          self.arr[12].wrapping_sub(rhs.arr[12]),
126          self.arr[13].wrapping_sub(rhs.arr[13]),
127          self.arr[14].wrapping_sub(rhs.arr[14]),
128          self.arr[15].wrapping_sub(rhs.arr[15]),
129        ]}
130      }
131    }
132  }
133}
134
135impl Mul for u8x16 {
136  type Output = Self;
137
138  #[inline]
139  fn mul(self, rhs: Self) -> Self::Output {
140    // For x86 and wasm, this technically can be done explicitly by converting
141    // to `i16` then converting back after multiplication, but that may not
142    // actually be faster than auto-vectorization.
143    pick! {
144      if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
145        unsafe { Self { neon: vmulq_u8(self.neon, rhs.neon) } }
146      } else {
147        let self_array: [u8; 16] = cast(self);
148        let rhs_array: [u8; 16] = cast(rhs);
149
150        Self::new([
151          self_array[0].wrapping_mul(rhs_array[0]),
152          self_array[1].wrapping_mul(rhs_array[1]),
153          self_array[2].wrapping_mul(rhs_array[2]),
154          self_array[3].wrapping_mul(rhs_array[3]),
155          self_array[4].wrapping_mul(rhs_array[4]),
156          self_array[5].wrapping_mul(rhs_array[5]),
157          self_array[6].wrapping_mul(rhs_array[6]),
158          self_array[7].wrapping_mul(rhs_array[7]),
159          self_array[8].wrapping_mul(rhs_array[8]),
160          self_array[9].wrapping_mul(rhs_array[9]),
161          self_array[10].wrapping_mul(rhs_array[10]),
162          self_array[11].wrapping_mul(rhs_array[11]),
163          self_array[12].wrapping_mul(rhs_array[12]),
164          self_array[13].wrapping_mul(rhs_array[13]),
165          self_array[14].wrapping_mul(rhs_array[14]),
166          self_array[15].wrapping_mul(rhs_array[15]),
167        ])
168      }
169    }
170  }
171}
172
173integer_impl_div_rem!(
174  u8,
175  u8x16,
176  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
177);
178
179impl Shl for u8x16 {
180  type Output = Self;
181
182  /// Shifts lanes by the corresponding lane.
183  ///
184  /// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
185  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
186  /// of the type. (same as `wrapping_shl`)
187  #[inline]
188  fn shl(self, rhs: Self) -> Self::Output {
189    // For x86, this technically can be done explicitly by converting
190    // to `u16` or `u32` then converting back after multiplication, but that may
191    // not actually be faster than auto-vectorization.
192    pick! {
193      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
194        unsafe {
195          // Mask `rhs` to 7 to match `wrapping_shl`.
196          let shift_by = vreinterpretq_s8_u8(vandq_u8(rhs.neon, vmovq_n_u8(7)));
197          Self { neon: vshlq_u8(self.neon, shift_by) }
198        }
199      } else {
200        let self_array: [u8; 16] = cast(self);
201        let rhs_array: [u8; 16] = cast(rhs);
202
203        Self::new([
204          self_array[0].wrapping_shl(rhs_array[0] as u32),
205          self_array[1].wrapping_shl(rhs_array[1] as u32),
206          self_array[2].wrapping_shl(rhs_array[2] as u32),
207          self_array[3].wrapping_shl(rhs_array[3] as u32),
208          self_array[4].wrapping_shl(rhs_array[4] as u32),
209          self_array[5].wrapping_shl(rhs_array[5] as u32),
210          self_array[6].wrapping_shl(rhs_array[6] as u32),
211          self_array[7].wrapping_shl(rhs_array[7] as u32),
212          self_array[8].wrapping_shl(rhs_array[8] as u32),
213          self_array[9].wrapping_shl(rhs_array[9] as u32),
214          self_array[10].wrapping_shl(rhs_array[10] as u32),
215          self_array[11].wrapping_shl(rhs_array[11] as u32),
216          self_array[12].wrapping_shl(rhs_array[12] as u32),
217          self_array[13].wrapping_shl(rhs_array[13] as u32),
218          self_array[14].wrapping_shl(rhs_array[14] as u32),
219          self_array[15].wrapping_shl(rhs_array[15] as u32),
220        ])
221      }
222    }
223  }
224}
225
226impl Shr for u8x16 {
227  type Output = Self;
228
229  /// Shifts lanes by the corresponding lane.
230  ///
231  /// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
232  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
233  /// of the type. (same as `wrapping_shr`)
234  #[inline]
235  fn shr(self, rhs: Self) -> Self::Output {
236    // For x86, this technically can be done explicitly by converting
237    // to `u16` or `u32` then converting back after multiplication, but that may
238    // not actually be faster than auto-vectorization.
239    pick! {
240      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
241        unsafe {
242          // Mask `rhs` to 7 to match `wrapping_shr`, and negate it because
243          // there is no shift-right intrinsic.
244          let neg_rhs = vnegq_s8(vreinterpretq_s8_u8(vandq_u8(rhs.neon, vmovq_n_u8(7))));
245          Self { neon: vshlq_u8(self.neon, neg_rhs) }
246        }
247      } else {
248        let self_array: [u8; 16] = cast(self);
249        let rhs_array: [u8; 16] = cast(rhs);
250
251        Self::new([
252          self_array[0].wrapping_shr(rhs_array[0] as u32),
253          self_array[1].wrapping_shr(rhs_array[1] as u32),
254          self_array[2].wrapping_shr(rhs_array[2] as u32),
255          self_array[3].wrapping_shr(rhs_array[3] as u32),
256          self_array[4].wrapping_shr(rhs_array[4] as u32),
257          self_array[5].wrapping_shr(rhs_array[5] as u32),
258          self_array[6].wrapping_shr(rhs_array[6] as u32),
259          self_array[7].wrapping_shr(rhs_array[7] as u32),
260          self_array[8].wrapping_shr(rhs_array[8] as u32),
261          self_array[9].wrapping_shr(rhs_array[9] as u32),
262          self_array[10].wrapping_shr(rhs_array[10] as u32),
263          self_array[11].wrapping_shr(rhs_array[11] as u32),
264          self_array[12].wrapping_shr(rhs_array[12] as u32),
265          self_array[13].wrapping_shr(rhs_array[13] as u32),
266          self_array[14].wrapping_shr(rhs_array[14] as u32),
267          self_array[15].wrapping_shr(rhs_array[15] as u32),
268        ])
269      }
270    }
271  }
272}
273
274impl Add<u8> for u8x16 {
275  type Output = Self;
276  #[inline]
277  fn add(self, rhs: u8) -> Self::Output {
278    self.add(Self::splat(rhs))
279  }
280}
281
282impl Sub<u8> for u8x16 {
283  type Output = Self;
284  #[inline]
285  fn sub(self, rhs: u8) -> Self::Output {
286    self.sub(Self::splat(rhs))
287  }
288}
289
290impl Mul<u8> for u8x16 {
291  type Output = Self;
292
293  #[inline]
294  fn mul(self, rhs: u8) -> Self::Output {
295    self * Self::splat(rhs)
296  }
297}
298
299macro_rules! impl_shl_scalar {
300  ($Rhs:ident) => {
301    impl Shl<$Rhs> for u8x16 {
302      type Output = Self;
303
304      /// Shifts all lanes by a uniform value.
305      ///
306      /// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
307      /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
308      /// of the type. (same as `wrapping_shl`)
309      #[inline]
310      fn shl(self, rhs: $Rhs) -> Self::Output {
311        // For x86, this technically can be done explicitly by converting
312        // to `u16` or `u32` then converting back after multiplication, but that
313        // may not actually be faster than auto-vectorization.
314        pick! {
315          if #[cfg(target_feature="simd128")] {
316            // Mask `rhs` to 7 to match `wrapping_shl`.
317            Self { simd: u8x16_shl(self.simd, rhs as u32 & 7) }
318          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
319            // Mask `rhs` to 7 to match `wrapping_shl`.
320            unsafe { Self { neon: vshlq_u8(self.neon, vmovq_n_s8(rhs as i8 & 7)) } }
321          } else {
322            let self_array = self.to_array();
323            let rhs = rhs as u32;
324
325            cast([
326              self_array[0].wrapping_shl(rhs),
327              self_array[1].wrapping_shl(rhs),
328              self_array[2].wrapping_shl(rhs),
329              self_array[3].wrapping_shl(rhs),
330              self_array[4].wrapping_shl(rhs),
331              self_array[5].wrapping_shl(rhs),
332              self_array[6].wrapping_shl(rhs),
333              self_array[7].wrapping_shl(rhs),
334              self_array[8].wrapping_shl(rhs),
335              self_array[9].wrapping_shl(rhs),
336              self_array[10].wrapping_shl(rhs),
337              self_array[11].wrapping_shl(rhs),
338              self_array[12].wrapping_shl(rhs),
339              self_array[13].wrapping_shl(rhs),
340              self_array[14].wrapping_shl(rhs),
341              self_array[15].wrapping_shl(rhs),
342            ])
343          }
344        }
345      }
346    }
347  };
348}
349impl_shl_scalar!(i8);
350impl_shl_scalar!(u8);
351impl_shl_scalar!(i16);
352impl_shl_scalar!(u16);
353impl_shl_scalar!(i32);
354impl_shl_scalar!(u32);
355impl_shl_scalar!(i64);
356impl_shl_scalar!(u64);
357impl_shl_scalar!(i128);
358impl_shl_scalar!(u128);
359
360macro_rules! impl_shr_scalar {
361  ($Rhs:ident) => {
362    impl Shr<$Rhs> for u8x16 {
363      type Output = Self;
364
365      /// Shifts all lanes by a uniform value.
366      ///
367      /// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
368      /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
369      /// of the type. (same as `wrapping_shr`)
370      #[inline]
371      fn shr(self, rhs: $Rhs) -> Self::Output {
372        // For x86, this technically can be done explicitly by converting
373        // to `u16` or `u32` then converting back after multiplication, but that
374        // may not actually be faster than auto-vectorization.
375        pick! {
376          if #[cfg(target_feature="simd128")] {
377            // Mask `rhs` to 7 to match `wrapping_shr`.
378            Self { simd: u8x16_shr(self.simd, rhs as u32 & 7) }
379          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
380            // Mask `rhs` to 7 to match `wrapping_shr`, and negate it because
381            // there is no shift-right intrinsic.
382            unsafe { Self { neon: vshlq_u8(self.neon, vmovq_n_s8(-(rhs as i8 & 7))) } }
383          } else {
384            let self_array = self.to_array();
385            let rhs = rhs as u32;
386
387            cast([
388              self_array[0].wrapping_shr(rhs),
389              self_array[1].wrapping_shr(rhs),
390              self_array[2].wrapping_shr(rhs),
391              self_array[3].wrapping_shr(rhs),
392              self_array[4].wrapping_shr(rhs),
393              self_array[5].wrapping_shr(rhs),
394              self_array[6].wrapping_shr(rhs),
395              self_array[7].wrapping_shr(rhs),
396              self_array[8].wrapping_shr(rhs),
397              self_array[9].wrapping_shr(rhs),
398              self_array[10].wrapping_shr(rhs),
399              self_array[11].wrapping_shr(rhs),
400              self_array[12].wrapping_shr(rhs),
401              self_array[13].wrapping_shr(rhs),
402              self_array[14].wrapping_shr(rhs),
403              self_array[15].wrapping_shr(rhs),
404            ])
405          }
406        }
407      }
408    }
409  };
410}
411impl_shr_scalar!(i8);
412impl_shr_scalar!(u8);
413impl_shr_scalar!(i16);
414impl_shr_scalar!(u16);
415impl_shr_scalar!(i32);
416impl_shr_scalar!(u32);
417impl_shr_scalar!(i64);
418impl_shr_scalar!(u64);
419impl_shr_scalar!(i128);
420impl_shr_scalar!(u128);
421
422impl Add<u8x16> for u8 {
423  type Output = u8x16;
424  #[inline]
425  fn add(self, rhs: u8x16) -> Self::Output {
426    u8x16::splat(self).add(rhs)
427  }
428}
429
430impl Sub<u8x16> for u8 {
431  type Output = u8x16;
432  #[inline]
433  fn sub(self, rhs: u8x16) -> Self::Output {
434    u8x16::splat(self).sub(rhs)
435  }
436}
437
438impl Mul<u8x16> for u8 {
439  type Output = u8x16;
440
441  #[inline]
442  fn mul(self, rhs: u8x16) -> Self::Output {
443    u8x16::splat(self) * rhs
444  }
445}
446
447impl BitAnd for u8x16 {
448  type Output = Self;
449  #[inline]
450  fn bitand(self, rhs: Self) -> Self::Output {
451    pick! {
452      if #[cfg(target_feature="sse2")] {
453        Self { sse: bitand_m128i(self.sse, rhs.sse) }
454      } else if #[cfg(target_feature="simd128")] {
455        Self { simd: v128_and(self.simd, rhs.simd) }
456      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
457        unsafe {Self { neon: vandq_u8(self.neon, rhs.neon) }}
458      } else {
459        Self { arr: [
460          self.arr[0].bitand(rhs.arr[0]),
461          self.arr[1].bitand(rhs.arr[1]),
462          self.arr[2].bitand(rhs.arr[2]),
463          self.arr[3].bitand(rhs.arr[3]),
464          self.arr[4].bitand(rhs.arr[4]),
465          self.arr[5].bitand(rhs.arr[5]),
466          self.arr[6].bitand(rhs.arr[6]),
467          self.arr[7].bitand(rhs.arr[7]),
468          self.arr[8].bitand(rhs.arr[8]),
469          self.arr[9].bitand(rhs.arr[9]),
470          self.arr[10].bitand(rhs.arr[10]),
471          self.arr[11].bitand(rhs.arr[11]),
472          self.arr[12].bitand(rhs.arr[12]),
473          self.arr[13].bitand(rhs.arr[13]),
474          self.arr[14].bitand(rhs.arr[14]),
475          self.arr[15].bitand(rhs.arr[15]),
476        ]}
477      }
478    }
479  }
480}
481
482impl BitOr for u8x16 {
483  type Output = Self;
484  #[inline]
485  fn bitor(self, rhs: Self) -> Self::Output {
486    pick! {
487      if #[cfg(target_feature="sse2")] {
488        Self { sse: bitor_m128i(self.sse, rhs.sse) }
489      } else if #[cfg(target_feature="simd128")] {
490        Self { simd: v128_or(self.simd, rhs.simd) }
491      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
492        unsafe {Self { neon: vorrq_u8(self.neon, rhs.neon) }}
493      } else {
494        Self { arr: [
495          self.arr[0].bitor(rhs.arr[0]),
496          self.arr[1].bitor(rhs.arr[1]),
497          self.arr[2].bitor(rhs.arr[2]),
498          self.arr[3].bitor(rhs.arr[3]),
499          self.arr[4].bitor(rhs.arr[4]),
500          self.arr[5].bitor(rhs.arr[5]),
501          self.arr[6].bitor(rhs.arr[6]),
502          self.arr[7].bitor(rhs.arr[7]),
503          self.arr[8].bitor(rhs.arr[8]),
504          self.arr[9].bitor(rhs.arr[9]),
505          self.arr[10].bitor(rhs.arr[10]),
506          self.arr[11].bitor(rhs.arr[11]),
507          self.arr[12].bitor(rhs.arr[12]),
508          self.arr[13].bitor(rhs.arr[13]),
509          self.arr[14].bitor(rhs.arr[14]),
510          self.arr[15].bitor(rhs.arr[15]),
511        ]}
512      }
513    }
514  }
515}
516
517impl BitXor for u8x16 {
518  type Output = Self;
519  #[inline]
520  fn bitxor(self, rhs: Self) -> Self::Output {
521    pick! {
522      if #[cfg(target_feature="sse2")] {
523        Self { sse: bitxor_m128i(self.sse, rhs.sse) }
524      } else if #[cfg(target_feature="simd128")] {
525        Self { simd: v128_xor(self.simd, rhs.simd) }
526      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
527        unsafe {Self { neon: veorq_u8(self.neon, rhs.neon) }}
528      } else {
529        Self { arr: [
530          self.arr[0].bitxor(rhs.arr[0]),
531          self.arr[1].bitxor(rhs.arr[1]),
532          self.arr[2].bitxor(rhs.arr[2]),
533          self.arr[3].bitxor(rhs.arr[3]),
534          self.arr[4].bitxor(rhs.arr[4]),
535          self.arr[5].bitxor(rhs.arr[5]),
536          self.arr[6].bitxor(rhs.arr[6]),
537          self.arr[7].bitxor(rhs.arr[7]),
538          self.arr[8].bitxor(rhs.arr[8]),
539          self.arr[9].bitxor(rhs.arr[9]),
540          self.arr[10].bitxor(rhs.arr[10]),
541          self.arr[11].bitxor(rhs.arr[11]),
542          self.arr[12].bitxor(rhs.arr[12]),
543          self.arr[13].bitxor(rhs.arr[13]),
544          self.arr[14].bitxor(rhs.arr[14]),
545          self.arr[15].bitxor(rhs.arr[15]),
546        ]}
547      }
548    }
549  }
550}
551
552#[expect(deprecated)]
553impl CmpEq for u8x16 {
554  type Output = Self;
555  #[inline]
556  fn simd_eq(self, rhs: Self) -> Self::Output {
557    pick! {
558      if #[cfg(target_feature="sse2")] {
559        Self { sse: cmp_eq_mask_i8_m128i(self.sse, rhs.sse) }
560      } else if #[cfg(target_feature="simd128")] {
561        Self { simd: u8x16_eq(self.simd, rhs.simd) }
562      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
563        unsafe {Self { neon: vceqq_u8(self.neon, rhs.neon) }}
564      } else {
565        Self { arr: [
566          if self.arr[0] == rhs.arr[0] { u8::MAX } else { 0 },
567          if self.arr[1] == rhs.arr[1] { u8::MAX } else { 0 },
568          if self.arr[2] == rhs.arr[2] { u8::MAX } else { 0 },
569          if self.arr[3] == rhs.arr[3] { u8::MAX } else { 0 },
570          if self.arr[4] == rhs.arr[4] { u8::MAX } else { 0 },
571          if self.arr[5] == rhs.arr[5] { u8::MAX } else { 0 },
572          if self.arr[6] == rhs.arr[6] { u8::MAX } else { 0 },
573          if self.arr[7] == rhs.arr[7] { u8::MAX } else { 0 },
574          if self.arr[8] == rhs.arr[8] { u8::MAX } else { 0 },
575          if self.arr[9] == rhs.arr[9] { u8::MAX } else { 0 },
576          if self.arr[10] == rhs.arr[10] { u8::MAX } else { 0 },
577          if self.arr[11] == rhs.arr[11] { u8::MAX } else { 0 },
578          if self.arr[12] == rhs.arr[12] { u8::MAX } else { 0 },
579          if self.arr[13] == rhs.arr[13] { u8::MAX } else { 0 },
580          if self.arr[14] == rhs.arr[14] { u8::MAX } else { 0 },
581          if self.arr[15] == rhs.arr[15] { u8::MAX } else { 0 },
582        ]}
583      }
584    }
585  }
586}
587
588#[expect(deprecated)]
589impl CmpNe for u8x16 {
590  type Output = Self;
591  #[inline]
592  fn simd_ne(self, rhs: Self) -> Self::Output {
593    pick! {
594      if #[cfg(target_feature="sse2")] {
595        !self.simd_eq(rhs)
596      } else if #[cfg(target_feature="simd128")] {
597        Self { simd: u8x16_ne(self.simd, rhs.simd) }
598      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
599        !self.simd_eq(rhs)
600      } else {
601        Self { arr: [
602          if self.arr[0] != rhs.arr[0] { u8::MAX } else { 0 },
603          if self.arr[1] != rhs.arr[1] { u8::MAX } else { 0 },
604          if self.arr[2] != rhs.arr[2] { u8::MAX } else { 0 },
605          if self.arr[3] != rhs.arr[3] { u8::MAX } else { 0 },
606          if self.arr[4] != rhs.arr[4] { u8::MAX } else { 0 },
607          if self.arr[5] != rhs.arr[5] { u8::MAX } else { 0 },
608          if self.arr[6] != rhs.arr[6] { u8::MAX } else { 0 },
609          if self.arr[7] != rhs.arr[7] { u8::MAX } else { 0 },
610          if self.arr[8] != rhs.arr[8] { u8::MAX } else { 0 },
611          if self.arr[9] != rhs.arr[9] { u8::MAX } else { 0 },
612          if self.arr[10] != rhs.arr[10] { u8::MAX } else { 0 },
613          if self.arr[11] != rhs.arr[11] { u8::MAX } else { 0 },
614          if self.arr[12] != rhs.arr[12] { u8::MAX } else { 0 },
615          if self.arr[13] != rhs.arr[13] { u8::MAX } else { 0 },
616          if self.arr[14] != rhs.arr[14] { u8::MAX } else { 0 },
617          if self.arr[15] != rhs.arr[15] { u8::MAX } else { 0 },
618        ]}
619      }
620    }
621  }
622}
623
624#[expect(deprecated)]
625impl CmpLt for u8x16 {
626  type Output = Self;
627  #[inline]
628  fn simd_lt(self, rhs: Self) -> Self::Output {
629    pick! {
630      if #[cfg(target_feature="sse2")] {
631        // Convert from u8 to i8.
632        let offset = Self::splat(0x80);
633        let self_i8 = self.bitxor(offset).sse;
634        let rhs_i8 = rhs.bitxor(offset).sse;
635        Self { sse: cmp_lt_mask_i8_m128i(self_i8, rhs_i8) }
636      } else if #[cfg(target_feature="simd128")] {
637        Self { simd: u8x16_lt(self.simd, rhs.simd) }
638      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
639        unsafe {Self { neon: vcltq_u8(self.neon, rhs.neon) }}
640      } else {
641        Self { arr: [
642          if self.arr[0] < rhs.arr[0] { u8::MAX } else { 0 },
643          if self.arr[1] < rhs.arr[1] { u8::MAX } else { 0 },
644          if self.arr[2] < rhs.arr[2] { u8::MAX } else { 0 },
645          if self.arr[3] < rhs.arr[3] { u8::MAX } else { 0 },
646          if self.arr[4] < rhs.arr[4] { u8::MAX } else { 0 },
647          if self.arr[5] < rhs.arr[5] { u8::MAX } else { 0 },
648          if self.arr[6] < rhs.arr[6] { u8::MAX } else { 0 },
649          if self.arr[7] < rhs.arr[7] { u8::MAX } else { 0 },
650          if self.arr[8] < rhs.arr[8] { u8::MAX } else { 0 },
651          if self.arr[9] < rhs.arr[9] { u8::MAX } else { 0 },
652          if self.arr[10] < rhs.arr[10] { u8::MAX } else { 0 },
653          if self.arr[11] < rhs.arr[11] { u8::MAX } else { 0 },
654          if self.arr[12] < rhs.arr[12] { u8::MAX } else { 0 },
655          if self.arr[13] < rhs.arr[13] { u8::MAX } else { 0 },
656          if self.arr[14] < rhs.arr[14] { u8::MAX } else { 0 },
657          if self.arr[15] < rhs.arr[15] { u8::MAX } else { 0 },
658        ]}
659      }
660    }
661  }
662}
663
664#[expect(deprecated)]
665impl CmpLe for u8x16 {
666  type Output = Self;
667  #[inline]
668  fn simd_le(self, rhs: Self) -> Self::Output {
669    pick! {
670      if #[cfg(target_feature="sse2")] {
671        // Convert from u8 to i8.
672        let offset = Self::splat(0x80);
673        let self_i8 = self.bitxor(offset).sse;
674        let rhs_i8 = rhs.bitxor(offset).sse;
675        // a <= b  is equivalent to  !(b < a)  or  !(a > b)
676        let gt_mask = u8x16 { sse: cmp_gt_mask_i8_m128i(self_i8, rhs_i8) };
677        Self { sse: gt_mask.bitxor(u8x16::splat(0xFF)).sse }
678      } else if #[cfg(target_feature="simd128")] {
679        Self { simd: u8x16_le(self.simd, rhs.simd) }
680      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
681        unsafe {Self { neon: vcleq_u8(self.neon, rhs.neon) }}
682      } else {
683        Self { arr: [
684          if self.arr[0] <= rhs.arr[0] { u8::MAX } else { 0 },
685          if self.arr[1] <= rhs.arr[1] { u8::MAX } else { 0 },
686          if self.arr[2] <= rhs.arr[2] { u8::MAX } else { 0 },
687          if self.arr[3] <= rhs.arr[3] { u8::MAX } else { 0 },
688          if self.arr[4] <= rhs.arr[4] { u8::MAX } else { 0 },
689          if self.arr[5] <= rhs.arr[5] { u8::MAX } else { 0 },
690          if self.arr[6] <= rhs.arr[6] { u8::MAX } else { 0 },
691          if self.arr[7] <= rhs.arr[7] { u8::MAX } else { 0 },
692          if self.arr[8] <= rhs.arr[8] { u8::MAX } else { 0 },
693          if self.arr[9] <= rhs.arr[9] { u8::MAX } else { 0 },
694          if self.arr[10] <= rhs.arr[10] { u8::MAX } else { 0 },
695          if self.arr[11] <= rhs.arr[11] { u8::MAX } else { 0 },
696          if self.arr[12] <= rhs.arr[12] { u8::MAX } else { 0 },
697          if self.arr[13] <= rhs.arr[13] { u8::MAX } else { 0 },
698          if self.arr[14] <= rhs.arr[14] { u8::MAX } else { 0 },
699          if self.arr[15] <= rhs.arr[15] { u8::MAX } else { 0 },
700        ]}
701      }
702    }
703  }
704}
705
706#[expect(deprecated)]
707impl CmpGe for u8x16 {
708  type Output = Self;
709  #[inline]
710  fn simd_ge(self, rhs: Self) -> Self::Output {
711    pick! {
712      if #[cfg(target_feature="sse2")] {
713        // Convert from u8 to i8.
714        let offset = Self::splat(0x80);
715        let self_i8 = self.bitxor(offset).sse;
716        let rhs_i8 = rhs.bitxor(offset).sse;
717        // a >= b  is equivalent to  !(b > a)  or  !(a < b)
718        let lt_mask = u8x16 { sse: cmp_lt_mask_i8_m128i(self_i8, rhs_i8) };
719        Self { sse: lt_mask.bitxor(u8x16::splat(0xFF)).sse }
720      } else if #[cfg(target_feature="simd128")] {
721        Self { simd: u8x16_ge(self.simd, rhs.simd) }
722      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
723        unsafe {Self { neon: vcgeq_u8(self.neon, rhs.neon) }}
724      } else {
725        Self { arr: [
726          if self.arr[0] >= rhs.arr[0] { u8::MAX } else { 0 },
727          if self.arr[1] >= rhs.arr[1] { u8::MAX } else { 0 },
728          if self.arr[2] >= rhs.arr[2] { u8::MAX } else { 0 },
729          if self.arr[3] >= rhs.arr[3] { u8::MAX } else { 0 },
730          if self.arr[4] >= rhs.arr[4] { u8::MAX } else { 0 },
731          if self.arr[5] >= rhs.arr[5] { u8::MAX } else { 0 },
732          if self.arr[6] >= rhs.arr[6] { u8::MAX } else { 0 },
733          if self.arr[7] >= rhs.arr[7] { u8::MAX } else { 0 },
734          if self.arr[8] >= rhs.arr[8] { u8::MAX } else { 0 },
735          if self.arr[9] >= rhs.arr[9] { u8::MAX } else { 0 },
736          if self.arr[10] >= rhs.arr[10] { u8::MAX } else { 0 },
737          if self.arr[11] >= rhs.arr[11] { u8::MAX } else { 0 },
738          if self.arr[12] >= rhs.arr[12] { u8::MAX } else { 0 },
739          if self.arr[13] >= rhs.arr[13] { u8::MAX } else { 0 },
740          if self.arr[14] >= rhs.arr[14] { u8::MAX } else { 0 },
741          if self.arr[15] >= rhs.arr[15] { u8::MAX } else { 0 },
742        ]}
743      }
744    }
745  }
746}
747
748#[expect(deprecated)]
749impl CmpGt for u8x16 {
750  type Output = Self;
751  #[inline]
752  fn simd_gt(self, rhs: Self) -> Self::Output {
753    pick! {
754      if #[cfg(target_feature="sse2")] {
755        // Convert from u8 to i8.
756        let offset = Self::splat(0x80);
757        let self_i8 = self.bitxor(offset).sse;
758        let rhs_i8 = rhs.bitxor(offset).sse;
759        Self { sse: cmp_gt_mask_i8_m128i(self_i8, rhs_i8) }
760      } else if #[cfg(target_feature="simd128")] {
761        Self { simd: u8x16_gt(self.simd, rhs.simd) }
762      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
763        unsafe {Self { neon: vcgtq_u8(self.neon, rhs.neon) }}
764      } else {
765        Self { arr: [
766          if self.arr[0] > rhs.arr[0] { u8::MAX } else { 0 },
767          if self.arr[1] > rhs.arr[1] { u8::MAX } else { 0 },
768          if self.arr[2] > rhs.arr[2] { u8::MAX } else { 0 },
769          if self.arr[3] > rhs.arr[3] { u8::MAX } else { 0 },
770          if self.arr[4] > rhs.arr[4] { u8::MAX } else { 0 },
771          if self.arr[5] > rhs.arr[5] { u8::MAX } else { 0 },
772          if self.arr[6] > rhs.arr[6] { u8::MAX } else { 0 },
773          if self.arr[7] > rhs.arr[7] { u8::MAX } else { 0 },
774          if self.arr[8] > rhs.arr[8] { u8::MAX } else { 0 },
775          if self.arr[9] > rhs.arr[9] { u8::MAX } else { 0 },
776          if self.arr[10] > rhs.arr[10] { u8::MAX } else { 0 },
777          if self.arr[11] > rhs.arr[11] { u8::MAX } else { 0 },
778          if self.arr[12] > rhs.arr[12] { u8::MAX } else { 0 },
779          if self.arr[13] > rhs.arr[13] { u8::MAX } else { 0 },
780          if self.arr[14] > rhs.arr[14] { u8::MAX } else { 0 },
781          if self.arr[15] > rhs.arr[15] { u8::MAX } else { 0 },
782        ]}
783      }
784    }
785  }
786}
787
788impl u8x16 {
789  #[inline]
790  #[must_use]
791  pub const fn new(array: [u8; 16]) -> Self {
792    unsafe { core::mem::transmute(array) }
793  }
794
795  simd_comparison_fns!();
796
797  #[inline]
798  #[must_use]
799  pub fn blend(self, t: Self, f: Self) -> Self {
800    pick! {
801      if #[cfg(target_feature="sse4.1")] {
802        Self { sse: blend_varying_i8_m128i(f.sse, t.sse, self.sse) }
803      } else if #[cfg(target_feature="simd128")] {
804        Self { simd: v128_bitselect(t.simd, f.simd, self.simd) }
805      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
806        unsafe {Self { neon: vbslq_u8(self.neon, t.neon, f.neon) }}
807      } else {
808        generic_bit_blend(self, t, f)
809      }
810    }
811  }
812
813  #[inline]
814  #[must_use]
815  pub fn reduce_add(self) -> u8 {
816    cast(i8x16::reduce_add(cast(self)))
817  }
818
819  #[inline]
820  #[must_use]
821  pub fn reduce_max(self) -> u8 {
822    #[allow(dead_code)]
823    const SHUFFLE_1: [i8; 16] =
824      [8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0];
825    #[allow(dead_code)]
826    const SHUFFLE_2: [i8; 16] =
827      [4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0];
828    #[allow(dead_code)]
829    const SHUFFLE_3: [i8; 16] =
830      [2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
831    #[allow(dead_code)]
832    const SHUFFLE_4: [i8; 16] =
833      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
834
835    pick! {
836      if #[cfg(target_feature="ssse3")] {
837        let rhs = shuffle_av_i8z_all_m128i(self.sse, m128i::from(SHUFFLE_1));
838        let max = max_u8_m128i(self.sse, rhs);
839        let rhs = shuffle_av_i8z_all_m128i(max, m128i::from(SHUFFLE_2));
840        let max = max_u8_m128i(max, rhs);
841        let rhs = shuffle_av_i8z_all_m128i(max, m128i::from(SHUFFLE_3));
842        let max = max_u8_m128i(max, rhs);
843        let rhs = shuffle_av_i8z_all_m128i(max, m128i::from(SHUFFLE_4));
844        let max = max_u8_m128i(max, rhs);
845        get_i32_from_m128i_s(max) as u8
846      } else if #[cfg(target_feature="simd128")] {
847        let rhs = u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>(self.simd, self.simd);
848        let max = u8x16_max(self.simd, rhs);
849        let rhs = u8x16_shuffle::<4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0>(max, max);
850        let max = u8x16_max(max, rhs);
851        let rhs = u8x16_shuffle::<2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(max, max);
852        let max = u8x16_max(max, rhs);
853        let rhs = u8x16_shuffle::<1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(max, max);
854        let max = u8x16_max(max, rhs);
855        u8x16_extract_lane::<0>(max)
856      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
857        unsafe {
858          // Use `transmute` instead of `cast` because `uint8x16_t` does not
859          // implement `bytemuck::Pod`.
860          let rhs = vqtbl1q_u8(self.neon, core::mem::transmute(SHUFFLE_1));
861          let max = vmaxq_u8(self.neon, rhs);
862          let rhs = vqtbl1q_u8(max, core::mem::transmute(SHUFFLE_2));
863          let max = vmaxq_u8(max, rhs);
864          let rhs = vqtbl1q_u8(max, core::mem::transmute(SHUFFLE_3));
865          let max = vmaxq_u8(max, rhs);
866          let rhs = vqtbl1q_u8(max, core::mem::transmute(SHUFFLE_4));
867          let max = vmaxq_u8(max, rhs);
868          vgetq_lane_u8(max, 0)
869        }
870      } else {
871        let array: [u8; 16] = cast(self);
872        array.into_iter().reduce(u8::max).unwrap()
873      }
874    }
875  }
876
877  #[inline]
878  #[must_use]
879  pub fn reduce_min(self) -> u8 {
880    #[allow(dead_code)]
881    const SHUFFLE_1: [i8; 16] =
882      [8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0];
883    #[allow(dead_code)]
884    const SHUFFLE_2: [i8; 16] =
885      [4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0];
886    #[allow(dead_code)]
887    const SHUFFLE_3: [i8; 16] =
888      [2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
889    #[allow(dead_code)]
890    const SHUFFLE_4: [i8; 16] =
891      [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
892
893    pick! {
894      if #[cfg(target_feature="ssse3")] {
895        let rhs = shuffle_av_i8z_all_m128i(self.sse, m128i::from(SHUFFLE_1));
896        let min = min_u8_m128i(self.sse, rhs);
897        let rhs = shuffle_av_i8z_all_m128i(min, m128i::from(SHUFFLE_2));
898        let min = min_u8_m128i(min, rhs);
899        let rhs = shuffle_av_i8z_all_m128i(min, m128i::from(SHUFFLE_3));
900        let min = min_u8_m128i(min, rhs);
901        let rhs = shuffle_av_i8z_all_m128i(min, m128i::from(SHUFFLE_4));
902        let min = min_u8_m128i(min, rhs);
903        get_i32_from_m128i_s(min) as u8
904      } else if #[cfg(target_feature="simd128")] {
905        let rhs = u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7>(self.simd, self.simd);
906        let min = u8x16_min(self.simd, rhs);
907        let rhs = u8x16_shuffle::<4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0>(min, min);
908        let min = u8x16_min(min, rhs);
909        let rhs = u8x16_shuffle::<2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(min, min);
910        let min = u8x16_min(min, rhs);
911        let rhs = u8x16_shuffle::<1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0>(min, min);
912        let min = u8x16_min(min, rhs);
913        u8x16_extract_lane::<0>(min)
914      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
915        unsafe {
916          // Use `transmute` instead of `cast` because `uint8x16_t` does not
917          // implement `bytemuck::Pod`.
918          let rhs = vqtbl1q_u8(self.neon, core::mem::transmute(SHUFFLE_1));
919          let min = vminq_u8(self.neon, rhs);
920          let rhs = vqtbl1q_u8(min, core::mem::transmute(SHUFFLE_2));
921          let min = vminq_u8(min, rhs);
922          let rhs = vqtbl1q_u8(min, core::mem::transmute(SHUFFLE_3));
923          let min = vminq_u8(min, rhs);
924          let rhs = vqtbl1q_u8(min, core::mem::transmute(SHUFFLE_4));
925          let min = vminq_u8(min, rhs);
926          vgetq_lane_u8(min, 0)
927        }
928      } else {
929        let array: [u8; 16] = cast(self);
930        array.into_iter().reduce(u8::min).unwrap()
931      }
932    }
933  }
934
935  #[inline]
936  #[must_use]
937  pub fn max(self, rhs: Self) -> Self {
938    pick! {
939      if #[cfg(target_feature="sse2")] {
940        Self { sse: max_u8_m128i(self.sse, rhs.sse) }
941      } else if #[cfg(target_feature="simd128")] {
942        Self { simd: u8x16_max(self.simd, rhs.simd) }
943      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
944        unsafe {Self { neon: vmaxq_u8(self.neon, rhs.neon) }}
945      } else {
946        Self { arr: [
947          self.arr[0].max(rhs.arr[0]),
948          self.arr[1].max(rhs.arr[1]),
949          self.arr[2].max(rhs.arr[2]),
950          self.arr[3].max(rhs.arr[3]),
951          self.arr[4].max(rhs.arr[4]),
952          self.arr[5].max(rhs.arr[5]),
953          self.arr[6].max(rhs.arr[6]),
954          self.arr[7].max(rhs.arr[7]),
955          self.arr[8].max(rhs.arr[8]),
956          self.arr[9].max(rhs.arr[9]),
957          self.arr[10].max(rhs.arr[10]),
958          self.arr[11].max(rhs.arr[11]),
959          self.arr[12].max(rhs.arr[12]),
960          self.arr[13].max(rhs.arr[13]),
961          self.arr[14].max(rhs.arr[14]),
962          self.arr[15].max(rhs.arr[15]),
963        ]}
964      }
965    }
966  }
967  #[inline]
968  #[must_use]
969  pub fn min(self, rhs: Self) -> Self {
970    pick! {
971      if #[cfg(target_feature="sse2")] {
972        Self { sse: min_u8_m128i(self.sse, rhs.sse) }
973      } else if #[cfg(target_feature="simd128")] {
974        Self { simd: u8x16_min(self.simd, rhs.simd) }
975      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
976        unsafe {Self { neon: vminq_u8(self.neon, rhs.neon) }}
977      } else {
978        Self { arr: [
979          self.arr[0].min(rhs.arr[0]),
980          self.arr[1].min(rhs.arr[1]),
981          self.arr[2].min(rhs.arr[2]),
982          self.arr[3].min(rhs.arr[3]),
983          self.arr[4].min(rhs.arr[4]),
984          self.arr[5].min(rhs.arr[5]),
985          self.arr[6].min(rhs.arr[6]),
986          self.arr[7].min(rhs.arr[7]),
987          self.arr[8].min(rhs.arr[8]),
988          self.arr[9].min(rhs.arr[9]),
989          self.arr[10].min(rhs.arr[10]),
990          self.arr[11].min(rhs.arr[11]),
991          self.arr[12].min(rhs.arr[12]),
992          self.arr[13].min(rhs.arr[13]),
993          self.arr[14].min(rhs.arr[14]),
994          self.arr[15].min(rhs.arr[15]),
995        ]}
996      }
997    }
998  }
999
1000  integer_fn_clamp!();
1001
1002  #[inline]
1003  #[must_use]
1004  pub fn saturating_add(self, rhs: Self) -> Self {
1005    pick! {
1006      if #[cfg(target_feature="sse2")] {
1007        Self { sse: add_saturating_u8_m128i(self.sse, rhs.sse) }
1008      } else if #[cfg(target_feature="simd128")] {
1009        Self { simd: u8x16_add_sat(self.simd, rhs.simd) }
1010      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1011        unsafe {Self { neon: vqaddq_u8(self.neon, rhs.neon) }}
1012      } else {
1013        Self { arr: [
1014          self.arr[0].saturating_add(rhs.arr[0]),
1015          self.arr[1].saturating_add(rhs.arr[1]),
1016          self.arr[2].saturating_add(rhs.arr[2]),
1017          self.arr[3].saturating_add(rhs.arr[3]),
1018          self.arr[4].saturating_add(rhs.arr[4]),
1019          self.arr[5].saturating_add(rhs.arr[5]),
1020          self.arr[6].saturating_add(rhs.arr[6]),
1021          self.arr[7].saturating_add(rhs.arr[7]),
1022          self.arr[8].saturating_add(rhs.arr[8]),
1023          self.arr[9].saturating_add(rhs.arr[9]),
1024          self.arr[10].saturating_add(rhs.arr[10]),
1025          self.arr[11].saturating_add(rhs.arr[11]),
1026          self.arr[12].saturating_add(rhs.arr[12]),
1027          self.arr[13].saturating_add(rhs.arr[13]),
1028          self.arr[14].saturating_add(rhs.arr[14]),
1029          self.arr[15].saturating_add(rhs.arr[15]),
1030        ]}
1031      }
1032    }
1033  }
1034  #[inline]
1035  #[must_use]
1036  pub fn saturating_sub(self, rhs: Self) -> Self {
1037    pick! {
1038      if #[cfg(target_feature="sse2")] {
1039        Self { sse: sub_saturating_u8_m128i(self.sse, rhs.sse) }
1040      } else if #[cfg(target_feature="simd128")] {
1041        Self { simd: u8x16_sub_sat(self.simd, rhs.simd) }
1042      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1043        unsafe { Self { neon: vqsubq_u8(self.neon, rhs.neon) } }
1044      } else {
1045        Self { arr: [
1046          self.arr[0].saturating_sub(rhs.arr[0]),
1047          self.arr[1].saturating_sub(rhs.arr[1]),
1048          self.arr[2].saturating_sub(rhs.arr[2]),
1049          self.arr[3].saturating_sub(rhs.arr[3]),
1050          self.arr[4].saturating_sub(rhs.arr[4]),
1051          self.arr[5].saturating_sub(rhs.arr[5]),
1052          self.arr[6].saturating_sub(rhs.arr[6]),
1053          self.arr[7].saturating_sub(rhs.arr[7]),
1054          self.arr[8].saturating_sub(rhs.arr[8]),
1055          self.arr[9].saturating_sub(rhs.arr[9]),
1056          self.arr[10].saturating_sub(rhs.arr[10]),
1057          self.arr[11].saturating_sub(rhs.arr[11]),
1058          self.arr[12].saturating_sub(rhs.arr[12]),
1059          self.arr[13].saturating_sub(rhs.arr[13]),
1060          self.arr[14].saturating_sub(rhs.arr[14]),
1061          self.arr[15].saturating_sub(rhs.arr[15]),
1062        ]}
1063      }
1064    }
1065  }
1066
1067  /// Lanewise saturating multiply.
1068  #[inline]
1069  #[must_use]
1070  pub fn saturating_mul(self, rhs: Self) -> Self {
1071    pick! {
1072      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1073        unsafe {
1074          let low_wide_mul = vreinterpretq_u8_u16(
1075            vmull_u8(vget_low_u8(self.neon), vget_low_u8(rhs.neon)),
1076          );
1077          let high_wide_mul = vreinterpretq_u8_u16(
1078            vmull_u8(vget_high_u8(self.neon), vget_high_u8(rhs.neon)),
1079          );
1080          let low_high = vuzpq_u8(low_wide_mul, high_wide_mul);
1081          let low = Self { neon: low_high.0 };
1082          let high = Self { neon: low_high.1 };
1083
1084          let no_overflow = high.simd_eq(Self::ZERO);
1085          no_overflow.blend(low, Self::MAX)
1086        }
1087      } else {
1088        let self_array = self.to_array();
1089        let rhs_array = rhs.to_array();
1090
1091        Self::new([
1092          self_array[0].saturating_mul(rhs_array[0]),
1093          self_array[1].saturating_mul(rhs_array[1]),
1094          self_array[2].saturating_mul(rhs_array[2]),
1095          self_array[3].saturating_mul(rhs_array[3]),
1096          self_array[4].saturating_mul(rhs_array[4]),
1097          self_array[5].saturating_mul(rhs_array[5]),
1098          self_array[6].saturating_mul(rhs_array[6]),
1099          self_array[7].saturating_mul(rhs_array[7]),
1100          self_array[8].saturating_mul(rhs_array[8]),
1101          self_array[9].saturating_mul(rhs_array[9]),
1102          self_array[10].saturating_mul(rhs_array[10]),
1103          self_array[11].saturating_mul(rhs_array[11]),
1104          self_array[12].saturating_mul(rhs_array[12]),
1105          self_array[13].saturating_mul(rhs_array[13]),
1106          self_array[14].saturating_mul(rhs_array[14]),
1107          self_array[15].saturating_mul(rhs_array[15]),
1108        ])
1109      }
1110    }
1111  }
1112
1113  integer_fn_saturating_div!([
1114    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1115  ]);
1116
1117  /// Unpack and interleave low lanes of two `u8x16`
1118  #[inline]
1119  #[must_use]
1120  pub fn unpack_low(lhs: u8x16, rhs: u8x16) -> u8x16 {
1121    pick! {
1122        if #[cfg(target_feature = "sse2")] {
1123            u8x16 { sse: unpack_low_i8_m128i(lhs.sse, rhs.sse) }
1124        } else if #[cfg(target_feature = "simd128")] {
1125          u8x16 { simd: u8x16_shuffle::<0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23>(lhs.simd, rhs.simd) }
1126        } else if #[cfg(all(target_feature = "neon", target_arch = "aarch64"))] {
1127            let lhs = unsafe { vget_low_u8(lhs.neon) };
1128            let rhs = unsafe { vget_low_u8(rhs.neon) };
1129
1130            let zipped = unsafe { vzip_u8(lhs, rhs) };
1131            u8x16 { neon: unsafe { vcombine_u8(zipped.0, zipped.1) } }
1132        } else {
1133            u8x16::new([
1134                lhs.as_array()[0], rhs.as_array()[0],
1135                lhs.as_array()[1], rhs.as_array()[1],
1136                lhs.as_array()[2], rhs.as_array()[2],
1137                lhs.as_array()[3], rhs.as_array()[3],
1138                lhs.as_array()[4], rhs.as_array()[4],
1139                lhs.as_array()[5], rhs.as_array()[5],
1140                lhs.as_array()[6], rhs.as_array()[6],
1141                lhs.as_array()[7], rhs.as_array()[7],
1142            ])
1143        }
1144    }
1145  }
1146
1147  /// Unpack and interleave high lanes of two `u8x16`
1148  #[inline]
1149  #[must_use]
1150  pub fn unpack_high(lhs: u8x16, rhs: u8x16) -> u8x16 {
1151    pick! {
1152        if #[cfg(target_feature = "sse2")] {
1153            u8x16 { sse: unpack_high_i8_m128i(lhs.sse, rhs.sse) }
1154        } else if #[cfg(target_feature = "simd128")] {
1155            u8x16 { simd: u8x16_shuffle::<8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31>(lhs.simd, rhs.simd) }
1156        } else if #[cfg(all(target_feature = "neon", target_arch = "aarch64"))] {
1157            let lhs = unsafe { vget_high_u8(lhs.neon) };
1158            let rhs = unsafe { vget_high_u8(rhs.neon) };
1159
1160            let zipped = unsafe { vzip_u8(lhs, rhs) };
1161            u8x16 { neon: unsafe { vcombine_u8(zipped.0, zipped.1) } }
1162        } else {
1163            u8x16::new([
1164                lhs.as_array()[8], rhs.as_array()[8],
1165                lhs.as_array()[9], rhs.as_array()[9],
1166                lhs.as_array()[10], rhs.as_array()[10],
1167                lhs.as_array()[11], rhs.as_array()[11],
1168                lhs.as_array()[12], rhs.as_array()[12],
1169                lhs.as_array()[13], rhs.as_array()[13],
1170                lhs.as_array()[14], rhs.as_array()[14],
1171                lhs.as_array()[15], rhs.as_array()[15],
1172            ])
1173        }
1174    }
1175  }
1176
1177  /// Pack and saturate two `i16x8` to `u8x16`
1178  #[inline]
1179  #[must_use]
1180  pub fn narrow_i16x8(lhs: i16x8, rhs: i16x8) -> Self {
1181    pick! {
1182        if #[cfg(target_feature = "sse2")] {
1183            u8x16 { sse: pack_i16_to_u8_m128i(lhs.sse, rhs.sse) }
1184        } else if #[cfg(target_feature = "simd128")] {
1185            u8x16 { simd: u8x16_narrow_i16x8(lhs.simd, rhs.simd) }
1186        } else if #[cfg(all(target_feature = "neon", target_arch = "aarch64"))] {
1187            let lhs = unsafe { vqmovun_s16(lhs.neon) };
1188            let rhs = unsafe { vqmovun_s16(rhs.neon) };
1189            u8x16 { neon: unsafe { vcombine_u8(lhs, rhs) } }
1190        } else {
1191            fn clamp(a: i16) -> u8 {
1192                  if a < u8::MIN as i16 {
1193                      u8::MIN
1194                  } else if a > u8::MAX as i16 {
1195                      u8::MAX
1196                  } else {
1197                      a as u8
1198                  }
1199            }
1200
1201            Self { arr: [
1202                clamp(lhs.as_array()[0]),
1203                clamp(lhs.as_array()[1]),
1204                clamp(lhs.as_array()[2]),
1205                clamp(lhs.as_array()[3]),
1206                clamp(lhs.as_array()[4]),
1207                clamp(lhs.as_array()[5]),
1208                clamp(lhs.as_array()[6]),
1209                clamp(lhs.as_array()[7]),
1210                clamp(rhs.as_array()[0]),
1211                clamp(rhs.as_array()[1]),
1212                clamp(rhs.as_array()[2]),
1213                clamp(rhs.as_array()[3]),
1214                clamp(rhs.as_array()[4]),
1215                clamp(rhs.as_array()[5]),
1216                clamp(rhs.as_array()[6]),
1217                clamp(rhs.as_array()[7]),
1218            ]}
1219        }
1220    }
1221  }
1222
1223  /// Returns a new vector where each element is based on the index values in
1224  /// `rhs`.
1225  ///
1226  /// * Index values in the range `[0, 15]` select the i-th element of `self`.
1227  /// * Index values that are out of range will cause that output lane to be
1228  ///   `0`.
1229  #[inline]
1230  pub fn swizzle(self, rhs: i8x16) -> i8x16 {
1231    cast(i8x16::swizzle(cast(self), rhs))
1232  }
1233
1234  /// Works like [`swizzle`](Self::swizzle) with the following additional
1235  /// details
1236  ///
1237  /// * Indices in the range `[0, 15]` will select the i-th element of `self`.
1238  /// * If the high bit of any index is set (meaning that the index is
1239  ///   negative), then the corresponding output lane is guaranteed to be zero.
1240  /// * Otherwise the output lane is either `0` or `self[rhs[i] % 16]`,
1241  ///   depending on the implementation.
1242  #[inline]
1243  pub fn swizzle_relaxed(self, rhs: u8x16) -> u8x16 {
1244    cast(i8x16::swizzle_relaxed(cast(self), cast(rhs)))
1245  }
1246
1247  #[inline]
1248  #[must_use]
1249  #[doc(alias("movemask", "move_mask"))]
1250  pub fn to_bitmask(self) -> u32 {
1251    i8x16::to_bitmask(cast(self)) as u32
1252  }
1253
1254  #[inline]
1255  #[must_use]
1256  pub fn any(self) -> bool {
1257    i8x16::any(cast(self))
1258  }
1259
1260  #[inline]
1261  #[must_use]
1262  pub fn all(self) -> bool {
1263    i8x16::all(cast(self))
1264  }
1265
1266  #[inline]
1267  #[must_use]
1268  pub fn none(self) -> bool {
1269    i8x16::none(cast(self))
1270  }
1271
1272  /// Transpose matrix of 16x16 `u8` matrix. Currently not accelerated.
1273  #[must_use]
1274  #[inline]
1275  pub fn transpose(data: [u8x16; 16]) -> [u8x16; 16] {
1276    cast(i8x16::transpose(cast(data)))
1277  }
1278
1279  #[inline]
1280  pub fn to_array(self) -> [u8; 16] {
1281    cast(self)
1282  }
1283
1284  #[inline]
1285  pub fn as_array(&self) -> &[u8; 16] {
1286    cast_ref(self)
1287  }
1288
1289  #[inline]
1290  pub fn as_mut_array(&mut self) -> &mut [u8; 16] {
1291    cast_mut(self)
1292  }
1293}