queue_model/
lib.rs

1//! Abstraction of the concept of a queue.
2
3#![no_std]
4#![warn(missing_docs)]
5
6use core::{
7    cmp::max,
8    mem::{replace, MaybeUninit},
9};
10
11use cfg_if::cfg_if;
12use safe_modular_arithmetic::StaticModular;
13
14/// Trait which queue implementations must conform to.
15pub trait QueueModel {
16    /// The type of items in the queue.
17    type Item;
18
19    /// Attempts to enqueue an item; returns whether or not it was successful.
20    fn enqueue(&mut self, item: Self::Item) -> bool;
21
22    /// Attempts to dequeue an item; returns `None` if there are no items
23    /// available.
24    fn dequeue(&mut self) -> Option<Self::Item>;
25
26    /// Checks the current number of items in the queue.
27    fn count(&self) -> usize;
28
29    /// Checks if the queue is empty.
30    fn is_empty(&self) -> bool {
31        self.count() == 0
32    }
33}
34
35/// A statically-sized queue which is implemented as a ring buffer.
36pub struct StaticRingQueue<T, const N: usize> {
37    length: usize,
38    offset: StaticModular<usize, N>,
39    buffer: [MaybeUninit<T>; N],
40}
41
42impl<T, const N: usize> StaticRingQueue<T, N> {
43    /// Creates a new queue.
44    #[inline]
45    pub fn new() -> Self {
46        Self {
47            length: 0,
48            offset: 0.into(),
49            buffer: unsafe { MaybeUninit::uninit().assume_init() },
50        }
51    }
52}
53
54impl<T, const N: usize> Default for StaticRingQueue<T, N> {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl<T, const N: usize> QueueModel for StaticRingQueue<T, N> {
61    type Item = T;
62
63    fn enqueue(&mut self, item: Self::Item) -> bool {
64        if self.length == N {
65            false
66        } else {
67            self.buffer[self.offset + self.length.into()] = MaybeUninit::new(item);
68            self.length += 1;
69            true
70        }
71    }
72
73    fn dequeue(&mut self) -> Option<Self::Item> {
74        if self.length == 0 {
75            None
76        } else {
77            let item = replace(&mut self.buffer[self.offset], MaybeUninit::uninit());
78            self.offset = self.offset + 1.into();
79            self.length -= 1;
80            Some(unsafe { item.assume_init() })
81        }
82    }
83
84    fn count(&self) -> usize {
85        self.length
86    }
87}
88
89/// A statically-sized queue which is implemented as a max-heap.
90pub struct StaticPriorityQueue<T: Ord, const N: usize> {
91    length: usize,
92    buffer: [MaybeUninit<T>; N],
93}
94
95impl<T: Ord, const N: usize> StaticPriorityQueue<T, N> {
96    /// Creates a new queue.
97    #[inline]
98    pub fn new() -> Self {
99        Self {
100            length: 0,
101            buffer: unsafe { MaybeUninit::uninit().assume_init() },
102        }
103    }
104}
105
106impl<T: Ord, const N: usize> Default for StaticPriorityQueue<T, N> {
107    fn default() -> Self {
108        Self::new()
109    }
110}
111
112impl<T: Ord, const N: usize> QueueModel for StaticPriorityQueue<T, N> {
113    type Item = T;
114
115    fn enqueue(&mut self, item: Self::Item) -> bool {
116        if self.length == N {
117            false
118        } else {
119            up_heap(&mut self.buffer[0..=self.length], self.length, item);
120            self.length += 1;
121            true
122        }
123    }
124
125    fn dequeue(&mut self) -> Option<Self::Item> {
126        if self.length == 0 {
127            None
128        } else {
129            self.length -= 1;
130            let last = replace(&mut self.buffer[self.length], MaybeUninit::uninit());
131            let item = down_heap(&mut self.buffer[0..self.length], 0, unsafe {
132                last.assume_init()
133            });
134            Some(unsafe { item.assume_init() })
135        }
136    }
137
138    fn count(&self) -> usize {
139        self.length
140    }
141}
142
143cfg_if! {
144    if #[cfg(feature = "alloc")] {
145        extern crate alloc;
146        use alloc::collections::{BinaryHeap, VecDeque};
147
148        impl<T> QueueModel for VecDeque<T> {
149            type Item = T;
150
151            #[inline]
152            fn enqueue(&mut self, item: Self::Item) -> bool {
153                self.push_back(item);
154                true
155            }
156
157            #[inline]
158            fn dequeue(&mut self) -> Option<Self::Item> {
159                self.pop_front()
160            }
161
162            #[inline]
163            fn count(&self) -> usize {
164                self.len()
165            }
166
167            #[inline]
168            fn is_empty(&self) -> bool {
169                Self::is_empty(&self)
170            }
171        }
172
173        impl<T: Ord> QueueModel for BinaryHeap<T> {
174            type Item = T;
175
176            #[inline]
177            fn enqueue(&mut self, item: Self::Item) -> bool {
178                self.push(item);
179                true
180            }
181
182            #[inline]
183            fn dequeue(&mut self) -> Option<Self::Item> {
184                self.pop()
185            }
186
187            #[inline]
188            fn count(&self) -> usize {
189                self.len()
190            }
191
192            #[inline]
193            fn is_empty(&self) -> bool {
194                Self::is_empty(&self)
195            }
196        }
197    }
198}
199
200fn up_heap<T: Ord>(buffer: &mut [MaybeUninit<T>], index: usize, item: T) -> MaybeUninit<T> {
201    let next = if index == 0 {
202        MaybeUninit::new(item)
203    } else {
204        let parent = (index - 1) / 2;
205        if unsafe { &*(&buffer[parent] as *const _ as *const T) } < &item {
206            up_heap(buffer, parent, item)
207        } else {
208            MaybeUninit::new(item)
209        }
210    };
211    replace(&mut buffer[index], next)
212}
213
214fn down_heap<T: Ord>(buffer: &mut [MaybeUninit<T>], index: usize, item: T) -> MaybeUninit<T> {
215    if index >= buffer.len() {
216        return MaybeUninit::new(item);
217    }
218
219    let child = [index * 2 + 1, index * 2 + 2]
220        .iter()
221        .filter_map(|i| {
222            buffer
223                .get_mut(*i)
224                .map(|v| (unsafe { &*(v as *const _ as *const T) }, *i))
225        })
226        .fold(None, |acc, cur| {
227            if let Some(acc) = acc {
228                Some(max(acc, cur))
229            } else if *cur.0 > item {
230                Some(cur)
231            } else {
232                None
233            }
234        });
235    let next = if let Some((_, i)) = child {
236        down_heap(buffer, i, item)
237    } else {
238        MaybeUninit::new(item)
239    };
240    replace(&mut buffer[index], next)
241}