Skip to main content

mtb_entity_slab/container/
ring_list.rs

1//! Ring list implementation for entity allocation.
2//!
3//! # Safety
4//!
5//! These lists rely on the correctness of the entity allocator
6//! and the entity ID implementations. Improper use may lead to
7//! undefined behavior, such as broken lists or memory corruption.
8
9use crate::{
10    EntityListError, EntityListIter, EntityListNodeHead, EntityListRange, EntityListRes,
11    IBasicEntityListID, IDBoundAlloc, container::list_base::NodeOps,
12};
13
14/// An entity pointer list node that supports ring lists.
15pub trait IEntityRingListNodeID: IBasicEntityListID {
16    /// Detach the given node from its current ring list.
17    fn obj_detach(obj: &Self::ObjectT, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self> {
18        if Self::obj_is_sentinel(obj) {
19            return Err(EntityListError::NodeIsSentinel);
20        }
21        let (Some(prev), Some(next)) = Self::obj_load_head(obj).into_id() else {
22            // detach() 在节点未连接时调用是合法的,直接返回 Ok(())
23            return Ok(());
24        };
25        prev.set_next_id(alloc, Some(next));
26        next.set_prev_id(alloc, Some(prev));
27        Self::obj_store_head(obj, EntityListNodeHead::none());
28        Ok(())
29    }
30
31    /// Detach this node from the ring list it is currently attached to.
32    ///
33    /// ### Signal invocation
34    ///
35    /// `detach` will invoke `on_unplug` on `self` before modifying any links.
36    /// If the signal returns an error, the operation is aborted without modifying any links.
37    fn detach(self, alloc: &IDBoundAlloc<Self>) -> EntityListRes<Self> {
38        self.on_unplug(alloc)?;
39        Self::obj_detach(self.deref_alloc(alloc), alloc)
40    }
41}
42
43/// Ring list (circular) view over entities stored in an `EntityAlloc`.
44///
45/// The ring list is backed by a sentinel node that always exists; an empty
46/// ring has the sentinel's `next`/`prev` pointing to itself. Nodes embed
47/// `EntityListNodeHead` for linkage and can be detached/attached at O(1).
48pub struct EntityRingList<I: IEntityRingListNodeID> {
49    /// Sentinel node ID.
50    pub sentinel: I,
51}
52
53impl<I: IEntityRingListNodeID> EntityRingList<I> {
54    /// Create a new empty ring list with a sentinel node.
55    pub fn new(alloc: &IDBoundAlloc<I>) -> Self {
56        let sentinel = I::new_sentinel(alloc);
57        I::obj_store_head(
58            sentinel.deref_alloc(alloc),
59            EntityListNodeHead {
60                prev: Some(sentinel),
61                next: Some(sentinel),
62            },
63        );
64        Self { sentinel }
65    }
66    /// Get the front node ID, or None if the list is empty. (Not sentinel)
67    pub fn front_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
68        let next = self.sentinel.get_next_id(alloc)?;
69        if next == self.sentinel {
70            None
71        } else {
72            Some(next)
73        }
74    }
75    /// Get the back node ID, or None if the list is empty. (Not sentinel)
76    pub fn back_id(&self, alloc: &IDBoundAlloc<I>) -> Option<I> {
77        let prev = self.sentinel.get_prev_id(alloc)?;
78        if prev == self.sentinel {
79            None
80        } else {
81            Some(prev)
82        }
83    }
84    /// Check if the ring list is empty.
85    pub fn is_empty(&self, alloc: &IDBoundAlloc<I>) -> bool {
86        self.front_id(alloc).is_none()
87    }
88    /// Check if the ring list has exactly one node (excluding sentinel).
89    pub fn is_single(&self, alloc: &IDBoundAlloc<I>) -> bool {
90        let sentinel = self.sentinel.deref_alloc(alloc);
91        let head = I::obj_load_head(sentinel);
92        let (Some(prev), Some(next)) = head.into_id() else {
93            return false;
94        };
95        prev == next && prev != self.sentinel
96    }
97    /// Check if the ring list has multiple nodes (excluding sentinel).
98    pub fn is_multiple(&self, alloc: &IDBoundAlloc<I>) -> bool {
99        let sentinel = self.sentinel.deref_alloc(alloc);
100        let head = I::obj_load_head(sentinel);
101        let (Some(prev), Some(next)) = head.into_id() else {
102            return false;
103        };
104        prev != next
105    }
106
107    /// traverse each node in the list, invoking `pred` on each node.
108    ///
109    /// ### panics
110    ///
111    /// If a broken ring list is detected, this function will panic.
112    pub fn foreach(
113        &self,
114        alloc: &IDBoundAlloc<I>,
115        mut pred: impl FnMut(I, &I::ObjectT) -> bool,
116    ) -> bool {
117        let mut curr = self.sentinel.get_next_id(alloc);
118        let mut count = 0;
119        while let Some(node_id) = curr {
120            if node_id == self.sentinel {
121                return true;
122            }
123            let node_obj = node_id.deref_alloc(alloc);
124            if !pred(node_id, node_obj) {
125                return false;
126            }
127            curr = node_id.get_next_id(alloc);
128            count += 1;
129        }
130        panic!("Broken ring list detected after {count} nodes");
131    }
132
133    /// traverse each node in the list starting from the sentinel, invoking `pred` on each node.
134    ///
135    /// ### panics
136    ///
137    /// If a broken ring list is detected, this function will panic.
138    pub fn forall_with_sentinel(
139        &self,
140        alloc: &IDBoundAlloc<I>,
141        mut pred: impl FnMut(I, &I::ObjectT) -> bool,
142    ) -> bool {
143        let mut curr = Some(self.sentinel);
144        let mut count = 0;
145        while let Some(node_id) = curr {
146            let node_obj = node_id.deref_alloc(alloc);
147            if !pred(node_id, node_obj) {
148                return false;
149            }
150            curr = node_id.get_next_id(alloc);
151            count += 1;
152            if curr == Some(self.sentinel) {
153                return true;
154            }
155        }
156        panic!("Broken ring list detected after {count} nodes");
157    }
158
159    /// Get the number of nodes in the ring list (excluding sentinel).
160    ///
161    /// NOTE: This function traverses the entire list to count nodes,
162    /// so its time complexity is `O(n)`.
163    pub fn len(&self, alloc: &IDBoundAlloc<I>) -> usize {
164        let mut len = 0;
165        self.foreach(alloc, |_, _| {
166            len += 1;
167            true
168        });
169        len
170    }
171    /// Check if the ring list contains the given node.
172    pub fn contains(&self, node: I, alloc: &IDBoundAlloc<I>) -> bool {
173        !self.foreach(alloc, |id, _| id != node)
174    }
175
176    /// Create a clone of this ring list view (shares the same sentinel).
177    #[inline]
178    #[deprecated = "Use 'get_range' instead of cloning the view"]
179    pub fn clone_view(&self) -> Self {
180        Self {
181            sentinel: self.sentinel,
182        }
183    }
184
185    /// Get the range covering all nodes in the ring list (excluding sentinel).
186    pub fn get_range(&self, alloc: &IDBoundAlloc<I>) -> EntityListRange<I> {
187        EntityListRange {
188            start: self
189                .sentinel
190                .get_next_id(alloc)
191                .expect("Broken ring list detected in get_range()"),
192            end: Some(self.sentinel),
193        }
194    }
195
196    /// Push a new node to the back of the ring list.
197    pub fn push_back(&self, new_node: I, alloc: &IDBoundAlloc<I>) -> EntityListRes<I> {
198        const ERRMSG: &str = "Broken ring detected during push_back";
199
200        let sentinel_obj = self.sentinel.deref_alloc(alloc);
201        let prev = I::obj_get_prev_id(sentinel_obj).expect(ERRMSG);
202        if prev == new_node {
203            return Err(EntityListError::RepeatedNode);
204        }
205        if new_node.is_attached(alloc) {
206            return Err(EntityListError::ItemFalselyAttached(new_node));
207        }
208        // Link new_node between prev and sentinel
209        let new_node_obj = new_node.deref_alloc(alloc);
210        I::obj_store_head(
211            new_node_obj,
212            EntityListNodeHead::from_id(prev, self.sentinel),
213        );
214        // Update neighbors to point to new_node
215        prev.set_next_id(alloc, Some(new_node));
216        self.sentinel.set_prev_id(alloc, Some(new_node));
217        Ok(())
218    }
219
220    /// Pop a node from the back of the ring list.
221    pub fn pop_back(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
222        let back_id = self.back_id(alloc).ok_or(EntityListError::EmptyList)?;
223        back_id.detach(alloc)?;
224        Ok(back_id)
225    }
226    /// Pop a node from the front of the ring list.
227    pub fn pop_front(&self, alloc: &IDBoundAlloc<I>) -> EntityListRes<I, I> {
228        let front_id = self.front_id(alloc).ok_or(EntityListError::EmptyList)?;
229        front_id.detach(alloc)?;
230        Ok(front_id)
231    }
232
233    /// Move all nodes from `self` into `other`, preserving order; invoke on_move for each moved node.
234    pub fn move_all_to(
235        &self,
236        other: &Self,
237        alloc: &IDBoundAlloc<I>,
238        mut on_move: impl FnMut(I),
239    ) -> EntityListRes<I> {
240        if self.sentinel == other.sentinel {
241            return Ok(());
242        }
243        loop {
244            let node = match self.pop_front(alloc) {
245                Ok(n) => n,
246                Err(EntityListError::EmptyList) => break Ok(()),
247                Err(e) => break Err(e),
248            };
249            other.push_back(node, alloc)?;
250            on_move(node);
251        }
252    }
253
254    /// Move nodes that satisfy `predicate` from `self` to `other`, preserving order.
255    pub fn move_to_if(
256        &self,
257        other: &Self,
258        alloc: &IDBoundAlloc<I>,
259        mut pred: impl FnMut(I, &I::ObjectT) -> bool,
260        mut on_move: impl FnMut(I),
261    ) -> EntityListRes<I> {
262        if self.sentinel == other.sentinel {
263            return Err(EntityListError::RepeatedList);
264        }
265        let mut curr = self.sentinel;
266        loop {
267            let Some(next) = curr.get_next_id(alloc) else {
268                break;
269            };
270            if next == self.sentinel {
271                return Ok(());
272            }
273            let next_obj = next.deref_alloc(alloc);
274            if !pred(next, next_obj) {
275                curr = next;
276                continue;
277            }
278            next.detach(alloc)?;
279            other.push_back(next, alloc)?;
280            on_move(next);
281        }
282        Err(EntityListError::ListBroken)
283    }
284
285    /// Clean up the ring list by removing all nodes; panic if a broken ring is detected.
286    pub fn clean(&self, alloc: &IDBoundAlloc<I>) {
287        let mut count = 0;
288        let err = loop {
289            match self.pop_front(alloc) {
290                Ok(_) => count += 1,
291                Err(EntityListError::EmptyList) => return,
292                Err(e) => break e,
293            };
294        };
295        panic!("Broken ring list detected after removing {count} nodes: {err}");
296    }
297
298    /// Create an iterator over all nodes in the ring list (excluding sentinel).
299    pub fn iter<'a>(&self, alloc: &'a IDBoundAlloc<I>) -> EntityListIter<'a, I> {
300        let next_node = self
301            .sentinel
302            .get_next_id(alloc)
303            .expect("Broken ring list detected in iter()");
304        EntityListIter {
305            alloc,
306            curr: Some(next_node),
307            end: Some(self.sentinel),
308        }
309    }
310}