use core::mem::{transmute_copy, ManuallyDrop};
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::{boxed::Box, vec::Vec};
pub use palette_derive::ArrayCast;
use crate::ArrayExt;
pub unsafe trait ArrayCast: Sized {
type Array: ArrayExt;
}
#[inline]
pub fn into_array<T>(color: T) -> T::Array
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
unsafe { transmute_copy(&ManuallyDrop::new(color)) }
}
#[inline]
pub fn from_array<T>(array: T::Array) -> T
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
unsafe { transmute_copy(&ManuallyDrop::new(array)) }
}
#[inline]
pub fn into_array_ref<T>(value: &T) -> &T::Array
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let value: *const T = value;
unsafe { &*value.cast::<T::Array>() }
}
#[inline]
pub fn from_array_ref<T>(value: &T::Array) -> &T
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let value: *const T::Array = value;
unsafe { &*value.cast::<T>() }
}
#[inline]
pub fn into_array_mut<T>(value: &mut T) -> &mut T::Array
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let value: *mut T = value;
unsafe { &mut *value.cast::<T::Array>() }
}
#[inline]
pub fn from_array_mut<T>(value: &mut T::Array) -> &mut T
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let value: *mut T::Array = value;
unsafe { &mut *value.cast::<T>() }
}
#[inline]
pub fn into_array_array<T, const N: usize>(values: [T; N]) -> [T::Array; N]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
unsafe { transmute_copy(&ManuallyDrop::new(values)) }
}
#[inline]
pub fn into_component_array<T, const N: usize, const M: usize>(
values: [T; N],
) -> [<T::Array as ArrayExt>::Item; M]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
assert_eq!(
N * T::Array::LENGTH,
M,
"expected the output array to have length {}, but its length is {}",
N * T::Array::LENGTH,
M
);
assert_eq!(
core::mem::size_of::<[T; N]>(),
core::mem::size_of::<[<T::Array as ArrayExt>::Item; M]>()
);
assert_eq!(
core::mem::align_of::<[T; N]>(),
core::mem::align_of::<[<T::Array as ArrayExt>::Item; M]>()
);
unsafe { transmute_copy(&ManuallyDrop::new(values)) }
}
#[inline]
pub fn from_array_array<T, const N: usize>(values: [T::Array; N]) -> [T; N]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
unsafe { transmute_copy(&ManuallyDrop::new(values)) }
}
#[inline]
pub fn from_component_array<T, const N: usize, const M: usize>(
values: [<T::Array as ArrayExt>::Item; N],
) -> [T; M]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
assert_eq!(
N % T::Array::LENGTH,
0,
"expected the array length ({}) to be divisible by {}",
N,
T::Array::LENGTH
);
assert_eq!(
N / T::Array::LENGTH,
M,
"expected the output array to have length {}, but its length is {}",
N / T::Array::LENGTH,
M
);
assert_eq!(
core::mem::size_of::<[<T::Array as ArrayExt>::Item; N]>(),
core::mem::size_of::<[T; M]>()
);
assert_eq!(
core::mem::align_of::<[<T::Array as ArrayExt>::Item; N]>(),
core::mem::align_of::<[T; M]>()
);
unsafe { transmute_copy(&ManuallyDrop::new(values)) }
}
#[inline]
pub fn into_array_slice<T>(values: &[T]) -> &[T::Array]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<T::Array>(), values.len()) }
}
#[inline]
pub fn into_component_slice<T>(values: &[T]) -> &[<T::Array as ArrayExt>::Item]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let length = values.len() * T::Array::LENGTH;
unsafe {
core::slice::from_raw_parts(
values.as_ptr().cast::<<T::Array as ArrayExt>::Item>(),
length,
)
}
}
#[inline]
pub fn from_array_slice<T>(values: &[T::Array]) -> &[T]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<T>(), values.len()) }
}
#[inline]
pub fn from_component_slice<T>(values: &[<T::Array as ArrayExt>::Item]) -> &[T]
where
T: ArrayCast,
{
try_from_component_slice(values).unwrap()
}
#[inline]
pub fn try_from_component_slice<T>(
values: &[<T::Array as ArrayExt>::Item],
) -> Result<&[T], SliceCastError>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
if values.len() % T::Array::LENGTH != 0 {
return Err(SliceCastError);
}
let length = values.len() / T::Array::LENGTH;
let raw = values.as_ptr().cast::<T>();
unsafe { Ok(core::slice::from_raw_parts(raw, length)) }
}
#[inline]
pub fn into_array_slice_mut<T>(values: &mut [T]) -> &mut [T::Array]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
unsafe { core::slice::from_raw_parts_mut(values.as_mut_ptr().cast::<T::Array>(), values.len()) }
}
#[inline]
pub fn into_component_slice_mut<T>(values: &mut [T]) -> &mut [<T::Array as ArrayExt>::Item]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let length = values.len() * T::Array::LENGTH;
unsafe {
core::slice::from_raw_parts_mut(
values.as_mut_ptr().cast::<<T::Array as ArrayExt>::Item>(),
length,
)
}
}
#[inline]
pub fn from_array_slice_mut<T>(values: &mut [T::Array]) -> &mut [T]
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
unsafe { core::slice::from_raw_parts_mut(values.as_mut_ptr().cast::<T>(), values.len()) }
}
#[inline]
pub fn from_component_slice_mut<T>(values: &mut [<T::Array as ArrayExt>::Item]) -> &mut [T]
where
T: ArrayCast,
{
try_from_component_slice_mut(values).unwrap()
}
#[inline]
pub fn try_from_component_slice_mut<T>(
values: &mut [<T::Array as ArrayExt>::Item],
) -> Result<&mut [T], SliceCastError>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
if values.len() % T::Array::LENGTH != 0 {
return Err(SliceCastError);
}
let length = values.len() / T::Array::LENGTH;
let raw = values.as_mut_ptr().cast::<T>();
unsafe { Ok(core::slice::from_raw_parts_mut(raw, length)) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn into_array_box<T>(value: Box<T>) -> Box<T::Array>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let raw = Box::into_raw(value);
unsafe { Box::from_raw(raw.cast::<T::Array>()) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn from_array_box<T>(value: Box<T::Array>) -> Box<T>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let raw = Box::into_raw(value);
unsafe { Box::from_raw(raw.cast::<T>()) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn into_array_slice_box<T>(values: Box<[T]>) -> Box<[T::Array]>
where
T: ArrayCast,
{
let raw: *mut [T::Array] = into_array_slice_mut(Box::leak(values));
unsafe { Box::from_raw(raw) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn into_component_slice_box<T>(values: Box<[T]>) -> Box<[<T::Array as ArrayExt>::Item]>
where
T: ArrayCast,
{
let raw: *mut [<T::Array as ArrayExt>::Item] = into_component_slice_mut(Box::leak(values));
unsafe { Box::from_raw(raw) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn from_array_slice_box<T>(values: Box<[T::Array]>) -> Box<[T]>
where
T: ArrayCast,
{
let raw: *mut [T] = from_array_slice_mut(Box::leak(values));
unsafe { Box::from_raw(raw) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn from_component_slice_box<T>(values: Box<[<T::Array as ArrayExt>::Item]>) -> Box<[T]>
where
T: ArrayCast,
{
try_from_component_slice_box(values).unwrap()
}
#[cfg(feature = "alloc")]
#[inline]
pub fn try_from_component_slice_box<T>(
values: Box<[<T::Array as ArrayExt>::Item]>,
) -> Result<Box<[T]>, BoxedSliceCastError<<T::Array as ArrayExt>::Item>>
where
T: ArrayCast,
{
if values.len() % T::Array::LENGTH != 0 {
return Err(BoxedSliceCastError { values });
}
let raw: *mut [T] = from_component_slice_mut(Box::leak(values));
unsafe { Ok(Box::from_raw(raw)) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn into_array_vec<T>(values: Vec<T>) -> Vec<T::Array>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let mut values = ManuallyDrop::new(values);
let raw = values.as_mut_ptr();
unsafe { Vec::from_raw_parts(raw.cast::<T::Array>(), values.len(), values.capacity()) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn into_component_vec<T>(values: Vec<T>) -> Vec<<T::Array as ArrayExt>::Item>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let mut values = ManuallyDrop::new(values);
let raw = values.as_mut_ptr();
let length = values.len() * T::Array::LENGTH;
let capacity = values.capacity() * T::Array::LENGTH;
unsafe { Vec::from_raw_parts(raw.cast::<<T::Array as ArrayExt>::Item>(), length, capacity) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn from_array_vec<T>(values: Vec<T::Array>) -> Vec<T>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
let mut values = ManuallyDrop::new(values);
let raw = values.as_mut_ptr();
unsafe { Vec::from_raw_parts(raw.cast::<T>(), values.len(), values.capacity()) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn from_component_vec<T>(values: Vec<<T::Array as ArrayExt>::Item>) -> Vec<T>
where
T: ArrayCast,
{
try_from_component_vec(values).unwrap()
}
#[cfg(feature = "alloc")]
#[inline]
pub fn try_from_component_vec<T>(
values: Vec<<T::Array as ArrayExt>::Item>,
) -> Result<Vec<T>, VecCastError<<T::Array as ArrayExt>::Item>>
where
T: ArrayCast,
{
assert_eq!(core::mem::size_of::<T::Array>(), core::mem::size_of::<T>());
assert_eq!(
core::mem::align_of::<T::Array>(),
core::mem::align_of::<T>()
);
if values.len() % T::Array::LENGTH != 0 {
return Err(VecCastError {
values,
kind: VecCastErrorKind::LengthMismatch,
});
}
if values.capacity() % T::Array::LENGTH != 0 {
return Err(VecCastError {
values,
kind: VecCastErrorKind::CapacityMismatch,
});
}
let mut values = ManuallyDrop::new(values);
let raw = values.as_mut_ptr();
let length = values.len() / T::Array::LENGTH;
let capacity = values.capacity() / T::Array::LENGTH;
unsafe { Ok(Vec::from_raw_parts(raw.cast::<T>(), length, capacity)) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn map_vec_in_place<A, B, F>(values: Vec<A>, mut map: F) -> Vec<B>
where
A: ArrayCast,
B: ArrayCast<Array = A::Array>,
F: FnMut(A) -> B,
{
assert_eq!(core::mem::size_of::<B::Array>(), core::mem::size_of::<B>());
assert_eq!(
core::mem::align_of::<B::Array>(),
core::mem::align_of::<B>()
);
let mut values = ManuallyDrop::new(into_array_vec(values));
for item in &mut *values {
let input = unsafe { core::ptr::read(item) };
let output = into_array::<B>(map(from_array::<A>(input)));
unsafe { core::ptr::write(item, output) };
}
from_array_vec(ManuallyDrop::into_inner(values))
}
#[cfg(feature = "alloc")]
#[inline]
pub fn map_slice_box_in_place<A, B, F>(values: Box<[A]>, mut map: F) -> Box<[B]>
where
A: ArrayCast,
B: ArrayCast<Array = A::Array>,
F: FnMut(A) -> B,
{
assert_eq!(core::mem::size_of::<B::Array>(), core::mem::size_of::<B>());
assert_eq!(
core::mem::align_of::<B::Array>(),
core::mem::align_of::<B>()
);
let mut values = ManuallyDrop::new(into_array_slice_box(values));
for item in &mut **values {
let input = unsafe { core::ptr::read(item) };
let output = into_array::<B>(map(from_array::<A>(input)));
unsafe { core::ptr::write(item, output) };
}
from_array_slice_box(ManuallyDrop::into_inner(values))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SliceCastError;
impl core::fmt::Display for SliceCastError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("could not convert component slice to colors")
}
}
#[cfg(feature = "std")]
impl std::error::Error for SliceCastError {}
#[derive(Clone, PartialEq, Eq)]
#[cfg(feature = "alloc")]
pub struct BoxedSliceCastError<T> {
pub values: Box<[T]>,
}
#[cfg(feature = "alloc")]
impl<T> core::fmt::Debug for BoxedSliceCastError<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BoxedSliceCastError").finish()
}
}
#[cfg(feature = "alloc")]
impl<T> core::fmt::Display for BoxedSliceCastError<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("could not convert boxed component slice to colors")
}
}
#[cfg(feature = "std")]
impl<T> std::error::Error for BoxedSliceCastError<T> {}
#[derive(Clone, PartialEq, Eq)]
#[cfg(feature = "alloc")]
pub struct VecCastError<T> {
pub kind: VecCastErrorKind,
pub values: Vec<T>,
}
#[cfg(feature = "alloc")]
impl<T> core::fmt::Debug for VecCastError<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VecCastError")
.field("kind", &self.kind)
.finish()
}
}
#[cfg(feature = "alloc")]
impl<T> core::fmt::Display for VecCastError<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("could not convert component vector to colors")
}
}
#[cfg(feature = "std")]
impl<T> std::error::Error for VecCastError<T> {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg(feature = "alloc")]
pub enum VecCastErrorKind {
LengthMismatch,
CapacityMismatch,
}
#[cfg(test)]
mod test {
#[cfg(feature = "alloc")]
use crate::{LinSrgb, Srgb};
#[cfg(feature = "alloc")]
#[test]
fn array_vec_len_cap() {
let mut original = vec![
Srgb::new(255u8, 0, 0),
Srgb::new(0, 255, 0),
Srgb::new(0, 0, 255),
];
original.reserve_exact(5);
let colors_arrays = super::into_array_vec(original);
assert_eq!(colors_arrays.len(), 3);
assert_eq!(colors_arrays.capacity(), 8);
let colors = super::from_array_vec::<Srgb<_>>(colors_arrays);
assert_eq!(colors.len(), 3);
assert_eq!(colors.capacity(), 8);
let colors_components = super::into_component_vec(colors);
assert_eq!(colors_components.len(), 9);
assert_eq!(colors_components.capacity(), 24);
let colors = super::from_component_vec::<Srgb<_>>(colors_components);
assert_eq!(colors.len(), 3);
assert_eq!(colors.capacity(), 8);
}
#[cfg(feature = "alloc")]
#[test]
fn map_vec_in_place() {
fn do_things(rgb: Srgb) -> LinSrgb {
let mut linear = rgb.into_linear();
core::mem::swap(&mut linear.red, &mut linear.blue);
linear
}
let values = vec![Srgb::new(0.8, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)];
let result = super::map_vec_in_place(values, do_things);
assert_eq!(
result,
vec![
do_things(Srgb::new(0.8, 1.0, 0.2)),
do_things(Srgb::new(0.9, 0.1, 0.3))
]
)
}
#[cfg(feature = "alloc")]
#[test]
fn map_slice_box_in_place() {
fn do_things(rgb: Srgb) -> LinSrgb {
let mut linear = rgb.into_linear();
core::mem::swap(&mut linear.red, &mut linear.blue);
linear
}
let values = vec![Srgb::new(0.8, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)].into_boxed_slice();
let result = super::map_slice_box_in_place(values, do_things);
assert_eq!(
result,
vec![
do_things(Srgb::new(0.8, 1.0, 0.2)),
do_things(Srgb::new(0.9, 0.1, 0.3))
]
.into_boxed_slice()
)
}
}