Skip to main content

mtb_entity_slab/container/
list.rs

1//! Linked list implementations for entity allocators.
2//!
3//! This module provides doubly linked list and ring list structures
4//! that operate on entities managed by `EntityAlloc`. The lists
5//! support various operations such as insertion, removal, and traversal.
6//!
7//! The lists are generic over entity ID types that implement the
8//! `IEntityListNodeID` or `IEntityRingListNodeID` traits, allowing
9//! flexible integration with different entity types (PtrID, IndexedID or
10//! anything defined by yourself).
11//!
12//! The lists also support signaling mechanisms to notify entities
13//! when they are added to or removed from a list, enabling custom
14//! behaviors during these operations.
15//!
16//! # Safety
17//!
18//! These lists rely on the correctness of the entity allocator
19//! and the entity ID implementations. Improper use may lead to
20//! undefined behavior, such as broken lists or memory corruption.
21//!
22//! Ensure that entities are properly managed and that list operations
23//! are performed in a safe manner.
24//!
25//! 基于 EntityAlloc 实现的链表结构。
26//!
27//! 本模块提供了双向链表和环形链表结构,支持在 EntityAlloc 管理的实体上进行各种操作。
28//! 这些链表支持插入、删除和遍历等操作。
29//!
30//! 链表是泛型的,适用于实现了 `IEntityListNodeID` 或 `IEntityRingListNodeID` 特征的实体 ID 类型,
31//! 允许与不同的实体类型(如 PtrID、IndexedID 或自定义类型)灵活集成。
32//!
33//! 链表还支持信号机制,在实体被添加到或从链表中移除时进行通知,从而实现自定义行为。
34//!
35//! # 安全性
36//!
37//! 这些链表依赖于实体分配器和实体 ID 实现的正确性。不当使用可能导致未定义行为,如链表损坏或内存损坏。
38
39use crate::{IDBoundAlloc, IEntityAllocID, IPoliciedID};
40use std::cell::Cell;
41
42/// Lightweight head metadata stored on each list node.
43///
44/// This small struct holds the previous and next node IDs for a node that
45/// participates in either a linked list or a ring list. It is intended to be
46/// embedded in entity storage so that list linkage does not require separate
47/// allocation.
48///
49/// The `prev`/`next` options are `None` when the node is detached. In the
50/// ring-list sentinel case both fields point to the sentinel itself.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub struct EntityListNodeHead<I: IPoliciedID> {
53    /// Previous node ID, if any.
54    pub prev: Option<I>,
55    /// Next node ID, if any.
56    pub next: Option<I>,
57}
58impl<I: IPoliciedID> EntityListNodeHead<I> {
59    /// Create a new empty (detached) node head.
60    pub fn none() -> Self {
61        Self {
62            prev: None,
63            next: None,
64        }
65    }
66    /// Create a new node head with given prev/next IDs.
67    pub fn new_full(prev: Option<I>, next: Option<I>) -> Self {
68        Self { prev, next }
69    }
70    /// Create a new node head from given prev/next IDs (both non-None).
71    pub fn from_id(prev: I, next: I) -> Self {
72        Self {
73            prev: Some(prev),
74            next: Some(next),
75        }
76    }
77    /// Decompose the node head into its prev/next IDs.
78    pub fn into_id(self) -> (Option<I>, Option<I>) {
79        (self.prev, self.next)
80    }
81
82    /// Get the previous node ID, if any.
83    pub fn get_prev(&self) -> Option<I> {
84        self.prev
85    }
86    /// Get the next node ID, if any.
87    pub fn get_next(&self) -> Option<I> {
88        self.next
89    }
90
91    fn prev(mut self, prev: Option<I>) -> Self {
92        self.prev = prev;
93        self
94    }
95    fn next(mut self, next: Option<I>) -> Self {
96        self.next = next;
97        self
98    }
99}
100
101mod list_util {
102    pub(super) struct RingList;
103    pub(super) struct LinkList;
104}
105trait NodeOpsPrivate<KindT>: IPoliciedID {
106    fn obj_get_prev_id(obj: &Self::ObjectT) -> Option<Self>;
107    fn obj_get_next_id(obj: &Self::ObjectT) -> Option<Self>;
108
109    fn obj_set_prev_id(obj: &Self::ObjectT, prev: Option<Self>);
110    fn obj_set_next_id(obj: &Self::ObjectT, next: Option<Self>);
111    fn obj_is_attached(obj: &Self::ObjectT) -> bool;
112
113    fn set_prev_id(self, alloc: &IDBoundAlloc<Self>, prev: Option<Self>) {
114        Self::obj_set_prev_id(self.deref_alloc(alloc), prev);
115    }
116    fn set_next_id(self, alloc: &IDBoundAlloc<Self>, next: Option<Self>) {
117        Self::obj_set_next_id(self.deref_alloc(alloc), next);
118    }
119}
120
121/// Error kinds returned by `EntityList` operations.
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
123pub enum EntityListError<I: IPoliciedID> {
124    /// The list is empty.
125    EmptyList,
126    /// The list structure is broken (inconsistent links).
127    ListBroken,
128    /// A sentinel node was used where a normal node was expected.
129    NodeIsSentinel,
130    /// The same node was added multiple times.
131    RepeatedNode,
132    /// Operations are done in the same list which is not allowed.
133    RepeatedList,
134    /// The node was expected to be attached but is not.
135    SelfNotAttached(I),
136    /// The node was expected to be detached but is attached.
137    ItemFalselyAttached(I),
138    /// The node was expected to be attached but is detached.
139    ItemFalselyDetached(I),
140}
141impl<I: IPoliciedID> std::fmt::Display for EntityListError<I> {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        std::fmt::Debug::fmt(&self, f)
144    }
145}
146impl<I: IPoliciedID> std::error::Error for EntityListError<I> {}
147
148/// Result type returned by list operations.
149///
150/// Uses `EntityListError<I>` as the error kind so callers can handle list
151/// specific failures (broken invariants, sentinel misuse, etc.).
152pub type EntityListRes<I, T = ()> = Result<T, EntityListError<I>>;
153
154/// Trait required for an entity ID to be used as a node in `EntityList`.
155///
156/// Implementors must provide accessors to load/store the embedded
157/// `EntityListNodeHead` inside the entity object, create sentinel objects,
158/// and handle optional push/unplug signals. This trait lets the list logic
159/// remain generic over different ID representations (pointer-based or
160/// index-based).
161pub trait IEntityListNodeID: IPoliciedID {
162    /// Load the `EntityListNodeHead` from the entity object.
163    fn obj_load_head(obj: &Self::ObjectT) -> EntityListNodeHead<Self>;
164    /// NOTE: Entity Lists are internally mutable
165    fn obj_store_head(obj: &Self::ObjectT, head: EntityListNodeHead<Self>);
166    /// Check if the entity object is a sentinel node.
167    fn obj_is_sentinel(obj: &Self::ObjectT) -> bool;
168    /// Create a new sentinel entity object.
169    fn new_sentinel_obj() -> Self::ObjectT;
170
171    /// Check if this node ID is a sentinel.
172    #[inline]
173    fn is_sentinel(self, alloc: &IDBoundAlloc<Self>) -> bool {
174        Self::obj_is_sentinel(self.deref_alloc(alloc))
175    }
176    /// Create a new sentinel node ID allocated from the given allocator.
177    fn new_sentinel(alloc: &IDBoundAlloc<Self>) -> Self {
178        let obj = Self::new_sentinel_obj();
179        Self::from_backend(Self::BackID::allocate_from(alloc, obj))
180    }
181
182    /// Signal: invoked when this node is being added after `prev` node.
183    fn on_push_prev(self, prev: Self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self>;
184    /// Signal: invoked when this node is being added before `next` node.
185    fn on_push_next(self, next: Self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self>;
186    /// Signal: invoked when this node is being unplugged from a list.
187    fn on_unplug(self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self>;
188
189    /// Get the previous node ID, if any.
190    fn get_prev_id(self, alloc: &IDBoundAlloc<Self>) -> Option<Self> {
191        Self::obj_get_prev_id(self.deref_alloc(alloc))
192    }
193    /// Get the next node ID, if any.
194    fn get_next_id(self, alloc: &IDBoundAlloc<Self>) -> Option<Self> {
195        Self::obj_get_next_id(self.deref_alloc(alloc))
196    }
197    /// Check if this node is currently attached to a list.
198    fn is_attached(self, alloc: &IDBoundAlloc<Self>) -> bool {
199        Self::obj_is_attached(self.deref_alloc(alloc))
200    }
201
202    /// Check whether this node comes before another node in the same list.
203    fn comes_before(self, latter: Self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self, bool> {
204        let mut node = self;
205        while let Some(next) = node.get_next_id(alloc) {
206            if next == latter {
207                return Ok(true);
208            }
209            node = next;
210        }
211        if node.is_sentinel(alloc) {
212            Ok(false)
213        } else {
214            Err(EntityListError::ListBroken)
215        }
216    }
217}
218impl<I: IEntityListNodeID> NodeOpsPrivate<list_util::LinkList> for I {
219    fn obj_get_prev_id(obj: &Self::ObjectT) -> Option<Self> {
220        I::obj_load_head(obj).get_prev()
221    }
222    fn obj_get_next_id(obj: &Self::ObjectT) -> Option<Self> {
223        I::obj_load_head(obj).get_next()
224    }
225    fn obj_set_prev_id(obj: &Self::ObjectT, prev: Option<Self>) {
226        Self::obj_store_head(obj, Self::obj_load_head(obj).prev(prev));
227    }
228    fn obj_set_next_id(obj: &Self::ObjectT, next: Option<Self>) {
229        Self::obj_store_head(obj, Self::obj_load_head(obj).next(next));
230    }
231    fn obj_is_attached(obj: &Self::ObjectT) -> bool {
232        let head = Self::obj_load_head(obj);
233        let is_sentinel = Self::obj_is_sentinel(obj);
234        let has_prev = head.prev.is_some();
235        let has_next = head.next.is_some();
236        if cfg!(debug_assertions) && !is_sentinel && has_prev != has_next {
237            panic!("EntityListNodeID: obj_is_attached: inconsistent attachment state detected");
238        }
239        has_prev || has_next
240    }
241}
242
243/// A partial-close range of an entity pointer list representing `[start, end)`.
244///
245/// - if `end is None`, the range is open-ended.
246/// - the `start` is non-nullable, meaning any range must have a valid start.
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub struct EntityListRange<I: IEntityListNodeID> {
249    /// Start ID (inclusive).
250    pub start: I,
251    /// Optional end ID (exclusive). If `None`, the range is open-ended.
252    pub end: Option<I>,
253}
254impl<I: IEntityListNodeID> EntityListRange<I> {
255    /// Check if the range is empty.
256    pub fn is_empty(&self) -> bool {
257        Some(self.start) == self.end
258    }
259
260    /// Create an iterator over this range.
261    pub fn iter<'a>(&self, alloc: &'a IDBoundAlloc<I>) -> EntityListIter<'a, I> {
262        EntityListIter::new(*self, alloc)
263    }
264}
265
266/// Doubly-linked list view over entities stored in an `EntityAlloc`.
267///
268/// Logical structure: the list uses head/tail sentinel nodes (allocated via
269/// the entity allocator) and maintains `EntityListNodeHead` inside each
270/// entity to chain nodes. Physical structure: no extra heap nodes are
271/// allocated—linkage is stored inline within entity storage.
272///
273/// The list invokes signals (`on_push_*`, `on_unplug`) on nodes before
274/// modifying links; these signals may veto operations by returning an error.
275/// Many operations assume the node is not already attached to another list;
276/// violating that assumption may lead to an inconsistent list (reported as
277/// `ListBroken`).
278pub struct EntityList<I: IEntityListNodeID> {
279    /// Head sentinel node ID.
280    pub head: I,
281    /// Tail sentinel node ID.
282    pub tail: I,
283    /// Number of nodes in the list (excluding sentinels).
284    pub len: Cell<usize>,
285}
286impl<I: IEntityListNodeID> EntityList<I> {
287    /// create a new empty list with head and tail sentinels linked
288    /// with each other.
289    pub fn new(alloc: &IDBoundAlloc<I>) -> Self {
290        let head = I::new_sentinel(alloc);
291        let tail = I::new_sentinel(alloc);
292        head.set_next_id(alloc, Some(tail));
293        tail.set_prev_id(alloc, Some(head));
294        Self {
295            head,
296            tail,
297            len: Cell::new(0),
298        }
299    }
300
301    /// get the number of nodes in the list.
302    #[inline]
303    pub fn len(&self) -> usize {
304        self.len.get()
305    }
306    /// check if the list is empty.
307    #[inline]
308    pub fn is_empty(&self) -> bool {
309        self.len() == 0
310    }
311
312    /// try to get the front node ID, or None if the list is empty
313    #[inline]
314    pub fn get_front_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
315        let next = self.head.get_next_id(alloc)?;
316        if next == self.tail { None } else { Some(next) }
317    }
318    /// try to get the back node ID, or None if the list is empty
319    #[inline]
320    pub fn get_back_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
321        let prev = self.tail.get_prev_id(alloc)?;
322        if prev == self.head { None } else { Some(prev) }
323    }
324
325    /// Add a new node after an existing node linked to `this` list.
326    ///
327    /// If the current node is linked to another list, then the operation is undefined.
328    /// You'll get a broken list without reporting an error.
329    ///
330    /// ### Signal invocation
331    ///
332    /// `node_add_next` will invoke `on_push_next` on `node` before modifying any links.
333    /// If the signal returns an error, the operation is aborted without modifying any links.
334    pub fn node_add_next(&self, node: I, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
335        if node == new_node {
336            return Err(EntityListError::RepeatedNode);
337        }
338        if node == self.tail {
339            return Err(EntityListError::NodeIsSentinel);
340        }
341        node.on_push_next(new_node, alloc)?;
342
343        let node_obj = node.deref_alloc(alloc);
344        let new_node_obj = new_node.deref_alloc(alloc);
345
346        debug_assert!(
347            I::obj_is_attached(node_obj),
348            "Cannot add next to a detached node"
349        );
350        debug_assert!(
351            !I::obj_is_attached(new_node_obj),
352            "Cannot add a node that is already attached"
353        );
354
355        let Some(old_next) = I::obj_get_next_id(node_obj) else {
356            return Err(EntityListError::ListBroken);
357        };
358        I::obj_set_next_id(node_obj, Some(new_node));
359        I::obj_store_head(new_node_obj, EntityListNodeHead::from_id(node, old_next));
360        old_next.set_prev_id(alloc, Some(new_node));
361        self.len.set(self.len.get() + 1);
362        Ok(())
363    }
364
365    /// Add a new node before an existing node linked to `this` list.
366    ///
367    /// If the current node is linked to another list, then the operation is undefined.
368    /// You'll get a broken list without reporting an error.
369    ///
370    /// ### Signal invocation
371    ///
372    /// `node_add_prev` will invoke `on_push_prev` on `node` before modifying any links.
373    /// If the signal returns an error, the operation is aborted without modifying any links.
374    pub fn node_add_prev(&self, node: I, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
375        if node == new_node {
376            return Err(EntityListError::RepeatedNode);
377        }
378        if node == self.head {
379            return Err(EntityListError::NodeIsSentinel);
380        }
381        node.on_push_prev(new_node, alloc)?;
382
383        let node_obj = node.deref_alloc(alloc);
384        let new_node_obj = new_node.deref_alloc(alloc);
385
386        debug_assert!(
387            I::obj_is_attached(node_obj),
388            "Cannot add prev to a detached node"
389        );
390        debug_assert!(
391            !I::obj_is_attached(new_node_obj),
392            "Cannot add a node that is already attached"
393        );
394
395        let Some(old_prev) = I::obj_get_prev_id(node_obj) else {
396            return Err(EntityListError::ListBroken);
397        };
398        I::obj_set_prev_id(node_obj, Some(new_node));
399        I::obj_store_head(new_node_obj, EntityListNodeHead::from_id(old_prev, node));
400        old_prev.set_next_id(alloc, Some(new_node));
401        self.len.set(self.len.get() + 1);
402        Ok(())
403    }
404
405    /// Unplug a node from the list.
406    ///
407    /// ### Signal invocation
408    ///
409    /// `node_unplug` will invoke `on_unplug` on `node` before modifying any links.
410    /// If the signal returns an error, the operation is aborted without modifying any links.
411    pub fn node_unplug(&self, node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
412        if node == self.head || node == self.tail {
413            return Err(EntityListError::NodeIsSentinel);
414        }
415        node.on_unplug(alloc)?;
416
417        let node_obj = node.deref_alloc(alloc);
418
419        debug_assert!(
420            I::obj_is_attached(node_obj),
421            "Cannot unplug a detached node"
422        );
423
424        let Some(prev) = I::obj_get_prev_id(node_obj) else {
425            return Err(EntityListError::ListBroken);
426        };
427        let Some(next) = I::obj_get_next_id(node_obj) else {
428            return Err(EntityListError::ListBroken);
429        };
430        prev.set_next_id(alloc, Some(next));
431        next.set_prev_id(alloc, Some(prev));
432        I::obj_store_head(node_obj, EntityListNodeHead::none());
433        self.len.set(self.len.get() - 1);
434        Ok(())
435    }
436
437    /// Push a new node to the back of the list.
438    #[inline]
439    pub fn push_back_id(&self, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
440        self.node_add_prev(self.tail, new_node, alloc)
441    }
442
443    /// Push a new node to the front of the list.
444    #[inline]
445    pub fn push_front_id(&self, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
446        self.node_add_next(self.head, new_node, alloc)
447    }
448
449    /// Pop a node from the back of the list.
450    pub fn pop_back(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
451        let back_id = self.get_back_id(alloc).ok_or(EntityListError::EmptyList)?;
452        self.node_unplug(back_id, alloc)?;
453        Ok(back_id)
454    }
455
456    /// Pop a node from the front of the list.
457    pub fn pop_front(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
458        let front_id = self.get_front_id(alloc).ok_or(EntityListError::EmptyList)?;
459        self.node_unplug(front_id, alloc)?;
460        Ok(front_id)
461    }
462
463    /// Get a range covering all nodes in the list (excluding sentinels).
464    pub fn get_range(&self, alloc: &IDBoundAlloc<I>) -> EntityListRange<I> {
465        EntityListRange {
466            start: self.head.get_next_id(alloc).unwrap(),
467            end: Some(self.tail),
468        }
469    }
470    /// Get a range covering all nodes in the list (including sentinels).
471    pub fn get_range_with_sentinels(&self) -> EntityListRange<I> {
472        EntityListRange {
473            start: self.head,
474            end: None,
475        }
476    }
477
478    /// Create an iterator over all nodes in the list (excluding sentinels).
479    pub fn iter<'alloc>(&self, alloc: &'alloc IDBoundAlloc<I>) -> EntityListIter<'alloc, I> {
480        EntityListIter::new(self.get_range(alloc), alloc)
481    }
482    /// Create an iterator over all nodes in the list (including sentinels).
483    pub fn iter_with_sentinels<'alloc>(
484        &self,
485        alloc: &'alloc IDBoundAlloc<I>,
486    ) -> EntityListIter<'alloc, I> {
487        EntityListIter::new(self.get_range_with_sentinels(), alloc)
488    }
489
490    /// Apply a function to all nodes in the list (including sentinels).
491    pub fn forall_with_sentinel(
492        &self,
493        alloc: &IDBoundAlloc<I>,
494        mut f: impl FnMut(I, &I::ObjectT) -> EntityListRes<I>,
495    ) -> EntityListRes<I, ()> {
496        let mut curr = self.head;
497        loop {
498            f(curr, curr.deref_alloc(alloc))?;
499            if curr == self.tail {
500                break;
501            }
502            let next = curr.get_next_id(alloc).ok_or(EntityListError::ListBroken)?;
503            curr = next;
504        }
505        Ok(())
506    }
507}
508
509/// Iterator over a range of nodes in an `EntityList`.
510///
511/// Yields `(I, &I::ObjectT)` pairs where the ID identifies the node and the
512/// reference points to the underlying entity. The iterator borrows the
513/// `EntityAlloc` for the duration of the iteration and follows the `next`
514/// pointers stored inside node objects.
515pub struct EntityListIter<'alloc, I: IEntityListNodeID> {
516    alloc: &'alloc IDBoundAlloc<I>,
517    curr: Option<I>,
518    end: Option<I>,
519}
520impl<'alloc, I: IEntityListNodeID> Iterator for EntityListIter<'alloc, I>
521where
522    I::ObjectT: 'alloc,
523{
524    type Item = (I, &'alloc I::ObjectT);
525
526    fn next(&mut self) -> Option<Self::Item> {
527        let curr = self.curr?;
528        if Some(curr) == self.end {
529            return None;
530        }
531        let obj = curr.deref_alloc(self.alloc);
532        self.curr = curr.get_next_id(self.alloc);
533        Some((curr, obj))
534    }
535}
536impl<'alloc, I: IEntityListNodeID> EntityListIter<'alloc, I> {
537    /// Create a new iterator over the given range.
538    pub fn new(range: EntityListRange<I>, alloc: &'alloc IDBoundAlloc<I>) -> Self {
539        Self {
540            alloc,
541            curr: Some(range.start),
542            end: range.end,
543        }
544    }
545}
546
547/// An entity pointer list node that supports ring lists.
548pub trait IEntityRingListNodeID: IPoliciedID {
549    /// Load the `EntityListNodeHead` from the entity object.
550    fn obj_load_head(obj: &Self::ObjectT) -> EntityListNodeHead<Self>;
551    /// Store the `EntityListNodeHead` into the entity object.
552    /// NOTE: Entity Lists are internally mutable
553    fn obj_store_head(obj: &Self::ObjectT, head: EntityListNodeHead<Self>);
554    /// Check if the entity object is a sentinel node.
555    fn obj_is_sentinel(obj: &Self::ObjectT) -> bool;
556    /// Create a new sentinel entity object.
557    fn new_sentinel_obj() -> Self::ObjectT;
558
559    /// Create a new sentinel node ID allocated from the given allocator.
560    fn new_sentinel(alloc: &IDBoundAlloc<Self>) -> Self {
561        let obj = Self::new_sentinel_obj();
562        let back = Self::BackID::allocate_from(alloc, obj);
563        let id = Self::from_backend(back);
564        let obj = id.deref_alloc(alloc);
565        let head = EntityListNodeHead::from_id(id, id);
566        Self::obj_store_head(obj, head);
567        id
568    }
569
570    /// Signal: invoked when this node is being unplugged from a list.
571    fn on_unplug(self, obj: &Self::ObjectT, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self>;
572
573    /// Check if this node ID is a sentinel.
574    fn is_sentinel(self, alloc: &IDBoundAlloc<Self>) -> bool {
575        Self::obj_is_sentinel(self.deref_alloc(alloc))
576    }
577    /// Get the previous node ID, if any.
578    fn get_prev_id(self, alloc: &IDBoundAlloc<Self>) -> Option<Self> {
579        Self::obj_get_prev_id(self.deref_alloc(alloc))
580    }
581    /// Get the next node ID, if any.
582    fn get_next_id(self, alloc: &IDBoundAlloc<Self>) -> Option<Self> {
583        Self::obj_get_next_id(self.deref_alloc(alloc))
584    }
585    /// Check if this node is currently attached to a list.
586    fn is_attached(self, alloc: &IDBoundAlloc<Self>) -> bool {
587        Self::obj_is_attached(self.deref_alloc(alloc))
588    }
589
590    /// Detach the given node from its current ring list.
591    fn obj_detach(obj: &Self::ObjectT, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self> {
592        if Self::obj_is_sentinel(obj) {
593            return Err(EntityListError::NodeIsSentinel);
594        }
595        let (Some(prev), Some(next)) = Self::obj_load_head(obj).into_id() else {
596            // detach() 在节点未连接时调用是合法的,直接返回 Ok(())
597            return Ok(());
598        };
599        prev.set_next_id(alloc, Some(next));
600        next.set_prev_id(alloc, Some(prev));
601        Self::obj_store_head(obj, EntityListNodeHead::none());
602        Ok(())
603    }
604
605    /// Detach this node from the ring list it is currently attached to.
606    ///
607    /// ### Signal invocation
608    ///
609    /// `detach` will invoke `on_unplug` on `self` before modifying any links.
610    /// If the signal returns an error, the operation is aborted without modifying any links.
611    fn detach(self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self> {
612        Self::obj_detach(self.deref_alloc(alloc), alloc)
613    }
614}
615impl<I: IEntityRingListNodeID> NodeOpsPrivate<list_util::RingList> for I {
616    fn obj_get_prev_id(obj: &Self::ObjectT) -> Option<Self> {
617        I::obj_load_head(obj).get_prev()
618    }
619    fn obj_get_next_id(obj: &Self::ObjectT) -> Option<Self> {
620        I::obj_load_head(obj).get_next()
621    }
622    fn obj_set_prev_id(obj: &Self::ObjectT, prev: Option<Self>) {
623        I::obj_store_head(obj, I::obj_load_head(obj).prev(prev));
624    }
625    fn obj_set_next_id(obj: &Self::ObjectT, next: Option<Self>) {
626        I::obj_store_head(obj, I::obj_load_head(obj).next(next));
627    }
628    fn obj_is_attached(obj: &Self::ObjectT) -> bool {
629        let head = Self::obj_load_head(obj);
630        let has_prev = head.prev.is_some();
631        let has_next = head.next.is_some();
632        if cfg!(debug_assertions) && (has_prev != has_next) {
633            panic!("EntityRingListNodeID: obj_is_attached: inconsistent attachment state detected");
634        }
635        has_prev || has_next
636    }
637}
638
639/// Ring list (circular) view over entities stored in an `EntityAlloc`.
640///
641/// The ring list is backed by a sentinel node that always exists; an empty
642/// ring has the sentinel's `next`/`prev` pointing to itself. Nodes embed
643/// `EntityListNodeHead` for linkage and can be detached/attached at O(1).
644pub struct EntityRingList<I: IEntityRingListNodeID> {
645    /// Sentinel node ID.
646    pub sentinel: I,
647}
648
649impl<I: IEntityRingListNodeID> EntityRingList<I> {
650    /// Create a new empty ring list with a sentinel node.
651    pub fn new(alloc: &IDBoundAlloc<I>) -> Self {
652        Self {
653            sentinel: I::new_sentinel(alloc),
654        }
655    }
656    /// Get the front node ID, or None if the list is empty. (Not sentinel)
657    pub fn front_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
658        let next = self.sentinel.get_next_id(alloc)?;
659        if next == self.sentinel {
660            None
661        } else {
662            Some(next)
663        }
664    }
665    /// Get the back node ID, or None if the list is empty. (Not sentinel)
666    pub fn back_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
667        let prev = self.sentinel.get_prev_id(alloc)?;
668        if prev == self.sentinel {
669            None
670        } else {
671            Some(prev)
672        }
673    }
674    /// Check if the ring list is empty.
675    pub fn is_empty(&self, alloc: &IDBoundAlloc<I>) -> bool {
676        self.front_id(alloc).is_none()
677    }
678    /// Check if the ring list has exactly one node (excluding sentinel).
679    pub fn is_single(&self, alloc: &IDBoundAlloc<I>) -> bool {
680        let sentinel = self.sentinel.deref_alloc(alloc);
681        let head = I::obj_load_head(sentinel);
682        let (Some(prev), Some(next)) = head.into_id() else {
683            return false;
684        };
685        prev == next && prev != self.sentinel
686    }
687    /// Check if the ring list has multiple nodes (excluding sentinel).
688    pub fn is_multiple(&self, alloc: &IDBoundAlloc<I>) -> bool {
689        let sentinel = self.sentinel.deref_alloc(alloc);
690        let head = I::obj_load_head(sentinel);
691        let (Some(prev), Some(next)) = head.into_id() else {
692            return false;
693        };
694        prev != next
695    }
696
697    /// traverse each node in the list, invoking `pred` on each node.
698    ///
699    /// ### panics
700    ///
701    /// If a broken ring list is detected, this function will panic.
702    pub fn foreach(
703        &self,
704        alloc: &IDBoundAlloc<I>,
705        mut pred: impl FnMut(I, &I::ObjectT) -> bool,
706    ) -> bool {
707        let mut curr = self.sentinel.get_next_id(alloc);
708        let mut count = 0;
709        while let Some(node_id) = curr {
710            if node_id == self.sentinel {
711                return true;
712            }
713            let node_obj = node_id.deref_alloc(alloc);
714            if !pred(node_id, node_obj) {
715                return false;
716            }
717            curr = node_id.get_next_id(alloc);
718            count += 1;
719        }
720        panic!("Broken ring list detected after {count} nodes");
721    }
722
723    /// traverse each node in the list starting from the sentinel, invoking `pred` on each node.
724    ///
725    /// ### panics
726    ///
727    /// If a broken ring list is detected, this function will panic.
728    pub fn forall_with_sentinel(
729        &self,
730        alloc: &IDBoundAlloc<I>,
731        mut pred: impl FnMut(I, &I::ObjectT) -> bool,
732    ) -> bool {
733        let mut curr = Some(self.sentinel);
734        let mut count = 0;
735        while let Some(node_id) = curr {
736            let node_obj = node_id.deref_alloc(alloc);
737            if !pred(node_id, node_obj) {
738                return false;
739            }
740            curr = node_id.get_next_id(alloc);
741            count += 1;
742            if curr == Some(self.sentinel) {
743                return true;
744            }
745        }
746        panic!("Broken ring list detected after {count} nodes");
747    }
748
749    /// Get the number of nodes in the ring list (excluding sentinel).
750    ///
751    /// NOTE: This function traverses the entire list to count nodes,
752    /// so its time complexity is `O(n)`.
753    pub fn len(&self, alloc: &IDBoundAlloc<I>) -> usize {
754        let mut len = 0;
755        self.foreach(alloc, |_, _| {
756            len += 1;
757            true
758        });
759        len
760    }
761    /// Check if the ring list contains the given node.
762    pub fn contains(&self, node: I, alloc: &IDBoundAlloc<I>) -> bool {
763        !self.foreach(alloc, |id, _| id != node)
764    }
765
766    /// Create a clone of this ring list view (shares the same sentinel).
767    #[inline]
768    pub fn clone_view(&self) -> Self {
769        Self {
770            sentinel: self.sentinel,
771        }
772    }
773
774    /// Push a new node to the back of the ring list.
775    pub fn push_back(&self, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
776        const ERRMSG: &str = "Broken ring detected during push_back";
777
778        let sentinel_obj = self.sentinel.deref_alloc(alloc);
779        let prev = I::obj_get_prev_id(sentinel_obj).expect(ERRMSG);
780        if prev == new_node {
781            return Err(EntityListError::RepeatedNode);
782        }
783        if new_node.is_attached(alloc) {
784            return Err(EntityListError::ItemFalselyAttached(new_node));
785        }
786        // Link new_node between prev and sentinel
787        let new_node_obj = new_node.deref_alloc(alloc);
788        I::obj_store_head(
789            new_node_obj,
790            EntityListNodeHead::from_id(prev, self.sentinel),
791        );
792        // Update neighbors to point to new_node
793        prev.set_next_id(alloc, Some(new_node));
794        self.sentinel.set_prev_id(alloc, Some(new_node));
795        Ok(())
796    }
797
798    /// Pop a node from the back of the ring list.
799    pub fn pop_back(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
800        let back_id = self.back_id(alloc).ok_or(EntityListError::EmptyList)?;
801        back_id.detach(alloc)?;
802        Ok(back_id)
803    }
804    /// Pop a node from the front of the ring list.
805    pub fn pop_front(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
806        let front_id = self.front_id(alloc).ok_or(EntityListError::EmptyList)?;
807        front_id.detach(alloc)?;
808        Ok(front_id)
809    }
810
811    /// Move all nodes from `self` into `other`, preserving order; invoke on_move for each moved node.
812    pub fn move_all_to(
813        &self,
814        other: &Self,
815        alloc: &IDBoundAlloc<I>,
816        mut on_move: impl FnMut(I),
817    ) -> EntityListRes<I> {
818        if self.sentinel == other.sentinel {
819            return Ok(());
820        }
821        loop {
822            let node = match self.pop_front(alloc) {
823                Ok(n) => n,
824                Err(EntityListError::EmptyList) => break Ok(()),
825                Err(e) => break Err(e),
826            };
827            other.push_back(node, alloc)?;
828            on_move(node);
829        }
830    }
831
832    /// Move nodes that satisfy `predicate` from `self` to `other`, preserving order.
833    pub fn move_to_if(
834        &self,
835        other: &Self,
836        alloc: &IDBoundAlloc<I>,
837        mut pred: impl FnMut(I, &I::ObjectT) -> bool,
838        mut on_move: impl FnMut(I),
839    ) -> EntityListRes<I> {
840        if self.sentinel == other.sentinel {
841            return Err(EntityListError::RepeatedList);
842        }
843        loop {
844            let Some(next) = self.sentinel.get_next_id(alloc) else {
845                break;
846            };
847            if next == self.sentinel {
848                return Ok(());
849            }
850            let next_obj = next.deref_alloc(alloc);
851            if !pred(next, next_obj) {
852                continue;
853            }
854            next.detach(alloc)?;
855            other.push_back(next, alloc)?;
856            on_move(next);
857        }
858        Err(EntityListError::ListBroken)
859    }
860
861    /// Clean up the ring list by removing all nodes; panic if a broken ring is detected.
862    pub fn clean(&self, alloc: &IDBoundAlloc<I>) {
863        let mut count = 0;
864        let err = loop {
865            match self.pop_front(alloc) {
866                Ok(_) => count += 1,
867                Err(EntityListError::EmptyList) => return,
868                Err(e) => break e,
869            };
870        };
871        panic!("Broken ring list detected after removing {count} nodes: {err}");
872    }
873
874    /// Create an iterator over all nodes in the ring list (excluding sentinel).
875    pub fn iter<'a>(&self, alloc: &'a IDBoundAlloc<I>) -> EntityRingListIter<'a, I> {
876        EntityRingListIter {
877            alloc,
878            curr: self
879                .sentinel
880                .get_next_id(alloc)
881                .expect("Broken ring list detected in iter()"),
882        }
883    }
884}
885
886/// Iterator for `EntityRingList` that visits each node once starting after the sentinel.
887///
888/// The iterator yields `(I, &I::ObjectT)` and will stop when it encounters
889/// the sentinel again. It assumes the ring is structurally sound; a broken
890/// ring will cause a panic during iteration.
891#[derive(Clone, Copy)]
892pub struct EntityRingListIter<'alloc, I: IEntityRingListNodeID> {
893    alloc: &'alloc IDBoundAlloc<I>,
894    curr: I,
895}
896impl<'alloc, I: IEntityRingListNodeID> Iterator for EntityRingListIter<'alloc, I>
897where
898    I::ObjectT: 'alloc,
899{
900    type Item = (I, &'alloc I::ObjectT);
901
902    fn next(&mut self) -> Option<Self::Item> {
903        let curr = self.curr;
904        if curr.is_sentinel(self.alloc) {
905            return None;
906        }
907        let obj = curr.deref_alloc(self.alloc);
908        let Some(next) = curr.get_next_id(self.alloc) else {
909            panic!("Broken ring list detected during iteration");
910        };
911        self.curr = next;
912        Some((curr, obj))
913    }
914}