use std::{
collections::VecDeque,
num::NonZeroUsize,
time::{Duration, Instant},
};
use arrayvec::{self, ArrayVec};
use bucket::KBucket;
pub use entry::*;
use crate::IdBytes;
mod bucket;
mod entry;
mod key;
pub const K_VALUE: NonZeroUsize = NonZeroUsize::new(20).unwrap();
pub const ALPHA_VALUE: NonZeroUsize = NonZeroUsize::new(3).unwrap();
const NUM_BUCKETS: usize = 256;
#[derive(Debug, Clone)]
pub struct KBucketsTable<TVal> {
local_key: IdBytes,
buckets: Vec<KBucket<TVal>>,
applied_pending: VecDeque<AppliedPending<TVal>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct BucketIndex(usize);
impl BucketIndex {
fn new(d: &Distance) -> Option<BucketIndex> {
(NUM_BUCKETS - d.0.leading_zeros() as usize)
.checked_sub(1)
.map(BucketIndex)
}
fn get(&self) -> usize {
self.0
}
fn rand_distance(&self, rng: &mut impl rand::Rng) -> Distance {
let mut bytes = [0u8; 32];
let quot = self.0 / 8;
for i in 0..quot {
bytes[31 - i] = rng.r#gen();
}
let rem = (self.0 % 8) as u32;
let lower = usize::pow(2, rem);
let upper = usize::pow(2, rem + 1);
bytes[31 - quot] = rng.gen_range(lower, upper) as u8;
Distance(U256::from(bytes))
}
}
impl<TVal> KBucketsTable<TVal>
where
TVal: Clone,
{
pub fn new(local_key: IdBytes, pending_timeout: Duration) -> Self {
KBucketsTable {
local_key,
buckets: (0..NUM_BUCKETS)
.map(|_| KBucket::new(pending_timeout))
.collect(),
applied_pending: VecDeque::new(),
}
}
pub fn local_key(&self) -> &IdBytes {
&self.local_key
}
pub fn entry<'a>(&'a mut self, key: &'a IdBytes) -> Entry<'a, TVal> {
let index = BucketIndex::new(&self.local_key.distance(key.as_ref()));
if let Some(i) = index {
let bucket = &mut self.buckets[i.get()];
if let Some(applied) = bucket.apply_pending() {
self.applied_pending.push_back(applied)
}
Entry::new(bucket, key)
} else {
Entry::SelfEntry
}
}
pub fn iter(&mut self) -> impl Iterator<Item = EntryRefView<'_, TVal>> {
let applied_pending = &mut self.applied_pending;
self.buckets.iter_mut().flat_map(move |table| {
if let Some(applied) = table.apply_pending() {
applied_pending.push_back(applied)
}
let table = &*table;
table.iter().map(move |(n, status)| EntryRefView {
node: NodeRefView {
key: &n.key,
value: &n.value,
},
status,
})
})
}
pub fn buckets(&mut self) -> impl Iterator<Item = KBucketRef<'_, TVal>> + '_ {
let applied_pending = &mut self.applied_pending;
self.buckets.iter_mut().enumerate().map(move |(i, b)| {
if let Some(applied) = b.apply_pending() {
applied_pending.push_back(applied)
}
KBucketRef {
index: BucketIndex(i),
bucket: b,
}
})
}
pub fn take_applied_pending(&mut self) -> Option<AppliedPending<TVal>> {
self.applied_pending.pop_front()
}
pub fn closest_keys<'a>(
&'a mut self,
target: &'a IdBytes,
) -> impl Iterator<Item = IdBytes> + 'a {
let distance = self.local_key.distance(target.as_ref());
ClosestIter {
target,
iter: None,
table: self,
buckets_iter: ClosestBucketsIter::new(distance),
}
.map(|e| e.node.key)
}
pub fn closest<'a>(
&'a mut self,
target: &'a IdBytes,
) -> impl Iterator<Item = EntryView<TVal>> + 'a
where
TVal: Clone,
{
let distance = self.local_key.distance(target.as_ref());
ClosestIter {
target,
iter: None,
table: self,
buckets_iter: ClosestBucketsIter::new(distance),
}
}
pub fn count_nodes_between(&self, target: &IdBytes) -> usize {
let local_key = self.local_key;
let distance = target.distance(local_key.as_ref());
let mut iter = ClosestBucketsIter::new(distance).take_while(|i| i.get() != 0);
if let Some(i) = iter.next() {
let num_first = self.buckets[i.get()]
.iter()
.filter(|(n, _)| n.key.distance(local_key) <= distance)
.count();
let num_rest: usize = iter.map(|i| self.buckets[i.get()].num_entries()).sum();
num_first + num_rest
} else {
0
}
}
}
struct ClosestIter<'a, TVal> {
target: &'a IdBytes,
table: &'a mut KBucketsTable<TVal>,
buckets_iter: ClosestBucketsIter,
iter: Option<arrayvec::IntoIter<[EntryView<TVal>; K_VALUE.get()]>>,
}
struct ClosestBucketsIter {
distance: Distance,
state: ClosestBucketsIterState,
}
enum ClosestBucketsIterState {
Start(BucketIndex),
ZoomIn(BucketIndex),
ZoomOut(BucketIndex),
Done,
}
impl ClosestBucketsIter {
fn new(distance: Distance) -> Self {
let state = match BucketIndex::new(&distance) {
Some(i) => ClosestBucketsIterState::Start(i),
None => ClosestBucketsIterState::Start(BucketIndex(0)),
};
Self { distance, state }
}
fn next_in(&self, i: BucketIndex) -> Option<BucketIndex> {
(0..i.get()).rev().find_map(|i| {
if self.distance.0.bit(i) {
Some(BucketIndex(i))
} else {
None
}
})
}
fn next_out(&self, i: BucketIndex) -> Option<BucketIndex> {
(i.get() + 1..NUM_BUCKETS).find_map(|i| {
if !self.distance.0.bit(i) {
Some(BucketIndex(i))
} else {
None
}
})
}
}
impl Iterator for ClosestBucketsIter {
type Item = BucketIndex;
fn next(&mut self) -> Option<Self::Item> {
match self.state {
ClosestBucketsIterState::Start(i) => {
self.state = ClosestBucketsIterState::ZoomIn(i);
Some(i)
}
ClosestBucketsIterState::ZoomIn(i) => {
if let Some(i) = self.next_in(i) {
self.state = ClosestBucketsIterState::ZoomIn(i);
Some(i)
} else {
let i = BucketIndex(0);
self.state = ClosestBucketsIterState::ZoomOut(i);
Some(i)
}
}
ClosestBucketsIterState::ZoomOut(i) => {
if let Some(i) = self.next_out(i) {
self.state = ClosestBucketsIterState::ZoomOut(i);
Some(i)
} else {
self.state = ClosestBucketsIterState::Done;
None
}
}
ClosestBucketsIterState::Done => None,
}
}
}
impl<TVal> Iterator for ClosestIter<'_, TVal>
where
TVal: Clone,
{
type Item = EntryView<TVal>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match &mut self.iter {
Some(iter) => match iter.next() {
Some(k) => return Some(k),
None => self.iter = None,
},
None => {
if let Some(i) = self.buckets_iter.next() {
let bucket = &mut self.table.buckets[i.get()];
if let Some(applied) = bucket.apply_pending() {
self.table.applied_pending.push_back(applied)
}
let mut v: ArrayVec<[EntryView<_>; K_VALUE.get()]> = bucket
.iter()
.map(|(n, status)| EntryView {
node: n.clone(),
status,
})
.collect();
v.sort_by(|a, b| {
self.target
.distance(a.as_ref())
.cmp(&self.target.distance(b.as_ref()))
});
self.iter = Some(v.into_iter());
} else {
return None;
}
}
}
}
}
}
pub struct KBucketRef<'a, TVal> {
index: BucketIndex,
bucket: &'a mut KBucket<TVal>,
}
impl<TVal> KBucketRef<'_, TVal>
where
TVal: Clone,
{
pub fn num_entries(&self) -> usize {
self.bucket.num_entries()
}
pub fn has_pending(&self) -> bool {
self.bucket.pending().is_some_and(|n| !n.is_ready())
}
pub fn contains(&self, d: &Distance) -> bool {
BucketIndex::new(d) == Some(self.index)
}
pub fn rand_distance(&self, rng: &mut impl rand::Rng) -> Distance {
self.index.rand_distance(rng)
}
}