diff --git a/cordyceps/src/list.rs b/cordyceps/src/list.rs index 1ad23d931..b43435f6c 100644 --- a/cordyceps/src/list.rs +++ b/cordyceps/src/list.rs @@ -614,6 +614,7 @@ impl> + ?Sized> List { Cursor { curr: self.head, list: self, + index: 0, } } @@ -627,6 +628,7 @@ impl> + ?Sized> List { Cursor { curr: self.tail, list: self, + index: self.len().saturating_sub(1), } } diff --git a/cordyceps/src/list/cursor.rs b/cordyceps/src/list/cursor.rs index 17c4fe7ce..a67949e0e 100644 --- a/cordyceps/src/list/cursor.rs +++ b/cordyceps/src/list/cursor.rs @@ -9,6 +9,7 @@ use core::{fmt, pin::Pin, ptr::NonNull}; pub struct Cursor<'list, T: Linked> + ?Sized> { pub(super) list: &'list mut List, pub(super) curr: Link, + pub(super) index: usize, } // === impl Cursor ==== @@ -26,9 +27,16 @@ impl<'a, T: Linked> + ?Sized> Iterator for Cursor<'a, T> { }) } + /// A [`Cursor`] can never return an accurate `size_hint` --- its lower + /// bound is always 0 and its upper bound is always `None`. + /// + /// This is because the cursor may be moved around within the list through + /// methods outside of its `Iterator` implementation, and elements may be + /// added or removed using the cursor. This would make any `size_hint`s a + /// [`Cursor`] returns inaccurate. #[inline] fn size_hint(&self) -> (usize, Option) { - (self.list.len(), Some(self.list.len())) + (0, None) } } @@ -36,9 +44,19 @@ impl<'a, T: Linked> + ?Sized> Cursor<'a, T> { fn next_ptr(&mut self) -> Link { let curr = self.curr.take()?; self.curr = unsafe { T::links(curr).as_ref().next() }; + self.index += 1; Some(curr) } + /// Returns the index of this cursor's position in the [`List`]. + /// + /// This returns `None` if the cursor is currently pointing to the + /// null element. + pub fn index(&self) -> Option { + self.curr?; + Some(self.index) + } + /// Moves the cursor position to the next element in the [`List`]. /// /// If the cursor is pointing at the null element, this moves it to the first @@ -49,10 +67,12 @@ impl<'a, T: Linked> + ?Sized> Cursor<'a, T> { // Advance the cursor to the current node's next element. Some(curr) => unsafe { self.curr = T::links(curr).as_ref().next(); + self.index += 1; }, // We have no current element --- move to the start of the list. None => { self.curr = self.list.head; + self.index = 0; } } } @@ -70,10 +90,14 @@ impl<'a, T: Linked> + ?Sized> Cursor<'a, T> { // Advance the cursor to the current node's prev element. Some(curr) => unsafe { self.curr = T::links(curr).as_ref().prev(); + // this is saturating because the current node might be the 0th + // and we might have set `self.curr` to `None`. + self.index = self.index.saturating_sub(1); }, // We have no current element --- move to the end of the list. None => { self.curr = self.list.tail; + self.index = self.index.checked_sub(1).unwrap_or(self.list.len()); } } } @@ -254,6 +278,7 @@ impl> + ?Sized> fmt::Debug for Cursor<'_, T> { f.debug_struct("Cursor") .field("curr", &FmtOption::new(&self.curr)) .field("list", &self.list) + .field("index", &self.index) .finish() } } diff --git a/cordyceps/src/list/tests/cursor.rs b/cordyceps/src/list/tests/cursor.rs index ec621bc26..bea56d6a9 100644 --- a/cordyceps/src/list/tests/cursor.rs +++ b/cordyceps/src/list/tests/cursor.rs @@ -30,35 +30,35 @@ fn move_peek() { assert_eq!(val(cursor.current()), Some(1)); assert_eq!(val(cursor.peek_next()), Some(2)); assert_eq!(val(cursor.peek_prev()), None); - // assert_eq!(cursor.index(), Some(0)); + assert_eq!(cursor.index(), Some(0)); cursor.move_prev(); assert_eq!(val(cursor.current()), None); assert_eq!(val(cursor.peek_next()), Some(1)); assert_eq!(val(cursor.peek_prev()), Some(6)); - // assert_eq!(cursor.index(), None); + assert_eq!(cursor.index(), None); cursor.move_next(); cursor.move_next(); assert_eq!(val(cursor.current()), Some(2)); assert_eq!(val(cursor.peek_next()), Some(3)); assert_eq!(val(cursor.peek_prev()), Some(1)); - // assert_eq!(cursor.index(), Some(1)); + assert_eq!(cursor.index(), Some(1)); let mut cursor = list.cursor_back_mut(); assert_eq!(val(cursor.current()), Some(6)); assert_eq!(val(cursor.peek_next()), None); assert_eq!(val(cursor.peek_prev()), Some(5)); - // assert_eq!(cursor.index(), Some(5)); + assert_eq!(cursor.index(), Some(5)); cursor.move_next(); assert_eq!(val(cursor.current()), None); assert_eq!(val(cursor.peek_next()), Some(1)); assert_eq!(val(cursor.peek_prev()), Some(6)); - // assert_eq!(cursor.index(), None); + assert_eq!(cursor.index(), None); cursor.move_prev(); cursor.move_prev(); assert_eq!(val(cursor.current()), Some(5)); assert_eq!(val(cursor.peek_next()), Some(6)); assert_eq!(val(cursor.peek_prev()), Some(4)); - // assert_eq!(cursor.index(), Some(4)); + assert_eq!(cursor.index(), Some(4)); // let mut m: LinkedList = LinkedList::new(); // m.extend(&[1, 2, 3, 4, 5, 6]);