#![no_std]
#![cfg_attr(feature = "nightly_std_simd", feature(portable_simd))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "rc")]
use alloc::rc::Rc;
#[cfg(feature = "arc")]
use alloc::sync::Arc;
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, vec::Vec};
pub trait ArrayExt {
fn as_slice_of<T>(&self) -> &[T]
where
Self: AsArrayOf<T>,
{
as_slice(self)
}
fn as_mut_slice_of<T>(&mut self) -> &mut [T]
where
Self: AsArrayOf<T>,
{
as_mut_slice(self)
}
#[cfg(feature = "alloc")]
fn into_boxed_slice_of<T>(self: Box<Self>) -> Box<[T]>
where
Self: IsArrayOf<T>,
{
into_boxed_slice(self)
}
#[cfg(feature = "rc")]
fn into_rc_slice_of<T>(self: Rc<Self>) -> Rc<[T]>
where
Self: IsArrayOf<T>,
{
into_rc_slice(self)
}
#[cfg(feature = "arc")]
fn into_arc_slice_of<T>(self: Arc<Self>) -> Arc<[T]>
where
Self: IsArrayOf<T>,
{
into_arc_slice(self)
}
}
impl<A: ?Sized> ArrayExt for A {}
#[test]
fn array_ext() {
let x: [[u32; 4]; 5] = Default::default();
let _: &[u32] = x.as_slice_of();
let _: &[[u32; 4]] = x.as_slice_of();
#[cfg(feature = "primitives")]
{
let _: &[u8] = x.as_slice_of::<u32>().as_slice_of();
}
}
pub unsafe trait AsArrayOf<T> {
fn array_len(this: &Self) -> Option<usize>;
}
pub unsafe trait IsArrayOf<T>: AsArrayOf<T> {}
pub unsafe trait ConstantSizeArrayOf<T>: IsArrayOf<T> + Sized {
const ARRAY_CONST_LEN: Option<usize>;
}
pub unsafe trait TryFromArrayOf<T>: IsArrayOf<T> {
fn try_with_array_len(
ptr: *const T,
array_len: usize,
) -> Option<(*const Self, usize)>;
}
pub unsafe trait FromArrayOf<T>: TryFromArrayOf<T> {
fn with_array_len(ptr: *const T, array_len: usize) -> (*const Self, usize);
}
unsafe impl<T, A: ConstantSizeArrayOf<T>> TryFromArrayOf<T> for A {
fn try_with_array_len(
ptr: *const T,
array_len: usize,
) -> Option<(*const Self, usize)> {
let a_len_in_t = try_const_len::<T, A>()?;
(a_len_in_t <= array_len).then_some((ptr as _, a_len_in_t))
}
}
unsafe impl<T, A: ConstantSizeArrayOf<T>> FromArrayOf<T> for [A] {
fn with_array_len(ptr: *const T, array_len: usize) -> (*const Self, usize) {
let Some(a_len_in_t) = try_const_len::<T, A>() else {
return (core::ptr::slice_from_raw_parts(ptr as _, 0), 0);
};
let self_len_in_a = array_len / a_len_in_t;
let self_len_in_t = self_len_in_a * a_len_in_t;
(
core::ptr::slice_from_raw_parts(ptr as _, self_len_in_a),
self_len_in_t,
)
}
}
unsafe impl<T, A: ConstantSizeArrayOf<T>> TryFromArrayOf<T> for [A] {
fn try_with_array_len(
ptr: *const T,
array_len: usize,
) -> Option<(*const Self, usize)> {
Some(Self::with_array_len(ptr, array_len))
}
}
mod slice {
use crate::*;
unsafe impl<T, A: AsArrayOf<T>> AsArrayOf<T> for [A] {
fn array_len(this: &Self) -> Option<usize> {
match this.len() {
0 => Some(0),
len => len.checked_mul(try_len::<T>(&this[0])?),
}
}
}
unsafe impl<T, A: IsArrayOf<T>> IsArrayOf<T> for [A] {}
}
mod single {
use crate::*;
unsafe impl<T> AsArrayOf<T> for T {
fn array_len(_: &Self) -> Option<usize> {
Some(1)
}
}
unsafe impl<T> IsArrayOf<T> for T {}
unsafe impl<T> ConstantSizeArrayOf<T> for T {
const ARRAY_CONST_LEN: Option<usize> = Some(1);
}
}
macro_rules! checked_mul_or_zero {
($val:expr) => {
Some($val)
};
($head:ident $(, $tail:ident)+) => {
match ($head, checked_mul_or_zero!($($tail),+)) {
(0, _) => Some(0),
(head, Some(tail)) => head.checked_mul(tail),
(_, None) => None
}
}
}
macro_rules! array {
([] $T:ty) => {
$T
};
([$len:expr $(, $lens:expr)*] $T:ty) => {
[array!([$($lens),*] $T); $len]
}
}
macro_rules! make_array_impls {
($($lens:ident)+) => {
unsafe impl<T, $(const $lens: usize),*> ConstantSizeArrayOf<T> for array!([$($lens),*] T) {
const ARRAY_CONST_LEN: Option<usize> = checked_mul_or_zero!($($lens),*);
}
unsafe impl<T, $(const $lens: usize),*> AsArrayOf<T> for array!([$($lens),*] T) {
fn array_len(_: &Self) -> Option<usize> {
<Self as ConstantSizeArrayOf<T>>::ARRAY_CONST_LEN
}
}
unsafe impl<T, $(const $lens: usize),*> IsArrayOf<T> for array!([$($lens),*] T) {
}
};
}
make_array_impls!(A);
#[cfg(feature = "depth_2")]
make_array_impls!(A B);
#[cfg(feature = "depth_3")]
make_array_impls!(A B C);
#[cfg(feature = "depth_4")]
make_array_impls!(A B C D);
#[cfg(feature = "depth_5")]
make_array_impls!(A B C D E);
#[cfg(feature = "depth_6")]
make_array_impls!(A B C D E F);
#[cfg(feature = "depth_7")]
make_array_impls!(A B C D E F G);
#[allow(unused)]
macro_rules! make_primitive_impls {
(of $of:ty: exact = [$($exact_tys:ty),*], as = [$($as_tys:ty),*]) => {
$(
unsafe impl AsArrayOf<$of> for $exact_tys where $of: Copy, $exact_tys: Copy {
fn array_len(_: &Self) -> Option<usize> {
<Self as ConstantSizeArrayOf<$of>>::ARRAY_CONST_LEN
}
}
unsafe impl IsArrayOf<$of> for $exact_tys {}
unsafe impl ConstantSizeArrayOf<$of> for $exact_tys {
const ARRAY_CONST_LEN: Option<usize> = {
const LEN: usize = {
let of_size = core::mem::size_of::<$of>();
let from_size = core::mem::size_of::<$exact_tys>();
assert!(from_size % of_size == 0);
let of_align = core::mem::align_of::<$of>();
let from_align = core::mem::align_of::<$exact_tys>();
assert!(from_align == of_align);
from_size / of_size
};
Some(LEN)
};
}
)*
$(
unsafe impl AsArrayOf<$of> for $as_tys where $of: Copy, $as_tys: Copy {
fn array_len(_: &Self) -> Option<usize> {
const LEN: usize = {
let of_size = core::mem::size_of::<$of>();
let from_size = core::mem::size_of::<$as_tys>();
assert!(from_size % of_size == 0);
let of_align = core::mem::align_of::<$of>();
let from_align = core::mem::align_of::<$as_tys>();
assert!(from_align % of_align == 0);
from_size / of_size
};
Some(LEN)
}
}
)*
};
}
#[allow(unused)]
macro_rules! make_many_primitive_impls {
(of [$($int_tys:ty),*]: as = [$($simd_tys:ty),*]) => {
make_many_primitive_impls!(@helper [$($int_tys),*] [$($simd_tys),*]);
};
(@helper [] [$($simd_tys:ty),*]) => {};
(@helper [$int_ty:ty $(, $int_tys:ty)*] [$($simd_tys:ty),*]) => {
make_primitive_impls!(of $int_ty: exact = [], as = [$($simd_tys),*]);
make_many_primitive_impls!(@helper [$($int_tys),*] [$($simd_tys),*]);
};
}
#[cfg(feature = "primitives")]
mod primitive_impls {
use crate::{AsArrayOf, ConstantSizeArrayOf, IsArrayOf};
make_primitive_impls!(of u8: exact = [i8], as = [u16, i16, u32, i32, f32, u64, i64, f64, u128, i128]);
make_primitive_impls!(of i8: exact = [u8], as = [u16, i16, u32, i32, f32, u64, i64, f64, u128, i128]);
make_primitive_impls!(of u16: exact = [i16], as = [u32, i32, f32, u64, i64, f64, u128, i128]);
make_primitive_impls!(of i16: exact = [u16], as = [u32, i32, f32, u64, i64, f64, u128, i128]);
make_primitive_impls!(of u32: exact = [i32, f32], as = [u64, i64, f64, u128, i128]);
make_primitive_impls!(of i32: exact = [u32, f32], as = [u64, i64, f64, u128, i128]);
make_primitive_impls!(of f32: exact = [u32, i32], as = [u64, i64, f64, u128, i128]);
make_primitive_impls!(of u64: exact = [i64, f64], as = [u128, i128]);
make_primitive_impls!(of i64: exact = [u64, f64], as = [u128, i128]);
make_primitive_impls!(of f64: exact = [u64, i64], as = [u128, i128]);
make_primitive_impls!(of u128: exact = [i128], as = []);
make_primitive_impls!(of i128: exact = [u128], as = []);
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
mod x86_arch {
#[cfg(target_arch = "x86")]
use core::arch::x86::{
__m128, __m128d, __m128i, __m256, __m256d, __m256i, __m512, __m512d,
__m512i,
};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{
__m128, __m128d, __m128i, __m256, __m256d, __m256i, __m512, __m512d,
__m512i,
};
use crate::AsArrayOf;
#[cfg(not(feature = "primitives"))]
make_primitive_impls!(of f32: exact = [], as = [__m128, __m256, __m512]);
#[cfg(not(feature = "primitives"))]
make_primitive_impls!(of f64: exact = [], as = [__m128d, __m256d, __m512d]);
#[cfg(not(feature = "primitives"))]
make_many_primitive_impls!(of [i8, u8, i16, u16, i32, u32, i64, u64, i128, u128]: as = [__m128i, __m256i, __m512i]);
#[cfg(feature = "primitives")]
make_many_primitive_impls!(of [i8, u8, i16, u16, i32, u32, f32, i64, u64, f64, i128, u128]: as = [__m128i, __m256i, __m512i, __m128, __m256, __m512, __m128d, __m256d, __m512d]);
}
#[cfg(feature = "nightly_std_simd")]
mod std_simd {
use core::simd::{Simd, SimdElement, LaneCount, SupportedLaneCount};
use crate::AsArrayOf;
unsafe impl<T: SimdElement, const N: usize> AsArrayOf<T> for Simd<T, N>
where LaneCount<N>: SupportedLaneCount
{
fn array_len(_: &Self) -> Option<usize> {
Some(N)
}
}
}
pub fn try_len<T>(array: &(impl AsArrayOf<T> + ?Sized)) -> Option<usize> {
AsArrayOf::<T>::array_len(array)
}
pub fn len<T>(array: &(impl AsArrayOf<T> + ?Sized)) -> usize {
try_len::<T>(array).expect("array length overflow")
}
pub const fn try_const_len<T, A: ConstantSizeArrayOf<T>>() -> Option<usize> {
<A as ConstantSizeArrayOf<T>>::ARRAY_CONST_LEN
}
pub const fn const_len<T, A: ConstantSizeArrayOf<T>>() -> usize {
try_const_len::<T, A>().expect("array length overflow")
}
pub fn as_slice<T>(array: &(impl AsArrayOf<T> + ?Sized)) -> &[T] {
let len = try_len::<T>(array).unwrap_or(usize::MAX);
let data = array as *const _ as *const T;
unsafe { core::slice::from_raw_parts(data, len) }
}
pub fn as_mut_slice<T>(array: &mut (impl AsArrayOf<T> + ?Sized)) -> &mut [T] {
let len = try_len::<T>(array).unwrap_or(usize::MAX);
let data = array as *mut _ as *mut T;
unsafe { core::slice::from_raw_parts_mut(data, len) }
}
#[cfg(feature = "alloc")]
pub fn into_boxed_slice<T>(array: Box<impl IsArrayOf<T> + ?Sized>) -> Box<[T]> {
let len = len::<T>(&*array);
let data = Box::into_raw(array) as *mut _ as *mut T;
let raw = core::ptr::slice_from_raw_parts_mut(data, len);
unsafe { Box::from_raw(raw) }
}
#[cfg(feature = "rc")]
pub fn into_rc_slice<T>(array: Rc<impl IsArrayOf<T> + ?Sized>) -> Rc<[T]> {
let len = len::<T>(&*array);
let data = Rc::into_raw(array) as *const _ as *const T;
let raw = core::ptr::slice_from_raw_parts(data, len);
unsafe { Rc::from_raw(raw) }
}
#[cfg(feature = "arc")]
pub fn into_arc_slice<T>(array: Arc<impl IsArrayOf<T> + ?Sized>) -> Arc<[T]> {
let len = len::<T>(&*array);
let data = Arc::into_raw(array) as *const _ as *const T;
let raw = core::ptr::slice_from_raw_parts(data, len);
unsafe { Arc::from_raw(raw) }
}
#[cfg(feature = "alloc")]
pub fn into_vec<T>(vec: Vec<impl ConstantSizeArrayOf<T>>) -> Vec<T> {
fn into_vec_impl<T, A: ConstantSizeArrayOf<T>>(vec: Vec<A>) -> Vec<T> {
use core::mem::ManuallyDrop;
let mut vec = ManuallyDrop::new(vec);
let a_len = try_const_len::<T, A>()
.expect("ARRAY_CONST_LEN was None when casting vec");
let len = vec
.len()
.checked_mul(a_len)
.expect("length overflow when casting vec");
let cap = vec.capacity().checked_mul(a_len).unwrap_or_else(|| {
debug_assert!(
core::mem::size_of::<T>() == 0,
"capacity overflow when casting vec of non-ZST; should not be possible"
);
debug_assert!(
core::mem::size_of::<A>() == 0,
"if T is a ZST then A must be a ZST"
);
usize::MAX
});
let ptr = vec.as_mut_ptr();
unsafe { Vec::from_raw_parts(ptr.cast(), len, cap) }
}
into_vec_impl(vec)
}
pub fn from_slice<T, A: FromArrayOf<T> + ?Sized>(slice: &[T]) -> (&A, &[T]) {
let slice_len = slice.len();
let ptr = slice.as_ptr();
let (array_ptr, array_len) = A::with_array_len(ptr, slice_len);
let tail_len = slice_len - array_len;
unsafe {
let tail_ptr =
core::ptr::slice_from_raw_parts(ptr.add(array_len), tail_len);
(&*array_ptr, &*tail_ptr)
}
}
pub fn try_from_slice<T, A: TryFromArrayOf<T> + ?Sized>(
slice: &[T],
) -> Result<(&A, &[T]), &[T]> {
let slice_len = slice.len();
let ptr = slice.as_ptr();
let Some((array_ptr, array_len)) = A::try_with_array_len(ptr, slice_len)
else {
return Err(slice);
};
let tail_len = slice_len - array_len;
unsafe {
let tail_ptr =
core::ptr::slice_from_raw_parts(ptr.add(array_len), tail_len);
Ok((&*array_ptr, &*tail_ptr))
}
}
pub fn from_mut_slice<T, A: FromArrayOf<T> + ?Sized>(
slice: &mut [T],
) -> (&mut A, &mut [T]) {
let slice_len = slice.len();
let ptr = slice.as_mut_ptr();
let (array_ptr, array_len) = A::with_array_len(ptr, slice_len);
let array_ptr = array_ptr.cast_mut();
let tail_len = slice_len - array_len;
unsafe {
let tail_ptr =
core::ptr::slice_from_raw_parts_mut(ptr.add(array_len), tail_len);
(&mut *array_ptr, &mut *tail_ptr)
}
}
pub fn try_from_mut_slice<T, A: TryFromArrayOf<T> + ?Sized>(
slice: &mut [T],
) -> Result<(&mut A, &mut [T]), &mut [T]> {
let slice_len = slice.len();
let ptr = slice.as_mut_ptr();
let Some((array_ptr, array_len)) = A::try_with_array_len(ptr, slice_len)
else {
return Err(slice);
};
let array_ptr = array_ptr.cast_mut();
let tail_len = slice_len - array_len;
unsafe {
let tail_ptr =
core::ptr::slice_from_raw_parts_mut(ptr.add(array_len), tail_len);
Ok((&mut *array_ptr, &mut *tail_ptr))
}
}
pub fn take_front<'a, T, A: TryFromArrayOf<T>>(
slice: &mut &'a [T],
) -> Option<&'a A> {
let (head, tail) = try_from_slice(slice).ok()?;
*slice = tail;
Some(head)
}
pub fn take_front_mut<'a, T, A: TryFromArrayOf<T>>(
slice: &mut &'a mut [T],
) -> Option<&'a mut A> {
match try_from_mut_slice(core::mem::take(slice)) {
Ok((head, tail)) => {
*slice = tail;
Some(head)
}
Err(tail) => {
*slice = tail;
None
}
}
}
#[cfg(feature = "alloc")]
pub fn try_from_boxed_slice<T, A: TryFromArrayOf<T> + ?Sized>(
slice: Box<[T]>,
) -> Result<Box<A>, Box<[T]>> {
try_cast_box(slice)
}
pub fn empty<'a, T: 'a, B: FromArrayOf<T> + ?Sized + 'a>() -> &'a B {
from_slice(&[]).0
}
pub fn empty_mut<'a, T: 'a, B: FromArrayOf<T> + ?Sized + 'a>() -> &'a mut B {
from_mut_slice(&mut []).0
}
#[cfg(feature = "alloc")]
pub fn empty_boxed<T, B: FromArrayOf<T> + ?Sized>() -> Box<B> {
let slice_len = 0;
let ptr = Box::into_raw(Box::<[T; 0]>::new([]));
let (array_ptr, array_len) = B::with_array_len(ptr as *mut T, slice_len);
debug_assert_eq!(array_len, 0);
unsafe { Box::from_raw(array_ptr.cast_mut()) }
}
pub fn cast_ref<T, A: AsArrayOf<T> + ?Sized, B: FromArrayOf<T> + ?Sized>(
slice: &A,
) -> (&B, &[T]) {
from_slice(as_slice(slice))
}
pub fn try_cast_ref<
T,
A: AsArrayOf<T> + ?Sized,
B: TryFromArrayOf<T> + ?Sized,
>(
from: &A,
) -> Result<(&B, &[T]), &A> {
let slice = as_slice::<T>(from);
match try_from_slice(slice) {
Ok(result) => Ok(result),
Err(_) => Err(from),
}
}
pub fn cast_mut<T, A: AsArrayOf<T> + ?Sized, B: FromArrayOf<T> + ?Sized>(
from: &mut A,
) -> (&mut B, &mut [T]) {
from_mut_slice(as_mut_slice(from))
}
pub fn try_cast_mut<
T,
A: AsArrayOf<T> + ?Sized,
B: TryFromArrayOf<T> + ?Sized,
>(
from: &mut A,
) -> Result<(&mut B, &mut [T]), &mut A> {
let polonius_the_crab = unsafe { &mut *(from as *mut A) };
let slice = as_mut_slice::<T>(polonius_the_crab);
match try_from_mut_slice(slice) {
Ok(result) => Ok(result),
Err(_) => Err(from),
}
}
#[cfg(feature = "alloc")]
pub fn try_cast_box<
T,
A: IsArrayOf<T> + ?Sized,
B: TryFromArrayOf<T> + ?Sized,
>(
from: Box<A>,
) -> Result<Box<B>, Box<A>> {
let Some(slice_len) = try_len::<T>(&*from) else {
return Err(from);
};
let ptr = Box::into_raw(from);
match B::try_with_array_len(ptr as *mut T, slice_len) {
Some((array_ptr, array_len)) if array_len == slice_len => {
let array_ptr = array_ptr.cast_mut();
unsafe { Ok(Box::from_raw(array_ptr)) }
}
_ => Err(unsafe { Box::from_raw(ptr) }),
}
}
#[cfg(feature = "alloc")]
pub fn try_cast_vec<T, A: ConstantSizeArrayOf<T>, B: ConstantSizeArrayOf<T>>(
from: Vec<A>,
) -> Result<Vec<B>, Vec<A>> {
use core::mem::ManuallyDrop;
let Some(slice_cap) = try_const_len::<T, A>()
.and_then(|elem_size| elem_size.checked_mul(from.capacity()))
else {
return Err(from);
};
let Some(slice_len) = try_len::<T>(&*from) else {
unreachable!("length <= capacity");
};
let mut from = ManuallyDrop::new(from);
let ptr = from.as_mut_ptr();
match (
<[B]>::try_with_array_len(ptr as *mut T, slice_cap),
<[B]>::try_with_array_len(ptr as *mut T, slice_len),
) {
(Some((cap_ptr, array_cap)), Some((len_ptr, array_len)))
if array_cap == slice_cap && array_len == slice_len =>
{
let cap_ptr = cap_ptr.cast_mut();
#[allow(unstable_name_collisions)]
unsafe {
Ok(Vec::from_raw_parts(
cap_ptr.cast::<B>(),
cap_ptr.len(),
len_ptr.len(),
))
}
}
_ => Err(ManuallyDrop::into_inner(from)),
}
}
#[cfg(feature = "rc")]
pub fn try_cast_rc<
T,
A: IsArrayOf<T> + ?Sized,
B: TryFromArrayOf<T> + ?Sized,
>(
from: Rc<A>,
) -> Result<Rc<B>, Rc<A>> {
let Some(slice_len) = try_len::<T>(&*from) else {
return Err(from);
};
let ptr = Rc::into_raw(from);
match B::try_with_array_len(ptr as *const T, slice_len) {
Some((array_ptr, array_len)) if array_len == slice_len => {
let array_ptr = array_ptr.cast_mut();
unsafe { Ok(Rc::from_raw(array_ptr)) }
}
_ => Err(unsafe { Rc::from_raw(ptr) }),
}
}
#[cfg(feature = "arc")]
pub fn try_cast_arc<
T,
A: IsArrayOf<T> + ?Sized,
B: TryFromArrayOf<T> + ?Sized,
>(
from: Arc<A>,
) -> Result<Arc<B>, Arc<A>> {
let Some(slice_len) = try_len::<T>(&*from) else {
return Err(from);
};
let ptr = Arc::into_raw(from);
match B::try_with_array_len(ptr as *const T, slice_len) {
Some((array_ptr, array_len)) if array_len == slice_len => {
let array_ptr = array_ptr.cast_mut();
unsafe { Ok(Arc::from_raw(array_ptr)) }
}
_ => Err(unsafe { Arc::from_raw(ptr) }),
}
}