Skip to main content

embed_slist/
lib.rs

1#![allow(rustdoc::redundant_explicit_links)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![cfg_attr(docsrs, allow(unused_attributes))]
4#![cfg_attr(not(feature = "std"), no_std)]
5
6//! An intrusive singly linked list implementation.
7//!
8//! This module provides `SLinkedList`, a singly linked list optimized for FIFO (First-In, First-Out)
9//! queue-like behavior. Elements embed the list nodes themselves, offering memory efficiency
10//! and explicit control over allocation.
11//!
12//! # Features
13//! - O(1) push to back and pop from front.
14//! - Generic over pointer types (`Box`, `Arc`, `NonNull`, raw pointers).
15//!
16//! # Example
17//!
18//! ```rust
19//! use embed_slist::{SLinkedList, SListItem, SListNode};
20//! use core::cell::UnsafeCell;
21//! use std::sync::Arc;
22//! use core::ptr::NonNull;
23//!
24//! struct MyTask {
25//!     priority: u8,
26//!     description: String,
27//!     node: UnsafeCell<SListNode<()>>,
28//! }
29//!
30//! impl MyTask {
31//!     fn new(priority: u8, desc: &str) -> Self {
32//!         MyTask {
33//!             priority,
34//!             description: desc.to_string(),
35//!             node: UnsafeCell::new(SListNode::default()),
36//!         }
37//!     }
38//! }
39//!
40//! unsafe impl SListItem<()> for MyTask {
41//!     fn get_node(&self) -> &mut SListNode<()> {
42//!         unsafe { &mut *self.node.get() }
43//!     }
44//! }
45//!
46//! // Using Box<T> (owned pointers)
47//! {
48//!     let mut task_queue = SLinkedList::<Box<MyTask>, ()>::new();
49//!     task_queue.push_back(Box::new(MyTask::new(1, "Handle user login")));
50//!     task_queue.push_back(Box::new(MyTask::new(2, "Process analytics data")));
51//!     task_queue.push_back(Box::new(MyTask::new(1, "Send welcome email")));
52//!     assert_eq!(task_queue.len(), 3);
53//!     assert_eq!(task_queue.pop_front().unwrap().description, "Handle user login");
54//!     assert_eq!(task_queue.pop_front().unwrap().description, "Process analytics data");
55//!     assert_eq!(task_queue.pop_front().unwrap().description, "Send welcome email");
56//!     assert!(task_queue.is_empty());
57//! }
58//!
59//! // Using Arc<T> (shared ownership)
60//! {
61//!     let mut task_queue = SLinkedList::<Arc<MyTask>, ()>::new();
62//!     task_queue.push_back(Arc::new(MyTask::new(1, "Handle user login")));
63//!     task_queue.push_back(Arc::new(MyTask::new(2, "Process analytics data")));
64//!     task_queue.push_back(Arc::new(MyTask::new(1, "Send welcome email")));
65//!     assert_eq!(task_queue.len(), 3);
66//!     assert_eq!(task_queue.pop_front().unwrap().description, "Handle user login");
67//!     assert_eq!(task_queue.pop_front().unwrap().description, "Process analytics data");
68//!     assert_eq!(task_queue.pop_front().unwrap().description, "Send welcome email");
69//!     assert!(task_queue.is_empty());
70//! }
71//!
72//! // Using NonNull<T> (raw pointers without ownership)
73//! {
74//!     let mut task_queue = SLinkedList::<NonNull<MyTask>, ()>::new();
75//!     let task1 = Box::into_raw(Box::new(MyTask::new(1, "Handle user login")));
76//!     let task2 = Box::into_raw(Box::new(MyTask::new(2, "Process analytics data")));
77//!     let task3 = Box::into_raw(Box::new(MyTask::new(1, "Send welcome email")));
78//!     task_queue.push_back(unsafe{NonNull::new_unchecked(task1)});
79//!     task_queue.push_back(unsafe{NonNull::new_unchecked(task2)});
80//!     task_queue.push_back(unsafe{NonNull::new_unchecked(task3)});
81//!     assert_eq!(task_queue.len(), 3);
82//!     assert_eq!(unsafe { &task_queue.pop_front().unwrap().as_ref().description }, "Handle user login");
83//!     assert_eq!(unsafe { &task_queue.pop_front().unwrap().as_ref().description }, "Process analytics data");
84//!     assert_eq!(unsafe { &task_queue.pop_front().unwrap().as_ref().description }, "Send welcome email");
85//!     assert!(task_queue.is_empty());
86//!     unsafe{
87//!         let _ = Box::from_raw(task1);
88//!         let _ = Box::from_raw(task2);
89//!         let _ = Box::from_raw(task3);
90//!     }
91//! }
92//! ```
93//!
94//! # Mixed Types
95//!
96//! This example show mixed multiple types (which have the same SListNode field) in one list,
97//! with custom pointer type.
98//!
99//! ```
100//! use embed_slist::{SLinkedList, SListItem, SListNode};
101//! use pointers::Pointer;
102//! use core::cell::UnsafeCell;
103//! use std::sync::Arc;
104//!
105//! enum MixedPointer {
106//!     Multi(Arc<ArcNode>),
107//!     One(Box<BoxNode>),
108//! }
109//!
110//! impl MixedPointer {
111//!
112//!     fn as_multi(&self) -> &Arc<ArcNode> {
113//!         if let Self::Multi(inner) = self {
114//!             inner
115//!         } else {
116//!             panic!("not Multi")
117//!         }
118//!     }
119//!
120//!     fn as_one(&self) -> &Box<BoxNode> {
121//!         if let Self::One(inner) = self {
122//!             inner
123//!         } else {
124//!             panic!("not One")
125//!         }
126//!     }
127//! }
128//!
129//! const IS_ARC: usize = 0x1;
130//! const TYPE_MASK: usize = IS_ARC;
131//!
132//! struct ArcNode {
133//!     _node: UnsafeCell<SListNode<()>>,
134//!     v: usize,
135//! }
136//!
137//! struct BoxNode {
138//!     _node: UnsafeCell<SListNode<()>>,
139//!     v: String,
140//! }
141//!
142//! impl ArcNode {
143//!     fn new(v: usize) -> Arc<Self> {
144//!         Arc::new(Self{
145//!             _node: Default::default(),
146//!             v,
147//!         })
148//!     }
149//! }
150//!
151//! impl BoxNode {
152//!     fn new(v: String) -> Box<Self> {
153//!         Box::new(Self{
154//!             _node: Default::default(),
155//!             v,
156//!         })
157//!     }
158//! }
159//!
160//! struct MixedAddr (usize);
161//!
162//! impl Pointer for MixedPointer {
163//!     type Target = MixedAddr;
164//!
165//!     fn as_ref(&self) -> &MixedAddr {
166//!         unreachable!("we don't call as_ref in SLinkedList");
167//!     }
168//!
169//!     /// # Safety
170//!     ///
171//!     /// just a mock
172//!     unsafe fn from_raw(p: *const MixedAddr) -> Self {
173//!         let real_p = (p as usize) & (!TYPE_MASK);
174//!         debug_assert!(real_p != 0);
175//!         let type_flag = (p as usize) & TYPE_MASK;
176//!         if type_flag == IS_ARC {
177//!             Self::Multi(unsafe{Arc::from_raw(real_p as *mut ArcNode)})
178//!         } else {
179//!             Self::One(unsafe{Box::from_raw(real_p as *mut BoxNode)})
180//!         }
181//!     }
182//!
183//!     /// # Safety
184//!     /// the address into_raw cannot be defer directly
185//!     fn into_raw(self) -> *const MixedAddr {
186//!         let addr: usize = match self {
187//!             MixedPointer::Multi(item)=>{Arc::into_raw(item) as usize | IS_ARC},
188//!             MixedPointer::One(item)=>{Box::into_raw(item) as usize},
189//!         };
190//!         addr as *const MixedAddr
191//!     }
192//! }
193//!
194//! unsafe impl SListItem<()> for MixedAddr {
195//!     
196//!     fn get_node(&self) -> &mut SListNode<()> {
197//!         unreachable!();
198//!     }
199//!
200//!     unsafe fn get_node_from_ptr<'a>(p: *mut u8) -> &'a mut SListNode<()>
201//!     where
202//!         Self: 'a,
203//!     {
204//!         let real_p = (p as usize) & (!TYPE_MASK);
205//!         debug_assert!(real_p != 0);
206//!         let type_flag = (p as usize) & TYPE_MASK;
207//!         if type_flag == IS_ARC {
208//!             unsafe{&mut *(*(real_p as *const ArcNode))._node.get()}
209//!         } else {
210//!             unsafe{&mut *(*(real_p as *const BoxNode))._node.get()}
211//!         }
212//!     }
213//! }
214//!
215//! // list operation
216//! let mut l = SLinkedList::<MixedPointer, ()>::new();
217//! l.push_back(MixedPointer::Multi(ArcNode::new(1)));
218//! l.push_back(MixedPointer::One(BoxNode::new("box1".to_string())));
219//! l.push_back(MixedPointer::One(BoxNode::new("box2".to_string())));
220//! l.push_back(MixedPointer::Multi(ArcNode::new(2)));
221//!
222//! let mut iter = l.iter();
223//! assert_eq!(iter.next().unwrap().as_multi().v, 1);
224//! assert_eq!(iter.next().unwrap().as_one().v.as_str(), "box1");
225//! assert_eq!(iter.next().unwrap().as_one().v.as_str(), "box2");
226//! assert_eq!(iter.next().unwrap().as_multi().v, 2);
227//! assert!(iter.next().is_none());
228//!
229//! let mut iter = l.drain();
230//! assert_eq!(iter.next().unwrap().as_multi().v, 1);
231//! assert_eq!(iter.next().unwrap().as_one().v.as_str(), "box1");
232//! assert_eq!(iter.next().unwrap().as_one().v.as_str(), "box2");
233//! assert_eq!(iter.next().unwrap().as_multi().v, 2);
234//! assert!(iter.next().is_none());
235//! ```
236
237extern crate alloc;
238#[cfg(any(feature = "std", test))]
239extern crate std;
240
241use core::borrow::BorrowMut;
242use core::fmt;
243use core::marker::PhantomData;
244use core::mem::{self, MaybeUninit};
245use core::ptr::{self, null_mut};
246use pointers::Pointer;
247
248/// A trait to return internal mutable SListNode for specified list.
249///
250/// The tag is used to distinguish different SListNodes within the same item.
251/// For only one ownership, you can use `()`.
252///
253/// # Safety
254/// Implementors must ensure `get_node` returns a valid reference to the `SListNode`
255/// embedded within `Self`. Users must use `UnsafeCell` to hold `SListNode` to support
256/// interior mutability required by list operations.
257pub unsafe trait SListItem<Tag>: Sized {
258    fn get_node(&self) -> &mut SListNode<Tag>;
259
260    /// # Safety
261    ///
262    /// This is just map from raw pointer to `&mut SListNode`.
263    ///
264    /// You only have to implement this when using custom pointer type with abnormal address.
265    /// Refer to [Mixed Type Example](index.html#mixed-types)
266    #[allow(unsafe_op_in_unsafe_fn)]
267    #[inline(always)]
268    unsafe fn get_node_from_ptr<'a>(p: *mut u8) -> &'a mut SListNode<Tag>
269    where
270        Self: 'a,
271    {
272        debug_assert!(!p.is_null());
273        (*(p as *const Self)).get_node()
274    }
275}
276
277/// The node structure that must be embedded in items to be stored in a `SLinkedList`.
278///
279#[repr(C)]
280pub struct SListNode<Tag> {
281    next: *mut u8,
282    _phan: PhantomData<fn(&Tag)>,
283}
284
285unsafe impl<Tag> Send for SListNode<Tag> {}
286
287impl<Tag> Default for SListNode<Tag> {
288    #[inline(always)]
289    fn default() -> Self {
290        Self { next: null_mut(), _phan: Default::default() }
291    }
292}
293
294impl<Tag> fmt::Debug for SListNode<Tag> {
295    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296        write!(f, "(")?;
297        if !self.next.is_null() {
298            write!(f, "next: {:p} ", self.next)?;
299        } else {
300            write!(f, "next: none ")?;
301        }
302        write!(f, ")")
303    }
304}
305
306/// A singly linked list with head and tail pointers (FIFO queue).
307///
308/// Supports O(1) push to back and pop from front.
309#[repr(C)]
310pub struct SLinkedList<P, Tag>
311where
312    P: Pointer,
313    P::Target: SListItem<Tag>,
314{
315    length: usize,
316    head: *mut u8,
317    tail: *mut u8,
318    _phan: PhantomData<fn(&P, &Tag)>,
319}
320
321unsafe impl<P, Tag> Send for SLinkedList<P, Tag>
322where
323    P: Pointer + Send,
324    P::Target: SListItem<Tag>,
325{
326}
327
328unsafe impl<P, Tag> Sync for SLinkedList<P, Tag>
329where
330    P: Pointer + Send,
331    P::Target: SListItem<Tag>,
332{
333}
334
335impl<P: fmt::Debug, Tag> fmt::Debug for SLinkedList<P, Tag>
336where
337    P: Pointer,
338    P::Target: SListItem<Tag>,
339{
340    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
341        write!(f, "{{ length: {} ", self.length)?;
342        if !self.head.is_null() {
343            write!(f, "head: {:?} ", self.head)?;
344        } else {
345            write!(f, "head: none ")?;
346        }
347        if !self.tail.is_null() {
348            write!(f, "tail: {:?} ", self.tail)?;
349        } else {
350            write!(f, "tail: none ")?;
351        }
352        write!(f, "}}")
353    }
354}
355
356impl<P, Tag> SLinkedList<P, Tag>
357where
358    P: Pointer,
359    P::Target: SListItem<Tag>,
360{
361    /// Creates a new, empty singly linked list.
362    #[inline(always)]
363    pub fn new() -> Self {
364        SLinkedList { length: 0, head: null_mut(), tail: null_mut(), _phan: Default::default() }
365    }
366
367    /// Clears the list, dropping all of its elements if the pointer type `P` owns them.
368    #[inline]
369    pub fn clear(&mut self) {
370        // By repeatedly popping from the front, we drop each element.
371        // If P is an owned pointer (like Box), the element is dropped.
372        // If P is a raw pointer, it's a no-op, but the list is still emptied.
373        while self.pop_front().is_some() {}
374    }
375
376    /// Returns the length of the list as `usize`.
377    #[inline(always)]
378    pub fn len(&self) -> usize {
379        self.length
380    }
381
382    /// Returns `true` if the list contains no elements.
383    #[inline(always)]
384    pub fn is_empty(&self) -> bool {
385        self.length == 0
386    }
387
388    /// Appends an element to the back of the list (FIFO: enqueue).
389    #[inline]
390    pub fn push_back(&mut self, item: P) {
391        let ptr = item.into_raw() as *mut u8;
392        unsafe {
393            let node = <P::Target as SListItem<Tag>>::get_node_from_ptr(ptr);
394            node.next = null_mut();
395            if !self.tail.is_null() {
396                // List is not empty, update current tail's next
397                <P::Target as SListItem<Tag>>::get_node_from_ptr(self.tail).next = ptr;
398            } else {
399                // List is empty
400                self.head = ptr;
401            }
402        }
403        self.tail = ptr;
404        self.length += 1;
405    }
406
407    /// Pushes an element to the front of the list.
408    #[inline]
409    pub fn push_front(&mut self, item: P) {
410        let ptr = item.into_raw() as *mut u8;
411        let node = unsafe { <P::Target as SListItem<Tag>>::get_node_from_ptr(ptr) };
412        node.next = self.head;
413        if !self.head.is_null() {
414        } else {
415            // List was empty
416            self.tail = ptr;
417        }
418        self.head = ptr;
419        self.length += 1;
420    }
421
422    /// Removes the first element and returns it (FIFO: dequeue).
423    pub fn pop_front(&mut self) -> Option<P> {
424        if !self.head.is_null() {
425            let head_ptr = self.head;
426            let node = unsafe { <P::Target as SListItem<Tag>>::get_node_from_ptr(head_ptr) };
427            let next_ptr = node.next;
428            node.next = null_mut();
429            // Update head to next
430            self.head = next_ptr;
431            self.length -= 1;
432            // If head became null (list empty), update tail to null too
433            if !self.head.is_null() {
434            } else {
435                self.tail = null_mut();
436            }
437            Some(unsafe { P::from_raw(head_ptr as *const P::Target) })
438        } else {
439            None
440        }
441    }
442
443    #[inline]
444    pub fn get_front_ptr(&self) -> *const P::Target {
445        self.head as *const P::Target
446    }
447
448    #[inline]
449    pub fn get_back_ptr(&self) -> *const P::Target {
450        self.tail as *const P::Target
451    }
452
453    /// Returns a reference to the front element.
454    #[inline]
455    pub fn get_front(&self) -> Option<&P::Target> {
456        if !self.head.is_null() {
457            unsafe { Some(&(*(self.head as *const P::Target))) }
458        } else {
459            None
460        }
461    }
462
463    /// Returns a reference to the back element.
464    #[inline]
465    pub fn get_back(&self) -> Option<&P::Target> {
466        if !self.tail.is_null() {
467            unsafe { Some(&(*(self.tail as *const P::Target))) }
468        } else {
469            None
470        }
471    }
472
473    /// Checks if the given node is the head of the list.
474    #[inline(always)]
475    pub fn is_front(&self, node: &P::Target) -> bool {
476        if !self.head.is_null() {
477            ptr::eq(self.head, node as *const P::Target as *const u8)
478        } else {
479            false
480        }
481    }
482
483    /// Returns an iterator over the list (borrowed).
484    ///
485    /// # NOTE
486    ///
487    /// If you plan on turn the raw pointer to owned, use drain instead
488    ///
489    /// # Safety
490    ///
491    /// The caller must ensure that the list is not modified in a way that can
492    /// invalidate internal pointers (such as removing elements or dropping
493    /// items) for the duration of the iterator's use.
494    #[inline(always)]
495    pub fn iter<'a>(&'a self) -> SLinkedListIter<'a, P, Tag> {
496        SLinkedListIter {
497            len: self.length,
498            next_p: self.head,
499            cur: MaybeUninit::uninit(),
500            _phan: Default::default(),
501        }
502    }
503
504    /// Returns a draining iterator that removes items from the list.
505    #[inline(always)]
506    pub fn drain<'a>(&'a mut self) -> SLinkedListDrainer<'a, P, Tag> {
507        SLinkedListDrainer { list: self, _phan: Default::default() }
508    }
509}
510
511impl<P, Tag> Drop for SLinkedList<P, Tag>
512where
513    P: Pointer,
514    P::Target: SListItem<Tag>,
515{
516    fn drop(&mut self) {
517        if mem::needs_drop::<P>() {
518            self.drain().for_each(drop);
519        }
520    }
521}
522
523pub struct SLinkedListIter<'a, P, Tag>
524where
525    P: Pointer,
526    P::Target: SListItem<Tag>,
527{
528    next_p: *mut u8,
529    cur: MaybeUninit<P>,
530    len: usize,
531    _phan: PhantomData<&'a SLinkedList<P, Tag>>,
532}
533
534unsafe impl<'a, P, Tag> Send for SLinkedListIter<'a, P, Tag>
535where
536    P: Pointer + Send,
537    P::Target: SListItem<Tag>,
538{
539}
540
541impl<'a, P, Tag> Iterator for SLinkedListIter<'a, P, Tag>
542where
543    P: Pointer,
544    P::Target: SListItem<Tag>,
545{
546    type Item = &'a P;
547
548    #[inline]
549    fn next(&mut self) -> Option<Self::Item> {
550        if !self.next_p.is_null() {
551            let cur = self.next_p;
552            unsafe {
553                self.next_p = <P::Target as SListItem<Tag>>::get_node_from_ptr(cur).next;
554                self.cur.write(P::from_raw(cur as *const P::Target));
555                // use transmute to convert lifetime from '_ to 'a
556                return Some(mem::transmute::<&P, &P>(self.cur.assume_init_ref()));
557            }
558        }
559        None
560    }
561
562    #[inline]
563    fn size_hint(&self) -> (usize, Option<usize>) {
564        (self.len, Some(self.len))
565    }
566}
567
568impl<'a, P, Tag> ExactSizeIterator for SLinkedListIter<'a, P, Tag>
569where
570    P: Pointer,
571    P::Target: SListItem<Tag>,
572{
573}
574
575pub type SLinkedListDrainer<'a, P, Tag> = Drainer<&'a mut SLinkedList<P, Tag>, P, Tag>;
576pub type SLinkedListIntoIter<P, Tag> = Drainer<SLinkedList<P, Tag>, P, Tag>;
577
578pub struct Drainer<L, P, Tag>
579where
580    L: BorrowMut<SLinkedList<P, Tag>>,
581    P: Pointer,
582    P::Target: SListItem<Tag>,
583{
584    list: L,
585    _phan: PhantomData<(P, Tag)>,
586}
587
588unsafe impl<L, P, Tag> Send for Drainer<L, P, Tag>
589where
590    L: BorrowMut<SLinkedList<P, Tag>>,
591    P: Pointer + Send,
592    P::Target: SListItem<Tag>,
593{
594}
595
596impl<L, P, Tag> Iterator for Drainer<L, P, Tag>
597where
598    L: BorrowMut<SLinkedList<P, Tag>>,
599    P: Pointer,
600    P::Target: SListItem<Tag>,
601{
602    type Item = P;
603
604    #[inline]
605    fn next(&mut self) -> Option<P> {
606        self.list.borrow_mut().pop_front()
607    }
608
609    #[inline]
610    fn size_hint(&self) -> (usize, Option<usize>) {
611        let l = self.list.borrow().len();
612        (l, Some(l))
613    }
614}
615
616impl<L, P, Tag> ExactSizeIterator for Drainer<L, P, Tag>
617where
618    L: BorrowMut<SLinkedList<P, Tag>>,
619    P: Pointer,
620    P::Target: SListItem<Tag>,
621{
622}
623
624impl<'a, P, Tag> IntoIterator for &'a SLinkedList<P, Tag>
625where
626    P: Pointer,
627    P::Target: SListItem<Tag>,
628{
629    type Item = &'a P;
630    type IntoIter = SLinkedListIter<'a, P, Tag>;
631
632    #[inline]
633    fn into_iter(self) -> Self::IntoIter {
634        self.iter()
635    }
636}
637
638impl<P, Tag> IntoIterator for SLinkedList<P, Tag>
639where
640    P: Pointer,
641    P::Target: SListItem<Tag>,
642{
643    type Item = P;
644    type IntoIter = SLinkedListIntoIter<P, Tag>;
645
646    #[inline]
647    fn into_iter(self) -> Self::IntoIter {
648        Drainer { list: self, _phan: Default::default() }
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use super::*;
655    use alloc::boxed::Box;
656    use std::cell::UnsafeCell;
657    use std::sync::atomic::{AtomicUsize, Ordering};
658
659    pub struct TestTag;
660
661    #[derive(Debug)]
662    pub struct TestNode {
663        pub value: i64,
664        pub node: UnsafeCell<SListNode<TestTag>>,
665    }
666
667    static ACTIVE_NODE_COUNT: AtomicUsize = AtomicUsize::new(0);
668
669    impl Drop for TestNode {
670        fn drop(&mut self) {
671            ACTIVE_NODE_COUNT.fetch_sub(1, Ordering::SeqCst);
672        }
673    }
674
675    unsafe impl Send for TestNode {}
676
677    unsafe impl SListItem<TestTag> for TestNode {
678        fn get_node(&self) -> &mut SListNode<TestTag> {
679            unsafe { &mut *self.node.get() }
680        }
681    }
682
683    fn new_node(v: i64) -> TestNode {
684        ACTIVE_NODE_COUNT.fetch_add(1, Ordering::SeqCst);
685        TestNode { value: v, node: UnsafeCell::new(SListNode::default()) }
686    }
687
688    #[test]
689    fn test_push_back_pop_front_box() {
690        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
691        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
692
693        let node1 = Box::new(new_node(1));
694        l.push_back(node1);
695
696        let node2 = Box::new(new_node(2));
697        l.push_back(node2);
698
699        let node3 = Box::new(new_node(3));
700        l.push_back(node3);
701
702        assert_eq!(3, l.len());
703        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
704
705        // Test iterator
706        let mut iter = l.iter();
707        assert_eq!(iter.next().unwrap().value, 1);
708        assert_eq!(iter.next().unwrap().value, 2);
709        assert_eq!(iter.next().unwrap().value, 3);
710        assert!(iter.next().is_none());
711
712        // Test pop_front (FIFO)
713        let n1 = l.pop_front();
714        assert!(n1.is_some());
715        assert_eq!(n1.unwrap().value, 1);
716        assert_eq!(l.len(), 2);
717
718        let n2 = l.pop_front();
719        assert!(n2.is_some());
720        assert_eq!(n2.unwrap().value, 2);
721        assert_eq!(l.len(), 1);
722
723        let n3 = l.pop_front();
724        assert!(n3.is_some());
725        assert_eq!(n3.unwrap().value, 3);
726        assert_eq!(l.len(), 0);
727
728        assert!(l.pop_front().is_none());
729        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
730    }
731
732    #[test]
733    fn test_push_front_box() {
734        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
735        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
736
737        let node1 = Box::new(new_node(1));
738        l.push_front(node1); // List: [1]
739
740        let node2 = Box::new(new_node(2));
741        l.push_front(node2); // List: [2, 1]
742
743        let node3 = Box::new(new_node(3));
744        l.push_front(node3); // List: [3, 2, 1]
745
746        assert_eq!(3, l.len());
747        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
748
749        // Test iterator (should be 3, 2, 1)
750        let mut iter = l.iter();
751        assert_eq!(iter.next().unwrap().value, 3);
752        assert_eq!(iter.next().unwrap().value, 2);
753        assert_eq!(iter.next().unwrap().value, 1);
754        assert!(iter.next().is_none());
755
756        // Test pop_front (FIFO)
757        let n1 = l.pop_front();
758        assert!(n1.is_some());
759        assert_eq!(n1.unwrap().value, 3);
760        assert_eq!(l.len(), 2);
761
762        let n2 = l.pop_front();
763        assert!(n2.is_some());
764        assert_eq!(n2.unwrap().value, 2);
765        assert_eq!(l.len(), 1);
766
767        let n3 = l.pop_front();
768        assert!(n3.is_some());
769        assert_eq!(n3.unwrap().value, 1);
770        assert_eq!(l.len(), 0);
771
772        assert!(l.pop_front().is_none());
773        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
774    }
775
776    #[test]
777    fn test_drain() {
778        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
779        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
780
781        l.push_back(Box::new(new_node(10)));
782        l.push_back(Box::new(new_node(20)));
783        l.push_back(Box::new(new_node(30)));
784
785        assert_eq!(l.len(), 3);
786        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
787
788        {
789            let mut drain = l.drain();
790            assert_eq!(drain.next().unwrap().value, 10);
791            assert_eq!(drain.next().unwrap().value, 20);
792            assert_eq!(drain.next().unwrap().value, 30);
793            assert!(drain.next().is_none());
794        }
795
796        assert_eq!(l.len(), 0);
797        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
798    }
799
800    #[test]
801    fn test_clear() {
802        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
803        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
804
805        l.push_back(Box::new(new_node(1)));
806        l.push_back(Box::new(new_node(2)));
807        assert_eq!(l.len(), 2);
808        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 2);
809
810        l.clear();
811
812        assert!(l.is_empty());
813        assert_eq!(l.len(), 0);
814        assert!(l.get_front().is_none());
815        assert!(l.get_back().is_none());
816        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
817
818        // Can still push to the list
819        l.push_back(Box::new(new_node(3)));
820        assert_eq!(l.len(), 1);
821        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 1);
822    }
823}