1 | use core::fmt::{self, Debug, Formatter}; |
2 | use core::marker::PhantomData; |
3 | use core::mem::MaybeUninit; |
4 | |
5 | #[cfg (feature = "alloc" )] |
6 | use alloc::vec::Vec; |
7 | |
8 | /// Slice backed by a potentially-unaligned pointer. |
9 | /// |
10 | /// This wrapper can be used to safely expose slices that are inside a |
11 | /// [`repr(packed)`] struct. The element type must be [`Copy`]. |
12 | /// |
13 | /// [`repr(packed)`]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked |
14 | #[derive (Clone)] |
15 | pub struct UnalignedSlice<'a, T: Copy> { |
16 | data: *const T, |
17 | len: usize, |
18 | _phantom_lifetime: PhantomData<&'a T>, |
19 | } |
20 | |
21 | impl<'a, T: Copy> UnalignedSlice<'a, T> { |
22 | /// Create an `UnalignedSlice` from a raw pointer. The pointer must |
23 | /// not be dangling but can be unaligned. The `len` parameter is the |
24 | /// number of elements in the slice (not the number of bytes). |
25 | /// |
26 | /// # Safety |
27 | /// |
28 | /// The `data` pointer must point to a packed array of at least |
29 | /// `len` elements of type `T`. The pointer must remain valid for as |
30 | /// long as the `'a` lifetime. |
31 | pub unsafe fn new(data: *const T, len: usize) -> Self { |
32 | Self { |
33 | data, |
34 | len, |
35 | _phantom_lifetime: PhantomData::default(), |
36 | } |
37 | } |
38 | |
39 | /// Returns true if the slice has a length of 0. |
40 | #[must_use ] |
41 | pub const fn is_empty(&self) -> bool { |
42 | self.len == 0 |
43 | } |
44 | |
45 | /// Returns the number of elements in the slice. |
46 | #[must_use ] |
47 | pub const fn len(&self) -> usize { |
48 | self.len |
49 | } |
50 | |
51 | /// Returns the element at `index`, or `None` if the `index` is out |
52 | /// of bounds. |
53 | #[must_use ] |
54 | pub fn get(&self, index: usize) -> Option<T> { |
55 | if index < self.len { |
56 | Some(unsafe { self.data.add(index).read_unaligned() }) |
57 | } else { |
58 | None |
59 | } |
60 | } |
61 | |
62 | /// Returns an iterator over the slice. |
63 | /// |
64 | /// The iterator yields all items from start to end. |
65 | #[must_use ] |
66 | pub const fn iter(&'a self) -> UnalignedSliceIter<'a, T> { |
67 | UnalignedSliceIter { |
68 | slice: self, |
69 | index: 0, |
70 | } |
71 | } |
72 | |
73 | /// Copy the data to an aligned buffer. |
74 | /// |
75 | /// The length of `dest` must be the same as `self`. |
76 | /// |
77 | /// # Panics |
78 | /// |
79 | /// This function will panic if the two slices have different lengths. |
80 | pub fn copy_to(&self, dest: &mut [T]) { |
81 | if dest.len() != self.len { |
82 | panic!( |
83 | "source slice length ( {}) does not match destination slice length ( {})" , |
84 | self.len(), |
85 | dest.len(), |
86 | ); |
87 | } |
88 | |
89 | for (i, elem) in dest.iter_mut().enumerate() { |
90 | *elem = unsafe { self.data.add(i).read_unaligned() }; |
91 | } |
92 | } |
93 | |
94 | /// Copy the data to an aligned [`MaybeUninit`] buffer. |
95 | /// |
96 | /// The length of `dest` must be the same as `self`. |
97 | /// |
98 | /// This function fully initializes the `dest` slice. |
99 | /// |
100 | /// # Panics |
101 | /// |
102 | /// This function will panic if the two slices have different lengths. |
103 | pub fn copy_to_maybe_uninit(&self, dest: &mut [MaybeUninit<T>]) { |
104 | if dest.len() != self.len { |
105 | panic!( |
106 | "source slice length ( {}) does not match destination slice length ( {})" , |
107 | self.len(), |
108 | dest.len(), |
109 | ); |
110 | } |
111 | |
112 | for (i, elem) in dest.iter_mut().enumerate() { |
113 | unsafe { elem.write(self.data.add(i).read_unaligned()) }; |
114 | } |
115 | } |
116 | |
117 | /// Copies `self` into a new `Vec`. |
118 | #[cfg (feature = "alloc" )] |
119 | #[must_use ] |
120 | pub fn to_vec(&self) -> Vec<T> { |
121 | let len = self.len(); |
122 | let mut v = Vec::with_capacity(len); |
123 | unsafe { |
124 | self.copy_to_maybe_uninit(v.spare_capacity_mut()); |
125 | v.set_len(len); |
126 | } |
127 | v |
128 | } |
129 | } |
130 | |
131 | impl<'a, T: Copy + Debug> Debug for UnalignedSlice<'a, T> { |
132 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
133 | f.debug_list().entries(self.iter()).finish() |
134 | } |
135 | } |
136 | |
137 | #[cfg (feature = "alloc" )] |
138 | impl<'a, T: Copy> From<UnalignedSlice<'a, T>> for Vec<T> { |
139 | fn from(input: UnalignedSlice<'a, T>) -> Self { |
140 | input.to_vec() |
141 | } |
142 | } |
143 | |
144 | impl<'a, T: Copy> IntoIterator for UnalignedSlice<'a, T> { |
145 | type Item = T; |
146 | type IntoIter = UnalignedSliceIntoIter<'a, T>; |
147 | |
148 | fn into_iter(self) -> Self::IntoIter { |
149 | UnalignedSliceIntoIter { |
150 | slice: self, |
151 | index: 0, |
152 | } |
153 | } |
154 | } |
155 | |
156 | impl<'a, T: Copy> IntoIterator for &'a UnalignedSlice<'a, T> { |
157 | type Item = T; |
158 | type IntoIter = UnalignedSliceIter<'a, T>; |
159 | |
160 | fn into_iter(self) -> Self::IntoIter { |
161 | self.iter() |
162 | } |
163 | } |
164 | |
165 | /// Iterator for a [`UnalignedSlice`]. |
166 | pub struct UnalignedSliceIntoIter<'a, T: Copy> { |
167 | slice: UnalignedSlice<'a, T>, |
168 | index: usize, |
169 | } |
170 | |
171 | impl<'a, T: Copy> Iterator for UnalignedSliceIntoIter<'a, T> { |
172 | type Item = T; |
173 | |
174 | fn next(&mut self) -> Option<T> { |
175 | let output: T = self.slice.get(self.index)?; |
176 | self.index += 1; |
177 | Some(output) |
178 | } |
179 | } |
180 | |
181 | /// Iterator for a [`UnalignedSlice`] reference. |
182 | pub struct UnalignedSliceIter<'a, T: Copy> { |
183 | slice: &'a UnalignedSlice<'a, T>, |
184 | index: usize, |
185 | } |
186 | |
187 | impl<'a, T: Copy> Iterator for UnalignedSliceIter<'a, T> { |
188 | type Item = T; |
189 | |
190 | fn next(&mut self) -> Option<T> { |
191 | let output: T = self.slice.get(self.index)?; |
192 | self.index += 1; |
193 | Some(output) |
194 | } |
195 | } |
196 | |
197 | #[cfg (test)] |
198 | mod tests { |
199 | use super::*; |
200 | use alloc::vec::Vec; |
201 | |
202 | #[test ] |
203 | fn test_unaligned_slice() { |
204 | #[rustfmt::skip] |
205 | let bytes: [u8; 13] = [ |
206 | // Extra byte to make the rest of the data unaligned. |
207 | 0, |
208 | // First element. |
209 | 0x10, 0x11, 0x12, 0x13, |
210 | // Second element. |
211 | 0x20, 0x21, 0x22, 0x23, |
212 | // Third element. |
213 | 0x30, 0x31, 0x32, 0x33, |
214 | ]; |
215 | |
216 | // Skip past the first byte and create an unaligned `*const u32` pointer. |
217 | let bytes = &bytes[1..]; |
218 | let slice_ptr: *const u32 = bytes.as_ptr().cast(); |
219 | |
220 | let slice: UnalignedSlice<u32> = unsafe { UnalignedSlice::new(slice_ptr, 0) }; |
221 | assert!(slice.is_empty()); |
222 | |
223 | let slice: UnalignedSlice<u32> = unsafe { UnalignedSlice::new(slice_ptr, 3) }; |
224 | assert!(!slice.is_empty()); |
225 | assert_eq!(slice.len(), 3); |
226 | |
227 | assert_eq!(slice.get(0), Some(0x13121110)); |
228 | assert_eq!(slice.get(1), Some(0x23222120)); |
229 | assert_eq!(slice.get(2), Some(0x33323130)); |
230 | assert_eq!(slice.get(3), None); |
231 | |
232 | let mut copy = [0; 3]; |
233 | slice.copy_to(&mut copy); |
234 | assert_eq!(copy, [0x13121110, 0x23222120, 0x33323130]); |
235 | |
236 | assert_eq!( |
237 | slice.iter().collect::<Vec<_>>(), |
238 | [0x13121110, 0x23222120, 0x33323130] |
239 | ); |
240 | |
241 | assert_eq!( |
242 | slice.into_iter().collect::<Vec<_>>(), |
243 | [0x13121110, 0x23222120, 0x33323130] |
244 | ); |
245 | } |
246 | } |
247 | |