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

cordyceps: make cursor interface (mostly) match std #227

Merged
merged 13 commits into from
Jun 18, 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
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

172 changes: 97 additions & 75 deletions cordyceps/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ use core::{
#[cfg(not(loom))]
mod tests;

mod cursor;
pub use self::cursor::Cursor;

/// An [intrusive] doubly-linked list.
///
/// This data structure may be used as a first-in, first-out queue by using the
Expand Down Expand Up @@ -222,16 +225,6 @@ pub struct Links<T: ?Sized> {
inner: UnsafeCell<LinksInner<T>>,
}

/// 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.
pub struct Cursor<'list, T: Linked<Links<T>> + ?Sized> {
list: &'list mut List<T>,
curr: Link<T>,
len: usize,
}

/// Iterates over the items in a [`List`] by reference.
pub struct Iter<'list, T: Linked<Links<T>> + ?Sized> {
_list: &'list List<T>,
Expand Down Expand Up @@ -606,12 +599,37 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
/// however, it also permits modifying the *structure* of the list by
/// inserting or removing elements at the cursor's current position.
#[must_use]
#[deprecated(since = "0.2.2", note = "renamed to `List::cursor_front_mut`")]
pub fn cursor(&mut self) -> Cursor<'_, T> {
let len = self.len();
self.cursor_front_mut()
}

/// Returns a [`Cursor`] starting at the first element.
///
/// The [`Cursor`] type can be used as a mutable [`Iterator`]. In addition,
/// however, it also permits modifying the *structure* of the list by
/// inserting or removing elements at the cursor's current position.
#[must_use]
pub fn cursor_front_mut(&mut self) -> Cursor<'_, T> {
Cursor {
curr: self.head,
list: self,
len,
index: 0,
}
}

/// Returns a [`Cursor`] s+tarting at the last element.
///
/// The [`Cursor`] type can be used as a mutable [`Iterator`]. In addition,
/// however, it also permits modifying the *structure* of the list by
/// inserting or removing elements at the cursor's current position.
#[must_use]
pub fn cursor_back_mut(&mut self) -> Cursor<'_, T> {
let index = self.len().saturating_sub(1);
Cursor {
curr: self.tail,
list: self,
index,
}
}

Expand Down Expand Up @@ -658,9 +676,74 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
where
F: FnMut(&T) -> bool,
{
let cursor = self.cursor();
let cursor = self.cursor_front_mut();
DrainFilter { cursor, pred }
}

/// Inserts the list segment represented by `splice_start` and `splice_end`
/// between `next` and `prev`.
///
/// # Safety
///
/// This method requires the following invariants be upheld:
///
/// - `prev` and `next` are part of the same list.
/// - `prev` and `next` are not the same node.
/// - `splice_start` and `splice_end` are part of the same list, which is
/// *not* the same list that `prev` and `next` are part of.
/// -`prev` is `next`'s `prev` node, and `next` is `prev`'s `prev` node.
/// - `splice_start` is ahead of `splice_end` in the list that they came from.
#[inline]
unsafe fn insert_nodes_between(
&mut self,
prev: Link<T>,
next: Link<T>,
splice_start: NonNull<T>,
splice_end: NonNull<T>,
spliced_length: usize,
) {
debug_assert!(
(prev.is_none() && next.is_none()) || prev != next,
"cannot insert between a node and itself!\n \
prev: {prev:?}\n next: {next:?}",
);
// This method takes care not to create multiple mutable references to
// whole nodes at the same time, to maintain validity of aliasing
// pointers into `element`.

if let Some(prev) = prev {
let links = T::links(prev).as_mut();
debug_assert_eq!(links.next(), next);
links.set_next(Some(splice_start));
} else {
self.head = Some(splice_start);
}

if let Some(next) = next {
let links = T::links(next).as_mut();
debug_assert_eq!(links.prev(), prev);
links.set_prev(Some(splice_end));
} else {
self.tail = Some(splice_end);
}

let start_links = T::links(splice_start).as_mut();
let end_links = T::links(splice_end).as_mut();
debug_assert!(
splice_start == splice_end
|| (start_links.next().is_some() && end_links.prev().is_some()),
"splice_start must be ahead of splice_end!\n \
splice_start: {splice_start:?}\n \
splice_end: {splice_end:?}\n \
start_links: {start_links:?}\n \
end_links: {end_links:?}",
);

start_links.set_prev(prev);
end_links.set_next(next);

self.len += spliced_length;
}
}

unsafe impl<T: Linked<Links<T>> + ?Sized> Send for List<T> where T: Send {}
Expand Down Expand Up @@ -836,67 +919,6 @@ unsafe impl<T: Send> Send for Links<T> {}
/// [`List`] upholds its own invariants, `Links` should not make a type `!Sync`.
unsafe impl<T: Sync> Sync for Links<T> {}

// === impl Cursor ====

impl<'a, T: Linked<Links<T>> + ?Sized> Iterator for Cursor<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
self.next_ptr().map(|mut ptr| unsafe {
// safety: it is safe for us to mutate `curr`, because the cursor
// mutably borrows the `List`, ensuring that the list will not be dropped
// while the iterator exists. the returned item will not outlive the
// cursor, and no one else can mutate it, as we have exclusive
// access to the list..
ptr.as_mut()
})
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}

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() };
Some(curr)
}

/// Find and remove the first element matching the provided `predicate`.
///
/// This traverses the list from the cursor's current position and calls
/// `predicate` with each element in the list. If `predicate` returns
/// `true` for a given element, that element is removed from the list and
/// returned, and the traversal ends. If the entire list is traversed
/// without finding a matching element, this returns `None`.
///
/// This method may be called multiple times to remove more than one
/// matching element.
pub fn remove_first(&mut self, mut predicate: impl FnMut(&T) -> bool) -> Option<T::Handle> {
let mut item = None;
while let Some(node) = self.next_ptr() {
if predicate(unsafe { node.as_ref() }) {
item = Some(node);
self.len -= 1;
break;
}
}
unsafe { self.list.remove(item?) }
}
}

impl<T: Linked<Links<T>> + ?Sized> fmt::Debug for Cursor<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Cursor")
.field("curr", &FmtOption::new(&self.curr))
.field("list", &self.list)
.field("len", &self.len)
.finish()
}
}

// === impl Iter ====

impl<'list, T: Linked<Links<T>> + ?Sized> Iterator for Iter<'list, T> {
Expand Down Expand Up @@ -1034,7 +1056,7 @@ where
}

fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.cursor.len))
(0, Some(self.cursor.len()))
}
}

Expand Down
Loading