use crate::common::{DatabaseId, Entry};
use std::collections::VecDeque;
use std::fmt::Debug;
use std::time::Instant;
use log::trace;
pub struct KBucket<Id, Info> {
bucket_size: usize,
nodes: VecDeque<Entry<Id, Info>>,
pending: Option<Entry<Id, Info>>,
updated: Option<Instant>,
}
impl<Id, Info> KBucket<Id, Info>
where
Id: DatabaseId + 'static,
Info: Clone + Debug + 'static,
{
pub fn new(bucket_size: usize) -> KBucket<Id, Info> {
KBucket {
bucket_size,
nodes: VecDeque::with_capacity(bucket_size),
pending: None,
updated: None,
}
}
pub fn create_or_update(&mut self, node: &Entry<Id, Info>) -> bool {
let res = if let Some(_n) = self.nodes.clone().iter().find(|n| n.id() == node.id()) {
trace!(target: "dht", "[KBucket] Updating node {:?}", node);
KBucket::update_position(&mut self.nodes, node);
true
} else if self.nodes.len() < self.bucket_size {
trace!(target: "dht", "[KBucket] Adding node {:?}", node);
self.nodes.push_front(node.clone());
true
} else {
trace!(target: "dht", "[KBucket] No space to add node {:?}", node);
self.pending = Some(node.clone());
false
};
if res {
self.updated = Some(Instant::now());
}
res
}
pub fn find(&self, id: &Id) -> Option<Entry<Id, Info>> {
self.nodes
.iter()
.find(|n| *n.id() == *id)
.map(|n| n.clone())
}
pub fn nodes(&self) -> Vec<Entry<Id, Info>> {
self.nodes.iter().map(|n| n.clone()).collect()
}
pub fn updated(&self) -> Option<Instant> {
self.updated
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn oldest(&self) -> Option<Entry<Id, Info>> {
if self.nodes.len() == 0 {
return None;
}
self.nodes.get(self.nodes.len() - 1).map(|n| n.clone())
}
fn update_position(nodes: &mut VecDeque<Entry<Id, Info>>, node: &Entry<Id, Info>) {
let found =
nodes.iter().enumerate().find_map(
|(i, n)| {
if n.id() == node.id() {
Some(i)
} else {
None
}
},
);
let i = match found {
Some(v) => v,
None => return,
};
nodes.remove(i);
nodes.push_front(node.clone());
}
pub fn update_entry<F>(&mut self, id: &Id, f: F) -> bool
where
F: Fn(&mut Entry<Id, Info>),
{
if let Some(ref mut n) = self.nodes.iter_mut().find(|n| n.id() == id) {
(f)(n);
return true;
}
false
}
pub fn remove_entry(&mut self, id: &Id, replace: bool) {
let index =
self.nodes.iter().enumerate().find_map(
|(i, n)| {
if n.id() == id {
Some(i)
} else {
None
}
},
);
let index = match index {
Some(i) => i,
None => return,
};
self.nodes.remove(index);
if replace && self.nodes.len() < self.bucket_size && self.pending.is_some() {
self.nodes.push_back(self.pending.take().unwrap());
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_k_bucket_update() {
let mut b = KBucket::<[u8; 1], u64>::new(4);
assert_eq!(true, b.find(&[0b0000]).is_none());
let n1 = Entry::new([0b0000], 1);
let n2 = Entry::new([0b0001], 2);
let n3 = Entry::new([0b0010], 3);
let n4 = Entry::new([0b0011], 4);
let n5 = Entry::new([0b0100], 5);
assert_eq!(true, b.create_or_update(&n1));
assert_eq!(n1, b.find(n1.id()).unwrap());
assert_eq!(true, b.create_or_update(&n2));
assert_eq!(n2, b.find(n2.id()).unwrap());
assert_eq!(true, b.create_or_update(&n3));
assert_eq!(n3, b.find(n3.id()).unwrap());
assert_eq!(true, b.create_or_update(&n4));
assert_eq!(n4, b.find(n4.id()).unwrap());
assert_eq!(false, b.create_or_update(&n5));
assert_eq!(
b.nodes(),
vec![n4.clone(), n3.clone(), n2.clone(), n1.clone()]
);
assert_eq!(true, b.create_or_update(&n1));
assert_eq!(
b.nodes(),
vec![n1.clone(), n4.clone(), n3.clone(), n2.clone()]
);
let mut n4a = n4.clone();
n4a.set_info(&5);
assert_eq!(true, b.create_or_update(&n4a));
assert_eq!(n4a, b.find(n4.id()).unwrap());
assert_eq!(
b.nodes(),
vec![n4a.clone(), n1.clone(), n3.clone(), n2.clone()]
);
b.remove_entry(n1.id(), true);
assert_eq!(
b.nodes(),
vec![n4a.clone(), n3.clone(), n2.clone(), n5.clone()]
);
}
}