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}