use std::fmt::{Debug, Error, Formatter};
use std::mem::MaybeUninit;
use crate::counter::Counter;
use crate::pointer::Pointer;
use crate::refbox::RefBox;
use crate::stack::Stack;
use crate::types::{ElementPointer, PoolPointer};
unsafe fn init_box<A>(ref_box: *mut RefBox<A>, pool: Pool<A>) {
let count_ptr: *mut _ = &mut (*(ref_box)).count;
let pool_ptr: *mut _ = &mut (*(ref_box)).pool;
count_ptr.write(Default::default());
pool_ptr.write(pool);
}
pub struct Pool<A> {
inner: PoolPointer<A>,
}
impl<A> Pool<A> {
pub fn new(max_size: usize) -> Self {
if max_size == 0 {
Self {
inner: PoolPointer::null(),
}
} else {
Box::new(PoolInner::new(max_size)).into_ref()
}
}
pub(crate) fn push(&self, value: ElementPointer<A>) {
debug_assert!(self.inner.get_ptr_checked().is_some());
unsafe { (*self.inner.get_ptr()).push(value) }
}
pub(crate) fn pop(&self) -> Box<MaybeUninit<RefBox<A>>> {
let mut obj = if let Some(inner) = self.inner.get_ptr_checked() {
unsafe { (*inner).pop() }
} else {
None
}
.unwrap_or_else(|| Box::new(MaybeUninit::uninit()));
unsafe { init_box(obj.as_mut_ptr(), self.clone()) };
obj
}
fn deref(&self) -> Option<&PoolInner<A>> {
self.inner.get_ptr_checked().map(|p| unsafe { &*p })
}
pub fn get_max_size(&self) -> usize {
self.deref().map(|p| p.get_max_size()).unwrap_or(0)
}
pub fn get_pool_size(&self) -> usize {
self.deref().map(|p| p.get_pool_size()).unwrap_or(0)
}
pub fn is_full(&self) -> bool {
self.deref()
.map(|p| p.get_pool_size() >= p.get_max_size())
.unwrap_or(true)
}
pub fn fill(&self) {
if let Some(inner) = self.deref() {
while inner.get_max_size() > inner.get_pool_size() {
let chunk = unsafe {
std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked(
std::mem::size_of::<RefBox<A>>(),
std::mem::align_of::<RefBox<A>>(),
))
};
self.push(ElementPointer::wrap(chunk.cast()));
}
}
}
pub fn filled(self) -> Self {
self.fill();
self
}
pub fn cast<B>(&self) -> Pool<B> {
assert!(std::mem::size_of::<A>() == std::mem::size_of::<B>());
assert!(std::mem::align_of::<A>() == std::mem::align_of::<B>());
if let Some(ptr) = self.inner.get_ptr_checked() {
let inner: *mut PoolInner<B> = ptr.cast();
unsafe { (*inner).make_ref() }
} else {
Pool::new(0)
}
}
}
impl<A> Clone for Pool<A> {
fn clone(&self) -> Self {
if let Some(inner) = self.inner.get_ptr_checked() {
unsafe { (*inner).make_ref() }
} else {
Self::new(0)
}
}
}
impl<A> Drop for Pool<A> {
fn drop(&mut self) {
if let Some(ptr) = self.inner.get_ptr_checked() {
if unsafe { (*ptr).dec() } == 1 {
std::mem::drop(unsafe { Box::from_raw(ptr) });
}
}
}
}
impl<A> Debug for Pool<A> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(
f,
"Pool[{}/{}]:{:p}",
self.get_pool_size(),
self.get_max_size(),
self.inner
)
}
}
pub(crate) struct PoolInner<A> {
count: usize,
max_size: usize,
stack: Vec<ElementPointer<A>>,
}
impl<A> PoolInner<A> {
fn new(max_size: usize) -> Self {
Self {
count: Default::default(),
max_size,
stack: Stack::stack_new(max_size),
}
}
fn into_ref(mut self: Box<Self>) -> Pool<A> {
self.inc();
Pool {
inner: PoolPointer::wrap(Box::into_raw(self)),
}
}
fn make_ref(&mut self) -> Pool<A> {
self.inc();
Pool {
inner: PoolPointer::wrap(self),
}
}
fn get_max_size(&self) -> usize {
self.max_size
}
fn get_pool_size(&self) -> usize {
self.stack.stack_len()
}
#[inline(always)]
fn inc(&mut self) {
self.count.inc();
}
#[inline(always)]
fn dec(&mut self) -> usize {
self.count.dec()
}
fn pop(&mut self) -> Option<Box<MaybeUninit<RefBox<A>>>> {
self.stack.stack_pop().map(|value_ptr| {
let box_ptr = value_ptr.cast::<MaybeUninit<RefBox<A>>>();
unsafe { Box::from_raw(box_ptr.as_ptr()) }
})
}
fn push(&mut self, handle: ElementPointer<A>) {
self.stack.stack_push(handle);
}
}
impl<A> Drop for PoolInner<A> {
fn drop(&mut self) {
while let Some(chunk) = self.stack.stack_pop() {
unsafe {
std::alloc::dealloc(
chunk.as_ptr().cast(),
std::alloc::Layout::from_size_align_unchecked(
std::mem::size_of::<RefBox<A>>(),
std::mem::align_of::<RefBox<A>>(),
),
);
}
}
}
}