/** * Splits the bucket in two by keeping all peers with a DHT key less than * <code>pivot</code> in the existing bucket, and moving the rest into a new bucket. * @param pivot * @return The new bucket (which contains the higher IDs) */ private KBucket split(BigInteger pivot) { depth++; KBucket newBucket = new KBucket(pivot, endId, depth); endId = pivot; for (int i=peers.size()-1; i>=0; i--) { KademliaPeer peer = peers.get(i); BigInteger nodeId = new BigInteger(1, peer.getDestinationHash().getData()); if (nodeId.compareTo(pivot) >= 0) { newBucket.add(peer); remove(peer); } } return newBucket; }
/** * Moves peers from the k-buckets to the s-bucket until the s-bucket is full * or all k-buckets are empty. */ private void refillSiblings() { // Sort all k-peers by distance to the local destination List<KademliaPeer> kPeers = new ArrayList<KademliaPeer>(); for (KBucket kBucket: kBuckets) kPeers.addAll(kBucket.getPeers()); Collections.sort(kPeers, new PeerDistanceComparator(localDestinationHash)); while (!sBucket.isFull() && !kPeers.isEmpty()) { // move the closest k-peer to the s-bucket KademliaPeer peerToMove = kPeers.remove(0); int bucketIndex = getBucketIndex(peerToMove.getDestinationHash()); kBuckets.get(bucketIndex).remove(peerToMove); sBucket.addOrUpdate(peerToMove); } }
/** * Adds a <code>{@link KademliaPeer}</code> to the s-bucket or a k-bucket, * depending on its distance to the local node and how full the buckets are. * @param destination */ public void addOrUpdate(KademliaPeer peer) { Hash destHash = peer.getDestinationHash(); if (localDestinationHash.equals(destHash)) { log.debug("Not adding local destination to bucket."); return; } KademliaPeer removedOrNotAdded = sBucket.addOrUpdate(peer); if (removedOrNotAdded == null) getKBucket(destHash).remove(peer); // if the peer was in a k-bucket, remove it because it is now in the s-bucket else addToKBucket(removedOrNotAdded); // if a peer was removed from the s-bucket or didn't qualify as a sibling, add it to a k-bucket // log int numBuckets = kBuckets.size(); int numPeers = getAllPeers().size(); int numSiblings = sBucket.size(); log.debug("Peer " + Util.toBase32(destHash) + " added/updated. Peers=" + numPeers + " sib=" + numSiblings + " buk=" + numBuckets + " (not counting the sibling bucket)"); }
@Test public void testRemove() { for (KBucket bucket: buckets) { Destination[] destinations = destinationMap.get(bucket); assertEquals(0, bucket.size()); bucket.addOrUpdate(new KademliaPeer(destinations[0])); assertEquals(1, bucket.size()); bucket.remove(destinations[0]); assertEquals(0, bucket.size()); bucket.addOrUpdate(new KademliaPeer(destinations[0])); bucket.addOrUpdate(new KademliaPeer(destinations[1])); bucket.addOrUpdate(new KademliaPeer(destinations[2])); assertEquals(3, bucket.size()); bucket.remove(destinations[0]); assertEquals(2, bucket.size()); bucket.remove(destinations[1]); assertEquals(1, bucket.size()); bucket.remove(destinations[2]); assertEquals(0, bucket.size()); } }