use std::alloc::{Layout, alloc, dealloc};
use std::mem::MaybeUninit;
use std::num::NonZero;
use std::pin::Pin;
use std::ptr::{self, NonNull};
use std::sync::atomic::{self, AtomicUsize};
use crate::mem::{Block, BlockMeta, BlockRef, BlockRefDynamic, BlockRefDynamicWithMeta, BlockRefVTable, BlockSize};
pub(crate) struct TestMemoryBlock {
capacity_ptr: NonNull<MaybeUninit<u8>>,
len: NonZero<BlockSize>,
meta: Option<Box<dyn BlockMeta>>,
ref_count: AtomicUsize,
}
impl TestMemoryBlock {
pub(crate) unsafe fn new(len: NonZero<BlockSize>, meta: Option<Box<dyn BlockMeta>>) -> Self {
let capacity_ptr = NonNull::new(unsafe { alloc(byte_array_layout(len)) })
.expect("we do not intend to handle failed allocations - they are fatal")
.cast::<MaybeUninit<u8>>();
Self {
capacity_ptr,
len,
meta,
ref_count: AtomicUsize::new(0),
}
}
pub(crate) fn to_block_ref(self: Pin<&Self>) -> BlockRef {
let function_table = if self.meta.is_some() {
&BLOCK_WITHOUT_MEMORY_FNS_WITH_META
} else {
&BLOCK_WITHOUT_MEMORY_FNS
};
self.ref_count.fetch_add(1, atomic::Ordering::Relaxed);
let state_ptr = NonNull::from(self.get_ref());
unsafe { BlockRef::new(state_ptr, function_table) }
}
pub(crate) unsafe fn to_block(self: Pin<&Self>) -> Block {
unsafe { Block::new(self.capacity_ptr, self.len, self.to_block_ref()) }
}
pub(crate) fn ref_count(&self) -> usize {
self.ref_count.load(atomic::Ordering::Relaxed)
}
}
impl Drop for TestMemoryBlock {
fn drop(&mut self) {
unsafe {
dealloc(self.capacity_ptr.as_ptr().cast(), byte_array_layout(self.len));
}
}
}
unsafe impl BlockRefDynamic for TestMemoryBlock {
type State = Self;
fn clone(state_ptr: NonNull<Self::State>) -> NonNull<Self::State> {
let state = unsafe { state_ptr.as_ref() };
state.ref_count.fetch_add(1, atomic::Ordering::Relaxed);
state_ptr
}
fn drop(state_ptr: NonNull<Self::State>) {
let state = unsafe { state_ptr.as_ref() };
state.ref_count.fetch_sub(1, atomic::Ordering::Release);
}
}
unsafe impl BlockRefDynamicWithMeta for TestMemoryBlock {
fn meta(state_ptr: NonNull<Self::State>) -> NonNull<dyn BlockMeta> {
let state = unsafe { state_ptr.as_ref() };
let as_ref_box = state.meta.as_ref().expect("meta must be set if using with-meta function table");
let as_any: &dyn BlockMeta = as_ref_box.as_ref();
NonNull::new(ptr::from_ref(as_any).cast_mut()).expect("field of non-null is non-null")
}
}
fn byte_array_layout(len: NonZero<BlockSize>) -> Layout {
Layout::array::<u8>(len.get() as usize).expect("the layout of a byte array can always be determined")
}
const BLOCK_WITHOUT_MEMORY_FNS: BlockRefVTable<TestMemoryBlock> = BlockRefVTable::from_trait();
const BLOCK_WITHOUT_MEMORY_FNS_WITH_META: BlockRefVTable<TestMemoryBlock> = BlockRefVTable::from_trait_with_meta();