use crate::{ElemPool, Index, IndexError, PieList};
#[derive(Debug)]
#[must_use]
pub struct CursorMut<'a, T> {
pub(crate) list: &'a mut PieList<T>,
pub(crate) current: Index<T>,
pub(crate) logical_index: usize,
}
impl<'a, T> CursorMut<'a, T> {
pub(crate) fn new(list: &'a mut PieList<T>, current: Index<T>, logical_index: usize) -> Self {
Self {
list,
current,
logical_index,
}
}
#[inline]
pub fn index(&self) -> Option<usize> {
if self.current == self.list.sentinel {
None
} else {
Some(self.logical_index)
}
}
#[inline]
pub fn peek<'p>(&self, pool: &'p ElemPool<T>) -> Option<&'p T> {
if self.current == self.list.sentinel {
None
} else {
pool.data(self.current)
}
}
#[inline]
pub fn peek_mut<'p>(&mut self, pool: &'p mut ElemPool<T>) -> Option<&'p mut T> {
if self.current == self.list.sentinel {
None
} else {
pool.data_mut(self.current)
}
}
#[inline]
pub fn move_next(&mut self, pool: &ElemPool<T>) {
let sentinel_slot = self.list.sentinel.slot as usize;
let current_slot = self.current.slot as usize;
if current_slot == sentinel_slot && self.logical_index == self.list.len() {
return;
}
if current_slot == sentinel_slot && self.logical_index == usize::MAX {
let next_slot = pool.next_slot(sentinel_slot).unwrap();
self.current = pool.index_from_slot(next_slot);
self.logical_index = 0;
return;
}
let next_slot = pool.next_slot(current_slot).unwrap();
self.current = pool.index_from_slot(next_slot);
if next_slot == sentinel_slot {
self.logical_index = self.list.len();
} else {
self.logical_index += 1;
}
}
#[inline]
pub fn move_prev(&mut self, pool: &ElemPool<T>) {
let sentinel_slot = self.list.sentinel.slot as usize;
let current_slot = self.current.slot as usize;
if current_slot == sentinel_slot && self.logical_index == usize::MAX {
return;
}
if current_slot == sentinel_slot && self.logical_index == self.list.len() {
if self.list.is_empty() {
self.logical_index = usize::MAX; return;
}
let prev_slot = pool.prev_slot(sentinel_slot).unwrap();
self.current = pool.index_from_slot(prev_slot);
self.logical_index = self.list.len() - 1;
return;
}
let prev_slot = pool.prev_slot(current_slot).unwrap();
self.current = pool.index_from_slot(prev_slot);
if prev_slot == sentinel_slot {
self.logical_index = usize::MAX; } else {
self.logical_index -= 1;
}
}
pub fn move_to_front(&mut self, pool: &ElemPool<T>) {
let sentinel_slot = self.list.sentinel.slot as usize;
let next_slot = pool.next_slot(sentinel_slot).unwrap();
self.current = pool.index_from_slot(next_slot);
self.logical_index = 0;
}
pub fn move_to_back(&mut self, pool: &ElemPool<T>) {
let sentinel_slot = self.list.sentinel.slot as usize;
let prev_slot = pool.prev_slot(sentinel_slot).unwrap();
self.current = pool.index_from_slot(prev_slot);
if self.list.is_empty() {
self.logical_index = 0;
} else {
self.logical_index = self.list.len() - 1;
}
}
pub fn insert_before(&mut self, data: T, pool: &mut ElemPool<T>) -> Result<(), IndexError> {
let new_idx = pool.index_new_with_data(data)?;
if self.current == self.list.sentinel && self.logical_index == usize::MAX {
pool.index_link_after(new_idx, self.list.sentinel)?;
self.current = new_idx;
self.logical_index = 0;
} else {
pool.index_link_before(new_idx, self.current)?;
self.current = new_idx;
}
self.list.len += 1;
Ok(())
}
pub fn insert_after(&mut self, data: T, pool: &mut ElemPool<T>) -> Result<(), IndexError> {
let new_idx = pool.index_new_with_data(data)?;
if self.current == self.list.sentinel && self.logical_index == usize::MAX {
pool.index_link_after(new_idx, self.list.sentinel)?;
} else {
pool.index_link_after(new_idx, self.current)?;
}
self.list.len += 1;
Ok(())
}
pub fn remove_current(&mut self, pool: &mut ElemPool<T>) -> Option<T> {
if self.current == self.list.sentinel {
return None;
}
let old_current = self.current;
self.current = pool.next(old_current);
pool.index_linkout(old_current).expect("cursor element is linked in list");
self.list.len -= 1;
let data = pool.data_swap(old_current, None);
pool.index_del(old_current).expect("valid node for deletion");
if self.current == self.list.sentinel {
self.logical_index = self.list.len();
}
data
}
pub fn split_before(&mut self, pool: &mut ElemPool<T>) -> Result<PieList<T>, IndexError> {
if self.current == self.list.sentinel && self.logical_index == usize::MAX {
return Ok(PieList::new(pool));
}
let split_len = self.logical_index;
let new_list = self.list
.split_off(self.current, split_len, pool)?;
self.logical_index = 0;
Ok(new_list)
}
pub fn splice_before(
&mut self,
other: &mut PieList<T>,
pool: &mut ElemPool<T>,
) -> Result<(), IndexError> {
let other_len = other.len();
if other_len == 0 {
return Ok(());
}
if self.current == self.list.sentinel && self.logical_index == usize::MAX {
self.list.prepend(other, pool)?;
} else {
self.list.splice(self.current, other, pool)?;
self.logical_index += other_len;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::list::PieList;
use crate::pool::{ElemPool, IndexError};
fn list_with_items(pool: &mut ElemPool<i32>, items: &[i32]) -> PieList<i32> {
let mut list = PieList::new(pool);
for &item in items {
list.push_back(item, pool).unwrap();
}
list
}
#[test]
fn test_cursor_nav_edges() {
let mut pool = ElemPool::new();
let mut list = list_with_items(&mut pool, &[10, 20]);
let mut cursor = list.cursor_mut(&mut pool);
cursor.move_prev(&pool);
assert_eq!(cursor.index(), None);
cursor.move_next(&pool);
assert_eq!(cursor.index(), Some(0));
cursor.move_next(&pool);
cursor.move_next(&pool);
assert_eq!(cursor.index(), None);
assert_eq!(cursor.peek(&pool), None);
cursor.move_prev(&pool);
assert_eq!(cursor.index(), Some(1));
assert_eq!(cursor.peek(&pool), Some(&20));
list.clear(&mut pool);
}
#[test]
fn test_insert_at_edges() {
let mut pool = ElemPool::new();
let mut list = PieList::new(&mut pool);
let mut cursor = list.cursor_mut(&mut pool);
cursor.move_to_back(&pool); cursor.insert_before(100, &mut pool).unwrap(); assert_eq!(cursor.index(), Some(0));
assert_eq!(cursor.peek(&pool), Some(&100));
cursor.move_prev(&pool); cursor.insert_before(50, &mut pool).unwrap(); assert_eq!(cursor.index(), Some(0)); assert_eq!(cursor.peek(&pool), Some(&50));
let vec: Vec<_> = list.iter(&pool).copied().collect();
assert_eq!(vec, vec![50, 100]);
list.clear(&mut pool);
}
#[test]
fn test_split_and_splice() {
let mut pool = ElemPool::new();
let mut list1 = list_with_items(&mut pool, &[1, 2, 3, 4]);
let mut list2;
{
let mut cursor = list1.cursor_mut_at(2, &mut pool).unwrap(); list2 = cursor.split_before(&mut pool).unwrap();
assert_eq!(cursor.index(), Some(0));
assert_eq!(cursor.peek(&pool), Some(&3));
}
{
let mut cursor = list2.cursor_mut_at(1, &mut pool).unwrap(); cursor.splice_before(&mut list1, &mut pool).unwrap();
}
let vec: Vec<_> = list2.iter(&pool).copied().collect();
assert_eq!(vec, vec![1, 3, 4, 2]);
assert!(list1.is_empty());
list2.clear(&mut pool);
list1.clear(&mut pool);
}
#[test]
fn test_error_conditions() {
let mut pool = ElemPool::new();
let mut list = list_with_items(&mut pool, &[10]);
assert!(matches!(list.cursor_mut_at(1, &mut pool), Err(IndexError::IndexOutOfBounds)));
list.clear(&mut pool);
}
#[test]
fn test_split_splice_large() {
let mut pool = ElemPool::new();
let n = 100;
let mut list = list_with_items(&mut pool, &(0..n).collect::<Vec<_>>());
assert_eq!(pool.list_count(), 1);
assert_eq!(pool.len(), n as usize);
let mut back_half;
{
let mut cursor = list.cursor_mut_at(50, &mut pool).unwrap();
back_half = cursor.split_before(&mut pool).unwrap();
}
assert_eq!(list.len(), 50);
assert_eq!(back_half.len(), 50);
assert_eq!(pool.list_count(), 2);
assert_eq!(pool.len(), n as usize);
let list_vec: Vec<_> = list.iter(&pool).copied().collect();
let back_vec: Vec<_> = back_half.iter(&pool).copied().collect();
assert_eq!(list_vec, (50..100).collect::<Vec<_>>());
assert_eq!(back_vec, (0..50).collect::<Vec<_>>());
{
let mut cursor = list.cursor_mut_at(25, &mut pool).unwrap();
cursor.splice_before(&mut back_half, &mut pool).unwrap();
}
assert!(back_half.is_empty());
assert_eq!(list.len(), n as usize);
assert_eq!(pool.list_count(), 2); assert_eq!(pool.len(), n as usize);
let result: Vec<_> = list.iter(&pool).copied().collect();
let mut expected: Vec<i32> = (50..75).collect();
expected.extend(0..50);
expected.extend(75..100);
assert_eq!(result, expected);
list.clear(&mut pool);
back_half.clear(&mut pool);
}
#[test]
fn test_split_splice_repeated_no_leak() {
let mut pool = ElemPool::new();
let mut list = list_with_items(&mut pool, &(0..40).collect::<Vec<_>>());
for _ in 0..20 {
let mut tail;
{
let mut cursor = list.cursor_mut_at(20, &mut pool).unwrap();
tail = cursor.split_before(&mut pool).unwrap();
}
assert_eq!(list.len(), 20);
assert_eq!(tail.len(), 20);
{
let mut cursor = list.cursor_mut(&mut pool);
cursor.move_to_front(&pool);
cursor.splice_before(&mut tail, &mut pool).unwrap();
}
assert_eq!(list.len(), 40);
assert!(tail.is_empty());
tail.clear(&mut pool);
}
assert_eq!(pool.len(), 40);
list.clear(&mut pool);
}
#[test]
fn test_insert_after() {
let mut pool = ElemPool::new();
let mut list = list_with_items(&mut pool, &[10, 30]);
{
let mut cursor = list.cursor_mut(&mut pool);
cursor.insert_after(20, &mut pool).unwrap();
assert_eq!(cursor.index(), Some(0));
assert_eq!(*cursor.peek(&pool).unwrap(), 10);
}
let items: Vec<_> = list.iter(&pool).copied().collect();
assert_eq!(items, vec![10, 20, 30]);
{
let mut cursor = list.cursor_mut(&mut pool);
cursor.move_to_back(&pool);
cursor.insert_after(40, &mut pool).unwrap();
}
let items: Vec<_> = list.iter(&pool).copied().collect();
assert_eq!(items, vec![10, 20, 30, 40]);
{
let mut cursor = list.cursor_mut(&mut pool);
cursor.move_prev(&pool);
assert_eq!(cursor.index(), None);
cursor.insert_after(5, &mut pool).unwrap();
}
let items: Vec<_> = list.iter(&pool).copied().collect();
assert_eq!(items, vec![5, 10, 20, 30, 40]);
assert_eq!(list.len(), 5);
list.clear(&mut pool);
}
#[test]
fn test_remove_current_edges() {
let mut pool = ElemPool::new();
let mut list = list_with_items(&mut pool, &[10, 20, 30]);
{
let mut cursor = list.cursor_mut(&mut pool);
cursor.move_prev(&pool); assert_eq!(cursor.remove_current(&mut pool), None);
}
assert_eq!(list.len(), 3);
{
let mut cursor = list.cursor_mut_at(2, &mut pool).unwrap();
assert_eq!(*cursor.peek(&pool).unwrap(), 30);
let removed = cursor.remove_current(&mut pool);
assert_eq!(removed, Some(30));
assert_eq!(cursor.index(), None); }
let items: Vec<_> = list.iter(&pool).copied().collect();
assert_eq!(items, vec![10, 20]);
{
let mut cursor = list.cursor_mut(&mut pool);
let removed = cursor.remove_current(&mut pool);
assert_eq!(removed, Some(10));
assert_eq!(cursor.index(), Some(0));
assert_eq!(*cursor.peek(&pool).unwrap(), 20);
}
assert_eq!(list.len(), 1);
list.clear(&mut pool);
}
}