1 //! Implements methods to write a vector type to a slice.
2 
3 macro_rules! impl_slice_write_to_slice {
4     ([$elem_ty:ident; $elem_count:expr]: $id:ident | $test_tt:tt) => {
5         impl $id {
6             /// Writes the values of the vector to the `slice`.
7             ///
8             /// # Panics
9             ///
10             /// If `slice.len() < Self::lanes()` or `&slice[0]` is not
11             /// aligned to an `align_of::<Self>()` boundary.
12             #[inline]
13             pub fn write_to_slice_aligned(self, slice: &mut [$elem_ty]) {
14                 unsafe {
15                     assert!(slice.len() >= $elem_count);
16                     let target_ptr =
17                         slice.get_unchecked_mut(0) as *mut $elem_ty;
18                     assert_eq!(
19                         target_ptr
20                             .align_offset(crate::mem::align_of::<Self>()),
21                         0
22                     );
23                     self.write_to_slice_aligned_unchecked(slice);
24                 }
25             }
26 
27             /// Writes the values of the vector to the `slice`.
28             ///
29             /// # Panics
30             ///
31             /// If `slice.len() < Self::lanes()`.
32             #[inline]
33             pub fn write_to_slice_unaligned(self, slice: &mut [$elem_ty]) {
34                 unsafe {
35                     assert!(slice.len() >= $elem_count);
36                     self.write_to_slice_unaligned_unchecked(slice);
37                 }
38             }
39 
40             /// Writes the values of the vector to the `slice`.
41             ///
42             /// # Safety
43             ///
44             /// If `slice.len() < Self::lanes()` or `&slice[0]` is not
45             /// aligned to an `align_of::<Self>()` boundary, the behavior is
46             /// undefined.
47             #[inline]
48             pub unsafe fn write_to_slice_aligned_unchecked(
49                 self, slice: &mut [$elem_ty],
50             ) {
51                 debug_assert!(slice.len() >= $elem_count);
52                 let target_ptr = slice.get_unchecked_mut(0) as *mut $elem_ty;
53                 debug_assert_eq!(
54                     target_ptr.align_offset(crate::mem::align_of::<Self>()),
55                     0
56                 );
57 
58                                 #[allow(clippy::cast_ptr_alignment)]
59                         #[allow(clippy::cast_ptr_alignment)]
60                 #[allow(clippy::cast_ptr_alignment)]
61                 #[allow(clippy::cast_ptr_alignment)]
62                 *(target_ptr as *mut Self) = self;
63             }
64 
65             /// Writes the values of the vector to the `slice`.
66             ///
67             /// # Safety
68             ///
69             /// If `slice.len() < Self::lanes()` the behavior is undefined.
70             #[inline]
71             pub unsafe fn write_to_slice_unaligned_unchecked(
72                 self, slice: &mut [$elem_ty],
73             ) {
74                 debug_assert!(slice.len() >= $elem_count);
75                 let target_ptr =
76                     slice.get_unchecked_mut(0) as *mut $elem_ty as *mut u8;
77                 let self_ptr = &self as *const Self as *const u8;
78                 crate::ptr::copy_nonoverlapping(
79                     self_ptr,
80                     target_ptr,
81                     crate::mem::size_of::<Self>(),
82                 );
83             }
84         }
85 
86         test_if! {
87             $test_tt:
88             paste::item! {
89                 // Comparisons use integer casts within mantissa^1 range.
90                 #[allow(clippy::float_cmp)]
91                 pub mod [<$id _slice_write_to_slice>] {
92                     use super::*;
93                     use crate::iter::Iterator;
94 
95                     #[cfg_attr(not(target_arch = "wasm32"), test)]
96                     #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
97                     fn write_to_slice_unaligned() {
98                         let mut unaligned = [0 as $elem_ty; $id::lanes() + 1];
99                         let vec = $id::splat(42 as $elem_ty);
100                         vec.write_to_slice_unaligned(&mut unaligned[1..]);
101                         for (index, &b) in unaligned.iter().enumerate() {
102                             if index == 0 {
103                                 assert_eq!(b, 0 as $elem_ty);
104                             } else {
105                                 assert_eq!(b, 42 as $elem_ty);
106                                 assert_eq!(b, vec.extract(index - 1));
107                             }
108                         }
109                     }
110 
111                     // FIXME: wasm-bindgen-test does not support #[should_panic]
112                     // #[cfg_attr(not(target_arch = "wasm32"), test)]
113                     // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
114                     #[cfg(not(target_arch = "wasm32"))]
115                     #[test]
116                     #[should_panic]
117                     fn write_to_slice_unaligned_fail() {
118                         let mut unaligned = [0 as $elem_ty; $id::lanes() + 1];
119                         let vec = $id::splat(42 as $elem_ty);
120                         vec.write_to_slice_unaligned(&mut unaligned[2..]);
121                     }
122 
123                     union A {
124                         data: [$elem_ty; 2 * $id::lanes()],
125                         _vec: $id,
126                     }
127 
128                     #[cfg_attr(not(target_arch = "wasm32"), test)]
129                     #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
130                     fn write_to_slice_aligned() {
131                         let mut aligned = A {
132                             data: [0 as $elem_ty; 2 * $id::lanes()],
133                         };
134                         let vec = $id::splat(42 as $elem_ty);
135                         unsafe {
136                             vec.write_to_slice_aligned(
137                                 &mut aligned.data[$id::lanes()..]
138                             );
139                             for (idx, &b) in aligned.data.iter().enumerate() {
140                                 if idx < $id::lanes() {
141                                     assert_eq!(b, 0 as $elem_ty);
142                                 } else {
143                                     assert_eq!(b, 42 as $elem_ty);
144                                     assert_eq!(
145                                         b, vec.extract(idx - $id::lanes())
146                                     );
147                                 }
148                             }
149                         }
150                     }
151 
152                     // FIXME: wasm-bindgen-test does not support #[should_panic]
153                     // #[cfg_attr(not(target_arch = "wasm32"), test)]
154                     // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
155                     #[cfg(not(target_arch = "wasm32"))]
156                     #[test]
157                     #[should_panic]
158                     fn write_to_slice_aligned_fail_lanes() {
159                         let mut aligned = A {
160                             data: [0 as $elem_ty; 2 * $id::lanes()],
161                         };
162                         let vec = $id::splat(42 as $elem_ty);
163                         unsafe {
164                             vec.write_to_slice_aligned(
165                                 &mut aligned.data[2 * $id::lanes()..]
166                             )
167                         };
168                     }
169 
170                     // FIXME: wasm-bindgen-test does not support #[should_panic]
171                     // #[cfg_attr(not(target_arch = "wasm32"), test)]
172                     // #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
173                     #[cfg(not(target_arch = "wasm32"))]
174                     #[test]
175                     #[should_panic]
176                     fn write_to_slice_aligned_fail_align() {
177                         unsafe {
178                             let mut aligned = A {
179                                 data: [0 as $elem_ty; 2 * $id::lanes()],
180                             };
181 
182                             // get a pointer to the front of data
183                             let ptr: *mut $elem_ty
184                                 = aligned.data.as_mut_ptr() as *mut $elem_ty;
185                             // offset pointer by one element
186                             let ptr = ptr.wrapping_add(1);
187 
188                             if ptr.align_offset(crate::mem::align_of::<$id>())
189                                 == 0 {
190                                 // the pointer is properly aligned, so
191                                 // write_to_slice_aligned won't fail here (e.g.
192                                 // this can happen for i128x1). So we panic to
193                                 // make the "should_fail" test pass:
194                                 panic!("ok");
195                             }
196 
197                             // create a slice - this is safe, because the
198                             // elements of the slice exist, are properly
199                             // initialized, and properly aligned:
200                             let s: &mut [$elem_ty]
201                                 = slice::from_raw_parts_mut(ptr, $id::lanes());
202                             // this should always panic because the slice
203                             // alignment does not match the alignment
204                             // requirements for the vector type:
205                             let vec = $id::splat(42 as $elem_ty);
206                             vec.write_to_slice_aligned(s);
207                         }
208                     }
209                 }
210             }
211         }
212     };
213 }
214