use core::cell::RefCell;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr;
use bun_core::Error;
#[repr(C)]
pub struct Node<T> {
pub next: *mut Node<T>,
pub data: MaybeUninit<T>,
}
impl<T> Node<T> {
#[inline(always)]
fn next_of(p: *const Node<T>) -> *mut Node<T> {
debug_assert!(!p.is_null());
unsafe { (*p).next }
}
#[inline]
pub unsafe fn data_ref(&self) -> &T {
unsafe { self.data.assume_init_ref() }
}
#[inline]
pub unsafe fn data_mut(&mut self) -> &mut T {
unsafe { self.data.assume_init_mut() }
}
pub fn insert_after(&mut self, new_node: &mut Node<T>) {
new_node.next = self.next;
self.next = std::ptr::from_mut::<Node<T>>(new_node);
}
pub fn remove_next(&mut self) -> Option<*mut Node<T>> {
let next_node = if self.next.is_null() {
return None;
} else {
self.next
};
self.next = Node::next_of(next_node);
Some(next_node)
}
pub fn find_last(&mut self) -> *mut Node<T> {
let mut it: *mut Node<T> = std::ptr::from_mut::<Node<T>>(self);
loop {
let next = Node::next_of(it);
if next.is_null() {
return it;
}
it = next;
}
}
pub fn count_children(&self) -> usize {
let mut count: usize = 0;
let mut it: *const Node<T> = self.next;
while !it.is_null() {
count += 1;
it = Node::next_of(it);
}
count
}
}
pub struct SinglyLinkedList<T> {
pub first: *mut Node<T>,
}
impl<T> Default for SinglyLinkedList<T> {
fn default() -> Self {
Self {
first: ptr::null_mut(),
}
}
}
impl<T> Drop for SinglyLinkedList<T> {
fn drop(&mut self) {
let mut next = core::mem::replace(&mut self.first, ptr::null_mut());
while !next.is_null() {
let node = next;
next = Node::next_of(node);
unsafe {
(*node).data.assume_init_drop();
drop(bun_core::heap::take(node));
}
}
}
}
impl<T> SinglyLinkedList<T> {
pub fn prepend(&mut self, new_node: &mut Node<T>) {
new_node.next = self.first;
self.first = new_node;
}
pub fn remove(&mut self, node: &Node<T>) {
let node = std::ptr::from_ref(node).cast_mut();
if self.first == node {
self.first = Node::next_of(node);
} else {
let mut current_elm = self.first;
unsafe {
while (*current_elm).next != node {
current_elm = (*current_elm).next;
}
(*current_elm).next = (*node).next;
}
}
}
pub fn pop_first(&mut self) -> Option<*mut Node<T>> {
let first = if self.first.is_null() {
return None;
} else {
self.first
};
self.first = Node::next_of(first);
Some(first)
}
pub fn len(&self) -> usize {
if !self.first.is_null() {
1 + unsafe { (*self.first).count_children() }
} else {
0
}
}
}
const LOG_ALLOCATIONS: bool = false;
pub trait ObjectPoolType: Sized {
const INIT: Option<fn() -> Result<Self, Error>> = None;
#[inline]
fn reset(&mut self) {}
}
pub struct DataStruct<T> {
pub list: SinglyLinkedList<T>,
pub loaded: bool,
pub count: usize,
}
impl<T> Default for DataStruct<T> {
fn default() -> Self {
Self {
list: SinglyLinkedList::default(),
loaded: false,
count: 0,
}
}
}
pub struct ObjectPool<
T: ObjectPoolType,
const THREADSAFE: bool,
const MAX_COUNT: usize,
S = UnwiredStorage,
>(core::marker::PhantomData<(T, S)>);
pub trait PoolStorage<T>: 'static {
fn with<R>(f: impl FnOnce(&RefCell<DataStruct<T>>) -> R) -> R;
}
pub struct UnwiredStorage;
impl<T: 'static> PoolStorage<T> for UnwiredStorage {
fn with<R>(_f: impl FnOnce(&RefCell<DataStruct<T>>) -> R) -> R {
unreachable!(
"ObjectPool<{}> storage not wired — declare with `object_pool!`",
core::any::type_name::<T>()
)
}
}
pub trait ObjectPoolTrait {
type Item;
type Node;
}
impl<T: ObjectPoolType, const TS: bool, const MAX: usize, S> ObjectPoolTrait
for ObjectPool<T, TS, MAX, S>
{
type Item = T;
type Node = Node<T>;
}
pub struct PoolGuard<'a, T: ObjectPoolType + 'static> {
node: *mut Node<T>,
release: unsafe fn(&mut Node<T>),
_marker: PhantomData<&'a mut T>,
}
impl<'a, T: ObjectPoolType> core::ops::Deref for PoolGuard<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { (*self.node).data.assume_init_ref() }
}
}
impl<'a, T: ObjectPoolType> core::ops::DerefMut for PoolGuard<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { (*self.node).data.assume_init_mut() }
}
}
impl<'a, T: ObjectPoolType> Drop for PoolGuard<'a, T> {
fn drop(&mut self) {
unsafe { (self.release)(&mut *self.node) };
}
}
impl<'a, T: ObjectPoolType> PoolGuard<'a, T> {
#[inline]
pub fn node_ptr(&self) -> *mut Node<T> {
self.node
}
}
impl<T: ObjectPoolType + 'static, const THREADSAFE: bool, const MAX_COUNT: usize, S>
ObjectPool<T, THREADSAFE, MAX_COUNT, S>
where
S: PoolStorage<T>,
{
#[inline]
pub(crate) fn data<R>(f: impl FnOnce(&RefCell<DataStruct<T>>) -> R) -> R {
S::with(f)
}
pub fn full() -> bool {
if MAX_COUNT == 0 {
return false;
}
Self::data(|cell| {
let d = cell.borrow();
d.loaded && d.count >= MAX_COUNT
})
}
pub fn push(pooled: T) {
if cfg!(debug_assertions) {
debug_assert!(!Self::full());
}
let new_node = bun_core::heap::into_raw(Box::new(Node::<T> {
next: ptr::null_mut(),
data: MaybeUninit::new(pooled),
}));
unsafe { Self::release(&mut *new_node) };
}
pub fn get_if_exists() -> Option<*mut Node<T>> {
Self::data(|cell| {
let mut d = cell.borrow_mut();
if !d.loaded {
return None;
}
let node = d.list.pop_first()?;
unsafe { (*node).data.assume_init_mut().reset() };
if MAX_COUNT > 0 {
d.count = d.count.saturating_sub(1);
}
Some(node)
})
}
pub fn first() -> *mut T {
unsafe { (*Self::get_node()).data.as_mut_ptr() }
}
pub fn get_node() -> *mut Node<T> {
let reused = Self::data(|cell| {
let mut d = cell.borrow_mut();
if d.loaded {
if let Some(node) = d.list.pop_first() {
unsafe { (*node).data.assume_init_mut().reset() };
if MAX_COUNT > 0 {
d.count = d.count.saturating_sub(1);
}
return Some(node);
}
}
None
});
if let Some(node) = reused {
return node;
}
if LOG_ALLOCATIONS {
}
let data = match T::INIT {
Some(init_) => MaybeUninit::new(init_().expect("unreachable")),
None => MaybeUninit::uninit(),
};
bun_core::heap::into_raw(Box::new(Node::<T> {
next: ptr::null_mut(),
data,
}))
}
pub fn get() -> PoolGuard<'static, T> {
PoolGuard {
node: Self::get_node(),
release: Self::release,
_marker: PhantomData,
}
}
pub fn release_value(value: &mut T) {
let node = unsafe { bun_core::from_field_ptr!(Node<T>, data, value) };
unsafe { Self::release(&mut *node) };
}
pub unsafe fn release(node: &mut Node<T>) {
let node_ptr: *mut Node<T> = node;
let overflowed = Self::data(|cell| {
let mut d = cell.borrow_mut();
if MAX_COUNT > 0 && d.count >= MAX_COUNT {
if LOG_ALLOCATIONS {
}
return true;
}
if MAX_COUNT > 0 {
d.count = d.count.saturating_add(1);
}
if d.loaded {
d.list.prepend(node);
} else {
d.list = SinglyLinkedList { first: node_ptr };
d.loaded = true;
}
false
});
if overflowed {
Self::destroy_node(node_ptr);
}
}
pub fn delete_all() {
let mut next = Self::data(|cell| {
let mut dat = cell.borrow_mut();
if !dat.loaded {
return ptr::null_mut();
}
dat.loaded = false;
dat.count = 0;
let head = dat.list.first;
dat.list.first = ptr::null_mut();
head
});
while !next.is_null() {
let node = next;
next = Node::next_of(node);
Self::destroy_node(node);
}
}
fn destroy_node(node: *mut Node<T>) {
unsafe {
(*node).data.assume_init_drop();
drop(bun_core::heap::take(node));
}
}
}
#[macro_export]
macro_rules! object_pool {
($vis:vis $name:ident : $ty:ty, threadsafe, $max:expr) => {
$crate::object_pool!(@storage_tls $name, $ty);
$vis type $name = $crate::pool::ObjectPool<
$ty, true, { $max }, $crate::__paste_storage!($name)
>;
};
($vis:vis $name:ident : $ty:ty, global, $max:expr) => {
$crate::object_pool!(@storage_global $name, $ty);
$vis type $name = $crate::pool::ObjectPool<
$ty, false, { $max }, $crate::__paste_storage!($name)
>;
};
(@storage_tls $name:ident, $ty:ty) => {
$crate::__object_pool_storage! { $name, $ty, tls }
};
(@storage_global $name:ident, $ty:ty) => {
$crate::__object_pool_storage! { $name, $ty, global }
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __object_pool_storage {
($name:ident, $ty:ty, tls) => {
#[allow(non_camel_case_types)]
#[doc(hidden)]
pub struct __ObjectPoolStorage;
::std::thread_local! {
static __OBJECT_POOL_DATA: ::core::cell::RefCell<
$crate::pool::DataStruct<$ty>
> = ::core::cell::RefCell::new($crate::pool::DataStruct::default());
}
impl $crate::pool::PoolStorage<$ty> for __ObjectPoolStorage {
fn with<R>(
f: impl FnOnce(&::core::cell::RefCell<$crate::pool::DataStruct<$ty>>) -> R,
) -> R {
__OBJECT_POOL_DATA.with(|cell| f(cell))
}
}
};
($name:ident, $ty:ty, global) => {
#[allow(non_camel_case_types)]
#[doc(hidden)]
pub struct __ObjectPoolStorage;
impl $crate::pool::PoolStorage<$ty> for __ObjectPoolStorage {
fn with<R>(
f: impl FnOnce(&::core::cell::RefCell<$crate::pool::DataStruct<$ty>>) -> R,
) -> R {
::std::thread_local! {
static __OBJECT_POOL_DATA: ::core::cell::RefCell<
$crate::pool::DataStruct<$ty>
> = ::core::cell::RefCell::new($crate::pool::DataStruct::default());
}
__OBJECT_POOL_DATA.with(|cell| f(cell))
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __paste_storage {
($name:ident) => {
__ObjectPoolStorage
};
}
impl ObjectPoolType for bun_core::MutableString {
const INIT: Option<fn() -> Result<Self, Error>> =
Some(|| bun_core::MutableString::init2048().map_err(Into::into));
#[inline]
fn reset(&mut self) {
bun_core::MutableString::reset(self);
}
}