use std::cell::Cell;
use crate::{
EntityListError, EntityListIter, EntityListNodeHead, EntityListRange, EntityListRes,
IBasicEntityListID, IDBoundAlloc, container::list_base::NodeOps,
};
pub trait IEntityListNodeID: IBasicEntityListID {
fn comes_before(self, latter: Self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self, bool> {
let mut node = self;
while let Some(next) = node.get_next_id(alloc) {
if next == latter {
return Ok(true);
}
node = next;
}
if node.is_sentinel(alloc) {
Ok(false)
} else {
Err(EntityListError::ListBroken)
}
}
}
pub struct EntityList<I: IEntityListNodeID> {
pub head: I,
pub tail: I,
pub len: Cell<usize>,
}
impl<I: IEntityListNodeID> EntityList<I> {
pub fn new(alloc: &IDBoundAlloc<I>) -> Self {
let head = I::new_sentinel(alloc);
let tail = I::new_sentinel(alloc);
head.set_next_id(alloc, Some(tail));
tail.set_prev_id(alloc, Some(head));
Self {
head,
tail,
len: Cell::new(0),
}
}
#[inline]
pub fn len(&self) -> usize {
self.len.get()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn get_front_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
let next = self.head.get_next_id(alloc)?;
if next == self.tail { None } else { Some(next) }
}
#[inline]
pub fn get_back_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
let prev = self.tail.get_prev_id(alloc)?;
if prev == self.head { None } else { Some(prev) }
}
pub fn node_add_next(&self, node: I, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
if node == new_node {
return Err(EntityListError::RepeatedNode);
}
if node == self.tail {
return Err(EntityListError::NodeIsSentinel);
}
node.on_push_next(new_node, alloc)?;
let node_obj = node.deref_alloc(alloc);
let new_node_obj = new_node.deref_alloc(alloc);
debug_assert!(
I::obj_is_attached(node_obj),
"Cannot add next to a detached node"
);
debug_assert!(
!I::obj_is_attached(new_node_obj),
"Cannot add a node that is already attached"
);
let Some(old_next) = I::obj_get_next_id(node_obj) else {
return Err(EntityListError::ListBroken);
};
I::obj_set_next_id(node_obj, Some(new_node));
I::obj_store_head(new_node_obj, EntityListNodeHead::from_id(node, old_next));
old_next.set_prev_id(alloc, Some(new_node));
self.len.set(self.len.get() + 1);
Ok(())
}
pub fn node_add_prev(&self, node: I, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
if node == new_node {
return Err(EntityListError::RepeatedNode);
}
if node == self.head {
return Err(EntityListError::NodeIsSentinel);
}
node.on_push_prev(new_node, alloc)?;
let node_obj = node.deref_alloc(alloc);
let new_node_obj = new_node.deref_alloc(alloc);
debug_assert!(
I::obj_is_attached(node_obj),
"Cannot add prev to a detached node"
);
debug_assert!(
!I::obj_is_attached(new_node_obj),
"Cannot add a node that is already attached"
);
let Some(old_prev) = I::obj_get_prev_id(node_obj) else {
return Err(EntityListError::ListBroken);
};
NodeOps::obj_set_prev_id(node_obj, Some(new_node));
I::obj_store_head(new_node_obj, EntityListNodeHead::from_id(old_prev, node));
old_prev.set_next_id(alloc, Some(new_node));
self.len.set(self.len.get() + 1);
Ok(())
}
pub fn node_unplug(&self, node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
if node == self.head || node == self.tail {
return Err(EntityListError::NodeIsSentinel);
}
node.on_unplug(alloc)?;
let node_obj = node.deref_alloc(alloc);
debug_assert!(
I::obj_is_attached(node_obj),
"Cannot unplug a detached node"
);
let Some(prev) = I::obj_get_prev_id(node_obj) else {
return Err(EntityListError::ListBroken);
};
let Some(next) = I::obj_get_next_id(node_obj) else {
return Err(EntityListError::ListBroken);
};
prev.set_next_id(alloc, Some(next));
next.set_prev_id(alloc, Some(prev));
I::obj_store_head(node_obj, EntityListNodeHead::none());
self.len.set(self.len.get() - 1);
Ok(())
}
#[inline]
pub fn push_back_id(&self, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
self.node_add_prev(self.tail, new_node, alloc)
}
#[inline]
pub fn push_front_id(&self, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
self.node_add_next(self.head, new_node, alloc)
}
pub fn pop_back(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
let back_id = self.get_back_id(alloc).ok_or(EntityListError::EmptyList)?;
self.node_unplug(back_id, alloc)?;
Ok(back_id)
}
pub fn pop_front(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
let front_id = self.get_front_id(alloc).ok_or(EntityListError::EmptyList)?;
self.node_unplug(front_id, alloc)?;
Ok(front_id)
}
pub fn get_range(&self, alloc: &IDBoundAlloc<I>) -> EntityListRange<I> {
EntityListRange {
start: self.head.get_next_id(alloc).unwrap(),
end: Some(self.tail),
}
}
pub fn get_range_with_sentinels(&self) -> EntityListRange<I> {
EntityListRange {
start: self.head,
end: None,
}
}
pub fn iter<'alloc>(&self, alloc: &'alloc IDBoundAlloc<I>) -> EntityListIter<'alloc, I> {
EntityListIter::new(self.get_range(alloc), alloc)
}
pub fn iter_with_sentinels<'alloc>(
&self,
alloc: &'alloc IDBoundAlloc<I>,
) -> EntityListIter<'alloc, I> {
EntityListIter::new(self.get_range_with_sentinels(), alloc)
}
pub fn forall_with_sentinel(
&self,
alloc: &IDBoundAlloc<I>,
mut f: impl FnMut(I, &I::ObjectT) -> EntityListRes<I>,
) -> EntityListRes<I, ()> {
let mut curr = self.head;
loop {
f(curr, curr.deref_alloc(alloc))?;
if curr == self.tail {
break;
}
let next = curr.get_next_id(alloc).ok_or(EntityListError::ListBroken)?;
curr = next;
}
Ok(())
}
}