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

feat(cordyceps): add Cursor::splice_before/after, fix split before #234

Merged
merged 3 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
29 changes: 24 additions & 5 deletions cordyceps/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,18 +310,18 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
};

// if `other` is empty, do nothing.
if let Some(head) = other.head.take() {
if let Some((other_head, other_tail, other_len)) = other.take_all() {
// attach the other list's head node to this list's tail node.
unsafe {
T::links(tail).as_mut().set_next(Some(head));
T::links(head).as_mut().set_prev(Some(tail));
T::links(tail).as_mut().set_next(Some(other_head));
T::links(other_head).as_mut().set_prev(Some(tail));
}

// this list's tail node is now the other list's tail node.
self.tail = other.tail.take();
self.tail = Some(other_tail);
// this list's length increases by the other list's length, which
// becomes 0.
self.len += mem::replace(&mut other.len, 0);
self.len += other_len;
}
}

Expand Down Expand Up @@ -900,6 +900,25 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {

split
}

/// Empties this list, returning its head, tail, and length if it is
/// non-empty. If the list is empty, this returns `None`.
#[inline]
fn take_all(&mut self) -> Option<(NonNull<T>, NonNull<T>, usize)> {
let head = self.head.take()?;
let tail = self.tail.take();
debug_assert!(
tail.is_some(),
"if a list's `head` is `Some`, its tail must also be `Some`"
);
let tail = tail?;
let len = mem::replace(&mut self.len, 0);
debug_assert_ne!(
len, 0,
"if a list is non-empty, its `len` must be greater than 0"
);
Some((head, tail, len))
}
}

