Skip to content

Commit

Permalink
doc(cordyceps): improve List and CursorMut docs (#237)
Browse files Browse the repository at this point in the history
* a3d6088 doc(cordyceps): reorganize `List` methods
* 2c0cbbb doc(cordyceps): improve push/pop documentation
* 26010fc doc(cordyceps): fix broken link
* e97bcfc doc(cordyceps): improve `CursorMut` docs
  • Loading branch information
hawkw authored Jun 21, 2022
1 parent 3035be4 commit 7504b88
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 67 deletions.
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

0 comments on commit 7504b88

Please sign in to comment.