diff --git a/README.md b/README.md index 922c770..0902ae4 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A binary-compatible EASTL implementation written in Rust - `Queue` - `VectorMap` - `FixedVector` +- `List` * there might be some things missing :) @@ -22,7 +23,6 @@ A binary-compatible EASTL implementation written in Rust ## Planned containers - `HashMultimap` - `HashMultiset` -- `List` - `Map` - `Multimap` - `Set` diff --git a/src/lib.rs b/src/lib.rs index 2473c9b..004e0a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,3 +15,4 @@ pub mod set; pub mod string; pub mod vector; pub mod vector_map; +pub mod list; diff --git a/src/list/iter.rs b/src/list/iter.rs new file mode 100644 index 0000000..88e799d --- /dev/null +++ b/src/list/iter.rs @@ -0,0 +1,78 @@ +use crate::list::node::{ListNode, ListNodeBase}; +use std::marker::PhantomData; + +/// Iterator over `eastl::List`, yielding references in the list's order +pub struct Iter<'a, T: 'a> { + sentinel_node: *const ListNodeBase, + current_node: *mut ListNodeBase, + len: usize, + marker: PhantomData<&'a ListNode>, +} + +impl<'a, T> Iter<'a, T> { + pub(crate) fn new(sentinel_node: *const ListNodeBase, len: usize) -> Self { + Self { + sentinel_node, + current_node: sentinel_node.cast_mut(), + len, + marker: PhantomData, + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if unsafe { (*self.current_node).next.cast_const() } == self.sentinel_node { + None + } else { + self.len -= 1; + self.current_node = unsafe { (*self.current_node).next }; + let node = self.current_node as *const ListNode; + Some(unsafe { (*node).value() }) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +/// Iterator over `eastl::List`, yielding mutable references in the list's order +pub struct IterMut<'a, T: 'a> { + sentinel_node: *const ListNodeBase, + current_node: *mut ListNodeBase, + len: usize, + marker: PhantomData<&'a mut ListNode>, +} + +impl<'a, T> IterMut<'a, T> { + pub(crate) fn new(sentinel_node: *const ListNodeBase, len: usize) -> Self { + Self { + sentinel_node, + current_node: sentinel_node.cast_mut(), + len, + marker: PhantomData, + } + } +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + if unsafe { (*self.current_node).next.cast_const() } == self.sentinel_node { + None + } else { + self.len -= 1; + self.current_node = unsafe { (*self.current_node).next }; + let node = self.current_node as *mut ListNode; + Some(unsafe { (*node).value_mut() }) + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} diff --git a/src/list/mod.rs b/src/list/mod.rs new file mode 100644 index 0000000..9ac5568 --- /dev/null +++ b/src/list/mod.rs @@ -0,0 +1,450 @@ +use crate::allocator::{Allocator, DefaultAllocator}; +use crate::list::iter::{Iter, IterMut}; +use crate::list::node::{ListNode, ListNodeBase}; +use moveit::{new, New}; +use std::marker::PhantomData; +use std::mem::size_of; +use std::{fmt, ptr}; + +mod iter; +mod node; + +/// List with the default allocator. +pub type DefaultList = List; + +/// A doubly linked list. +/// The API is modelled after `std::collections::LinkedList`. +#[repr(C)] +pub struct List { + /// Sentinel node, contains the front and back node pointers (prev = back, next = front) + node: ListNodeBase, + size: u32, + allocator: A, + _holds_data: PhantomData, +} + +impl List { + /// Create a new, empty list. + /// + /// # Arguments + /// `allocator`: The allocator to use + /// + /// # Safety + /// The resulting list must not be moved. + pub unsafe fn new_in(allocator: A) -> impl New { + new::of(Self { + node: ListNodeBase::default(), + size: 0, + allocator, + _holds_data: PhantomData, + }) + .with(|this| { + let this = this.get_unchecked_mut(); + this.init_sentinel_node(); + }) + } + + /// Get a reference to the last value, if any + /// + /// # Return + /// A reference to the last value if present, `None` if the list is empty. + pub fn back(&self) -> Option<&T> { + if self.node.prev.cast_const() != &self.node { + // this cast works as the base is the first struct member of ListNode + Some(unsafe { &((*(self.node.prev as *const ListNode)).value) }) + } else { + None + } + } + + /// Get a mutable reference to the last value, if any + /// + /// # Return + /// A mutable reference to the last value if present, `None` if the list is empty. + pub fn back_mut(&mut self) -> Option<&mut T> { + if self.node.prev.cast_const() != &self.node { + // this cast works as the base is the first struct member of ListNode + Some(unsafe { &mut ((*(self.node.prev as *mut ListNode)).value) }) + } else { + None + } + } + + /// Remove all elements from this list + pub fn clear(&mut self) { + let mut next = self.node.next; + unsafe { + while next != &mut self.node { + let to_drop = next; + // Advance the next pointer before we delete the current node + next = (*next).next; + // Drop the value + ptr::drop_in_place(&mut (*(to_drop as *mut ListNode)).value); + // Deallocate the memory for the node + self.allocator.deallocate(to_drop, size_of::>()); + } + } + self.init_sentinel_node(); + self.size = 0; + } + + /// If the list is empty or not + pub fn empty(&self) -> bool { + self.size() == 0 + } + + /// Get a reference to the first value, if any + /// + /// # Return + /// A reference to the first value if present, `None` if the list is empty. + pub fn front(&self) -> Option<&T> { + if self.node.next.cast_const() != &self.node { + // this cast works as the base is the first struct member of ListNode + Some(unsafe { (*(self.node.next as *const ListNode)).value() }) + } else { + None + } + } + + /// Get a mutable reference to the first value, if any + /// + /// # Return + /// A mutable reference to the first value if present, `None` if the list is empty. + pub fn front_mut(&mut self) -> Option<&mut T> { + if self.node.next.cast_const() != &self.node { + // this cast works as the base is the first struct member of ListNode + Some(unsafe { &mut ((*(self.node.next as *mut ListNode)).value) }) + } else { + None + } + } + + /// Return a forward iterator for this list + pub fn iter(&self) -> Iter<'_, T> { + Iter::new(&self.node, self.size()) + } + + /// Return a mutable forward iterator for this list + pub fn iter_mut(&self) -> IterMut<'_, T> { + IterMut::new(&self.node, self.size()) + } + + /// Removes the last element in the list, returning its value + /// + /// # Return + /// The last value if present, `None` if the list is empty. + pub fn pop_back(&mut self) -> Option { + if self.node.prev.cast_const() != &self.node { + let value = unsafe { self.remove_node(self.node.prev) }; + Some(value) + } else { + None + } + } + + /// Removes the first element in the list, returning its value + /// + /// # Return + /// The first value if present, `None` if the list is empty. + pub fn pop_front(&mut self) -> Option { + if self.node.next.cast_const() != &self.node { + let value = unsafe { self.remove_node(self.node.next) }; + Some(value) + } else { + None + } + } + + /// Push a value to the back of the list + pub fn push_back(&mut self, value: T) { + unsafe { + let new_node = self.create_node(value); + (*new_node).base.insert(&mut self.node); + } + self.size += 1; + } + + /// Push a value to the front of the list + pub fn push_front(&mut self, value: T) { + unsafe { + let new_node = self.create_node(value); + (*new_node).base.insert(self.node.next); + } + + self.size += 1; + } + + /// Get the list's size + pub fn size(&self) -> usize { + self.size as usize + } + + // Allocate and initialise a new node + unsafe fn create_node(&mut self, value: T) -> *mut ListNode { + let node = unsafe { + self.allocator + .allocate::>(size_of::>()) + .as_mut() + } + .unwrap(); + ptr::write(node.value_mut(), value); + node + } + + // Init list sentinel node + fn init_sentinel_node(&mut self) { + self.node.prev = &mut self.node; + self.node.next = &mut self.node; + } + + /// Removes the given node, extracting its value + unsafe fn remove_node(&mut self, node: *mut ListNodeBase) -> T { + (*node).remove(); + let value = ptr::read(&(*(node as *mut ListNode)).value); + // Deallocate the memory for the node + self.allocator.deallocate(node, size_of::>()); + self.size -= 1; + value + } +} + +impl Drop for List { + fn drop(&mut self) { + self.clear() + } +} + +impl fmt::Debug for List { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +impl List { + /// Create a new, empty list + /// + /// # Safety + /// The resulting list must not be moved + pub unsafe fn new() -> impl New { + Self::new_in(A::default()) + } +} + +impl Extend for List { + fn extend>(&mut self, iter: I) { + for item in iter { + self.push_back(item) + } + } +} + +impl<'a, T: 'a + Copy, A: Allocator + Default> Extend<&'a T> for List { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } +} + +#[cfg(test)] +mod test { + use crate::list::DefaultList; + use moveit::moveit; + + #[test] + fn empty() { + moveit! { + let mut list = unsafe { DefaultList::::new() }; + } + assert!(list.empty()); + } + + #[test] + fn size_empty() { + moveit! { + let mut list = unsafe { DefaultList::::new() }; + } + assert_eq!(list.size(), 0); + } + + #[test] + fn front_empty() { + moveit! { + let mut list = unsafe { DefaultList::::new() }; + } + assert_eq!(list.front(), None) + } + + #[test] + fn back_empty() { + moveit! { + let mut list = unsafe { DefaultList::::new() }; + } + assert_eq!(list.back(), None) + } + + #[test] + fn push_front() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + list.push_front(12u32); + assert_eq!(list.size(), 1); + assert_eq!(list.front(), Some(&12u32)); + assert_eq!(list.back(), Some(&12u32)); + list.push_front(6u32); + assert_eq!(list.size(), 2); + assert_eq!(list.front(), Some(&6u32)); + assert_eq!(list.back(), Some(&12u32)); + } + + #[test] + fn push_front_owned_value() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + let hello = "hello".to_string(); + let world = "world".to_string(); + list.push_front(world.clone()); + assert_eq!(list.size(), 1); + assert_eq!(list.front(), Some(&world)); + assert_eq!(list.back(), Some(&world)); + list.push_front(hello.clone()); + assert_eq!(list.size(), 2); + assert_eq!(list.front(), Some(&hello)); + assert_eq!(list.back(), Some(&world)); + } + + #[test] + fn push_back() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + let hello = "hello".to_string(); + let world = "world".to_string(); + list.push_back(hello.clone()); + assert_eq!(list.size(), 1); + assert_eq!(list.front(), Some(&hello)); + assert_eq!(list.back(), Some(&hello)); + list.push_back(world.clone()); + assert_eq!(list.size(), 2); + assert_eq!(list.front(), Some(&hello)); + assert_eq!(list.back(), Some(&world)); + } + + #[test] + fn modify_front() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + list.push_front("hello".to_string()); + let world = "world".to_string(); + *list.front_mut().unwrap() = world.clone(); + assert_eq!(list.back(), Some(&world)); + } + + #[test] + fn clear() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + list.push_back(12u32); + list.push_back(6u32); + assert_eq!(list.size(), 2); + list.clear(); + assert!(list.empty()); + assert_eq!(list.front(), None); + assert_eq!(list.back(), None); + } + + struct Test<'a> { + r: &'a mut u32, + } + + impl<'a> Drop for Test<'a> { + fn drop(&mut self) { + *self.r *= 2; + } + } + + #[test] + fn drop() { + let mut foo = 1; + let mut bar = 1; + { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + list.push_back(Test { r: &mut foo }); + list.push_back(Test { r: &mut bar }); + } + assert_eq!(foo, 2); + assert_eq!(bar, 2); + } + + #[test] + fn iter() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + let mut iter = list.iter(); + assert_eq!(iter.next(), None); + assert_eq!(iter.next(), None); + assert_eq!(iter.size_hint(), (0, Some(0))); + list.push_back(12u32); + list.push_back(6u32); + let iter = list.iter(); + assert_eq!(iter.size_hint(), (2, Some(2))); + let vec = iter.collect::>(); + assert_eq!(vec, vec![&12u32, &6u32]); + } + #[test] + fn iter_mut() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + list.push_back(12u32); + list.push_back(6u32); + let mut iter = list.iter_mut(); + let first_val = iter.next().unwrap(); + assert_eq!(first_val, &mut 12u32); + *first_val = 14u32; + let mut iter = list.iter_mut(); + let first_val = iter.next().unwrap(); + assert_eq!(first_val, &mut 14u32); + let last_val = iter.last().unwrap(); + assert_eq!(last_val, &mut 6u32); + } + + #[test] + fn pop_front() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + let hello = "hello".to_string(); + let world = "world".to_string(); + list.push_back(hello.clone()); + list.push_back(world.clone()); + assert_eq!(list.size(), 2); + assert_eq!(list.pop_front(), Some(hello)); + assert_eq!(list.size(), 1); + assert_eq!(list.pop_front(), Some(world)); + assert!(list.empty()); + assert_eq!(list.pop_front(), None); + } + + #[test] + fn pop_back() { + moveit! { + let mut list = unsafe { DefaultList::new() }; + } + let hello = "hello".to_string(); + let world = "world".to_string(); + list.push_back(hello.clone()); + list.push_back(world.clone()); + assert_eq!(list.size(), 2); + assert_eq!(list.pop_back(), Some(world)); + assert_eq!(list.size(), 1); + assert_eq!(list.pop_back(), Some(hello)); + assert!(list.empty()); + assert_eq!(list.pop_front(), None); + } +} diff --git a/src/list/node.rs b/src/list/node.rs new file mode 100644 index 0000000..93067d9 --- /dev/null +++ b/src/list/node.rs @@ -0,0 +1,51 @@ +use std::ptr::null_mut; + +#[repr(C)] +pub(crate) struct ListNodeBase { + pub(crate) next: *mut ListNodeBase, + pub(crate) prev: *mut ListNodeBase, +} + +impl Default for ListNodeBase { + fn default() -> Self { + Self { + next: null_mut(), + prev: null_mut(), + } + } +} + +impl ListNodeBase { + /// Inserts this node in `next`s list before `next`. + pub(crate) unsafe fn insert(&mut self, next: *mut ListNodeBase) { + self.next = next; + self.prev = (*next).prev; + (*(*next).prev).next = self; + (*next).prev = self; + } + + // Removes this node from the list that it's in. Assumes that the + // node is within a list and thus that its prev/next pointers are valid. + pub(crate) unsafe fn remove(&mut self) { + (*self.next).prev = self.prev; + (*self.prev).next = self.next; + } +} + +#[repr(C)] +pub(crate) struct ListNode { + pub(crate) base: ListNodeBase, + pub(crate) value: T, +} + +impl ListNode { + /// Get a reference to the contained value + pub(crate) fn value(&self) -> &T { + &self.value + } + + /// Get a mutable reference to the contained value + pub(crate) fn value_mut(&mut self) -> &mut T { + &mut self.value + } +}