gtether/util/priority.rs
1//! Data structures and traits used for determining priority ordering.
2//!
3//! In contrast with existing solutions like [`std::collections::BinaryHeap`], this module provides
4//! the capability to have "dynamic" priority, in that the priority for a value can change via
5//! interior mutability and the data structures in this module will do their best to accommodate.
6
7use std::collections::{BinaryHeap, VecDeque};
8use std::sync::{atomic, Arc};
9use educe::Educe;
10
11/// Trait describing something that provides a static priority value.
12///
13/// This trait is auto-implemented for anything that implements `Ord`, and is semantically
14/// equivalent to `Ord`. It exists only as a convenient marker trait, and to serve as a pair to
15/// [`HasDynamicPriority`].
16pub trait HasStaticPriority: Ord {}
17
18impl<P: Ord> HasStaticPriority for P {}
19
20/// Trait describing something that provides a dynamically changing priority value.
21///
22/// Implementors of this trait do not necessarily implement [Ord], as their ordering can change over
23/// time. However, the priority value that implementors yield _do_ implement [Ord], and can be
24/// safely used for ordering.
25///
26/// This trait is automatically implemented for many common types and wrappers.
27pub trait HasDynamicPriority {
28 /// Type of the priority value.
29 ///
30 /// Must implement [Ord].
31 type Value: Ord;
32
33 /// Priority value used for ordering.
34 fn priority(&self) -> Self::Value;
35}
36
37macro_rules! impl_priority_copy_self {
38 ($self_type:ty) => {
39 impl HasDynamicPriority for $self_type {
40 type Value = $self_type;
41
42 #[inline]
43 fn priority(&self) -> Self::Value {
44 *self
45 }
46 }
47 };
48}
49
50impl_priority_copy_self!(i8);
51impl_priority_copy_self!(i16);
52impl_priority_copy_self!(i32);
53impl_priority_copy_self!(i64);
54impl_priority_copy_self!(isize);
55impl_priority_copy_self!(u8);
56impl_priority_copy_self!(u16);
57impl_priority_copy_self!(u32);
58impl_priority_copy_self!(u64);
59impl_priority_copy_self!(usize);
60
61impl<P: HasDynamicPriority> HasDynamicPriority for Arc<P> {
62 type Value = P::Value;
63
64 #[inline]
65 fn priority(&self) -> Self::Value {
66 (**self).priority()
67 }
68}
69
70macro_rules! impl_priority_wrapper_method {
71 ($wrapper_type:ty, $method:ident) => {
72 impl<P: HasDynamicPriority> HasDynamicPriority for $wrapper_type {
73 type Value = P::Value;
74
75 #[inline]
76 fn priority(&self) -> Self::Value {
77 self.$method().priority()
78 }
79 }
80 };
81}
82
83impl_priority_wrapper_method!(parking_lot::Mutex<P>, lock);
84impl_priority_wrapper_method!(parking_lot::RwLock<P>, read);
85impl_priority_wrapper_method!(smol::lock::Mutex<P>, lock_blocking);
86impl_priority_wrapper_method!(smol::lock::RwLock<P>, read_blocking);
87
88macro_rules! impl_priority_atomic {
89 ($atomic_type:ty, $int_type:ty) => {
90 impl HasDynamicPriority for $atomic_type {
91 type Value = $int_type;
92
93 #[inline]
94 fn priority(&self) -> Self::Value {
95 self.load(atomic::Ordering::Relaxed)
96 }
97 }
98 };
99}
100
101impl_priority_atomic!(atomic::AtomicI8, i8);
102impl_priority_atomic!(atomic::AtomicI16, i16);
103impl_priority_atomic!(atomic::AtomicI32, i32);
104impl_priority_atomic!(atomic::AtomicI64, i64);
105impl_priority_atomic!(atomic::AtomicIsize, isize);
106impl_priority_atomic!(atomic::AtomicU8, u8);
107impl_priority_atomic!(atomic::AtomicU16, u16);
108impl_priority_atomic!(atomic::AtomicU32, u32);
109impl_priority_atomic!(atomic::AtomicU64, u64);
110impl_priority_atomic!(atomic::AtomicUsize, usize);
111
112/// Common priority queue logic.
113///
114/// This trait is implemented by the various types of priority queues in this module, such as:
115/// * [StaticPriorityQueue]
116/// * [DynamicPriorityQueue]
117///
118/// For more specific documentation and examples, see the implementor type.
119pub trait PriorityQueue<T> {
120 /// Returns the length of the queue.
121 fn len(&self) -> usize;
122
123 /// Checks if the queue is empty.
124 fn is_empty(&self) -> bool;
125
126 /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
127 ///
128 /// Time complexity depends on the implementation; see individual documentation for more.
129 fn peek(&self) -> Option<&T>;
130
131 /// Push an item onto the queue.
132 ///
133 /// Time complexity depends on the implementation; see individual documentation for more.
134 fn push(&mut self, value: T);
135
136 /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
137 ///
138 /// Time complexity depends on the implementation; see individual documentation for more.
139 fn pop(&mut self) -> Option<T>;
140
141 /// Compares `value` to the highest priority in the queue, and swaps with it if it is higher.
142 ///
143 /// Time complexity depends on the implementation; see individual documentation for more.
144 fn swap_if_higher(&mut self, value: T) -> T;
145}
146
147/// FIFO queue.
148///
149/// Uses a [VecDeque] internally, so most operations simply delegate to [VecDeque].
150#[derive(Educe)]
151#[educe(Default)]
152pub struct FifoQueue<T>(VecDeque<T>);
153
154impl<T> PriorityQueue<T> for FifoQueue<T> {
155 /// Returns the length of the queue.
156 ///
157 /// ```
158 /// use gtether::util::priority::{PriorityQueue, FifoQueue};
159 /// let queue = FifoQueue::<isize>::from([-2, 3]);
160 /// assert_eq!(queue.len(), 2);
161 /// ```
162 #[inline]
163 fn len(&self) -> usize {
164 self.0.len()
165 }
166
167 /// Checks if the queue is empty.
168 ///
169 /// ```
170 /// use gtether::util::priority::{PriorityQueue, FifoQueue};
171 /// let mut queue = FifoQueue::<isize>::default();
172 /// assert!(queue.is_empty());
173 ///
174 /// queue.push(0);
175 /// queue.push(-2);
176 /// queue.push(3);
177 /// assert!(!queue.is_empty());
178 /// ```
179 #[inline]
180 fn is_empty(&self) -> bool {
181 self.0.is_empty()
182 }
183
184 /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
185 ///
186 /// ```
187 /// use gtether::util::priority::{PriorityQueue, FifoQueue};
188 /// let mut queue = FifoQueue::<isize>::default();
189 /// assert_eq!(queue.peek(), None);
190 ///
191 /// queue.push(0);
192 /// queue.push(3);
193 /// queue.push(-2);
194 /// assert_eq!(queue.peek(), Some(&0));
195 /// ```
196 ///
197 /// # Time complexity
198 ///
199 /// Delegates to [`VecDeque::front()`]; cost should be _O_(1).
200 #[inline]
201 fn peek(&self) -> Option<&T> {
202 self.0.front()
203 }
204
205 /// Push an item onto the queue.
206 ///
207 /// ```
208 /// use gtether::util::priority::{PriorityQueue, FifoQueue};
209 /// let mut queue = FifoQueue::<isize>::default();
210 /// queue.push(0);
211 /// queue.push(3);
212 /// queue.push(-2);
213 ///
214 /// assert_eq!(queue.len(), 3);
215 /// assert_eq!(queue.peek(), Some(&0));
216 /// ```
217 ///
218 /// # Time complexity
219 ///
220 /// Delegates to [`VecDeque::push_back()`]; cost should be amortized _O_(1).
221 #[inline]
222 fn push(&mut self, value: T) {
223 self.0.push_back(value)
224 }
225
226 /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
227 ///
228 /// ```
229 /// use gtether::util::priority::{PriorityQueue, FifoQueue};
230 /// let mut queue = FifoQueue::<isize>::from([-2, 3]);
231 ///
232 /// assert_eq!(queue.pop(), Some(-2));
233 /// assert_eq!(queue.pop(), Some(3));
234 /// assert_eq!(queue.pop(), None);
235 /// ```
236 ///
237 /// # Time complexity
238 ///
239 /// Delegates to [`VecDeque::pop_front()`]; cost should be _O_(1).
240 #[inline]
241 fn pop(&mut self) -> Option<T> {
242 self.0.pop_front()
243 }
244
245 /// Never swaps `value`; effectively an identity function.
246 ///
247 /// For a FIFO queue, the front of the queue is always highest priority, but it is assumed that
248 /// the `value` being compared has already been popped, so it is always considered to be the
249 /// highest priority.
250 ///
251 /// ```
252 /// use gtether::util::priority::{PriorityQueue, FifoQueue};
253 /// let mut queue = FifoQueue::<isize>::from([-2, 3]);
254 ///
255 /// assert_eq!(queue.swap_if_higher(0), 0);
256 /// assert_eq!(queue.swap_if_higher(10), 10);
257 /// ```
258 ///
259 /// # Time complexity
260 ///
261 /// _O_(1).
262 #[inline]
263 fn swap_if_higher(&mut self, value: T) -> T {
264 value
265 }
266}
267
268impl<T> From<Vec<T>> for FifoQueue<T> {
269 #[inline]
270 fn from(value: Vec<T>) -> Self {
271 Self(VecDeque::from(value))
272 }
273}
274
275impl<T> FromIterator<T> for FifoQueue<T> {
276 #[inline]
277 fn from_iter<II: IntoIterator<Item=T>>(iter: II) -> Self {
278 Self::from(iter.into_iter().collect::<Vec<_>>())
279 }
280}
281
282impl<T, const N: usize> From<[T; N]> for FifoQueue<T> {
283 #[inline]
284 fn from(value: [T; N]) -> Self {
285 Self::from_iter(value)
286 }
287}
288
289/// Priority queue using [static priorities](HasStaticPriority).
290///
291/// Uses a [BinaryHeap] internally, so most operations simply delegate to [BinaryHeap].
292#[derive(Educe)]
293#[educe(Default)]
294pub struct StaticPriorityQueue<T: HasStaticPriority> {
295 inner: BinaryHeap<T>,
296}
297
298impl<T: HasStaticPriority> PriorityQueue<T> for StaticPriorityQueue<T> {
299 /// Returns the length of the queue.
300 ///
301 /// ```
302 /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
303 /// let queue = StaticPriorityQueue::<isize>::from([-2, 3]);
304 /// assert_eq!(queue.len(), 2);
305 /// ```
306 #[inline]
307 fn len(&self) -> usize {
308 self.inner.len()
309 }
310
311 /// Checks if the queue is empty.
312 ///
313 /// ```
314 /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
315 /// let mut queue = StaticPriorityQueue::<isize>::default();
316 /// assert!(queue.is_empty());
317 ///
318 /// queue.push(0);
319 /// queue.push(-2);
320 /// queue.push(3);
321 /// assert!(!queue.is_empty());
322 /// ```
323 #[inline]
324 fn is_empty(&self) -> bool {
325 self.inner.is_empty()
326 }
327
328 /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
329 ///
330 /// ```
331 /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
332 /// let mut queue = StaticPriorityQueue::<isize>::default();
333 /// assert_eq!(queue.peek(), None);
334 ///
335 /// queue.push(0);
336 /// queue.push(3);
337 /// queue.push(-2);
338 /// assert_eq!(queue.peek(), Some(&3));
339 /// ```
340 ///
341 /// # Time complexity
342 ///
343 /// Delegates to [`BinaryHeap::peek()`]; see that method for time complexity.
344 #[inline]
345 fn peek(&self) -> Option<&T> {
346 self.inner.peek()
347 }
348
349 /// Push an item onto the queue.
350 ///
351 /// ```
352 /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
353 /// let mut queue = StaticPriorityQueue::<isize>::default();
354 /// queue.push(0);
355 /// queue.push(3);
356 /// queue.push(-2);
357 ///
358 /// assert_eq!(queue.len(), 3);
359 /// assert_eq!(queue.peek(), Some(&3));
360 /// ```
361 ///
362 /// # Time complexity
363 ///
364 /// Delegates to [`BinaryHeap::push()`]; see that method for time complexity.
365 #[inline]
366 fn push(&mut self, value: T) {
367 self.inner.push(value)
368 }
369
370 /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
371 ///
372 /// ```
373 /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
374 /// let mut queue = StaticPriorityQueue::<isize>::from([-2, 3]);
375 ///
376 /// assert_eq!(queue.pop(), Some(3));
377 /// assert_eq!(queue.pop(), Some(-2));
378 /// assert_eq!(queue.pop(), None);
379 /// ```
380 ///
381 /// # Time complexity
382 ///
383 /// Delegates to [`BinaryHeap::pop()`]; see that method for time complexity.
384 #[inline]
385 fn pop(&mut self) -> Option<T> {
386 self.inner.pop()
387 }
388
389 /// Compares `value` to the highest priority in the queue, and swaps with it if it is higher.
390 ///
391 /// ```
392 /// use gtether::util::priority::{PriorityQueue, StaticPriorityQueue};
393 /// let mut queue = StaticPriorityQueue::<isize>::from([-2, 3]);
394 ///
395 /// assert_eq!(queue.swap_if_higher(0), 3);
396 /// assert_eq!(queue.swap_if_higher(10), 10);
397 /// ```
398 ///
399 /// # Time complexity
400 ///
401 /// This is equivalent to a `peek()`, comparison, `pop()` and `push()`, and inherits time
402 /// complexity appropriately. This means the worst is in general is _O_(log(_n_)), but can be as
403 /// bad as _O_(_n_) in accordance with [`BinaryHeap::push()`].
404 fn swap_if_higher(&mut self, value: T) -> T {
405 if let Some(highest) = self.inner.peek() && highest > &value {
406 let new_value = self.inner.pop().unwrap();
407 self.inner.push(value);
408 new_value
409 } else {
410 value
411 }
412 }
413}
414
415impl<T: HasStaticPriority> From<Vec<T>> for StaticPriorityQueue<T> {
416 #[inline]
417 fn from(value: Vec<T>) -> Self {
418 Self {
419 inner: BinaryHeap::from(value)
420 }
421 }
422}
423
424impl<T: HasStaticPriority> FromIterator<T> for StaticPriorityQueue<T> {
425 #[inline]
426 fn from_iter<II: IntoIterator<Item=T>>(iter: II) -> Self {
427 Self::from(iter.into_iter().collect::<Vec<_>>())
428 }
429}
430
431impl<T: HasStaticPriority, const N: usize> From<[T; N]> for StaticPriorityQueue<T> {
432 #[inline]
433 fn from(value: [T; N]) -> Self {
434 Self::from_iter(value)
435 }
436}
437
438/// Priority queue using [dynamic priorities](HasDynamicPriority).
439#[derive(Educe)]
440#[educe(Default)]
441pub struct DynamicPriorityQueue<T: HasDynamicPriority> {
442 inner: Vec<T>,
443}
444
445impl<T: HasDynamicPriority> DynamicPriorityQueue<T> {
446 fn find_highest(&self) -> Option<(usize, &T, T::Value)> {
447 let mut highest: Option<(usize, &T, T::Value)> = None;
448 for (idx, value) in self.inner.iter().enumerate() {
449 let priority = value.priority();
450 match highest {
451 Some((_, _, ref highest_priority)) => {
452 if &priority > highest_priority {
453 highest = Some((idx, value, priority));
454 }
455 },
456 None => {
457 highest = Some((idx, value, priority));
458 },
459 }
460 }
461 highest
462 }
463}
464
465impl<T: HasDynamicPriority> PriorityQueue<T> for DynamicPriorityQueue<T> {
466 /// Returns the length of the queue.
467 ///
468 /// ```
469 /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
470 /// use std::sync::atomic::AtomicIsize;
471 /// let queue = DynamicPriorityQueue::<AtomicIsize>::from([AtomicIsize::new(-2), AtomicIsize::new(3)]);
472 /// assert_eq!(queue.len(), 2);
473 /// ```
474 #[inline]
475 fn len(&self) -> usize {
476 self.inner.len()
477 }
478
479 /// Checks if the queue is empty.
480 ///
481 /// ```
482 /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
483 /// use std::sync::atomic::AtomicIsize;
484 /// let mut queue = DynamicPriorityQueue::<AtomicIsize>::default();
485 /// assert!(queue.is_empty());
486 ///
487 /// queue.push(AtomicIsize::new(0));
488 /// queue.push(AtomicIsize::new(-2));
489 /// queue.push(AtomicIsize::new(3));
490 /// assert!(!queue.is_empty());
491 /// ```
492 #[inline]
493 fn is_empty(&self) -> bool {
494 self.inner.is_empty()
495 }
496
497 /// Returns the item in the queue with the highest priority, or `None` if the queue is empty.
498 ///
499 /// ```
500 /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
501 /// use std::sync::{Arc, atomic::{AtomicIsize, Ordering}};
502 /// let mut queue = DynamicPriorityQueue::<Arc<AtomicIsize>>::default();
503 /// assert!(queue.peek().is_none());
504 ///
505 /// let val_a = Arc::new(AtomicIsize::new(-2));
506 /// let val_b = Arc::new(AtomicIsize::new(3));
507 /// queue.push(val_a.clone());
508 /// queue.push(val_b.clone());
509 ///
510 /// {
511 /// let val = queue.peek().expect("should be Some()");
512 /// assert_eq!(val.load(Ordering::Relaxed), 3);
513 /// }
514 ///
515 /// {
516 /// val_a.store(10, Ordering::Relaxed);
517 /// let val = queue.peek().expect("should be Some()");
518 /// assert_eq!(val.load(Ordering::Relaxed), 10);
519 /// }
520 /// ```
521 ///
522 /// # Time complexity
523 ///
524 /// Because priorities are dynamic, the entire queue must be iterated to find the highest
525 /// priority every time, making the cost _O_(_n_).
526 #[inline]
527 fn peek(&self) -> Option<&T> {
528 self.find_highest().map(|(_, value, _)| value)
529 }
530
531 /// Push an item onto the queue.
532 ///
533 /// ```
534 /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
535 /// use std::sync::atomic::{AtomicIsize, Ordering};
536 /// let mut queue = DynamicPriorityQueue::<AtomicIsize>::default();
537 /// queue.push(AtomicIsize::new(0));
538 /// queue.push(AtomicIsize::new(-2));
539 /// queue.push(AtomicIsize::new(3));
540 ///
541 /// assert_eq!(queue.len(), 3);
542 /// let val = queue.peek().expect("should be Some()");
543 /// assert_eq!(val.load(Ordering::Relaxed), 3);
544 /// ```
545 ///
546 /// # Time complexity
547 ///
548 /// Takes amortized _O_(1) time. See [`Vec::push()`] for more.
549 #[inline]
550 fn push(&mut self, value: T) {
551 self.inner.push(value)
552 }
553
554 /// Removes the item with the highest priority and returns it, or `None` if the queue is empty.
555 ///
556 /// ```
557 /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
558 /// use std::sync::{Arc, atomic::{AtomicIsize, Ordering}};
559 ///
560 /// let val_a = Arc::new(AtomicIsize::new(0));
561 /// let val_b = Arc::new(AtomicIsize::new(-2));
562 /// let val_c = Arc::new(AtomicIsize::new(3));
563 ///
564 /// let mut queue = DynamicPriorityQueue::<Arc<AtomicIsize>>::from([
565 /// val_a.clone(),
566 /// val_b.clone(),
567 /// val_c.clone(),
568 /// ]);
569 ///
570 /// {
571 /// let val = queue.pop().expect("should be Some()");
572 /// assert_eq!(val.load(Ordering::Relaxed), 3);
573 /// }
574 ///
575 /// {
576 /// val_b.store(10, Ordering::Relaxed);
577 /// let val = queue.pop().expect("should be Some()");
578 /// assert_eq!(val.load(Ordering::Relaxed), 10);
579 /// }
580 ///
581 /// {
582 /// let val = queue.pop().expect("should be Some()");
583 /// assert_eq!(val.load(Ordering::Relaxed), 0);
584 /// }
585 ///
586 /// assert!(queue.pop().is_none());
587 /// ```
588 ///
589 /// # Time complexity
590 ///
591 /// Because priorities are dynamic, the entire queue must be iterated to find the highest
592 /// priority every time, making the cost _O_(_n_).
593 #[inline]
594 fn pop(&mut self) -> Option<T> {
595 self.find_highest()
596 // Drop the value ref so that we can mutate self.inner
597 .map(|(idx, _, _)| idx)
598 .map(|idx| self.inner.swap_remove(idx))
599 }
600
601 /// Compares `value` to the highest priority in the queue, and swaps with it if it is higher.
602 ///
603 /// ```
604 /// use gtether::util::priority::{PriorityQueue, DynamicPriorityQueue};
605 /// use std::sync::{Arc, atomic::{AtomicIsize, Ordering}};
606 ///
607 /// let val_a = Arc::new(AtomicIsize::new(5));
608 /// let mut queue = DynamicPriorityQueue::<Arc<AtomicIsize>>::from([val_a.clone()]);
609 ///
610 /// let val_b = Arc::new(AtomicIsize::new(10));
611 /// {
612 /// let val = queue.swap_if_higher(val_b.clone());
613 /// assert_eq!(val.load(Ordering::Relaxed), 10);
614 /// }
615 ///
616 /// val_a.store(20, Ordering::Relaxed);
617 /// {
618 /// let val = queue.swap_if_higher(val_b.clone());
619 /// assert_eq!(val.load(Ordering::Relaxed), 20);
620 /// }
621 /// ```
622 ///
623 /// # Time complexity
624 ///
625 /// Because priorities are dynamic, the entire queue must be iterated to find the highest
626 /// priority every time, making the cost _O_(_n_).
627 ///
628 /// This method only searches for the highest priority once before comparing and swapping, so
629 /// it is faster than manually comparing with `peek()` and then calling `pop()` and `push()`.
630 fn swap_if_higher(&mut self, value: T) -> T {
631 let priority = value.priority();
632 let highest = self.find_highest().map(|(idx, _, priority)| (idx, priority));
633 if let Some((idx, highest_priority)) = highest && highest_priority > priority {
634 let new_value = self.inner.swap_remove(idx);
635 self.inner.push(value);
636 new_value
637 } else {
638 value
639 }
640 }
641}
642
643impl<T: HasDynamicPriority> From<Vec<T>> for DynamicPriorityQueue<T> {
644 #[inline]
645 fn from(value: Vec<T>) -> Self {
646 Self {
647 inner: value,
648 }
649 }
650}
651
652impl<T: HasDynamicPriority> FromIterator<T> for DynamicPriorityQueue<T> {
653 #[inline]
654 fn from_iter<II: IntoIterator<Item=T>>(iter: II) -> Self {
655 Self::from(iter.into_iter().collect::<Vec<_>>())
656 }
657}
658
659impl<T: HasDynamicPriority, const N: usize> From<[T; N]> for DynamicPriorityQueue<T> {
660 #[inline]
661 fn from(value: [T; N]) -> Self {
662 Self::from_iter(value)
663 }
664}