use core::cell::{Cell, UnsafeCell};
use core::marker::PhantomData;
use core::mem::{ManuallyDrop, MaybeUninit, size_of};
use core::ops::Deref;
use core::ptr::NonNull;
use bun_core::asan;
#[repr(C)]
pub struct HiveBitSet<const CAPACITY: usize> {
masks: [Cell<usize>; HIVE_BITSET_WORDS],
}
const HIVE_BITSET_WORDS: usize = 32;
const WORD_BITS: usize = usize::BITS as usize;
impl<const CAPACITY: usize> HiveBitSet<CAPACITY> {
const NUM_WORDS: usize = if CAPACITY == 0 {
0
} else {
CAPACITY.div_ceil(WORD_BITS)
};
const _FITS: () = assert!(
CAPACITY <= HIVE_BITSET_WORDS * WORD_BITS,
"HiveArray CAPACITY exceeds HiveBitSet backing (raise HIVE_BITSET_WORDS)"
);
const LAST_WORD_MASK: usize = {
let rem = CAPACITY % WORD_BITS;
if rem == 0 {
usize::MAX
} else {
(1usize << rem) - 1
}
};
pub const fn init_empty() -> Self {
Self {
masks: [const { Cell::new(0) }; HIVE_BITSET_WORDS],
}
}
#[inline]
pub fn is_set(&self, index: usize) -> bool {
debug_assert!(index < CAPACITY);
(self.masks[index / WORD_BITS].get() >> (index % WORD_BITS)) & 1 != 0
}
#[inline]
pub(crate) fn set(&self, index: usize) {
debug_assert!(index < CAPACITY);
let w = index / WORD_BITS;
self.masks[w].set(self.masks[w].get() | (1usize << (index % WORD_BITS)));
}
#[inline]
pub(crate) fn unset(&self, index: usize) {
debug_assert!(index < CAPACITY);
let w = index / WORD_BITS;
self.masks[w].set(self.masks[w].get() & !(1usize << (index % WORD_BITS)));
}
#[inline]
pub fn find_first_set(&self) -> Option<usize> {
let mut i = 0;
while i < Self::NUM_WORDS {
let m = self.masks[i].get();
if m != 0 {
return Some(i * WORD_BITS + m.trailing_zeros() as usize);
}
i += 1;
}
None
}
#[inline]
pub fn find_first_unset(&self) -> Option<usize> {
let mut i = 0;
while i < Self::NUM_WORDS {
let live_mask = if i + 1 == Self::NUM_WORDS {
Self::LAST_WORD_MASK
} else {
usize::MAX
};
let inv = !self.masks[i].get() & live_mask;
if inv != 0 {
return Some(i * WORD_BITS + inv.trailing_zeros() as usize);
}
i += 1;
}
None
}
#[inline]
pub fn iter_set(&self) -> HiveBitSetIter<CAPACITY> {
self.iterator::<true, true>()
}
#[inline]
pub fn iterator<const KIND_SET: bool, const DIR_FWD: bool>(&self) -> HiveBitSetIter<CAPACITY> {
const {
assert!(
KIND_SET && DIR_FWD,
"HiveBitSet::iterator only supports <true,true>"
)
};
HiveBitSetIter {
masks: self.masks.each_ref().map(Cell::get),
word: 0,
}
}
}
pub struct HiveBitSetIter<const CAPACITY: usize> {
masks: [usize; HIVE_BITSET_WORDS],
word: usize,
}
impl<const CAPACITY: usize> HiveBitSetIter<CAPACITY> {
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<usize> {
while self.word < HiveBitSet::<CAPACITY>::NUM_WORDS {
let m = self.masks[self.word];
if m != 0 {
let bit = m.trailing_zeros() as usize;
self.masks[self.word] &= m - 1;
return Some(self.word * WORD_BITS + bit);
}
self.word += 1;
}
None
}
}
pub struct HiveArray<T, const CAPACITY: usize> {
buffer: UnsafeCell<[MaybeUninit<T>; CAPACITY]>,
pub used: HiveBitSet<CAPACITY>,
}
impl<T, const CAPACITY: usize> HiveArray<T, CAPACITY> {
pub const SIZE: usize = CAPACITY;
pub const fn init() -> Self {
Self {
buffer: UnsafeCell::new([const { MaybeUninit::uninit() }; CAPACITY]),
used: HiveBitSet::init_empty(),
}
}
#[inline]
pub unsafe fn init_in_place(out: *mut Self) {
unsafe {
core::ptr::addr_of_mut!((*out).used).write(HiveBitSet::init_empty());
}
}
#[inline]
pub fn ptr_at(&self, index: usize) -> *mut T {
assert!(index < CAPACITY);
unsafe {
self.buffer
.get()
.cast::<MaybeUninit<T>>()
.add(index)
.cast::<T>()
}
}
#[inline]
pub fn alloc(&self, value: T) -> Option<HiveBox<'_, T, CAPACITY>> {
let index = self.used.find_first_unset()?;
self.used.set(index);
let p = self.ptr_at(index);
asan::unpoison(p.cast(), size_of::<T>());
unsafe { p.write(value) };
Some(HiveBox {
slot: unsafe { NonNull::new_unchecked(p) },
owner: self,
})
}
#[inline]
pub unsafe fn box_at(&self, index: usize) -> Option<HiveBox<'_, T, CAPACITY>> {
if index >= CAPACITY || !self.used.is_set(index) {
return None;
}
Some(HiveBox {
slot: unsafe { NonNull::new_unchecked(self.ptr_at(index)) },
owner: self,
})
}
#[deprecated = "returns *mut T to uninitialized memory; use get_init / emplace / claim"]
pub fn get(&self) -> Option<*mut T> {
let index = self.used.find_first_unset()?;
self.used.set(index);
let ret = self.ptr_at(index);
asan::unpoison(ret.cast(), size_of::<T>());
Some(ret)
}
#[inline]
pub fn get_init(&self, value: T) -> Option<NonNull<T>> {
Some(self.claim()?.write(value))
}
#[inline]
pub fn emplace(&self, init: impl FnOnce(NonNull<T>) -> T) -> Option<NonNull<T>> {
let slot = self.claim()?;
let addr = slot.addr();
Some(slot.write(init(addr)))
}
pub fn claim(&self) -> Option<HiveSlot<'_, T, CAPACITY>> {
let index = self.used.find_first_unset()?;
self.used.set(index);
let slot = unsafe {
NonNull::new_unchecked(self.buffer.get().cast::<MaybeUninit<T>>().add(index))
};
asan::unpoison(slot.as_ptr().cast(), size_of::<T>());
Some(HiveSlot {
slot,
owner: core::ptr::from_ref(self),
_marker: PhantomData,
})
}
pub unsafe fn put_raw(&self, value: *mut T) -> bool {
let Some(index) = self.index_of(value) else {
return false;
};
debug_assert!(self.used.is_set(index as usize));
asan::poison(value.cast(), size_of::<T>());
self.used.unset(index as usize);
true
}
pub fn at(&self, index: u16) -> *mut T {
debug_assert!((index as usize) < CAPACITY);
let ret = self.ptr_at(index as usize);
asan::assert_unpoisoned(ret.cast::<u8>());
ret
}
pub fn index_of(&self, value: *const T) -> Option<u32> {
asan::assert_unpoisoned(value.cast::<u8>());
let start = self.buffer.get().cast::<T>();
let end = start.wrapping_add(CAPACITY);
if !((value as usize) >= (start as usize) && (value as usize) < (end as usize)) {
return None;
}
let index = ((value as usize) - (start as usize)) / size_of::<T>();
debug_assert!(index < CAPACITY);
debug_assert!(self.ptr_at(index).cast_const() == value);
Some(u32::try_from(index).expect("int cast"))
}
pub fn r#in(&self, value: *const T) -> bool {
asan::assert_unpoisoned(value.cast::<u8>());
let start = self.buffer.get().cast::<T>();
let end = start.wrapping_add(CAPACITY);
(value as usize) >= (start as usize) && (value as usize) < (end as usize)
}
pub unsafe fn put(&self, value: *mut T) -> bool {
let Some(index) = self.index_of(value) else {
return false;
};
debug_assert!(self.used.is_set(index as usize));
debug_assert!(self.ptr_at(index as usize).cast_const() == value.cast_const());
unsafe { core::ptr::drop_in_place(value) };
asan::poison(value.cast(), size_of::<T>());
self.used.unset(index as usize);
true
}
}
#[must_use = "claimed hive slot is leaked if neither written nor dropped"]
pub struct HiveSlot<'h, T, const CAPACITY: usize> {
slot: NonNull<MaybeUninit<T>>,
owner: *const HiveArray<T, CAPACITY>,
_marker: PhantomData<&'h HiveArray<T, CAPACITY>>,
}
impl<'h, T, const CAPACITY: usize> HiveSlot<'h, T, CAPACITY> {
#[inline]
pub fn addr(&self) -> NonNull<T> {
self.slot.cast::<T>()
}
#[inline]
pub unsafe fn as_uninit(&mut self) -> &mut MaybeUninit<T> {
unsafe { self.slot.as_mut() }
}
#[inline]
pub fn write(self, value: T) -> NonNull<T> {
let this = ManuallyDrop::new(self);
let p = this.slot.cast::<T>();
unsafe { p.as_ptr().write(value) };
p
}
#[inline]
pub unsafe fn assume_init(self) -> NonNull<T> {
let this = ManuallyDrop::new(self);
this.slot.cast::<T>()
}
}
impl<T, const CAPACITY: usize> Drop for HiveSlot<'_, T, CAPACITY> {
fn drop(&mut self) {
if !self.owner.is_null() {
let hive = self.owner;
unsafe {
let index = (*hive)
.index_of(self.slot.as_ptr().cast::<T>())
.expect("HiveSlot points outside its owning hive");
asan::poison(self.slot.as_ptr().cast(), size_of::<T>());
(*hive).used.unset(index as usize);
}
} else {
drop(unsafe { Box::from_raw(self.slot.as_ptr()) });
}
}
}
pub struct HiveBox<'a, T, const CAPACITY: usize> {
slot: NonNull<T>,
owner: &'a HiveArray<T, CAPACITY>,
}
impl<'a, T, const CAPACITY: usize> HiveBox<'a, T, CAPACITY> {
#[inline]
pub fn index(&self) -> usize {
self.owner
.index_of(self.slot.as_ptr())
.expect("HiveBox slot in owner") as usize
}
#[inline]
pub fn into_inner(self) -> T {
let this = ManuallyDrop::new(self);
let value = unsafe { core::ptr::read(this.slot.as_ptr()) };
unsafe { this.owner.put_raw(this.slot.as_ptr()) };
value
}
}
impl<T, const CAPACITY: usize> Deref for HiveBox<'_, T, CAPACITY> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { self.slot.as_ref() }
}
}
impl<T, const CAPACITY: usize> core::ops::DerefMut for HiveBox<'_, T, CAPACITY> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { self.slot.as_mut() }
}
}
impl<T, const CAPACITY: usize> Drop for HiveBox<'_, T, CAPACITY> {
#[inline]
fn drop(&mut self) {
unsafe { self.owner.put(self.slot.as_ptr()) };
}
}
pub struct Fallback<T, const CAPACITY: usize> {
pub hive: HiveArray<T, CAPACITY>,
}
impl<T, const CAPACITY: usize> Fallback<T, CAPACITY> {
pub const fn init() -> Self {
Self {
hive: HiveArray::init(),
}
}
#[inline]
pub unsafe fn init_in_place(out: *mut Self) {
unsafe { HiveArray::<T, CAPACITY>::init_in_place(core::ptr::addr_of_mut!((*out).hive)) };
}
#[inline]
pub fn new_boxed() -> NonNull<Self> {
let mut boxed = Box::<Self>::new_uninit();
unsafe { Self::init_in_place(boxed.as_mut_ptr()) };
NonNull::from(Box::leak(unsafe { boxed.assume_init() }))
}
#[deprecated = "returns *mut T to uninitialized memory; use get_init / emplace / claim"]
pub fn get(&self) -> *mut T {
ManuallyDrop::new(self.claim()).addr().as_ptr()
}
#[deprecated = "returns *mut T to uninitialized memory; use get_init / emplace / claim"]
pub fn get_and_see_if_new(&self, new: &mut bool) -> *mut T {
if CAPACITY > 0 {
#[allow(deprecated)]
if let Some(value) = self.hive.get() {
*new = false;
return value;
}
}
bun_core::heap::into_raw(Box::<T>::new_uninit()).cast::<T>()
}
#[deprecated = "returns *mut T to uninitialized memory; use get_init / emplace / claim"]
pub fn try_get(&self) -> *mut T {
ManuallyDrop::new(self.claim()).addr().as_ptr()
}
#[inline]
pub fn get_init(&self, value: T) -> NonNull<T> {
self.claim().write(value)
}
#[inline]
pub fn emplace(&self, init: impl FnOnce(NonNull<T>) -> T) -> NonNull<T> {
let slot = self.claim();
let addr = slot.addr();
slot.write(init(addr))
}
pub fn claim(&self) -> HiveSlot<'_, T, CAPACITY> {
if CAPACITY > 0 {
if let Some(slot) = self.hive.claim() {
return slot;
}
}
let slot = NonNull::from(Box::leak(Box::<T>::new_uninit()));
HiveSlot {
slot,
owner: core::ptr::null(),
_marker: PhantomData,
}
}
pub unsafe fn put_raw(&self, value: *mut T) {
if CAPACITY > 0 {
if unsafe { self.hive.put_raw(value) } {
return;
}
}
drop(unsafe { Box::from_raw(value.cast::<MaybeUninit<T>>()) });
}
pub fn r#in(&self, value: *const T) -> bool {
if CAPACITY > 0 {
if self.hive.r#in(value) {
return true;
}
}
false
}
pub unsafe fn put(&self, value: *mut T) {
if CAPACITY > 0 {
if unsafe { self.hive.put(value) } {
return;
}
}
unsafe { bun_core::heap::destroy(value) };
}
}
#[repr(C)]
pub struct HiveRef<T, const CAPACITY: usize> {
pub ref_count: Cell<u32>,
pub pool: *const Fallback<HiveRef<T, CAPACITY>, CAPACITY>,
pub value: T,
}
impl<T, const CAPACITY: usize> HiveRef<T, CAPACITY> {
pub unsafe fn init(value: T, pool: *const Fallback<Self, CAPACITY>) -> *mut Self {
unsafe {
(*pool)
.get_init(HiveRef {
ref_count: Cell::new(1),
pool,
value,
})
.as_ptr()
}
}
#[inline]
pub fn ref_(&self) -> &Self {
self.ref_count.set(self.ref_count.get() + 1);
self
}
pub unsafe fn unref(this: *mut Self) -> Option<*mut Self> {
unsafe {
let ref_count = (*this).ref_count.get();
(*this).ref_count.set(ref_count - 1);
if ref_count == 1 {
let pool = (*this).pool;
(*pool).put(this);
return None;
}
}
Some(this)
}
}
pub struct HiveRefHandle<T, const CAP: usize> {
ptr: NonNull<HiveRef<T, CAP>>,
}
impl<T, const CAP: usize> HiveRefHandle<T, CAP> {
#[inline]
fn slot(&self) -> &HiveRef<T, CAP> {
unsafe { self.ptr.as_ref() }
}
pub unsafe fn new(value: T, pool: *const Fallback<HiveRef<T, CAP>, CAP>) -> Self {
let ptr = unsafe { HiveRef::init(value, pool) };
Self {
ptr: NonNull::new(ptr).expect("Fallback::get_init returned null"),
}
}
#[inline]
pub fn into_raw(self) -> *mut HiveRef<T, CAP> {
ManuallyDrop::new(self).ptr.as_ptr()
}
#[inline]
pub unsafe fn from_raw(ptr: *mut HiveRef<T, CAP>) -> Self {
Self {
ptr: NonNull::new(ptr).expect("HiveRefHandle::from_raw(null)"),
}
}
#[inline]
pub fn as_ptr(&self) -> *mut HiveRef<T, CAP> {
self.ptr.as_ptr()
}
#[inline]
pub fn get_mut(&mut self) -> Option<&mut T> {
if self.slot().ref_count.get() != 1 {
return None;
}
Some(unsafe { &mut (*self.ptr.as_ptr()).value })
}
}
impl<T, const CAP: usize> Deref for HiveRefHandle<T, CAP> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.slot().value
}
}
impl<T, const CAP: usize> Clone for HiveRefHandle<T, CAP> {
#[inline]
fn clone(&self) -> Self {
self.slot().ref_();
Self { ptr: self.ptr }
}
}
impl<T, const CAP: usize> Drop for HiveRefHandle<T, CAP> {
#[inline]
fn drop(&mut self) {
unsafe { HiveRef::unref(self.ptr.as_ptr()) };
}
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
#[test]
fn hive_array() {
const SIZE: usize = 64;
type Int = u128;
let a = HiveArray::<Int, SIZE>::init();
{
let b = a.get().unwrap();
unsafe { *b = 0 };
assert!(a.get().unwrap() != b);
assert_eq!(a.index_of(b), Some(0));
assert!(unsafe { a.put(b) });
assert!(a.get().unwrap() == b);
let c = a.get().unwrap();
unsafe { *c = 123 };
let mut d: Int = 12345;
assert!(unsafe { a.put(&mut d) } == false);
assert!(a.r#in(&d) == false);
}
let mut a = a;
a.used = HiveBitSet::init_empty();
{
for i in 0..SIZE {
let b = a.get().unwrap();
unsafe { *b = 0 };
assert_eq!(a.index_of(b), Some(u32::try_from(i).expect("int cast")));
assert!(unsafe { a.put(b) });
assert!(a.get().unwrap() == b);
}
for _ in 0..SIZE {
assert!(a.get().is_none());
}
}
}
struct Tracked<'c> {
v: u64,
drops: &'c core::cell::Cell<u32>,
}
impl Drop for Tracked<'_> {
fn drop(&mut self) {
self.drops.set(self.drops.get() + 1);
}
}
#[test]
fn hive_slot_drop_releases_without_dtor() {
let drops = core::cell::Cell::new(0u32);
let mk = |v| Tracked { v, drops: &drops };
let a = HiveArray::<Tracked, 4>::init();
drop(a.claim().unwrap());
assert!(!a.used.is_set(0));
assert_eq!(drops.get(), 0);
let p = a.get_init(mk(7)).unwrap();
assert!(a.used.is_set(0));
assert_eq!(drops.get(), 0);
unsafe { a.put(p.as_ptr()) };
assert_eq!(drops.get(), 1);
let p = a.get_init(mk(8)).unwrap();
assert!(unsafe { a.put_raw(p.as_ptr()) });
assert_eq!(drops.get(), 1);
let f = Fallback::<Tracked, 0>::init();
drop(f.claim());
assert_eq!(drops.get(), 1);
let p = f.get_init(mk(9));
unsafe { f.put(p.as_ptr()) };
assert_eq!(drops.get(), 2);
}
#[test]
fn emplace_sees_own_address() {
struct SelfAddr {
me: *const SelfAddr,
tag: u32,
}
let a = HiveArray::<SelfAddr, 4>::init();
let p = a
.emplace(|addr| SelfAddr {
me: addr.as_ptr(),
tag: 1,
})
.unwrap();
unsafe {
assert_eq!((*p.as_ptr()).me, p.as_ptr().cast_const());
assert_eq!((*p.as_ptr()).tag, 1);
}
assert!(unsafe { a.put_raw(p.as_ptr()) });
let f = Fallback::<SelfAddr, 0>::init();
let p = f.emplace(|addr| SelfAddr {
me: addr.as_ptr(),
tag: 2,
});
unsafe {
assert_eq!((*p.as_ptr()).me, p.as_ptr().cast_const());
assert_eq!((*p.as_ptr()).tag, 2);
f.put_raw(p.as_ptr());
}
}
#[test]
fn slot_addr_as_uninit_assume_init() {
let a = HiveArray::<[u32; 2], 4>::init();
let mut slot = a.claim().unwrap();
let pre = slot.addr().as_ptr();
unsafe { slot.as_uninit() }.write([10, 20]);
let p = unsafe { slot.assume_init() };
assert_eq!(pre, p.as_ptr());
unsafe {
assert_eq!(*p.as_ptr(), [10, 20]);
}
assert!(unsafe { a.put_raw(p.as_ptr()) });
let slot = a.claim().unwrap();
let pre = slot.addr().as_ptr();
let p = slot.write([30, 40]);
assert_eq!(pre, p.as_ptr());
assert!(unsafe { a.put_raw(p.as_ptr()) });
}
#[test]
fn at_returns_claimed_slot() {
let a = HiveArray::<u64, 4>::init();
let p0 = a.get_init(100).unwrap();
let p1 = a.get_init(200).unwrap();
assert_eq!(a.at(0), p0.as_ptr());
assert_eq!(a.at(1), p1.as_ptr());
assert_eq!(a.ptr_at(0), p0.as_ptr());
assert_eq!(a.ptr_at(1), p1.as_ptr());
unsafe {
assert_eq!(*a.at(0), 100);
assert_eq!(*a.at(1), 200);
}
assert!(unsafe { a.put_raw(p0.as_ptr()) });
assert!(unsafe { a.put_raw(p1.as_ptr()) });
}
#[test]
fn fallback_inline_then_heap() {
const CAP: usize = 2;
let drops = core::cell::Cell::new(0u32);
let mk = |v| Tracked { v, drops: &drops };
let f = Fallback::<Tracked, CAP>::init();
let inline0 = f.get_init(mk(0));
let inline1 = f.get_init(mk(1));
let heap0 = f.get_init(mk(2));
let heap1 = f.get_init(mk(3));
assert!(f.r#in(inline0.as_ptr()));
assert!(f.r#in(inline1.as_ptr()));
assert!(!f.r#in(heap0.as_ptr()));
assert!(!f.r#in(heap1.as_ptr()));
unsafe {
assert_eq!((*inline0.as_ptr()).v, 0);
assert_eq!((*inline1.as_ptr()).v, 1);
assert_eq!((*heap0.as_ptr()).v, 2);
assert_eq!((*heap1.as_ptr()).v, 3);
}
unsafe {
f.put(inline0.as_ptr());
f.put(heap0.as_ptr());
}
assert_eq!(drops.get(), 2);
let reuse = f.get_init(mk(4));
assert_eq!(reuse.as_ptr(), inline0.as_ptr());
assert!(f.r#in(reuse.as_ptr()));
unsafe {
f.put(inline1.as_ptr());
f.put(heap1.as_ptr());
f.put(reuse.as_ptr());
}
assert_eq!(drops.get(), 5);
}
#[test]
fn fallback_claim_drop_inline_and_heap() {
let f = Fallback::<u64, 1>::init();
drop(f.claim());
assert!(!f.hive.used.is_set(0));
let f = Fallback::<u64, 1>::init();
let inline = f.get_init(1);
assert!(f.hive.used.is_set(0));
drop(f.claim());
assert!(f.hive.used.is_set(0));
unsafe { f.put(inline.as_ptr()) };
assert!(!f.hive.used.is_set(0));
}
#[test]
fn fallback_deprecated_get_apis() {
const CAP: usize = 1;
let f = Fallback::<u64, CAP>::init();
let p0 = f.get();
assert!(f.r#in(p0));
let p1 = f.try_get();
assert!(!f.r#in(p1));
unsafe {
p0.write(11);
p1.write(22);
assert_eq!(*p0, 11);
assert_eq!(*p1, 22);
f.put(p0);
f.put(p1);
}
let mut new = true;
let p0 = f.get_and_see_if_new(&mut new);
assert!(!new);
let mut new = true;
let p1 = f.get_and_see_if_new(&mut new);
assert!(new);
unsafe {
p0.write(33);
p1.write(44);
f.put_raw(p0);
f.put_raw(p1);
}
}
#[test]
fn fallback_new_boxed_and_init_in_place() {
const CAP: usize = 4;
let boxed = Fallback::<u64, CAP>::new_boxed();
unsafe {
let f = &*boxed.as_ptr();
for i in 0..CAP {
let p = f.get_init(i as u64 * 10);
assert!(f.r#in(p.as_ptr()));
assert_eq!(*p.as_ptr(), i as u64 * 10);
}
let p = f.get_init(999);
assert!(!f.r#in(p.as_ptr()));
f.put(p.as_ptr());
drop(Box::from_raw(boxed.as_ptr()));
}
}
#[test]
fn hive_ref_lifecycle() {
let drops = core::cell::Cell::new(0u32);
const CAP: usize = 2;
type Pool<'c> = Fallback<HiveRef<Tracked<'c>, CAP>, CAP>;
let pool: Pool = Fallback::init();
let pool_ptr: *const Pool = &pool;
let r = unsafe {
HiveRef::init(
Tracked {
v: 1,
drops: &drops,
},
pool_ptr,
)
};
unsafe {
assert_eq!((*r).ref_count.get(), 1);
(*r).ref_();
assert_eq!((*r).ref_count.get(), 2);
assert!(HiveRef::unref(r).is_some());
assert_eq!((*r).ref_count.get(), 1);
assert!(HiveRef::unref(r).is_none());
}
assert_eq!(drops.get(), 1);
let inline0 = unsafe {
HiveRef::init(
Tracked {
v: 2,
drops: &drops,
},
pool_ptr,
)
};
let inline1 = unsafe {
HiveRef::init(
Tracked {
v: 3,
drops: &drops,
},
pool_ptr,
)
};
let heap = unsafe {
HiveRef::init(
Tracked {
v: 4,
drops: &drops,
},
pool_ptr,
)
};
assert!(pool.r#in(inline0));
assert!(pool.r#in(inline1));
assert!(!pool.r#in(heap));
unsafe {
assert!(HiveRef::unref(heap).is_none());
assert!(HiveRef::unref(inline1).is_none());
assert!(HiveRef::unref(inline0).is_none());
}
assert_eq!(drops.get(), 4);
}
#[test]
fn hive_ref_handle_lifecycle() {
let drops = core::cell::Cell::new(0u32);
const CAP: usize = 1;
type Pool<'c> = Fallback<HiveRef<Tracked<'c>, CAP>, CAP>;
let pool: Pool = Fallback::init();
let pool_ptr: *const Pool = &pool;
let mut h = unsafe {
HiveRefHandle::new(
Tracked {
v: 1,
drops: &drops,
},
pool_ptr,
)
};
assert_eq!(h.v, 1);
h.get_mut().unwrap().v = 11;
assert_eq!(h.v, 11);
let h2 = h.clone();
assert_eq!(h2.v, 11);
assert!(h.get_mut().is_none());
drop(h);
assert_eq!(drops.get(), 0);
drop(h2);
assert_eq!(drops.get(), 1);
let h = unsafe {
HiveRefHandle::new(
Tracked {
v: 2,
drops: &drops,
},
pool_ptr,
)
};
let raw = h.into_raw();
assert_eq!(drops.get(), 1);
let h = unsafe { HiveRefHandle::<Tracked, CAP>::from_raw(raw) };
drop(h);
assert_eq!(drops.get(), 2);
let inline = unsafe {
HiveRefHandle::new(
Tracked {
v: 3,
drops: &drops,
},
pool_ptr,
)
};
let heap = unsafe {
HiveRefHandle::new(
Tracked {
v: 4,
drops: &drops,
},
pool_ptr,
)
};
assert!(pool.r#in(inline.as_ptr()));
assert!(!pool.r#in(heap.as_ptr()));
drop(heap);
drop(inline);
assert_eq!(drops.get(), 4);
}
#[test]
fn hive_bitset_iteration() {
let a = HiveArray::<u8, 8>::init();
assert_eq!(a.used.find_first_set(), None);
assert_eq!(a.used.find_first_unset(), Some(0));
let s0 = a.get_init(0).unwrap();
let _s1 = a.get_init(1).unwrap();
let s2 = a.get_init(2).unwrap();
let _s3 = a.get_init(3).unwrap();
assert!(unsafe { a.put_raw(s0.as_ptr()) });
assert!(unsafe { a.put_raw(s2.as_ptr()) });
assert_eq!(a.used.find_first_set(), Some(1));
assert_eq!(a.used.find_first_unset(), Some(0));
let mut it = a.used.iter_set();
assert_eq!(it.next(), Some(1));
assert_eq!(it.next(), Some(3));
assert_eq!(it.next(), None);
let mut it = a.used.iterator::<true, true>();
assert_eq!(it.next(), Some(1));
assert_eq!(it.next(), Some(3));
assert_eq!(it.next(), None);
}
#[test]
fn init_in_place_zeroes_only_bitset() {
let mut a: MaybeUninit<HiveArray<u64, 4>> = MaybeUninit::uninit();
unsafe {
HiveArray::init_in_place(a.as_mut_ptr());
let a = &*a.as_ptr();
assert_eq!(a.used.find_first_set(), None);
let p = a.get_init(7).unwrap();
assert_eq!(*p.as_ptr(), 7);
assert!(a.put_raw(p.as_ptr()));
}
}
#[test]
fn hive_box_lifecycle() {
let drops = core::cell::Cell::new(0u32);
let mk = |v| Tracked { v, drops: &drops };
let pool = HiveArray::<Tracked, 4>::init();
{
let mut b = pool.alloc(mk(1)).unwrap();
assert_eq!(b.v, 1);
b.v = 11;
assert_eq!(b.v, 11);
}
assert_eq!(drops.get(), 1);
assert!(!pool.used.is_set(0));
let b = pool.alloc(mk(2)).unwrap();
let i = b.index();
let val = b.into_inner();
assert_eq!(val.v, 2);
assert_eq!(drops.get(), 1);
assert!(!pool.used.is_set(i));
drop(val);
assert_eq!(drops.get(), 2);
let b0 = pool.alloc(mk(10)).unwrap();
let b1 = pool.alloc(mk(20)).unwrap();
let (i0, i1) = (b0.index(), b1.index());
core::mem::forget(b0);
core::mem::forget(b1);
let v1 = unsafe { pool.box_at(i1) }.unwrap().into_inner();
let v0 = unsafe { pool.box_at(i0) }.unwrap().into_inner();
assert_eq!(v0.v, 10);
assert_eq!(v1.v, 20);
assert!(!pool.used.is_set(i0));
assert!(!pool.used.is_set(i1));
assert!(unsafe { pool.box_at(i0) }.is_none());
assert!(unsafe { pool.box_at(999) }.is_none());
}
}