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}