use super::PeerId;
use std::collections::BTreeMap;
pub const MAX_AHEAD: u64 = 120;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TickInput<I> {
pub peer: PeerId,
pub input: I,
pub fresh: bool,
}
#[derive(Clone, Debug)]
struct Peer<I> {
waiting: BTreeMap<u64, I>,
last: Option<I>,
late: u64,
}
#[derive(Clone, Debug)]
pub struct InputBuffer<I> {
peers: BTreeMap<PeerId, Peer<I>>,
next: u64,
}
impl<I: Clone> InputBuffer<I> {
pub fn new(tick: u64) -> Self {
Self {
peers: BTreeMap::new(),
next: tick,
}
}
pub fn push(
&mut self,
peer: PeerId,
tick: u64,
input: I,
) -> Result<(), String> {
if tick < self.next {
self.peer(peer).late += 1;
return Err(format!(
"peer {peer}: input for tick {tick} came too late"
));
}
if tick - self.next > MAX_AHEAD {
return Err(format!(
"peer {peer}: input for tick {tick} is too far ahead"
));
}
self.peer(peer).waiting.insert(tick, input);
Ok(())
}
fn peer(&mut self, peer: PeerId) -> &mut Peer<I> {
self.peers.entry(peer).or_insert_with(|| Peer {
waiting: BTreeMap::new(),
last: None,
late: 0,
})
}
pub fn take(&mut self) -> (u64, Vec<TickInput<I>>) {
let tick = self.next;
self.next += 1;
let inputs = self
.peers
.iter_mut()
.filter_map(|(&peer, state)| {
state.waiting = state.waiting.split_off(&tick);
let fresh = state.waiting.remove(&tick);
let is_fresh = fresh.is_some();
if let Some(input) = fresh {
state.last = Some(input);
}
let input = state.last.clone()?;
Some(TickInput {
peer,
input,
fresh: is_fresh,
})
})
.collect();
(tick, inputs)
}
pub fn buffered(&self, peer: PeerId) -> usize {
self.peers.get(&peer).map_or(0, |state| state.waiting.len())
}
pub fn late(&self, peer: PeerId) -> u64 {
self.peers.get(&peer).map_or(0, |state| state.late)
}
pub fn forget(&mut self, peer: PeerId) {
self.peers.remove(&peer);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn early_inputs_wait_and_missing_ones_repeat_the_last() {
let mut buffer = InputBuffer::new(10);
buffer.push(1, 11, 'b').unwrap();
buffer.push(1, 10, 'a').unwrap();
buffer.push(2, 10, 'x').unwrap();
buffer.push(2, 12, 'z').unwrap();
assert_eq!(buffer.buffered(1), 2);
let input = |peer, input, fresh| TickInput { peer, input, fresh };
assert_eq!(
buffer.take(),
(10, vec![input(1, 'a', true), input(2, 'x', true)])
);
assert_eq!(
buffer.take(),
(11, vec![input(1, 'b', true), input(2, 'x', false)])
);
assert_eq!(
buffer.take(),
(12, vec![input(1, 'b', false), input(2, 'z', true)])
);
assert!(buffer.push(2, 11, 'y').is_err());
assert_eq!(buffer.late(2), 1);
assert!(buffer.push(1, 13 + MAX_AHEAD + 1, 'q').is_err());
buffer.push(3, 20, 'n').unwrap();
assert_eq!(buffer.take().1.len(), 2); buffer.forget(1);
assert_eq!(buffer.take().1, [input(2, 'z', false)]);
}
}