use core::ptr;
use crate::bindings::ngx_queue_t;
#[macro_export]
macro_rules! ngx_queue_data {
($q:expr, $type:path, $link:ident) => {
$q.byte_sub(::core::mem::offset_of!($type, $link))
.cast::<$type>()
};
}
#[inline]
pub unsafe fn ngx_queue_init(q: *mut ngx_queue_t) {
(*q).prev = q;
(*q).next = q;
}
#[inline]
pub unsafe fn ngx_queue_empty(q: *const ngx_queue_t) -> bool {
ptr::eq(q, (*q).prev)
}
#[inline]
pub unsafe fn ngx_queue_insert_after(q: *mut ngx_queue_t, x: *mut ngx_queue_t) {
(*x).next = (*q).next;
(*(*x).next).prev = x;
(*x).prev = q;
(*q).next = x;
}
#[inline]
pub unsafe fn ngx_queue_insert_before(q: *mut ngx_queue_t, x: *mut ngx_queue_t) {
(*x).prev = (*q).prev;
(*(*x).prev).next = x;
(*x).next = q;
(*q).prev = x;
}
#[inline]
pub unsafe fn ngx_queue_remove(q: *mut ngx_queue_t) {
(*(*q).next).prev = (*q).prev;
(*(*q).prev).next = (*q).next;
(*q).prev = ptr::null_mut();
(*q).next = ptr::null_mut();
}
#[inline]
pub unsafe fn ngx_queue_split(h: *mut ngx_queue_t, q: *mut ngx_queue_t, n: *mut ngx_queue_t) {
(*n).prev = (*h).prev;
(*(*n).prev).next = n;
(*n).next = q;
(*h).prev = (*q).prev;
(*(*h).prev).next = h;
(*q).prev = n;
}
#[inline]
pub unsafe fn ngx_queue_add(h: *mut ngx_queue_t, n: *mut ngx_queue_t) {
(*(*h).prev).next = (*n).next;
(*(*n).next).prev = (*h).prev;
(*h).prev = (*n).prev;
(*(*h).prev).next = h;
}
impl ngx_queue_t {
pub fn is_empty(&self) -> bool {
unsafe { ngx_queue_empty(self) }
}
}
impl Default for ngx_queue_t {
fn default() -> ngx_queue_t {
ngx_queue_t {
prev: ptr::null_mut(),
next: ptr::null_mut(),
}
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::boxed::Box;
use super::*;
struct TestData {
value: usize,
queue: ngx_queue_t,
}
impl TestData {
pub fn new(value: usize) -> *mut Self {
let mut x = Box::new(Self {
value,
queue: Default::default(),
});
unsafe { ngx_queue_init(ptr::addr_of_mut!(x.queue)) };
Box::into_raw(x)
}
pub unsafe fn free(x: *mut Self) {
let _ = Box::from_raw(x);
}
}
impl Drop for TestData {
fn drop(&mut self) {
if !self.queue.next.is_null() && !self.queue.is_empty() {
unsafe { ngx_queue_remove(ptr::addr_of_mut!(self.queue)) };
}
}
}
struct Iter {
h: *mut ngx_queue_t,
q: *mut ngx_queue_t,
next: fn(*mut ngx_queue_t) -> *mut ngx_queue_t,
}
impl Iter {
pub fn new(h: *mut ngx_queue_t) -> Self {
let next = |x: *mut ngx_queue_t| unsafe { (*x).next };
Self {
h,
q: next(h),
next,
}
}
pub fn new_reverse(h: *mut ngx_queue_t) -> Self {
let next = |x: *mut ngx_queue_t| unsafe { (*x).prev };
Self {
h,
q: next(h),
next,
}
}
}
impl Iterator for Iter {
type Item = *mut ngx_queue_t;
fn next(&mut self) -> Option<Self::Item> {
if ptr::eq(self.h, self.q) {
return None;
}
let item = self.q;
self.q = (self.next)(self.q);
Some(item)
}
}
#[test]
fn test_queue() {
fn value(q: *mut ngx_queue_t) -> usize {
unsafe { (*ngx_queue_data!(q, TestData, queue)).value }
}
fn cmp(h: *mut ngx_queue_t, other: &[usize]) -> bool {
Iter::new(h).map(value).eq(other.iter().cloned())
&& Iter::new_reverse(h)
.map(value)
.eq(other.iter().rev().cloned())
}
unsafe {
let mut h1 = ngx_queue_t::default();
ngx_queue_init(ptr::addr_of_mut!(h1));
let mut h2 = ngx_queue_t::default();
ngx_queue_init(ptr::addr_of_mut!(h2));
for i in 1..=5 {
let elem = TestData::new(i);
ngx_queue_insert_before(ptr::addr_of_mut!(h1), ptr::addr_of_mut!((*elem).queue));
let elem = TestData::new(i);
ngx_queue_insert_after(ptr::addr_of_mut!(h2), ptr::addr_of_mut!((*elem).queue));
}
assert!(cmp(ptr::addr_of_mut!(h1), &[1, 2, 3, 4, 5]));
assert!(cmp(ptr::addr_of_mut!(h2), &[5, 4, 3, 2, 1]));
ngx_queue_add(ptr::addr_of_mut!(h1), ptr::addr_of_mut!(h2));
assert!(cmp(ptr::addr_of_mut!(h1), &[1, 2, 3, 4, 5, 5, 4, 3, 2, 1]));
ngx_queue_split(
ptr::addr_of_mut!(h1),
(*h2.next).next,
ptr::addr_of_mut!(h2),
);
assert!(cmp(ptr::addr_of_mut!(h1), &[1, 2, 3, 4, 5, 5]));
assert!(cmp(ptr::addr_of_mut!(h2), &[4, 3, 2, 1]));
for q in Iter::new(ptr::addr_of_mut!(h1)) {
let td = ngx_queue_data!(q, TestData, queue);
TestData::free(td);
}
assert!(h1.is_empty());
for q in Iter::new(ptr::addr_of_mut!(h2)) {
let td = ngx_queue_data!(q, TestData, queue);
TestData::free(td);
}
assert!(h2.is_empty());
};
}
}