1use std::cmp::Ordering;
13use std::collections::BinaryHeap;
14
15use crate::{trace, HashMap, HashMapExt};
16
17pub struct Queue<T, P: Eq + PartialEq + Ord> {
32 queue: BinaryHeap<PlanSchedule<P>>,
33 data_map: HashMap<u64, T>,
34 plan_counter: u64,
37 #[cfg(feature = "profiling")]
40 pub(crate) max_plans_in_flight: u64,
41 #[cfg(feature = "profiling")]
42 pub(crate) max_memory_in_use: u64,
43}
44
45impl<T, P: Eq + PartialEq + Ord> Queue<T, P> {
46 #[must_use]
48 pub fn new() -> Queue<T, P> {
49 Queue {
50 queue: BinaryHeap::new(),
51 data_map: HashMap::new(),
52 plan_counter: 0,
53 #[cfg(feature = "profiling")]
54 max_plans_in_flight: 0,
55 #[cfg(feature = "profiling")]
56 max_memory_in_use: 0,
57 }
58 }
59
60 pub fn add_plan(&mut self, time: f64, data: T, priority: P) -> PlanId {
65 trace!("adding plan at {time}");
66 let plan_id = self.plan_counter;
68 self.queue.push(PlanSchedule {
69 plan_id,
70 time,
71 priority,
72 });
73 self.data_map.insert(plan_id, data);
74 self.plan_counter += 1;
75 #[cfg(feature = "profiling")]
76 {
77 self.max_plans_in_flight = self.max_plans_in_flight.max(self.queue.len() as u64);
78 self.max_memory_in_use = self
79 .max_memory_in_use
80 .max(self.estimated_memory_in_use() as u64);
81 }
82
83 PlanId(plan_id)
84 }
85
86 pub fn cancel_plan(&mut self, plan_id: &PlanId) -> Option<T> {
88 trace!("cancel plan {plan_id:?}");
89 self.data_map.remove(&plan_id.0)
92 }
93
94 #[must_use]
95 pub fn is_empty(&self) -> bool {
96 self.queue.is_empty()
97 }
98
99 #[must_use]
100 pub fn next_time(&self) -> Option<f64> {
101 self.queue.peek().map(|e| e.time)
102 }
103
104 #[allow(dead_code)]
105 pub(crate) fn clear(&mut self) {
106 self.data_map.clear();
107 self.queue.clear();
108 self.plan_counter = 0;
109 }
110
111 #[must_use]
112 #[allow(dead_code)]
113 pub(crate) fn peek(&self) -> Option<(&PlanSchedule<P>, &T)> {
114 for entry in &self.queue {
116 if let Some(data) = self.data_map.get(&entry.plan_id) {
118 return Some((entry, data));
119 }
120 }
121 None
122 }
123
124 pub fn get_next_plan(&mut self) -> Option<Plan<T>> {
128 trace!("getting next plan");
129 loop {
130 match self.queue.pop() {
132 Some(entry) => {
133 if let Some(data) = self.data_map.remove(&entry.plan_id) {
135 return Some(Plan {
136 time: entry.time,
137 data,
138 });
139 }
140 }
141 None => {
142 return None;
143 }
144 }
145 }
146 }
147
148 #[must_use]
151 pub fn list_schedules(&self, at_most: usize) -> Vec<&PlanSchedule<P>> {
152 let mut items = vec![];
153
154 for entry in &self.queue {
156 if self.data_map.contains_key(&entry.plan_id) {
158 items.push(entry);
159 if items.len() == at_most {
160 break;
161 }
162 }
163 }
164 items
165 }
166
167 #[doc(hidden)]
168 pub(crate) fn remaining_plan_count(&self) -> usize {
169 self.queue.len()
170 }
171
172 #[cfg(feature = "profiling")]
173 fn estimated_memory_in_use(&self) -> usize {
174 let queue_bytes = self.queue.capacity() * size_of::<PlanSchedule<P>>();
175
176 let map_entry_bytes = self.data_map.capacity() * size_of::<(u64, T)>();
177
178 queue_bytes + map_entry_bytes
179 }
180}
181
182impl<T, P: Eq + PartialEq + Ord> Default for Queue<T, P> {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188#[derive(PartialEq, Debug)]
193pub struct PlanSchedule<P: Eq + PartialEq + Ord> {
194 pub plan_id: u64,
195 pub time: f64,
196 pub priority: P,
197}
198
199impl<P: Eq + PartialEq + Ord + Clone> Clone for PlanSchedule<P> {
200 fn clone(&self) -> Self {
201 PlanSchedule {
202 priority: self.priority.clone(),
203 ..*self
204 }
205 }
206}
207
208impl<P: Eq + PartialEq + Ord + Copy + Clone> Copy for PlanSchedule<P> {}
209
210impl<P: Eq + PartialEq + Ord> Eq for PlanSchedule<P> {}
211
212impl<P: Eq + PartialEq + Ord> PartialOrd for PlanSchedule<P> {
213 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
214 Some(self.cmp(other))
215 }
216}
217
218impl<P: Eq + PartialEq + Ord> Ord for PlanSchedule<P> {
221 fn cmp(&self, other: &Self) -> Ordering {
222 let time_ordering = self.time.partial_cmp(&other.time).unwrap().reverse();
223 match time_ordering {
224 Ordering::Equal => {
226 let priority_ordering = self
227 .priority
228 .partial_cmp(&other.priority)
229 .unwrap()
230 .reverse();
231 match priority_ordering {
232 Ordering::Equal => self.plan_id.cmp(&other.plan_id).reverse(),
233 _ => priority_ordering,
234 }
235 }
236 _ => time_ordering,
237 }
238 }
239}
240
241#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
243pub struct PlanId(pub(crate) u64);
244
245pub struct Plan<T> {
247 pub time: f64,
248 pub data: T,
249}
250
251#[cfg(test)]
252mod tests {
253 use super::Queue;
254
255 #[test]
256 fn empty_queue() {
257 let mut plan_queue = Queue::<(), ()>::new();
258 assert!(plan_queue.get_next_plan().is_none());
259 }
260
261 #[test]
262 fn add_plans() {
263 let mut plan_queue = Queue::new();
264 plan_queue.add_plan(1.0, 1, ());
265 plan_queue.add_plan(3.0, 3, ());
266 plan_queue.add_plan(2.0, 2, ());
267 assert!(!plan_queue.is_empty());
268
269 let next_plan = plan_queue.get_next_plan().unwrap();
270 assert_eq!(next_plan.time, 1.0);
271 assert_eq!(next_plan.data, 1);
272
273 assert!(!plan_queue.is_empty());
274 let next_plan = plan_queue.get_next_plan().unwrap();
275 assert_eq!(next_plan.time, 2.0);
276 assert_eq!(next_plan.data, 2);
277
278 assert!(!plan_queue.is_empty());
279 let next_plan = plan_queue.get_next_plan().unwrap();
280 assert_eq!(next_plan.time, 3.0);
281 assert_eq!(next_plan.data, 3);
282
283 assert!(plan_queue.is_empty());
284 assert!(plan_queue.get_next_plan().is_none());
285 }
286
287 #[test]
288 fn add_plans_at_same_time_with_same_priority() {
289 let mut plan_queue = Queue::new();
290 plan_queue.add_plan(1.0, 1, ());
291 plan_queue.add_plan(1.0, 2, ());
292 assert!(!plan_queue.is_empty());
293
294 let next_plan = plan_queue.get_next_plan().unwrap();
295 assert_eq!(next_plan.time, 1.0);
296 assert_eq!(next_plan.data, 1);
297
298 assert!(!plan_queue.is_empty());
299 let next_plan = plan_queue.get_next_plan().unwrap();
300 assert_eq!(next_plan.time, 1.0);
301 assert_eq!(next_plan.data, 2);
302
303 assert!(plan_queue.is_empty());
304 assert!(plan_queue.get_next_plan().is_none());
305 }
306
307 #[test]
308 fn add_plans_at_same_time_with_different_priority() {
309 let mut plan_queue = Queue::new();
310 plan_queue.add_plan(1.0, 1, 1);
311 plan_queue.add_plan(1.0, 2, 0);
312
313 assert!(!plan_queue.is_empty());
314 let next_plan = plan_queue.get_next_plan().unwrap();
315 assert_eq!(next_plan.time, 1.0);
316 assert_eq!(next_plan.data, 2);
317
318 let next_plan = plan_queue.get_next_plan().unwrap();
319 assert_eq!(next_plan.time, 1.0);
320 assert_eq!(next_plan.data, 1);
321
322 assert!(plan_queue.is_empty());
323 assert!(plan_queue.get_next_plan().is_none());
324 }
325
326 #[test]
327 fn add_and_cancel_plans() {
328 let mut plan_queue = Queue::new();
329 plan_queue.add_plan(1.0, 1, ());
330 let plan_to_cancel = plan_queue.add_plan(2.0, 2, ());
331 plan_queue.add_plan(3.0, 3, ());
332 plan_queue.cancel_plan(&plan_to_cancel);
333 assert!(!plan_queue.is_empty());
334
335 let next_plan = plan_queue.get_next_plan().unwrap();
336 assert_eq!(next_plan.time, 1.0);
337 assert_eq!(next_plan.data, 1);
338
339 assert!(!plan_queue.is_empty());
340 let next_plan = plan_queue.get_next_plan().unwrap();
341 assert_eq!(next_plan.time, 3.0);
342 assert_eq!(next_plan.data, 3);
343
344 assert!(plan_queue.is_empty());
345 assert!(plan_queue.get_next_plan().is_none());
346 }
347
348 #[test]
349 fn add_and_get_plans() {
350 let mut plan_queue = Queue::new();
351 plan_queue.add_plan(1.0, 1, ());
352 plan_queue.add_plan(2.0, 2, ());
353 assert!(!plan_queue.is_empty());
354
355 let next_plan = plan_queue.get_next_plan().unwrap();
356 assert_eq!(next_plan.time, 1.0);
357 assert_eq!(next_plan.data, 1);
358
359 plan_queue.add_plan(1.5, 3, ());
360
361 assert!(!plan_queue.is_empty());
362 let next_plan = plan_queue.get_next_plan().unwrap();
363 assert_eq!(next_plan.time, 1.5);
364 assert_eq!(next_plan.data, 3);
365
366 assert!(!plan_queue.is_empty());
367 let next_plan = plan_queue.get_next_plan().unwrap();
368 assert_eq!(next_plan.time, 2.0);
369 assert_eq!(next_plan.data, 2);
370
371 assert!(plan_queue.is_empty());
372 assert!(plan_queue.get_next_plan().is_none());
373 }
374
375 #[test]
376 fn cancel_invalid_plan() {
377 let mut plan_queue = Queue::new();
378 let plan_to_cancel = plan_queue.add_plan(1.0, (), ());
379 assert!(!plan_queue.is_empty());
381 plan_queue.get_next_plan();
382 assert!(plan_queue.is_empty());
383 let result = plan_queue.cancel_plan(&plan_to_cancel);
384 assert!(result.is_none());
385 }
386}