Skip to main content

osiris/types/
queue.rs

1//! This module provides a queue implementation.
2
3use crate::error::Result;
4
5use super::array::Vec;
6use super::boxed::Box;
7
8/// A ring-buffer based queue, with N elements stored inline. TODO: Make this growable.
9#[proc_macros::fmt]
10pub struct Queue<T: Clone, const N: usize> {
11    data: Vec<T, N>,
12    len: usize,
13    front: usize,
14}
15
16#[allow(dead_code)]
17impl<T: Clone + Copy, const N: usize> Queue<T, N> {
18    /// Create a new empty queue.
19    pub const fn new() -> Self {
20        Self {
21            data: Vec::new(),
22            len: 0,
23            front: 0,
24        }
25    }
26
27    /// Push a value onto the back of the queue.
28    ///
29    /// `value` - The value to push onto the back of the queue.
30    ///
31    /// Returns `Ok(())` if the value was pushed onto the back of the queue, or an error if the queue is full.
32    pub fn push_back(&mut self, value: T) -> Result<()> {
33        if self.len == self.data.capacity() {
34            return Err(kerr!(ENOMEM));
35        }
36        self.len += 1;
37        if self.data.len() != self.data.capacity() {
38            self.data.push(value)?;
39        } else {
40            self.insert(self.len - 1, value)?;
41        }
42
43        Ok(())
44    }
45
46    /// Pop a value from the front of the queue.
47    ///
48    /// Returns the value at the front of the queue, or `None` if the queue is empty.
49    pub fn pop_front(&mut self) -> Option<T> {
50        if self.len == 0 {
51            return None;
52        }
53
54        let value = self.data.at(self.front).cloned();
55
56        self.front = (self.front + 1) % self.data.capacity();
57        self.len -= 1;
58        value
59    }
60
61    /// Insert a value at the given index in the queue.
62    ///
63    /// `index` - The index to insert the value at.
64    /// `value` - The value to insert.
65    ///
66    /// Returns `Ok(())` if the value was inserted at the given index, or an error if the index is out of bounds.
67    pub fn insert(&mut self, index: usize, value: T) -> Result<()> {
68        if index >= self.len() {
69            return Err(kerr!(EINVAL));
70        }
71        let real_idx = (self.front + index) % self.data.capacity();
72        self.data
73            .at_mut(real_idx)
74            .map(|insertion_point| *insertion_point = value)
75            .ok_or(kerr!(EINVAL))
76    }
77
78    /// Returns the value at the front of the queue.
79    pub fn front(&self) -> Option<&T> {
80        if self.is_empty() {
81            return None;
82        }
83
84        self.data.at(self.front)
85    }
86
87    /// Returns the value at the back of the queue.
88    pub fn back(&self) -> Option<&T> {
89        if self.is_empty() {
90            return None;
91        }
92        let back = (self.front + self.len - 1) % self.data.capacity();
93        self.data.at(back)
94    }
95
96    /// Returns the length of the queue.
97    pub fn len(&self) -> usize {
98        self.len
99    }
100
101    /// Returns `true` if the queue is empty.
102    pub fn is_empty(&self) -> bool {
103        self.len == 0
104    }
105
106    /// Increases total space in the queue preserving queue-integrity, potentially reallocating and copying existing contents
107    ///
108    /// `new_size` - The total amount of space in the queue afterwards
109    ///
110    /// Returns `Ok(())` if the queue was successfully enlargened or the requested size was smaller than the current capacity.
111    /// Returns An error if the queue could not be grown
112    pub fn grow_capacity(&mut self, new_size: usize) -> Result<()> {
113        if new_size <= self.data.capacity() {
114            return Ok(());
115        }
116        // if the queue wraps
117        if self.front + self.len >= self.data.capacity() {
118            // copy the queue to the front to make further logic straighforward
119            // When the queue wraps around the end, the wrapping would not happen anymore with the new size
120
121            // we could do some complicated in-place swapping here instead of using a potentially expensive temporary storage
122            let non_wrapping_queue_start_len = self.data.capacity() - self.front;
123            let mut swap_helper = Box::new_slice_uninit(non_wrapping_queue_start_len)?;
124            bug_on!(swap_helper.len() != non_wrapping_queue_start_len);
125
126            // we take the start of the queue (which is located at the end of the curr memory region) and copy it to temp storage
127            for i in 0..swap_helper.len() {
128                // Returning an error here should never happen if the queue is in a consistant state prior. If not no guarantees about contents are made.
129                swap_helper[i].write(self.data.at(self.front + i).copied().ok_or(kerr!(EINVAL))?);
130            }
131            // One past the logically last element of the queue
132            let end = (self.front + self.len) % self.data.capacity();
133            // now move the logical end of the queue further back to make space for the logical start
134            for i in 0..end {
135                bug_on!(i + non_wrapping_queue_start_len >= self.data.capacity());
136                self.data.swap(i, i + non_wrapping_queue_start_len);
137            }
138            // now copy the data back from the temp helper
139            for i in 0..non_wrapping_queue_start_len {
140                // Safety: values copied into our helper are part of the active queue, must therefore be initialized
141                if let Some(el) = self.data.at_mut(i) {
142                    *el = unsafe { swap_helper[i].assume_init() };
143                }
144            }
145            self.front = 0;
146        }
147        self.data.reserve_total_capacity(new_size)
148    }
149}
150
151// TESTING ------------------------------------------------------------------------------------------------------------
152
153#[cfg(test)]
154mod tests {
155    use hal_api::mem::PhysAddr;
156
157    use super::*;
158    use crate::mem::GLOBAL_ALLOCATOR;
159    use core::ops::Range;
160
161    fn alloc_range(length: usize) -> Range<PhysAddr> {
162        let alloc_range = std::alloc::Layout::from_size_align(length, align_of::<u128>()).unwrap();
163        let ptr = unsafe { std::alloc::alloc(alloc_range) };
164        PhysAddr::new(ptr as usize)..PhysAddr::new(ptr as usize) + length
165    }
166
167    fn setup_memory(mem_size: usize) {
168        unsafe {
169            GLOBAL_ALLOCATOR
170                .lock()
171                .add_range(&alloc_range(mem_size))
172                .unwrap()
173        };
174    }
175
176    #[test]
177    fn growing_retains_queue_state_without_wrapping() {
178        setup_memory(1000);
179        let mut queue = Queue::<usize, 10>::new();
180        for i in 0..10 {
181            assert_eq!(queue.push_back(i), Ok(()));
182        }
183
184        assert_eq!(queue.grow_capacity(20), Ok(()));
185        for i in 0..10 {
186            assert_eq!(queue.pop_front(), Some(i));
187        }
188    }
189
190    #[test]
191    fn growing_retains_queue_state_with_wrapping() {
192        setup_memory(1000);
193        let mut queue = Queue::<usize, 10>::new();
194        for i in 0..10 {
195            assert_eq!(queue.push_back(i), Ok(()));
196        }
197        // sanity check that queue really is full
198        assert_eq!(queue.push_back(1), Err(kerr!(ENOMEM)));
199        assert_eq!(queue.len(), 10);
200
201        // pop and subsequently push more elements to make queue wrap
202        for i in 0..5 {
203            assert_eq!(queue.pop_front(), Some(i));
204        }
205
206        assert_eq!(*queue.front().unwrap(), 5);
207        assert_eq!(*queue.back().unwrap(), 9);
208        assert_eq!(queue.len(), 5);
209
210        for i in 10..15 {
211            assert_eq!(queue.push_back(i), Ok(()));
212        }
213
214        assert_eq!(queue.len(), 10);
215        assert_eq!(*queue.front().unwrap(), 5);
216        assert_eq!(*queue.back().unwrap(), 14);
217        assert_eq!(queue.grow_capacity(20), Ok(()));
218        for i in 5..15 {
219            assert_eq!(queue.pop_front(), Some(i));
220        }
221    }
222
223    #[test]
224    fn growing_to_smaller_size_has_no_effect() {
225        setup_memory(1000);
226        let mut queue = Queue::<usize, 10>::new();
227        for i in 0..10 {
228            assert_eq!(queue.push_back(i), Ok(()));
229        }
230        assert_eq!(queue.len(), 10);
231        queue.grow_capacity(1).unwrap();
232        assert_eq!(queue.len(), 10);
233        assert_eq!(queue.push_back(1), Err(kerr!(ENOMEM)));
234    }
235
236    #[test]
237    fn growing_multiple_times_consecutively_retains_state() {
238        setup_memory(10000);
239        let mut queue = Queue::<usize, 10>::new();
240        for i in 0..10 {
241            assert_eq!(queue.push_back(i), Ok(()));
242        }
243        assert_eq!(queue.len(), 10);
244
245        // pop and subsequently push more elements to make queue wrap
246        for i in 0..5 {
247            assert_eq!(queue.pop_front(), Some(i));
248        }
249        assert_eq!(queue.len(), 5);
250
251        for i in 10..15 {
252            assert_eq!(queue.push_back(i), Ok(()));
253        }
254
255        assert_eq!(queue.len(), 10);
256        assert_eq!(*queue.front().unwrap(), 5);
257        assert_eq!(*queue.back().unwrap(), 14);
258        assert_eq!(queue.grow_capacity(20), Ok(()));
259        assert_eq!(queue.grow_capacity(40), Ok(()));
260        assert_eq!(queue.grow_capacity(100), Ok(()));
261        assert_eq!(queue.grow_capacity(200), Ok(()));
262        for i in 5..15 {
263            assert_eq!(queue.pop_front(), Some(i));
264        }
265    }
266}
267// END TESTING
268
269// VERIFICATION -------------------------------------------------------------------------------------------------------
270#[cfg(kani)]
271mod verification {
272    use super::*;
273    use core::cmp::max;
274    use std::cmp::min;
275    use std::vec::Vec;
276
277    #[test]
278    fn kani_concrete_playback_growing_retains_queue_state_with_wrapping_7154119071478699851() {
279        let concrete_vals: Vec<Vec<u8>> = vec![
280            // 99968ul
281            vec![128, 134, 1, 0, 0, 0, 0, 0],
282        ];
283        kani::concrete_playback_run(concrete_vals, growing_retains_queue_state_with_wrapping);
284    }
285
286    //#[kani::proof]
287    //#[kani::unwind(15)]
288    fn growing_retains_queue_state_with_wrapping() {
289        let mut queue = Queue::<usize, 10>::new();
290        for i in 0..10 {
291            assert_eq!(queue.push_back(i), Ok(()));
292        }
293        // sanity check that queue really is full
294        assert_eq!(queue.push_back(1), Err(kerr!(ENOMEM)));
295        assert_eq!(queue.len(), 10);
296
297        // pop and subsequently push more elements to make queue wrap
298        for i in 0..5 {
299            assert_eq!(queue.pop_front(), Some(i));
300        }
301
302        assert_eq!(*queue.front().unwrap(), 5);
303        assert_eq!(*queue.back().unwrap(), 9);
304        assert_eq!(queue.len(), 5);
305
306        for i in 10..15 {
307            assert_eq!(queue.push_back(i), Ok(()));
308        }
309
310        assert_eq!(queue.len(), 10);
311        assert_eq!(*queue.front().unwrap(), 5);
312        assert_eq!(*queue.back().unwrap(), 14);
313        let new_cap = kani::any();
314        let res = queue.grow_capacity(new_cap);
315
316        if res == Ok(()) && new_cap > 10 {
317            for i in 0..(new_cap - 10) {
318                // add some more elements for good measure
319                assert_eq!(queue.push_back(i), Ok(()));
320            }
321        }
322
323        for i in 5..15 {
324            assert_eq!(queue.pop_front(), Some(i));
325        }
326
327        while !queue.is_empty() {
328            let _ = queue.pop_front();
329        }
330
331        // now add and remove elements again to show queue still works as expected
332        if res == Ok(()) && new_cap > 10 {
333            for i in 0..new_cap {
334                assert_eq!(queue.push_back(i), Ok(()));
335            }
336
337            for i in 0..new_cap {
338                assert_eq!(queue.pop_front(), Some(i));
339            }
340        }
341    }
342}
343// END VERIFICATION