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