Skip to content

Commit 1093c36

Browse files
committed
feat(cordyceps): add Cursor::split_before/after (#233)
This commit adds `split_before` and `split_after` methods to `cordyceps::list::Cursor`, similar to the ones on `std::collections::linked_list::CursorMut`. Signed-off-by: Eliza Weisman <[email protected]>
1 parent 48167ce commit 1093c36

File tree

3 files changed

+132
-6
lines changed

3 files changed

+132
-6
lines changed

cordyceps/src/list.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -874,16 +874,18 @@ impl<T: Linked<Links<T>> + ?Sized> List<T> {
874874
// the head of the new list is the split node's `next` node (which is
875875
// replaced with `None`)
876876
let head = unsafe { T::links(split_node).as_mut().set_next(None) };
877-
if let Some(head) = head {
877+
let tail = if let Some(head) = head {
878878
// since `head` is now the head of its own list, it has no `prev`
879879
// link any more.
880880
let _prev = unsafe { T::links(head).as_mut().set_prev(None) };
881881
debug_assert_eq!(_prev, Some(split_node));
882-
}
883882

884-
// the tail of the new list is this list's old tail, if the split list
885-
// is not empty.
886-
let tail = self.tail.replace(split_node);
883+
// the tail of the new list is this list's old tail, if the split list
884+
// is not empty.
885+
self.tail.replace(split_node)
886+
} else {
887+
None
888+
};
887889

888890
let split = Self {
889891
head,

cordyceps/src/list/cursor.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{Link, Links, List};
22
use crate::{util::FmtOption, Linked};
3-
use core::{fmt, pin::Pin, ptr::NonNull};
3+
use core::{fmt, mem, pin::Pin, ptr::NonNull};
44

55
/// A cursor over a [`List`].
66
///
@@ -267,6 +267,48 @@ impl<'a, T: Linked<Links<T>> + ?Sized> Cursor<'a, T> {
267267
self.list.is_empty()
268268
}
269269

270+
/// Splits the list into two after the current element. This will return a
271+
/// new list consisting of everything after the cursor, with the original
272+
/// list retaining everything before.
273+
///
274+
/// If the cursor is pointing at the null element, then the entire contents
275+
/// of the `List` are moved.
276+
pub fn split_after(&mut self) -> List<T> {
277+
let split_at = if self.index == self.list.len {
278+
self.index = 0;
279+
0
280+
} else {
281+
self.index + 1
282+
};
283+
unsafe {
284+
// safety: we know we are splitting at a node that belongs to our list.
285+
self.list.split_after_node(self.curr, split_at)
286+
}
287+
}
288+
289+
/// Splits the list into two before the current element. This will return a
290+
/// new list consisting of everything before the cursor, with the original
291+
/// list retaining everything after the cursor.
292+
///
293+
/// If the cursor is pointing at the null element, then the entire contents
294+
/// of the `List` are moved.
295+
pub fn split_before(&mut self) -> List<T> {
296+
let split_at = self.index;
297+
self.index = 0;
298+
299+
let split = if split_at == self.list.len() {
300+
// if we're at the end of the list, "before" is the whole thing.
301+
List::new()
302+
} else {
303+
unsafe {
304+
let node = self.curr.and_then(|curr| T::links(curr).as_mut().prev());
305+
// safety: we know we are splitting at a node that belongs to our list.
306+
self.list.split_after_node(node, split_at)
307+
}
308+
};
309+
mem::replace(self.list, split)
310+
}
311+
270312
#[inline(always)]
271313
fn next_link(&self) -> Link<T> {
272314
match self.curr {

cordyceps/src/list/tests/cursor.rs

+82
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,85 @@ fn cursor_mut_insert() {
175175
// &[200, 201, 202, 203, 1, 100, 101]
176176
// );
177177
}
178+
179+
#[test]
180+
fn cursor_mut_split_after() {
181+
let _trace = trace_init();
182+
let entries = [entry(1), entry(2), entry(3), entry(4), entry(5)];
183+
let vals = [1, 2, 3, 4, 5];
184+
185+
// test all splits
186+
for i in 0..vals.len() + 1 {
187+
let _span = tracing::info_span!("split_after", i).entered();
188+
189+
let mut list = list_from_iter(&entries);
190+
let mut cursor = list.cursor_front_mut();
191+
192+
for _ in 0..i {
193+
cursor.move_next();
194+
}
195+
196+
tracing::info!(?cursor);
197+
198+
// split off at this index
199+
let split = cursor.split_after();
200+
tracing::info!(?split, ?list);
201+
202+
assert_valid!(list);
203+
assert_valid!(split);
204+
205+
let split_entries = split.iter().map(|entry| entry.val).collect::<Vec<_>>();
206+
let list_entries = list.iter().map(|entry| entry.val).collect::<Vec<_>>();
207+
tracing::info!(?list_entries, ?split_entries);
208+
209+
if i < vals.len() {
210+
assert_eq!(list_entries, vals[..i + 1], "list after split");
211+
assert_eq!(split_entries, vals[i + 1..], "split");
212+
} else {
213+
// if we were at the end of the list, the split should contain
214+
// everything.
215+
assert_eq!(list_entries, &[], "list after split");
216+
assert_eq!(split_entries, vals[..], "split");
217+
}
218+
}
219+
}
220+
221+
#[test]
222+
fn cursor_mut_split_before() {
223+
let _trace = trace_init();
224+
let entries = [entry(1), entry(2), entry(3), entry(4), entry(5)];
225+
let vals = [1, 2, 3, 4, 5];
226+
227+
// test all splits
228+
for i in 0..vals.len() + 1 {
229+
let _span = tracing::info_span!("split_before", i).entered();
230+
let mut list = list_from_iter(&entries);
231+
let mut cursor = list.cursor_front_mut();
232+
for _ in 0..i {
233+
cursor.move_next();
234+
}
235+
236+
tracing::info!(?cursor);
237+
238+
// split off at this index
239+
let split = cursor.split_before();
240+
tracing::info!(?split, ?list);
241+
242+
assert_valid!(list);
243+
assert_valid!(split);
244+
245+
let split_entries = split.iter().map(|entry| entry.val).collect::<Vec<_>>();
246+
let list_entries = list.iter().map(|entry| entry.val).collect::<Vec<_>>();
247+
tracing::info!(?list_entries, ?split_entries);
248+
249+
if i > 0 {
250+
assert_eq!(list_entries, vals[i..], "list after split");
251+
assert_eq!(split_entries, vals[..i], "split");
252+
} else {
253+
// if we were at the beginning of the list, the split should contain
254+
// everything.
255+
assert_eq!(list_entries, vals[..], "list after split");
256+
assert_eq!(split_entries, &[], "split");
257+
}
258+
}
259+
}

0 commit comments

Comments
 (0)