diff --git a/src/lib.rs b/src/lib.rs index acb82e2..5194682 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ +/// A doubly linked list pub mod linked_list; diff --git a/src/linked_list.rs b/src/linked_list.rs index fbad6de..f7dc075 100644 --- a/src/linked_list.rs +++ b/src/linked_list.rs @@ -2,55 +2,113 @@ use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; use std::ptr::NonNull; -/// A doubly linked list with unsafe :O, except it's kind of shit compared to the std one +/// A doubly linked list using unsafe code. +/// It is loosely inspired by the `std::collections::LinkedList`, but I haven't looked at that one too close, +/// so most it is my own. +/// +/// It was not made with efficiency in mind, but at least it doesn't but `std::rc::Rc` everywhere, but uses +/// unsafe pointers instead. +/// +/// # How to use +/// ``` +/// use datastructures::linked_list::LinkedList; +/// +/// let mut list = LinkedList::new(); +/// list.push_front("hello"); +/// assert_eq!(list.get(0), Some(&"hello")); +/// list.push_end("bye"); +/// assert_eq!(list.get(1), Some(&"bye")); +/// ``` +/// +/// The list can also be edited using the `Node` methods +/// ``` +/// use datastructures::linked_list::LinkedList; +/// +/// let mut list = LinkedList::new(); +/// list.push_front(1); +/// let mut node = list.get_head_node_mut().unwrap(); +/// node.push_after(3); +/// node.push_after(2); +/// let next = node.get_next().unwrap(); +/// let next = next.get_next().unwrap(); +/// assert_eq!(*next.get_value(), 3); +/// ``` +/// +/// # Note +/// You should generally not use Linked Lists, and if you really do need to use one, use `std::collections::LinkedList` pub struct LinkedList { start: Option>>, end: Option>>, - _maker: PhantomData, + _marker: PhantomData, } impl LinkedList { /// Creates a new empty Linked List pub fn new() -> LinkedList { - std::collections::LinkedList::new().push_front("hi"); Self { start: None, end: None, - _maker: PhantomData, + _marker: PhantomData, } } - /// Prepend an element to the start of the list + /// Push an element to the start of the list (O(1)) pub fn push_front(&mut self, element: T) { match self.start { // empty list None => { - let node = allocate_nonnull(Node { + let new_node = allocate_nonnull(Node { value: element, next: None, prev: None, - _maker: PhantomData, + _marker: PhantomData, }); - self.start = Some(node); - self.end = Some(node); + self.start = Some(new_node); + self.end = Some(new_node); } // at lest one element - Some(mut node) => { - let new = allocate_nonnull(Node { + Some(mut old_start) => { + let new_node = allocate_nonnull(Node { value: element, - next: Some(node), + next: Some(old_start), prev: None, - _maker: PhantomData, + _marker: PhantomData, }); // SAFETY: All pointers should always be valid - unsafe { node.as_mut() }.prev = Some(new); - self.start = Some(new); + unsafe { old_start.as_mut() }.prev = Some(new_node); + self.start = Some(new_node); } } } - pub fn _insert_end(&mut self, _element: T) {} + /// Push an element to the end of the list (O(1)) + pub fn push_end(&mut self, element: T) { + match self.end { + None => { + let new_node = allocate_nonnull(Node { + value: element, + next: None, + prev: None, + _marker: PhantomData, + }); + self.start = Some(new_node); + self.end = Some(new_node); + } + Some(mut old_end) => { + let new_node = allocate_nonnull(Node { + value: element, + next: None, + prev: Some(old_end), + _marker: PhantomData, + }); + // SAFETY: All pointers should always be valid + unsafe { old_end.as_mut() }.next = Some(new_node); + self.end = Some(new_node); + } + } + } + /// Get an element from the list (O(n)) pub fn get(&self, mut index: usize) -> Option<&T> { let mut node = &self.start; let mut result = None; @@ -67,7 +125,68 @@ impl LinkedList { result } - pub fn _get_node(&self, _index: usize) {} + /// Gets the last element from the list (O(1)) + pub fn get_tail(&self) -> Option<&T> { + self.end.as_ref().map(|nn| unsafe { &nn.as_ref().value }) + } + + /// Gets the first element from the list (O(1)) + pub fn get_head(&self) -> Option<&T> { + self.start.as_ref().map(|nn| unsafe { &nn.as_ref().value }) + } + + /// Get a node from the list that can only be used for navigation + pub fn get_node(&self, mut index: usize) -> Option<&Node> { + let mut node = &self.start; + let mut result = None; + while let Some(content) = node { + // SAFETY: All pointers should always be valid + let content = unsafe { content.as_ref() }; + if index == 0 { + result = Some(content); + break; + } + index -= 1; + node = &content.next; + } + result + } + + /// Get the head node from the list that can only be used for navigation + pub fn get_head_node(&self) -> Option<&Node> { + self.start.as_ref().map(|nn| unsafe { nn.as_ref() }) + } + + /// Get the tail node from the list that can only be used for navigation + pub fn get_tail_node(&self) -> Option<&Node> { + self.end.as_ref().map(|nn| unsafe { nn.as_ref() }) + } + /// Get the head node from the list that can be used the edit the list + pub fn get_head_node_mut(&mut self) -> Option<&mut Node> { + self.start.as_mut().map(|nn| unsafe { nn.as_mut() }) + } + + /// Get the tail node from the list that can be used the edit the list + pub fn get_tail_node_mut(&mut self) -> Option<&mut Node> { + self.end.as_mut().map(|nn| unsafe { nn.as_mut() }) + } + + /// Get a node from the list that can be used the edit the list + pub fn get_node_mut(&mut self, mut index: usize) -> Option<&mut Node> { + let mut node = &mut self.start; + let mut result = None; + while let Some(ref mut content) = node { + // SAFETY: All pointers should always be valid + let content = unsafe { content.as_mut() }; + if index == 0 { + result = Some(content); + break; + } + index -= 1; + node = &mut content.next; + } + result + } /// Returns an iterator over the items pub fn iter(&self) -> Iter { @@ -75,10 +194,7 @@ impl LinkedList { } } -impl Debug for LinkedList -where - T: Debug, -{ +impl Debug for LinkedList { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_list().entries(self.iter()).finish() } @@ -104,12 +220,102 @@ impl Drop for LinkedList { } /// A Node in a `LinkedList` +/// Can be used to navigate the `LinkedList`, using the `Node::get_next` and `Node::get_previous` methods, +/// and edit the List using the push methods. +/// +/// # Examples +/// ``` +/// use datastructures::linked_list::*; +/// +/// let mut list = LinkedList::new(); +/// list.push_front(1); +/// let mut node = list.get_node_mut(0); +/// ``` +/// #[derive(Debug)] pub struct Node { value: T, next: Option>>, prev: Option>>, - _maker: PhantomData, + _marker: PhantomData, +} + +impl Node { + /// Push a value after this node + pub fn push_after(&mut self, element: T) { + let new_node = Some(allocate_nonnull(Node { + value: element, + next: self.next, + prev: NonNull::new(self as _), + _marker: PhantomData, + })); + self.next.map(|mut next| { + // SAFETY: All pointers should always be valid and created from a box + unsafe { next.as_mut() }.prev = new_node + }); + self.next = new_node; + } + + /// Push a value before this node + pub fn push_before(&mut self, element: T) { + let new_node = Some(allocate_nonnull(Node { + value: element, + next: NonNull::new(self as _), + prev: self.prev, + _marker: PhantomData, + })); + self.prev.map(|mut next| { + // SAFETY: All pointers should always be valid and created from a box + unsafe { next.as_mut() }.next = new_node + }); + self.prev = new_node; + } + + /// Get the next node + pub fn get_next(&self) -> Option<&Node> { + match &self.next { + None => None, + Some(nn) => unsafe { Some(nn.as_ref()) }, + } + } + /// Get the next node mutably + pub fn get_next_mut(&mut self) -> Option<&mut Node> { + match &mut self.next { + None => None, + Some(nn) => unsafe { Some(nn.as_mut()) }, + } + } + + /// Get the previous node + pub fn get_previous(&self) -> Option<&Node> { + match &self.prev { + None => None, + Some(nn) => unsafe { Some(nn.as_ref()) }, + } + } + + /// Get the previous node mutably + pub fn get_previous_mut(&mut self) -> Option<&mut Node> { + match &mut self.prev { + None => None, + Some(nn) => unsafe { Some(nn.as_mut()) }, + } + } + + /// Gets the value from the node + pub fn get_value(&self) -> &T { + &self.value + } + + /// Gets the value from the node + pub fn set_value(&mut self, value: T) { + self.value = value; + } + + /// Gets the value from the node and replaces it with the old one + pub fn replace_value(&mut self, value: T) -> T { + std::mem::replace(&mut self.value, value) + } } fn allocate_nonnull(element: T) -> NonNull { @@ -118,6 +324,7 @@ fn allocate_nonnull(element: T) -> NonNull { unsafe { NonNull::new_unchecked(Box::leak(boxed)) } } +/// The iterator over the linked list pub struct Iter<'a, T> { item: Option<&'a Node>, } @@ -133,7 +340,7 @@ impl<'a, T> Iter<'a, T> { } } -impl<'a, T: Debug> Iterator for Iter<'a, T> { +impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { @@ -167,6 +374,18 @@ mod test { assert_eq!(list.get(3), None); } + #[test] + fn push_start_end() { + let mut list = LinkedList::new(); + list.push_end(3); + list.push_front(2); + list.push_front(1); + list.push_end(4); + list.push_end(5); + let vec = list.iter().cloned().collect::>(); + assert_eq!(&vec[..], &[1, 2, 3, 4, 5]); + } + #[test] fn iter_simple() { let mut list = LinkedList::new(); @@ -202,4 +421,35 @@ mod test { } assert_eq!(list.get(999999), Some(&0)); } + + #[test] + fn node_operations() { + let mut list = LinkedList::new(); + list.push_front(1); + list.push_end(2); + { + let node = list.get_node_mut(1).unwrap(); + assert_eq!(*node.get_value(), 2); + node.push_after(4); + let next = node.get_next_mut().unwrap(); + assert!(matches!(next.get_next(), None)); + next.push_before(3) + } + let vec = list.iter().cloned().collect::>(); + assert_eq!(&vec[..], &[1, 2, 3, 4]); + } + + #[test] + fn node_values() { + let mut list = LinkedList::new(); + list.push_front(1); + let node = list.get_node_mut(0).unwrap(); + assert_eq!(*node.get_value(), 1); + assert_eq!(node.replace_value(2), 1); + assert_eq!(*node.get_value(), 2); + node.push_after(3); + let node = node.get_next_mut().unwrap(); + node.set_value(4); + assert_eq!(*node.get_value(), 4); + } }