diff --git a/cordyceps/src/list.rs b/cordyceps/src/list.rs index b13615bb..9c5d4537 100644 --- a/cordyceps/src/list.rs +++ b/cordyceps/src/list.rs @@ -330,7 +330,7 @@ impl> + ?Sized> List { /// 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. /// @@ -339,6 +339,8 @@ impl> + ?Sized> List { /// - [`Some`]`(List)` 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 { let len = self.len(); // what is the index of the last node that should be left in this list? @@ -382,7 +384,7 @@ impl> + ?Sized> List { /// 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 /// @@ -501,32 +503,76 @@ impl> + ?Sized> List { ); } - /// 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 { + 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 { + 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)); @@ -546,23 +592,38 @@ impl> + ?Sized> List { self.len += 1; } - /// Remove an item from the head of the list - pub fn pop_front(&mut self) -> Option { - 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` @@ -649,35 +710,13 @@ impl> + ?Sized> List { Some(pin) } - /// Removes an item from the tail of the list. - pub fn pop_back(&mut self) -> Option { - 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 diff --git a/cordyceps/src/list/cursor.rs b/cordyceps/src/list/cursor.rs index 1ea1c59d..73320753 100644 --- a/cordyceps/src/list/cursor.rs +++ b/cordyceps/src/list/cursor.rs @@ -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 — 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> + ?Sized> { pub(super) list: &'list mut List, pub(super) curr: Link, 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>;