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}