Skip to content

Commit

Permalink
feat(cordyceps): add Cursor::index (#227)
Browse files Browse the repository at this point in the history
This adds a `Cursor::index` method to match the one on
`std::collections::linked_list::CursorMut`.

Closes #224

Signed-off-by: Eliza Weisman <[email protected]>
  • Loading branch information
hawkw committed Jun 18, 2022
1 parent 24faf31 commit bb26fff
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 7 deletions.
2 changes: 2 additions & 0 deletions cordyceps/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
Cursor {
curr: self.head,
list: self,
index: 0,
}
}

Expand All @@ -627,6 +628,7 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
Cursor {
curr: self.tail,
list: self,
index: self.len().saturating_sub(1),
}
}

Expand Down
27 changes: 26 additions & 1 deletion cordyceps/src/list/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use core::{fmt, pin::Pin, ptr::NonNull};
pub struct Cursor<'list, T: Linked<Links<T>> + ?Sized> {
pub(super) list: &'list mut List<T>,
pub(super) curr: Link<T>,
pub(super) index: usize,
}

// === impl Cursor ====
Expand All @@ -26,19 +27,36 @@ impl<'a, T: Linked<Links<T>> + ?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<usize>) {
(self.list.len(), Some(self.list.len()))
(0, None)
}
}

impl<'a, T: Linked<Links<T>> + ?Sized> Cursor<'a, T> {
fn next_ptr(&mut self) -> Link<T> {
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<usize> {
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
Expand All @@ -49,10 +67,12 @@ impl<'a, T: Linked<Links<T>> + ?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;
}
}
}
Expand All @@ -70,10 +90,14 @@ impl<'a, T: Linked<Links<T>> + ?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());
}
}
}
Expand Down Expand Up @@ -254,6 +278,7 @@ impl<T: Linked<Links<T>> + ?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()
}
}
12 changes: 6 additions & 6 deletions cordyceps/src/list/tests/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32> = LinkedList::new();
// m.extend(&[1, 2, 3, 4, 5, 6]);
Expand Down

0 comments on commit bb26fff

Please sign in to comment.