engine/collections/
queue.rs1use core::mem::{transmute, MaybeUninit};
6
7use crate::allocators::LinearAllocator;
8
9pub struct Queue<'a, T> {
11 uninit_slice: &'a mut [MaybeUninit<T>],
16 initialized_offset: usize,
17 initialized_len: usize,
18}
19
20impl<T> Queue<'_, T> {
21 pub fn new<'a>(allocator: &'a LinearAllocator, capacity: usize) -> Option<Queue<'a, T>> {
23 let uninit_slice = allocator.try_alloc_uninit_slice(capacity, None)?;
24 Some(Queue {
25 initialized_offset: 0,
26 initialized_len: 0,
27 uninit_slice,
28 })
29 }
30
31 pub fn from_mut(buffer: &mut [MaybeUninit<T>]) -> Option<Queue<T>> {
33 Some(Queue {
34 initialized_offset: 0,
35 initialized_len: 0,
36 uninit_slice: buffer,
37 })
38 }
39
40 pub fn push_back(&mut self, value: T) -> Result<(), T> {
43 if self.initialized_len >= self.uninit_slice.len() {
44 return Err(value);
45 }
46
47 let i = (self.initialized_offset + self.initialized_len) % self.uninit_slice.len();
51
52 self.uninit_slice[i].write(value);
56
57 self.initialized_len += 1;
60
61 Ok(())
62 }
63
64 pub fn pop_front(&mut self) -> Option<T> {
67 if self.initialized_len == 0 {
68 return None;
69 }
70
71 let value = unsafe { self.uninit_slice[self.initialized_offset].assume_init_read() };
77
78 self.initialized_offset = (self.initialized_offset + 1) % self.uninit_slice.len();
81 self.initialized_len -= 1;
82
83 Some(value)
84 }
85
86 pub fn peek_front(&mut self) -> Option<&mut T> {
89 if self.initialized_len == 0 {
90 return None;
91 }
92 Some(unsafe { self.uninit_slice[self.initialized_offset].assume_init_mut() })
96 }
97
98 pub fn spare_capacity(&self) -> usize {
100 self.uninit_slice.len() - self.initialized_len
101 }
102
103 pub fn is_full(&self) -> bool {
105 self.initialized_len == self.uninit_slice.len()
106 }
107
108 pub fn is_empty(&self) -> bool {
110 self.initialized_len == 0
111 }
112
113 pub fn iter(&self) -> impl Iterator<Item = &T> {
115 let len = self.uninit_slice.len();
116
117 let head = &self.uninit_slice
118 [self.initialized_offset..(self.initialized_offset + self.initialized_len).min(len)];
119 let head = unsafe { transmute::<&[MaybeUninit<T>], &[T]>(head) };
123
124 let tail = &self.uninit_slice
125 [..(self.initialized_offset + self.initialized_len).saturating_sub(len)];
126 let tail = unsafe { transmute::<&[MaybeUninit<T>], &[T]>(tail) };
130
131 head.iter().chain(tail.iter())
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::allocators::{static_allocator, LinearAllocator};
138
139 use super::Queue;
140
141 #[test]
142 fn pushes_and_pops_in_fifo_order() {
143 static ARENA: &LinearAllocator = static_allocator!(2);
144 let alloc = LinearAllocator::new(ARENA, 2).unwrap();
145 let mut queue = Queue::<u8>::new(&alloc, 2).unwrap();
146
147 assert!(queue.push_back(0).is_ok());
148 assert!(queue.push_back(1).is_ok());
149 assert!(
150 queue.push_back(2).is_err(),
151 "pushed a third element into a queue with capacity for two?",
152 );
153 assert_eq!(Some(0), queue.pop_front());
154 assert!(queue.push_back(2).is_ok());
155 assert_eq!(Some(1), queue.pop_front());
156 assert_eq!(Some(2), queue.pop_front());
157 assert_eq!(
158 None,
159 queue.pop_front(),
160 "popped a fourth element after only pushing three elements?",
161 );
162 }
163
164 #[test]
165 fn iter_works() {
166 static ARENA: &LinearAllocator = static_allocator!(3);
167 let alloc = LinearAllocator::new(ARENA, 3).unwrap();
168 let mut queue = Queue::<u8>::new(&alloc, 3).unwrap();
169 queue.push_back(0).unwrap();
170 queue.push_back(1).unwrap();
171 queue.push_back(2).unwrap();
172 queue.pop_front().unwrap();
173 queue.push_back(3).unwrap();
174
175 let mut iter = queue.iter();
176 assert_eq!(Some(&1), iter.next());
177 assert_eq!(Some(&2), iter.next());
178 assert_eq!(Some(&3), iter.next());
179 assert_eq!(None, iter.next());
180 }
181}