diff --git a/src/lib.rs b/src/lib.rs index 005be008..da59768a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,7 @@ mod defmt; pub mod mpmc; #[cfg(all(has_cas, feature = "cas"))] pub mod pool; +pub mod sorted_linked_list; #[cfg(has_atomics)] pub mod spsc; diff --git a/src/sealed.rs b/src/sealed.rs index b963bb8b..aeabca58 100644 --- a/src/sealed.rs +++ b/src/sealed.rs @@ -22,6 +22,36 @@ pub mod binary_heap { } } +/// Sealed traits and implementations for `LinkedList` +pub mod sorted_linked_list { + use crate::sorted_linked_list::{Max, Min}; + use core::cmp::Ordering; + + /// The linked list kind: min-list or max-list + pub unsafe trait Kind { + #[doc(hidden)] + fn ordering() -> Ordering; + } + + unsafe impl Kind for Min { + fn ordering() -> Ordering { + Ordering::Less + } + } + + unsafe impl Kind for Max { + fn ordering() -> Ordering { + Ordering::Greater + } + } +} + +#[allow(dead_code)] +#[allow(path_statements)] +pub(crate) const fn smaller_than() { + Assert::::LESS; +} + #[allow(dead_code)] #[allow(path_statements)] pub(crate) const fn greater_than_0() { diff --git a/src/sorted_linked_list.rs b/src/sorted_linked_list.rs new file mode 100644 index 00000000..76287523 --- /dev/null +++ b/src/sorted_linked_list.rs @@ -0,0 +1,840 @@ +//! A fixed sorted priority linked list, similar to [`BinaryHeap`] but with different properties +//! on `push`, `pop`, etc. +//! For example, the sorting of the list will never `memcpy` the underlying value, so having large +//! objects in the list will not cause a performance hit. +//! +//! # Examples +//! +//! ``` +//! use heapless::sorted_linked_list::{SortedLinkedList, Max}; +//! let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); +//! +//! // The largest value will always be first +//! ll.push(1).unwrap(); +//! assert_eq!(ll.peek(), Some(&1)); +//! +//! ll.push(2).unwrap(); +//! assert_eq!(ll.peek(), Some(&2)); +//! +//! ll.push(3).unwrap(); +//! assert_eq!(ll.peek(), Some(&3)); +//! +//! // This will not fit in the queue. +//! assert_eq!(ll.push(4), Err(4)); +//! ``` +//! +//! [`BinaryHeap`]: `crate::binary_heap::BinaryHeap` + +use crate::sealed::sorted_linked_list::Kind as LLKind; +use core::fmt; +use core::marker::PhantomData; +use core::mem::MaybeUninit; +use core::ops::{Deref, DerefMut}; +use core::ptr; + +/// Trait for defining an index for the linked list, never implemented by users. +pub trait SortedLinkedListIndex: Copy { + #[doc(hidden)] + unsafe fn new_unchecked(val: usize) -> Self; + #[doc(hidden)] + unsafe fn get_unchecked(self) -> usize; + #[doc(hidden)] + fn option(self) -> Option; + #[doc(hidden)] + fn none() -> Self; +} + +/// Marker for Min sorted [`SortedLinkedList`]. +pub struct Min; + +/// Marker for Max sorted [`SortedLinkedList`]. +pub struct Max; + +/// A node in the [`SortedLinkedList`]. +pub struct Node { + val: MaybeUninit, + next: Idx, +} + +/// The linked list. +pub struct SortedLinkedList +where + Idx: SortedLinkedListIndex, +{ + list: [Node; N], + head: Idx, + free: Idx, + _kind: PhantomData, +} + +// Internal macro for generating indexes for the linkedlist and const new for the linked list +macro_rules! impl_index_and_const_new { + ($name:ident, $ty:ty, $new_name:ident, $max_val:literal) => { + /// Index for the [`SortedLinkedList`] with specific backing storage. + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub struct $name($ty); + + impl SortedLinkedListIndex for $name { + #[inline(always)] + unsafe fn new_unchecked(val: usize) -> Self { + Self::new_unchecked(val as $ty) + } + + /// This is only valid if `self.option()` is not `None`. + #[inline(always)] + unsafe fn get_unchecked(self) -> usize { + self.0 as usize + } + + #[inline(always)] + fn option(self) -> Option { + if self.0 == <$ty>::MAX { + None + } else { + Some(self.0 as usize) + } + } + + #[inline(always)] + fn none() -> Self { + Self::none() + } + } + + impl $name { + /// Needed for a `const fn new()`. + #[inline] + const unsafe fn new_unchecked(value: $ty) -> Self { + $name(value) + } + + /// Needed for a `const fn new()`. + #[inline] + const fn none() -> Self { + $name(<$ty>::MAX) + } + } + + impl SortedLinkedList { + const UNINIT: Node = Node { + val: MaybeUninit::uninit(), + next: $name::none(), + }; + + /// Create a new linked list. + pub const fn $new_name() -> Self { + // Const assert N < MAX + crate::sealed::smaller_than::(); + + let mut list = SortedLinkedList { + list: [Self::UNINIT; N], + head: $name::none(), + free: unsafe { $name::new_unchecked(0) }, + _kind: PhantomData, + }; + + if N == 0 { + list.free = $name::none(); + return list; + } + + let mut free = 0; + + // Initialize indexes + while free < N - 1 { + list.list[free].next = unsafe { $name::new_unchecked(free as $ty + 1) }; + free += 1; + } + + list + } + } + }; +} + +impl_index_and_const_new!(LinkedIndexU8, u8, new_u8, 254); // val is 2^8 - 2 (one less than max) +impl_index_and_const_new!(LinkedIndexU16, u16, new_u16, 65_534); // val is 2^16 - 2 +impl_index_and_const_new!(LinkedIndexUsize, usize, new_usize, 4_294_967_294); // val is 2^32 - 2 + +impl SortedLinkedList +where + Idx: SortedLinkedListIndex, +{ + /// Internal access helper + #[inline(always)] + fn node_at(&self, index: usize) -> &Node { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { self.list.get_unchecked(index) } + } + + /// Internal access helper + #[inline(always)] + fn node_at_mut(&mut self, index: usize) -> &mut Node { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { self.list.get_unchecked_mut(index) } + } + + /// Internal access helper + #[inline(always)] + fn write_data_in_node_at(&mut self, index: usize, data: T) { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { + self.node_at_mut(index).val.as_mut_ptr().write(data); + } + } + + /// Internal access helper + #[inline(always)] + fn read_data_in_node_at(&self, index: usize) -> &T { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { &*self.node_at(index).val.as_ptr() } + } + + /// Internal access helper + #[inline(always)] + fn read_mut_data_in_node_at(&mut self, index: usize) -> &mut T { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { &mut *self.node_at_mut(index).val.as_mut_ptr() } + } + + /// Internal access helper + #[inline(always)] + fn extract_data_in_node_at(&mut self, index: usize) -> T { + // Safety: The entire `self.list` is initialized in `new`, which makes this safe. + unsafe { self.node_at(index).val.as_ptr().read() } + } +} + +impl SortedLinkedList +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + /// Pushes a value onto the list without checking if the list is full. + /// + /// Complexity is worst-case `O(N)`. + /// + /// # Safety + /// + /// Assumes that the list is not full. + pub unsafe fn push_unchecked(&mut self, value: T) { + let new = self.free.get_unchecked(); + + // Store the data and update the next free spot + self.write_data_in_node_at(new, value); + self.free = self.node_at(new).next; + + if let Some(head) = self.head.option() { + // Check if we need to replace head + if self + .read_data_in_node_at(head) + .cmp(self.read_data_in_node_at(new)) + != Kind::ordering() + { + self.node_at_mut(new).next = self.head; + self.head = Idx::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).next.option() { + if self + .read_data_in_node_at(next) + .cmp(self.read_data_in_node_at(new)) + != Kind::ordering() + { + break; + } + + current = next; + } + + self.node_at_mut(new).next = self.node_at(current).next; + self.node_at_mut(current).next = Idx::new_unchecked(new); + } + } else { + self.node_at_mut(new).next = self.head; + self.head = Idx::new_unchecked(new); + } + } + + /// Pushes an element to the linked list and sorts it into place. + /// + /// Complexity is worst-case `O(N)`. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// // The largest value will always be first + /// ll.push(1).unwrap(); + /// assert_eq!(ll.peek(), Some(&1)); + /// + /// ll.push(2).unwrap(); + /// assert_eq!(ll.peek(), Some(&2)); + /// + /// ll.push(3).unwrap(); + /// assert_eq!(ll.peek(), Some(&3)); + /// + /// // This will not fit in the queue. + /// assert_eq!(ll.push(4), Err(4)); + /// ``` + 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. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// ll.push(1).unwrap(); + /// ll.push(2).unwrap(); + /// + /// let mut iter = ll.iter(); + /// + /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), None); + /// ``` + pub fn iter(&self) -> Iter<'_, T, Idx, Kind, N> { + Iter { + list: self, + index: self.head, + } + } + + /// Find an element in the list that can be changed and resorted. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// ll.push(1).unwrap(); + /// ll.push(2).unwrap(); + /// ll.push(3).unwrap(); + /// + /// // Find a value and update it + /// let mut find = ll.find_mut(|v| *v == 2).unwrap(); + /// *find += 1000; + /// find.finish(); + /// + /// assert_eq!(ll.pop(), Ok(1002)); + /// assert_eq!(ll.pop(), Ok(3)); + /// assert_eq!(ll.pop(), Ok(1)); + /// assert_eq!(ll.pop(), Err(())); + /// ``` + pub fn find_mut(&mut self, mut f: F) -> Option> + where + F: FnMut(&T) -> bool, + { + let head = self.head.option()?; + + // Special-case, first element + if f(self.read_data_in_node_at(head)) { + return Some(FindMut { + is_head: true, + prev_index: Idx::none(), + index: self.head, + list: self, + maybe_changed: false, + }); + } + + let mut current = head; + + while let Some(next) = self.node_at(current).next.option() { + if f(self.read_data_in_node_at(next)) { + return Some(FindMut { + is_head: false, + prev_index: unsafe { Idx::new_unchecked(current) }, + index: unsafe { Idx::new_unchecked(next) }, + list: self, + maybe_changed: false, + }); + } + + current = next; + } + + None + } + + /// Peek at the first element. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max, Min}; + /// let mut ll_max: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// // The largest value will always be first + /// ll_max.push(1).unwrap(); + /// assert_eq!(ll_max.peek(), Some(&1)); + /// ll_max.push(2).unwrap(); + /// assert_eq!(ll_max.peek(), Some(&2)); + /// ll_max.push(3).unwrap(); + /// assert_eq!(ll_max.peek(), Some(&3)); + /// + /// let mut ll_min: SortedLinkedList<_, _, Min, 3> = SortedLinkedList::new_usize(); + /// + /// // The Smallest value will always be first + /// ll_min.push(3).unwrap(); + /// assert_eq!(ll_min.peek(), Some(&3)); + /// ll_min.push(2).unwrap(); + /// assert_eq!(ll_min.peek(), Some(&2)); + /// ll_min.push(1).unwrap(); + /// assert_eq!(ll_min.peek(), Some(&1)); + /// ``` + pub fn peek(&self) -> Option<&T> { + self.head + .option() + .map(|head| self.read_data_in_node_at(head)) + } + + /// Pop an element from the list without checking so the list is not empty. + /// + /// # Safety + /// + /// Assumes that the list is not empty. + pub unsafe fn pop_unchecked(&mut self) -> T { + let head = self.head.get_unchecked(); + let current = head; + self.head = self.node_at(head).next; + self.node_at_mut(current).next = self.free; + self.free = Idx::new_unchecked(current); + + self.extract_data_in_node_at(current) + } + + /// Pops the first element in the list. + /// + /// Complexity is worst-case `O(1)`. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// ll.push(1).unwrap(); + /// ll.push(2).unwrap(); + /// + /// assert_eq!(ll.pop(), Ok(2)); + /// assert_eq!(ll.pop(), Ok(1)); + /// assert_eq!(ll.pop(), Err(())); + /// ``` + pub fn pop(&mut self) -> Result { + if !self.is_empty() { + Ok(unsafe { self.pop_unchecked() }) + } else { + Err(()) + } + } + + /// Checks if the linked list is full. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// assert_eq!(ll.is_full(), false); + /// + /// ll.push(1).unwrap(); + /// assert_eq!(ll.is_full(), false); + /// ll.push(2).unwrap(); + /// assert_eq!(ll.is_full(), false); + /// ll.push(3).unwrap(); + /// assert_eq!(ll.is_full(), true); + /// ``` + #[inline] + pub fn is_full(&self) -> bool { + self.free.option().is_none() + } + + /// Checks if the linked list is empty. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// assert_eq!(ll.is_empty(), true); + /// + /// ll.push(1).unwrap(); + /// assert_eq!(ll.is_empty(), false); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.head.option().is_none() + } +} + +/// Iterator for the linked list. +pub struct Iter<'a, T, Idx, Kind, const N: usize> +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + list: &'a SortedLinkedList, + index: Idx, +} + +impl<'a, T, Idx, Kind, const N: usize> Iterator for Iter<'a, T, Idx, Kind, N> +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + let index = self.index.option()?; + + let node = self.list.node_at(index); + self.index = node.next; + + Some(self.list.read_data_in_node_at(index)) + } +} + +/// Comes from [`SortedLinkedList::find_mut`]. +pub struct FindMut<'a, T, Idx, Kind, const N: usize> +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + list: &'a mut SortedLinkedList, + is_head: bool, + prev_index: Idx, + index: Idx, + maybe_changed: bool, +} + +impl<'a, T, Idx, Kind, const N: usize> FindMut<'a, T, Idx, Kind, N> +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + 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 + let prev = unsafe { self.prev_index.get_unchecked() }; + let curr = unsafe { self.index.get_unchecked() }; + + // Re-point the previous index + self.list.node_at_mut(prev).next = self.list.node_at_mut(curr).next; + + // Release the index into the free queue + self.list.node_at_mut(curr).next = self.list.free; + self.list.free = self.index; + + self.list.extract_data_in_node_at(curr) + } + } + + /// This will pop the element from the list. + /// + /// Complexity is worst-case `O(1)`. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// ll.push(1).unwrap(); + /// ll.push(2).unwrap(); + /// ll.push(3).unwrap(); + /// + /// // Find a value and update it + /// let mut find = ll.find_mut(|v| *v == 2).unwrap(); + /// find.pop(); + /// + /// assert_eq!(ll.pop(), Ok(3)); + /// assert_eq!(ll.pop(), Ok(1)); + /// assert_eq!(ll.pop(), Err(())); + /// ``` + #[inline] + pub fn pop(mut self) -> T { + self.pop_internal() + } + + /// This will resort the element into the correct position in the list if needed. The resorting + /// will only happen if the element has been accessed mutably. + /// + /// Same as calling `drop`. + /// + /// Complexity is worst-case `O(N)`. + /// + /// # Example + /// + /// ``` + /// use heapless::sorted_linked_list::{SortedLinkedList, Max}; + /// let mut ll: SortedLinkedList<_, _, Max, 3> = SortedLinkedList::new_usize(); + /// + /// ll.push(1).unwrap(); + /// ll.push(2).unwrap(); + /// ll.push(3).unwrap(); + /// + /// let mut find = ll.find_mut(|v| *v == 2).unwrap(); + /// find.finish(); // No resort, we did not access the value. + /// + /// let mut find = ll.find_mut(|v| *v == 2).unwrap(); + /// *find += 1000; + /// find.finish(); // Will resort, we accessed (and updated) the value. + /// + /// assert_eq!(ll.pop(), Ok(1002)); + /// assert_eq!(ll.pop(), Ok(3)); + /// assert_eq!(ll.pop(), Ok(1)); + /// assert_eq!(ll.pop(), Err(())); + /// ``` + #[inline] + pub fn finish(self) { + drop(self) + } +} + +impl Drop for FindMut<'_, T, Idx, Kind, N> +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + 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 Deref for FindMut<'_, T, Idx, Kind, N> +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + self.list + .read_data_in_node_at(unsafe { self.index.get_unchecked() }) + } +} + +impl DerefMut for FindMut<'_, T, Idx, Kind, N> +where + T: Ord, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.maybe_changed = true; + self.list + .read_mut_data_in_node_at(unsafe { self.index.get_unchecked() }) + } +} + +// /// Useful for debug during development. +// impl fmt::Debug for FindMut<'_, T, Idx, Kind, N> +// where +// T: Ord + core::fmt::Debug, +// Idx: SortedLinkedListIndex, +// Kind: LLKind, +// { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// f.debug_struct("FindMut") +// .field("prev_index", &self.prev_index.option()) +// .field("index", &self.index.option()) +// .field( +// "prev_value", +// &self +// .list +// .read_data_in_node_at(self.prev_index.option().unwrap()), +// ) +// .field( +// "value", +// &self.list.read_data_in_node_at(self.index.option().unwrap()), +// ) +// .finish() +// } +// } + +impl fmt::Debug for SortedLinkedList +where + T: Ord + core::fmt::Debug, + Idx: SortedLinkedListIndex, + Kind: LLKind, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +impl Drop for SortedLinkedList +where + Idx: SortedLinkedListIndex, +{ + fn drop(&mut self) { + let mut index = self.head; + + while let Some(i) = index.option() { + let node = self.node_at_mut(i); + index = node.next; + + unsafe { + ptr::drop_in_place(node.val.as_mut_ptr()); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn const_new() { + static mut _V1: SortedLinkedList = SortedLinkedList::new_u8(); + static mut _V2: SortedLinkedList = + SortedLinkedList::new_u16(); + static mut _V3: SortedLinkedList = + SortedLinkedList::new_usize(); + } + + #[test] + fn test_peek() { + let mut ll: SortedLinkedList = SortedLinkedList::new_usize(); + + 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: SortedLinkedList = SortedLinkedList::new_usize(); + + 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: SortedLinkedList = SortedLinkedList::new_usize(); + ll.push(1).unwrap(); + ll.push(2).unwrap(); + ll.push(3).unwrap(); + + assert!(ll.is_full()) + } + + #[test] + fn test_empty() { + let ll: SortedLinkedList = SortedLinkedList::new_usize(); + + assert!(ll.is_empty()) + } + + #[test] + fn test_zero_size() { + let ll: SortedLinkedList = SortedLinkedList::new_usize(); + + assert!(ll.is_empty()); + assert!(ll.is_full()); + } + + #[test] + fn test_rejected_push() { + let mut ll: SortedLinkedList = SortedLinkedList::new_usize(); + 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: SortedLinkedList = SortedLinkedList::new_usize(); + 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); + } + + #[test] + fn test_updating_1() { + let mut ll: SortedLinkedList = SortedLinkedList::new_usize(); + ll.push(1).unwrap(); + + let v = ll.pop().unwrap(); + + assert_eq!(v, 1); + } + + #[test] + fn test_updating_2() { + let mut ll: SortedLinkedList = SortedLinkedList::new_usize(); + ll.push(1).unwrap(); + + let mut find = ll.find_mut(|v| *v == 1).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1001); + } +}