mirror of
https://github.com/Noratrieb/datastructures.git
synced 2026-01-14 17:35:02 +01:00
packed linked list
This commit is contained in:
parent
bdc9a79368
commit
7249de2bd4
4 changed files with 336 additions and 8 deletions
|
|
@ -1,14 +1,15 @@
|
|||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use std::iter::FromIterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
fn allocate_nonnull<T>(element: T) -> NonNull<T> {
|
||||
let boxed = Box::new(element);
|
||||
// SAFETY: box is always non-null
|
||||
unsafe { NonNull::new_unchecked(Box::leak(boxed)) }
|
||||
unsafe { NonNull::new_unchecked(Box::leak(Box::new(element))) }
|
||||
}
|
||||
|
||||
///
|
||||
|
|
@ -27,6 +28,16 @@ pub struct PackedLinkedList<T, const COUNT: usize> {
|
|||
_maker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, const COUNT: usize> Drop for PackedLinkedList<T, COUNT> {
|
||||
fn drop(&mut self) {
|
||||
let mut item = self.first;
|
||||
while let Some(node) = item {
|
||||
let boxed = unsafe { Box::from_raw(node.as_ptr()) };
|
||||
item = boxed.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const COUNT: usize> PackedLinkedList<T, COUNT> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
|
@ -36,6 +47,7 @@ impl<T, const COUNT: usize> PackedLinkedList<T, COUNT> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Pushes a new value to the front of the list
|
||||
pub fn push_front(&mut self, element: T) {
|
||||
// SAFETY: All pointers should always point to valid memory,
|
||||
unsafe {
|
||||
|
|
@ -53,19 +65,143 @@ impl<T, const COUNT: usize> PackedLinkedList<T, COUNT> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Pushes a new value to the back of the list
|
||||
pub fn push_back(&mut self, element: T) {
|
||||
// SAFETY: All pointers should always point to valid memory,
|
||||
unsafe {
|
||||
match self.last {
|
||||
None => {
|
||||
self.insert_node_end();
|
||||
self.last.unwrap().as_mut().push_back(element)
|
||||
}
|
||||
Some(node) if node.as_ref().is_full() => {
|
||||
self.insert_node_end();
|
||||
self.last.unwrap().as_mut().push_front(element)
|
||||
}
|
||||
Some(mut node) => node.as_mut().push_back(element),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops the front element and returns it
|
||||
pub fn pop_front(&mut self) -> Option<T> {
|
||||
let first = &mut self.first?;
|
||||
unsafe {
|
||||
let node = first.as_mut();
|
||||
debug_assert_ne!(node.size, 0);
|
||||
|
||||
let item = mem::replace(&mut node.values[0], MaybeUninit::uninit()).assume_init();
|
||||
|
||||
if node.size == 1 {
|
||||
// the last item, deallocate it
|
||||
let mut boxed = Box::from_raw(first.as_ptr());
|
||||
boxed.next.as_mut().map(|next| next.as_mut().prev = None);
|
||||
self.first = boxed.next;
|
||||
if let None = self.first {
|
||||
// if this node was the last one, also remove it from the tail pointer
|
||||
self.last = None;
|
||||
}
|
||||
} else {
|
||||
// more items, move them down
|
||||
std::ptr::copy(
|
||||
&node.values[1] as *const _,
|
||||
&mut node.values[0] as *mut _,
|
||||
node.size,
|
||||
);
|
||||
node.size -= 1;
|
||||
}
|
||||
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pops the back value and returns it
|
||||
pub fn pop_back(&mut self) -> Option<T> {
|
||||
let last = &mut self.last?;
|
||||
unsafe {
|
||||
let node = last.as_mut();
|
||||
debug_assert_ne!(node.size, 0);
|
||||
|
||||
let item =
|
||||
mem::replace(&mut node.values[node.size - 1], MaybeUninit::uninit()).assume_init();
|
||||
|
||||
if node.size == 1 {
|
||||
// the last item, deallocate it
|
||||
let mut boxed = Box::from_raw(last.as_ptr());
|
||||
boxed
|
||||
.prev
|
||||
.as_mut()
|
||||
.map(|previous| previous.as_mut().next = None);
|
||||
self.last = boxed.prev;
|
||||
if let None = self.last {
|
||||
// if this node was the last one, also remove it from the tail pointer
|
||||
self.first = None;
|
||||
}
|
||||
} else {
|
||||
// more items
|
||||
node.size -= 1;
|
||||
}
|
||||
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<T, COUNT> {
|
||||
Iter::new(self)
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> IntoIter<T, COUNT> {
|
||||
IntoIter::new(self)
|
||||
}
|
||||
|
||||
fn insert_node_start(&mut self) {
|
||||
let node = Some(allocate_nonnull(Node::new(None, self.first)));
|
||||
self.first
|
||||
.as_mut()
|
||||
.map(|first| unsafe { first.as_mut().prev = node });
|
||||
self.first = node;
|
||||
if let None = self.last {
|
||||
self.last = node;
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_node_end(&mut self) {
|
||||
let node = Some(allocate_nonnull(Node::new(self.last, None)));
|
||||
self.last
|
||||
.as_mut()
|
||||
.map(|last| unsafe { last.as_mut().next = node });
|
||||
self.last = node;
|
||||
if let None = self.first {
|
||||
self.first = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const COUNT: usize> FromIterator<T> for PackedLinkedList<T, COUNT> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut list = PackedLinkedList::new();
|
||||
let mut iter = iter.into_iter();
|
||||
while let Some(item) = iter.next() {
|
||||
list.push_back(item);
|
||||
}
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const COUNT: usize> Extend<T> for PackedLinkedList<T, COUNT> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
let mut iter = iter.into_iter();
|
||||
while let Some(item) = iter.next() {
|
||||
self.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A single node in the packed linked list
|
||||
///
|
||||
/// The node can have 1 to `COUNT` items.
|
||||
/// A node is never guaranteed to be full, even if it has a next node
|
||||
/// A node is always guaranteed to be non-empty
|
||||
#[derive(Debug)]
|
||||
struct Node<T, const COUNT: usize> {
|
||||
prev: Option<NonNull<Node<T, COUNT>>>,
|
||||
|
|
@ -86,6 +222,7 @@ impl<T, const COUNT: usize> Node<T, COUNT> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks whether the node is full
|
||||
fn is_full(&self) -> bool {
|
||||
self.size == COUNT
|
||||
}
|
||||
|
|
@ -105,18 +242,20 @@ impl<T, const COUNT: usize> Node<T, COUNT> {
|
|||
unsafe fn push_front(&mut self, element: T) {
|
||||
debug_assert!(self.size < COUNT);
|
||||
// copy all values up
|
||||
unsafe {
|
||||
if COUNT > 1 {
|
||||
std::ptr::copy(
|
||||
&self.values[0] as *const _,
|
||||
&mut self.values[1] as *mut _,
|
||||
self.size,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
self.values[0] = MaybeUninit::new(element);
|
||||
self.size += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Iter<'a, T, const COUNT: usize> {
|
||||
node: Option<&'a Node<T, COUNT>>,
|
||||
index: usize,
|
||||
|
|
@ -156,3 +295,70 @@ impl<'a, T, const COUNT: usize> Iterator for Iter<'a, T, COUNT> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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> {
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,32 @@ fn push_front_single_node() {
|
|||
let mut list = PackedLinkedList::<_, 16>::new();
|
||||
list.push_front("hallo");
|
||||
}
|
||||
#[test]
|
||||
fn push_front_multiple_nodes_count_1() {
|
||||
let mut list = PackedLinkedList::<_, 1>::new();
|
||||
list.push_front("3");
|
||||
list.push_front("2");
|
||||
list.push_front("1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_front_multiple_nodes_count_2() {
|
||||
let mut list = PackedLinkedList::<_, 2>::new();
|
||||
list.push_front("3");
|
||||
list.push_front("2");
|
||||
list.push_front("1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pop_front() {
|
||||
let mut list = create_sized_list::<_, 2>(&[1, 2, 3, 4]);
|
||||
assert_eq!(list.pop_front(), Some(1));
|
||||
assert_eq!(list.pop_front(), Some(2));
|
||||
assert_eq!(list.pop_front(), Some(3));
|
||||
assert_eq!(list.pop_front(), Some(4));
|
||||
assert_eq!(list.pop_front(), None);
|
||||
assert_eq!(list.pop_front(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_single_node() {
|
||||
|
|
@ -22,7 +48,40 @@ fn iter_single_node() {
|
|||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
fn create_list<T: Clone, const COUNT: usize>(iter: &[T]) -> PackedLinkedList<T, COUNT> {
|
||||
//iter.into_iter().cloned().collect()
|
||||
todo!()
|
||||
#[test]
|
||||
fn into_iter() {
|
||||
let mut iter = create_list(&[1, 2, 3]).into_iter();
|
||||
assert_eq!(iter.next(), Some(1));
|
||||
assert_eq!(iter.next(), Some(2));
|
||||
assert_eq!(iter.next(), Some(3));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
// ignore this test for now
|
||||
#[test]
|
||||
#[cfg(all(test, not(test)))]
|
||||
fn iter_mut() {
|
||||
let mut list = create_list(&[1, 2, 3, 4]);
|
||||
let mut iter_mut = list.iter_mut();
|
||||
*iter_mut.next().unwrap() = 10;
|
||||
assert!([10, 2, 3, 4].iter().zip(list.iter()).all(|(a, b)| a == b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_iter() {
|
||||
let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let list = vec
|
||||
.clone()
|
||||
.into_iter()
|
||||
.collect::<PackedLinkedList<_, 1024>>();
|
||||
let list_iter = list.iter();
|
||||
assert!(list_iter.zip(vec.iter()).all(|(a, b)| a == b));
|
||||
}
|
||||
|
||||
fn create_list<T: Clone>(iter: &[T]) -> PackedLinkedList<T, 16> {
|
||||
iter.into_iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn create_sized_list<T: Clone, const COUNT: usize>(iter: &[T]) -> PackedLinkedList<T, COUNT> {
|
||||
iter.into_iter().cloned().collect()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue