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}