use std::collections::{HashMap, VecDeque};
use std::net::SocketAddr;
use std::sync::RwLock;
use std::time::{Duration, SystemTime};
use crate::block::BlockID;
const MAX_QUEUE_WAIT: Duration = Duration::from_secs(2 * 60);
pub struct BlockQueue {
inner: RwLock<BlockQueueInner>,
}
struct BlockQueueInner {
block_map: HashMap<BlockID, usize>,
block_queue: VecDeque<BlockQueueEntry>,
}
#[derive(Clone)]
struct BlockQueueEntry {
id: BlockID,
who: SocketAddr,
when: SystemTime,
}
impl BlockQueue {
pub fn new() -> Self {
Self {
inner: RwLock::new(BlockQueueInner {
block_map: HashMap::new(),
block_queue: VecDeque::new(),
}),
}
}
pub fn add(&self, id: &BlockID, who: &SocketAddr) -> bool {
let mut inner = self.inner.write().unwrap();
if let Some(&index) = inner.block_map.get(id) {
let entry = &mut inner.block_queue[index];
let elapsed = entry
.when
.elapsed()
.expect("couldn't get elapsed time from block queue entry");
if elapsed < MAX_QUEUE_WAIT {
return false;
}
entry.when = SystemTime::now();
entry.who = *who;
return true;
}
let entry = BlockQueueEntry {
id: *id,
who: *who,
when: SystemTime::now(),
};
inner.block_queue.push_back(entry);
let index = inner.block_queue.len() - 1;
inner.block_map.insert(*id, index);
true
}
pub fn remove(&self, id: &BlockID, who: &SocketAddr) -> bool {
let mut inner = self.inner.write().unwrap();
if let Some(&index) = inner.block_map.get(id) {
if inner.block_queue[index].who == *who {
inner.block_queue.remove(index);
inner.block_map.remove(id);
for (_, idx) in inner.block_map.iter_mut() {
if *idx > index {
*idx -= 1;
}
}
return true;
}
}
false
}
pub fn exists(&self, id: &BlockID) -> bool {
self.inner.read().unwrap().block_map.contains_key(id)
}
pub fn peek(&self) -> Option<BlockID> {
let inner = self.inner.read().unwrap();
inner.block_queue.front().map(|entry| entry.id)
}
pub fn len(&self) -> usize {
self.inner.read().unwrap().block_queue.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Default for BlockQueue {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
fn make_test_id(n: u8) -> BlockID {
BlockID::from(&[n; 32][..])
}
fn make_test_addr(port: u16) -> SocketAddr {
SocketAddr::from_str(&format!("127.0.0.1:{}", port)).unwrap()
}
#[test]
fn test_add_remove() {
let queue = BlockQueue::new();
let id1 = make_test_id(1);
let id2 = make_test_id(2);
let addr1 = make_test_addr(8080);
let addr2 = make_test_addr(8081);
assert!(queue.add(&id1, &addr1));
assert!(queue.add(&id2, &addr1));
assert_eq!(queue.len(), 2);
assert!(!queue.add(&id1, &addr2));
assert_eq!(queue.len(), 2);
assert!(queue.remove(&id1, &addr1));
assert_eq!(queue.len(), 1);
assert!(!queue.remove(&id2, &addr2));
assert_eq!(queue.len(), 1);
assert!(queue.remove(&id2, &addr1));
assert_eq!(queue.len(), 0);
}
#[test]
fn test_peek_order() {
let queue = BlockQueue::new();
let id1 = make_test_id(1);
let id2 = make_test_id(2);
let id3 = make_test_id(3);
let addr = make_test_addr(8080);
queue.add(&id1, &addr);
queue.add(&id2, &addr);
queue.add(&id3, &addr);
assert_eq!(queue.peek(), Some(id1));
queue.remove(&id2, &addr);
assert_eq!(queue.peek(), Some(id1));
queue.remove(&id1, &addr);
assert_eq!(queue.peek(), Some(id3));
}
}