musli_zerocopy/slice/
packed.rs

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