2015-08-20 17:27:15 +02:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#
|
|
|
|
# This library is free software, distributed under the terms of
|
|
|
|
# the GNU Lesser General Public License Version 3, or any later version.
|
|
|
|
# See the COPYING file included in this archive
|
|
|
|
#
|
|
|
|
# The docstrings in this module contain epytext markup; API documentation
|
|
|
|
# may be created by processing this file with epydoc: http://epydoc.sf.net
|
|
|
|
|
|
|
|
import constants
|
|
|
|
|
2017-03-31 19:32:43 +02:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
class BucketFull(Exception):
|
|
|
|
""" Raised when the bucket is full """
|
|
|
|
|
|
|
|
|
|
|
|
class KBucket(object):
|
|
|
|
""" Description - later
|
|
|
|
"""
|
2017-03-31 19:32:43 +02:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
def __init__(self, rangeMin, rangeMax):
|
|
|
|
"""
|
|
|
|
@param rangeMin: The lower boundary for the range in the n-bit ID
|
|
|
|
space covered by this k-bucket
|
|
|
|
@param rangeMax: The upper boundary for the range in the ID space
|
|
|
|
covered by this k-bucket
|
|
|
|
"""
|
|
|
|
self.lastAccessed = 0
|
|
|
|
self.rangeMin = rangeMin
|
|
|
|
self.rangeMax = rangeMax
|
|
|
|
self._contacts = list()
|
|
|
|
|
|
|
|
def addContact(self, contact):
|
|
|
|
""" Add contact to _contact list in the right order. This will move the
|
|
|
|
contact to the end of the k-bucket if it is already present.
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@raise kademlia.kbucket.BucketFull: Raised when the bucket is full and
|
|
|
|
the contact isn't in the bucket
|
|
|
|
already
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@param contact: The contact to add
|
|
|
|
@type contact: kademlia.contact.Contact
|
|
|
|
"""
|
|
|
|
if contact in self._contacts:
|
|
|
|
# Move the existing contact to the end of the list
|
2016-11-30 21:20:45 +01:00
|
|
|
# - using the new contact to allow add-on data
|
|
|
|
# (e.g. optimization-specific stuff) to pe updated as well
|
2015-08-20 17:27:15 +02:00
|
|
|
self._contacts.remove(contact)
|
|
|
|
self._contacts.append(contact)
|
|
|
|
elif len(self._contacts) < constants.k:
|
|
|
|
self._contacts.append(contact)
|
|
|
|
else:
|
|
|
|
raise BucketFull("No space in bucket to insert contact")
|
|
|
|
|
|
|
|
def getContact(self, contactID):
|
|
|
|
""" Get the contact specified node ID"""
|
|
|
|
index = self._contacts.index(contactID)
|
|
|
|
return self._contacts[index]
|
|
|
|
|
|
|
|
def getContacts(self, count=-1, excludeContact=None):
|
|
|
|
""" Returns a list containing up to the first count number of contacts
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@param count: The amount of contacts to return (if 0 or less, return
|
|
|
|
all contacts)
|
|
|
|
@type count: int
|
|
|
|
@param excludeContact: A contact to exclude; if this contact is in
|
|
|
|
the list of returned values, it will be
|
|
|
|
discarded before returning. If a C{str} is
|
|
|
|
passed as this argument, it must be the
|
2016-12-14 00:08:29 +01:00
|
|
|
contact's ID.
|
2015-08-20 17:27:15 +02:00
|
|
|
@type excludeContact: kademlia.contact.Contact or str
|
2016-12-14 00:08:29 +01:00
|
|
|
|
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@raise IndexError: If the number of requested contacts is too large
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@return: Return up to the first count number of contacts in a list
|
|
|
|
If no contacts are present an empty is returned
|
|
|
|
@rtype: list
|
|
|
|
"""
|
|
|
|
# Return all contacts in bucket
|
|
|
|
if count <= 0:
|
|
|
|
count = len(self._contacts)
|
|
|
|
|
|
|
|
# Get current contact number
|
|
|
|
currentLen = len(self._contacts)
|
|
|
|
|
|
|
|
# If count greater than k - return only k contacts
|
|
|
|
if count > constants.k:
|
|
|
|
count = constants.k
|
|
|
|
|
|
|
|
# Check if count value in range and,
|
|
|
|
# if count number of contacts are available
|
|
|
|
if not currentLen:
|
|
|
|
contactList = list()
|
|
|
|
|
|
|
|
# length of list less than requested amount
|
|
|
|
elif currentLen < count:
|
|
|
|
contactList = self._contacts[0:currentLen]
|
|
|
|
# enough contacts in list
|
|
|
|
else:
|
|
|
|
contactList = self._contacts[0:count]
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
if excludeContact in contactList:
|
|
|
|
contactList.remove(excludeContact)
|
|
|
|
|
|
|
|
return contactList
|
|
|
|
|
|
|
|
def removeContact(self, contact):
|
|
|
|
""" Remove given contact from list
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@param contact: The contact to remove, or a string containing the
|
|
|
|
contact's node ID
|
|
|
|
@type contact: kademlia.contact.Contact or str
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@raise ValueError: The specified contact is not in this bucket
|
|
|
|
"""
|
|
|
|
self._contacts.remove(contact)
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
def keyInRange(self, key):
|
|
|
|
""" Tests whether the specified key (i.e. node ID) is in the range
|
|
|
|
of the n-bit ID space covered by this k-bucket (in otherwords, it
|
|
|
|
returns whether or not the specified key should be placed in this
|
|
|
|
k-bucket)
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@param key: The key to test
|
|
|
|
@type key: str or int
|
2016-12-14 00:08:29 +01:00
|
|
|
|
2015-08-20 17:27:15 +02:00
|
|
|
@return: C{True} if the key is in this k-bucket's range, or C{False}
|
|
|
|
if not.
|
|
|
|
@rtype: bool
|
|
|
|
"""
|
|
|
|
if isinstance(key, str):
|
|
|
|
key = long(key.encode('hex'), 16)
|
|
|
|
return self.rangeMin <= key < self.rangeMax
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self._contacts)
|