Skip to main content

musli_zerocopy/pointer/
pointee.rs

1use core::alloc::{Layout, LayoutError};
2use core::mem::size_of;
3
4use crate::error::{CoerceError, CoerceErrorKind};
5use crate::pointer::Size;
6use crate::traits::ZeroCopy;
7
8mod sealed {
9    use crate::mem::PackedMaybeUninit;
10    use crate::pointer::Pointee;
11    use crate::traits::ZeroCopy;
12
13    pub trait Sealed {}
14
15    impl<T> Sealed for PackedMaybeUninit<T> where T: Pointee {}
16    impl<T> Sealed for T where T: ZeroCopy {}
17    impl<T> Sealed for [T] where T: ZeroCopy {}
18    impl Sealed for str {}
19}
20
21/// The trait for a value that can be pointed to by a [`Ref<T>`].
22///
23/// This ultimately determines the layout of [`Ref<T>`] as for unsized types it
24/// needs to accommodate the size of the pointed-to type as well.
25///
26/// ```
27/// use std::mem::size_of;
28///
29/// use musli_zerocopy::Ref;
30///
31/// assert_eq!(size_of::<Ref::<u32>>(), 4);
32/// assert_eq!(size_of::<Ref::<[u32]>>(), 8);
33/// ```
34///
35/// [`Ref<T>`]: crate::Ref
36pub trait Pointee
37where
38    Self: self::sealed::Sealed,
39{
40    /// Metadata associated with a pointee.
41    #[doc(hidden)]
42    type Metadata: Copy;
43
44    /// The stored representation of the pointee metadata.
45    #[doc(hidden)]
46    type Stored<O>: Copy + ZeroCopy
47    where
48        O: Size;
49
50    /// Try to construct packed value from its metadata.
51    #[doc(hidden)]
52    fn try_from_metadata<O>(metadata: Self::Metadata) -> Result<Self::Stored<O>, CoerceError>
53    where
54        O: Size;
55
56    /// Convert packed metadata to metadata.
57    #[doc(hidden)]
58    fn to_metadata<O>(stored: Self::Stored<O>) -> Self::Metadata
59    where
60        O: Size;
61
62    /// The size of `T` with the given stored metadata.
63    #[doc(hidden)]
64    fn size(metadata: Self::Metadata) -> Option<usize>;
65
66    /// The alignment of `T` with the given stored metadata.
67    #[doc(hidden)]
68    fn align(metadata: Self::Metadata) -> usize;
69
70    /// The layout of `T` with the given stored metadata.
71    #[doc(hidden)]
72    fn pointee_layout(metadata: Self::Metadata) -> Result<Layout, LayoutError>;
73
74    #[inline(always)]
75    #[doc(hidden)]
76    fn check_layout(offset: usize, metadata: Self::Metadata) -> Result<(), CoerceError> {
77        let Ok(layout) = Self::pointee_layout(metadata) else {
78            return Err(CoerceError::new(CoerceErrorKind::InvalidLayout {
79                size: Self::size(metadata),
80                align: Self::align(metadata),
81            }));
82        };
83
84        if offset.checked_add(layout.size()).is_none() {
85            return Err(CoerceError::new(CoerceErrorKind::InvalidOffsetRange {
86                offset,
87                end: usize::MAX - layout.size(),
88            }));
89        };
90
91        Ok(())
92    }
93}
94
95impl<T> Pointee for T
96where
97    T: ZeroCopy,
98{
99    type Metadata = ();
100    type Stored<O>
101        = ()
102    where
103        O: Size;
104
105    #[inline(always)]
106    fn try_from_metadata<O>((): ()) -> Result<Self::Stored<O>, CoerceError>
107    where
108        O: Size,
109    {
110        Ok(())
111    }
112
113    #[inline(always)]
114    fn to_metadata<O>((): ()) -> Self::Metadata
115    where
116        O: Size,
117    {
118    }
119
120    #[inline(always)]
121    fn size((): Self::Metadata) -> Option<usize> {
122        Some(size_of::<T>())
123    }
124
125    #[inline(always)]
126    fn align((): Self::Metadata) -> usize {
127        align_of::<T>()
128    }
129
130    #[inline(always)]
131    fn pointee_layout((): Self::Metadata) -> Result<Layout, LayoutError> {
132        Ok(Layout::new::<T>())
133    }
134}
135
136impl<T> Pointee for [T]
137where
138    T: ZeroCopy,
139{
140    type Metadata = usize;
141    type Stored<O>
142        = O
143    where
144        O: Size;
145
146    #[inline(always)]
147    fn try_from_metadata<O>(metadata: usize) -> Result<O, CoerceError>
148    where
149        O: Size,
150    {
151        O::try_from_usize(metadata)
152    }
153
154    #[inline(always)]
155    fn to_metadata<O>(metadata: Self::Stored<O>) -> Self::Metadata
156    where
157        O: Size,
158    {
159        metadata.as_usize()
160    }
161
162    #[inline(always)]
163    fn size(metadata: Self::Metadata) -> Option<usize> {
164        size_of::<T>().checked_mul(metadata)
165    }
166
167    #[inline(always)]
168    fn align(_: Self::Metadata) -> usize {
169        align_of::<T>()
170    }
171
172    #[inline(always)]
173    fn pointee_layout(metadata: Self::Metadata) -> Result<Layout, LayoutError> {
174        Layout::array::<T>(metadata)
175    }
176}
177
178impl Pointee for str {
179    type Metadata = usize;
180    type Stored<O>
181        = O
182    where
183        O: Size;
184
185    #[inline(always)]
186    fn try_from_metadata<O>(metadata: usize) -> Result<O, CoerceError>
187    where
188        O: Size,
189    {
190        O::try_from_usize(metadata)
191    }
192
193    #[inline(always)]
194    fn to_metadata<O>(metadata: Self::Stored<O>) -> Self::Metadata
195    where
196        O: Size,
197    {
198        metadata.as_usize()
199    }
200
201    #[inline(always)]
202    fn size(metadata: Self::Metadata) -> Option<usize> {
203        Some(metadata.as_usize())
204    }
205
206    #[inline(always)]
207    fn align(_: Self::Metadata) -> usize {
208        align_of::<u8>()
209    }
210
211    #[inline(always)]
212    fn pointee_layout(metadata: Self::Metadata) -> Result<Layout, LayoutError> {
213        Layout::array::<u8>(metadata)
214    }
215}