This commit is contained in:
nora 2021-08-08 16:41:43 +02:00
parent a0b2f91a18
commit e41d110dd7
3 changed files with 144 additions and 60 deletions

View file

@ -6,4 +6,9 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dev-dependencies]
criterion = "0.3.5"
[[bench]]
name = "linked_list"
harness = false

59
benches/linked_list.rs Normal file
View file

@ -0,0 +1,59 @@
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use datastructures::linked_list::LinkedList;
fn create_random_list(size: usize) -> LinkedList<i32> {
let mut number = 837582573;
let mut list = LinkedList::new();
for _ in 0..size {
// just random stuff I cam up with, does not need to be actually random
number = (number ^ (number << 5)) >> 3;
list.push_back(number);
}
list
}
fn create_random_std_list(size: usize) -> std::collections::LinkedList<i32> {
let mut number = 837582573;
let mut list = std::collections::LinkedList::new();
for _ in 0..size {
// just random stuff I cam up with, does not need to be actually random
number = (number ^ (number << 5)) >> 3;
list.push_back(number);
}
list
}
fn list_length(list: &LinkedList<i32>) -> usize {
list.len()
}
fn bench_list_length(c: &mut Criterion) {
let list = create_random_list(100);
c.bench_function("Short list length", |b| {
b.iter(|| list_length(black_box(&list)))
});
let list = create_random_list(10_000_000);
c.bench_function("Long list length", |b| {
b.iter(|| list_length(black_box(&list)))
});
}
fn push_back(c: &mut Criterion) {
let mut group = c.benchmark_group("push_back");
for i in [100, 10_000_000].iter() {
group.bench_with_input(BenchmarkId::new("create_random_std_list", i), i, |b, i| {
b.iter(|| create_random_std_list(*i))
});
group.bench_with_input(BenchmarkId::new("create_random_list", i), i, |b, i| {
b.iter(|| create_random_list(*i))
});
}
group.finish();
}
criterion_group!(
name = benches;
config = Criterion::default();
targets = bench_list_length, push_back
);
criterion_main!(benches);

View file

@ -11,27 +11,28 @@ use std::ptr::NonNull;
/// ///
/// # How to use /// # How to use
/// ``` /// ```
/// use datastructures::linked_list::LinkedList; /// # use datastructures::linked_list::LinkedList;
/// /// #
/// let mut list = LinkedList::new(); /// let mut list = LinkedList::new();
/// list.push_front("hello"); /// list.push_front("hello");
/// assert_eq!(list.get(0), Some(&"hello")); /// assert_eq!(list.get(0), Some(&"hello"));
/// list.push_end("bye"); /// list.push_back("bye");
/// assert_eq!(list.get(1), Some(&"bye")); /// assert_eq!(list.get(1), Some(&"bye"));
/// ``` /// ```
/// ///
/// The list can also be edited using the `Node` methods /// The list can also be edited using the `Node` methods
/// ``` /// ```
/// use datastructures::linked_list::LinkedList; /// # use datastructures::linked_list::LinkedList;
/// /// #
/// let mut list = LinkedList::new(); /// let mut list = LinkedList::new();
///
/// list.push_front(1); /// list.push_front(1);
/// let mut node = list.get_head_node_mut().unwrap(); /// let mut node = list.get_mut_head_node().unwrap();
/// node.push_after(3); /// node.push_after(3);
/// node.push_after(2); /// node.push_after(2);
/// let next = node.get_next().unwrap(); /// let next = node.next().unwrap();
/// let next = next.get_next().unwrap(); /// let next = next.next().unwrap();
/// assert_eq!(*next.get_value(), 3); /// assert_eq!(*next.get(), 3);
/// ``` /// ```
/// ///
/// # Note /// # Note
@ -61,7 +62,6 @@ impl<T> LinkedList<T> {
value: element, value: element,
next: None, next: None,
prev: None, prev: None,
_marker: PhantomData,
}); });
self.start = Some(new_node); self.start = Some(new_node);
self.end = Some(new_node); self.end = Some(new_node);
@ -72,7 +72,6 @@ impl<T> LinkedList<T> {
value: element, value: element,
next: Some(old_start), next: Some(old_start),
prev: None, prev: None,
_marker: PhantomData,
}); });
// SAFETY: All pointers should always be valid // SAFETY: All pointers should always be valid
unsafe { old_start.as_mut() }.prev = Some(new_node); unsafe { old_start.as_mut() }.prev = Some(new_node);
@ -82,14 +81,13 @@ impl<T> LinkedList<T> {
} }
/// Push an element to the end of the list (O(1)) /// Push an element to the end of the list (O(1))
pub fn push_end(&mut self, element: T) { pub fn push_back(&mut self, element: T) {
match self.end { match self.end {
None => { None => {
let new_node = allocate_nonnull(Node { let new_node = allocate_nonnull(Node {
value: element, value: element,
next: None, next: None,
prev: None, prev: None,
_marker: PhantomData,
}); });
self.start = Some(new_node); self.start = Some(new_node);
self.end = Some(new_node); self.end = Some(new_node);
@ -99,7 +97,6 @@ impl<T> LinkedList<T> {
value: element, value: element,
next: None, next: None,
prev: Some(old_end), prev: Some(old_end),
_marker: PhantomData,
}); });
// SAFETY: All pointers should always be valid // SAFETY: All pointers should always be valid
unsafe { old_end.as_mut() }.next = Some(new_node); unsafe { old_end.as_mut() }.next = Some(new_node);
@ -162,17 +159,17 @@ impl<T> LinkedList<T> {
self.end.as_ref().map(|nn| unsafe { nn.as_ref() }) 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 /// 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<T>> { pub fn get_mut_head_node(&mut self) -> Option<&mut Node<T>> {
self.start.as_mut().map(|nn| unsafe { nn.as_mut() }) 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 /// 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<T>> { pub fn get_mut_tail_node(&mut self) -> Option<&mut Node<T>> {
self.end.as_mut().map(|nn| unsafe { nn.as_mut() }) self.end.as_mut().map(|nn| unsafe { nn.as_mut() })
} }
/// Get a node from the list that can be used the edit the list /// 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<T>> { pub fn get_mut_node(&mut self, mut index: usize) -> Option<&mut Node<T>> {
let mut node = &mut self.start; let mut node = &mut self.start;
let mut result = None; let mut result = None;
while let Some(ref mut content) = node { while let Some(ref mut content) = node {
@ -188,6 +185,15 @@ impl<T> LinkedList<T> {
result result
} }
/// Calculates the length of the list
/// # Important
/// This implementation is O(n), since unlike in `std::collections::LinkedList`, the length of the list is not stored
/// (and can't be because the list can be modified through nodes - a node could theoretically have a reference to the list,
/// but that would make node extraction slower because you'd always have to construct a new struct.
pub fn len(&self) -> usize {
self.iter().count()
}
/// Returns an iterator over the items /// Returns an iterator over the items
pub fn iter(&self) -> Iter<T> { pub fn iter(&self) -> Iter<T> {
Iter::new(self) Iter::new(self)
@ -225,11 +231,11 @@ impl<T> Drop for LinkedList<T> {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use datastructures::linked_list::*; /// # use datastructures::linked_list::*;
/// /// #
/// let mut list = LinkedList::new(); /// let mut list = LinkedList::new();
/// list.push_front(1); /// list.push_front(1);
/// let mut node = list.get_node_mut(0); /// let mut node = list.get_mut_node(0);
/// ``` /// ```
/// ///
#[derive(Debug)] #[derive(Debug)]
@ -237,7 +243,6 @@ pub struct Node<T> {
value: T, value: T,
next: Option<NonNull<Node<T>>>, next: Option<NonNull<Node<T>>>,
prev: Option<NonNull<Node<T>>>, prev: Option<NonNull<Node<T>>>,
_marker: PhantomData<T>,
} }
impl<T> Node<T> { impl<T> Node<T> {
@ -247,7 +252,6 @@ impl<T> Node<T> {
value: element, value: element,
next: self.next, next: self.next,
prev: NonNull::new(self as _), prev: NonNull::new(self as _),
_marker: PhantomData,
})); }));
self.next.map(|mut next| { self.next.map(|mut next| {
// SAFETY: All pointers should always be valid and created from a box // SAFETY: All pointers should always be valid and created from a box
@ -262,7 +266,6 @@ impl<T> Node<T> {
value: element, value: element,
next: NonNull::new(self as _), next: NonNull::new(self as _),
prev: self.prev, prev: self.prev,
_marker: PhantomData,
})); }));
self.prev.map(|mut next| { self.prev.map(|mut next| {
// SAFETY: All pointers should always be valid and created from a box // SAFETY: All pointers should always be valid and created from a box
@ -272,43 +275,32 @@ impl<T> Node<T> {
} }
/// Get the next node /// Get the next node
pub fn get_next(&self) -> Option<&Node<T>> { pub fn next(&self) -> Option<&Node<T>> {
match &self.next { self.next.as_ref().map(|nn| unsafe { nn.as_ref() })
None => None,
Some(nn) => unsafe { Some(nn.as_ref()) },
}
} }
/// Get the next node mutably /// Get the next node mutably
pub fn get_next_mut(&mut self) -> Option<&mut Node<T>> { pub fn next_mut(&mut self) -> Option<&mut Node<T>> {
match &mut self.next { self.next.as_mut().map(|nn| unsafe { nn.as_mut() })
None => None,
Some(nn) => unsafe { Some(nn.as_mut()) },
}
} }
/// Get the previous node /// Get the previous node
pub fn get_previous(&self) -> Option<&Node<T>> { pub fn previous(&self) -> Option<&Node<T>> {
match &self.prev { self.prev.as_ref().map(|nn| unsafe { nn.as_ref() })
None => None,
Some(nn) => unsafe { Some(nn.as_ref()) },
}
} }
/// Get the previous node mutably /// Get the previous node mutably
pub fn get_previous_mut(&mut self) -> Option<&mut Node<T>> { pub fn previous_mut(&mut self) -> Option<&mut Node<T>> {
match &mut self.prev { self.prev.as_mut().map(|nn| unsafe { nn.as_mut() })
None => None,
Some(nn) => unsafe { Some(nn.as_mut()) },
}
} }
/// Gets the value from the node /// Gets the value from the node
pub fn get_value(&self) -> &T { pub fn get(&self) -> &T {
&self.value &self.value
} }
/// Gets the value from the node /// Gets the value from the node
pub fn set_value(&mut self, value: T) { pub fn set(&mut self, value: T) {
self.value = value; self.value = value;
} }
@ -316,6 +308,19 @@ impl<T> Node<T> {
pub fn replace_value(&mut self, value: T) -> T { pub fn replace_value(&mut self, value: T) -> T {
std::mem::replace(&mut self.value, value) std::mem::replace(&mut self.value, value)
} }
/// Removes a value from the List and returns it
pub fn remove(&mut self) -> T {
// SAFETY: All pointers should always be valid
unsafe {
self.next.map(|mut next| next.as_mut().prev = self.prev);
self.prev.map(|mut prev| prev.as_mut().next = self.next);
}
// SAFETY: A reference is always valid and we have the only one now
let node = unsafe { Box::from_raw(self) };
node.value
}
} }
fn allocate_nonnull<T>(element: T) -> NonNull<T> { fn allocate_nonnull<T>(element: T) -> NonNull<T> {
@ -377,11 +382,11 @@ mod test {
#[test] #[test]
fn push_start_end() { fn push_start_end() {
let mut list = LinkedList::new(); let mut list = LinkedList::new();
list.push_end(3); list.push_back(3);
list.push_front(2); list.push_front(2);
list.push_front(1); list.push_front(1);
list.push_end(4); list.push_back(4);
list.push_end(5); list.push_back(5);
let vec = list.iter().cloned().collect::<Vec<_>>(); let vec = list.iter().cloned().collect::<Vec<_>>();
assert_eq!(&vec[..], &[1, 2, 3, 4, 5]); assert_eq!(&vec[..], &[1, 2, 3, 4, 5]);
} }
@ -426,13 +431,13 @@ mod test {
fn node_operations() { fn node_operations() {
let mut list = LinkedList::new(); let mut list = LinkedList::new();
list.push_front(1); list.push_front(1);
list.push_end(2); list.push_back(2);
{ {
let node = list.get_node_mut(1).unwrap(); let node = list.get_mut_node(1).unwrap();
assert_eq!(*node.get_value(), 2); assert_eq!(*node.get(), 2);
node.push_after(4); node.push_after(4);
let next = node.get_next_mut().unwrap(); let next = node.next_mut().unwrap();
assert!(matches!(next.get_next(), None)); assert!(matches!(next.next(), None));
next.push_before(3) next.push_before(3)
} }
let vec = list.iter().cloned().collect::<Vec<_>>(); let vec = list.iter().cloned().collect::<Vec<_>>();
@ -443,13 +448,28 @@ mod test {
fn node_values() { fn node_values() {
let mut list = LinkedList::new(); let mut list = LinkedList::new();
list.push_front(1); list.push_front(1);
let node = list.get_node_mut(0).unwrap(); let node = list.get_mut_node(0).unwrap();
assert_eq!(*node.get_value(), 1); assert_eq!(*node.get(), 1);
assert_eq!(node.replace_value(2), 1); assert_eq!(node.replace_value(2), 1);
assert_eq!(*node.get_value(), 2); assert_eq!(*node.get(), 2);
node.push_after(3); node.push_after(3);
let node = node.get_next_mut().unwrap(); let node = node.next_mut().unwrap();
node.set_value(4); node.set(4);
assert_eq!(*node.get_value(), 4); assert_eq!(*node.get(), 4);
}
#[test]
fn node_removal() {
let mut list = LinkedList::new();
list.push_back(1);
list.push_back(2);
list.push_back(4);
let node_two = list.get_mut_head_node().unwrap().next_mut().unwrap();
node_two.replace_value(3);
let three = node_two.remove();
assert_eq!(three, 3);
assert_eq!(list.get_head(), Some(&1));
assert_eq!(list.get_tail(), Some(&4));
assert_eq!(*list.get_head_node().unwrap().next().unwrap().get(), 4);
} }
} }