1use core::fmt::{self, Debug, Formatter};
2use core::marker::PhantomData;
3use core::mem::MaybeUninit;
4
5#[cfg(feature = "alloc")]
6use 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)]
15pub struct UnalignedSlice<'a, T: Copy> {
16 data: *const T,
17 len: usize,
18 _phantom_lifetime: PhantomData<&'a T>,
19}
20
21impl<'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
131impl<'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")]
138impl<'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
144impl<'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
156impl<'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`].
166pub struct UnalignedSliceIntoIter<'a, T: Copy> {
167 slice: UnalignedSlice<'a, T>,
168 index: usize,
169}
170
171impl<'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.
182pub struct UnalignedSliceIter<'a, T: Copy> {
183 slice: &'a UnalignedSlice<'a, T>,
184 index: usize,
185}
186
187impl<'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)]
198mod 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