KBucket.remove_node
removes nodes in replacement_nodes. (#66)
This commit is contained in:
parent
44007c5211
commit
62bdcd852a
@ -5,14 +5,14 @@ import asyncio
|
|||||||
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from kademlia.utils import OrderedSet, shared_prefix, bytes_to_bit_string
|
from kademlia.utils import shared_prefix, bytes_to_bit_string
|
||||||
|
|
||||||
|
|
||||||
class KBucket:
|
class KBucket:
|
||||||
def __init__(self, rangeLower, rangeUpper, ksize):
|
def __init__(self, rangeLower, rangeUpper, ksize):
|
||||||
self.range = (rangeLower, rangeUpper)
|
self.range = (rangeLower, rangeUpper)
|
||||||
self.nodes = OrderedDict()
|
self.nodes = OrderedDict()
|
||||||
self.replacement_nodes = OrderedSet()
|
self.replacement_nodes = OrderedDict()
|
||||||
self.touch_last_updated()
|
self.touch_last_updated()
|
||||||
self.ksize = ksize
|
self.ksize = ksize
|
||||||
|
|
||||||
@ -26,21 +26,23 @@ class KBucket:
|
|||||||
midpoint = (self.range[0] + self.range[1]) / 2
|
midpoint = (self.range[0] + self.range[1]) / 2
|
||||||
one = KBucket(self.range[0], midpoint, self.ksize)
|
one = KBucket(self.range[0], midpoint, self.ksize)
|
||||||
two = KBucket(midpoint + 1, self.range[1], self.ksize)
|
two = KBucket(midpoint + 1, self.range[1], self.ksize)
|
||||||
for node in chain(self.nodes.values(), self.replacement_nodes):
|
nodes = chain(self.nodes.values(), self.replacement_nodes.values())
|
||||||
|
for node in nodes:
|
||||||
bucket = one if node.long_id <= midpoint else two
|
bucket = one if node.long_id <= midpoint else two
|
||||||
bucket.add_node(node)
|
bucket.add_node(node)
|
||||||
|
|
||||||
return (one, two)
|
return (one, two)
|
||||||
|
|
||||||
def remove_node(self, node):
|
def remove_node(self, node):
|
||||||
if node.id not in self.nodes:
|
if node.id in self.replacement_nodes:
|
||||||
return
|
del self.replacement_nodes[node.id]
|
||||||
|
|
||||||
# delete node, and see if we can add a replacement
|
if node.id in self.nodes:
|
||||||
del self.nodes[node.id]
|
del self.nodes[node.id]
|
||||||
if self.replacement_nodes:
|
|
||||||
newnode = self.replacement_nodes.pop()
|
if self.replacement_nodes:
|
||||||
self.nodes[newnode.id] = newnode
|
newnode_id, newnode = self.replacement_nodes.popitem()
|
||||||
|
self.nodes[newnode_id] = newnode
|
||||||
|
|
||||||
def has_in_range(self, node):
|
def has_in_range(self, node):
|
||||||
return self.range[0] <= node.long_id <= self.range[1]
|
return self.range[0] <= node.long_id <= self.range[1]
|
||||||
@ -62,7 +64,9 @@ class KBucket:
|
|||||||
elif len(self) < self.ksize:
|
elif len(self) < self.ksize:
|
||||||
self.nodes[node.id] = node
|
self.nodes[node.id] = node
|
||||||
else:
|
else:
|
||||||
self.replacement_nodes.push(node)
|
if node.id in self.replacement_nodes:
|
||||||
|
del self.replacement_nodes[node.id]
|
||||||
|
self.replacement_nodes[node.id] = node
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from random import shuffle
|
||||||
from kademlia.routing import KBucket, TableTraverser
|
from kademlia.routing import KBucket, TableTraverser
|
||||||
from kademlia.tests.utils import mknode, FakeProtocol
|
from kademlia.tests.utils import mknode, FakeProtocol
|
||||||
|
|
||||||
@ -31,6 +32,31 @@ class KBucketTest(unittest.TestCase):
|
|||||||
for index, node in enumerate(bucket.get_nodes()):
|
for index, node in enumerate(bucket.get_nodes()):
|
||||||
self.assertEqual(node, nodes[index])
|
self.assertEqual(node, nodes[index])
|
||||||
|
|
||||||
|
def test_remove_node(self):
|
||||||
|
k = 3
|
||||||
|
bucket = KBucket(0, 10, k)
|
||||||
|
nodes = [mknode() for _ in range(10)]
|
||||||
|
for node in nodes:
|
||||||
|
bucket.add_node(node)
|
||||||
|
|
||||||
|
replacement_nodes = bucket.replacement_nodes
|
||||||
|
self.assertEqual(list(bucket.nodes.values()), nodes[:k])
|
||||||
|
self.assertEqual(list(replacement_nodes.values()), nodes[k:])
|
||||||
|
|
||||||
|
bucket.remove_node(nodes.pop())
|
||||||
|
self.assertEqual(list(bucket.nodes.values()), nodes[:k])
|
||||||
|
self.assertEqual(list(replacement_nodes.values()), nodes[k:])
|
||||||
|
|
||||||
|
bucket.remove_node(nodes.pop(0))
|
||||||
|
self.assertEqual(list(bucket.nodes.values()), nodes[:k-1] + nodes[-1:])
|
||||||
|
self.assertEqual(list(replacement_nodes.values()), nodes[k-1:-1])
|
||||||
|
|
||||||
|
shuffle(nodes)
|
||||||
|
for node in nodes:
|
||||||
|
bucket.remove_node(node)
|
||||||
|
self.assertEqual(len(bucket), 0)
|
||||||
|
self.assertEqual(len(replacement_nodes), 0)
|
||||||
|
|
||||||
def test_in_range(self):
|
def test_in_range(self):
|
||||||
bucket = KBucket(0, 10, 10)
|
bucket = KBucket(0, 10, 10)
|
||||||
self.assertTrue(bucket.has_in_range(mknode(intid=5)))
|
self.assertTrue(bucket.has_in_range(mknode(intid=5)))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from kademlia.utils import digest, shared_prefix, OrderedSet
|
from kademlia.utils import digest, shared_prefix
|
||||||
|
|
||||||
|
|
||||||
class UtilsTest(unittest.TestCase):
|
class UtilsTest(unittest.TestCase):
|
||||||
@ -24,13 +24,3 @@ class UtilsTest(unittest.TestCase):
|
|||||||
|
|
||||||
args = ['hi']
|
args = ['hi']
|
||||||
self.assertEqual(shared_prefix(args), 'hi')
|
self.assertEqual(shared_prefix(args), 'hi')
|
||||||
|
|
||||||
|
|
||||||
class OrderedSetTest(unittest.TestCase):
|
|
||||||
def test_order(self):
|
|
||||||
oset = OrderedSet()
|
|
||||||
oset.push('1')
|
|
||||||
oset.push('1')
|
|
||||||
oset.push('2')
|
|
||||||
oset.push('1')
|
|
||||||
self.assertEqual(oset, ['2', '1'])
|
|
||||||
|
@ -18,22 +18,6 @@ def digest(string):
|
|||||||
return hashlib.sha1(string).digest()
|
return hashlib.sha1(string).digest()
|
||||||
|
|
||||||
|
|
||||||
class OrderedSet(list):
|
|
||||||
"""
|
|
||||||
Acts like a list in all ways, except in the behavior of the
|
|
||||||
:meth:`push` method.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def push(self, thing):
|
|
||||||
"""
|
|
||||||
1. If the item exists in the list, it's removed
|
|
||||||
2. The item is pushed to the end of the list
|
|
||||||
"""
|
|
||||||
if thing in self:
|
|
||||||
self.remove(thing)
|
|
||||||
self.append(thing)
|
|
||||||
|
|
||||||
|
|
||||||
def shared_prefix(args):
|
def shared_prefix(args):
|
||||||
"""
|
"""
|
||||||
Find the shared prefix between the strings.
|
Find the shared prefix between the strings.
|
||||||
|
Loading…
Reference in New Issue
Block a user