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;
assert!(
!current_elm.is_null(),
"SinglyLinkedList::remove: node not in list (list is empty)",
);
unsafe {
loop {
let next = (*current_elm).next;
if next == node {
break;
}
assert!(
!next.is_null(),
"SinglyLinkedList::remove: node not found in list",
);
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);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn boxed_node(v: u32) -> *mut Node<u32> {
Box::into_raw(Box::new(Node {
next: ptr::null_mut(),
data: MaybeUninit::new(v),
}))
}
unsafe fn free_node(node: *mut Node<u32>) {
unsafe { drop(Box::from_raw(node)) };
}
fn list_of(vs: &[u32]) -> (SinglyLinkedList<u32>, Vec<*mut Node<u32>>) {
let mut list = SinglyLinkedList::default();
let mut nodes = Vec::with_capacity(vs.len());
for v in vs.iter().rev() {
let n = boxed_node(*v);
unsafe { list.prepend(&mut *n) };
nodes.push(n);
}
nodes.reverse();
(list, nodes)
}
unsafe fn values(list: &SinglyLinkedList<u32>) -> Vec<u32> {
let mut values = Vec::new();
let mut it = list.first;
while !it.is_null() {
values.push(unsafe { *(*it).data.assume_init_ref() });
it = unsafe { (*it).next };
}
values
}
unsafe fn drain(list: &mut SinglyLinkedList<u32>) -> Vec<u32> {
let mut values = Vec::new();
while let Some(node) = list.pop_first() {
values.push(unsafe { *(*node).data.assume_init_ref() });
unsafe { free_node(node) };
}
values
}
#[test]
fn remove_head_middle_tail() {
let (mut list, nodes) = list_of(&[1, 2, 3]);
let (a, b, c) = (nodes[0], nodes[1], nodes[2]);
unsafe {
list.remove(&*a); assert_eq!(values(&list), vec![2, 3]);
list.remove(&*b); assert_eq!(values(&list), vec![3]);
list.remove(&*c); assert_eq!(values(&list), Vec::<u32>::new());
}
assert_eq!(list.len(), 0);
assert!(list.first.is_null());
unsafe { assert!(drain(&mut list).is_empty()) };
for n in nodes {
unsafe { free_node(n) };
}
}
#[test]
fn remove_single_element_empties_list() {
let (mut list, nodes) = list_of(&[7]);
unsafe { list.remove(&*nodes[0]) };
assert_eq!(list.len(), 0);
assert!(list.first.is_null());
unsafe { assert!(drain(&mut list).is_empty()) };
unsafe { free_node(nodes[0]) };
}
#[test]
#[should_panic(expected = "list is empty")]
fn remove_on_empty_list_panics() {
let mut list = SinglyLinkedList::<u32>::default();
let stranger = boxed_node(0);
list.remove(unsafe { &*stranger });
}
#[test]
#[should_panic(expected = "node not found in list")]
fn remove_node_not_in_list_panics() {
let (mut list, _nodes) = list_of(&[1, 2]);
let stranger = boxed_node(9);
list.remove(unsafe { &*stranger });
}
#[test]
fn failed_remove_leaves_list_intact() {
let (mut list, _nodes) = list_of(&[1, 2, 3]);
let stranger = boxed_node(9);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
list.remove(unsafe { &*stranger });
}));
assert!(result.is_err());
unsafe { assert_eq!(drain(&mut list), vec![1, 2, 3]) };
unsafe { free_node(stranger) };
}
}