musli_zerocopy/slice/packed.rs
1use core::marker::PhantomData;
2use core::mem::size_of;
3
4use crate::buf::{Buf, Load};
5use crate::endian::{ByteOrder, DefaultEndian, Native};
6use crate::error::{CoerceError, CoerceErrorKind, Error};
7use crate::pointer::{Pointee, Ref, Size};
8use crate::slice::Slice;
9use crate::{DefaultSize, ZeroCopy};
10
11/// A packed slice representation that uses exactly `O` and `L` for offset and
12/// length respectively.
13///
14/// This is functionally equivalent to a [`Ref<[T]>`], but pointer and metadata
15/// `L` does not have to be the same size and its representation is packed.
16///
17/// ```
18/// use core::mem::{size_of, align_of};
19///
20/// use musli_zerocopy::slice::Packed;
21/// use musli_zerocopy::{DefaultSize, DefaultEndian, Ref};
22///
23/// assert_eq!(size_of::<Packed<[u32], DefaultEndian, u32, u8>>(), 5);
24/// assert_eq!(align_of::<Packed<[u32], DefaultEndian, u32, u8>>(), 1);
25///
26/// assert_eq!(size_of::<Ref<[u32]>>(), size_of::<DefaultSize>() * 2);
27/// assert_eq!(align_of::<Ref<[u32]>>(), align_of::<DefaultSize>());
28/// ```
29///
30/// Since this implements [`Slice<T>`] it can be used to build collection
31/// flavors like [`trie::Flavor`].
32///
33/// [`trie::Flavor`]: crate::trie::Flavor
34#[derive(ZeroCopy)]
35#[zero_copy(crate, bounds = {O: ZeroCopy, L: ZeroCopy})]
36#[repr(C, packed)]
37pub struct Packed<T, E = DefaultEndian, O = DefaultSize, L = DefaultSize>
38where
39 T: ?Sized,
40 E: ByteOrder,
41 O: Size,
42 L: Size,
43{
44 offset: O,
45 len: L,
46 #[zero_copy(ignore)]
47 _marker: PhantomData<(E, T)>,
48}
49
50impl<T, E, O, L> Slice<T> for Packed<[T], E, O, L>
51where
52 T: ZeroCopy,
53 O: Size + TryFrom<usize>,
54 L: Size + TryFrom<usize>,
55 E: ByteOrder,
56{
57 type ItemRef = Ref<T, E, usize>;
58
59 #[inline]
60 fn try_from_ref<A, B>(slice: Ref<[T], A, B>) -> Result<Self, CoerceError>
61 where
62 A: ByteOrder,
63 B: Size,
64 {
65 Self::try_with_metadata(slice.offset(), slice.len())
66 }
67
68 #[inline]
69 fn try_with_metadata(offset: usize, len: usize) -> Result<Self, CoerceError> {
70 Packed::try_from_raw_parts(offset, len)
71 }
72
73 #[inline]
74 fn get(self, index: usize) -> Option<Self::ItemRef> {
75 Packed::get(self, index)
76 }
77
78 #[inline]
79 fn split_at(self, at: usize) -> Result<(Self, Self), CoerceError> {
80 Packed::split_at(self, at)
81 }
82
83 #[inline]
84 fn get_unchecked(self, index: usize) -> Self::ItemRef {
85 Packed::get_unchecked(self, index)
86 }
87
88 #[inline]
89 fn offset(self) -> usize {
90 Packed::offset(self)
91 }
92
93 #[inline]
94 fn len(self) -> usize {
95 Packed::len(self)
96 }
97
98 #[inline]
99 fn is_empty(self) -> bool {
100 Packed::is_empty(self)
101 }
102}
103
104impl<T, E, O, L> Packed<[T], E, O, L>
105where
106 T: ZeroCopy,
107 O: Size,
108 L: Size,
109 E: ByteOrder,
110{
111 /// Construct a packed slice from a reference.
112 #[inline]
113 pub fn from_ref<A, B>(slice: Ref<[T], A, B>) -> Self
114 where
115 T: ZeroCopy,
116 A: ByteOrder,
117 B: Size,
118 {
119 Self::from_raw_parts(slice.offset(), slice.len())
120 }
121
122 /// Construct a packed slice from its raw parts.
123 ///
124 /// # Panics
125 ///
126 /// This panics in case any components in the path overflow its
127 /// representation.
128 #[inline]
129 pub fn from_raw_parts(offset: usize, len: usize) -> Self {
130 match Self::try_from_raw_parts(offset, len) {
131 Ok(slice) => slice,
132 Err(error) => panic!("{error}"),
133 }
134 }
135
136 /// Try to construct a packed slice from its raw parts.
137 ///
138 /// # Errors
139 ///
140 /// This errors in case any components in the path overflow its
141 /// representation.
142 ///
143 /// # Examples
144 ///
145 /// ```
146 /// use musli_zerocopy::DefaultEndian;
147 /// use musli_zerocopy::slice::Packed;
148 ///
149 /// let slice = Packed::<[u32], DefaultEndian, u32, u8>::try_from_raw_parts(42, 2)?;
150 /// assert_eq!(slice.offset(), 42);
151 ///
152 /// assert!(Packed::<[u32], DefaultEndian, u32, u8>::try_from_raw_parts(42, usize::MAX).is_err());
153 /// # Ok::<_, musli_zerocopy::Error>(())
154 /// ```
155 #[inline]
156 pub fn try_from_raw_parts(offset: usize, len: usize) -> Result<Self, CoerceError> {
157 <[T]>::check_layout(offset, len)?;
158
159 Ok(Self {
160 offset: O::try_from(offset)?.swap_bytes::<E>(),
161 len: L::try_from(len)?.swap_bytes::<E>(),
162 _marker: PhantomData,
163 })
164 }
165
166 /// Try to get a reference directly out of the slice without validation.
167 ///
168 /// This avoids having to validate every element in a slice in order to
169 /// address them.
170 ///
171 /// # Examples
172 ///
173 /// ```
174 /// use musli_zerocopy::OwnedBuf;
175 /// use musli_zerocopy::slice::Packed;
176 ///
177 /// let mut buf = OwnedBuf::new();
178 ///
179 /// let slice: Packed<[i32]> = Packed::from_ref(buf.store_slice(&[1, 2, 3, 4])?);
180 ///
181 /// let two = slice.get(2).expect("Missing element 2");
182 /// assert_eq!(buf.load(two)?, &3);
183 ///
184 /// assert!(slice.get(4).is_none());
185 /// # Ok::<_, musli_zerocopy::Error>(())
186 /// ```
187 #[inline]
188 pub fn get(self, index: usize) -> Option<Ref<T, E, usize>> {
189 if index >= self.len() {
190 return None;
191 }
192
193 Some(self.get_unchecked(index))
194 }
195
196 /// Split the slice reference at the given position `at`.
197 ///
198 /// # Panics
199 ///
200 /// This panics if the given range is out of bounds.
201 ///
202 /// # Examples
203 ///
204 /// ```
205 /// use musli_zerocopy::OwnedBuf;
206 /// use musli_zerocopy::slice::Packed;
207 ///
208 /// let mut buf = OwnedBuf::new();
209 ///
210 /// let slice: Packed<[i32]> = Packed::from_ref(buf.store_slice(&[1, 2, 3, 4])?);
211 ///
212 /// buf.align_in_place()?;
213 ///
214 /// let (a, b) = slice.split_at(3)?;
215 /// let (c, d) = slice.split_at(4)?;
216 ///
217 /// assert_eq!(buf.load(a)?, &[1, 2, 3]);
218 /// assert_eq!(buf.load(b)?, &[4]);
219 /// assert_eq!(buf.load(c)?, &[1, 2, 3, 4]);
220 /// assert_eq!(buf.load(d)?, &[]);
221 /// # Ok::<_, musli_zerocopy::Error>(())
222 /// ```
223 #[inline]
224 pub fn split_at(self, at: usize) -> Result<(Self, Self), CoerceError> {
225 let offset = self.offset.swap_bytes::<E>().as_usize();
226 let len = self.len.swap_bytes::<E>().as_usize();
227
228 if at > len {
229 return Err(CoerceError::new(CoerceErrorKind::SplitAt { at, len }));
230 }
231
232 let a = Self::try_from_raw_parts(offset, at)?;
233 let b = Self::try_from_raw_parts(offset + at * size_of::<T>(), len - at)?;
234 Ok((a, b))
235 }
236
237 /// Get an unchecked reference directly out of the slice without validation.
238 ///
239 /// This avoids having to validate every element in a slice in order to
240 /// address them.
241 ///
242 /// In contrast to [`get()`], this does not check that the index is within
243 /// the bounds of the current slice, all though it's not unsafe since it
244 /// cannot lead to anything inherently unsafe. Only garbled data.
245 ///
246 /// [`get()`]: Packed::get
247 ///
248 /// # Examples
249 ///
250 /// ```
251 /// use musli_zerocopy::OwnedBuf;
252 /// use musli_zerocopy::slice::Packed;
253 ///
254 /// let mut buf = OwnedBuf::new();
255 ///
256 /// let slice: Packed<[i32]> = Packed::from_ref(buf.store_slice(&[1, 2, 3, 4])?);
257 ///
258 /// let two = slice.get_unchecked(2);
259 /// assert_eq!(buf.load(two)?, &3);
260 ///
261 /// let oob = slice.get_unchecked(4);
262 /// assert!(buf.load(oob).is_err());
263 /// # Ok::<_, musli_zerocopy::Error>(())
264 /// ```
265 #[inline]
266 pub fn get_unchecked(self, index: usize) -> Ref<T, E, usize> {
267 let offset = self.offset.swap_bytes::<E>().as_usize() + size_of::<T>() * index;
268 Ref::new(offset)
269 }
270
271 /// Get the offset the packed slice points to.
272 ///
273 /// # Examples
274 ///
275 /// ```
276 /// use musli_zerocopy::DefaultEndian;
277 /// use musli_zerocopy::slice::Packed;
278 ///
279 /// let slice = Packed::<[u32], DefaultEndian, u32, u8>::from_raw_parts(42, 2);
280 /// assert_eq!(slice.offset(), 42);
281 /// ```
282 pub fn offset(self) -> usize {
283 self.offset.swap_bytes::<E>().as_usize()
284 }
285
286 /// Return the number of elements in the packed slice.
287 ///
288 /// # Examples
289 ///
290 /// ```
291 /// use musli_zerocopy::DefaultEndian;
292 /// use musli_zerocopy::slice::Packed;
293 ///
294 /// let slice = Packed::<[u32], DefaultEndian, u32, u8>::from_raw_parts(0, 2);
295 /// assert_eq!(slice.len(), 2);
296 /// ```
297 #[inline]
298 pub fn len(self) -> usize {
299 self.len.swap_bytes::<E>().as_usize()
300 }
301
302 /// Test if the packed slice is empty.
303 ///
304 /// # Examples
305 ///
306 /// ```
307 /// use musli_zerocopy::DefaultEndian;
308 /// use musli_zerocopy::slice::Packed;
309 ///
310 /// let slice = Packed::<[u32], DefaultEndian, u32, u8>::from_raw_parts(0, 0);
311 /// assert!(slice.is_empty());
312 ///
313 /// let slice = Packed::<[u32], DefaultEndian, u32, u8>::from_raw_parts(0, 2);
314 /// assert!(!slice.is_empty());
315 /// ```
316 #[inline]
317 pub fn is_empty(self) -> bool {
318 self.len.is_zero()
319 }
320}
321
322impl<T, E, O, L> Load for Packed<[T], E, O, L>
323where
324 T: ZeroCopy,
325 O: Size,
326 L: Size,
327 E: ByteOrder,
328{
329 type Target = [T];
330
331 #[inline]
332 fn load<'buf>(&self, buf: &'buf Buf) -> Result<&'buf Self::Target, Error> {
333 // SAFETY: We ensure the same invariants as Ref<[T]> through
334 // construction.
335 let r = unsafe {
336 Ref::<[T], Native, usize>::new_unchecked(
337 self.offset.swap_bytes::<E>().as_usize(),
338 self.len.swap_bytes::<E>().as_usize(),
339 )
340 };
341
342 buf.load(r)
343 }
344}
345
346impl<T, E, O, L> Clone for Packed<[T], E, O, L>
347where
348 O: Size,
349 L: Size,
350 E: ByteOrder,
351{
352 #[inline]
353 fn clone(&self) -> Self {
354 *self
355 }
356}
357
358impl<T, E, O, L> Copy for Packed<[T], E, O, L>
359where
360 O: Size,
361 L: Size,
362 E: ByteOrder,
363{
364}