use core::marker::PhantomData;
use core::mem::ManuallyDrop;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use core::slice;
use crate::alloc::alloc;
use crate::alloc::dealloc;
use crate::alloc::handle_alloc_error;
use crate::index::Concrete;
use crate::params::Params;
use crate::params::ParamsExt;
#[repr(transparent)]
pub(crate) struct Array<T, P>
where
P: Params + ?Sized,
{
nonnull: NonNull<T>,
phantom: PhantomData<P>,
}
impl<T, P> Array<T, P>
where
P: Params + ?Sized,
{
#[inline]
pub(crate) fn new<F>(init: F) -> Self
where
F: Fn(usize, &mut MaybeUninit<T>),
{
let this: Array<MaybeUninit<T>, P> = Self::new_uninit();
for index in 0..P::LENGTH.as_usize() {
init(index, unsafe { this.as_non_null().add(index).as_mut() });
}
unsafe { this.assume_init() }
}
#[inline]
pub(crate) fn new_uninit() -> Array<MaybeUninit<T>, P> {
let raw: *mut u8 = unsafe { alloc(P::LAYOUT) };
Array {
nonnull: match NonNull::new(raw.cast()) {
Some(ptr) => ptr,
None => handle_alloc_error(P::LAYOUT),
},
phantom: PhantomData,
}
}
#[inline]
pub(crate) const fn as_non_null(&self) -> NonNull<T> {
self.nonnull
}
#[cfg(test)]
#[inline]
pub(crate) const fn as_ptr(&self) -> *const T {
self.as_non_null().as_ptr()
}
#[inline]
pub(crate) const fn as_mut_ptr(&self) -> *mut T {
self.as_non_null().as_ptr()
}
#[cfg(test)]
#[inline]
pub(crate) const fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.as_ptr(), P::LENGTH.as_usize()) }
}
#[inline]
pub(crate) const fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), P::LENGTH.as_usize()) }
}
#[inline]
pub(crate) const fn get(&self, index: Concrete<P>) -> &T {
unsafe { self.get_unchecked(index.get()) }
}
#[inline]
pub(crate) const unsafe fn get_unchecked(&self, index: usize) -> &T {
debug_assert!(
index < P::LENGTH.as_usize(),
"Array::get_unchecked requires that the index is in bounds",
);
unsafe { self.as_non_null().add(index).as_ref() }
}
}
impl<T, P> Array<MaybeUninit<T>, P>
where
P: Params + ?Sized,
{
#[inline]
pub(crate) unsafe fn assume_init(self) -> Array<T, P> {
Array {
nonnull: ManuallyDrop::new(self).as_non_null().cast(),
phantom: PhantomData,
}
}
}
impl<T, P> Drop for Array<T, P>
where
P: Params + ?Sized,
{
fn drop(&mut self) {
unsafe {
dealloc(self.as_non_null().cast().as_ptr(), P::LAYOUT);
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use crate::array::Array;
use crate::index::Concrete;
use crate::params::CACHE_LINE;
use crate::params::Params;
use crate::utils::each_capacity;
#[cfg_attr(loom, ignore = "loom does not run this test")]
#[test]
fn alignment() {
each_capacity!({
let array: Array<usize, P> = Array::new(|_, slot| {
slot.write(0);
});
assert_eq!(array.as_ptr().addr() & (CACHE_LINE - 1), 0);
});
}
#[cfg_attr(loom, ignore = "loom does not run this test")]
#[test]
fn get() {
each_capacity!({
let array: Array<usize, P> = Array::new(|index, slot| {
slot.write(index);
});
for index in 0..P::LENGTH.as_usize() {
assert_eq!(array.get(Concrete::<P>::new(index)), &index);
}
});
}
#[cfg_attr(loom, ignore = "loom does not run this test")]
#[test]
fn as_slice() {
each_capacity!({
let array: Array<usize, P> = Array::new(|index, slot| {
slot.write(index);
});
assert_eq!(array.as_slice().len(), P::LENGTH.as_usize());
for (index, value) in array.as_slice().iter().enumerate() {
assert_eq!(*value, index);
}
});
}
#[cfg_attr(loom, ignore = "loom does not run this test")]
#[test]
fn as_mut_slice() {
each_capacity!({
let mut array: Array<usize, P> = Array::new(|index, slot| {
slot.write(index);
});
assert_eq!(array.as_mut_slice().len(), P::LENGTH.as_usize());
for (index, value) in array.as_mut_slice().iter_mut().enumerate() {
assert_eq!(*value, index);
*value += 1;
}
for (index, value) in array.as_slice().iter().enumerate() {
assert_eq!(*value, index + 1);
}
});
}
}