diff options
Diffstat (limited to 'src/linked_list.rs')
-rw-r--r-- | src/linked_list.rs | 599 |
1 files changed, 599 insertions, 0 deletions
diff --git a/src/linked_list.rs b/src/linked_list.rs new file mode 100644 index 00000000..6a9836ee --- /dev/null +++ b/src/linked_list.rs @@ -0,0 +1,599 @@ +use core::fmt; +use core::marker::PhantomData; +use core::mem::MaybeUninit; +use core::ops::{Deref, DerefMut}; +use core::ptr; +pub use generic_array::ArrayLength; +use generic_array::GenericArray; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct LinkedIndex(u16); + +impl LinkedIndex { + #[inline] + const unsafe fn new_unchecked(value: u16) -> Self { + LinkedIndex(value) + } + + #[inline] + const fn none() -> Self { + LinkedIndex(u16::MAX) + } + + #[inline] + const fn option(self) -> Option<u16> { + if self.0 == u16::MAX { + None + } else { + Some(self.0) + } + } +} + +/// A node in the linked list. +pub struct Node<T> { + val: MaybeUninit<T>, + next: LinkedIndex, +} + +/// Iterator for the linked list. +pub struct Iter<'a, T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + list: &'a LinkedList<T, Kind, N>, + index: LinkedIndex, +} + +impl<'a, T, Kind, N> Iterator for Iter<'a, T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + type Item = &'a T; + + fn next(&mut self) -> Option<Self::Item> { + let index = self.index.option()?; + + let node = self.list.node_at(index as usize); + self.index = node.next; + + Some(self.list.read_data_in_node_at(index as usize)) + } +} + +/// Comes from [`LinkedList::find_mut`]. +pub struct FindMut<'a, T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + list: &'a mut LinkedList<T, Kind, N>, + is_head: bool, + prev_index: LinkedIndex, + index: LinkedIndex, + maybe_changed: bool, +} + +impl<'a, T, Kind, N> FindMut<'a, T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + fn pop_internal(&mut self) -> T { + if self.is_head { + // If it is the head element, we can do a normal pop + unsafe { self.list.pop_unchecked() } + } else { + // Somewhere in the list + + // Re-point the previous index + self.list.node_at_mut(self.prev_index.0 as usize).next = + self.list.node_at_mut(self.index.0 as usize).next; + + // Release the index into the free queue + self.list.node_at_mut(self.index.0 as usize).next = self.list.free; + self.list.free = self.index; + + self.list.extract_data_in_node_at(self.index.0 as usize) + } + } + + /// This will pop the element from the list. + /// + /// Complexity is O(1). + #[inline] + pub fn pop(mut self) -> T { + self.pop_internal() + } + + /// This will resort the element into the correct position in the list in needed. + /// Same as calling `drop`. + /// + /// Complexity is worst-case O(N). + #[inline] + pub fn finish(self) { + drop(self) + } +} + +impl<T, Kind, N> Drop for FindMut<'_, T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + fn drop(&mut self) { + // Only resort the list if the element has changed + if self.maybe_changed { + let val = self.pop_internal(); + unsafe { self.list.push_unchecked(val) }; + } + } +} + +impl<T, Kind, N> Deref for FindMut<'_, T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + self.list.read_data_in_node_at(self.index.0 as usize) + } +} + +impl<T, Kind, N> DerefMut for FindMut<'_, T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.maybe_changed = true; + self.list.read_mut_data_in_node_at(self.index.0 as usize) + } +} + +impl<T, Kind, N> fmt::Debug for FindMut<'_, T, Kind, N> +where + T: PartialEq + PartialOrd + core::fmt::Debug, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FindMut") + .field("prev_index", &self.prev_index) + .field("index", &self.index) + .field( + "prev_value", + &self + .list + .read_data_in_node_at(self.prev_index.option().unwrap() as usize), + ) + .field( + "value", + &self + .list + .read_data_in_node_at(self.index.option().unwrap() as usize), + ) + .finish() + } +} + +/// The linked list. +pub struct LinkedList<T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + list: MaybeUninit<GenericArray<Node<T>, N>>, + head: LinkedIndex, + free: LinkedIndex, + _kind: PhantomData<Kind>, +} + +impl<T, Kind, N> LinkedList<T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + /// Internal helper to not do pointer arithmetic all over the place. + #[inline] + fn node_at(&self, index: usize) -> &Node<T> { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { &*(self.list.as_ptr() as *const Node<T>).add(index) } + } + + /// Internal helper to not do pointer arithmetic all over the place. + #[inline] + fn node_at_mut(&mut self, index: usize) -> &mut Node<T> { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { &mut *(self.list.as_mut_ptr() as *mut Node<T>).add(index) } + } + + /// Internal helper to not do pointer arithmetic all over the place. + #[inline] + fn write_data_in_node_at(&mut self, index: usize, data: T) { + unsafe { + self.node_at_mut(index).val.as_mut_ptr().write(data); + } + } + + /// Internal helper to not do pointer arithmetic all over the place. + #[inline] + fn read_data_in_node_at(&self, index: usize) -> &T { + unsafe { &*self.node_at(index).val.as_ptr() } + } + + /// Internal helper to not do pointer arithmetic all over the place. + #[inline] + fn read_mut_data_in_node_at(&mut self, index: usize) -> &mut T { + unsafe { &mut *self.node_at_mut(index).val.as_mut_ptr() } + } + + /// Internal helper to not do pointer arithmetic all over the place. + #[inline] + fn extract_data_in_node_at(&mut self, index: usize) -> T { + unsafe { self.node_at(index).val.as_ptr().read() } + } + + /// Internal helper to not do pointer arithmetic all over the place. + /// Safety: This can overwrite existing allocated nodes if used improperly, meaning their + /// `Drop` methods won't run. + #[inline] + unsafe fn write_node_at(&mut self, index: usize, node: Node<T>) { + (self.list.as_mut_ptr() as *mut Node<T>) + .add(index) + .write(node) + } + + /// Create a new linked list. + pub fn new() -> Self { + let mut list = LinkedList { + list: MaybeUninit::uninit(), + head: LinkedIndex::none(), + free: unsafe { LinkedIndex::new_unchecked(0) }, + _kind: PhantomData, + }; + + let len = N::U16; + let mut free = 0; + + // Initialize indexes + while free < len - 1 { + unsafe { + list.write_node_at( + free as usize, + Node { + val: MaybeUninit::uninit(), + next: LinkedIndex::new_unchecked(free + 1), + }, + ); + } + free += 1; + } + + // Initialize final index + unsafe { + list.write_node_at( + free as usize, + Node { + val: MaybeUninit::uninit(), + next: LinkedIndex::none(), + }, + ); + } + + list + } + + /// Push unchecked + /// + /// Complexity is O(N). + /// + /// # Safety + /// + /// Assumes that the list is not full. + pub unsafe fn push_unchecked(&mut self, value: T) { + let new = self.free.0; + // Store the data and update the next free spot + self.write_data_in_node_at(new as usize, value); + self.free = self.node_at(new as usize).next; + + if let Some(head) = self.head.option() { + // Check if we need to replace head + if self + .read_data_in_node_at(head as usize) + .partial_cmp(self.read_data_in_node_at(new as usize)) + != Kind::ordering() + { + self.node_at_mut(new as usize).next = self.head; + self.head = LinkedIndex::new_unchecked(new); + } else { + // It's not head, search the list for the correct placement + let mut current = head; + + while let Some(next) = self.node_at(current as usize).next.option() { + if self + .read_data_in_node_at(next as usize) + .partial_cmp(self.read_data_in_node_at(new as usize)) + != Kind::ordering() + { + break; + } + + current = next; + } + + self.node_at_mut(new as usize).next = self.node_at(current as usize).next; + self.node_at_mut(current as usize).next = LinkedIndex::new_unchecked(new); + } + } else { + self.node_at_mut(new as usize).next = self.head; + self.head = LinkedIndex::new_unchecked(new); + } + } + + /// Pushes an element to the linked list and sorts it into place. + /// + /// Complexity is O(N). + pub fn push(&mut self, value: T) -> Result<(), T> { + if !self.is_full() { + Ok(unsafe { self.push_unchecked(value) }) + } else { + Err(value) + } + } + + /// Get an iterator over the sorted list. + pub fn iter(&self) -> Iter<'_, T, Kind, N> { + Iter { + list: self, + index: self.head, + } + } + + /// Find an element in the list. + pub fn find_mut<F>(&mut self, mut f: F) -> Option<FindMut<'_, T, Kind, N>> + where + F: FnMut(&T) -> bool, + { + let head = self.head.option()?; + + // Special-case, first element + if f(self.read_data_in_node_at(head as usize)) { + return Some(FindMut { + is_head: true, + prev_index: LinkedIndex::none(), + index: self.head, + list: self, + maybe_changed: false, + }); + } + + let mut current = head; + + while let Some(next) = self.node_at(current as usize).next.option() { + if f(self.read_data_in_node_at(next as usize)) { + return Some(FindMut { + is_head: false, + prev_index: unsafe { LinkedIndex::new_unchecked(current) }, + index: unsafe { LinkedIndex::new_unchecked(next) }, + list: self, + maybe_changed: false, + }); + } + + current = next; + } + + None + } + + /// Peek at the first element. + pub fn peek(&self) -> Option<&T> { + self.head + .option() + .map(|head| self.read_data_in_node_at(head as usize)) + } + + /// Pop unchecked + /// + /// # Safety + /// + /// Assumes that the list is not empty. + pub unsafe fn pop_unchecked(&mut self) -> T { + let head = self.head.0; + let current = head; + self.head = self.node_at(head as usize).next; + self.node_at_mut(current as usize).next = self.free; + self.free = LinkedIndex::new_unchecked(current); + + self.extract_data_in_node_at(current as usize) + } + + /// Pops the first element in the list. + /// + /// Complexity is O(1). + pub fn pop(&mut self) -> Result<T, ()> { + if !self.is_empty() { + Ok(unsafe { self.pop_unchecked() }) + } else { + Err(()) + } + } + + /// Checks if the linked list is full. + #[inline] + pub fn is_full(&self) -> bool { + self.free.option().is_none() + } + + /// Checks if the linked list is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.head.option().is_none() + } +} + +impl<T, Kind, N> Drop for LinkedList<T, Kind, N> +where + T: PartialEq + PartialOrd, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + fn drop(&mut self) { + let mut index = self.head; + + while let Some(i) = index.option() { + let node = self.node_at_mut(i as usize); + index = node.next; + + unsafe { + ptr::drop_in_place(node.val.as_mut_ptr()); + } + } + } +} + +impl<T, Kind, N> fmt::Debug for LinkedList<T, Kind, N> +where + T: PartialEq + PartialOrd + core::fmt::Debug, + Kind: kind::Kind, + N: ArrayLength<Node<T>>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +/// Min sorted linked list. +pub struct Min; + +/// Max sorted linked list. +pub struct Max; + +/// Sealed traits and implementations for `linked_list` +pub mod kind { + use super::{Max, Min}; + use core::cmp::Ordering; + + /// The linked list kind: min first or max first + pub unsafe trait Kind { + #[doc(hidden)] + fn ordering() -> Option<Ordering>; + } + + unsafe impl Kind for Min { + #[inline] + fn ordering() -> Option<Ordering> { + Some(Ordering::Less) + } + } + + unsafe impl Kind for Max { + #[inline] + fn ordering() -> Option<Ordering> { + Some(Ordering::Greater) + } + } +} + +#[cfg(test)] +mod tests { + // Note this useful idiom: importing names from outer (for mod tests) scope. + use super::*; + use generic_array::typenum::consts::*; + + #[test] + fn test_peek() { + let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); + + ll.push(1).unwrap(); + assert_eq!(ll.peek().unwrap(), &1); + + ll.push(2).unwrap(); + assert_eq!(ll.peek().unwrap(), &2); + + ll.push(3).unwrap(); + assert_eq!(ll.peek().unwrap(), &3); + + let mut ll: LinkedList<u32, Min, U3> = LinkedList::new(); + + ll.push(2).unwrap(); + assert_eq!(ll.peek().unwrap(), &2); + + ll.push(1).unwrap(); + assert_eq!(ll.peek().unwrap(), &1); + + ll.push(3).unwrap(); + assert_eq!(ll.peek().unwrap(), &1); + } + + #[test] + fn test_full() { + let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); + ll.push(1).unwrap(); + ll.push(2).unwrap(); + ll.push(3).unwrap(); + + assert!(ll.is_full()) + } + + #[test] + fn test_empty() { + let ll: LinkedList<u32, Max, U3> = LinkedList::new(); + + assert!(ll.is_empty()) + } + + #[test] + fn test_rejected_push() { + let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); + ll.push(1).unwrap(); + ll.push(2).unwrap(); + ll.push(3).unwrap(); + + // This won't fit + let r = ll.push(4); + + assert_eq!(r, Err(4)); + } + + #[test] + fn test_updating() { + let mut ll: LinkedList<u32, Max, U3> = LinkedList::new(); + ll.push(1).unwrap(); + ll.push(2).unwrap(); + ll.push(3).unwrap(); + + let mut find = ll.find_mut(|v| *v == 2).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1002); + + let mut find = ll.find_mut(|v| *v == 3).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1003); + + // Remove largest element + ll.find_mut(|v| *v == 1003).unwrap().pop(); + + assert_eq!(ll.peek().unwrap(), &1002); + } +} |