started work on cursors

This commit is contained in:
nora 2021-08-13 17:02:22 +02:00
parent c0a009374d
commit 5c4deeadc0
2 changed files with 412 additions and 137 deletions

View file

@ -7,6 +7,7 @@ use std::iter::FromIterator;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::option::Option::Some;
use std::ptr::NonNull; use std::ptr::NonNull;
fn allocate_nonnull<T>(element: T) -> NonNull<T> { fn allocate_nonnull<T>(element: T) -> NonNull<T> {
@ -160,16 +161,56 @@ impl<T, const COUNT: usize> PackedLinkedList<T, COUNT> {
} }
} }
pub fn iter(&self) -> Iter<T, COUNT> { pub fn cursor_front(&self) -> Cursor<T, COUNT> {
Iter::new(self) Cursor {
node: self.first,
index: 0,
list: self,
}
} }
pub fn iter_mut(&mut self) -> IterMut<T, COUNT> { pub fn cursor_back(&self) -> Cursor<T, COUNT> {
IterMut::new(self) Cursor {
node: self.last,
// point to the last element in the last node, or 0 if no node is found
index: self
.last
.map(|last| unsafe { last.as_ref().size - 1 })
.unwrap_or(0),
list: self,
}
} }
pub fn into_iter(self) -> IntoIter<T, COUNT> { pub fn cursor_mut_front(&mut self) -> CursorMut<T, COUNT> {
IntoIter::new(self) CursorMut {
node: self.first,
index: 0,
list: self,
}
}
pub fn cursor_mut_back(&mut self) -> CursorMut<T, COUNT> {
CursorMut {
node: self.last,
// point to the last element in the last node, or 0 if no node is found
index: self
.last
.map(|last| unsafe { last.as_ref().size - 1 })
.unwrap_or(0),
list: self,
}
}
pub fn iter(&self) -> iter::Iter<T, COUNT> {
iter::Iter::new(self)
}
pub fn iter_mut(&mut self) -> iter::IterMut<T, COUNT> {
iter::IterMut::new(self)
}
pub fn into_iter(self) -> iter::IntoIter<T, COUNT> {
iter::IntoIter::new(self)
} }
fn insert_node_start(&mut self) { fn insert_node_start(&mut self) {
@ -253,10 +294,7 @@ where
T: PartialEq, T: PartialEq,
{ {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() { self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a == b)
return false;
}
self.iter().zip(other.iter()).all(|(a, b)| a == b)
} }
} }
@ -316,158 +354,353 @@ impl<T, const COUNT: usize> Node<T, COUNT> {
self.values[0] = MaybeUninit::new(element); self.values[0] = MaybeUninit::new(element);
self.size += 1; self.size += 1;
} }
}
#[derive(Debug)] /// Inserts a new value at the index, copying the values up
pub struct Iter<'a, T, const COUNT: usize> { /// # Safety
node: Option<&'a Node<T, COUNT>>, /// The node must not be full and the index must not be out of bounds
index: usize, unsafe fn insert(&mut self, element: T, index: usize) {
} debug_assert!(self.size < COUNT);
// copy all values up
impl<'a, T, const COUNT: usize> Iter<'a, T, COUNT> { if COUNT > 1 {
fn new(list: &'a PackedLinkedList<T, COUNT>) -> Self { std::ptr::copy(
Self { &self.values[index] as *const _,
node: list.first.as_ref().map(|nn| unsafe { nn.as_ref() }), &mut self.values[index + 1] as *mut _,
index: 0, self.size - index,
);
} }
self.values[index] = MaybeUninit::new(element);
} }
} }
impl<'a, T, const COUNT: usize> Iterator for Iter<'a, T, COUNT> { macro_rules! implement_cursor {
type Item = &'a T; ($cursor:ident) => {
impl<'a, T, const COUNT: usize> $cursor<'a, T, COUNT> {
pub fn get(&self) -> Option<&T> {
self.node
.map(|nn| unsafe { nn.as_ref().values[self.index].as_ptr().as_ref().unwrap() })
}
fn next(&mut self) -> Option<Self::Item> { pub fn move_next(&mut self) {
let node = self.node?; match self.node {
// SAFETY: assume that all pointers point to the correct nodes, None => {
// and that the sizes of the nodes are set correctly // currently on the ghost node, move to the first node
unsafe { self.node = self.list.first;
if node.size > self.index { self.index = 0;
// take more }
let item = node.values[self.index].as_ptr().as_ref().unwrap(); Some(node) => unsafe {
self.index += 1; let node = node.as_ref();
Some(item) if self.index == node.size - 1 {
} else { // the last item, go to the next node
// next node self.node = node.next;
let next_node = node.next.as_ref()?.as_ref(); self.index = 0;
self.index = 1; } else {
self.node = Some(next_node); // stay on the same node
// a node should never be empty self.index += 1;
debug_assert_ne!(next_node.size, 0); }
Some(next_node.values[0].as_ptr().as_ref().unwrap()) },
}
}
pub fn move_prev(&mut self) {
match self.node {
None => {
// currently on the ghost node, move to the first node
self.node = self.list.last;
self.index = self
.list
.last
.map(|nn| unsafe { nn.as_ref().size - 1 })
.unwrap_or(0);
}
Some(node) => unsafe {
let node = node.as_ref();
if self.index == 0 {
// the first item, go to the previous node
self.node = node.prev;
self.index = node.prev.map(|nn| nn.as_ref().size - 1).unwrap_or(0);
} else {
// stay on the same node
self.index -= 1;
}
},
}
} }
} }
} };
} }
#[derive(Debug)] /// A cursor for navigating the Packed Linked List
pub struct IterMut<'a, T, const COUNT: usize> { pub struct Cursor<'a, T, const COUNT: usize> {
node: Option<NonNull<Node<T, COUNT>>>, node: Option<NonNull<Node<T, COUNT>>>,
index: usize, index: usize,
_marker: PhantomData<&'a T>, list: &'a PackedLinkedList<T, COUNT>,
} }
impl<'a, T, const COUNT: usize> IterMut<'a, T, COUNT> { // A cursor for navigating and editing the Packed Linked List
fn new(list: &'a mut PackedLinkedList<T, COUNT>) -> Self { pub struct CursorMut<'a, T, const COUNT: usize> {
Self { node: Option<NonNull<Node<T, COUNT>>>,
node: list.first,
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T: 'a, const COUNT: usize> Iterator for IterMut<'a, T, COUNT> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
// SAFETY: assume that all pointers point to the correct nodes,
// and that the sizes of the nodes are set correctly
unsafe {
let mut node = self.node?;
let node = node.as_mut();
if node.size > self.index {
// take more
let ptr = node.values[self.index].as_ptr() as *mut T;
let item = ptr.as_mut().unwrap();
self.index += 1;
Some(item)
} else {
// next node
let mut next_node = node.next?;
debug_assert_ne!(next_node.as_ref().size, 0);
self.index = 1;
self.node = Some(next_node);
// a node should never be empty
let ptr = next_node.as_mut().values[0].as_ptr() as *mut T;
Some(ptr.as_mut().unwrap())
}
}
}
}
#[derive(Debug)]
pub struct IntoIter<T, const COUNT: usize> {
node: Option<Box<Node<T, COUNT>>>,
index: usize, index: usize,
list: &'a mut PackedLinkedList<T, COUNT>,
} }
impl<T, const COUNT: usize> Drop for IntoIter<T, COUNT> { implement_cursor!(Cursor);
fn drop(&mut self) { implement_cursor!(CursorMut);
while let Some(_) = self.next() {}
impl<'a, T, const COUNT: usize> CursorMut<'a, T, COUNT> {
pub fn get_mut(&mut self) -> Option<&mut T> {
let index = self.index;
self.node
.as_mut()
.map(|nn| unsafe { nn.as_mut().values[index].as_mut_ptr().as_mut().unwrap() })
}
pub fn replace(&mut self, _element: T) -> Option<T> {
todo!()
}
pub fn remove(&mut self) -> Option<T> {
todo!()
}
/// Inserts a new element after the element this cursor is pointing to.
/// If the cursor is pointing at the ghost node, the item gets inserted at the start of the list
/// The cursor position will not change.
pub fn insert_after(&mut self, element: T) {
match self.node {
None => self.list.push_front(element),
Some(mut current_node) => {
let current = unsafe { current_node.as_mut() };
// if we point at the last element, we do not need to copy anything
let append = self.index == current.size - 1;
// There are several cases here
// 1. we append an item to the node, and it is not full
// 2. we append an item to the node, and it is full
// 3. we insert an item into the node, and it is not full
// 4. we insert an item into the node, and it is full
match (append, current.is_full()) {
(true, false) => {
// SAFETY: the node is not full
unsafe { current.push_back(element) };
}
(true, true) => {
// check whether the next node is full. if it is not full, insert it at the start
// if it is full or the next node doesn't exist, allocate a new node inbetween
let next_node = unsafe { current.next.as_mut().map(|nn| nn.as_mut()) };
let need_allocate = next_node
.as_ref()
.map(|node| node.is_full())
.unwrap_or(true);
if need_allocate {
unsafe {
let mut new_node = self.allocate_new_node_after();
new_node.as_mut().push_back(element);
}
} else {
let next_node = next_node
.unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() });
// SAFETY: the node is not full, because `need_allocate` is false
unsafe { next_node.push_back(element) };
}
}
// SAFETY: the node is not full and the index is not out of bounds
(false, false) => unsafe { current.insert(element, self.index + 1) },
(false, true) => {
// check whether the next node is full. if it is not full, insert it at the start
// if it is full or the next node doesn't exist, allocate a new node inbetween
}
}
}
}
}
pub fn insert_before(&mut self, _element: T) {}
/// allocates a new node after the cursor
/// if self.node is None, it allocates the node at the start of the list
/// # Safety
/// The node must immediately be filled with at least on element, since an empty node is not a valid state
unsafe fn allocate_new_node_after(&mut self) -> NonNull<Node<T, COUNT>> {
let mut new_node = allocate_nonnull(Node::new(
self.node, None, // will be replaced in the match below
));
match self.node {
None => {
match self.list.first {
None => self.list.last = Some(new_node),
Some(mut first) => first.as_mut().prev = Some(new_node),
}
new_node.as_mut().next = self.list.first;
self.list.first = Some(new_node);
}
Some(mut node) => {
new_node.as_mut().next = node.as_ref().next;
node.as_mut().next = Some(new_node);
}
}
new_node
} }
} }
impl<T, const COUNT: usize> IntoIter<T, COUNT> { mod iter {
fn new(list: PackedLinkedList<T, COUNT>) -> Self { use super::{Node, PackedLinkedList};
let iter = Self { use std::marker::PhantomData;
node: list.first.map(|nn| unsafe { Box::from_raw(nn.as_ptr()) }), use std::mem;
index: 0, use std::mem::MaybeUninit;
}; use std::ptr::NonNull;
// do not drop the list
mem::forget(list); #[derive(Debug)]
iter pub struct Iter<'a, T, const COUNT: usize> {
node: Option<&'a Node<T, COUNT>>,
index: usize,
} }
}
impl<T, const COUNT: usize> Iterator for IntoIter<T, COUNT> { impl<'a, T, const COUNT: usize> Iter<'a, T, COUNT> {
type Item = T; pub(super) fn new(list: &'a PackedLinkedList<T, COUNT>) -> Self {
Self {
node: list.first.as_ref().map(|nn| unsafe { nn.as_ref() }),
index: 0,
}
}
}
fn next(&mut self) -> Option<Self::Item> { impl<'a, T, const COUNT: usize> Iterator for Iter<'a, T, COUNT> {
// take the node. the node has to either be returned or replaced by a new one. the None left type Item = &'a T;
// behind here is *not* a valid state
let mut node = self.node.take()?;
// SAFETY: see more detailed comments fn next(&mut self) -> Option<Self::Item> {
unsafe { let node = self.node?;
if node.size > self.index { // SAFETY: assume that all pointers point to the correct nodes,
// take more items from the node // and that the sizes of the nodes are set correctly
// take out the item and replace it with uninitialized memory unsafe {
// the index pointer is increased, so no one will access this again if node.size > self.index {
let item = // take more
mem::replace(&mut node.values[self.index], MaybeUninit::uninit()).assume_init(); let item = node.values[self.index].as_ptr().as_ref().unwrap();
self.index += 1; self.index += 1;
// re-insert the node Some(item)
self.node = Some(node); } else {
Some(item) // next node
} else { let next_node = node.next.as_ref()?.as_ref();
// go to the next node self.index = 1;
// if next is empty, return None and stop the iteration self.node = Some(next_node);
// take ownership over the node. the last node will be dropped here // a node should never be empty
let mut next_node = Box::from_raw(node.next?.as_ptr()); debug_assert_ne!(next_node.size, 0);
next_node.prev = None; Some(next_node.values[0].as_ptr().as_ref().unwrap())
self.index = 1; }
// a node should never be empty }
debug_assert_ne!(next_node.size, 0); }
self.node = Some(next_node); }
// see comment above
Some( #[derive(Debug)]
mem::replace( pub struct IterMut<'a, T, const COUNT: usize> {
&mut self.node.as_mut().unwrap().values[0], node: Option<NonNull<Node<T, COUNT>>>,
MaybeUninit::uninit(), index: usize,
_marker: PhantomData<&'a T>,
}
impl<'a, T, const COUNT: usize> IterMut<'a, T, COUNT> {
pub(super) fn new(list: &'a mut PackedLinkedList<T, COUNT>) -> Self {
Self {
node: list.first,
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T: 'a, const COUNT: usize> Iterator for IterMut<'a, T, COUNT> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
// SAFETY: assume that all pointers point to the correct nodes,
// and that the sizes of the nodes are set correctly
unsafe {
let mut node = self.node?;
let node = node.as_mut();
if node.size > self.index {
// take more
let ptr = node.values[self.index].as_ptr() as *mut T;
let item = ptr.as_mut().unwrap();
self.index += 1;
Some(item)
} else {
// next node
let mut next_node = node.next?;
debug_assert_ne!(next_node.as_ref().size, 0);
self.index = 1;
self.node = Some(next_node);
// a node should never be empty
let ptr = next_node.as_mut().values[0].as_ptr() as *mut T;
Some(ptr.as_mut().unwrap())
}
}
}
}
#[derive(Debug)]
pub struct IntoIter<T, const COUNT: usize> {
node: Option<Box<Node<T, COUNT>>>,
index: usize,
}
impl<T, const COUNT: usize> Drop for IntoIter<T, COUNT> {
fn drop(&mut self) {
while let Some(_) = self.next() {}
}
}
impl<T, const COUNT: usize> IntoIter<T, COUNT> {
pub(super) fn new(list: PackedLinkedList<T, COUNT>) -> Self {
let iter = Self {
node: list.first.map(|nn| unsafe { Box::from_raw(nn.as_ptr()) }),
index: 0,
};
// do not drop the list
mem::forget(list);
iter
}
}
impl<T, const COUNT: usize> Iterator for IntoIter<T, COUNT> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
// take the node. the node has to either be returned or replaced by a new one. the None left
// behind here is *not* a valid state
let mut node = self.node.take()?;
// SAFETY: see more detailed comments
unsafe {
if node.size > self.index {
// take more items from the node
// take out the item and replace it with uninitialized memory
// the index pointer is increased, so no one will access this again
let item = mem::replace(&mut node.values[self.index], MaybeUninit::uninit())
.assume_init();
self.index += 1;
// re-insert the node
self.node = Some(node);
Some(item)
} else {
// go to the next node
// if next is empty, return None and stop the iteration
// take ownership over the node. the last node will be dropped here
let mut next_node = Box::from_raw(node.next?.as_ptr());
next_node.prev = None;
self.index = 1;
// a node should never be empty
debug_assert_ne!(next_node.size, 0);
self.node = Some(next_node);
// see comment above
Some(
mem::replace(
&mut self.node.as_mut().unwrap().values[0],
MaybeUninit::uninit(),
)
.assume_init(),
) )
.assume_init(), }
)
} }
} }
} }