impl<T> iter::Extend<T::Handle> for List<T>
Expand Down
88 changes: 79 additions & 9 deletions cordyceps/src/list/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,17 +296,87 @@ impl<'a, T: Linked<Links<T>> + ?Sized> Cursor<'a, T> {
let split_at = self.index;
self.index = 0;

let split = if split_at == self.list.len() {
// if we're at the end of the list, "before" is the whole thing.
List::new()
let split_node = match self.curr {
Some(node) => node,
// the split portion is the entire list. just return it.
None => return mem::replace(self.list, List::new()),
};

// the tail of the new list is the split node's `prev` node (which is
// replaced with `None`), as the split node is the new head of this list.
let tail = unsafe { T::links(split_node).as_mut().set_prev(None) };
let head = if let Some(tail) = tail {
// since `tail` is now the head of its own list, it has no `next`
// link any more.
let _next = unsafe { T::links(tail).as_mut().set_next(None) };
debug_assert_eq!(_next, Some(split_node));

// this list's head is now the split node.
self.list.head.replace(split_node)
} else {
unsafe {
let node = self.curr.and_then(|curr| T::links(curr).as_mut().prev());
// safety: we know we are splitting at a node that belongs to our list.
self.list.split_after_node(node, split_at)
}
None
};

let split = List {
head,
tail,
len: split_at,
};
mem::replace(self.list, split)

// update this list's length (note that this occurs after constructing
// the new list, because we use this list's length to determine the new
// list's length).
self.list.len -= split_at;

split
}

/// Inserts all elements from `spliced` after the cursor's current position.
///
/// If the cursor is pointing at the null element, then the contents of
/// `spliced` are inserted at the beginning of the `List` the cursor points to.
pub fn splice_after(&mut self, mut spliced: List<T>) {
let (splice_head, splice_tail, splice_len) = match spliced.take_all() {
Some(spliced) => spliced,
// the spliced list is empty, do nothing.
None => return,
};

let next = self.next_link();
unsafe {
// safety: we know `curr` and `next` came from the same list that we
// are calling `insert_nodes_between` from, because they came from
// this cursor, which points at `self.list`.
self.list
.insert_nodes_between(self.curr, next, splice_head, splice_tail, splice_len);
}

if self.curr.is_none() {
self.index = self.list.len();
}
}

/// Inserts all elements from `spliced` before the cursor's current position.
///
/// If the cursor is pointing at the null element, then the contents of
/// `spliced` are inserted at the end of the `List` the cursor points to.
pub fn splice_before(&mut self, mut spliced: List<T>) {
let (splice_head, splice_tail, splice_len) = match spliced.take_all() {
Some(spliced) => spliced,
// the spliced list is empty, do nothing.
None => return,
};

let prev = self.prev_link();
unsafe {
// safety: we know `curr` and `prev` came from the same list that we
// are calling `insert_nodes_between` from, because they came from
// this cursor, which points at `self.list`.
self.list
.insert_nodes_between(prev, self.curr, splice_head, splice_tail, splice_len);
}

self.index += splice_len;
}

#[inline(always)]
Expand Down
53 changes: 27 additions & 26 deletions cordyceps/src/list/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@ mod append {
let mut b = List::new();
a.append(&mut b);

a.assert_valid();
b.assert_valid();
assert_valid!(a);
assert_valid!(b);

assert_eq!(a.len(), 0);
assert_eq!(b.len(), 0);
Expand All @@ -339,16 +339,14 @@ mod append {

a.append(&mut b);

a.assert_valid();
b.assert_valid();
assert_valid!(a);
assert_valid!(b);

assert_eq!(a.len(), 1);
assert_eq!(b.len(), 0);

assert_eq!(val(a.pop_back()), Some(1));
assert_eq!(a.len(), 0);
assert_eq!(b.len(), 0);
a.assert_valid();
assert_eq!(val(a.front()), Some(1));
assert_eq!(val(a.back()), Some(1));
}

#[test]
Expand All @@ -361,16 +359,14 @@ mod append {

a.append(&mut b);

a.assert_valid();
b.assert_valid();
assert_valid!(a);
assert_valid!(b);

assert_eq!(a.len(), 1);
assert_eq!(b.len(), 0);

assert_eq!(val(a.pop_back()), Some(1));
assert_eq!(a.len(), 0);
assert_eq!(b.len(), 0);
a.assert_valid();
assert_eq!(val(a.front()), Some(1));
assert_eq!(val(a.back()), Some(1));
}

#[test]
Expand All @@ -392,8 +388,9 @@ mod append {

a.append(&mut b);

a.assert_valid();
b.assert_valid();
assert_valid!(a);
assert_valid!(b);

assert_eq!(a.len(), a_entries.len() + b_entries.len());
assert_eq!(b.len(), 0);

Expand All @@ -406,12 +403,12 @@ mod append {
}

// make sure `b` wasn't broken by being messed with...

b.push_back(three.as_ref());
b.assert_valid();
assert_valid!(b);
assert_eq!(b.len(), 1);
assert_eq!(val(b.pop_front()), Some(3));
b.assert_valid();
assert_eq!(val(b.front()), Some(3));
assert_eq!(val(b.back()), Some(3));
assert_valid!(b);
}
}

Expand All @@ -425,14 +422,16 @@ mod split_off {
let mut list = List::<Entry<'_>>::new();
list.push_back(entry.as_ref());

let mut split = list.split_off(0);
let split = list.split_off(0);
assert_eq!(list.len(), 0);
assert_eq!(split.len(), 1);
assert_valid!(list);
assert_valid!(split);

assert_eq!(val(split.pop_front()), Some(1));
assert_eq!(split.len(), 0);
assert_eq!(val(split.front()), Some(1));
assert_eq!(val(split.back()), Some(1));
assert_eq!(val(list.front()), None);
assert_eq!(val(list.back()), None);
}

#[test]
Expand Down Expand Up @@ -482,15 +481,17 @@ mod split_off {
let mut list = List::<Entry<'_>>::new();
list.push_back(one.as_ref());

let mut split = list.split_off(1);
let split = list.split_off(1);
assert_eq!(list.len(), 1);
assert_eq!(split.len(), 0);

assert_valid!(list);
assert_valid!(split);

assert_eq!(val(list.pop_front()), Some(1));
assert_eq!(val(split.pop_front()), None);
assert_eq!(val(list.front()), Some(1));
assert_eq!(val(list.back()), Some(1));
assert_eq!(val(split.front()), None);
assert_eq!(val(split.back()), None);
}

#[test]
Expand Down
72 changes: 38 additions & 34 deletions cordyceps/src/list/tests/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,41 +139,45 @@ fn cursor_mut_insert() {
assert_eq!(val(cursor.remove_current()), Some(10));
list.assert_valid();
assert_eq!(collect_vals(&list), &[1, 8, 2, 3, 4, 5, 6]);
}

// let mut cursor = m.cursor_front_mut();
// let mut p: LinkedList<u32> = LinkedList::new();
// p.extend(&[100, 101, 102, 103]);
// let mut q: LinkedList<u32> = LinkedList::new();
// q.extend(&[200, 201, 202, 203]);
// cursor.splice_after(p);
// cursor.splice_before(q);
// check_links(&m);
// assert_eq!(
// m.iter().cloned().collect::<Vec<_>>(),
// &[200, 201, 202, 203, 1, 100, 101, 102, 103, 8, 2, 3, 4, 5, 6]
// );
// let mut cursor = m.cursor_front_mut();
// cursor.move_prev();
// let tmp = cursor.split_before();
// assert_eq!(m.into_iter().collect::<Vec<_>>(), &[]);
// m = tmp;
// let mut cursor = m.cursor_front_mut();
// cursor.move_next();
// cursor.move_next();
// cursor.move_next();
// cursor.move_next();
// cursor.move_next();
// cursor.move_next();
// let tmp = cursor.split_after();
// assert_eq!(
// tmp.into_iter().collect::<Vec<_>>(),
// &[102, 103, 8, 2, 3, 4, 5, 6]
// );
// check_links(&m);
// assert_eq!(
// m.iter().cloned().collect::<Vec<_>>(),
// &[200, 201, 202, 203, 1, 100, 101]
// );
#[test]
fn cursor_mut_splice() {
let entries_a = [entry(1), entry(2), entry(3), entry(4), entry(5), entry(6)];
let entries_b = [entry(100), entry(101), entry(102), entry(103)];
let entries_c = [entry(200), entry(201), entry(202), entry(203)];

let mut a = list_from_iter(&entries_a);
let b = list_from_iter(&entries_b);
let c = list_from_iter(&entries_c);

let mut cursor = a.cursor_front_mut();
cursor.splice_after(b);
cursor.splice_before(c);

assert_valid!(a);
assert_eq!(
collect_vals(&a),
&[200, 201, 202, 203, 1, 100, 101, 102, 103, 2, 3, 4, 5, 6]
);
let mut cursor = a.cursor_front_mut();
cursor.move_prev();
let tmp = cursor.split_before();
assert_eq!(collect_vals(&a), &[]);
a = tmp;
let mut cursor = a.cursor_front_mut();
cursor.move_next();
cursor.move_next();
cursor.move_next();
cursor.move_next();
cursor.move_next();
cursor.move_next();

let tmp = cursor.split_after();
assert_eq!(collect_vals(&tmp), &[102, 103, 2, 3, 4, 5, 6]);
assert_eq!(collect_vals(&a), &[200, 201, 202, 203, 1, 100, 101]);
assert_valid!(tmp);
assert_valid!(a);
}

#[test]
Expand Down