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,
324    P::Target: SListItem<Tag>,
325{
326}
327
328impl<P: fmt::Debug, Tag> fmt::Debug for SLinkedList<P, Tag>
329where
330    P: Pointer,
331    P::Target: SListItem<Tag>,
332{
333    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334        write!(f, "{{ length: {} ", self.length)?;
335        if !self.head.is_null() {
336            write!(f, "head: {:?} ", self.head)?;
337        } else {
338            write!(f, "head: none ")?;
339        }
340        if !self.tail.is_null() {
341            write!(f, "tail: {:?} ", self.tail)?;
342        } else {
343            write!(f, "tail: none ")?;
344        }
345        write!(f, "}}")
346    }
347}
348
349impl<P, Tag> SLinkedList<P, Tag>
350where
351    P: Pointer,
352    P::Target: SListItem<Tag>,
353{
354    /// Creates a new, empty singly linked list.
355    #[inline(always)]
356    pub fn new() -> Self {
357        SLinkedList { length: 0, head: null_mut(), tail: null_mut(), _phan: Default::default() }
358    }
359
360    /// Clears the list, dropping all of its elements if the pointer type `P` owns them.
361    #[inline]
362    pub fn clear(&mut self) {
363        // By repeatedly popping from the front, we drop each element.
364        // If P is an owned pointer (like Box), the element is dropped.
365        // If P is a raw pointer, it's a no-op, but the list is still emptied.
366        while self.pop_front().is_some() {}
367    }
368
369    /// Returns the length of the list as `usize`.
370    #[inline(always)]
371    pub fn len(&self) -> usize {
372        self.length
373    }
374
375    /// Returns `true` if the list contains no elements.
376    #[inline(always)]
377    pub fn is_empty(&self) -> bool {
378        self.length == 0
379    }
380
381    /// Appends an element to the back of the list (FIFO: enqueue).
382    #[inline]
383    pub fn push_back(&mut self, item: P) {
384        let ptr = item.into_raw() as *mut u8;
385        unsafe {
386            let node = <P::Target as SListItem<Tag>>::get_node_from_ptr(ptr);
387            node.next = null_mut();
388            if !self.tail.is_null() {
389                // List is not empty, update current tail's next
390                <P::Target as SListItem<Tag>>::get_node_from_ptr(self.tail).next = ptr;
391            } else {
392                // List is empty
393                self.head = ptr;
394            }
395        }
396        self.tail = ptr;
397        self.length += 1;
398    }
399
400    /// Pushes an element to the front of the list.
401    #[inline]
402    pub fn push_front(&mut self, item: P) {
403        let ptr = item.into_raw() as *mut u8;
404        let node = unsafe { <P::Target as SListItem<Tag>>::get_node_from_ptr(ptr) };
405        node.next = self.head;
406        if !self.head.is_null() {
407        } else {
408            // List was empty
409            self.tail = ptr;
410        }
411        self.head = ptr;
412        self.length += 1;
413    }
414
415    /// Removes the first element and returns it (FIFO: dequeue).
416    pub fn pop_front(&mut self) -> Option<P> {
417        if !self.head.is_null() {
418            let head_ptr = self.head;
419            let node = unsafe { <P::Target as SListItem<Tag>>::get_node_from_ptr(head_ptr) };
420            let next_ptr = node.next;
421            node.next = null_mut();
422            // Update head to next
423            self.head = next_ptr;
424            self.length -= 1;
425            // If head became null (list empty), update tail to null too
426            if !self.head.is_null() {
427            } else {
428                self.tail = null_mut();
429            }
430            Some(unsafe { P::from_raw(head_ptr as *const P::Target) })
431        } else {
432            None
433        }
434    }
435
436    #[inline]
437    pub fn get_front_ptr(&self) -> *const P::Target {
438        self.head as *const P::Target
439    }
440
441    #[inline]
442    pub fn get_back_ptr(&self) -> *const P::Target {
443        self.tail as *const P::Target
444    }
445
446    /// Returns a reference to the front element.
447    #[inline]
448    pub fn get_front(&self) -> Option<&P::Target> {
449        if !self.head.is_null() {
450            unsafe { Some(&(*(self.head as *const P::Target))) }
451        } else {
452            None
453        }
454    }
455
456    /// Returns a reference to the back element.
457    #[inline]
458    pub fn get_back(&self) -> Option<&P::Target> {
459        if !self.tail.is_null() {
460            unsafe { Some(&(*(self.tail as *const P::Target))) }
461        } else {
462            None
463        }
464    }
465
466    /// Checks if the given node is the head of the list.
467    #[inline(always)]
468    pub fn is_front(&self, node: &P::Target) -> bool {
469        if !self.head.is_null() {
470            ptr::eq(self.head, node as *const P::Target as *const u8)
471        } else {
472            false
473        }
474    }
475
476    /// Returns an iterator over the list (borrowed).
477    ///
478    /// # NOTE
479    ///
480    /// If you plan on turn the raw pointer to owned, use drain instead
481    ///
482    /// # Safety
483    ///
484    /// The caller must ensure that the list is not modified in a way that can
485    /// invalidate internal pointers (such as removing elements or dropping
486    /// items) for the duration of the iterator's use.
487    #[inline(always)]
488    pub fn iter<'a>(&'a self) -> SLinkedListIter<'a, P, Tag> {
489        SLinkedListIter {
490            len: self.length,
491            next_p: self.head,
492            cur: MaybeUninit::uninit(),
493            _phan: Default::default(),
494        }
495    }
496
497    /// Returns a draining iterator that removes items from the list.
498    #[inline(always)]
499    pub fn drain<'a>(&'a mut self) -> SLinkedListDrainer<'a, P, Tag> {
500        SLinkedListDrainer { list: self, _phan: Default::default() }
501    }
502}
503
504impl<P, Tag> Drop for SLinkedList<P, Tag>
505where
506    P: Pointer,
507    P::Target: SListItem<Tag>,
508{
509    fn drop(&mut self) {
510        if mem::needs_drop::<P>() {
511            self.drain().for_each(drop);
512        }
513    }
514}
515
516pub struct SLinkedListIter<'a, P, Tag>
517where
518    P: Pointer,
519    P::Target: SListItem<Tag>,
520{
521    next_p: *mut u8,
522    cur: MaybeUninit<P>,
523    len: usize,
524    _phan: PhantomData<&'a SLinkedList<P, Tag>>,
525}
526
527unsafe impl<'a, P, Tag> Send for SLinkedListIter<'a, P, Tag>
528where
529    P: Pointer,
530    P::Target: SListItem<Tag>,
531{
532}
533
534impl<'a, P, Tag> Iterator for SLinkedListIter<'a, P, Tag>
535where
536    P: Pointer,
537    P::Target: SListItem<Tag>,
538{
539    type Item = &'a P;
540
541    #[inline]
542    fn next(&mut self) -> Option<Self::Item> {
543        if !self.next_p.is_null() {
544            let cur = self.next_p;
545            unsafe {
546                self.next_p = <P::Target as SListItem<Tag>>::get_node_from_ptr(cur).next;
547                self.cur.write(P::from_raw(cur as *const P::Target));
548                // use transmute to convert lifetime from '_ to 'a
549                return Some(mem::transmute::<&P, &P>(self.cur.assume_init_ref()));
550            }
551        }
552        None
553    }
554
555    #[inline]
556    fn size_hint(&self) -> (usize, Option<usize>) {
557        (self.len, Some(self.len))
558    }
559}
560
561impl<'a, P, Tag> ExactSizeIterator for SLinkedListIter<'a, P, Tag>
562where
563    P: Pointer,
564    P::Target: SListItem<Tag>,
565{
566}
567
568pub type SLinkedListDrainer<'a, P, Tag> = Drainer<&'a mut SLinkedList<P, Tag>, P, Tag>;
569pub type SLinkedListIntoIter<P, Tag> = Drainer<SLinkedList<P, Tag>, P, Tag>;
570
571pub struct Drainer<L, P, Tag>
572where
573    L: BorrowMut<SLinkedList<P, Tag>>,
574    P: Pointer,
575    P::Target: SListItem<Tag>,
576{
577    list: L,
578    _phan: PhantomData<(P, Tag)>,
579}
580
581impl<L, P, Tag> Iterator for Drainer<L, P, Tag>
582where
583    L: BorrowMut<SLinkedList<P, Tag>>,
584    P: Pointer,
585    P::Target: SListItem<Tag>,
586{
587    type Item = P;
588
589    #[inline]
590    fn next(&mut self) -> Option<P> {
591        self.list.borrow_mut().pop_front()
592    }
593
594    #[inline]
595    fn size_hint(&self) -> (usize, Option<usize>) {
596        let l = self.list.borrow().len();
597        (l, Some(l))
598    }
599}
600
601impl<L, P, Tag> ExactSizeIterator for Drainer<L, P, Tag>
602where
603    L: BorrowMut<SLinkedList<P, Tag>>,
604    P: Pointer,
605    P::Target: SListItem<Tag>,
606{
607}
608
609impl<'a, P, Tag> IntoIterator for &'a SLinkedList<P, Tag>
610where
611    P: Pointer,
612    P::Target: SListItem<Tag>,
613{
614    type Item = &'a P;
615    type IntoIter = SLinkedListIter<'a, P, Tag>;
616
617    #[inline]
618    fn into_iter(self) -> Self::IntoIter {
619        self.iter()
620    }
621}
622
623impl<P, Tag> IntoIterator for SLinkedList<P, Tag>
624where
625    P: Pointer,
626    P::Target: SListItem<Tag>,
627{
628    type Item = P;
629    type IntoIter = SLinkedListIntoIter<P, Tag>;
630
631    #[inline]
632    fn into_iter(self) -> Self::IntoIter {
633        Drainer { list: self, _phan: Default::default() }
634    }
635}
636
637#[cfg(test)]
638mod tests {
639    use super::*;
640    use alloc::boxed::Box;
641    use std::cell::UnsafeCell;
642    use std::sync::atomic::{AtomicUsize, Ordering};
643
644    pub struct TestTag;
645
646    #[derive(Debug)]
647    pub struct TestNode {
648        pub value: i64,
649        pub node: UnsafeCell<SListNode<TestTag>>,
650    }
651
652    static ACTIVE_NODE_COUNT: AtomicUsize = AtomicUsize::new(0);
653
654    impl Drop for TestNode {
655        fn drop(&mut self) {
656            ACTIVE_NODE_COUNT.fetch_sub(1, Ordering::SeqCst);
657        }
658    }
659
660    unsafe impl Send for TestNode {}
661
662    unsafe impl SListItem<TestTag> for TestNode {
663        fn get_node(&self) -> &mut SListNode<TestTag> {
664            unsafe { &mut *self.node.get() }
665        }
666    }
667
668    fn new_node(v: i64) -> TestNode {
669        ACTIVE_NODE_COUNT.fetch_add(1, Ordering::SeqCst);
670        TestNode { value: v, node: UnsafeCell::new(SListNode::default()) }
671    }
672
673    #[test]
674    fn test_push_back_pop_front_box() {
675        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
676        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
677
678        let node1 = Box::new(new_node(1));
679        l.push_back(node1);
680
681        let node2 = Box::new(new_node(2));
682        l.push_back(node2);
683
684        let node3 = Box::new(new_node(3));
685        l.push_back(node3);
686
687        assert_eq!(3, l.len());
688        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
689
690        // Test iterator
691        let mut iter = l.iter();
692        assert_eq!(iter.next().unwrap().value, 1);
693        assert_eq!(iter.next().unwrap().value, 2);
694        assert_eq!(iter.next().unwrap().value, 3);
695        assert!(iter.next().is_none());
696
697        // Test pop_front (FIFO)
698        let n1 = l.pop_front();
699        assert!(n1.is_some());
700        assert_eq!(n1.unwrap().value, 1);
701        assert_eq!(l.len(), 2);
702
703        let n2 = l.pop_front();
704        assert!(n2.is_some());
705        assert_eq!(n2.unwrap().value, 2);
706        assert_eq!(l.len(), 1);
707
708        let n3 = l.pop_front();
709        assert!(n3.is_some());
710        assert_eq!(n3.unwrap().value, 3);
711        assert_eq!(l.len(), 0);
712
713        assert!(l.pop_front().is_none());
714        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
715    }
716
717    #[test]
718    fn test_push_front_box() {
719        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
720        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
721
722        let node1 = Box::new(new_node(1));
723        l.push_front(node1); // List: [1]
724
725        let node2 = Box::new(new_node(2));
726        l.push_front(node2); // List: [2, 1]
727
728        let node3 = Box::new(new_node(3));
729        l.push_front(node3); // List: [3, 2, 1]
730
731        assert_eq!(3, l.len());
732        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
733
734        // Test iterator (should be 3, 2, 1)
735        let mut iter = l.iter();
736        assert_eq!(iter.next().unwrap().value, 3);
737        assert_eq!(iter.next().unwrap().value, 2);
738        assert_eq!(iter.next().unwrap().value, 1);
739        assert!(iter.next().is_none());
740
741        // Test pop_front (FIFO)
742        let n1 = l.pop_front();
743        assert!(n1.is_some());
744        assert_eq!(n1.unwrap().value, 3);
745        assert_eq!(l.len(), 2);
746
747        let n2 = l.pop_front();
748        assert!(n2.is_some());
749        assert_eq!(n2.unwrap().value, 2);
750        assert_eq!(l.len(), 1);
751
752        let n3 = l.pop_front();
753        assert!(n3.is_some());
754        assert_eq!(n3.unwrap().value, 1);
755        assert_eq!(l.len(), 0);
756
757        assert!(l.pop_front().is_none());
758        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
759    }
760
761    #[test]
762    fn test_drain() {
763        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
764        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
765
766        l.push_back(Box::new(new_node(10)));
767        l.push_back(Box::new(new_node(20)));
768        l.push_back(Box::new(new_node(30)));
769
770        assert_eq!(l.len(), 3);
771        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 3);
772
773        {
774            let mut drain = l.drain();
775            assert_eq!(drain.next().unwrap().value, 10);
776            assert_eq!(drain.next().unwrap().value, 20);
777            assert_eq!(drain.next().unwrap().value, 30);
778            assert!(drain.next().is_none());
779        }
780
781        assert_eq!(l.len(), 0);
782        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
783    }
784
785    #[test]
786    fn test_clear() {
787        ACTIVE_NODE_COUNT.store(0, Ordering::SeqCst);
788        let mut l = SLinkedList::<Box<TestNode>, TestTag>::new();
789
790        l.push_back(Box::new(new_node(1)));
791        l.push_back(Box::new(new_node(2)));
792        assert_eq!(l.len(), 2);
793        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 2);
794
795        l.clear();
796
797        assert!(l.is_empty());
798        assert_eq!(l.len(), 0);
799        assert!(l.get_front().is_none());
800        assert!(l.get_back().is_none());
801        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 0);
802
803        // Can still push to the list
804        l.push_back(Box::new(new_node(3)));
805        assert_eq!(l.len(), 1);
806        assert_eq!(ACTIVE_NODE_COUNT.load(Ordering::SeqCst), 1);
807    }
808}