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