use super::alloc_utils::*;
use super::traits::*;
use const_utils::{cond, max, safe_div};
use core::alloc::Layout;
use core::marker::PhantomData;
use core::mem;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicPtr, Ordering};
#[repr(transparent)]
pub struct MemBlock<E, L = ()> {
_placeholder: u8,
_phantom: PhantomData<(E, L, *mut u8)>,
}
type MutMB<E, L> = *mut MemBlock<E, L>;
impl<E, L> MemBlock<E, L> {
pub const fn max_len() -> usize {
let max_len = core::isize::MAX as usize;
let max_len_calc = {
let (esize, ealign) = size_align::<E>(1);
let lsize = aligned_size::<L>(ealign);
safe_div(max_len - lsize, esize)
};
cond(mem::size_of::<E>() == 0, max_len, max_len_calc)
}
pub const fn memory_layout(len: usize) -> (usize, usize) {
let (l_size, l_align) = size_align::<L>(1);
let (calc_size, calc_align) = {
let (dsize, dalign) = size_align::<E>(len);
let l_size = aligned_size::<L>(dalign);
(l_size + dsize, max(l_align, dalign))
};
(
cond(len == 0, l_size, calc_size),
cond(len == 0, l_align, calc_align),
)
}
}
fn check_len<E, L>(len: usize) {
if cfg!(not(feature = "mem-block-skip-size-check")) && len > MemBlock::<E, L>::max_len() {
panic!(
"Length {} is invalid: Block cannot be bigger than\
core::isize::MAX bytes ({} elements)",
len,
MemBlock::<E, L>::max_len()
);
}
}
fn get_layout<E, L>(len: usize) -> Layout {
check_len::<E, L>(len);
let (size, align) = MemBlock::<E, L>::memory_layout(len);
if cfg!(feature = "mem-block-skip-layout-check") {
unsafe { Layout::from_size_align_unchecked(size, align) }
} else {
match Layout::from_size_align(size, align) {
Ok(layout) => layout,
Err(err) => {
panic!(
"MemBlock of length {} is invalid for this platform;\n\
it has (size, align) = ({}, {}), causing error\n{:#?}",
len, size, align, err
);
}
}
}
}
unsafe impl<E, L> BaseArrayPtr<E, L> for *mut MemBlock<E, L> {
unsafe fn alloc(len: usize) -> Self {
let layout = get_layout::<E, L>(len);
let ptr = allocate(layout);
if cfg!(feature = "mem-block-skip-ptr-check") {
ptr
} else {
assert!(
!ptr.is_null(),
"Allocated a null pointer.\
You may be out of memory.",
);
ptr
}
}
unsafe fn dealloc(&mut self, len: usize) {
let layout = get_layout::<E, L>(len);
deallocate(*self, layout);
}
unsafe fn from_ptr(ptr: *mut u8) -> Self {
ptr as *mut MemBlock<E, L>
}
fn as_ptr(&self) -> *mut u8 {
self.clone() as *const u8 as *mut u8
}
fn is_null(&self) -> bool {
self.clone().is_null()
}
fn lbl_ptr(&self) -> *mut L {
*self as *mut L
}
fn elem_ptr(&self, idx: usize) -> *mut E {
check_len::<E, L>(idx + 1);
let e_align = mem::align_of::<E>();
let lsize = aligned_size::<L>(e_align);
let element = unsafe { (*self as *mut u8).add(lsize) as *mut E };
unsafe { element.add(idx) }
}
}
unsafe impl<E, L> BaseArrayPtr<E, L> for NonNull<MemBlock<E, L>> {
unsafe fn alloc(len: usize) -> Self {
NonNull::new_unchecked(MutMB::alloc(len))
}
unsafe fn dealloc(&mut self, len: usize) {
self.clone().as_ptr().dealloc(len)
}
unsafe fn from_ptr(ptr: *mut u8) -> Self {
NonNull::new_unchecked(MutMB::from_ptr(ptr))
}
fn as_ptr(&self) -> *mut u8 {
(*self).cast::<u8>().as_ptr()
}
fn is_null(&self) -> bool {
(*self).as_ptr().is_null()
}
fn lbl_ptr(&self) -> *mut L {
(*self).as_ptr().lbl_ptr()
}
fn elem_ptr(&self, idx: usize) -> *mut E {
(*self).as_ptr().elem_ptr(idx)
}
}
unsafe impl<E, L> BaseArrayPtr<E, L> for AtomicPtr<MemBlock<E, L>> {
unsafe fn alloc(len: usize) -> Self {
AtomicPtr::new(MutMB::alloc(len))
}
unsafe fn dealloc(&mut self, len: usize) {
self.load(Ordering::Acquire).dealloc(len)
}
unsafe fn from_ptr(ptr: *mut u8) -> Self {
AtomicPtr::new(MutMB::from_ptr(ptr))
}
fn as_ptr(&self) -> *mut u8 {
self.load(Ordering::Acquire) as *mut u8
}
fn is_null(&self) -> bool {
self.load(Ordering::Acquire).is_null()
}
fn lbl_ptr(&self) -> *mut L {
self.load(Ordering::Acquire).lbl_ptr()
}
fn elem_ptr(&self, idx: usize) -> *mut E {
self.load(Ordering::Acquire).elem_ptr(idx)
}
}