From 67161e67c9e92b7e49be0d9110146894308e967c Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 30 Jun 2025 14:04:37 -0700 Subject: [PATCH 1/2] feat: implement accessor type for existing NGINX rbtrees --- src/collections/rbtree.rs | 169 +++++++++++++++++++++++++++++++------- 1 file changed, 140 insertions(+), 29 deletions(-) diff --git a/src/collections/rbtree.rs b/src/collections/rbtree.rs index 9d824f7..29462a6 100644 --- a/src/collections/rbtree.rs +++ b/src/collections/rbtree.rs @@ -19,6 +19,96 @@ use nginx_sys::{ use crate::allocator::{self, AllocError, Allocator}; +/// Trait for pointer conversions between the tree entry and its container. +/// +/// # Safety +/// +/// This trait must only be implemented on types that contain a tree node or wrappers with +/// compatible layout. The type then can be used to access elements of a raw rbtree type +/// [NgxRbTree] linked via specified field. +/// +/// If the struct can belong to several trees through multiple embedded `ngx_rbtree_node_t` fields, +/// a separate [NgxRbTreeEntry] implementation via wrapper type should be used for each tree. +pub unsafe trait NgxRbTreeEntry { + /// Gets a container pointer from tree node. + fn from_rbtree_node(node: NonNull) -> NonNull; + /// Gets an rbtree node from a container reference. + fn to_rbtree_node(&mut self) -> &mut ngx_rbtree_node_t; +} + +unsafe impl NgxRbTreeEntry for ngx_rbtree_node_t { + fn from_rbtree_node(node: NonNull) -> NonNull { + node + } + + fn to_rbtree_node(&mut self) -> &mut ngx_rbtree_node_t { + self + } +} + +/// A wrapper over a raw `ngx_rbtree_t`, a red-black tree implementation. +/// +/// This wrapper is defined in terms of type `T` that embeds and can be converted from or to the +/// tree nodes. +/// +/// See . +#[derive(Debug)] +#[repr(transparent)] +pub struct NgxRbTree { + inner: ngx_rbtree_t, + _type: PhantomData, +} + +impl NgxRbTree +where + T: NgxRbTreeEntry, +{ + /// Creates a tree reference from a pointer to [ngx_rbtree_t]. + /// + /// # Safety + /// + /// `tree` is a valid pointer to [ngx_rbtree_t], and `T::from_rbtree_node` on the tree nodes + /// results in valid pointers to `T`. + pub unsafe fn from_ptr<'a>(tree: *const ngx_rbtree_t) -> &'a Self { + &*tree.cast() + } + + /// Creates a mutable tree reference from a pointer to [ngx_rbtree_t]. + /// + /// # Safety + /// + /// `tree` is a valid pointer to [ngx_rbtree_t], and `T::from_rbtree_node` on the tree nodes + /// results in valid pointers to `T`. + pub unsafe fn from_ptr_mut<'a>(tree: *mut ngx_rbtree_t) -> &'a mut Self { + &mut *tree.cast() + } + + /// Returns `true` if the tree contains no elements. + pub fn is_empty(&self) -> bool { + ptr::addr_eq(self.inner.root, self.inner.sentinel) + } + + /// Appends a node to the tree. + pub fn insert(&mut self, node: &mut T) { + unsafe { ngx_rbtree_insert(&mut self.inner, node.to_rbtree_node()) }; + } + + /// Removes the specified node from the tree. + pub fn remove(&mut self, node: &mut T) { + unsafe { ngx_rbtree_delete(&mut self.inner, node.to_rbtree_node()) }; + } + + /// Returns an iterator over the nodes of the tree. + pub fn iter(&self) -> NgxRbTreeIter<'_> { + unsafe { NgxRbTreeIter::new(NonNull::from(&self.inner)) } + } + + /// Returns a mutable iterator over the nodes of the tree. + pub fn iter_mut(&mut self) -> NgxRbTreeIter<'_> { + unsafe { NgxRbTreeIter::new(NonNull::from(&mut self.inner)) } + } +} + /// Raw iterator over the `ngx_rbtree_t` nodes. /// /// This iterator type can be used to access elements of any correctly initialized `ngx_rbtree_t` @@ -31,7 +121,7 @@ pub struct NgxRbTreeIter<'a> { _lifetime: PhantomData<&'a ()>, } -impl<'a> NgxRbTreeIter<'a> { +impl NgxRbTreeIter<'_> { /// Creates an iterator for the `ngx_rbtree_t`. /// /// # Safety @@ -54,7 +144,7 @@ impl<'a> NgxRbTreeIter<'a> { } } -impl<'a> Iterator for NgxRbTreeIter<'a> { +impl Iterator for NgxRbTreeIter<'_> { type Item = NonNull; fn next(&mut self) -> Option { @@ -79,15 +169,15 @@ pub struct RbTreeMap where A: Allocator, { - tree: ngx_rbtree_t, + tree: NgxRbTree>, sentinel: NonNull, alloc: A, - _kv_type: PhantomData<(K, V)>, } /// Entry type for the [RbTreeMap]. /// /// The struct is used from the Rust code only and thus does not need to be compatible with C. +#[derive(Debug)] struct MapEntry { node: ngx_rbtree_node_t, key: K, @@ -110,20 +200,30 @@ where } } +unsafe impl NgxRbTreeEntry for MapEntry { + fn from_rbtree_node(node: NonNull) -> NonNull { + unsafe { ngx_rbtree_data!(node, Self, node) } + } + + fn to_rbtree_node(&mut self) -> &mut ngx_rbtree_node_t { + &mut self.node + } +} + /// An iterator for the [RbTreeMap]. -pub struct Iter<'a, K: 'a, V: 'a>(NgxRbTreeIter<'a>, PhantomData<(K, V)>); +pub struct MapIter<'a, K: 'a, V: 'a>(NgxRbTreeIter<'a>, PhantomData<(K, V)>); -impl<'a, K: 'a, V: 'a> Iter<'a, K, V> { +impl<'a, K: 'a, V: 'a> MapIter<'a, K, V> { /// Creates an iterator for the [RbTreeMap]. pub fn new(tree: &'a RbTreeMap) -> Self { // msrv(1.89.0): NonNull::from_ref() - let rbtree = NonNull::from(&tree.tree); + let rbtree = NonNull::from(&tree.tree.inner); // SAFETY: Iter borrows from the tree, ensuring that the tree would outlive it. Self(unsafe { NgxRbTreeIter::new(rbtree) }, Default::default()) } } -impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { +impl<'a, K: 'a, V: 'a> Iterator for MapIter<'a, K, V> { type Item = (&'a K, &'a V); fn next(&mut self) -> Option { @@ -134,24 +234,24 @@ impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { } /// A mutable iterator for the [RbTreeMap]. -pub struct IterMut<'a, K: 'a, V: 'a>(NgxRbTreeIter<'a>, PhantomData<(K, V)>); +pub struct MapIterMut<'a, K: 'a, V: 'a>(NgxRbTreeIter<'a>, PhantomData<(K, V)>); -impl<'a, K: 'a, V: 'a> IterMut<'a, K, V> { +impl<'a, K: 'a, V: 'a> MapIterMut<'a, K, V> { /// Creates an iterator for the [RbTreeMap]. pub fn new(tree: &'a mut RbTreeMap) -> Self { // msrv(1.89.0): NonNull::from_mut() - let rbtree = NonNull::from(&mut tree.tree); + let rbtree = NonNull::from(&mut tree.tree.inner); // SAFETY: IterMut borrows from the tree, ensuring that the tree would outlive it. Self(unsafe { NgxRbTreeIter::new(rbtree) }, Default::default()) } } -impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> { +impl<'a, K: 'a, V: 'a> Iterator for MapIterMut<'a, K, V> { type Item = (&'a K, &'a mut V); fn next(&mut self) -> Option { - let item = self.0.next()?; - let item = unsafe { ngx_rbtree_data!(item, MapEntry, node).as_mut() }; + let mut item = MapEntry::::from_rbtree_node(self.0.next()?); + let item = unsafe { item.as_mut() }; Some((&item.key, &mut item.value)) } } @@ -168,14 +268,14 @@ where /// Clears the tree, removing all elements. pub fn clear(&mut self) { // SAFETY: the iter lives until the end of the scope - let iter = unsafe { NgxRbTreeIter::new(NonNull::from(&self.tree)) }; + let iter = unsafe { NgxRbTreeIter::new(NonNull::from(&self.tree.inner)) }; let layout = Layout::new::>(); for node in iter { unsafe { - let mut data = ngx_rbtree_data!(node, MapEntry, node); + let mut data = MapEntry::::from_rbtree_node(node); - ngx_rbtree_delete(&mut self.tree, &mut data.as_mut().node); + ngx_rbtree_delete(&mut self.tree.inner, &mut data.as_mut().node); ptr::drop_in_place(data.as_mut()); self.allocator().deallocate(data.cast(), layout) } @@ -184,19 +284,19 @@ where /// Returns true if the tree contains no entries. pub fn is_empty(&self) -> bool { - ptr::addr_eq(self.tree.root, self.tree.sentinel) + self.tree.is_empty() } /// Returns an iterator over the entries of the tree. #[inline] - pub fn iter(&self) -> Iter<'_, K, V> { - Iter::new(self) + pub fn iter(&self) -> MapIter<'_, K, V> { + MapIter::new(self) } /// Returns a mutable iterator over the entries of the tree. #[inline] - pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - IterMut::new(self) + pub fn iter_mut(&mut self) -> MapIterMut<'_, K, V> { + MapIterMut::new(self) } } @@ -210,14 +310,24 @@ where let layout = Layout::new::(); let sentinel: NonNull = alloc.allocate_zeroed(layout)?.cast(); + let tree = NgxRbTree { + inner: unsafe { mem::zeroed() }, + _type: PhantomData, + }; + let mut this = RbTreeMap { - tree: unsafe { mem::zeroed() }, + tree, sentinel, alloc, - _kv_type: PhantomData, }; - unsafe { ngx_rbtree_init(&mut this.tree, this.sentinel.as_ptr(), Some(Self::insert)) }; + unsafe { + ngx_rbtree_init( + &mut this.tree.inner, + this.sentinel.as_ptr(), + Some(Self::insert), + ) + }; Ok(this) } @@ -260,7 +370,8 @@ where { let mut node = self.lookup(key)?; unsafe { - ngx_rbtree_delete(&mut self.tree, &mut node.as_mut().node); + self.tree.remove(node.as_mut()); + let layout = Layout::for_value(node.as_ref()); // SAFETY: we make a bitwise copy of the node and dispose of the original value without // dropping it. @@ -278,7 +389,7 @@ where } else { let node = MapEntry::new(key, value); let mut node = allocator::allocate(node, self.allocator())?; - unsafe { ngx_rbtree_insert(&mut self.tree, &mut node.as_mut().node) }; + self.tree.insert(unsafe { node.as_mut() }); node }; @@ -324,10 +435,10 @@ where K: borrow::Borrow, Q: Hash + Ord + ?Sized, { - let mut node = self.tree.root; + let mut node = self.tree.inner.root; let hash = BuildMapHasher::default().hash_one(key) as ngx_rbtree_key_t; - while !ptr::addr_eq(node, self.tree.sentinel) { + while !ptr::addr_eq(node, self.tree.inner.sentinel) { let n = unsafe { NonNull::new_unchecked(ngx_rbtree_data!(node, MapEntry, node)) }; let nr = unsafe { n.as_ref() }; From 93db16a2e3102a67e74162dfd39f0e1190dcaad6 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Fri, 20 Jun 2025 15:52:43 -0700 Subject: [PATCH 2/2] feat: Queue implementation based on the ngx_queue_t --- src/collections/mod.rs | 2 + src/collections/queue.rs | 431 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 433 insertions(+) create mode 100644 src/collections/queue.rs diff --git a/src/collections/mod.rs b/src/collections/mod.rs index 5689cd0..36e8c92 100644 --- a/src/collections/mod.rs +++ b/src/collections/mod.rs @@ -10,6 +10,8 @@ pub use allocator_api2::{ vec::Vec, }; +pub use queue::Queue; pub use rbtree::RbTreeMap; +pub mod queue; pub mod rbtree; diff --git a/src/collections/queue.rs b/src/collections/queue.rs new file mode 100644 index 0000000..757d6d0 --- /dev/null +++ b/src/collections/queue.rs @@ -0,0 +1,431 @@ +//! Types and utilities for working with [ngx_queue_t], an intrusive doubly-linked list. +//! +//! This module provides both the tools for interaction with the existing `ngx_queue_t` objects in +//! the NGINX, and useful high-level types built on top of the `ngx_queue_t`. +//! +//! See . + +use core::alloc::Layout; +use core::marker::PhantomData; +use core::mem; +use core::ptr::{self, NonNull}; + +use nginx_sys::{ + ngx_queue_data, ngx_queue_empty, ngx_queue_init, ngx_queue_insert_after, + ngx_queue_insert_before, ngx_queue_remove, ngx_queue_t, +}; + +use crate::allocator::{AllocError, Allocator}; + +/// Trait for pointer conversions between the queue entry and its container. +/// +/// # Safety +/// +/// This trait must only be implemented on types that contain a queue link or wrappers with +/// compatible layout. The type then can be used to access elements of a raw queue type +/// [NgxQueue] linked via specified field. +/// +/// If the struct can belong to several queues through multiple embedded `ngx_queue_t` fields, +/// a separate [NgxQueueEntry] implementation via wrapper type should be used for each queue. +pub unsafe trait NgxQueueEntry { + /// Gets a container pointer from queue node. + fn from_queue(queue: NonNull) -> NonNull; + /// Gets a queue node from a container reference. + fn to_queue(&mut self) -> &mut ngx_queue_t; +} + +unsafe impl NgxQueueEntry for ngx_queue_t { + fn from_queue(queue: NonNull) -> NonNull { + queue + } + + fn to_queue(&mut self) -> &mut ngx_queue_t { + self + } +} + +/// A wrapper over a raw `ngx_queue_t`, an intrusive doubly-linked list. +/// +/// This wrapper is defined in terms of type `T` that embeds and can be converted from or to the +/// list entries. +/// +/// Example: +/// ```rust,no_run +/// # use core::ptr::{NonNull, addr_of_mut}; +/// # use nginx_sys::{ngx_event_t, ngx_posted_events, ngx_queue_data, ngx_queue_t}; +/// # use ngx::collections::queue::{NgxQueue, NgxQueueEntry}; +/// // We need a wrapper type to define [NgxQueueEntry] on. +/// #[repr(transparent)] +/// struct PostedEvent(ngx_event_t); +/// +/// unsafe impl NgxQueueEntry for PostedEvent { +/// fn from_queue(queue: NonNull) -> NonNull { +/// // We can safely cast obtained ngx_event_t to a transparent wrapper. +/// unsafe { ngx_queue_data!(queue, ngx_event_t, queue) }.cast() +/// } +/// +/// fn to_queue(&mut self) -> &mut ngx_queue_t { +/// &mut self.0.queue +/// } +/// } +/// +/// // SAFETY: `ngx_posted_events` global static is a list of `ngx_event_t` linked via +/// // `ngx_event_t.queue`. +/// // NGINX is single-threaded, so we get exclusive access to the static. +/// let posted: &mut NgxQueue = +/// unsafe { NgxQueue::from_ptr_mut(addr_of_mut!(ngx_posted_events)) }; +/// ``` +/// +/// See . +#[derive(Debug)] +#[repr(transparent)] +pub struct NgxQueue { + head: ngx_queue_t, + _type: PhantomData, +} + +impl NgxQueue +where + T: NgxQueueEntry, +{ + /// Creates a queue reference from a pointer to [ngx_queue_t]. + /// + /// # Safety + /// + /// `head` is a valid pointer to a list head, and `T::from_queue` on the list entries results in + /// valid pointers to `T`. + pub unsafe fn from_ptr<'a>(head: *const ngx_queue_t) -> &'a Self { + &*head.cast() + } + + /// Creates a mutable queue reference from a pointer to [ngx_queue_t]. + /// + /// # Safety + /// + /// `head` is a valid pointer to a list head, and `T::from_queue` on the list entries results in + /// valid pointers to `T`. + pub unsafe fn from_ptr_mut<'a>(head: *mut ngx_queue_t) -> &'a mut Self { + &mut *head.cast() + } + + /// Returns `true` if the queue contains no elements. + pub fn is_empty(&self) -> bool { + self.head.prev.is_null() || unsafe { ngx_queue_empty(&self.head) } + } + + /// Appends an element to the end of the queue. + pub fn push_back(&mut self, entry: &mut T) { + if self.head.prev.is_null() { + unsafe { ngx_queue_init(&mut self.head) } + } + + unsafe { ngx_queue_insert_before(&mut self.head, entry.to_queue()) } + } + + /// Appends an element to the beginning of the queue. + pub fn push_front(&mut self, entry: &mut T) { + if self.head.prev.is_null() { + unsafe { ngx_queue_init(&mut self.head) } + } + + unsafe { ngx_queue_insert_after(&mut self.head, entry.to_queue()) } + } + + /// Returns an iterator over the entries of the queue. + pub fn iter(&self) -> NgxQueueIter<'_, T> { + NgxQueueIter::new(&self.head) + } + + /// Returns a mutable iterator over the entries of the queue. + pub fn iter_mut(&mut self) -> NgxQueueIterMut<'_, T> { + NgxQueueIterMut::new(&mut self.head) + } +} + +/// An iterator for the queue. +pub struct NgxQueueIter<'a, T> { + head: NonNull, + current: NonNull, + _lifetime: PhantomData<&'a T>, +} + +impl<'a, T> NgxQueueIter<'a, T> +where + T: NgxQueueEntry, +{ + /// Creates a new queue iterator. + pub fn new(head: &'a ngx_queue_t) -> Self { + let head = NonNull::from(head); + NgxQueueIter { + head, + current: head, + _lifetime: PhantomData, + } + } +} + +impl<'a, T> Iterator for NgxQueueIter<'a, T> +where + T: NgxQueueEntry + 'a, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + unsafe { + let next = NonNull::new(self.current.as_ref().next)?; + if next == self.head { + return None; + } + + self.current = next; + Some(T::from_queue(self.current).as_ref()) + } + } +} + +/// A mutable iterator for the queue. +pub struct NgxQueueIterMut<'a, T> { + head: NonNull, + current: NonNull, + _lifetime: PhantomData<&'a T>, +} + +impl<'a, T> NgxQueueIterMut<'a, T> +where + T: NgxQueueEntry, +{ + /// Creates a new mutable queue iterator. + pub fn new(head: &'a mut ngx_queue_t) -> Self { + let head = NonNull::from(head); + NgxQueueIterMut { + head, + current: head, + _lifetime: PhantomData, + } + } +} + +impl<'a, T> Iterator for NgxQueueIterMut<'a, T> +where + T: NgxQueueEntry + 'a, +{ + type Item = &'a mut T; + + fn next(&mut self) -> Option { + unsafe { + let next = NonNull::new(self.current.as_ref().next)?; + if next == self.head { + return None; + } + + self.current = next; + Some(T::from_queue(self.current).as_mut()) + } + } +} + +/// A doubly-linked list that owns elements of type `T` backed by the specified allocator `A`. +#[derive(Debug)] +pub struct Queue +where + A: Allocator, +{ + // The address of the NgxQueue with queue head has to be stable, as the queue elements will + // contain pointers to the head. + raw: NonNull>>, + len: usize, + alloc: A, +} + +impl Drop for Queue +where + A: Allocator, +{ + fn drop(&mut self) { + while self.pop_front().is_some() {} + + let layout = Layout::for_value(unsafe { self.raw.as_ref() }); + unsafe { self.allocator().deallocate(self.raw.cast(), layout) }; + } +} + +unsafe impl Send for Queue +where + A: Send + Allocator, + T: Send, +{ +} + +unsafe impl Sync for Queue +where + A: Sync + Allocator, + T: Sync, +{ +} + +impl Queue { + /// Creates a new list with specified allocator. + pub fn try_new_in(alloc: A) -> Result { + let raw = NgxQueue { + head: unsafe { mem::zeroed() }, + _type: PhantomData, + }; + let raw = crate::allocator::allocate(raw, &alloc)?; + Ok(Self { raw, len: 0, alloc }) + } + + /// Returns a reference to the underlying allocator. + pub fn allocator(&self) -> &A { + &self.alloc + } + + /// Returns `true` if the list contains no elements. + pub fn is_empty(&self) -> bool { + self.raw().is_empty() + } + + /// Returns the number of elements in the queue. + pub fn len(&self) -> usize { + self.len + } + + /// Returns an iterator over the entries of the list. + pub fn iter(&self) -> QueueIter<'_, T> { + QueueIter::new(&self.raw().head) + } + + /// Returns a mutable iterator over the entries of the list. + pub fn iter_mut(&mut self) -> QueueIterMut<'_, T> { + QueueIterMut::new(&mut self.raw_mut().head) + } + + /// Removes the last element and returns it or `None` if the list is empty. + pub fn pop_back(&mut self) -> Option { + if self.is_empty() { + return None; + } + let node = NonNull::new(self.raw_mut().head.prev)?; + Some(unsafe { self.remove(node) }) + } + + /// Removes the first element and returns it or `None` if the list is empty. + pub fn pop_front(&mut self) -> Option { + if self.is_empty() { + return None; + } + let node = NonNull::new(self.raw_mut().head.next)?; + Some(unsafe { self.remove(node) }) + } + + /// Appends an element to the end of the list. + pub fn push_back(&mut self, item: T) -> Result<&mut T, AllocError> { + let mut entry = QueueEntry::new_in(item, self.allocator())?; + let entry = unsafe { entry.as_mut() }; + self.raw_mut().push_back(entry); + self.len += 1; + Ok(&mut entry.item) + } + + /// Appends an element to the beginning of the list. + pub fn push_front(&mut self, item: T) -> Result<&mut T, AllocError> { + let mut entry = QueueEntry::new_in(item, self.allocator())?; + let entry = unsafe { entry.as_mut() }; + self.raw_mut().push_front(entry); + self.len += 1; + Ok(&mut entry.item) + } + + fn raw(&self) -> &NgxQueue> { + // SAFETY: we allocated this pointer as well-aligned and convertible to reference. + unsafe { self.raw.as_ref() } + } + + fn raw_mut(&mut self) -> &mut NgxQueue> { + // SAFETY: we allocated this pointer as well-aligned and convertible to reference. + unsafe { self.raw.as_mut() } + } + + /// Removes a node from the queue and returns the contained value. + /// + /// # Safety + /// + /// `node` must be an element of this list. + unsafe fn remove(&mut self, node: NonNull) -> T { + ngx_queue_remove(node.as_ptr()); + self.len -= 1; + + let entry = QueueEntry::::from_queue(node); + let copy = entry.read(); + // Skip drop as QueueEntry is already copied to `x`. + self.allocator() + .deallocate(entry.cast(), Layout::for_value(entry.as_ref())); + copy.item + } +} + +#[derive(Debug)] +struct QueueEntry { + queue: ngx_queue_t, + item: T, +} + +unsafe impl NgxQueueEntry for QueueEntry { + fn from_queue(queue: NonNull) -> NonNull { + unsafe { ngx_queue_data!(queue, Self, queue) } + } + + fn to_queue(&mut self) -> &mut ngx_queue_t { + &mut self.queue + } +} + +impl QueueEntry { + pub fn new_in(item: T, alloc: &impl Allocator) -> Result, AllocError> { + let p: NonNull = alloc.allocate(Layout::new::())?.cast(); + + unsafe { + let u = p.cast::>().as_mut(); + // does not read the uninitialized data + ngx_queue_init(&mut u.assume_init_mut().queue); + ptr::write(&mut u.assume_init_mut().item, item); + } + + Ok(p) + } +} + +/// An iterator for the linked list [Queue]. +pub struct QueueIter<'a, T>(NgxQueueIter<'a, QueueEntry>); + +impl<'a, T> QueueIter<'a, T> { + /// Creates a new iterator for the linked list. + pub fn new(head: &'a ngx_queue_t) -> Self { + Self(NgxQueueIter::new(head)) + } +} + +impl<'a, T> Iterator for QueueIter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + Some(&self.0.next()?.item) + } +} + +/// A mutable iterator for the linked list [Queue]. +pub struct QueueIterMut<'a, T>(NgxQueueIterMut<'a, QueueEntry>); + +impl<'a, T> QueueIterMut<'a, T> { + /// Creates a new mutable iterator for the linked list. + pub fn new(head: &'a mut ngx_queue_t) -> Self { + Self(NgxQueueIterMut::new(head)) + } +} + +impl<'a, T> Iterator for QueueIterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + Some(&mut self.0.next()?.item) + } +}