Skip to main content

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}