Skip to content
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

doc(cordyceps): improve List and CursorMut docs #237

Merged
merged 4 commits into from
Jun 21, 2022
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
159 changes: 99 additions & 60 deletions cordyceps/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
/// Attempts to split the list into two at the given index (inclusive).
///
/// Returns everything after the given index (including the node at that
/// index), or `None` if the index is greater than the list's [`length`].
/// index), or `None` if the index is greater than the list's [length].
///
/// This operation should compute in *O*(*n*) time.
///
Expand All @@ -339,6 +339,8 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
/// - [`Some`]`(List<T>)` with a new list containing every element after
/// `at`, if `at` <= `self.len()`
/// - [`None`] if `at > self.len()`
///
/// [length]: Self::len
pub fn try_split_off(&mut self, at: usize) -> Option<Self> {
let len = self.len();
// what is the index of the last node that should be left in this list?
Expand Down Expand Up @@ -382,7 +384,7 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
/// Returns everything after the given index (including the node at that
/// index).
///
/// This operation should compute in *O*(*n*) time.
/// This operation should compute in *O*(1) time and *O*(1) memory.
///
/// # Panics
///
Expand Down Expand Up @@ -501,32 +503,76 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
);
}

/// Appends an item to the head of the list.
pub fn push_front(&mut self, item: T::Handle) {
let ptr = T::into_ptr(item);
// tracing::trace!(?self, ?ptr, "push_front");
assert_ne!(self.head, Some(ptr));
/// Removes an item from the tail of the list.
///
/// This operation should compute in *O*(*n*) time.
///
/// This returns a [`Handle`] that owns the popped element. Dropping the
/// [`Handle`] will drop the element.
///
/// [`Handle`]: crate::Linked::Handle
pub fn pop_back(&mut self) -> Option<T::Handle> {
let tail = self.tail?;
self.len -= 1;

unsafe {
T::links(ptr).as_mut().set_next(self.head);
T::links(ptr).as_mut().set_prev(None);
// tracing::trace!(?links);
if let Some(head) = self.head {
T::links(head).as_mut().set_prev(Some(ptr));
// tracing::trace!(head.links = ?T::links(head).as_ref(), "set head prev ptr",);
let mut tail_links = T::links(tail);
// tracing::trace!(?self, tail.addr = ?tail, tail.links = ?tail_links, "pop_back");
self.tail = tail_links.as_ref().prev();
debug_assert_eq!(
tail_links.as_ref().next(),
None,
"the tail node must not have a next link"
);

if let Some(prev) = tail_links.as_mut().prev() {
T::links(prev).as_mut().set_next(None);
} else {
self.head = None;
}

tail_links.as_mut().unlink();
// tracing::trace!(?self, tail.links = ?tail_links, "pop_back: popped");
Some(T::from_ptr(tail))
}
}

self.head = Some(ptr);
/// Remove an item from the head of the list.
///
/// This operation should compute in *O*(*n*) time.
///
/// This returns a [`Handle`] that owns the popped element. Dropping the
/// [`Handle`] will drop the element.
///
/// [`Handle`]: crate::Linked::Handle
pub fn pop_front(&mut self) -> Option<T::Handle> {
let head = self.head?;
self.len -= 1;

if self.tail.is_none() {
self.tail = Some(ptr);
}
unsafe {
let mut head_links = T::links(head);
self.head = head_links.as_ref().next();
if let Some(next) = head_links.as_mut().next() {
T::links(next).as_mut().set_prev(None);
} else {
self.tail = None;
}

self.len += 1;
// tracing::trace!(?self, "push_front: pushed");
head_links.as_mut().unlink();
Some(T::from_ptr(head))
}
}

/// Appends an item to the tail of the list
/// Appends an item to the tail of the list.
///
/// This operation should compute in *O*(*n*) time.
///
/// This takes a [`Handle`] that owns the appended `item`. While the element
/// is in the list, it is owned by the list, and will be dropped when the
/// list is dropped. If the element is removed or otherwise unlinked from
/// the list, ownership is assigned back to the [`Handle`].
///
/// [`Handle`]: crate::Linked::Handle
pub fn push_back(&mut self, item: T::Handle) {
let ptr = T::into_ptr(item);
assert_ne!(self.tail, Some(ptr));
Expand All @@ -546,23 +592,38 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
self.len += 1;
}

/// Remove an item from the head of the list
pub fn pop_front(&mut self) -> Option<T::Handle> {
let head = self.head?;
self.len -= 1;

/// Appends an item to the head of the list.
///
/// This operation should compute in *O*(*n*) time.
///
/// This takes a [`Handle`] that owns the appended `item`. While the element
/// is in the list, it is owned by the list, and will be dropped when the
/// list is dropped. If the element is removed or otherwise unlinked from
/// the list, ownership is assigned back to the [`Handle`].
///
/// [`Handle`]: crate::Linked::Handle
pub fn push_front(&mut self, item: T::Handle) {
let ptr = T::into_ptr(item);
// tracing::trace!(?self, ?ptr, "push_front");
assert_ne!(self.head, Some(ptr));
unsafe {
let mut head_links = T::links(head);
self.head = head_links.as_ref().next();
if let Some(next) = head_links.as_mut().next() {
T::links(next).as_mut().set_prev(None);
} else {
self.tail = None;
T::links(ptr).as_mut().set_next(self.head);
T::links(ptr).as_mut().set_prev(None);
// tracing::trace!(?links);
if let Some(head) = self.head {
T::links(head).as_mut().set_prev(Some(ptr));
// tracing::trace!(head.links = ?T::links(head).as_ref(), "set head prev ptr",);
}
}

head_links.as_mut().unlink();
Some(T::from_ptr(head))
self.head = Some(ptr);

if self.tail.is_none() {
self.tail = Some(ptr);
}

self.len += 1;
// tracing::trace!(?self, "push_front: pushed");
}

/// Returns a reference to the first element in the list, or `None`
Expand Down Expand Up @@ -649,35 +710,13 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
Some(pin)
}

/// Removes an item from the tail of the list.
pub fn pop_back(&mut self) -> Option<T::Handle> {
let tail = self.tail?;
self.len -= 1;

unsafe {
let mut tail_links = T::links(tail);
// tracing::trace!(?self, tail.addr = ?tail, tail.links = ?tail_links, "pop_back");
self.tail = tail_links.as_ref().prev();
debug_assert_eq!(
tail_links.as_ref().next(),
None,
"the tail node must not have a next link"
);

if let Some(prev) = tail_links.as_mut().prev() {
T::links(prev).as_mut().set_next(None);
} else {
self.head = None;
}

tail_links.as_mut().unlink();
// tracing::trace!(?self, tail.links = ?tail_links, "pop_back: popped");
Some(T::from_ptr(tail))
}
}

/// Remove an arbitrary node from the list.
///
/// This returns a [`Handle`] that owns the popped element. Dropping the
/// [`Handle`] will drop the element.
///
/// [`Handle`]: crate::Linked::Handle
///
/// # Safety
///
/// The caller *must* ensure that the removed node is an element of this
Expand Down
30 changes: 23 additions & 7 deletions cordyceps/src/list/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,40 @@ use super::{Link, Links, List};
use crate::{util::FmtOption, Linked};
use core::{fmt, mem, pin::Pin, ptr::NonNull};

/// A cursor over a [`List`].
/// A cursor over a [`List`] with editing operations.
///
/// This is similar to a mutable iterator (and implements the [`Iterator`]
/// trait), but it also permits modification to the list itself.
/// A `CursorMut` is like a mutable [`Iterator`] (and it [implements the
/// `Iterator` trait](#impl-Iterator)), except that it can freely seek
/// back and forth, and can safely mutate the list during iteration. This is
/// because the lifetime of its yielded references is tied to its own lifetime,
/// instead of that of the underlying underlying list. This means cursors cannot
/// yield multiple elements at once.
///
/// Cursors always rest between two elements in the list, and index in a
/// logically circular way &mdash; once a cursor has advanced past the end of
/// the list, advancing it again will "wrap around" to the first element, and
/// seeking past the first element will wrap the cursor around to the end.
///
/// To accommodate this, there is a null non-element that yields `None` between
/// the head and tail of the list. This indicates that the cursor has reached
/// an end of the list.
///
/// This type implements the same interface as the
/// [`alloc::collections::linked_list::CursorMut`] type, and should behave
/// similarly.
pub struct CursorMut<'list, T: Linked<Links<T>> + ?Sized> {
pub(super) list: &'list mut List<T>,
pub(super) curr: Link<T>,
pub(super) index: usize,
}

/// A cursor over a [`List`].
///
/// This is similar to a mutable iterator (and implements the [`Iterator`]
/// trait), but it also permits modification to the list itself.
/// A cursor over a [`List`], with editing operations.
///
/// # Deprecated
///
/// This is a deprecated alias for [`CursorMut`].
///
/// See the [`CursorMut`] documentation for details.
#[deprecated(since = "0.2.2", note = "renamed to `CursorMut`")]
pub type Cursor<'list, T> = CursorMut<'list, T>;

Expand Down