mtb_entity_slab/container/
ptrlist.rs

1use crate::{EntityAlloc, IEntityAllocID, PtrID, alloc::IEntityAllocatable};
2use std::{
3    cell::Cell,
4    error::Error,
5    fmt::{Debug, Display},
6    iter::Rev,
7};
8
9pub struct EntityListHead<E: IEntityAllocatable>(pub Option<PtrID<E>>, pub Option<PtrID<E>>);
10
11impl<E: IEntityAllocatable> Clone for EntityListHead<E> {
12    #[inline]
13    fn clone(&self) -> Self {
14        Self(self.0, self.1)
15    }
16}
17impl<E: IEntityAllocatable> Copy for EntityListHead<E> {}
18impl<E: IEntityAllocatable> EntityListHead<E> {
19    #[inline]
20    pub fn none() -> Self {
21        Self(None, None)
22    }
23
24    fn get_prev_ptr(&self) -> Option<PtrID<E>> {
25        self.0
26    }
27    fn get_next_ptr(&self) -> Option<PtrID<E>> {
28        self.1
29    }
30    pub fn get_prev_id(&self) -> Option<E::PtrID> {
31        self.0.map(E::PtrID::from)
32    }
33    pub fn get_next_id(&self) -> Option<E::PtrID> {
34        self.1.map(E::PtrID::from)
35    }
36
37    fn prev_ptr(self, prev: Option<PtrID<E>>) -> Self {
38        Self(prev, self.1)
39    }
40    fn next_ptr(self, next: Option<PtrID<E>>) -> Self {
41        Self(self.0, next)
42    }
43    pub fn next_id(self, next: Option<E::PtrID>) -> Self {
44        Self(self.0, next.map(E::ptr_of_id))
45    }
46    pub fn prev_id(self, prev: Option<E::PtrID>) -> Self {
47        Self(prev.map(E::ptr_of_id), self.1)
48    }
49}
50
51mod list_util {
52    pub(super) struct RingList;
53    pub(super) struct LinkList;
54}
55
56trait RawListNodeOps<E: IEntityAllocatable, KindT> {
57    fn get_prev_ptr(&self) -> Option<PtrID<E>>;
58    fn get_next_ptr(&self) -> Option<PtrID<E>>;
59    fn set_prev_ptr(&self, prev: Option<PtrID<E>>);
60    fn set_next_ptr(&self, next: Option<PtrID<E>>);
61}
62
63pub enum EntityListError<E: IEntityAllocatable> {
64    EmptyList,
65    ListBroken,
66    NodeIsSentinel,
67    RepeatedNode,
68    RepeatedList,
69    SelfNotAttached(PtrID<E>),
70    ItemFalselyAttached(PtrID<E>),
71    ItemFalselyDetached(PtrID<E>),
72}
73impl<E: IEntityAllocatable> Debug for EntityListError<E> {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        use EntityListError::*;
76        match self {
77            EmptyList => write!(f, "EmptyList"),
78            ListBroken => write!(f, "ListBroken"),
79            NodeIsSentinel => write!(f, "NodeIsSentinel"),
80            RepeatedNode => write!(f, "RepeatedNode"),
81            RepeatedList => write!(f, "RepeatedList"),
82            SelfNotAttached(id) => {
83                write!(f, "SelfNotAttached({id:?})")
84            }
85            ItemFalselyAttached(id) => {
86                write!(f, "ItemFalselyAttached({id:?})")
87            }
88            ItemFalselyDetached(id) => {
89                write!(f, "ItemFalselyDetached({id:?})")
90            }
91        }
92    }
93}
94impl<E: IEntityAllocatable> Clone for EntityListError<E> {
95    fn clone(&self) -> Self {
96        use EntityListError::*;
97        match self {
98            EmptyList => EmptyList,
99            ListBroken => ListBroken,
100            NodeIsSentinel => NodeIsSentinel,
101            RepeatedNode => RepeatedNode,
102            RepeatedList => RepeatedList,
103            SelfNotAttached(id) => SelfNotAttached(*id),
104            ItemFalselyAttached(id) => ItemFalselyAttached(*id),
105            ItemFalselyDetached(id) => ItemFalselyDetached(*id),
106        }
107    }
108}
109impl<E: IEntityAllocatable> Copy for EntityListError<E> {}
110impl<E: IEntityAllocatable> Display for EntityListError<E> {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        write!(f, "{self:?}")
113    }
114}
115impl<E: IEntityAllocatable> Error for EntityListError<E> {}
116
117pub type PtrListRes<E, T = ()> = Result<T, EntityListError<E>>;
118
119pub trait IEntityListNode: Sized + IEntityAllocatable {
120    fn load_head(&self) -> EntityListHead<Self>;
121    fn store_head(&self, head: EntityListHead<Self>);
122    fn is_sentinel(&self) -> bool;
123    fn new_sentinel() -> Self;
124    fn on_push_next(
125        curr: Self::PtrID,
126        next: Self::PtrID,
127        alloc: &EntityAlloc<Self>,
128    ) -> PtrListRes<Self>;
129    fn on_push_prev(
130        curr: Self::PtrID,
131        prev: Self::PtrID,
132        alloc: &EntityAlloc<Self>,
133    ) -> PtrListRes<Self>;
134    fn on_unplug(curr: Self::PtrID, alloc: &EntityAlloc<Self>) -> PtrListRes<Self>;
135
136    #[inline]
137    fn get_prev_id(&self) -> Option<Self::PtrID> {
138        self.load_head().get_prev_id()
139    }
140    #[inline]
141    fn get_next_id(&self) -> Option<Self::PtrID> {
142        self.load_head().get_next_id()
143    }
144
145    #[inline]
146    fn set_prev_id(&self, prev: Option<Self::PtrID>) {
147        self.store_head(self.load_head().prev_id(prev));
148    }
149    #[inline]
150    fn set_next_id(&self, next: Option<Self::PtrID>) {
151        self.store_head(self.load_head().next_id(next));
152    }
153
154    #[inline]
155    fn is_attached(&self) -> bool {
156        let EntityListHead(prev, next) = self.load_head();
157        assert!(
158            self.is_sentinel() || prev.is_some() == next.is_some(),
159            "ptrlist node is corrupted"
160        );
161        prev.is_some() || next.is_some()
162    }
163
164    fn comes_before(
165        curr: Self::PtrID,
166        other: Self::PtrID,
167        alloc: &EntityAlloc<Self>,
168    ) -> PtrListRes<Self> {
169        let mut node = Self::ptr_of_id(curr);
170        while let Some(next) = node.deref(alloc).get_next_ptr() {
171            if next == other.into() {
172                return Ok(());
173            }
174            node = next;
175        }
176        Err(EntityListError::ListBroken)
177    }
178}
179impl<E: IEntityListNode> RawListNodeOps<E, list_util::LinkList> for E {
180    fn get_prev_ptr(&self) -> Option<PtrID<E>> {
181        self.load_head().get_prev_ptr()
182    }
183    fn get_next_ptr(&self) -> Option<PtrID<E>> {
184        self.load_head().get_next_ptr()
185    }
186    fn set_prev_ptr(&self, prev: Option<PtrID<E>>) {
187        self.store_head(self.load_head().prev_ptr(prev))
188    }
189    fn set_next_ptr(&self, next: Option<PtrID<E>>) {
190        self.store_head(self.load_head().next_ptr(next))
191    }
192}
193
194/// A partial-close range of an entity pointer list representing `[start, end)`.
195///
196/// - if `end is None`, the range is open-ended.
197/// - the `start` is non-nullable, meaning any range must have a valid start.
198pub struct EntityListRange<E: IEntityListNode> {
199    pub start: E::PtrID,
200    pub end: Option<E::PtrID>,
201}
202
203impl<E: IEntityListNode> EntityListRange<E> {
204    #[inline]
205    pub fn is_empty(&self) -> bool {
206        Some(self.start) == self.end
207    }
208
209    pub fn iter<'alloc>(&self, alloc: &'alloc EntityAlloc<E>) -> EntityListReadIter<'alloc, E> {
210        EntityListReadIter {
211            alloc,
212            curr: Some(E::ptr_of_id(self.start)),
213            end: self.end.map(E::ptr_of_id),
214        }
215    }
216}
217
218pub struct EntityList<E: IEntityListNode> {
219    pub head: PtrID<E>,
220    pub tail: PtrID<E>,
221    pub len: Cell<usize>,
222}
223
224impl<E: IEntityListNode> EntityList<E> {
225    /// create a new empty list with head and tail sentinels linked
226    /// with each other.
227    pub fn new(alloc: &EntityAlloc<E>) -> Self {
228        let head = alloc.allocate(E::new_sentinel());
229        let tail = alloc.allocate(E::new_sentinel());
230        head.deref(alloc).set_next_ptr(Some(tail));
231        tail.deref(alloc).set_prev_ptr(Some(head));
232        Self {
233            head,
234            tail,
235            len: Cell::new(0),
236        }
237    }
238
239    /// get the number of nodes in the list.
240    #[inline]
241    pub fn len(&self) -> usize {
242        self.len.get()
243    }
244    /// check if the list is empty.
245    #[inline]
246    pub fn is_empty(&self) -> bool {
247        self.len.get() == 0
248    }
249
250    /// try to get the front node ID, or None if the list is empty
251    #[inline]
252    pub fn get_front_id(&self, alloc: &EntityAlloc<E>) -> Option<E::PtrID> {
253        self.get_front_ptr(alloc).map(E::id_of_ptr)
254    }
255    /// try to get the back node ID, or None if the list is empty
256    #[inline]
257    pub fn get_back_id(&self, alloc: &EntityAlloc<E>) -> Option<E::PtrID> {
258        self.get_back_ptr(alloc).map(E::id_of_ptr)
259    }
260    fn get_front_ptr(&self, alloc: &EntityAlloc<E>) -> Option<PtrID<E>> {
261        let next = self.head.deref(alloc).get_next_ptr()?;
262        if next == self.tail { None } else { Some(next) }
263    }
264    fn get_back_ptr(&self, alloc: &EntityAlloc<E>) -> Option<PtrID<E>> {
265        let prev = self.tail.deref(alloc).get_prev_ptr()?;
266        if prev == self.head { None } else { Some(prev) }
267    }
268
269    /// Add a new node after an existing node linked to `this` list.
270    ///
271    /// If the current node is linked to another list, then the operation is undefined.
272    /// You'll get a broken list without reporting an error.
273    pub fn node_add_next(
274        &self,
275        node: E::PtrID,
276        new_node: E::PtrID,
277        alloc: &EntityAlloc<E>,
278    ) -> PtrListRes<E> {
279        if node == new_node {
280            return Err(EntityListError::RepeatedNode);
281        }
282        let node_ptr = node.into();
283        let new_node_ptr = new_node.into();
284        // Disallow inserting after tail sentinel; the new node would be unreachable
285        if node_ptr == self.tail {
286            return Err(EntityListError::NodeIsSentinel);
287        }
288        E::on_push_next(node, new_node, alloc)?;
289
290        let node_obj = node_ptr.deref(alloc);
291        let new_node_obj = new_node_ptr.deref(alloc);
292        assert!(node_obj.is_attached(), "Cannot add next to a detached node");
293        assert!(
294            !new_node_obj.is_attached(),
295            "Cannot add a node that is already attached"
296        );
297
298        let orig_next = node_obj.get_next_ptr();
299        new_node_obj.store_head(EntityListHead(Some(node_ptr), orig_next));
300        node_obj.set_next_ptr(Some(new_node_ptr));
301        if let Some(orig_next) = orig_next {
302            orig_next.deref(alloc).set_prev_ptr(Some(new_node_ptr));
303        }
304        self.len.set(self.len.get() + 1);
305        Ok(())
306    }
307
308    /// Add a new node before an existing node linked to `this` list.
309    ///
310    /// If the current node is linked to another list, then the operation is undefined.
311    /// You'll get a broken list without reporting an error.
312    pub fn node_add_prev(
313        &self,
314        node: E::PtrID,
315        new_node: E::PtrID,
316        alloc: &EntityAlloc<E>,
317    ) -> PtrListRes<E> {
318        if node == new_node {
319            return Err(EntityListError::RepeatedNode);
320        }
321        E::on_push_prev(node, new_node, alloc)?;
322        // Disallow inserting before head sentinel; it would corrupt invariants
323        let node_ptr = node.into();
324        let new_node_ptr = new_node.into();
325        if node_ptr == self.head {
326            return Err(EntityListError::NodeIsSentinel);
327        }
328        let node_obj = node_ptr.deref(alloc);
329        let new_node_obj = new_node_ptr.deref(alloc);
330
331        assert!(node_obj.is_attached(), "Cannot add prev to a detached node");
332        assert!(
333            !new_node_obj.is_attached(),
334            "Cannot add a node that is already attached"
335        );
336        let orig_prev = node_obj.get_prev_ptr();
337        new_node_obj.store_head(EntityListHead(orig_prev, Some(node_ptr)));
338        node_obj.set_prev_ptr(Some(new_node_ptr));
339        if let Some(orig_prev) = orig_prev {
340            orig_prev.deref(alloc).set_next_ptr(Some(new_node_ptr));
341        }
342        self.len.set(self.len.get() + 1);
343        Ok(())
344    }
345    pub fn node_unplug(&self, node: E::PtrID, alloc: &EntityAlloc<E>) -> PtrListRes<E> {
346        let node_ptr = node.into();
347        if node_ptr == self.head || node_ptr == self.tail {
348            return Err(EntityListError::NodeIsSentinel);
349        }
350        E::on_unplug(node, alloc)?;
351        let node_obj = node_ptr.deref(alloc);
352        assert!(node_obj.is_attached(), "Cannot unplug a detached node");
353
354        let prev = node_obj.get_prev_ptr().ok_or(EntityListError::ListBroken)?;
355        let next = node_obj.get_next_ptr().ok_or(EntityListError::ListBroken)?;
356        prev.deref(alloc).set_next_ptr(Some(next));
357        next.deref(alloc).set_prev_ptr(Some(prev));
358        node_obj.store_head(EntityListHead(None, None));
359        self.len.set(self.len.get() - 1);
360        Ok(())
361    }
362
363    #[inline]
364    pub fn push_back_id(&self, new_node: E::PtrID, alloc: &EntityAlloc<E>) -> PtrListRes<E> {
365        self.node_add_prev(E::id_of_ptr(self.tail), new_node, alloc)
366    }
367    #[inline]
368    pub fn push_front_id(&self, new_node: E::PtrID, alloc: &EntityAlloc<E>) -> PtrListRes<E> {
369        self.node_add_next(E::id_of_ptr(self.head), new_node, alloc)
370    }
371    #[inline]
372    pub fn push_back_val(&self, val: E, alloc: &EntityAlloc<E>) -> PtrListRes<E> {
373        self.push_back_id(E::PtrID::from(alloc.allocate(val)), alloc)
374    }
375    #[inline]
376    pub fn push_front_val(&self, val: E, alloc: &EntityAlloc<E>) -> PtrListRes<E> {
377        self.push_front_id(E::PtrID::from(alloc.allocate(val)), alloc)
378    }
379
380    pub fn pop_back(&self, alloc: &EntityAlloc<E>) -> PtrListRes<E, E::PtrID> {
381        let back_id = self.get_back_id(alloc).ok_or(EntityListError::EmptyList)?;
382        self.node_unplug(back_id, alloc)?;
383        Ok(back_id)
384    }
385    pub fn pop_front(&self, alloc: &EntityAlloc<E>) -> PtrListRes<E, E::PtrID> {
386        let front_id = self.get_front_id(alloc).ok_or(EntityListError::EmptyList)?;
387        self.node_unplug(front_id, alloc)?;
388        Ok(front_id)
389    }
390
391    pub fn foreach(&self, alloc: &EntityAlloc<E>, mut f: impl FnMut(E::PtrID, &E) -> bool) -> bool {
392        let mut node_opt = self.get_front_ptr(alloc);
393        let mut count = 0;
394        while let Some(node) = node_opt {
395            let node_obj = node.deref(alloc);
396            if !f(E::id_of_ptr(node), node_obj) {
397                return false;
398            }
399            node_opt = node_obj.get_next_ptr();
400            if node_opt == Some(self.tail) {
401                assert_eq!(count, self.len(), "Broken list detected in foreach");
402                return true;
403            }
404            count += 1;
405        }
406        unreachable!("Broken list {self:p} detected in foreach after {count} nodes");
407    }
408    pub fn forall_with_sentinel(
409        &self,
410        alloc: &EntityAlloc<E>,
411        mut f: impl FnMut(E::PtrID, &E) -> bool,
412    ) -> bool {
413        let mut node_opt = Some(self.head);
414        let mut count = 0;
415        while let Some(node) = node_opt {
416            let node_obj = node.deref(alloc);
417            if !f(E::PtrID::from(node), node_obj) {
418                return false;
419            }
420            node_opt = node_obj.get_next_ptr();
421            count += 1;
422        }
423        assert_eq!(
424            count,
425            self.len() + 2,
426            "Broken list {self:p} detected in forall_with_sentinel after {count} nodes"
427        );
428        true
429    }
430
431    pub fn get_range(&self, alloc: &EntityAlloc<E>) -> EntityListRange<E> {
432        let Some(start) = self.head.deref(alloc).get_next_id() else {
433            panic!("EntityList corrupted: head has no next");
434        };
435        EntityListRange {
436            start,
437            end: Some(E::id_of_ptr(self.tail)),
438        }
439    }
440    /// Get the range including sentinels.
441    pub fn get_range_with_sentinels(&self) -> EntityListRange<E> {
442        EntityListRange {
443            start: E::id_of_ptr(self.head),
444            end: None,
445        }
446    }
447
448    pub fn iter<'alloc>(
449        &'alloc self,
450        alloc: &'alloc EntityAlloc<E>,
451    ) -> EntityListReadIter<'alloc, E> {
452        EntityListReadIter {
453            alloc,
454            curr: self.head.deref(alloc).get_next_ptr(),
455            end: Some(self.tail),
456        }
457    }
458    pub fn iter_with_sentinels<'alloc>(
459        &'alloc self,
460        alloc: &'alloc EntityAlloc<E>,
461    ) -> EntityListReadIter<'alloc, E> {
462        EntityListReadIter {
463            alloc,
464            curr: Some(self.head),
465            end: None,
466        }
467    }
468}
469
470pub struct EntityListReadIter<'alloc, E: IEntityListNode> {
471    alloc: &'alloc EntityAlloc<E>,
472    curr: Option<PtrID<E>>,
473    end: Option<PtrID<E>>,
474}
475impl<'alloc, E: IEntityListNode> Iterator for EntityListReadIter<'alloc, E> {
476    type Item = (E::PtrID, &'alloc E);
477
478    fn next(&mut self) -> Option<Self::Item> {
479        let curr_ptr = self.curr?;
480        if Some(curr_ptr) == self.end {
481            return None;
482        }
483        let curr_obj = curr_ptr.deref(self.alloc);
484        self.curr = curr_obj.get_next_ptr();
485        Some((E::id_of_ptr(curr_ptr), curr_obj))
486    }
487}
488
489/// An entity pointer list node that supports ring lists.
490pub trait IEntityRingListNode: Sized + IEntityAllocatable {
491    fn load_head(&self) -> EntityListHead<Self>;
492    fn store_head(&self, head: EntityListHead<Self>);
493    fn is_sentinel(&self) -> bool;
494    fn new_sentinel() -> Self;
495
496    fn ring_list_node_dispose(&self, alloc: &EntityAlloc<Self>) {
497        self.detach(alloc)
498            .expect("Failed to detach node during disposal");
499    }
500    fn on_self_unplug(&self, self_id: Self::PtrID, alloc: &EntityAlloc<Self>);
501
502    #[inline]
503    fn get_prev_id(&self) -> Option<Self::PtrID> {
504        self.load_head().get_prev_id()
505    }
506    #[doc(hidden)]
507    fn get_prev_ptr(&self) -> Option<PtrID<Self>> {
508        self.load_head().get_prev_ptr()
509    }
510    #[inline]
511    fn get_next_id(&self) -> Option<Self::PtrID> {
512        self.load_head().get_next_id()
513    }
514    #[doc(hidden)]
515    fn get_next_ptr(&self) -> Option<PtrID<Self>> {
516        self.load_head().get_next_ptr()
517    }
518    #[inline]
519    fn set_prev_id(&self, prev: Option<Self::PtrID>) {
520        self.store_head(self.load_head().prev_id(prev));
521    }
522    #[doc(hidden)]
523    fn set_prev_ptr(&self, prev: Option<PtrID<Self>>) {
524        self.store_head(self.load_head().prev_ptr(prev));
525    }
526    #[inline]
527    fn set_next_id(&self, next: Option<Self::PtrID>) {
528        self.store_head(self.load_head().next_id(next));
529    }
530    #[doc(hidden)]
531    fn set_next_ptr(&self, next: Option<PtrID<Self>>) {
532        self.store_head(self.load_head().next_ptr(next));
533    }
534
535    #[inline]
536    fn is_attached(&self) -> bool {
537        let EntityListHead(prev, next) = self.load_head();
538        assert_eq!(prev.is_some(), next.is_some(), "ptrlist node is corrupted");
539        prev.is_some() || next.is_some()
540    }
541    fn detach(&self, alloc: &EntityAlloc<Self>) -> PtrListRes<Self> {
542        if self.is_sentinel() {
543            return Err(EntityListError::NodeIsSentinel);
544        }
545        let EntityListHead(Some(prev), Some(next)) = self.load_head() else {
546            // detach() 在节点未连接时调用是合法的,直接返回 Ok(())
547            return Ok(());
548        };
549        prev.deref(alloc).set_next_ptr(Some(next));
550        next.deref(alloc).set_prev_ptr(Some(prev));
551        self.store_head(EntityListHead(None, None));
552        Ok(())
553    }
554}
555impl<E: IEntityRingListNode> RawListNodeOps<E, list_util::RingList> for E {
556    fn get_prev_ptr(&self) -> Option<PtrID<E>> {
557        self.load_head().get_prev_ptr()
558    }
559    fn get_next_ptr(&self) -> Option<PtrID<E>> {
560        self.load_head().get_next_ptr()
561    }
562    fn set_prev_ptr(&self, prev: Option<PtrID<E>>) {
563        self.store_head(self.load_head().prev_ptr(prev))
564    }
565    fn set_next_ptr(&self, next: Option<PtrID<E>>) {
566        self.store_head(self.load_head().next_ptr(next))
567    }
568}
569
570impl<E: IEntityRingListNode> PtrID<E> {
571    pub fn detach(self, alloc: &EntityAlloc<E>) -> PtrListRes<E> {
572        self.deref(alloc).detach(alloc)
573    }
574}
575
576/// A doubly-linked ring list of entity pointers.
577pub struct EntityRingList<E: IEntityRingListNode> {
578    pub sentinel: E::PtrID,
579}
580
581impl<E: IEntityRingListNode> EntityRingList<E> {
582    pub fn new(alloc: &EntityAlloc<E>) -> Self {
583        let sentinel = alloc.allocate(E::new_sentinel());
584        let head = Some(sentinel);
585        sentinel.deref(alloc).store_head(EntityListHead(head, head));
586        Self {
587            sentinel: E::id_of_ptr(sentinel),
588        }
589    }
590    pub fn len(&self, alloc: &EntityAlloc<E>) -> usize {
591        let mut count = 0;
592        self.foreach(alloc, |_, _| {
593            count += 1;
594            true
595        });
596        count
597    }
598    pub fn front_id(&self, alloc: &EntityAlloc<E>) -> Option<E::PtrID> {
599        let sentinel = E::ptr_of_id(self.sentinel);
600        let next = sentinel.deref(alloc).get_next_id()?;
601        if next == self.sentinel {
602            None
603        } else {
604            Some(next)
605        }
606    }
607    pub fn back_id(&self, alloc: &EntityAlloc<E>) -> Option<E::PtrID> {
608        let sentinel = E::ptr_of_id(self.sentinel);
609        let prev = sentinel.deref(alloc).get_prev_id()?;
610        if prev == self.sentinel {
611            None
612        } else {
613            Some(prev)
614        }
615    }
616
617    pub fn is_empty(&self, alloc: &EntityAlloc<E>) -> bool {
618        const ERRMSG: &str = "ring list corrupted when checking is_empty";
619        let sentinel = E::ptr_of_id(self.sentinel);
620        let sentinel_obj = sentinel.deref(alloc);
621        let next = sentinel_obj.get_next_ptr().expect(ERRMSG);
622        next == sentinel
623    }
624    pub fn is_single(&self, alloc: &EntityAlloc<E>) -> bool {
625        const ERRMSG: &str = "ring list corrupted when checking is_single";
626        let sentinel = E::ptr_of_id(self.sentinel);
627        let sentinel_obj = sentinel.deref(alloc);
628        let next = sentinel_obj.get_next_ptr().expect(ERRMSG);
629        let prev = sentinel_obj.get_prev_ptr().expect(ERRMSG);
630        next == prev && next != sentinel
631    }
632    pub fn is_multiple(&self, alloc: &EntityAlloc<E>) -> bool {
633        const ERRMSG: &str = "ring list corrupted when checking is_multiple";
634        let sentinel = E::ptr_of_id(self.sentinel);
635        let sentinel_obj = sentinel.deref(alloc);
636        let next = sentinel_obj.get_next_ptr().expect(ERRMSG);
637        let prev = sentinel_obj.get_prev_ptr().expect(ERRMSG);
638        next != prev
639    }
640    pub fn foreach(
641        &self,
642        alloc: &EntityAlloc<E>,
643        mut pred: impl FnMut(E::PtrID, &E) -> bool,
644    ) -> bool {
645        let sentinel = E::ptr_of_id(self.sentinel);
646        let mut node_opt = sentinel.deref(alloc).get_next_ptr();
647        let mut count = 0;
648        while let Some(node_ptr) = node_opt {
649            if node_ptr == sentinel {
650                return true;
651            }
652            let node_obj = node_ptr.deref(alloc);
653            if !pred(E::id_of_ptr(node_ptr), node_obj) {
654                return false;
655            }
656            node_opt = node_obj.get_next_ptr();
657            count += 1;
658        }
659        panic!("Broken ring list detected after {count} iterations");
660    }
661    pub fn forall_with_sentinel(
662        &self,
663        alloc: &EntityAlloc<E>,
664        mut pred: impl FnMut(E::PtrID, &E) -> bool,
665    ) -> bool {
666        let mut node_opt = Some(self.sentinel);
667        let mut count = 0;
668        while let Some(node_id) = node_opt {
669            let node_ptr = E::ptr_of_id(node_id);
670            let node_obj = node_ptr.deref(alloc);
671            if !pred(node_id, node_obj) {
672                return false;
673            }
674            node_opt = node_obj.get_next_id();
675            if node_opt == Some(self.sentinel) {
676                return true;
677            }
678            count += 1;
679        }
680        panic!("Broken ring list detected after {count} iterations");
681    }
682    pub fn contains(&self, node: E::PtrID, alloc: &EntityAlloc<E>) -> bool {
683        self.foreach(alloc, |id, _| id == node)
684    }
685    /// Clone a view over the same ring list (shares the sentinel)
686    #[inline]
687    pub fn clone_view(&self) -> Self {
688        Self {
689            sentinel: self.sentinel,
690        }
691    }
692    pub fn iter<'alloc>(
693        &'alloc self,
694        alloc: &'alloc EntityAlloc<E>,
695    ) -> EntityRingListReadIter<'alloc, E> {
696        let sentinel = E::ptr_of_id(self.sentinel);
697        let Some(curr) = sentinel.deref(alloc).get_next_ptr() else {
698            panic!("ring list corrupted when creating iterator");
699        };
700        EntityRingListReadIter { alloc, curr }
701    }
702    pub fn back_iter<'alloc>(
703        &'alloc self,
704        alloc: &'alloc EntityAlloc<E>,
705    ) -> Rev<EntityRingListReadIter<'alloc, E>> {
706        let sentinel = E::ptr_of_id(self.sentinel);
707        let Some(curr) = sentinel.deref(alloc).get_prev_ptr() else {
708            panic!("ring list corrupted when creating back iterator");
709        };
710        EntityRingListReadIter { alloc, curr }.rev()
711    }
712
713    /// Push to back (before sentinel)
714    pub fn push_back_id(&self, new_node: E::PtrID, alloc: &EntityAlloc<E>) -> PtrListRes<E> {
715        const ERRMSG: &str = "Broken ring detected during push_back";
716
717        // Insert immediately before sentinel: prev <-> new_node <-> sentinel
718        let new_node = E::ptr_of_id(new_node);
719        let sentinel = E::ptr_of_id(self.sentinel);
720        let sentinel_obj = sentinel.deref(alloc);
721
722        let prev = sentinel_obj.get_prev_ptr().expect(ERRMSG);
723        // new_node must not already be attached
724        assert!(!new_node.deref(alloc).is_attached());
725        new_node
726            .deref(alloc)
727            .store_head(EntityListHead(Some(prev), Some(sentinel)));
728        sentinel_obj.set_prev_ptr(Some(new_node));
729        prev.deref(alloc).set_next_ptr(Some(new_node));
730        Ok(())
731    }
732
733    // pop_front/pop_back are intentionally omitted for Remusys IR use-cases;
734    // detach a known node explicitly when needed.
735
736    /// Move all nodes from `self` into `other`, preserving order; invoke on_move for each moved node.
737    pub fn move_all_to(
738        &self,
739        other: &EntityRingList<E>,
740        alloc: &EntityAlloc<E>,
741        mut on_move: impl FnMut(E::PtrID),
742    ) -> PtrListRes<E> {
743        if self.sentinel == other.sentinel {
744            return Ok(());
745        }
746        let mut cur = self.front_id(alloc).map(E::ptr_of_id);
747        while let Some(ptr) = cur {
748            let next = ptr.deref(alloc).get_next_ptr();
749            ptr.detach(alloc)?;
750
751            let id = E::id_of_ptr(ptr);
752            other.push_back_id(id, alloc)?;
753            on_move(id);
754            cur = match next {
755                Some(n) if !n.deref(alloc).is_sentinel() => Some(n),
756                _ => None,
757            };
758        }
759        Ok(())
760    }
761    /// Move nodes that satisfy `predicate` from `self` to `other`, preserving order.
762    pub fn move_to_if(
763        &self,
764        other: &EntityRingList<E>,
765        alloc: &EntityAlloc<E>,
766        mut predicate: impl FnMut(E::PtrID, &E) -> bool,
767        mut on_move: impl FnMut(E::PtrID),
768    ) -> PtrListRes<E> {
769        if self.sentinel == other.sentinel {
770            return Ok(());
771        }
772        let mut cur = self.front_id(alloc).map(E::ptr_of_id);
773        while let Some(ptr) = cur {
774            let next = ptr.deref(alloc).get_next_ptr();
775            let id = E::PtrID::from(ptr);
776            let obj = ptr.deref(alloc);
777            if predicate(id, obj) {
778                ptr.detach(alloc)?;
779                other.push_back_id(id, alloc)?;
780                on_move(id);
781            }
782            cur = match next {
783                Some(n) if !n.deref(alloc).is_sentinel() => Some(n),
784                _ => None,
785            };
786        }
787        Ok(())
788    }
789
790    /// notifies all nodes in the ring list that the list is being dropped
791    pub fn clean(&self, alloc: &EntityAlloc<E>) {
792        let sentinel = E::ptr_of_id(self.sentinel);
793        let mut node = sentinel.deref(alloc).get_next_ptr();
794        let mut count = 0;
795        while let Some(node_ptr) = node {
796            if node_ptr == sentinel {
797                let head = EntityListHead(Some(sentinel), Some(sentinel));
798                sentinel.deref(alloc).store_head(head);
799                return;
800            }
801            let next = node_ptr.deref(alloc).get_next_ptr();
802            let node_obj = node_ptr.deref(alloc);
803            node_obj.store_head(EntityListHead(None, None));
804            node_obj.on_self_unplug(E::id_of_ptr(node_ptr), alloc);
805            node = next;
806            count += 1;
807        }
808        panic!("Broken ring list detected during clean after {count} iterations");
809    }
810
811    pub fn pop_back(&self, alloc: &EntityAlloc<E>) -> PtrListRes<E, E::PtrID> {
812        let back_id = self.back_id(alloc).ok_or(EntityListError::EmptyList)?;
813        let back_ptr = E::ptr_of_id(back_id);
814        back_ptr.detach(alloc)?;
815        back_ptr.deref(alloc).on_self_unplug(back_id, alloc);
816        Ok(back_id)
817    }
818    pub fn pop_front(&self, alloc: &EntityAlloc<E>) -> PtrListRes<E, PtrID<E>> {
819        let front_id = self.front_id(alloc).ok_or(EntityListError::EmptyList)?;
820        let front_ptr = E::ptr_of_id(front_id);
821        front_ptr.detach(alloc)?;
822        front_ptr.deref(alloc).on_self_unplug(front_id, alloc);
823        Ok(front_ptr)
824    }
825}
826
827pub struct EntityRingListReadIter<'alloc, E: IEntityRingListNode> {
828    alloc: &'alloc EntityAlloc<E>,
829    curr: PtrID<E>,
830}
831
832impl<'alloc, E: IEntityRingListNode> Iterator for EntityRingListReadIter<'alloc, E> {
833    type Item = (E::PtrID, &'alloc E);
834
835    fn next(&mut self) -> Option<Self::Item> {
836        let curr_ptr = self.curr;
837        let curr_obj = curr_ptr.deref(self.alloc);
838        if curr_obj.is_sentinel() {
839            return None;
840        }
841        let Some(next_ptr) = curr_obj.get_next_ptr() else {
842            panic!("Broken ring list detected during iteration");
843        };
844        if next_ptr == curr_ptr {
845            return None;
846        }
847        self.curr = next_ptr;
848        Some((E::id_of_ptr(curr_ptr), curr_obj))
849    }
850}
851impl<'alloc, E: IEntityRingListNode> DoubleEndedIterator for EntityRingListReadIter<'alloc, E> {
852    fn next_back(&mut self) -> Option<Self::Item> {
853        let curr_ptr = self.curr;
854        let curr_obj = curr_ptr.deref(self.alloc);
855        if curr_obj.is_sentinel() {
856            return None;
857        }
858        let Some(prev_ptr) = curr_obj.get_prev_ptr() else {
859            panic!("Broken ring list detected during back iteration");
860        };
861        if prev_ptr == curr_ptr {
862            return None;
863        }
864        self.curr = prev_ptr;
865        Some((E::id_of_ptr(curr_ptr), curr_obj))
866    }
867}