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}