• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
new(data: *const T, len: usize) -> Self31     pub const unsafe fn new(data: *const T, len: usize) -> Self {
32         Self {
33             data,
34             len,
35             _phantom_lifetime: PhantomData,
36         }
37     }
38 
39     /// Returns true if the slice has a length of 0.
40     #[must_use]
is_empty(&self) -> bool41     pub const fn is_empty(&self) -> bool {
42         self.len == 0
43     }
44 
45     /// Get the underlying pointer, which may be unaligned.
46     #[must_use]
as_ptr(&self) -> *const T47     pub const fn as_ptr(&self) -> *const T {
48         self.data
49     }
50 
51     /// Returns the number of elements in the slice.
52     #[must_use]
len(&self) -> usize53     pub const fn len(&self) -> usize {
54         self.len
55     }
56 
57     /// Returns the element at `index`, or `None` if the `index` is out
58     /// of bounds.
59     #[must_use]
get(&self, index: usize) -> Option<T>60     pub fn get(&self, index: usize) -> Option<T> {
61         if index < self.len {
62             Some(unsafe { self.data.add(index).read_unaligned() })
63         } else {
64             None
65         }
66     }
67 
68     /// Returns an iterator over the slice.
69     ///
70     /// The iterator yields all items from start to end.
71     #[must_use]
iter(&'a self) -> UnalignedSliceIter<'a, T>72     pub const fn iter(&'a self) -> UnalignedSliceIter<'a, T> {
73         UnalignedSliceIter {
74             slice: self,
75             index: 0,
76         }
77     }
78 
79     /// Copy the data to an aligned buffer.
80     ///
81     /// The length of `dest` must be the same as `self`.
82     ///
83     /// # Panics
84     ///
85     /// This function will panic if the two slices have different lengths.
copy_to(&self, dest: &mut [T])86     pub fn copy_to(&self, dest: &mut [T]) {
87         if dest.len() != self.len {
88             panic!(
89                 "source slice length ({}) does not match destination slice length ({})",
90                 self.len(),
91                 dest.len(),
92             );
93         }
94 
95         for (i, elem) in dest.iter_mut().enumerate() {
96             *elem = unsafe { self.data.add(i).read_unaligned() };
97         }
98     }
99 
100     /// Copy the data to an aligned [`MaybeUninit`] buffer.
101     ///
102     /// The length of `dest` must be the same as `self`.
103     ///
104     /// This function fully initializes the `dest` slice.
105     ///
106     /// # Panics
107     ///
108     /// This function will panic if the two slices have different lengths.
copy_to_maybe_uninit(&self, dest: &mut [MaybeUninit<T>])109     pub fn copy_to_maybe_uninit(&self, dest: &mut [MaybeUninit<T>]) {
110         if dest.len() != self.len {
111             panic!(
112                 "source slice length ({}) does not match destination slice length ({})",
113                 self.len(),
114                 dest.len(),
115             );
116         }
117 
118         for (i, elem) in dest.iter_mut().enumerate() {
119             unsafe { elem.write(self.data.add(i).read_unaligned()) };
120         }
121     }
122 
123     /// Copies `self` into a new `Vec`.
124     #[cfg(feature = "alloc")]
125     #[must_use]
to_vec(&self) -> Vec<T>126     pub fn to_vec(&self) -> Vec<T> {
127         let len = self.len();
128         let mut v = Vec::with_capacity(len);
129         unsafe {
130             self.copy_to_maybe_uninit(v.spare_capacity_mut());
131             v.set_len(len);
132         }
133         v
134     }
135 }
136 
137 impl<'a, T: Copy + Debug> Debug for UnalignedSlice<'a, T> {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result138     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
139         f.debug_list().entries(self.iter()).finish()
140     }
141 }
142 
143 #[cfg(feature = "alloc")]
144 impl<'a, T: Copy> From<UnalignedSlice<'a, T>> for Vec<T> {
from(input: UnalignedSlice<'a, T>) -> Self145     fn from(input: UnalignedSlice<'a, T>) -> Self {
146         input.to_vec()
147     }
148 }
149 
150 impl<'a, T: Copy> IntoIterator for UnalignedSlice<'a, T> {
151     type Item = T;
152     type IntoIter = UnalignedSliceIntoIter<'a, T>;
153 
into_iter(self) -> Self::IntoIter154     fn into_iter(self) -> Self::IntoIter {
155         UnalignedSliceIntoIter {
156             slice: self,
157             index: 0,
158         }
159     }
160 }
161 
162 impl<'a, T: Copy> IntoIterator for &'a UnalignedSlice<'a, T> {
163     type Item = T;
164     type IntoIter = UnalignedSliceIter<'a, T>;
165 
into_iter(self) -> Self::IntoIter166     fn into_iter(self) -> Self::IntoIter {
167         self.iter()
168     }
169 }
170 
171 /// Iterator for a [`UnalignedSlice`].
172 #[derive(Debug)]
173 pub struct UnalignedSliceIntoIter<'a, T: Copy> {
174     slice: UnalignedSlice<'a, T>,
175     index: usize,
176 }
177 
178 impl<'a, T: Copy> Iterator for UnalignedSliceIntoIter<'a, T> {
179     type Item = T;
180 
next(&mut self) -> Option<T>181     fn next(&mut self) -> Option<T> {
182         let output = self.slice.get(self.index)?;
183         self.index += 1;
184         Some(output)
185     }
186 }
187 
188 /// Iterator for a [`UnalignedSlice`] reference.
189 #[derive(Debug)]
190 pub struct UnalignedSliceIter<'a, T: Copy> {
191     slice: &'a UnalignedSlice<'a, T>,
192     index: usize,
193 }
194 
195 impl<'a, T: Copy> Iterator for UnalignedSliceIter<'a, T> {
196     type Item = T;
197 
next(&mut self) -> Option<T>198     fn next(&mut self) -> Option<T> {
199         let output = self.slice.get(self.index)?;
200         self.index += 1;
201         Some(output)
202     }
203 }
204 
205 #[cfg(test)]
206 mod tests {
207     use super::*;
208     use alloc::vec::Vec;
209 
210     #[test]
test_unaligned_slice()211     fn test_unaligned_slice() {
212         #[rustfmt::skip]
213         let bytes: [u8; 13] = [
214             // Extra byte to make the rest of the data unaligned.
215             0,
216             // First element.
217             0x10, 0x11, 0x12, 0x13,
218             // Second element.
219             0x20, 0x21, 0x22, 0x23,
220             // Third element.
221             0x30, 0x31, 0x32, 0x33,
222         ];
223 
224         // Skip past the first byte and create an unaligned `*const u32` pointer.
225         let bytes = &bytes[1..];
226         let slice_ptr: *const u32 = bytes.as_ptr().cast();
227 
228         let slice: UnalignedSlice<u32> = unsafe { UnalignedSlice::new(slice_ptr, 0) };
229         assert!(slice.is_empty());
230 
231         let slice: UnalignedSlice<u32> = unsafe { UnalignedSlice::new(slice_ptr, 3) };
232         assert!(!slice.is_empty());
233         assert_eq!(slice.len(), 3);
234 
235         assert_eq!(slice.get(0), Some(0x13121110));
236         assert_eq!(slice.get(1), Some(0x23222120));
237         assert_eq!(slice.get(2), Some(0x33323130));
238         assert_eq!(slice.get(3), None);
239 
240         let mut copy = [0; 3];
241         slice.copy_to(&mut copy);
242         assert_eq!(copy, [0x13121110, 0x23222120, 0x33323130]);
243 
244         assert_eq!(
245             slice.iter().collect::<Vec<_>>(),
246             [0x13121110, 0x23222120, 0x33323130]
247         );
248 
249         assert_eq!(
250             slice.into_iter().collect::<Vec<_>>(),
251             [0x13121110, 0x23222120, 0x33323130]
252         );
253     }
254 }
255