musli_zerocopy/pointer/
coerce.rs

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