Skip to content

Add linked list cursor end methods #86714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions library/alloc/src/collections/linked_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,20 @@ impl<'a, T> Cursor<'a, T> {
prev.map(|prev| &(*prev.as_ptr()).element)
}
}

/// Provides a reference to the front element of the cursor's parent list,
/// or None if the list is empty.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn front(&self) -> Option<&'a T> {
self.list.front()
}

/// Provides a reference to the back element of the cursor's parent list,
/// or None if the list is empty.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn back(&self) -> Option<&'a T> {
self.list.back()
}
}

impl<'a, T> CursorMut<'a, T> {
Expand Down Expand Up @@ -1506,6 +1520,135 @@ impl<'a, T> CursorMut<'a, T> {
self.index = 0;
unsafe { self.list.split_off_before_node(self.current, split_off_idx) }
}

/// Appends an element to the front of the cursor's parent list. The node
/// that the cursor points to is unchanged, even if it is the "ghost" node.
///
/// This operation should compute in O(1) time.
// `push_front` continues to point to "ghost" when it addes a node to mimic
// the behavior of `insert_before` on an empty list.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn push_front(&mut self, elt: T) {
// Safety: We know that `push_front` does not change the position in
// memory of other nodes. This ensures that `self.current` remains
// valid.
self.list.push_front(elt);
self.index += 1;
}

/// Appends an element to the back of the cursor's parent list. The node
/// that the cursor points to is unchanged, even if it is the "ghost" node.
///
/// This operation should compute in O(1) time.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn push_back(&mut self, elt: T) {
// Safety: We know that `push_back` does not change the position in
// memory of other nodes. This ensures that `self.current` remains
// valid.
self.list.push_back(elt);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to update index if the cursor is pointing to the ghost element. See the code in insert_after.

if self.current().is_none() {
// The index of "ghost" is the length of the list, so we just need
// to increment self.index to reflect the new length of the list.
self.index += 1;
}
}

/// Removes the first element from the cursor's parent list and returns it,
/// or None if the list is empty. The element the cursor points to remains
/// unchanged, unless it was pointing to the front element. In that case, it
/// points to the new front element.
///
/// This operation should compute in O(1) time.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn pop_front(&mut self) -> Option<T> {
// We can't check if current is empty, we must check the list directly.
// It is possible for `self.current == None` and the list to be
// non-empty.
if self.list.is_empty() {
None
} else {
// We can't point to the node that we pop. Copying the behavior of
// `remove_current`, we move on the the next node in the sequence.
// If the list is of length 1 then we end pointing to the "ghost"
// node at index 0, which is expected.
if self.list.head == self.current {
self.move_next();
} else {
self.index -= 1;
}
self.list.pop_front()
}
}

/// Removes the last element from the cursor's parent list and returns it,
/// or None if the list is empty. The element the cursor points to remains
/// unchanged, unless it was pointing to the back element. In that case, it
/// points to the "ghost" element.
///
/// This operation should compute in O(1) time.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn pop_back(&mut self) -> Option<T> {
if self.list.is_empty() {
None
} else {
if self.list.tail == self.current {
// The index now reflects the length of the list. It was the
// length of the list minus 1, but now the list is 1 smaller. No
// change is needed for `index`.
self.current = None;
} else if self.current.is_none() {
self.index = self.list.len - 1;
}
self.list.pop_back()
}
}

/// Provides a reference to the front element of the cursor's parent list,
/// or None if the list is empty.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn front(&self) -> Option<&T> {
self.list.front()
}

/// Provides a mutable reference to the front element of the cursor's
/// parent list, or None if the list is empty.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn front_mut(&mut self) -> Option<&mut T> {
self.list.front_mut()
}

/// Provides a reference to the back element of the cursor's parent list,
/// or None if the list is empty.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn back(&self) -> Option<&T> {
self.list.back()
}

/// Provides a mutable reference to back element of the cursor's parent
/// list, or `None` if the list is empty.
///
/// # Examples
/// Building and mutating a list with a cursor, then getting the back element:
/// ```
/// #![feature(linked_list_cursors)]
/// use std::collections::LinkedList;
/// let mut dl = LinkedList::new();
/// dl.push_front(3);
/// dl.push_front(2);
/// dl.push_front(1);
/// let mut cursor = dl.cursor_front_mut();
/// *cursor.current().unwrap() = 99;
/// *cursor.back_mut().unwrap() = 0;
/// let mut contents = dl.into_iter();
/// assert_eq!(contents.next(), Some(99));
/// assert_eq!(contents.next(), Some(2));
/// assert_eq!(contents.next(), Some(0));
/// assert_eq!(contents.next(), None);
/// ```
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn back_mut(&mut self) -> Option<&mut T> {
self.list.back_mut()
}
}

/// An iterator produced by calling `drain_filter` on LinkedList.
Expand Down
47 changes: 47 additions & 0 deletions library/alloc/src/collections/linked_list/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,50 @@ fn test_cursor_mut_insert() {
check_links(&m);
assert_eq!(m.iter().cloned().collect::<Vec<_>>(), &[200, 201, 202, 203, 1, 100, 101]);
}

#[test]
fn test_cursor_push_front_back() {
let mut ll: LinkedList<u32> = LinkedList::new();
ll.extend(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
let mut c = ll.cursor_front_mut();
assert_eq!(c.current(), Some(&mut 1));
assert_eq!(c.index(), Some(0));
c.push_front(0);
assert_eq!(c.current(), Some(&mut 1));
assert_eq!(c.peek_prev(), Some(&mut 0));
assert_eq!(c.index(), Some(1));
c.push_back(11);
drop(c);
let p = ll.cursor_back().front().unwrap();
assert_eq!(p, &0);
assert_eq!(ll, (0..12).collect());
check_links(&ll);
}

#[test]
fn test_cursor_pop_front_back() {
let mut ll: LinkedList<u32> = LinkedList::new();
ll.extend(&[1, 2, 3, 4, 5, 6]);
let mut c = ll.cursor_back_mut();
assert_eq!(c.pop_front(), Some(1));
c.move_prev();
c.move_prev();
c.move_prev();
assert_eq!(c.pop_back(), Some(6));
let c = c.as_cursor();
assert_eq!(c.front(), Some(&2));
assert_eq!(c.back(), Some(&5));
assert_eq!(c.index(), Some(1));
drop(c);
assert_eq!(ll, (2..6).collect());
check_links(&ll);
let mut c = ll.cursor_back_mut();
assert_eq!(c.current(), Some(&mut 5));
assert_eq!(c.index, 3);
assert_eq!(c.pop_back(), Some(5));
assert_eq!(c.current(), None);
assert_eq!(c.index, 3);
assert_eq!(c.pop_back(), Some(4));
assert_eq!(c.current(), None);
assert_eq!(c.index, 2);
}