nginx_sys/
queue.rs

1use core::ptr;
2
3use crate::bindings::ngx_queue_t;
4
5/// Get a reference to the beginning of a queue node data structure,
6/// considering the queue field offset in it.
7///
8/// # Safety
9///
10/// `$q` must be a valid pointer to the field `$link` in the struct `$type`
11#[macro_export]
12macro_rules! ngx_queue_data {
13    ($q:expr, $type:path, $link:ident) => {
14        $q.byte_sub(::core::mem::offset_of!($type, $link)).cast::<$type>()
15    };
16}
17
18/// Initializes the queue head before use.
19///
20/// # Safety
21///
22/// `q` must be a valid pointer to [ngx_queue_t].
23#[inline]
24pub unsafe fn ngx_queue_init(q: *mut ngx_queue_t) {
25    (*q).prev = q;
26    (*q).next = q;
27}
28
29/// Returns `true` if the queue contains no elements.
30///
31/// # Safety
32///
33/// `q` must be a valid pointer to [ngx_queue_t], initialized with [ngx_queue_init].
34#[inline]
35pub unsafe fn ngx_queue_empty(q: *const ngx_queue_t) -> bool {
36    q == (*q).prev
37}
38
39/// Inserts a new node after the current.
40///
41/// # Safety
42///
43/// Both `q` and `x` must be valid pointers to [ngx_queue_t]
44#[inline]
45pub unsafe fn ngx_queue_insert_after(q: *mut ngx_queue_t, x: *mut ngx_queue_t) {
46    (*x).next = (*q).next;
47    (*(*x).next).prev = x;
48    (*x).prev = q;
49    (*q).next = x;
50}
51
52/// Inserts a new node before the current.
53///
54/// # Safety
55///
56/// Both `q` and `x` must be valid pointers to [ngx_queue_t].
57#[inline]
58pub unsafe fn ngx_queue_insert_before(q: *mut ngx_queue_t, x: *mut ngx_queue_t) {
59    (*x).prev = (*q).prev;
60    (*(*x).prev).next = x;
61    (*x).next = q;
62    (*q).prev = x;
63}
64
65/// Removes a node from the queue.
66///
67/// # Safety
68///
69/// `q` must be a valid pointer to an [ngx_queue_t] node.
70#[inline]
71pub unsafe fn ngx_queue_remove(q: *mut ngx_queue_t) {
72    (*(*q).next).prev = (*q).prev;
73    (*(*q).prev).next = (*q).next;
74    (*q).prev = ptr::null_mut();
75    (*q).next = ptr::null_mut();
76}
77
78/// Splits a queue at a node, returning the queue tail in a separate queue.
79///
80/// # Safety
81///
82/// `h` must be a valid pointer to a head queue node.
83/// `q` must be a node in the queue `h`.
84/// `n` must be a valid pointer to [ngx_queue_t].
85#[inline]
86pub unsafe fn ngx_queue_split(h: *mut ngx_queue_t, q: *mut ngx_queue_t, n: *mut ngx_queue_t) {
87    (*n).prev = (*h).prev;
88    (*(*n).prev).next = n;
89    (*n).next = q;
90    (*h).prev = (*q).prev;
91    (*(*h).prev).next = h;
92    (*q).prev = n;
93}
94
95/// Adds a second queue to the first queue.
96///
97/// # Safety
98///
99/// Both `h` and `n` must be valid pointers to queue heads, initialized with [ngx_queue_init].
100/// `n` will be left in invalid state, pointing to the subrange of `h` without back references.
101#[inline]
102pub unsafe fn ngx_queue_add(h: *mut ngx_queue_t, n: *mut ngx_queue_t) {
103    (*(*h).prev).next = (*n).next;
104    (*(*n).next).prev = (*h).prev;
105    (*h).prev = (*n).prev;
106    (*(*h).prev).next = h;
107}
108
109impl ngx_queue_t {
110    /// Returns `true` if the queue contains no elements.
111    pub fn is_empty(&self) -> bool {
112        unsafe { ngx_queue_empty(self) }
113    }
114}
115
116impl Default for ngx_queue_t {
117    fn default() -> ngx_queue_t {
118        ngx_queue_t {
119            prev: ptr::null_mut(),
120            next: ptr::null_mut(),
121        }
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    extern crate alloc;
128    use alloc::boxed::Box;
129
130    use super::*;
131
132    struct TestData {
133        value: usize,
134        queue: ngx_queue_t,
135    }
136
137    impl TestData {
138        pub fn new(value: usize) -> *mut Self {
139            // We should be using `ngx_pool_t` here, but that is not possible without linking to
140            // the nginx
141            let mut x = Box::new(Self {
142                value,
143                queue: Default::default(),
144            });
145            unsafe { ngx_queue_init(ptr::addr_of_mut!(x.queue)) };
146            Box::into_raw(x)
147        }
148
149        pub unsafe fn free(x: *mut Self) {
150            let _ = Box::from_raw(x);
151        }
152    }
153
154    impl Drop for TestData {
155        fn drop(&mut self) {
156            if !self.queue.next.is_null() && !self.queue.is_empty() {
157                unsafe { ngx_queue_remove(ptr::addr_of_mut!(self.queue)) };
158            }
159        }
160    }
161
162    struct Iter {
163        h: *mut ngx_queue_t,
164        q: *mut ngx_queue_t,
165        next: fn(*mut ngx_queue_t) -> *mut ngx_queue_t,
166    }
167
168    impl Iter {
169        pub fn new(h: *mut ngx_queue_t) -> Self {
170            let next = |x: *mut ngx_queue_t| unsafe { (*x).next };
171            Self { h, q: next(h), next }
172        }
173
174        pub fn new_reverse(h: *mut ngx_queue_t) -> Self {
175            let next = |x: *mut ngx_queue_t| unsafe { (*x).prev };
176            Self { h, q: next(h), next }
177        }
178    }
179
180    impl Iterator for Iter {
181        type Item = *mut ngx_queue_t;
182
183        fn next(&mut self) -> Option<Self::Item> {
184            if self.h == self.q {
185                return None;
186            }
187
188            let item = self.q;
189            self.q = (self.next)(self.q);
190            Some(item)
191        }
192    }
193
194    #[test]
195    fn test_queue() {
196        fn value(q: *mut ngx_queue_t) -> usize {
197            unsafe { (*ngx_queue_data!(q, TestData, queue)).value }
198        }
199
200        // Check forward and reverse iteration
201        fn cmp(h: *mut ngx_queue_t, other: &[usize]) -> bool {
202            Iter::new(h).map(value).eq(other.iter().cloned())
203                && Iter::new_reverse(h).map(value).eq(other.iter().rev().cloned())
204        }
205
206        // Note how this test does not use references or borrows to avoid triggering UBs
207        // detectable by Miri. This does not mean that the code is safe or sound.
208        unsafe {
209            // Initialize and fill the queue
210
211            let mut h1 = ngx_queue_t::default();
212            ngx_queue_init(ptr::addr_of_mut!(h1));
213
214            let mut h2 = ngx_queue_t::default();
215            ngx_queue_init(ptr::addr_of_mut!(h2));
216
217            for i in 1..=5 {
218                let elem = TestData::new(i);
219                ngx_queue_insert_before(ptr::addr_of_mut!(h1), ptr::addr_of_mut!((*elem).queue));
220
221                let elem = TestData::new(i);
222                ngx_queue_insert_after(ptr::addr_of_mut!(h2), ptr::addr_of_mut!((*elem).queue));
223            }
224
225            // Iterate and test the values
226
227            assert!(cmp(ptr::addr_of_mut!(h1), &[1, 2, 3, 4, 5]));
228            assert!(cmp(ptr::addr_of_mut!(h2), &[5, 4, 3, 2, 1]));
229
230            // Move nodes from h2 to h1
231
232            // h2 still points to the subrange of h1 after this operation
233            ngx_queue_add(ptr::addr_of_mut!(h1), ptr::addr_of_mut!(h2));
234
235            assert!(cmp(ptr::addr_of_mut!(h1), &[1, 2, 3, 4, 5, 5, 4, 3, 2, 1]));
236
237            ngx_queue_split(ptr::addr_of_mut!(h1), (*h2.next).next, ptr::addr_of_mut!(h2));
238
239            assert!(cmp(ptr::addr_of_mut!(h1), &[1, 2, 3, 4, 5, 5]));
240            assert!(cmp(ptr::addr_of_mut!(h2), &[4, 3, 2, 1]));
241
242            // Cleanup
243
244            for q in Iter::new(ptr::addr_of_mut!(h1)) {
245                let td = ngx_queue_data!(q, TestData, queue);
246                TestData::free(td);
247            }
248            assert!(h1.is_empty());
249
250            for q in Iter::new(ptr::addr_of_mut!(h2)) {
251                let td = ngx_queue_data!(q, TestData, queue);
252                TestData::free(td);
253            }
254            assert!(h2.is_empty());
255        };
256    }
257}