use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::net::Ipv4Addr;
use std::time::Instant;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PendingReply {
pub deadline: Instant,
pub dest: Ipv4Addr,
pub identifier: u16,
pub sequence: u16,
pub payload: Vec<u8>,
}
impl PartialOrd for PendingReply { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } }
impl Ord for PendingReply { fn cmp(&self, other: &Self) -> Ordering { other.deadline.cmp(&self.deadline) } }
#[derive(Default)]
pub struct Scheduler {
heap: BinaryHeap<PendingReply>,
}
impl Scheduler {
pub fn new() -> Self { Self::default() }
pub fn push(&mut self, reply: PendingReply) {
self.heap.push(reply);
}
pub fn pop_ready(&mut self, now: Instant) -> Vec<PendingReply> {
let mut out = Vec::new();
while let Some(top) = self.heap.peek() {
if top.deadline <= now {
out.push(self.heap.pop().unwrap());
} else {
break;
}
}
out
}
pub fn next_deadline(&self) -> Option<Instant> {
self.heap.peek().map(|r| r.deadline)
}
pub fn len(&self) -> usize { self.heap.len() }
pub fn is_empty(&self) -> bool { self.heap.is_empty() }
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
fn reply(deadline: Instant, seq: u16) -> PendingReply {
PendingReply { deadline, dest: Ipv4Addr::LOCALHOST, identifier: 1, sequence: seq, payload: vec![] }
}
#[test]
fn next_deadline_empty_is_none() {
assert!(Scheduler::new().next_deadline().is_none());
}
#[test]
fn next_deadline_returns_earliest() {
let mut s = Scheduler::new();
let t = Instant::now();
s.push(reply(t + Duration::from_millis(500), 2));
s.push(reply(t + Duration::from_millis(100), 1));
assert_eq!(s.next_deadline(), Some(t + Duration::from_millis(100)));
}
#[test]
fn pop_ready_returns_only_due_in_order() {
let mut s = Scheduler::new();
let t = Instant::now();
s.push(reply(t + Duration::from_millis(300), 3));
s.push(reply(t + Duration::from_millis(100), 1));
s.push(reply(t + Duration::from_millis(200), 2));
let due = s.pop_ready(t + Duration::from_millis(250));
assert_eq!(due.len(), 2);
assert_eq!(due[0].sequence, 1);
assert_eq!(due[1].sequence, 2);
assert_eq!(s.len(), 1);
}
#[test]
fn pop_ready_nothing_due() {
let mut s = Scheduler::new();
let t = Instant::now();
s.push(reply(t + Duration::from_millis(100), 1));
assert!(s.pop_ready(t).is_empty());
assert_eq!(s.len(), 1);
}
}