use std::borrow::Borrow;
use crate::adnl;
use crate::proto;
use crate::util::*;
pub struct Buckets {
local_id: [u8; 32],
buckets: Box<[FxDashMap<adnl::NodeIdShort, proto::dht::NodeOwned>; 256]>,
}
impl Buckets {
pub fn new(local_id: &adnl::NodeIdShort) -> Self {
Self {
local_id: *local_id.as_slice(),
buckets: Box::new([(); 256].map(|_| Default::default())),
}
}
pub fn iter(&self) -> std::slice::Iter<FxDashMap<adnl::NodeIdShort, proto::dht::NodeOwned>> {
self.buckets.iter()
}
pub fn insert(&self, peer_id: &adnl::NodeIdShort, peer: proto::dht::NodeOwned) {
use dashmap::mapref::entry::Entry;
let affinity = get_affinity(&self.local_id, peer_id.borrow());
match self.buckets[affinity as usize].entry(*peer_id) {
Entry::Occupied(mut entry) => {
if entry.get().version < peer.version {
entry.insert(peer);
}
}
Entry::Vacant(entry) => {
entry.insert(peer);
}
}
}
pub fn find<T>(&self, peer_id: T, k: u32) -> proto::dht::NodesOwned
where
T: Borrow<[u8; 32]>,
{
let key1 = &self.local_id;
let key2 = peer_id.borrow();
let mut distance = 0u8;
let mut nodes = Vec::new();
'outer: for i in 0..32 {
let mut subdistance = distance;
let mut xor = key1[i] ^ key2[i];
while xor != 0 {
if xor & 0xf0 == 0 {
subdistance = subdistance.saturating_add(4);
xor <<= 4;
} else {
let shift = BITS[(xor >> 4) as usize];
subdistance = subdistance.saturating_add(shift);
let bucket = &self.buckets[subdistance as usize];
for item in bucket.iter() {
nodes.push(item.value().clone());
if nodes.len() >= k as usize {
break 'outer;
}
}
xor <<= shift + 1;
subdistance = subdistance.saturating_add(1);
}
}
distance = distance.saturating_add(8);
}
proto::dht::NodesOwned { nodes }
}
}
impl<'a> IntoIterator for &'a Buckets {
type Item = &'a FxDashMap<adnl::NodeIdShort, proto::dht::NodeOwned>;
type IntoIter = std::slice::Iter<'a, FxDashMap<adnl::NodeIdShort, proto::dht::NodeOwned>>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub fn get_affinity(key1: &[u8; 32], key2: &[u8; 32]) -> u8 {
let mut result = 0;
for i in 0..32 {
match key1[i] ^ key2[i] {
0 => result += 8,
x => {
if x & 0xf0 == 0 {
result += BITS[(x & 0x0f) as usize] + 4;
} else {
result += BITS[(x >> 4) as usize]
}
break;
}
}
}
result
}
const BITS: [u8; 16] = [4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0];