kernel/mem/
queue.rs

1//! This module provides a queue implementation.
2
3use super::array::Vec;
4use super::boxed::Box;
5use crate::utils::KernelError;
6
7/// A ring-buffer based queue, with N elements stored inline. TODO: Make this growable.
8pub struct Queue<T: Clone, const N: usize> {
9    data: Vec<T, N>,
10    len: usize,
11    front: usize,
12}
13
14impl<T: Clone + Copy, const N: usize> Queue<T, N> {
15    /// Create a new empty queue.
16    pub const fn new() -> Self {
17        Self {
18            data: Vec::new(),
19            len: 0,
20            front: 0,
21        }
22    }
23
24    /// Push a value onto the back of the queue.
25    ///
26    /// `value` - The value to push onto the back of the queue.
27    ///
28    /// Returns `Ok(())` if the value was pushed onto the back of the queue, or an error if the queue is full.
29    pub fn push_back(&mut self, value: T) -> Result<(), KernelError> {
30        if self.len == self.data.capacity() {
31            return Err(KernelError::OutOfMemory);
32        }
33        self.len += 1;
34        if self.data.len() != self.data.capacity() {
35            self.data.push(value)?;
36        } else {
37            self.insert(self.len - 1, value)?;
38        }
39
40        Ok(())
41    }
42
43    /// Pop a value from the front of the queue.
44    ///
45    /// Returns the value at the front of the queue, or `None` if the queue is empty.
46    pub fn pop_front(&mut self) -> Option<T> {
47        if self.len == 0 {
48            return None;
49        }
50
51        let value = self.data.at(self.front).cloned();
52
53        self.front = (self.front + 1) % self.data.capacity();
54        self.len -= 1;
55        value
56    }
57
58    /// Insert a value at the given index in the queue.
59    ///
60    /// `index` - The index to insert the value at.
61    /// `value` - The value to insert.
62    ///
63    /// Returns `Ok(())` if the value was inserted at the given index, or an error if the index is out of bounds.
64    pub fn insert(&mut self, index: usize, value: T) -> Result<(), KernelError> {
65        if index >= self.len() {
66            return Err(KernelError::InvalidAddress);
67        }
68        let real_idx = (self.front + index) % self.data.capacity();
69        self.data
70            .at_mut(real_idx)
71            .map(|insertion_point| *insertion_point = value)
72            .ok_or(KernelError::InvalidAddress)
73    }
74
75    /// Returns the value at the front of the queue.
76    pub fn front(&self) -> Option<&T> {
77        if self.is_empty() {
78            return None;
79        }
80
81        self.data.at(self.front)
82    }
83
84    /// Returns the value at the back of the queue.
85    pub fn back(&self) -> Option<&T> {
86        if self.is_empty() {
87            return None;
88        }
89        let back = (self.front + self.len - 1) % self.data.capacity();
90        self.data.at(back)
91    }
92
93    /// Returns the length of the queue.
94    pub fn len(&self) -> usize {
95        self.len
96    }
97
98    /// Returns `true` if the queue is empty.
99    pub fn is_empty(&self) -> bool {
100        self.len == 0
101    }
102
103    /// Increases total space in the queue preserving queue-integrity, potentially reallocating and copying existing contents
104    ///
105    /// `new_size` - The total amount of space in the queue afterwards
106    ///
107    /// Returns `Ok(())` if the queue was successfully enlargened or the requested size was smaller than the current capacity.
108    /// Returns An error if the queue could not be grown
109    pub fn grow_capacity(&mut self, new_size: usize) -> Result<(), KernelError> {
110        if new_size <= self.data.capacity() {
111            return Ok(());
112        }
113        // if the queue wraps
114        if self.front + self.len >= self.data.capacity() {
115            // copy the queue to the front to make further logic straighforward
116            // When the queue wraps around the end, the wrapping would not happen anymore with the new size
117
118            // we could do some complicated in-place swapping here instead of using a potentially expensive temporary storage
119            let non_wrapping_queue_start_len = self.data.capacity() - self.front;
120            let mut swap_helper = Box::new_slice_uninit(non_wrapping_queue_start_len)?;
121            BUG_ON!(swap_helper.len() != non_wrapping_queue_start_len);
122
123            // we take the start of the queue (which is located at the end of the curr memory region) and copy it to temp storage
124            for i in 0..swap_helper.len() {
125                // Returning an error here should never happen if the queue is in a consistant state prior. If not no guarantees about contents are made.
126                swap_helper[i].write(
127                    self.data
128                        .at(self.front + i)
129                        .copied()
130                        .ok_or(KernelError::InvalidAddress)?,
131                );
132            }
133            // One past the logically last element of the queue
134            let end = (self.front + self.len) % self.data.capacity();
135            // now move the logical end of the queue further back to make space for the logical start
136            for i in 0..end {
137                BUG_ON!(i + non_wrapping_queue_start_len >= self.data.capacity());
138                self.data.swap(i, i + non_wrapping_queue_start_len);
139            }
140            // now copy the data back from the temp helper
141            for i in 0..non_wrapping_queue_start_len {
142                // Safety: values copied into our helper are part of the active queue, must therefore be inited
143                self.data
144                    .at_mut(i)
145                    .map(|el| *el = unsafe { swap_helper[i].assume_init() });
146            }
147            self.front = 0;
148        }
149        self.data.reserve_total_capacity(new_size)
150    }
151}
152
153// TESTING ------------------------------------------------------------------------------------------------------------
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158    use crate::mem::GLOBAL_ALLOCATOR;
159    use core::ops::Range;
160
161    fn alloc_range(length: usize) -> Range<usize> {
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        ptr as usize..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(KernelError::OutOfMemory));
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(KernelError::OutOfMemory));
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(KernelError::OutOfMemory));
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