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};
pub unsafe trait NgxQueueEntry {
fn from_queue(queue: NonNull<ngx_queue_t>) -> NonNull<Self>;
fn to_queue(&mut self) -> &mut ngx_queue_t;
}
unsafe impl NgxQueueEntry for ngx_queue_t {
fn from_queue(queue: NonNull<ngx_queue_t>) -> NonNull<Self> {
queue
}
fn to_queue(&mut self) -> &mut ngx_queue_t {
self
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct NgxQueue<T> {
head: ngx_queue_t,
_type: PhantomData<T>,
}
impl<T> NgxQueue<T>
where
T: NgxQueueEntry,
{
pub unsafe fn from_ptr<'a>(head: *const ngx_queue_t) -> &'a Self {
&*head.cast()
}
pub unsafe fn from_ptr_mut<'a>(head: *mut ngx_queue_t) -> &'a mut Self {
&mut *head.cast()
}
pub fn is_empty(&self) -> bool {
self.head.prev.is_null() || unsafe { ngx_queue_empty(&self.head) }
}
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()) }
}
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()) }
}
pub fn iter(&self) -> NgxQueueIter<'_, T> {
NgxQueueIter::new(&self.head)
}
pub fn iter_mut(&mut self) -> NgxQueueIterMut<'_, T> {
NgxQueueIterMut::new(&mut self.head)
}
}
pub struct NgxQueueIter<'a, T> {
head: NonNull<ngx_queue_t>,
current: NonNull<ngx_queue_t>,
_lifetime: PhantomData<&'a T>,
}
impl<'a, T> NgxQueueIter<'a, T>
where
T: NgxQueueEntry,
{
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<Self::Item> {
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())
}
}
}
pub struct NgxQueueIterMut<'a, T> {
head: NonNull<ngx_queue_t>,
current: NonNull<ngx_queue_t>,
_lifetime: PhantomData<&'a T>,
}
impl<'a, T> NgxQueueIterMut<'a, T>
where
T: NgxQueueEntry,
{
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<Self::Item> {
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())
}
}
}
#[derive(Debug)]
pub struct Queue<T, A>
where
A: Allocator,
{
raw: NonNull<NgxQueue<QueueEntry<T>>>,
len: usize,
alloc: A,
}
impl<T, A> Drop for Queue<T, A>
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<T, A> Send for Queue<T, A>
where
A: Send + Allocator,
T: Send,
{
}
unsafe impl<T, A> Sync for Queue<T, A>
where
A: Sync + Allocator,
T: Sync,
{
}
impl<T, A: Allocator> Queue<T, A> {
pub fn try_new_in(alloc: A) -> Result<Self, AllocError> {
let raw = NgxQueue {
head: unsafe { mem::zeroed() },
_type: PhantomData,
};
let raw = crate::allocator::allocate(raw, &alloc)?;
Ok(Self { raw, len: 0, alloc })
}
pub fn allocator(&self) -> &A {
&self.alloc
}
pub fn is_empty(&self) -> bool {
self.raw().is_empty()
}
pub fn len(&self) -> usize {
self.len
}
pub fn iter(&self) -> QueueIter<'_, T> {
QueueIter::new(&self.raw().head)
}
pub fn iter_mut(&mut self) -> QueueIterMut<'_, T> {
QueueIterMut::new(&mut self.raw_mut().head)
}
pub fn pop_back(&mut self) -> Option<T> {
if self.is_empty() {
return None;
}
let node = NonNull::new(self.raw_mut().head.prev)?;
Some(unsafe { self.remove(node) })
}
pub fn pop_front(&mut self) -> Option<T> {
if self.is_empty() {
return None;
}
let node = NonNull::new(self.raw_mut().head.next)?;
Some(unsafe { self.remove(node) })
}
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)
}
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<QueueEntry<T>> {
unsafe { self.raw.as_ref() }
}
fn raw_mut(&mut self) -> &mut NgxQueue<QueueEntry<T>> {
unsafe { self.raw.as_mut() }
}
unsafe fn remove(&mut self, node: NonNull<ngx_queue_t>) -> T {
ngx_queue_remove(node.as_ptr());
self.len -= 1;
let entry = QueueEntry::<T>::from_queue(node);
let copy = entry.read();
self.allocator()
.deallocate(entry.cast(), Layout::for_value(entry.as_ref()));
copy.item
}
}
#[derive(Debug)]
struct QueueEntry<T> {
queue: ngx_queue_t,
item: T,
}
unsafe impl<T> NgxQueueEntry for QueueEntry<T> {
fn from_queue(queue: NonNull<ngx_queue_t>) -> NonNull<Self> {
unsafe { ngx_queue_data!(queue, Self, queue) }
}
fn to_queue(&mut self) -> &mut ngx_queue_t {
&mut self.queue
}
}
impl<T> QueueEntry<T> {
pub fn new_in(item: T, alloc: &impl Allocator) -> Result<NonNull<Self>, AllocError> {
let p: NonNull<Self> = alloc.allocate(Layout::new::<Self>())?.cast();
unsafe {
let u = p.cast::<mem::MaybeUninit<Self>>().as_mut();
ngx_queue_init(&mut u.assume_init_mut().queue);
ptr::write(&mut u.assume_init_mut().item, item);
}
Ok(p)
}
}
pub struct QueueIter<'a, T>(NgxQueueIter<'a, QueueEntry<T>>);
impl<'a, T> QueueIter<'a, T> {
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<Self::Item> {
Some(&self.0.next()?.item)
}
}
pub struct QueueIterMut<'a, T>(NgxQueueIterMut<'a, QueueEntry<T>>);
impl<'a, T> QueueIterMut<'a, T> {
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<Self::Item> {
Some(&mut self.0.next()?.item)
}
}