View file

@ -76,6 +76,48 @@ fn from_iter() {
assert!(list_iter.zip(vec.iter()).all(|(a, b)| a == b)); assert!(list_iter.zip(vec.iter()).all(|(a, b)| a == b));
} }
#[test]
fn get_cursor() {
let list = create_list(&[1, 2, 3, 4, 5, 6]);
let mut cursor = list.cursor_front();
assert_eq!(cursor.get(), Some(&1));
cursor.move_next();
assert_eq!(cursor.get(), Some(&2));
cursor.move_prev();
assert_eq!(cursor.get(), Some(&1));
cursor.move_prev();
assert_eq!(cursor.get(), None);
cursor.move_prev();
assert_eq!(cursor.get(), Some(&6));
cursor.move_prev();
cursor.move_prev();
cursor.move_prev();
cursor.move_prev();
cursor.move_prev();
assert_eq!(cursor.get(), Some(&1));
cursor.move_prev();
cursor.move_next();
assert_eq!(cursor.get(), Some(&1));
}
#[test]
fn insert_cursor() {
let mut list = create_list(&[1, 2, 3, 4, 5, 6]);
let mut cursor = list.cursor_mut_front();
cursor.move_prev();
cursor.move_prev();
*cursor.get_mut().unwrap() = 100;
cursor.move_next();
cursor.move_next();
cursor.insert_after(11);
cursor.insert_before(0);
cursor.move_next();
assert_eq!(cursor.replace(12), Some(1));
assert_eq!(cursor.get(), Some(&12));
assert_eq!(cursor.remove(), Some(12));
assert_eq!(list, create_list(&[0, 11, 2, 3, 4, 5, 100]));
}
fn create_list<T: Clone>(iter: &[T]) -> PackedLinkedList<T, 16> { fn create_list<T: Clone>(iter: &[T]) -> PackedLinkedList<T, 16> {
iter.into_iter().cloned().collect() iter.into_iter().cloned().collect()
} }