musli_zerocopy/pointer/
coerce.rs

1use crate::error::CoerceError;
2use crate::pointer::{CoerceSlice, Pointee, Size};
3use crate::traits::ZeroCopy;
4
5/// A trait indicating that a coercion from `Self` to `U` is correct from a size
6/// perspective.
7pub trait Coerce<U>
8where
9    Self: Pointee,
10    U: ?Sized + Pointee,
11{
12    /// Coerce metadata from `Self` to `U`.
13    ///
14    /// Any overflow will wrap around.
15    fn coerce_metadata<O>(metadata: Self::Stored<O>) -> U::Stored<O>
16    where
17        O: Size;
18
19    /// Try to coerce metadata from `Self` to `U`.
20    ///
21    /// Any overflow will result in `None`.
22    fn try_coerce_metadata<O>(metadata: Self::Stored<O>) -> Result<U::Stored<O>, CoerceError>
23    where
24        O: Size;
25}
26
27/// Defines a coercion from a slice `[T]` to `[U]`.
28///
29/// Since slices have a length which depends on the exact sizing of `T` and `U`,
30/// this conversion is constrained by a special trait [`CoerceSlice<T>`].
31impl<T, U> Coerce<[U]> for [T]
32where
33    T: ZeroCopy,
34    U: ZeroCopy,
35    [T]: CoerceSlice<[U]>,
36{
37    #[inline]
38    fn coerce_metadata<O>(metadata: O) -> O
39    where
40        O: Size,
41    {
42        <[T]>::resize(metadata)
43    }
44
45    #[inline]
46    fn try_coerce_metadata<O>(metadata: O) -> Result<O, CoerceError>
47    where
48        O: Size,
49    {
50        <[T]>::try_resize(metadata)
51    }
52}
53
54/// Defines the coercion from `str` to `[T]`.
55///
56/// Broadly speaking, this inherits the coercions which are possible for `[u8]`,
57/// which basically limits it to `u8` and `i8` and other single byte types.
58///
59/// # Examples
60///
61/// ```
62/// use musli_zerocopy::Ref;
63///
64/// let reference: Ref<str> = Ref::with_metadata(0u32, 12);
65/// let reference2 = reference.coerce::<[u8]>();
66/// let reference3 = reference.coerce::<[i8]>();
67/// assert_eq!(reference2.len(), 12);
68/// assert_eq!(reference3.len(), 12);
69/// ```
70impl<T> Coerce<[T]> for str
71where
72    [u8]: CoerceSlice<[T]>,
73    T: ZeroCopy,
74{
75    #[inline]
76    fn coerce_metadata<O: Size>(metadata: O) -> O {
77        <[u8]>::resize(metadata)
78    }
79
80    #[inline]
81    fn try_coerce_metadata<O: Size>(metadata: O) -> Result<O, CoerceError> {
82        <[u8]>::try_resize(metadata)
83    }
84}
85
86/// Defines the coercion from `[T]` to `str`.
87///
88/// Broadly speaking, this inherits the coercions which are possible into `[u8]`,
89/// which means any type can be coerced into a `str`.
90///
91/// # Examples
92///
93/// ```
94/// use musli_zerocopy::Ref;
95///
96/// let reference: Ref<[u32]> = Ref::with_metadata(0u32, 12);
97/// let reference2 = reference.coerce::<str>();
98/// assert_eq!(reference2.len(), 12 * 4);
99/// ```
100impl<T> Coerce<str> for [T]
101where
102    [T]: CoerceSlice<[u8]>,
103    T: ZeroCopy,
104{
105    #[inline]
106    fn coerce_metadata<O: Size>(metadata: O) -> O {
107        <[T]>::resize(metadata)
108    }
109
110    #[inline]
111    fn try_coerce_metadata<O: Size>(metadata: O) -> Result<O, CoerceError> {
112        <[T]>::try_resize(metadata)
113    }
114}
115
116macro_rules! same_size_inner {
117    ($from:ty, {$($to:ty),*}) => {
118        $(
119            #[doc = concat!("Defines the coercion for `", stringify!($from) ,"` to `", stringify!($to), "`.")]
120            ///
121            /// # Examples
122            ///
123            /// ```
124            /// use musli_zerocopy::Ref;
125            ///
126            #[doc = concat!("let reference: Ref<", stringify!($from), "> = Ref::zero();")]
127            #[doc = concat!("let reference2 = reference.coerce::<", stringify!($to), ">();")]
128            /// assert_eq!(reference.offset(), reference2.offset());
129            /// ```
130            impl Coerce<$to> for $from {
131                #[inline(always)]
132                fn coerce_metadata<O: Size>(metadata: ()) -> () {
133                    metadata
134                }
135
136                #[inline(always)]
137                fn try_coerce_metadata<O: Size>(metadata: ()) -> Result<(), CoerceError> {
138                    Ok(metadata)
139                }
140            }
141        )*
142    }
143}
144
145macro_rules! same_size {
146    ([$({$($from:ty),*}),*], [$($to:tt),*]) => {
147        $(
148            $(
149                same_size_inner!($from, $to);
150            )*
151        )*
152    };
153}
154
155same_size!([{u8, i8}], [{u8, i8}]);
156same_size!([{u16, i16}], [{u16, i16, [u8; 2], [i8; 2]}]);
157same_size!([{u32, i32}], [{u32, i32, [u16; 2], [i16; 2], [u8; 4], [i8; 4]}]);
158same_size!([{u64, i64}], [{u64, i64, [u32; 2], [i32; 2], [u16; 4], [i16; 4], [u8; 8], [i8; 8]}]);
159same_size!([{u128, i128}], [{u128, i128, [u64; 2], [i64; 2], [u32; 4], [i32; 4], [u16; 8], [i16; 8], [u8; 16], [i8; 16]}]);
160
161/// Defines the primitive coercion from `T` to `[U]`.
162///
163/// This coercion results in a single element slice of type `T`, and is largely
164/// defined through the help of [`CoerceSlice<T>`].
165///
166/// Note that coercing from a smaller to a larger type is not possible, since we
167/// don't know how many elements of the smaller type is in use:
168///
169/// ```compile_fail
170/// use musli_zerocopy::Ref;
171///
172/// let reference: Ref<u8> = Ref::zero();
173/// let reference2 = reference.coerce::<[u32]>();
174/// ```
175///
176/// # Examples
177///
178/// ```
179/// use musli_zerocopy::Ref;
180///
181/// let reference: Ref<u32> = Ref::zero();
182/// let reference2 = reference.coerce::<[u32]>();
183/// assert_eq!(reference2.len(), 1);
184///
185/// let reference: Ref<u64> = Ref::zero();
186/// let reference2 = reference.coerce::<[u32]>();
187/// assert_eq!(reference2.len(), 2);
188///
189/// let reference: Ref<u128> = Ref::zero();
190/// let reference2 = reference.coerce::<[u32]>();
191/// assert_eq!(reference2.len(), 4);
192/// ```
193impl<T, U> Coerce<[U]> for T
194where
195    T: ZeroCopy,
196    U: ZeroCopy,
197    [T]: CoerceSlice<[U]>,
198{
199    #[inline]
200    fn coerce_metadata<O: Size>((): ()) -> O {
201        <[T]>::resize(O::ONE)
202    }
203
204    #[inline]
205    fn try_coerce_metadata<O: Size>((): ()) -> Result<O, CoerceError> {
206        <[T]>::try_resize(O::ONE)
207    }
208}
209
210/// Defines the coercion from `[T; N]` to `[U]`.
211///
212/// This coercion results in a single element slice of type `T`, and is largely
213/// defined through the help of [`CoerceSlice<T>`].
214///
215/// # Examples
216///
217/// ```
218/// use musli_zerocopy::Ref;
219///
220/// let reference: Ref<[u32; 2]> = Ref::zero();
221/// let reference2 = reference.coerce::<[u32]>();
222/// assert_eq!(reference2.len(), 2);
223///
224/// let reference: Ref<[u128; 4]> = Ref::zero();
225/// let reference2 = reference.coerce::<[u64]>();
226/// assert_eq!(reference2.len(), 8);
227/// ```
228impl<T, const N: usize, U> Coerce<[U]> for [T; N]
229where
230    T: ZeroCopy,
231    U: ZeroCopy,
232    [T]: CoerceSlice<[U]>,
233{
234    #[inline]
235    fn coerce_metadata<O>((): ()) -> <[T] as Pointee>::Stored<O>
236    where
237        O: Size,
238    {
239        let factor = O::try_from_usize(N).unwrap_or(O::MAX);
240        <[T]>::resize(factor)
241    }
242
243    #[inline]
244    fn try_coerce_metadata<O>((): ()) -> Result<<[T] as Pointee>::Stored<O>, CoerceError>
245    where
246        O: Size,
247    {
248        let factor = O::try_from_usize(N)?;
249        <[T]>::try_resize(factor)
250    }
251}
252
253macro_rules! non_zero_inner {
254    ($from:ident, {$($to:ty),*}) => {
255        $(
256            #[doc = concat!("Defines the coercion for `", stringify!($from) ,"` to `", stringify!($to), "`.")]
257            ///
258            /// # Examples
259            ///
260            /// ```
261            #[doc = concat!("use std::num::", stringify!($from), ";")]
262            ///
263            /// use musli_zerocopy::Ref;
264            ///
265            #[doc = concat!("let reference: Ref<", stringify!($from), "> = Ref::zero();")]
266            #[doc = concat!("let reference2 = reference.coerce::<", stringify!($to), ">();")]
267            /// assert_eq!(reference.offset(), reference2.offset());
268            /// ```
269            impl Coerce<$to> for core::num::$from {
270                #[inline(always)]
271                fn coerce_metadata<O: Size>(metadata: ()) -> () {
272                    metadata
273                }
274
275                #[inline(always)]
276                fn try_coerce_metadata<O: Size>(metadata: ()) -> Result<(), CoerceError> {
277                    Ok(metadata)
278                }
279            }
280        )*
281    }
282}
283
284macro_rules! non_zero {
285    ([$({$($from:ident),*}),*], [$($to:tt),*]) => {
286        $(
287            $(
288                non_zero_inner!($from, $to);
289            )*
290        )*
291    };
292}
293
294non_zero!([{NonZeroU8, NonZeroI8}], [{u8, i8}]);
295non_zero!([{NonZeroU16, NonZeroI16}], [{u16, i16}]);
296non_zero!([{NonZeroU32, NonZeroI32}], [{u32, i32}]);
297non_zero!([{NonZeroU64, NonZeroI64}], [{u64, i64}]);
298non_zero!([{NonZeroU128, NonZeroI128}], [{u128, i128}]);
299
300/// Defines the coercion for `core::num::Wrapping<T>` to `U`.
301///
302/// This is largely defined by the [`Coerce<T>`] of `T` to `U`.
303///
304/// # Examples
305///
306/// ```
307/// use std::num::Wrapping;
308///
309/// use musli_zerocopy::Ref;
310///
311/// let reference: Ref<Wrapping<i128>> = Ref::zero();
312/// let reference2 = reference.coerce::<u128>();
313/// let reference3 = reference.coerce::<[u32; 4]>();
314/// let reference4 = reference.coerce::<u128>().coerce::<[u32]>();
315/// assert_eq!(reference4.len(), 4);
316/// ```
317impl<T, U> Coerce<U> for core::num::Wrapping<T>
318where
319    T: Coerce<U>,
320    U: ZeroCopy,
321    T: ZeroCopy,
322{
323    #[inline(always)]
324    fn coerce_metadata<O: Size>(metadata: ()) {
325        metadata
326    }
327
328    #[inline(always)]
329    fn try_coerce_metadata<O: Size>(metadata: ()) -> Result<(), CoerceError> {
330        Ok(metadata)
331    }
332}