use alloc::vec::Vec;
use core::clone::Clone;
use core::fmt::Debug;
use core::hint::unreachable_unchecked;
use core::mem::MaybeUninit;
use core::ops::Index;
use core::ops::IndexMut;
use core::ptr::NonNull;
use bumpalo::Bump;
use crate::Ptr;
#[cfg(debug_assertions)]
pub(crate) type ArenaContainer<K, T> = alloc::boxed::Box<Arena<K, T>>;
#[cfg(not(debug_assertions))]
pub(crate) type ArenaContainer<K, T> = Arena<K, T>;
pub(crate) struct ActiveSlotRef<K, T> {
slot: NonNull<LLSlot<K, T>>,
#[cfg(debug_assertions)]
arena: *const Arena<K, T>,
}
impl<K, T> Debug for ActiveSlotRef<K, T> {
fn fmt(
&self,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "ActiveSlotRef({:p})", self.slot)
}
}
impl<K, T> ActiveSlotRef<K, T> {
#[inline(always)]
pub(crate) fn data<'a>(
&self,
_arena: &'a Arena<K, T>,
) -> &'a NodeData<K, T> {
#[cfg(debug_assertions)]
{
debug_assert_eq!(
self.arena, _arena as *const Arena<K, T>,
"ActiveSlotRef used with wrong Arena"
);
}
unsafe { self.slot.as_ref().data.assume_init_ref() }
}
#[inline(always)]
pub(crate) fn data_mut<'a>(
&mut self,
_arena: &'a mut Arena<K, T>,
) -> &'a mut NodeData<K, T> {
#[cfg(debug_assertions)]
{
debug_assert_eq!(
self.arena, _arena as *const Arena<K, T>,
"ActiveSlotRef used with wrong Arena"
);
}
unsafe { self.slot.as_mut().data.assume_init_mut() }
}
#[inline(always)]
pub(crate) fn this(
&self,
_arena: &Arena<K, T>,
) -> Ptr {
#[cfg(debug_assertions)]
{
debug_assert_eq!(
self.arena, _arena as *const Arena<K, T>,
"ActiveSlotRef used with wrong Arena"
);
}
unsafe { self.slot.as_ref().this }
}
#[inline(always)]
pub(crate) fn next(
&self,
_arena: &Arena<K, T>,
) -> ActiveSlotRef<K, T> {
#[cfg(debug_assertions)]
{
debug_assert_eq!(
self.arena, _arena as *const Arena<K, T>,
"ActiveSlotRef used with wrong Arena"
);
}
unsafe {
match &self.slot.as_ref().links {
Links::Occupied { next, .. } => *next,
Links::Vacant { .. } => unreachable_unchecked(),
}
}
}
#[inline(always)]
pub(crate) fn next_mut<'a>(
&mut self,
_arena: &'a mut Arena<K, T>,
) -> &'a mut ActiveSlotRef<K, T> {
#[cfg(debug_assertions)]
{
debug_assert_eq!(
self.arena, _arena as *const Arena<K, T>,
"ActiveSlotRef used with wrong Arena"
);
}
unsafe {
match &mut self.slot.as_mut().links {
Links::Occupied { next, .. } => next,
Links::Vacant { .. } => unreachable_unchecked(),
}
}
}
#[inline(always)]
pub(crate) fn prev(
&self,
_arena: &Arena<K, T>,
) -> ActiveSlotRef<K, T> {
#[cfg(debug_assertions)]
{
debug_assert_eq!(
self.arena, _arena as *const Arena<K, T>,
"ActiveSlotRef used with wrong Arena"
);
}
unsafe {
match &self.slot.as_ref().links {
Links::Occupied { prev, .. } => *prev,
Links::Vacant { .. } => unreachable_unchecked(),
}
}
}
#[inline(always)]
pub(crate) fn prev_mut<'a>(
&mut self,
_arena: &'a mut Arena<K, T>,
) -> &'a mut ActiveSlotRef<K, T> {
#[cfg(debug_assertions)]
{
debug_assert_eq!(
self.arena, _arena as *const Arena<K, T>,
"ActiveSlotRef used with wrong Arena"
);
}
unsafe {
match &mut self.slot.as_mut().links {
Links::Occupied { prev, .. } => prev,
Links::Vacant { .. } => unreachable_unchecked(),
}
}
}
#[inline(always)]
pub(crate) fn as_ptr(&self) -> NonNull<LLSlot<K, T>> {
self.slot
}
}
impl<K, T> Clone for ActiveSlotRef<K, T> {
fn clone(&self) -> Self {
*self
}
}
impl<K, T> Copy for ActiveSlotRef<K, T> {}
impl<K, T> PartialEq for ActiveSlotRef<K, T> {
fn eq(
&self,
other: &Self,
) -> bool {
self.slot == other.slot
}
}
impl<K, T> Eq for ActiveSlotRef<K, T> {}
pub(crate) struct NodeData<K, T> {
pub(crate) key: K,
pub(crate) value: T,
}
pub(crate) enum Links<K, T> {
Occupied {
prev: ActiveSlotRef<K, T>,
next: ActiveSlotRef<K, T>,
},
Vacant {
next_free: Option<NonNull<LLSlot<K, T>>>,
},
}
impl<K, T> Debug for Links<K, T> {
fn fmt(
&self,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
match self {
Links::Occupied { prev, next } => f
.debug_struct("Links::Occupied")
.field("prev", prev)
.field("next", next)
.finish(),
Links::Vacant { next_free } => f
.debug_struct("Links::Vacant")
.field("next_free", next_free)
.finish(),
}
}
}
pub(crate) struct LLSlot<K, T> {
pub(crate) this: Ptr,
pub(crate) links: Links<K, T>,
pub(crate) data: MaybeUninit<NodeData<K, T>>,
}
impl<K, T> Debug for LLSlot<K, T> {
fn fmt(
&self,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
f.debug_struct("LLSlot")
.field("this", &self.this)
.field("links", &self.links)
.finish()
}
}
pub(crate) struct FreedSlot<K, T> {
pub(crate) data: NodeData<K, T>,
pub(crate) this: Ptr,
pub(crate) prev_next: Option<(ActiveSlotRef<K, T>, ActiveSlotRef<K, T>)>,
}
#[derive(Debug)]
pub(crate) struct Arena<K, T> {
bump: Bump,
slots: Vec<NonNull<LLSlot<K, T>>>,
#[cfg(feature = "generational")]
generations: Vec<u32>,
free_list_head: Option<NonNull<LLSlot<K, T>>>,
}
impl<K, T> Drop for Arena<K, T> {
fn drop(&mut self) {
unsafe {
for slot_ptr in &mut self.slots {
let slot_ref = slot_ptr.as_mut();
match &slot_ref.links {
Links::Occupied { .. } => {
slot_ref.links = Links::Vacant { next_free: None };
slot_ref.data.assume_init_drop();
}
Links::Vacant { .. } => {}
}
}
}
}
}
impl<K, T> Arena<K, T> {
#[inline]
pub(crate) fn with_capacity(capacity: usize) -> ArenaContainer<K, T> {
#[cfg(debug_assertions)]
{
alloc::boxed::Box::new(Self {
bump: Bump::with_capacity(capacity * core::mem::size_of::<LLSlot<K, T>>()),
slots: Vec::with_capacity(capacity),
free_list_head: None,
#[cfg(feature = "generational")]
generations: Vec::with_capacity(capacity),
})
}
#[cfg(not(debug_assertions))]
Self {
bump: Bump::with_capacity(capacity * core::mem::size_of::<LLSlot<K, T>>()),
slots: Vec::with_capacity(capacity),
free_list_head: None,
#[cfg(feature = "generational")]
generations: Vec::with_capacity(capacity),
}
}
#[inline]
pub(crate) fn map_ptr(
&self,
ptr: Ptr,
) -> Option<ActiveSlotRef<K, T>> {
self.slots
.get(ptr.unchecked_get())
.filter(|slot| {
unsafe { matches!(slot.as_ref().links, Links::Occupied { .. }) }
})
.filter(|_slot| {
#[cfg(feature = "generational")]
unsafe {
let index = _slot.as_ref().this.unchecked_get();
self.generations.get(index) == Some(&ptr.generation)
}
#[cfg(not(feature = "generational"))]
true
})
.map(|&slot| ActiveSlotRef {
slot,
#[cfg(debug_assertions)]
arena: self as *const Arena<K, T>,
})
}
#[inline]
pub(crate) fn is_occupied(
&self,
ptr: Ptr,
) -> bool {
self.slots.get(ptr.unchecked_get()).is_some_and(|slot| {
if unsafe { !matches!(slot.as_ref().links, Links::Occupied { .. }) } {
return false;
}
#[cfg(feature = "generational")]
unsafe {
let index = slot.as_ref().this.unchecked_get();
self.generations.get(index) == Some(&ptr.generation)
}
#[cfg(not(feature = "generational"))]
true
})
}
#[inline]
pub(crate) fn alloc_circular(
&mut self,
key: K,
value: T,
) -> ActiveSlotRef<K, T> {
if let Some(mut free_slot) = self.free_list_head {
unsafe {
let next_free = match &free_slot.as_ref().links {
Links::Vacant { next_free } => *next_free,
Links::Occupied { .. } => unreachable_unchecked(),
};
self.free_list_head = next_free;
let active_slot = ActiveSlotRef {
slot: free_slot,
#[cfg(debug_assertions)]
arena: self as *const Arena<K, T>,
};
free_slot.as_mut().links = Links::Occupied {
prev: active_slot,
next: active_slot,
};
free_slot.as_mut().data = MaybeUninit::new(NodeData { key, value });
#[cfg(feature = "generational")]
{
let index = free_slot.as_ref().this.unchecked_get();
self.generations[index] = self.generations[index].wrapping_add(1);
free_slot.as_mut().this = Ptr::unchecked_from(index, self.generations[index]);
}
active_slot
}
} else {
#[cfg(feature = "generational")]
let ptr = Ptr::unchecked_from(self.slots.len(), 0);
#[cfg(not(feature = "generational"))]
let ptr = Ptr::unchecked_from(self.slots.len());
let slot = self.bump.alloc(LLSlot {
this: ptr,
links: Links::Vacant { next_free: None },
data: MaybeUninit::new(NodeData { key, value }),
});
let mut slot_ptr = NonNull::from_mut(slot);
let active_slot = ActiveSlotRef {
slot: slot_ptr,
#[cfg(debug_assertions)]
arena: self as *const Arena<K, T>,
};
unsafe {
slot_ptr.as_mut().links = Links::Occupied {
prev: active_slot,
next: active_slot,
};
}
self.slots.push(slot_ptr);
#[cfg(feature = "generational")]
self.generations.push(0);
active_slot
}
}
#[inline]
pub(crate) fn alloc(
&mut self,
key: K,
value: T,
prev: ActiveSlotRef<K, T>,
next: ActiveSlotRef<K, T>,
) -> ActiveSlotRef<K, T> {
if let Some(mut free_slot) = self.free_list_head {
unsafe {
let next_free = match &free_slot.as_ref().links {
Links::Vacant { next_free } => *next_free,
Links::Occupied { .. } => unreachable_unchecked(),
};
self.free_list_head = next_free;
let active_slot = ActiveSlotRef {
slot: free_slot,
#[cfg(debug_assertions)]
arena: self as *const Arena<K, T>,
};
free_slot.as_mut().links = Links::Occupied { prev, next };
free_slot.as_mut().data = MaybeUninit::new(NodeData { key, value });
#[cfg(feature = "generational")]
{
let index = free_slot.as_ref().this.unchecked_get();
self.generations[index] = self.generations[index].wrapping_add(1);
free_slot.as_mut().this = Ptr::unchecked_from(index, self.generations[index]);
}
active_slot
}
} else {
#[cfg(feature = "generational")]
let ptr = Ptr::unchecked_from(self.slots.len(), 0);
#[cfg(not(feature = "generational"))]
let ptr = Ptr::unchecked_from(self.slots.len());
let slot = self.bump.alloc(LLSlot {
this: ptr,
links: Links::Occupied { prev, next },
data: MaybeUninit::new(NodeData { key, value }),
});
let slot_ptr = NonNull::from_mut(slot);
let active_slot = ActiveSlotRef {
slot: slot_ptr,
#[cfg(debug_assertions)]
arena: self as *const Arena<K, T>,
};
self.slots.push(slot_ptr);
#[cfg(feature = "generational")]
self.generations.push(0);
active_slot
}
}
#[inline]
pub(crate) unsafe fn free_and_unlink(
&mut self,
mut slot: ActiveSlotRef<K, T>,
) -> FreedSlot<K, T> {
let (prev_next, data, this) = unsafe {
let slot_ref = slot.slot.as_mut();
match slot_ref.links {
Links::Occupied { mut prev, mut next } => {
let data = slot_ref.data.assume_init_read();
let this = slot_ref.this;
slot_ref.links = Links::Vacant {
next_free: self.free_list_head,
};
self.free_list_head = Some(slot.slot);
let prev_next = if prev == slot || next == slot {
None
} else {
*prev.next_mut(self) = next;
*next.prev_mut(self) = prev;
Some((prev, next))
};
(prev_next, data, this)
}
Links::Vacant { .. } => unreachable_unchecked(),
}
};
FreedSlot {
data,
this,
prev_next,
}
}
}
impl<K, T> Index<Ptr> for Arena<K, T> {
type Output = NodeData<K, T>;
#[inline]
fn index(
&self,
index: Ptr,
) -> &Self::Output {
self.map_ptr(index)
.as_ref()
.expect("Indexing with invalid or freed Ptr")
.data(self)
}
}
impl<K, T> IndexMut<Ptr> for Arena<K, T> {
#[inline]
fn index_mut(
&mut self,
index: Ptr,
) -> &mut Self::Output {
self.map_ptr(index)
.as_mut()
.expect("Indexing with invalid or freed Ptr")
.data_mut(self)
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use super::*;
#[test]
fn test_arena_creation() {
let arena: ArenaContainer<i32, String> = Arena::with_capacity(10);
assert_eq!(arena.slots.len(), 0);
}
#[test]
fn test_alloc_circular_single_element() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(1, "first".to_string());
assert_eq!(slot.data(&arena).key, 1);
assert_eq!(slot.data(&arena).value, "first");
assert_eq!(slot.next(&arena), slot);
assert_eq!(slot.prev(&arena), slot);
assert_eq!(arena.slots.len(), 1);
}
#[test]
fn test_alloc_circular_multiple_elements() {
let mut arena = Arena::with_capacity(5);
let first = arena.alloc_circular(1, "first".to_string());
let second = arena.alloc_circular(2, "second".to_string());
assert_eq!(first.data(&arena).key, 1);
assert_eq!(second.data(&arena).key, 2);
assert_eq!(arena.slots.len(), 2);
assert_eq!(first.next(&arena), first);
assert_eq!(first.prev(&arena), first);
assert_eq!(second.next(&arena), second);
assert_eq!(second.prev(&arena), second);
}
#[test]
fn test_alloc_with_links() {
let mut arena = Arena::with_capacity(5);
let first = arena.alloc_circular(1, "first".to_string());
let second = arena.alloc(2, "second".to_string(), first, first);
assert_eq!(second.data(&arena).key, 2);
assert_eq!(second.prev(&arena), first);
assert_eq!(second.next(&arena), first);
assert_eq!(arena.slots.len(), 2);
}
#[test]
fn test_active_slot_ref_data_access() {
let mut arena = Arena::with_capacity(5);
let mut slot = arena.alloc_circular(42, "test".to_string());
let data = slot.data(&arena);
assert_eq!(data.key, 42);
assert_eq!(data.value, "test");
let data_mut = slot.data_mut(&mut arena);
data_mut.value = "modified".to_string();
assert_eq!(slot.data(&arena).value, "modified");
}
#[test]
fn test_active_slot_ref_navigation() {
let mut arena = Arena::with_capacity(5);
let mut first = arena.alloc_circular(1, "first".to_string());
let mut second = arena.alloc(2, "second".to_string(), first, first);
let third = arena.alloc(3, "third".to_string(), second, first);
*second.next_mut(&mut arena) = third;
*first.prev_mut(&mut arena) = third;
assert_eq!(second.next(&arena), third);
assert_eq!(third.prev(&arena), second);
}
#[test]
fn test_active_slot_ref_this() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(1, "test".to_string());
let ptr = slot.this(&arena);
assert_eq!(ptr.unchecked_get(), 0);
}
#[test]
fn test_active_slot_ref_as_ptr() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(1, "test".to_string());
let raw_ptr = slot.as_ptr();
assert_eq!(raw_ptr, slot.slot);
}
#[test]
fn test_active_slot_ref_equality() {
let mut arena = Arena::with_capacity(5);
let slot1 = arena.alloc_circular(1, "test".to_string());
let slot2 = slot1;
let slot3 = arena.alloc_circular(2, "other".to_string());
assert_eq!(slot1, slot2);
assert_ne!(slot1, slot3);
}
#[test]
fn test_active_slot_ref_debug() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(1, "test".to_string());
let debug_str = format!("{:?}", slot);
assert!(debug_str.starts_with("ActiveSlotRef("));
}
#[test]
fn test_map_ptr_valid() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
let mapped = arena.map_ptr(ptr);
assert!(mapped.is_some());
assert_eq!(mapped.unwrap().data(&arena).key, 42);
}
#[test]
fn test_map_ptr_invalid_index() {
let arena = Arena::<i32, String>::with_capacity(5);
#[cfg(not(feature = "generational"))]
let invalid_ptr = Ptr::unchecked_from(999);
#[cfg(feature = "generational")]
let invalid_ptr = Ptr::unchecked_from(999, 0);
let mapped = arena.map_ptr(invalid_ptr);
assert!(mapped.is_none());
}
#[test]
fn test_is_occupied_valid() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
assert!(arena.is_occupied(ptr));
}
#[test]
fn test_is_occupied_invalid() {
let arena = Arena::<i32, String>::with_capacity(5);
#[cfg(not(feature = "generational"))]
let invalid_ptr = Ptr::unchecked_from(999);
#[cfg(feature = "generational")]
let invalid_ptr = Ptr::unchecked_from(999, 0);
assert!(!arena.is_occupied(invalid_ptr));
}
#[test]
fn test_free_and_unlink_single_element() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
let freed = unsafe { arena.free_and_unlink(slot) };
assert_eq!(freed.data.key, 42);
assert_eq!(freed.data.value, "test");
assert_eq!(freed.this, ptr);
assert!(freed.prev_next.is_none());
assert!(!arena.is_occupied(ptr));
assert!(arena.free_list_head.is_some());
}
#[test]
fn test_free_and_unlink_with_neighbors() {
let mut arena = Arena::with_capacity(5);
let mut first = arena.alloc_circular(1, "first".to_string());
let mut second = arena.alloc(2, "second".to_string(), first, first);
let mut third = arena.alloc(3, "third".to_string(), second, first);
*first.next_mut(&mut arena) = second;
*first.prev_mut(&mut arena) = third;
*second.next_mut(&mut arena) = third;
*second.prev_mut(&mut arena) = first;
*third.next_mut(&mut arena) = first;
*third.prev_mut(&mut arena) = second;
let second_ptr = second.this(&arena);
let freed = unsafe { arena.free_and_unlink(second) };
assert_eq!(freed.data.key, 2);
assert!(freed.prev_next.is_some());
let (prev, next) = freed.prev_next.unwrap();
assert_eq!(prev, first);
assert_eq!(next, third);
assert_eq!(first.next(&arena), third);
assert_eq!(third.prev(&arena), first);
assert!(!arena.is_occupied(second_ptr));
}
#[test]
fn test_free_list_reuse() {
let mut arena = Arena::with_capacity(5);
let first = arena.alloc_circular(1, "first".to_string());
let first_ptr = first.this(&arena);
unsafe { arena.free_and_unlink(first) };
let second = arena.alloc_circular(2, "second".to_string());
let second_ptr = second.this(&arena);
assert_eq!(first_ptr.unchecked_get(), second_ptr.unchecked_get());
assert_eq!(arena.slots.len(), 1);
}
#[test]
fn test_multiple_free_list_operations() {
let mut arena = Arena::with_capacity(10);
let slots: Vec<_> = (0..5)
.map(|i| arena.alloc_circular(i, format!("value{}", i)))
.collect();
assert_eq!(arena.slots.len(), 5);
unsafe {
arena.free_and_unlink(slots[1]);
arena.free_and_unlink(slots[3]);
}
let new1 = arena.alloc_circular(10, "new1".to_string());
let new2 = arena.alloc_circular(11, "new2".to_string());
assert_eq!(arena.slots.len(), 5);
assert_eq!(new1.data(&arena).key, 10);
assert_eq!(new2.data(&arena).key, 11);
}
#[test]
fn test_index_trait() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
assert_eq!(arena[ptr].key, 42);
assert_eq!(arena[ptr].value, "test");
}
#[test]
fn test_index_mut_trait() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
arena[ptr].value = "modified".to_string();
assert_eq!(arena[ptr].value, "modified");
}
#[test]
#[should_panic]
fn test_index_invalid_ptr_panic() {
let arena = Arena::<i32, String>::with_capacity(5);
#[cfg(not(feature = "generational"))]
let invalid_ptr = Ptr::unchecked_from(999);
#[cfg(feature = "generational")]
let invalid_ptr = Ptr::unchecked_from(999, 0);
let _ = &arena[invalid_ptr];
}
#[test]
#[should_panic]
fn test_index_mut_invalid_ptr_panic() {
let mut arena = Arena::<i32, String>::with_capacity(5);
#[cfg(not(feature = "generational"))]
let invalid_ptr = Ptr::unchecked_from(999);
#[cfg(feature = "generational")]
let invalid_ptr = Ptr::unchecked_from(999, 0);
let _ = &mut arena[invalid_ptr];
}
#[test]
#[should_panic]
fn test_index_freed_ptr_panic() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
unsafe { arena.free_and_unlink(slot) };
let _ = &arena[ptr];
}
#[test]
fn test_freed_slot_struct() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
let freed = unsafe { arena.free_and_unlink(slot) };
assert_eq!(freed.data.key, 42);
assert_eq!(freed.data.value, "test");
assert_eq!(freed.this, ptr);
assert!(freed.prev_next.is_none());
}
#[test]
fn test_drop_behavior() {
let mut arena = Arena::with_capacity(5);
let _slot1 = arena.alloc_circular(1, "first".to_string());
let _slot2 = arena.alloc_circular(2, "second".to_string());
let slot3 = arena.alloc_circular(3, "third".to_string());
unsafe { arena.free_and_unlink(slot3) };
drop(arena);
}
#[test]
fn test_capacity_management() {
let arena = Arena::<i32, String>::with_capacity(100);
assert!(arena.slots.capacity() >= 100);
#[cfg(feature = "generational")]
assert!(arena.generations.capacity() >= 100);
}
#[cfg(feature = "generational")]
#[test]
fn test_generational_tracking() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let ptr = slot.this(&arena);
assert_eq!(ptr.generation, 0);
unsafe { arena.free_and_unlink(slot) };
let new_slot = arena.alloc_circular(43, "new".to_string());
let new_ptr = new_slot.this(&arena);
assert_eq!(new_ptr.generation, 1);
assert_eq!(new_ptr.unchecked_get(), ptr.unchecked_get());
assert!(!arena.is_occupied(ptr));
assert!(arena.is_occupied(new_ptr));
}
#[cfg(feature = "generational")]
#[test]
fn test_generational_map_ptr_stale() {
let mut arena = Arena::with_capacity(5);
let slot = arena.alloc_circular(42, "test".to_string());
let old_ptr = slot.this(&arena);
unsafe { arena.free_and_unlink(slot) };
let _new_slot = arena.alloc_circular(43, "new".to_string());
assert!(arena.map_ptr(old_ptr).is_none());
}
}