use std::{cmp::Ordering, collections::BinaryHeap};
#[derive(Clone, Default, Debug)]
pub struct ReadyBuffer<K: Ord, T: PartialEq> {
pub heap: BinaryHeap<ItemWithReadyKey<K, T>>,
}
impl<K: Ord, T: PartialEq> ReadyBuffer<K, T> {
pub fn new() -> Self {
Self {
heap: BinaryHeap::default(),
}
}
}
impl<K: Ord, T: PartialEq> ReadyBuffer<K, T> {
pub fn add_item(&mut self, key: K, item: T) {
self.heap.push(ItemWithReadyKey { key, item });
}
pub fn has_item(&self, current_key: &K) -> bool {
if let Some(item) = self.heap.peek() {
let cmp = item.key.cmp(current_key);
return matches!(cmp, Ordering::Less | Ordering::Equal);
}
false
}
pub fn pop_item(&mut self, current_key: &K) -> Option<(K, T)> {
if self.has_item(current_key) {
if let Some(container) = self.heap.pop() {
return Some((container.key, container.item));
}
}
None
}
pub(crate) fn pop_until(&mut self, key: &K) -> Option<(K, T)> {
if self.heap.is_empty() {
return None;
}
let mut val = None;
while let Some(item_with_key) = self.heap.peek() {
if item_with_key.key > *key {
break;
}
val = self.heap.pop().map(|item| (item.key, item.item));
}
val
}
pub(crate) fn drain_until(&mut self, key: &K) -> Vec<(K, T)> {
if self.heap.is_empty() {
return vec![];
}
let mut val = Vec::new();
while let Some(item_with_key) = self.heap.peek() {
if item_with_key.key > *key {
break;
}
if let Some(v) = self.heap.pop().map(|item| (item.key, item.item)) {
val.push(v);
}
}
val
}
pub fn len(&self) -> usize {
self.heap.len()
}
pub fn is_empty(&self) -> bool {
self.heap.is_empty()
}
}
#[derive(Clone, Debug)]
pub struct ItemWithReadyKey<K: Ord, T> {
pub key: K,
pub item: T,
}
impl<K: Ord, T: PartialEq> Eq for ItemWithReadyKey<K, T> {}
impl<K: Ord, T: PartialEq> PartialEq<Self> for ItemWithReadyKey<K, T> {
fn eq(&self, other: &Self) -> bool {
self.item == other.item && self.key == other.key
}
}
impl<K: Ord, T: PartialEq> PartialOrd<Self> for ItemWithReadyKey<K, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<K: Ord, T: PartialEq> Ord for ItemWithReadyKey<K, T> {
fn cmp(&self, other: &ItemWithReadyKey<K, T>) -> Ordering {
other.key.cmp(&self.key)
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use mock_instant::Instant;
use mock_instant::MockClock;
use crate::shared::tick_manager::Tick;
use super::*;
#[test]
fn test_time_heap() {
let mut heap = ReadyBuffer::<Instant, u64>::new();
let now = Instant::now();
heap.add_item(now + Duration::from_secs(2), 2);
heap.add_item(now + Duration::from_secs(1), 1);
heap.add_item(now + Duration::from_secs(3), 3);
assert!(!heap.has_item(&Instant::now()));
MockClock::advance(Duration::from_secs(2));
matches!(heap.pop_item(&Instant::now()), Some((_, 1)));
matches!(heap.pop_item(&Instant::now()), Some((_, 2)));
assert_eq!(heap.pop_item(&Instant::now()), None);
assert_eq!(heap.len(), 1);
}
#[test]
fn test_pop_until() {
let mut buffer = ReadyBuffer::new();
assert_eq!(buffer.pop_until(&Tick(0)), None);
buffer.add_item(Tick(1), 1);
buffer.add_item(Tick(2), 2);
assert_eq!(buffer.pop_until(&Tick(2)), Some((Tick(2), 2)));
assert!(buffer.is_empty());
buffer.add_item(Tick(1), 1);
buffer.add_item(Tick(3), 3);
assert_eq!(buffer.pop_until(&Tick(2)), Some((Tick(1), 1)));
assert_eq!(buffer.len(), 1);
assert_eq!(buffer.pop_until(&Tick(4)), Some((Tick(3), 3)));
assert!(buffer.is_empty());
buffer.add_item(Tick(1), 1);
assert_eq!(buffer.pop_until(&Tick(0)), None);
assert_eq!(buffer.len(), 1);
}
}