use alloc::boxed::Box;
use core::marker::PhantomData;
use core::ptr::NonNull;
pub trait SListItemOwned<Tag>: Sized {
fn get_node(&mut self) -> &mut Option<NonNull<Self>>;
fn append(&mut self, mut l: SLinkedListOwned<Self, Tag>) {
let node = self.get_node();
assert!(node.is_none(), "can only append to a tail node");
*node = l.head.take();
l.length = 0;
l.tail = None;
}
fn pop(&mut self) -> Option<Box<Self>> {
let node = self.get_node();
if let Some(next_ptr) = node {
let mut popped_box: Box<Self> = unsafe { Box::from_raw(next_ptr.as_ptr()) };
*node = popped_box.get_node().take(); Some(popped_box)
} else {
None
}
}
fn consume_all(&mut self) {
let mut current = self.get_node().take();
while let Some(node_ptr) = current {
let mut node_box: Box<Self> = unsafe { Box::from_raw(node_ptr.as_ptr()) };
current = node_box.get_node().take();
}
}
}
#[repr(C)]
pub struct SLinkedListOwned<T, Tag>
where
T: SListItemOwned<Tag>,
{
length: usize,
head: Option<NonNull<T>>,
tail: Option<NonNull<T>>,
_phan: PhantomData<fn(&Tag)>,
}
unsafe impl<T, Tag> Send for SLinkedListOwned<T, Tag> where T: SListItemOwned<Tag> {}
impl<T, Tag> SLinkedListOwned<T, Tag>
where
T: SListItemOwned<Tag>,
{
#[inline(always)]
pub const fn new() -> Self {
SLinkedListOwned { length: 0, head: None, tail: None, _phan: PhantomData }
}
pub fn clear(&mut self) {
while self.pop_front().is_some() {}
}
#[inline(always)]
pub fn len(&self) -> usize {
self.length
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.length == 0
}
#[inline]
pub fn push_front(&mut self, mut item: Box<T>) {
debug_assert!(item.get_node().is_none(), "Node is already in a list");
*item.get_node() = self.head;
let ptr = NonNull::from(Box::leak(item));
self.head = Some(ptr); if self.tail.is_none() {
self.tail = Some(ptr);
}
self.length += 1;
}
#[inline]
pub fn push_back(&mut self, mut item: Box<T>) {
debug_assert!(item.get_node().is_none(), "Node is already in a list");
let ptr = NonNull::from(Box::leak(item));
let old_tail = self.tail.replace(ptr);
match old_tail {
Some(mut old_tail_ptr) => {
unsafe {
*old_tail_ptr.as_mut().get_node() = Some(ptr);
}
}
None => {
debug_assert!(self.head.is_none());
self.head = Some(ptr);
}
}
self.length += 1;
}
pub fn pop_front(&mut self) -> Option<Box<T>> {
if let Some(head_ptr) = self.head {
let mut head_box: Box<T> = unsafe { Box::from_raw(head_ptr.as_ptr()) };
self.head = *head_box.get_node();
if self.head.is_none() {
self.tail = None;
}
self.length -= 1;
*head_box.get_node() = None;
Some(head_box)
} else {
None
}
}
#[inline]
pub fn get_front(&self) -> Option<&T> {
self.head.map(|ptr| unsafe { ptr.as_ref() })
}
#[inline]
pub fn get_front_mut(&mut self) -> Option<&mut T> {
self.head.map(|mut ptr| unsafe { ptr.as_mut() })
}
#[inline]
pub fn get_back(&self) -> Option<&T> {
self.tail.map(|ptr| unsafe { ptr.as_ref() })
}
#[inline]
pub fn get_back_mut(&mut self) -> Option<&mut T> {
self.tail.map(|mut ptr| unsafe { ptr.as_mut() })
}
#[inline(always)]
pub fn is_front(&self, node: &T) -> bool {
self.head.is_some_and(|head| core::ptr::eq(head.as_ptr(), node))
}
#[inline(always)]
pub fn drain(&mut self) -> SLinkedListOwnedDrainer<'_, T, Tag> {
SLinkedListOwnedDrainer { list: self }
}
#[inline(always)]
pub fn iter_mut(&mut self) -> SLinkedListOwnedIterMut<'_, T, Tag> {
SLinkedListOwnedIterMut { cur: self.head, _phan: PhantomData }
}
}
impl<T, Tag> Default for SLinkedListOwned<T, Tag>
where
T: SListItemOwned<Tag>,
{
fn default() -> Self {
Self::new()
}
}
impl<T, Tag> Drop for SLinkedListOwned<T, Tag>
where
T: SListItemOwned<Tag>,
{
fn drop(&mut self) {
self.drain().for_each(drop);
}
}
pub struct SLinkedListOwnedDrainer<'a, T, Tag>
where
T: SListItemOwned<Tag>,
{
list: &'a mut SLinkedListOwned<T, Tag>,
}
impl<'a, T, Tag> Iterator for SLinkedListOwnedDrainer<'a, T, Tag>
where
T: SListItemOwned<Tag>,
{
type Item = Box<T>;
#[inline]
fn next(&mut self) -> Option<Box<T>> {
self.list.pop_front()
}
}
pub struct SLinkedListOwnedIterMut<'a, T, Tag>
where
T: SListItemOwned<Tag>,
{
cur: Option<NonNull<T>>,
_phan: PhantomData<(&'a mut T, fn(&Tag))>,
}
impl<'a, T, Tag> Iterator for SLinkedListOwnedIterMut<'a, T, Tag>
where
T: SListItemOwned<Tag> + 'a,
{
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
if let Some(mut cur_ptr) = self.cur {
let cur_mut = unsafe { cur_ptr.as_mut() };
self.cur = *cur_mut.get_node();
Some(cur_mut)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::sync::atomic::{AtomicUsize, Ordering};
pub struct TestTag;
#[derive(Debug)]
pub struct TestNode {
pub value: i64,
pub next: Option<NonNull<TestNode>>,
}
static ACTIVE_NODE_COUNT: AtomicUsize = AtomicUsize::new(0);
impl Drop for TestNode {
fn drop(&mut self) {
ACTIVE_NODE_COUNT.fetch_sub(1, Ordering::SeqCst);
}
}
impl SListItemOwned<TestTag> for TestNode {
fn get_node(&mut self) -> &mut Option<NonNull<Self>> {
&mut self.next
}
}
fn new_node(v: i64) -> TestNode {
ACTIVE_NODE_COUNT.fetch_add(1, Ordering::SeqCst);
TestNode { value: v, next: None }
}
#[test]
fn test_push_back_pop_front() {
ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
let mut l = SLinkedListOwned::<TestNode, TestTag>::new();
l.push_back(Box::new(new_node(1)));
l.push_back(Box::new(new_node(2)));
l.push_back(Box::new(new_node(3)));
assert_eq!(3, l.len());
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
assert!(!l.is_empty());
assert_eq!(l.get_front().unwrap().value, 1);
assert_eq!(l.get_back().unwrap().value, 3);
let n1 = l.pop_front();
assert!(n1.is_some());
assert_eq!(n1.unwrap().value, 1);
assert_eq!(l.len(), 2);
let n2 = l.pop_front();
assert!(n2.is_some());
assert_eq!(n2.unwrap().value, 2);
assert_eq!(l.len(), 1);
let n3 = l.pop_front();
assert!(n3.is_some());
assert_eq!(n3.unwrap().value, 3);
assert_eq!(l.len(), 0);
assert!(l.pop_front().is_none());
assert!(l.is_empty());
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
}
#[test]
fn test_push_front() {
ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
let mut l = SLinkedListOwned::<TestNode, TestTag>::new();
l.push_front(Box::new(new_node(1))); assert_eq!(l.len(), 1);
assert_eq!(l.get_front().unwrap().value, 1);
assert_eq!(l.get_back().unwrap().value, 1);
l.push_front(Box::new(new_node(2))); assert_eq!(l.len(), 2);
assert_eq!(l.get_front().unwrap().value, 2);
assert_eq!(l.get_back().unwrap().value, 1);
l.push_front(Box::new(new_node(3))); assert_eq!(l.len(), 3);
assert_eq!(l.get_front().unwrap().value, 3);
assert_eq!(l.get_back().unwrap().value, 1);
assert_eq!(l.pop_front().unwrap().value, 3);
assert_eq!(l.pop_front().unwrap().value, 2);
assert_eq!(l.pop_front().unwrap().value, 1);
assert!(l.is_empty());
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
}
#[test]
fn test_drain() {
ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
let mut l = SLinkedListOwned::<TestNode, TestTag>::new();
l.push_back(Box::new(new_node(10)));
l.push_back(Box::new(new_node(20)));
l.push_back(Box::new(new_node(30)));
assert_eq!(l.len(), 3);
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
{
let mut drain = l.drain();
assert_eq!(drain.next().unwrap().value, 10);
assert_eq!(drain.next().unwrap().value, 20);
assert_eq!(drain.next().unwrap().value, 30);
assert!(drain.next().is_none());
}
assert_eq!(l.len(), 0);
assert!(l.is_empty());
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
}
#[test]
fn test_iter_mut() {
ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
let mut l = SLinkedListOwned::<TestNode, TestTag>::new();
l.push_back(Box::new(new_node(100)));
l.push_back(Box::new(new_node(200)));
l.push_back(Box::new(new_node(300)));
let mut iter = l.iter_mut();
let n1 = iter.next().unwrap();
assert_eq!(n1.value, 100);
n1.value = 101;
let n2 = iter.next().unwrap();
assert_eq!(n2.value, 200);
n2.value = 201;
let n3 = iter.next().unwrap();
assert_eq!(n3.value, 300);
n3.value = 301;
assert!(iter.next().is_none());
assert_eq!(l.pop_front().unwrap().value, 101);
assert_eq!(l.pop_front().unwrap().value, 201);
assert_eq!(l.pop_front().unwrap().value, 301);
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
}
#[test]
fn test_clear() {
ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
let mut l = SLinkedListOwned::<TestNode, TestTag>::new();
l.push_back(Box::new(new_node(1)));
l.push_back(Box::new(new_node(2)));
assert_eq!(l.len(), 2);
l.clear();
assert!(l.is_empty());
assert_eq!(l.len(), 0);
assert!(l.get_front().is_none());
assert!(l.get_back().is_none());
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
}
#[test]
fn test_drop() {
ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
{
let mut l = SLinkedListOwned::<TestNode, TestTag>::new();
l.push_back(Box::new(new_node(1)));
l.push_back(Box::new(new_node(2)));
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 2);
}
assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
}
}