Skip to main content

wide/
u8x32_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="avx2")] {
5    #[derive(Default, Clone, Copy, PartialEq, Eq)]
6    #[repr(C, align(32))]
7    pub struct u8x32 { pub(crate) avx: m256i }
8  } else {
9    #[derive(Default, Clone, Copy, PartialEq, Eq)]
10    #[repr(C, align(32))]
11    pub struct u8x32 { pub(crate) a : u8x16, pub(crate) b : u8x16 }
12  }
13}
14
15int_uint_consts!(u8, 32, u8x32, 256);
16
17unsafe impl Zeroable for u8x32 {}
18unsafe impl Pod for u8x32 {}
19
20impl AlignTo for u8x32 {
21  type Elem = u8;
22}
23
24impl Add for u8x32 {
25  type Output = Self;
26  #[inline]
27  fn add(self, rhs: Self) -> Self::Output {
28    pick! {
29      if #[cfg(target_feature="avx2")] {
30        Self { avx: add_i8_m256i(self.avx,rhs.avx) }
31      } else {
32        Self {
33          a : self.a.add(rhs.a),
34          b : self.b.add(rhs.b),
35        }
36      }
37    }
38  }
39}
40
41impl Sub for u8x32 {
42  type Output = Self;
43  #[inline]
44  fn sub(self, rhs: Self) -> Self::Output {
45    pick! {
46      if #[cfg(target_feature="avx2")] {
47        Self { avx: sub_i8_m256i(self.avx,rhs.avx) }
48      } else {
49        Self {
50          a : self.a.sub(rhs.a),
51          b : self.b.sub(rhs.b),
52        }
53      }
54    }
55  }
56}
57
58impl Mul for u8x32 {
59  type Output = Self;
60
61  #[inline]
62  fn mul(self, rhs: Self) -> Self::Output {
63    // For x86, this technically can be done explicitly by converting to `i16`
64    // then converting back after multiplication, but that may not actually be
65    // faster than auto-vectorization.
66    let [self_a, self_b]: [u8x16; 2] = cast(self);
67    let [rhs_a, rhs_b]: [u8x16; 2] = cast(rhs);
68    cast([self_a * rhs_a, self_b * rhs_b])
69  }
70}
71
72integer_impl_div_rem!(
73  u8,
74  u8x32,
75  [
76    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
77    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
78  ],
79);
80
81impl Shl for u8x32 {
82  type Output = Self;
83
84  /// Shifts lanes by the corresponding lane.
85  ///
86  /// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
87  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
88  /// of the type. (same as `wrapping_shl`)
89  #[inline]
90  fn shl(self, rhs: Self) -> Self::Output {
91    // For x86, this technically can be done explicitly by converting to `u16`
92    // or `u32` then converting back after multiplication, but that may not
93    // actually be faster than auto-vectorization.
94    let [self_a, self_b]: [u8x16; 2] = cast(self);
95    let [rhs_a, rhs_b]: [u8x16; 2] = cast(rhs);
96    cast([self_a << rhs_a, self_b << rhs_b])
97  }
98}
99
100impl Shr for u8x32 {
101  type Output = Self;
102
103  /// Shifts lanes by the corresponding lane.
104  ///
105  /// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
106  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
107  /// of the type. (same as `wrapping_shr`)
108  #[inline]
109  fn shr(self, rhs: Self) -> Self::Output {
110    // For x86, this technically can be done explicitly by converting to `u16`
111    // or `u32` then converting back after multiplication, but that may not
112    // actually be faster than auto-vectorization.
113    let [self_a, self_b]: [u8x16; 2] = cast(self);
114    let [rhs_a, rhs_b]: [u8x16; 2] = cast(rhs);
115    cast([self_a >> rhs_a, self_b >> rhs_b])
116  }
117}
118
119impl Add<u8> for u8x32 {
120  type Output = Self;
121  #[inline]
122  fn add(self, rhs: u8) -> Self::Output {
123    self.add(Self::splat(rhs))
124  }
125}
126
127impl Sub<u8> for u8x32 {
128  type Output = Self;
129  #[inline]
130  fn sub(self, rhs: u8) -> Self::Output {
131    self.sub(Self::splat(rhs))
132  }
133}
134
135impl Mul<u8> for u8x32 {
136  type Output = Self;
137
138  #[inline]
139  fn mul(self, rhs: u8) -> Self::Output {
140    self * Self::splat(rhs)
141  }
142}
143
144macro_rules! impl_shl_scalar {
145  ($Rhs:ident) => {
146    impl Shl<$Rhs> for u8x32 {
147      type Output = Self;
148
149      /// Shifts all lanes by a uniform value.
150      ///
151      /// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
152      /// high-order bits of `rhs` that would cause the shift to exceed the
153      /// bitwidth of the type. (same as `wrapping_shl`)
154      #[inline]
155      fn shl(self, rhs: $Rhs) -> Self::Output {
156        // For x86, this technically can be done explicitly by converting
157        // to `u16` or `u32` then converting back after multiplication, but that
158        // may not actually be faster than auto-vectorization.
159        let [self_a, self_b]: [u8x16; 2] = cast(self);
160        cast([self_a << rhs, self_b << rhs])
161      }
162    }
163  };
164}
165impl_shl_scalar!(i8);
166impl_shl_scalar!(u8);
167impl_shl_scalar!(i16);
168impl_shl_scalar!(u16);
169impl_shl_scalar!(i32);
170impl_shl_scalar!(u32);
171impl_shl_scalar!(i64);
172impl_shl_scalar!(u64);
173impl_shl_scalar!(i128);
174impl_shl_scalar!(u128);
175
176macro_rules! impl_shr_scalar {
177  ($Rhs:ident) => {
178    impl Shr<$Rhs> for u8x32 {
179      type Output = Self;
180
181      /// Shifts all lanes by a uniform value.
182      ///
183      /// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes
184      /// any high-order bits of `rhs` that would cause the shift to exceed
185      /// the bitwidth of the type. (same as `wrapping_shr`)
186      #[inline]
187      fn shr(self, rhs: $Rhs) -> Self::Output {
188        // For x86, this technically can be done explicitly by converting
189        // to `u16` or `u32` then converting back after multiplication, but that
190        // may not actually be faster than auto-vectorization.
191        let [self_a, self_b]: [u8x16; 2] = cast(self);
192        cast([self_a >> rhs, self_b >> rhs])
193      }
194    }
195  };
196}
197impl_shr_scalar!(i8);
198impl_shr_scalar!(u8);
199impl_shr_scalar!(i16);
200impl_shr_scalar!(u16);
201impl_shr_scalar!(i32);
202impl_shr_scalar!(u32);
203impl_shr_scalar!(i64);
204impl_shr_scalar!(u64);
205impl_shr_scalar!(i128);
206impl_shr_scalar!(u128);
207
208impl Add<u8x32> for u8 {
209  type Output = u8x32;
210  #[inline]
211  fn add(self, rhs: u8x32) -> Self::Output {
212    u8x32::splat(self).add(rhs)
213  }
214}
215
216impl Sub<u8x32> for u8 {
217  type Output = u8x32;
218  #[inline]
219  fn sub(self, rhs: u8x32) -> Self::Output {
220    u8x32::splat(self).sub(rhs)
221  }
222}
223
224impl Mul<u8x32> for u8 {
225  type Output = u8x32;
226
227  #[inline]
228  fn mul(self, rhs: u8x32) -> Self::Output {
229    u8x32::splat(self) * rhs
230  }
231}
232
233impl BitAnd for u8x32 {
234  type Output = Self;
235  #[inline]
236  fn bitand(self, rhs: Self) -> Self::Output {
237    pick! {
238      if #[cfg(target_feature="avx2")] {
239          Self { avx : bitand_m256i(self.avx,rhs.avx) }
240      } else {
241          Self {
242            a : self.a.bitand(rhs.a),
243            b : self.b.bitand(rhs.b),
244          }
245      }
246    }
247  }
248}
249
250impl BitOr for u8x32 {
251  type Output = Self;
252  #[inline]
253  fn bitor(self, rhs: Self) -> Self::Output {
254    pick! {
255      if #[cfg(target_feature="avx2")] {
256        Self { avx : bitor_m256i(self.avx,rhs.avx) }
257      } else {
258        Self {
259          a : self.a.bitor(rhs.a),
260          b : self.b.bitor(rhs.b),
261        }
262      }
263    }
264  }
265}
266
267impl BitXor for u8x32 {
268  type Output = Self;
269  #[inline]
270  fn bitxor(self, rhs: Self) -> Self::Output {
271    pick! {
272      if #[cfg(target_feature="avx2")] {
273        Self { avx : bitxor_m256i(self.avx,rhs.avx) }
274      } else {
275        Self {
276          a : self.a.bitxor(rhs.a),
277          b : self.b.bitxor(rhs.b),
278        }
279      }
280    }
281  }
282}
283
284#[expect(deprecated)]
285impl CmpEq for u8x32 {
286  type Output = Self;
287  #[inline]
288  fn simd_eq(self, rhs: Self) -> Self::Output {
289    pick! {
290      if #[cfg(target_feature="avx2")] {
291        Self { avx : cmp_eq_mask_i8_m256i(self.avx,rhs.avx) }
292      } else {
293        Self {
294          a : self.a.simd_eq(rhs.a),
295          b : self.b.simd_eq(rhs.b),
296        }
297      }
298    }
299  }
300}
301
302#[expect(deprecated)]
303impl CmpNe for u8x32 {
304  type Output = Self;
305  #[inline]
306  fn simd_ne(self, rhs: Self) -> Self::Output {
307    pick! {
308      if #[cfg(target_feature="avx2")] {
309        !self.simd_eq(rhs)
310      } else {
311        Self {
312          a : self.a.simd_ne(rhs.a),
313          b : self.b.simd_ne(rhs.b),
314        }
315      }
316    }
317  }
318}
319
320#[expect(deprecated)]
321impl CmpLt for u8x32 {
322  type Output = Self;
323  #[inline]
324  fn simd_lt(self, rhs: Self) -> Self::Output {
325    pick! {
326      if #[cfg(target_feature="avx2")] {
327        // Convert from u8 to i8.
328        let offset = Self::splat(0x80);
329        let self_i8 = self.bitxor(offset).avx;
330        let rhs_i8 = rhs.bitxor(offset).avx;
331        Self { avx: cmp_gt_mask_i8_m256i(rhs_i8, self_i8)}
332      } else {
333        Self { a: self.a.simd_lt(rhs.a), b: self.b.simd_lt(rhs.b) }
334      }
335    }
336  }
337}
338
339#[expect(deprecated)]
340impl CmpLe for u8x32 {
341  type Output = Self;
342  #[inline]
343  fn simd_le(self, rhs: Self) -> Self::Output {
344    pick! {
345      if #[cfg(target_feature="avx2")] {
346        // Convert from u8 to i8.
347        let offset = Self::splat(0x80);
348        let self_i8 = self.bitxor(offset).avx;
349        let rhs_i8 = rhs.bitxor(offset).avx;
350        let gt_mask = Self { avx : cmp_gt_mask_i8_m256i(self_i8,rhs_i8) };
351        Self { avx: gt_mask.bitxor(Self::splat(0xFF)).avx }
352      } else {
353        Self { a: self.a.simd_le(rhs.a), b: self.b.simd_le(rhs.b) }
354      }
355    }
356  }
357}
358
359#[expect(deprecated)]
360impl CmpGe for u8x32 {
361  type Output = Self;
362  #[inline]
363  fn simd_ge(self, rhs: Self) -> Self::Output {
364    pick! {
365      if #[cfg(target_feature="avx2")] {
366        // Convert from u8 to i8.
367        let offset = Self::splat(0x80);
368        let self_i8 = self.bitxor(offset).avx;
369        let rhs_i8 = rhs.bitxor(offset).avx;
370        let lt_mask = Self { avx: cmp_gt_mask_i8_m256i(rhs_i8, self_i8)};
371        Self { avx: lt_mask.bitxor(Self::splat(0xFF)).avx }
372      } else {
373        Self { a: self.a.simd_ge(rhs.a), b: self.b.simd_ge(rhs.b) }
374      }
375    }
376  }
377}
378
379#[expect(deprecated)]
380impl CmpGt for u8x32 {
381  type Output = Self;
382  #[inline]
383  fn simd_gt(self, rhs: Self) -> Self::Output {
384    pick! {
385      if #[cfg(target_feature="avx2")] {
386        // Convert from u8 to i8.
387        let offset = Self::splat(0x80);
388        let self_i8 = self.bitxor(offset).avx;
389        let rhs_i8 = rhs.bitxor(offset).avx;
390        Self { avx : cmp_gt_mask_i8_m256i(self_i8,rhs_i8) }
391      } else {
392        Self { a: self.a.simd_gt(rhs.a), b: self.b.simd_gt(rhs.b) }
393      }
394    }
395  }
396}
397
398impl Not for u8x32 {
399  type Output = Self;
400  #[inline]
401  fn not(self) -> Self {
402    pick! {
403      if #[cfg(target_feature="avx2")] {
404        Self { avx: self.avx.not()  }
405      } else {
406        Self {
407          a : self.a.not(),
408          b : self.b.not(),
409        }
410      }
411    }
412  }
413}
414
415impl u8x32 {
416  #[inline]
417  #[must_use]
418  pub const fn new(array: [u8; 32]) -> Self {
419    unsafe { core::mem::transmute(array) }
420  }
421
422  simd_comparison_fns!();
423
424  #[inline]
425  #[must_use]
426  pub fn blend(self, t: Self, f: Self) -> Self {
427    pick! {
428      if #[cfg(target_feature="avx2")] {
429        Self { avx: blend_varying_i8_m256i(f.avx, t.avx, self.avx) }
430      } else {
431        Self {
432          a : self.a.blend(t.a, f.a),
433          b : self.b.blend(t.b, f.b),
434        }
435      }
436    }
437  }
438
439  #[inline]
440  #[must_use]
441  pub fn reduce_add(self) -> u8 {
442    cast(i8x32::reduce_add(cast(self)))
443  }
444
445  #[inline]
446  #[must_use]
447  pub fn reduce_max(self) -> u8 {
448    let array: [u8x16; 2] = cast(self);
449    array[0].max(array[1]).reduce_max()
450  }
451
452  #[inline]
453  #[must_use]
454  pub fn reduce_min(self) -> u8 {
455    let array: [u8x16; 2] = cast(self);
456    array[0].min(array[1]).reduce_min()
457  }
458
459  #[inline]
460  #[must_use]
461  pub fn max(self, rhs: Self) -> Self {
462    pick! {
463      if #[cfg(target_feature="avx2")] {
464        Self { avx: max_u8_m256i(self.avx,rhs.avx) }
465      } else {
466        Self {
467          a : self.a.max(rhs.a),
468          b : self.b.max(rhs.b),
469        }
470      }
471    }
472  }
473  #[inline]
474  #[must_use]
475  pub fn min(self, rhs: Self) -> Self {
476    pick! {
477      if #[cfg(target_feature="avx2")] {
478        Self { avx: min_u8_m256i(self.avx,rhs.avx) }
479      } else {
480        Self {
481          a : self.a.min(rhs.a),
482          b : self.b.min(rhs.b),
483        }
484      }
485    }
486  }
487
488  integer_fn_clamp!();
489
490  #[inline]
491  #[must_use]
492  pub fn saturating_add(self, rhs: Self) -> Self {
493    pick! {
494      if #[cfg(target_feature="avx2")] {
495        Self { avx: add_saturating_u8_m256i(self.avx, rhs.avx) }
496      } else {
497        Self {
498          a : self.a.saturating_add(rhs.a),
499          b : self.b.saturating_add(rhs.b),
500        }
501      }
502    }
503  }
504  #[inline]
505  #[must_use]
506  pub fn saturating_sub(self, rhs: Self) -> Self {
507    pick! {
508      if #[cfg(target_feature="avx2")] {
509        Self { avx: sub_saturating_u8_m256i(self.avx, rhs.avx) }
510      } else {
511        Self {
512          a : self.a.saturating_sub(rhs.a),
513          b : self.b.saturating_sub(rhs.b),
514        }
515      }
516    }
517  }
518
519  /// Lanewise saturating multiply.
520  #[inline]
521  #[must_use]
522  pub fn saturating_mul(self, rhs: Self) -> Self {
523    let [self_a, self_b]: [u8x16; 2] = cast(self);
524    let [rhs_a, rhs_b]: [u8x16; 2] = cast(rhs);
525    cast([self_a.saturating_mul(rhs_a), self_b.saturating_mul(rhs_b)])
526  }
527
528  integer_fn_saturating_div!([
529    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
530    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
531  ]);
532
533  #[inline]
534  #[must_use]
535  #[doc(alias("movemask", "move_mask"))]
536  pub fn to_bitmask(self) -> u32 {
537    i8x32::to_bitmask(cast(self)) as u32
538  }
539
540  #[inline]
541  #[must_use]
542  pub fn any(self) -> bool {
543    i8x32::any(cast(self))
544  }
545
546  #[inline]
547  #[must_use]
548  pub fn all(self) -> bool {
549    i8x32::all(cast(self))
550  }
551
552  /// Returns a new vector with lanes selected from the lanes of the first input
553  /// vector a specified in the second input vector `rhs`.
554  /// The indices i in range `[0, 15]` select the i-th element of `self`. For
555  /// indices outside of the range the resulting lane is `0`.
556  ///
557  /// This note that is the equivalent of two parallel swizzle operations on the
558  /// two halves of the vector, and the indexes each refer to the
559  /// corresponding half.
560  #[inline]
561  pub fn swizzle_half(self, rhs: i8x32) -> i8x32 {
562    cast(i8x32::swizzle_half(cast(self), cast(rhs)))
563  }
564
565  /// Indices in the range `[0, 15]` will select the i-th element of `self`. If
566  /// the high bit of any element of `rhs` is set (negative) then the
567  /// corresponding output lane is guaranteed to be zero. Otherwise if the
568  /// element of `rhs` is within the range `[32, 127]` then the output lane is
569  /// either `0` or `self[rhs[i] % 16]` depending on the implementation.
570  ///
571  /// This is the equivalent to two parallel swizzle operations on the two
572  /// halves of the vector, and the indexes each refer to their corresponding
573  /// half.
574  #[inline]
575  pub fn swizzle_half_relaxed(self, rhs: u8x32) -> u8x32 {
576    cast(i8x32::swizzle_half_relaxed(cast(self), cast(rhs)))
577  }
578
579  #[inline]
580  #[must_use]
581  pub fn none(self) -> bool {
582    !self.any()
583  }
584
585  /// Transpose matrix of 32x32 `u8` matrix. Currently not accelerated.
586  #[must_use]
587  #[inline]
588  pub fn transpose(data: [u8x32; 32]) -> [u8x32; 32] {
589    cast(i8x32::transpose(cast(data)))
590  }
591
592  #[inline]
593  pub fn to_array(self) -> [u8; 32] {
594    cast(self)
595  }
596
597  #[inline]
598  pub fn as_array(&self) -> &[u8; 32] {
599    cast_ref(self)
600  }
601
602  #[inline]
603  pub fn as_mut_array(&mut self) -> &mut [u8; 32] {
604    cast_mut(self)
605  }
606}