palette/cast/
uint.rs

1use core::mem::{transmute_copy, ManuallyDrop};
2
3/// Marker trait for types that can be represented as an unsigned integer.
4///
5/// A type that implements this trait is assumed to have the exact same memory
6/// layout and representation as an unsigned integer, with the current compile
7/// target's endianness. This implies a couple of useful properties:
8///
9/// * Casting between `T` and `T::Uint` is free and will (or should) be
10///   optimized away.
11/// * `[T]` can be cast to and from `[T::Uint]`.
12///
13/// This allows a number of common and useful optimizations, including casting
14/// buffers and reusing memory. It does however come with some strict
15/// requirements.
16///
17/// ## Safety
18///
19/// * The type must be inhabited (eg: no
20///   [Infallible](std::convert::Infallible)).
21/// * The type must allow any bit pattern (eg: either no requirements or some
22///   ability to recover from invalid values).
23/// * The type must be either a wrapper around `Self::Uint` or be safe to transmute to and from `Self::Uint`.
24/// * The type must not contain any internal padding.
25/// * The type must be `repr(C)` or `repr(transparent)`.
26/// * The type must have the same size and alignment as `Self::Uint`.
27///
28/// Note also that the type is assumed to not implement `Drop`. This will
29/// rarely, if ever, be an issue. The requirements above ensures that the
30/// underlying field types stay the same and will be dropped.
31pub unsafe trait UintCast {
32    /// An unsigned integer with the same size as `Self`.
33    type Uint;
34}
35
36/// Cast from a color type to an unsigned integer.
37///
38/// ```
39/// use palette::{cast, rgb::PackedArgb, Srgba};
40///
41/// let color: PackedArgb = Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into();
42/// assert_eq!(cast::into_uint(color), 0xFF17C64C);
43/// ```
44///
45/// It's also possible to use `From` and `Into` when casting built-in types:
46///
47/// ```
48/// use palette::Srgba;
49///
50/// let color = Srgba::new(23u8, 198, 76, 255);
51///
52/// // Integers implement `Into`:
53/// let uint1: u32 = color.into();
54///
55/// // Integers implement `From`:
56/// let uint2 = u32::from(color);
57/// ```
58#[inline]
59pub fn into_uint<T>(color: T) -> T::Uint
60where
61    T: UintCast,
62{
63    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
64
65    // Safety: The requirements of implementing `UintCast`, as well as the size
66    // assert, ensures that transmuting `T` into `T::Uint` is safe.
67    unsafe { transmute_copy(&ManuallyDrop::new(color)) }
68}
69
70/// Cast from an unsigned integer to a color type.
71///
72/// ```
73/// use palette::{cast, rgb::PackedArgb, Srgba};
74///
75/// let color: PackedArgb = Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into();
76/// assert_eq!(cast::from_uint::<PackedArgb>(0xFF17C64C), color);
77/// ```
78///
79/// It's also possible to use `From` and `Into` when casting built-in types:
80///
81/// ```
82/// use palette::Srgba;
83///
84/// let uint = 0xFF17C64C;
85///
86/// // Integers implement `Into`:
87/// let color1: Srgba<u8> = uint.into();
88///
89/// // Colors implement `From`:
90/// let color2 = Srgba::from(uint);
91/// ```
92#[inline]
93pub fn from_uint<T>(uint: T::Uint) -> T
94where
95    T: UintCast,
96{
97    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
98
99    // Safety: The requirements of implementing `UintCast`, as well as the size
100    // assert, ensures that transmuting `T::Uint` into `T` is safe.
101    unsafe { transmute_copy(&ManuallyDrop::new(uint)) }
102}
103
104/// Cast from a color type reference to an unsigned integer reference.
105///
106/// ```
107/// use palette::{cast, rgb::PackedArgb, Srgba};
108///
109/// let color: PackedArgb = Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into();
110/// assert_eq!(cast::into_uint_ref(&color), &0xFF17C64C);
111/// ```
112#[inline]
113pub fn into_uint_ref<T>(value: &T) -> &T::Uint
114where
115    T: UintCast,
116{
117    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
118    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
119
120    let value: *const T = value;
121
122    // Safety: The requirements of implementing `UintCast`, as well as the size
123    // and alignment asserts, ensures that reading `T` as `T::Uint` is safe.
124    unsafe { &*value.cast::<T::Uint>() }
125}
126
127/// Cast from an unsigned integer reference to a color type reference.
128///
129/// ```
130/// use palette::{cast, rgb::PackedArgb, Srgba};
131///
132/// let color: PackedArgb = Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into();
133/// assert_eq!(cast::from_uint_ref::<PackedArgb>(&0xFF17C64C), &color);
134/// ```
135#[inline]
136pub fn from_uint_ref<T>(value: &T::Uint) -> &T
137where
138    T: UintCast,
139{
140    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
141    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
142
143    let value: *const T::Uint = value;
144
145    // Safety: The requirements of implementing `UintCast`, as well as the size
146    // and alignment asserts, ensures that reading `T::Uint` as `T` is safe.
147    unsafe { &*value.cast::<T>() }
148}
149
150/// Cast from a mutable color type reference to a mutable unsigned integer reference.
151///
152/// ```
153/// use palette::{cast, rgb::PackedArgb, Srgba};
154///
155/// let mut color: PackedArgb = Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into();
156/// assert_eq!(cast::into_uint_mut(&mut color), &mut 0xFF17C64C);
157/// ```
158#[inline]
159pub fn into_uint_mut<T>(value: &mut T) -> &mut T::Uint
160where
161    T: UintCast,
162{
163    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
164    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
165
166    let value: *mut T = value;
167
168    // Safety: The requirements of implementing `UintCast`, as well as the size
169    // and alignment asserts, ensures that reading `T` as `T::Uint` is safe.
170    unsafe { &mut *value.cast::<T::Uint>() }
171}
172
173/// Cast from a mutable unsigned integer reference to a mutable color type reference.
174///
175/// ```
176/// use palette::{cast, rgb::PackedArgb, Srgba};
177///
178/// let mut color: PackedArgb = Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into();
179/// assert_eq!(cast::from_uint_mut::<PackedArgb>(&mut 0xFF17C64C), &mut color);
180/// ```
181#[inline]
182pub fn from_uint_mut<T>(value: &mut T::Uint) -> &mut T
183where
184    T: UintCast,
185{
186    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
187    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
188
189    let value: *mut T::Uint = value;
190
191    // Safety: The requirements of implementing `UintCast`, as well as the size
192    // and alignment asserts, ensures that reading `T::Uint` as `T` is safe.
193    unsafe { &mut *value.cast::<T>() }
194}
195
196/// Cast from an array of colors to an array of unsigned integers.
197///
198/// ```
199/// use palette::{cast, rgb::PackedArgb, Srgba};
200///
201/// let colors: [PackedArgb; 2] = [
202///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
203///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
204/// ];
205/// assert_eq!(cast::into_uint_array(colors), [0xFF17C64C, 0xFF5D12D6])
206/// ```
207#[inline]
208pub fn into_uint_array<T, const N: usize>(values: [T; N]) -> [T::Uint; N]
209where
210    T: UintCast,
211{
212    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
213    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
214
215    // Safety: The requirements of implementing `UintCast`, as well as the size
216    // and alignment asserts, ensures transmuting `T` into `T::Uint` is safe.
217    // The length is the same because the size is the same.
218    unsafe { transmute_copy(&ManuallyDrop::new(values)) }
219}
220
221/// Cast from an array of unsigned integers to an array of colors.
222///
223/// ```
224/// use palette::{cast, rgb::PackedArgb, Srgba};
225///
226/// let colors: [PackedArgb; 2] = [
227///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
228///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
229/// ];
230/// assert_eq!(cast::from_uint_array::<PackedArgb, 2>([0xFF17C64C, 0xFF5D12D6]), colors)
231/// ```
232#[inline]
233pub fn from_uint_array<T, const N: usize>(values: [T::Uint; N]) -> [T; N]
234where
235    T: UintCast,
236{
237    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
238    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
239
240    // Safety: The requirements of implementing `UintCast`, as well as the size
241    // and alignment asserts, ensures transmuting `T::Uint` into `T` is safe.
242    // The length is the same because the size is the same.
243    unsafe { transmute_copy(&ManuallyDrop::new(values)) }
244}
245
246/// Cast from a slice of colors to a slice of unsigned integers.
247///
248/// ```
249/// use palette::{cast, rgb::PackedArgb, Srgba};
250///
251/// let colors: &[PackedArgb] = &[
252///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
253///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
254/// ];
255/// assert_eq!(cast::into_uint_slice(colors), &[0xFF17C64C, 0xFF5D12D6])
256/// ```
257#[inline]
258pub fn into_uint_slice<T>(values: &[T]) -> &[T::Uint]
259where
260    T: UintCast,
261{
262    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
263    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
264
265    // Safety: The requirements of implementing `UintCast`, as well as the size
266    // and alignment asserts, ensures that reading `T` as `T::Uint` is safe.
267    // The length is the same because the size is the same.
268    unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<T::Uint>(), values.len()) }
269}
270
271/// Cast from a slice of unsigned integers to a slice of colors.
272///
273/// ```
274/// use palette::{cast, rgb::PackedArgb, Srgba};
275///
276/// let colors: &[PackedArgb] = &[
277///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
278///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
279/// ];
280/// assert_eq!(cast::from_uint_slice::<PackedArgb>(&[0xFF17C64C, 0xFF5D12D6]), colors)
281/// ```
282#[inline]
283pub fn from_uint_slice<T>(values: &[T::Uint]) -> &[T]
284where
285    T: UintCast,
286{
287    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
288    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
289
290    // Safety: The requirements of implementing `UintCast`, as well as the size
291    // and alignment asserts, ensures that reading `T::Uint` as `T` is safe.
292    // The length is the same because the size is the same.
293    unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<T>(), values.len()) }
294}
295
296/// Cast from a mutable slice of colors to a mutable slice of unsigned integers.
297///
298/// ```
299/// use palette::{cast, rgb::PackedArgb, Srgba};
300///
301/// let colors: &mut [PackedArgb] = &mut [
302///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
303///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
304/// ];
305/// assert_eq!(cast::into_uint_slice_mut(colors), &mut [0xFF17C64C, 0xFF5D12D6])
306/// ```
307#[inline]
308pub fn into_uint_slice_mut<T>(values: &mut [T]) -> &mut [T::Uint]
309where
310    T: UintCast,
311{
312    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
313    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
314
315    // Safety: The requirements of implementing `UintCast`, as well as the size
316    // and alignment asserts, ensures that reading `T` as `T::Uint` is safe.
317    // The length is the same because the size is the same.
318    unsafe { core::slice::from_raw_parts_mut(values.as_mut_ptr().cast::<T::Uint>(), values.len()) }
319}
320
321/// Cast from a mutable slice of unsigned integers to a mutable slice of colors.
322///
323/// ```
324/// use palette::{cast, rgb::PackedArgb, Srgba};
325///
326/// let colors: &mut [PackedArgb] = &mut [
327///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
328///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
329/// ];
330/// assert_eq!(cast::from_uint_slice_mut::<PackedArgb>(&mut [0xFF17C64C, 0xFF5D12D6]), colors)
331/// ```
332#[inline]
333pub fn from_uint_slice_mut<T>(values: &mut [T::Uint]) -> &mut [T]
334where
335    T: UintCast,
336{
337    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
338    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
339
340    // Safety: The requirements of implementing `UintCast`, as well as the size
341    // and alignment asserts, ensures that reading `T::Uint` as `T` is safe.
342    // The length is the same because the size is the same.
343    unsafe { core::slice::from_raw_parts_mut(values.as_mut_ptr().cast::<T>(), values.len()) }
344}
345
346/// Cast from a boxed slice of colors to a boxed slice of unsigned integers.
347///
348/// ```
349/// use palette::{cast, rgb::PackedArgb, Srgba};
350///
351/// let colors: Box<[PackedArgb]> = vec![
352///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
353///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
354/// ].into_boxed_slice();
355///
356/// assert_eq!(
357///     cast::into_uint_slice_box(colors),
358///     vec![0xFF17C64C, 0xFF5D12D6].into_boxed_slice()
359/// )
360/// ```
361#[cfg(feature = "alloc")]
362#[inline]
363pub fn into_uint_slice_box<T>(values: alloc::boxed::Box<[T]>) -> alloc::boxed::Box<[T::Uint]>
364where
365    T: UintCast,
366{
367    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
368    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
369
370    let raw: *mut [T::Uint] = into_uint_slice_mut(alloc::boxed::Box::leak(values));
371
372    // Safety: The requirements of implementing `UintCast`, as well as the size
373    // and alignment asserts, ensures that reading `T` as `T::Uint` is safe.
374    unsafe { alloc::boxed::Box::from_raw(raw) }
375}
376
377/// Cast from a boxed slice of unsigned integers to a boxed slice of colors.
378///
379/// ```
380/// use palette::{cast, rgb::PackedArgb, Srgba};
381///
382/// let colors: Box<[PackedArgb]> = vec![
383///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
384///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
385/// ].into_boxed_slice();
386///
387/// assert_eq!(
388///     cast::from_uint_slice_box(vec![0xFF17C64C, 0xFF5D12D6].into_boxed_slice()),
389///     colors
390/// )
391/// ```
392#[cfg(feature = "alloc")]
393#[inline]
394pub fn from_uint_slice_box<T>(values: alloc::boxed::Box<[T::Uint]>) -> alloc::boxed::Box<[T]>
395where
396    T: UintCast,
397{
398    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
399    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
400
401    let raw: *mut [T] = from_uint_slice_mut(alloc::boxed::Box::leak(values));
402
403    // Safety: The requirements of implementing `UintCast`, as well as the size
404    // and alignment asserts, ensures that reading `T::Uint` as `T` is safe.
405    unsafe { alloc::boxed::Box::from_raw(raw) }
406}
407
408/// Cast from a `Vec` of colors to a `Vec` of unsigned integers.
409///
410/// ```
411/// use palette::{cast, rgb::PackedArgb, Srgba};
412///
413/// let colors: Vec<PackedArgb> = vec![
414///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
415///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
416/// ];
417///
418/// assert_eq!(
419///     cast::into_uint_vec(colors),
420///     vec![0xFF17C64C, 0xFF5D12D6]
421/// )
422/// ```
423#[cfg(feature = "alloc")]
424#[inline]
425pub fn into_uint_vec<T>(values: alloc::vec::Vec<T>) -> alloc::vec::Vec<T::Uint>
426where
427    T: UintCast,
428{
429    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
430    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
431    let mut values = ManuallyDrop::new(values);
432
433    let raw = values.as_mut_ptr();
434
435    // Safety: The requirements of implementing `UintCast`, as well as the size
436    // and alignment asserts, ensures that reading `T` as `T::Uint` is safe.
437    // Length and capacity are the same because the size is the same.
438    unsafe {
439        alloc::vec::Vec::from_raw_parts(raw.cast::<T::Uint>(), values.len(), values.capacity())
440    }
441}
442
443/// Cast from a `Vec` of unsigned integers to a `Vec` of colors.
444///
445/// ```
446/// use palette::{cast, rgb::PackedArgb, Srgba};
447///
448/// let colors: Vec<PackedArgb> = vec![
449///     Srgba::new(0x17, 0xC6, 0x4C, 0xFF).into(),
450///     Srgba::new(0x5D, 0x12, 0xD6, 0xFF).into()
451/// ];
452///
453/// assert_eq!(
454///     cast::from_uint_vec::<PackedArgb>(vec![0xFF17C64C, 0xFF5D12D6]),
455///     colors
456/// )
457/// ```
458#[cfg(feature = "alloc")]
459#[inline]
460pub fn from_uint_vec<T>(values: alloc::vec::Vec<T::Uint>) -> alloc::vec::Vec<T>
461where
462    T: UintCast,
463{
464    assert_eq!(core::mem::size_of::<T::Uint>(), core::mem::size_of::<T>());
465    assert_eq!(core::mem::align_of::<T::Uint>(), core::mem::align_of::<T>());
466    let mut values = ManuallyDrop::new(values);
467
468    let raw = values.as_mut_ptr();
469
470    // Safety: The requirements of implementing `UintCast`, as well as the size
471    // and alignment asserts, ensures that reading `T::Uint` as `T` is safe.
472    // Length and capacity are the same because the size is the same.
473    unsafe { alloc::vec::Vec::from_raw_parts(raw.cast::<T>(), values.len(), values.capacity()) }
474}