Comparing version 3.0.0 to 3.0.1
@@ -1,14 +0,12 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require("../index.js"); | ||
var _0000000100100100 = new Buffer('0124', 'hex') | ||
var _0100000000100100 = new Buffer('4024', 'hex') | ||
var _0000000100100100 = new Buffer("0124", "hex"); | ||
var _0100000000100100 = new Buffer("4024", "hex"); | ||
var hrtime = process.hrtime(); | ||
for (var i = 0; i < 1e7; i++) | ||
{ | ||
KBucket.distance(_0000000100100100, _0100000000100100); | ||
var hrtime = process.hrtime() | ||
for (var i = 0; i < 1e7; i++) { | ||
KBucket.distance(_0000000100100100, _0100000000100100) | ||
} | ||
var diff = process.hrtime(hrtime); | ||
console.log(diff[0] * 1e9 + diff[1]); | ||
var diff = process.hrtime(hrtime) | ||
console.log(diff[0] * 1e9 + diff[1]) |
488
index.js
/* | ||
index.js - Kademlia DHT K-bucket implementation as a binary tree. | ||
@@ -29,11 +28,12 @@ | ||
OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
"use strict"; | ||
'use strict' | ||
var bufferEquals = require('buffer-equals'); | ||
var EventEmitter = require('events').EventEmitter; | ||
var inherits = require('inherits'); | ||
var randomBytes = require('randombytes'); | ||
var bufferEquals = require('buffer-equals') | ||
var randomBytes = require('randombytes') | ||
var EventEmitter = require('events').EventEmitter | ||
var inherits = require('inherits') | ||
module.exports = KBucket | ||
/* | ||
@@ -59,46 +59,36 @@ * `options`: | ||
*/ | ||
var KBucket = module.exports = function KBucket (options) { | ||
var self = this; | ||
EventEmitter.call(self); | ||
options = options || {}; | ||
function KBucket (options) { | ||
EventEmitter.call(this) | ||
options = options || {} | ||
// use an arbiter from options or vectorClock arbiter by default | ||
self.arbiter = options.arbiter || function arbiter(incumbent, candidate) { | ||
if (incumbent.vectorClock > candidate.vectorClock) { | ||
return incumbent; | ||
} | ||
return candidate; | ||
}; | ||
// use an arbiter from options or vectorClock arbiter by default | ||
this.arbiter = options.arbiter || function arbiter (incumbent, candidate) { | ||
return incumbent.vectorClock > candidate.vectorClock ? incumbent : candidate | ||
} | ||
// the bucket array has least-recently-contacted at the "front/left" side | ||
// and the most-recently-contaced at the "back/right" side | ||
self.bucket = []; | ||
self.localNodeId = options.localNodeId || randomBytes(20); | ||
if (!Buffer.isBuffer(self.localNodeId)) { | ||
throw new TypeError("localNodeId is not a Buffer"); | ||
} | ||
self.numberOfNodesPerKBucket = options.numberOfNodesPerKBucket || 20; | ||
self.numberOfNodesToPing = options.numberOfNodesToPing || 3; | ||
self.root = options.root || self; | ||
// the bucket array has least-recently-contacted at the "front/left" side | ||
// and the most-recently-contaced at the "back/right" side | ||
this.bucket = [] | ||
this.localNodeId = options.localNodeId || randomBytes(20) | ||
if (!Buffer.isBuffer(this.localNodeId)) throw new TypeError('localNodeId is not a Buffer') | ||
this.numberOfNodesPerKBucket = options.numberOfNodesPerKBucket || 20 | ||
this.numberOfNodesToPing = options.numberOfNodesToPing || 3 | ||
this.root = options.root || this | ||
// V8 hints | ||
self.dontSplit = null; | ||
self.low = null; | ||
self.high = null; | ||
}; | ||
// V8 hints | ||
this.dontSplit = null | ||
this.low = null | ||
this.high = null | ||
} | ||
inherits(KBucket, EventEmitter); | ||
inherits(KBucket, EventEmitter) | ||
KBucket.distance = function distance (firstId, secondId) { | ||
var distance = 0; | ||
var min = Math.min(firstId.length, secondId.length); | ||
var max = Math.max(firstId.length, secondId.length); | ||
for (var i = 0; i < min; ++i) { | ||
distance = distance * 256 + (firstId[i] ^ secondId[i]); | ||
} | ||
for (; i < max; ++i) { | ||
distance = distance * 256 + 255; | ||
} | ||
return distance; | ||
}; | ||
KBucket.distance = function (firstId, secondId) { | ||
var distance = 0 | ||
var min = Math.min(firstId.length, secondId.length) | ||
var max = Math.max(firstId.length, secondId.length) | ||
for (var i = 0; i < min; ++i) distance = distance * 256 + (firstId[i] ^ secondId[i]) | ||
for (; i < max; ++i) distance = distance * 256 + 255 | ||
return distance | ||
} | ||
@@ -109,50 +99,46 @@ // contact: *required* the contact object to add | ||
KBucket.prototype._add = function (contact, bitIndex) { | ||
var self = this; | ||
// first check whether we are an inner node or a leaf (with bucket contents) | ||
if (!self.bucket) { | ||
// this is not a leaf node but an inner node with 'low' and 'high' | ||
// branches; we will check the appropriate bit of the identifier and | ||
// delegate to the appropriate node for further processing | ||
if (self._determineBucket(contact.id, bitIndex++) < 0) { | ||
return self.low._add(contact, bitIndex); | ||
} else { | ||
return self.high._add(contact, bitIndex); | ||
} | ||
// first check whether we are an inner node or a leaf (with bucket contents) | ||
if (this.bucket === null) { | ||
// this is not a leaf node but an inner node with 'low' and 'high' | ||
// branches; we will check the appropriate bit of the identifier and | ||
// delegate to the appropriate node for further processing | ||
if (this._determineBucket(contact.id, bitIndex++) < 0) { | ||
return this.low._add(contact, bitIndex) | ||
} else { | ||
return this.high._add(contact, bitIndex) | ||
} | ||
} | ||
// check if the contact already exists | ||
var index = self._indexOf(contact.id); | ||
if (index >= 0) { | ||
self._update(contact, index); | ||
return self; | ||
} | ||
// check if the contact already exists | ||
var index = this._indexOf(contact.id) | ||
if (index >= 0) { | ||
this._update(contact, index) | ||
return this | ||
} | ||
if (self.bucket.length < self.numberOfNodesPerKBucket) { | ||
self.bucket.push(contact); | ||
self.emit('added', contact); | ||
return self; | ||
} | ||
if (this.bucket.length < this.numberOfNodesPerKBucket) { | ||
this.bucket.push(contact) | ||
this.root.emit('added', contact) | ||
return this | ||
} | ||
// the bucket is full | ||
if (self.dontSplit) { | ||
// we are not allowed to split the bucket | ||
// we need to ping the first self.numberOfNodesToPing | ||
// in order to determine if they are alive | ||
// only if one of the pinged nodes does not respond, can the new contact | ||
// be added (this prevents DoS flodding with new invalid contacts) | ||
self.root.emit('ping', self.bucket.slice(0, self.numberOfNodesToPing), contact); | ||
return self; | ||
} | ||
// the bucket is full | ||
if (this.dontSplit) { | ||
// we are not allowed to split the bucket | ||
// we need to ping the first this.numberOfNodesToPing | ||
// in order to determine if they are alive | ||
// only if one of the pinged nodes does not respond, can the new contact | ||
// be added (this prevents DoS flodding with new invalid contacts) | ||
this.root.emit('ping', this.bucket.slice(0, this.numberOfNodesToPing), contact) | ||
return this | ||
} | ||
return self._splitAndAdd(contact, bitIndex); | ||
}; | ||
return this._splitAndAdd(contact, bitIndex) | ||
} | ||
// contact: *required* the contact object to add | ||
KBucket.prototype.add = function add (contact) { | ||
if (!Buffer.isBuffer(contact.id)) { | ||
throw new TypeError("contact.id is not a Buffer"); | ||
} | ||
return this._add(contact, 0); | ||
}; | ||
KBucket.prototype.add = function (contact) { | ||
if (!Buffer.isBuffer(contact.id)) throw new TypeError('contact.id is not a Buffer') | ||
return this._add(contact, 0) | ||
} | ||
@@ -164,31 +150,24 @@ // id: Buffer *required* node id | ||
KBucket.prototype._closest = function (id, n, bitIndex) { | ||
var self = this; | ||
var contacts; | ||
if (!self.bucket) { | ||
if (self._determineBucket(id, bitIndex++) < 0) { | ||
contacts = self.low._closest(id, n, bitIndex); | ||
if (contacts.length < n) { | ||
contacts = contacts.concat(self.high._closest(id, n, bitIndex)); | ||
} | ||
} else { | ||
contacts = self.high._closest(id, n, bitIndex); | ||
if (contacts.length < n) { | ||
contacts = contacts.concat(self.low._closest(id, n, bitIndex)); | ||
} | ||
} | ||
return contacts.slice(0, n); | ||
if (this.bucket === null) { | ||
var contacts | ||
if (this._determineBucket(id, bitIndex++) < 0) { | ||
contacts = this.low._closest(id, n, bitIndex) | ||
if (contacts.length < n) contacts = contacts.concat(this.high._closest(id, n, bitIndex)) | ||
} else { | ||
contacts = this.high._closest(id, n, bitIndex) | ||
if (contacts.length < n) contacts = contacts.concat(this.low._closest(id, n, bitIndex)) | ||
} | ||
contacts = self.bucket.slice(); | ||
contacts.forEach(function (storedContact) { | ||
storedContact.distance = KBucket.distance(storedContact.id, id); | ||
}); | ||
return contacts.slice(0, n) | ||
} | ||
contacts.sort(function (a, b) {return a.distance - b.distance;}); | ||
return this.bucket | ||
.map(function (storedContact) { | ||
storedContact.distance = KBucket.distance(storedContact.id, id) | ||
return storedContact | ||
}) | ||
.sort(function (a, b) { return a.distance - b.distance }) | ||
.slice(0, n) | ||
} | ||
return contacts.slice(0, n); | ||
}; | ||
// id: Buffer *required* node id | ||
@@ -198,7 +177,5 @@ // n: Integer *required* maximum number of closest contacts to return | ||
KBucket.prototype.closest = function (id, n) { | ||
if (!Buffer.isBuffer(id)) { | ||
throw new TypeError("id is not a Buffer"); | ||
} | ||
return this._closest(id, n, 0); | ||
}; | ||
if (!Buffer.isBuffer(id)) throw new TypeError('id is not a Buffer') | ||
return this._closest(id, n, 0) | ||
} | ||
@@ -209,11 +186,6 @@ // Counts the number of contacts recursively. | ||
KBucket.prototype.count = function count () { | ||
var self = this; | ||
if (this.bucket !== null) return this.bucket.length | ||
return this.high.count() + this.low.count() | ||
} | ||
if (self.bucket) { | ||
return self.bucket.length; | ||
} else { | ||
return self.high.count() + self.low.count(); | ||
} | ||
}; | ||
// Determines whether the id at the bitIndex is 0 or 1. If 0, returns -1, else 1 | ||
@@ -223,38 +195,32 @@ // id: a Buffer to compare localNodeId with | ||
KBucket.prototype._determineBucket = function (id, bitIndex) { | ||
var self = this; | ||
bitIndex = bitIndex || 0 | ||
bitIndex = bitIndex || 0; | ||
// **NOTE** remember that id is a Buffer and has granularity of | ||
// bytes (8 bits), whereas the bitIndex is the _bit_ index (not byte) | ||
// **NOTE** remember that id is a Buffer and has granularity of | ||
// bytes (8 bits), whereas the bitIndex is the _bit_ index (not byte) | ||
// id's that are too short are put in low bucket (1 byte = 8 bits) | ||
// parseInt(bitIndex / 8) finds how many bytes the bitIndex describes | ||
// bitIndex % 8 checks if we have extra bits beyond byte multiples | ||
// if number of bytes is <= no. of bytes described by bitIndex and there | ||
// are extra bits to consider, this means id has less bits than what | ||
// bitIndex describes, id therefore is too short, and will be put in low | ||
// bucket | ||
var bytesDescribedByBitIndex = parseInt(bitIndex / 8, 10) | ||
var bitIndexWithinByte = bitIndex % 8 | ||
if ((id.length <= bytesDescribedByBitIndex) && (bitIndexWithinByte !== 0)) return -1 | ||
// id's that are too short are put in low bucket (1 byte = 8 bits) | ||
// parseInt(bitIndex / 8) finds how many bytes the bitIndex describes | ||
// bitIndex % 8 checks if we have extra bits beyond byte multiples | ||
// if number of bytes is <= no. of bytes described by bitIndex and there | ||
// are extra bits to consider, this means id has less bits than what | ||
// bitIndex describes, id therefore is too short, and will be put in low | ||
// bucket | ||
var bytesDescribedByBitIndex = parseInt(bitIndex / 8, 10); | ||
var bitIndexWithinByte = bitIndex % 8; | ||
if ((id.length <= bytesDescribedByBitIndex) | ||
&& (bitIndexWithinByte != 0)) | ||
return -1; | ||
var byteUnderConsideration = id[bytesDescribedByBitIndex] | ||
var byteUnderConsideration = id[bytesDescribedByBitIndex]; | ||
// byteUnderConsideration is an integer from 0 to 255 represented by 8 bits | ||
// where 255 is 11111111 and 0 is 00000000 | ||
// in order to find out whether the bit at bitIndexWithinByte is set | ||
// we construct Math.pow(2, (7 - bitIndexWithinByte)) which will consist | ||
// of all bits being 0, with only one bit set to 1 | ||
// for example, if bitIndexWithinByte is 3, we will construct 00010000 by | ||
// Math.pow(2, (7 - 3)) -> Math.pow(2, 4) -> 16 | ||
if (byteUnderConsideration & Math.pow(2, (7 - bitIndexWithinByte))) return 1 | ||
// byteUnderConsideration is an integer from 0 to 255 represented by 8 bits | ||
// where 255 is 11111111 and 0 is 00000000 | ||
// in order to find out whether the bit at bitIndexWithinByte is set | ||
// we construct Math.pow(2, (7 - bitIndexWithinByte)) which will consist | ||
// of all bits being 0, with only one bit set to 1 | ||
// for example, if bitIndexWithinByte is 3, we will construct 00010000 by | ||
// Math.pow(2, (7 - 3)) -> Math.pow(2, 4) -> 16 | ||
if (byteUnderConsideration & Math.pow(2, (7 - bitIndexWithinByte))) { | ||
return 1; | ||
} | ||
return -1 | ||
} | ||
return -1; | ||
}; | ||
// Get a contact by its exact ID. | ||
@@ -268,19 +234,15 @@ // If this is a leaf, loop through the bucket contents and return the correct | ||
KBucket.prototype._get = function (id, bitIndex) { | ||
var self = this; | ||
if (!self.bucket) { | ||
if (self._determineBucket(id, bitIndex++) < 0) { | ||
return self.low._get(id, bitIndex); | ||
} else { | ||
return self.high._get(id, bitIndex); | ||
} | ||
if (this.bucket === null) { | ||
if (this._determineBucket(id, bitIndex++) < 0) { | ||
return this.low._get(id, bitIndex) | ||
} else { | ||
return this.high._get(id, bitIndex) | ||
} | ||
} | ||
var index = self._indexOf(id); // index of uses contact id for matching | ||
if (index < 0) { | ||
return null; // contact not found | ||
} | ||
var index = this._indexOf(id) // index of uses contact id for matching | ||
if (index < 0) return null // contact not found | ||
return self.bucket[index]; | ||
}; | ||
return this.bucket[index] | ||
} | ||
@@ -293,17 +255,16 @@ // Get a contact by its exact ID. | ||
KBucket.prototype.get = function get (id) { | ||
if (!Buffer.isBuffer(id)) { | ||
throw new TypeError("id is not a Buffer"); | ||
} | ||
return this._get(id, 0); | ||
}; | ||
if (!Buffer.isBuffer(id)) throw new TypeError('id is not a Buffer') | ||
return this._get(id, 0) | ||
} | ||
// id: Buffer Contact node id. | ||
// Returns the index of the contact with the given id if it exists | ||
KBucket.prototype._indexOf = function indexOf (id) { | ||
var self = this; | ||
for (var i = 0; i < self.bucket.length; i++) { | ||
if (bufferEquals(self.bucket[i].id, id)) return i; | ||
} | ||
return -1; | ||
}; | ||
for (var i = 0; i < this.bucket.length; i++) { | ||
if (bufferEquals(this.bucket[i].id, id)) return i | ||
} | ||
return -1 | ||
} | ||
// id: Buffer *required* The ID of the contact to remove. | ||
@@ -313,35 +274,32 @@ // bitIndex: the bitIndex to which bit to check in the Buffer for navigating | ||
KBucket.prototype._remove = function (id, bitIndex) { | ||
var self = this; | ||
// first check whether we are an inner node or a leaf (with bucket contents) | ||
if (!self.bucket) { | ||
// this is not a leaf node but an inner node with 'low' and 'high' | ||
// branches; we will check the appropriate bit of the identifier and | ||
// delegate to the appropriate node for further processing | ||
if (self._determineBucket(id, bitIndex++) < 0) { | ||
return self.low._remove(id, bitIndex); | ||
} else { | ||
return self.high._remove(id, bitIndex); | ||
} | ||
// first check whether we are an inner node or a leaf (with bucket contents) | ||
if (this.bucket === null) { | ||
// this is not a leaf node but an inner node with 'low' and 'high' | ||
// branches; we will check the appropriate bit of the identifier and | ||
// delegate to the appropriate node for further processing | ||
if (this._determineBucket(id, bitIndex++) < 0) { | ||
return this.low._remove(id, bitIndex) | ||
} else { | ||
return this.high._remove(id, bitIndex) | ||
} | ||
} | ||
var index = self._indexOf(id); | ||
if (index >= 0) { | ||
var contact = self.bucket.splice(index, 1)[0]; | ||
self.emit('removed', contact); | ||
} | ||
return self; | ||
}; | ||
var index = this._indexOf(id) | ||
if (index >= 0) { | ||
var contact = this.bucket.splice(index, 1)[0] | ||
this.root.emit('removed', contact) | ||
} | ||
return this | ||
} | ||
// id: Buffer *required* he ID of the contact to remove. | ||
KBucket.prototype.remove = function remove (id) { | ||
if (!Buffer.isBuffer(id)) { | ||
throw new TypeError("id is not a Buffer"); | ||
} | ||
return this._remove(id, 0); | ||
}; | ||
if (!Buffer.isBuffer(id)) throw new TypeError('id is not a Buffer') | ||
return this._remove(id, 0) | ||
} | ||
// Splits the bucket, redistributes contacts to the new buckets, and marks the | ||
// bucket that was split as an inner node of the binary tree of buckets by | ||
// setting self.bucket = undefined; | ||
// setting this.bucket = null | ||
// contact: *required* the contact object to add | ||
@@ -351,62 +309,55 @@ // bitIndex: the bitIndex to which byte to check in the Buffer for navigating the | ||
KBucket.prototype._splitAndAdd = function (contact, bitIndex) { | ||
var self = this; | ||
self.low = new KBucket({ | ||
arbiter: self.arbiter, | ||
localNodeId: self.localNodeId, | ||
numberOfNodesPerKBucket: self.numberOfNodesPerKBucket, | ||
numberOfNodesToPing: self.numberOfNodesToPing, | ||
root: self.root | ||
}); | ||
self.high = new KBucket({ | ||
arbiter: self.arbiter, | ||
localNodeId: self.localNodeId, | ||
numberOfNodesPerKBucket: self.numberOfNodesPerKBucket, | ||
numberOfNodesToPing: self.numberOfNodesToPing, | ||
root: self.root | ||
}); | ||
this.low = new KBucket({ | ||
arbiter: this.arbiter, | ||
localNodeId: this.localNodeId, | ||
numberOfNodesPerKBucket: this.numberOfNodesPerKBucket, | ||
numberOfNodesToPing: this.numberOfNodesToPing, | ||
root: this.root | ||
}) | ||
this.high = new KBucket({ | ||
arbiter: this.arbiter, | ||
localNodeId: this.localNodeId, | ||
numberOfNodesPerKBucket: this.numberOfNodesPerKBucket, | ||
numberOfNodesToPing: this.numberOfNodesToPing, | ||
root: this.root | ||
}) | ||
bitIndex = bitIndex || 0; | ||
bitIndex = bitIndex || 0 | ||
// redistribute existing contacts amongst the two newly created buckets | ||
self.bucket.forEach(function (storedContact) { | ||
if (self._determineBucket(storedContact.id, bitIndex) < 0) { | ||
self.low.add(storedContact); | ||
} else { | ||
self.high.add(storedContact); | ||
} | ||
}); | ||
self.bucket = undefined; // mark as inner tree node | ||
// don't split the "far away" bucket | ||
// we check where the local node would end up and mark the other one as | ||
// "dontSplit" (i.e. "far away") | ||
if (self._determineBucket(self.localNodeId, bitIndex) < 0) { | ||
// local node belongs to "low" bucket, so mark the other one | ||
self.high.dontSplit = true; | ||
// redistribute existing contacts amongst the two newly created buckets | ||
this.bucket.forEach(function (storedContact) { | ||
if (this._determineBucket(storedContact.id, bitIndex) < 0) { | ||
this.low.add(storedContact) | ||
} else { | ||
self.low.dontSplit = true; | ||
this.high.add(storedContact) | ||
} | ||
}.bind(this)) | ||
// add the contact being added | ||
self._add(contact, bitIndex); | ||
this.bucket = null // mark as inner tree node | ||
return self; | ||
}; | ||
// don't split the "far away" bucket | ||
// we check where the local node would end up and mark the other one as | ||
// "dontSplit" (i.e. "far away") | ||
if (this._determineBucket(this.localNodeId, bitIndex) < 0) { | ||
// local node belongs to "low" bucket, so mark the other one | ||
this.high.dontSplit = true | ||
} else { | ||
this.low.dontSplit = true | ||
} | ||
// add the contact being added | ||
this._add(contact, bitIndex) | ||
return this | ||
} | ||
// Returns all the contacts contained in the tree as an array. | ||
// If self is a leaf, return a copy of the bucket. `slice` is used so that we | ||
// If this is a leaf, return a copy of the bucket. `slice` is used so that we | ||
// don't accidentally leak an internal reference out that might be accidentally | ||
// misused. If self is not a leaf, return the union of the low and high | ||
// misused. If this is not a leaf, return the union of the low and high | ||
// branches (themselves also as arrays). | ||
KBucket.prototype.toArray = function toArray () { | ||
var self = this; | ||
KBucket.prototype.toArray = function () { | ||
if (this.bucket !== null) return this.bucket.slice(0) | ||
return this.low.toArray().concat(this.high.toArray()) | ||
} | ||
if (self.bucket) { | ||
return self.bucket.slice(0); | ||
} else { | ||
return self.low.toArray().concat(self.high.toArray()); | ||
} | ||
}; | ||
// Updates the contact selected by the arbiter. | ||
@@ -424,19 +375,16 @@ // If the selection is our old contact and the candidate is some new contact | ||
KBucket.prototype._update = function (contact, index) { | ||
var self = this; | ||
// sanity check | ||
if (!bufferEquals(self.bucket[index].id, contact.id)) { | ||
throw new Error("indexOf() calculation resulted in wrong index") | ||
} | ||
// sanity check | ||
if (!bufferEquals(this.bucket[index].id, contact.id)) { | ||
throw new Error('indexOf() calculation resulted in wrong index') | ||
} | ||
var incumbent = self.bucket[index]; | ||
var selection = self.arbiter(incumbent, contact); | ||
if (selection === incumbent && incumbent !== contact) { | ||
// if the selection is our old contact and the candidate is some new | ||
// contact, then there is nothing to do | ||
return; | ||
} | ||
var incumbent = this.bucket[index] | ||
var selection = this.arbiter(incumbent, contact) | ||
// if the selection is our old contact and the candidate is some new | ||
// contact, then there is nothing to do | ||
if (selection === incumbent && incumbent !== contact) return | ||
self.bucket.splice(index, 1); // remove old contact | ||
self.bucket.push(selection); // add more recent contact version | ||
self.emit('updated', incumbent, selection); | ||
}; | ||
this.bucket.splice(index, 1) // remove old contact | ||
this.bucket.push(selection) // add more recent contact version | ||
this.root.emit('updated', incumbent, selection) | ||
} |
{ | ||
"name": "k-bucket", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"description": "Kademlia DHT K-bucket implementation as a binary tree", | ||
"scripts": { | ||
"distance-benchmark": "node benchmarks/distance.js", | ||
"test": "node scripts/test.js" | ||
}, | ||
"main": "index.js", | ||
"devDependencies": { | ||
"nodeunit": "0.9.x" | ||
}, | ||
"dependencies": { | ||
"buffer-equals": "^1.0.3", | ||
"inherits": "^2.0.1", | ||
"randombytes": "^2.0.3" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git@github.com:tristanls/k-bucket.git" | ||
}, | ||
"keywords": [ | ||
@@ -27,2 +10,6 @@ "k-bucket", | ||
], | ||
"bugs": { | ||
"url": "https://github.com/tristanls/k-bucket/issues" | ||
}, | ||
"license": "MIT", | ||
"contributors": [ | ||
@@ -37,3 +24,22 @@ "Tristan Slominski <tristan.slominski@gmail.com>", | ||
], | ||
"license": "MIT" | ||
"main": "./index.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "git@github.com:tristanls/k-bucket.git" | ||
}, | ||
"scripts": { | ||
"benchmark:distance": "node benchmarks/distance.js", | ||
"lint": "standard", | ||
"test": "npm run lint && npm run unit", | ||
"unit": "node scripts/test.js" | ||
}, | ||
"dependencies": { | ||
"buffer-equals": "^1.0.3", | ||
"inherits": "^2.0.1", | ||
"randombytes": "^2.0.3" | ||
}, | ||
"devDependencies": { | ||
"nodeunit": "0.9.x", | ||
"standard": "^7.0.1" | ||
} | ||
} |
@@ -191,15 +191,11 @@ # k-bucket | ||
#### kBucket._indexOf(contact) | ||
#### kBucket._indexOf(id) | ||
_**CAUTION: reserved for internal use**_ | ||
* `contact`: _Object_ The contact object. | ||
* `id`: _Buffer_ Contact node id. | ||
* Any satellite data that is part of the `contact` object will not be altered, only `id` is used. | ||
* Return: _Integer_ Index of `contact` if it exists, -1 otherwise. | ||
* `id`: _Buffer_ Contact node id. | ||
* Return: _Integer_ Index of `contact` with provided `id` if it exists, -1 otherwise. | ||
Returns the index of the `contact` if it exists, returns -1 otherwise. | ||
Returns the index of the `contact` with provided `id` if it exists, returns -1 otherwise. | ||
_NOTE: `kBucket._indexOf(contact)` does not use `arbiter` in the comparison. | ||
#### kBucket._splitAndAdd(contact, [bitIndex]) | ||
@@ -206,0 +202,0 @@ |
#!/usr/bin/env node | ||
var reporter = require('nodeunit').reporters.default; | ||
reporter.run(['test']); | ||
var reporter = require('nodeunit').reporters.default | ||
reporter.run(['test']) |
163
test/add.js
@@ -1,90 +0,95 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['throws TypeError if contact.id is not a Buffer'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: "foo"}; | ||
test.throws(function () { | ||
kBucket.add(contact); | ||
}, TypeError); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: 'foo' } | ||
test.throws(function () { | ||
kBucket.add(contact) | ||
}, TypeError) | ||
test.done() | ||
} | ||
test['adding a contact places it in bucket'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.add(contact); | ||
test.ok(kBucket.bucket[0] === contact); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.add(contact) | ||
test.ok(kBucket.bucket[0] === contact) | ||
test.done() | ||
} | ||
test['adding an existing contact does not increase number of contacts in ' + | ||
'bucket' ] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.add(contact); | ||
kBucket.add({id: new Buffer("a")}); | ||
test.equal(kBucket.bucket.length, 1); | ||
test.done(); | ||
}; | ||
test['adding an existing contact does not increase number of contacts in bucket'] = function (test) { | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.add(contact) | ||
kBucket.add({id: new Buffer('a')}) | ||
test.equal(kBucket.bucket.length, 1) | ||
test.done() | ||
} | ||
test['adding same contact moves it to the end of the bucket ' + | ||
'(most-recently-contacted end)'] = function (test) { | ||
test.expect(5); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.add(contact); | ||
test.equal(kBucket.bucket.length, 1); | ||
kBucket.add({id: new Buffer("b")}); | ||
test.equal(kBucket.bucket.length, 2); | ||
test.equal(kBucket.bucket[0], contact); // least-recently-contacted end | ||
kBucket.add(contact); | ||
test.equal(kBucket.bucket.length, 2); | ||
test.equal(kBucket.bucket[1], contact); // most-recently-contacted end | ||
test.done(); | ||
}; | ||
test['adding same contact moves it to the end of the bucket (most-recently-contacted end)'] = function (test) { | ||
test.expect(5) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.add(contact) | ||
test.equal(kBucket.bucket.length, 1) | ||
kBucket.add({ id: new Buffer('b') }) | ||
test.equal(kBucket.bucket.length, 2) | ||
test.equal(kBucket.bucket[0], contact) // least-recently-contacted end | ||
kBucket.add(contact) | ||
test.equal(kBucket.bucket.length, 2) | ||
test.equal(kBucket.bucket[1], contact) // most-recently-contacted end | ||
test.done() | ||
} | ||
test['adding contact to bucket that can\'t be split results in calling' + | ||
' "ping" callback'] = function (test) { | ||
var i, iString, j; | ||
test.expect(3 /*numberOfNodesToPing*/ + 2); | ||
var kBucket = new KBucket({ | ||
localNodeId: new Buffer('0000', 'hex') | ||
}); | ||
kBucket.on('ping', function (contacts, replacement) { | ||
test.equal(contacts.length, kBucket.numberOfNodesToPing); | ||
// console.dir(kBucket.high.bucket[0]); | ||
for (var i = 0; i < kBucket.numberOfNodesToPing; i++) { | ||
// the least recently contacted end of the bucket should be pinged | ||
test.equal(contacts[i], kBucket.high.bucket[i]); | ||
} | ||
test.deepEqual(replacement, {id: new Buffer(iString, 'hex')}) | ||
test.done(); | ||
}) | ||
for (var j = 0; j < kBucket.numberOfNodesPerKBucket + 1; j++) { | ||
iString = j.toString('16'); | ||
if (iString.length < 2) { | ||
iString = '0' + iString; | ||
} | ||
iString = '80' + iString; // make sure all go into "far away" bucket | ||
kBucket.add({id: new Buffer(iString, 'hex')}); | ||
test['adding contact to bucket that can\'t be split results in calling "ping" callback'] = function (test) { | ||
test.expect(3 /* numberOfNodesToPing*/ + 2) | ||
var kBucket = new KBucket({ localNodeId: new Buffer([ 0x00, 0x00 ]) }) | ||
kBucket.on('ping', function (contacts, replacement) { | ||
test.equal(contacts.length, kBucket.numberOfNodesToPing) | ||
// console.dir(kBucket.high.bucket[0]) | ||
for (var i = 0; i < kBucket.numberOfNodesToPing; ++i) { | ||
// the least recently contacted end of the bucket should be pinged | ||
test.equal(contacts[i], kBucket.high.bucket[i]) | ||
} | ||
}; | ||
test.deepEqual(replacement, { id: new Buffer([ 0x80, j ]) }) | ||
test.done() | ||
}) | ||
for (var j = 0; j < kBucket.numberOfNodesPerKBucket + 1; ++j) { | ||
kBucket.add({ id: new Buffer([ 0x80, j ]) }) // make sure all go into "far away" bucket | ||
} | ||
} | ||
test['should generate event "added" once'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.on('added', function (newContact) { | ||
test.deepEqual(newContact, contact); | ||
}); | ||
kBucket.add(contact); | ||
kBucket.add(contact); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.on('added', function (newContact) { | ||
test.deepEqual(newContact, contact) | ||
}) | ||
kBucket.add(contact) | ||
kBucket.add(contact) | ||
test.done() | ||
} | ||
test['should generate event "added" when adding to a split bucket'] = function (test) { | ||
test.expect(2) | ||
var kBucket = new KBucket({ | ||
localNodeId: new Buffer('') // need non-random localNodeId for deterministic splits | ||
}) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; ++i) { | ||
kBucket.add({ id: new Buffer('' + i) }) | ||
} | ||
test.ok(!kBucket.bucket) | ||
var contact = { id: new Buffer('a') } | ||
kBucket.on('added', function (newContact) { | ||
test.deepEqual(newContact, contact) | ||
}) | ||
kBucket.add(contact) | ||
test.done() | ||
} |
@@ -1,116 +0,74 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['throws TypeError if contact.id is not a Buffer'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: "foo"}; | ||
test.throws(function () { | ||
kBucket.closest(contact.id, 4); | ||
}, TypeError); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: 'foo' } | ||
test.throws(function () { | ||
kBucket.closest(contact.id, 4) | ||
}, TypeError) | ||
test.done() | ||
} | ||
test['closest nodes are returned'] = function (test) { | ||
test.expect(3); | ||
var kBucket = new KBucket(); | ||
kBucket.add({id: new Buffer('00', 'hex')}); // 00000000 | ||
kBucket.add({id: new Buffer('01', 'hex')}); // 00000001 | ||
kBucket.add({id: new Buffer('02', 'hex')}); // 00000010 | ||
kBucket.add({id: new Buffer('03', 'hex')}); // 00000011 | ||
kBucket.add({id: new Buffer('04', 'hex')}); // 00000100 | ||
kBucket.add({id: new Buffer('05', 'hex')}); // 00000101 | ||
kBucket.add({id: new Buffer('06', 'hex')}); // 00000110 | ||
kBucket.add({id: new Buffer('07', 'hex')}); // 00000111 | ||
kBucket.add({id: new Buffer('08', 'hex')}); // 00001000 | ||
kBucket.add({id: new Buffer('09', 'hex')}); // 00001001 | ||
kBucket.add({id: new Buffer('0a', 'hex')}); // 00001010 | ||
kBucket.add({id: new Buffer('0b', 'hex')}); // 00001011 | ||
kBucket.add({id: new Buffer('0c', 'hex')}); // 00001100 | ||
kBucket.add({id: new Buffer('0d', 'hex')}); // 00001101 | ||
kBucket.add({id: new Buffer('0e', 'hex')}); // 00001110 | ||
kBucket.add({id: new Buffer('0f', 'hex')}); // 00001111 | ||
kBucket.add({id: new Buffer('10', 'hex')}); // 00010000 | ||
kBucket.add({id: new Buffer('11', 'hex')}); // 00010001 | ||
var contact = {id: new Buffer('15', 'hex')};// 00010101 | ||
var contacts = kBucket.closest(contact.id, 3); | ||
test.deepEqual(contacts[0].id, new Buffer('11', 'hex')); // distance: 00000100 | ||
test.deepEqual(contacts[1].id, new Buffer('10', 'hex')); // distance: 00000101 | ||
test.deepEqual(contacts[2].id, new Buffer('05', 'hex')); // distance: 00010000 | ||
test.done(); | ||
}; | ||
test.expect(3) | ||
var kBucket = new KBucket() | ||
for (var i = 0; i < 0x12; ++i) kBucket.add({ id: new Buffer([ i ]) }) | ||
var contact = { id: new Buffer([ 0x15 ]) } // 00010101 | ||
var contacts = kBucket.closest(contact.id, 3) | ||
test.deepEqual(contacts[0].id, new Buffer([ 0x11 ])) // distance: 00000100 | ||
test.deepEqual(contacts[1].id, new Buffer([ 0x10 ])) // distance: 00000101 | ||
test.deepEqual(contacts[2].id, new Buffer([ 0x05 ])) // distance: 00010000 | ||
test.done() | ||
} | ||
test['closest nodes are returned (including exact match)'] = function (test) { | ||
test.expect(3); | ||
var kBucket = new KBucket(); | ||
kBucket.add({id: new Buffer('00', 'hex')}); // 00000000 | ||
kBucket.add({id: new Buffer('01', 'hex')}); // 00000001 | ||
kBucket.add({id: new Buffer('02', 'hex')}); // 00000010 | ||
kBucket.add({id: new Buffer('03', 'hex')}); // 00000011 | ||
kBucket.add({id: new Buffer('04', 'hex')}); // 00000100 | ||
kBucket.add({id: new Buffer('05', 'hex')}); // 00000101 | ||
kBucket.add({id: new Buffer('06', 'hex')}); // 00000110 | ||
kBucket.add({id: new Buffer('07', 'hex')}); // 00000111 | ||
kBucket.add({id: new Buffer('08', 'hex')}); // 00001000 | ||
kBucket.add({id: new Buffer('09', 'hex')}); // 00001001 | ||
kBucket.add({id: new Buffer('0a', 'hex')}); // 00001010 | ||
kBucket.add({id: new Buffer('0b', 'hex')}); // 00001011 | ||
kBucket.add({id: new Buffer('0c', 'hex')}); // 00001100 | ||
kBucket.add({id: new Buffer('0d', 'hex')}); // 00001101 | ||
kBucket.add({id: new Buffer('0e', 'hex')}); // 00001110 | ||
kBucket.add({id: new Buffer('0f', 'hex')}); // 00001111 | ||
kBucket.add({id: new Buffer('10', 'hex')}); // 00010000 | ||
kBucket.add({id: new Buffer('11', 'hex')}); // 00010001 | ||
var contact = {id: new Buffer('11', 'hex')};// 00010001 | ||
var contacts = kBucket.closest(contact.id, 3); | ||
test.deepEqual(contacts[0].id, new Buffer('11', 'hex')); // distance: 00000000 | ||
test.deepEqual(contacts[1].id, new Buffer('10', 'hex')); // distance: 00000001 | ||
test.deepEqual(contacts[2].id, new Buffer('01', 'hex')); // distance: 00010000 | ||
test.done(); | ||
}; | ||
test.expect(3) | ||
var kBucket = new KBucket() | ||
for (var i = 0; i < 0x12; ++i) kBucket.add({ id: new Buffer([ i ]) }) | ||
var contact = {id: new Buffer([ 0x11 ])} // 00010001 | ||
var contacts = kBucket.closest(contact.id, 3) | ||
test.deepEqual(contacts[0].id, new Buffer([ 0x11 ])) // distance: 00000000 | ||
test.deepEqual(contacts[1].id, new Buffer([ 0x10 ])) // distance: 00000001 | ||
test.deepEqual(contacts[2].id, new Buffer([ 0x01 ])) // distance: 00010000 | ||
test.done() | ||
} | ||
test['closest nodes are returned even if there isn\'t enough in one bucket'] = function (test) { | ||
test.expect(22); | ||
var i, iString; | ||
var kBucket = new KBucket({localNodeId: new Buffer('0000', 'hex')}); | ||
for (i = 0; i < kBucket.numberOfNodesPerKBucket; i++) { | ||
iString = i.toString('16'); | ||
if (iString.length < 2) { | ||
iString = '0' + iString; | ||
} | ||
var farString = '80' + iString; // make sure all go into "far away" bucket | ||
kBucket.add({id: new Buffer(farString, 'hex')}); | ||
var nearString = '01' + iString; | ||
kBucket.add({id: new Buffer(nearString, 'hex')}); | ||
} | ||
kBucket.add({id: new Buffer('0001', 'hex')}); | ||
var contact = {id: new Buffer('0003', 'hex')}; // 0000000000000011 | ||
var contacts = kBucket.closest(contact.id, 22); | ||
test.deepEqual(contacts[0].id, new Buffer('0001', 'hex')); // distance: 0000000000000010 | ||
test.deepEqual(contacts[1].id, new Buffer('0103', 'hex')); // distance: 0000000100000000 | ||
test.deepEqual(contacts[2].id, new Buffer('0102', 'hex')); // distance: 0000000100000010 | ||
test.deepEqual(contacts[3].id, new Buffer('0101', 'hex')); | ||
test.deepEqual(contacts[4].id, new Buffer('0100', 'hex')); | ||
test.deepEqual(contacts[5].id, new Buffer('0107', 'hex')); | ||
test.deepEqual(contacts[6].id, new Buffer('0106', 'hex')); | ||
test.deepEqual(contacts[7].id, new Buffer('0105', 'hex')); | ||
test.deepEqual(contacts[8].id, new Buffer('0104', 'hex')); | ||
test.deepEqual(contacts[9].id, new Buffer('010b', 'hex')); | ||
test.deepEqual(contacts[10].id, new Buffer('010a', 'hex')); | ||
test.deepEqual(contacts[11].id, new Buffer('0109', 'hex')); | ||
test.deepEqual(contacts[12].id, new Buffer('0108', 'hex')); | ||
test.deepEqual(contacts[13].id, new Buffer('010f', 'hex')); | ||
test.deepEqual(contacts[14].id, new Buffer('010e', 'hex')); | ||
test.deepEqual(contacts[15].id, new Buffer('010d', 'hex')); | ||
test.deepEqual(contacts[16].id, new Buffer('010c', 'hex')); | ||
test.deepEqual(contacts[17].id, new Buffer('0113', 'hex')); | ||
test.deepEqual(contacts[18].id, new Buffer('0112', 'hex')); | ||
test.deepEqual(contacts[19].id, new Buffer('0111', 'hex')); | ||
test.deepEqual(contacts[20].id, new Buffer('0110', 'hex')); | ||
test.deepEqual(contacts[21].id, new Buffer('8003', 'hex')); // distance: 1000000000000000 | ||
// console.log(require('util').inspect(kBucket, false, null)); | ||
test.done(); | ||
}; | ||
test["closest nodes are returned even if there isn't enough in one bucket"] = function (test) { | ||
test.expect(22) | ||
var kBucket = new KBucket({ localNodeId: new Buffer([ 0x00, 0x00 ]) }) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; i++) { | ||
kBucket.add({ id: new Buffer([ 0x80, i ]) }) | ||
kBucket.add({ id: new Buffer([ 0x01, i ]) }) | ||
} | ||
kBucket.add({ id: new Buffer([ 0x00, 0x01 ]) }) | ||
var contact = { id: new Buffer([ 0x00, 0x03 ]) } // 0000000000000011 | ||
var contacts = kBucket.closest(contact.id, 22) | ||
test.deepEqual(contacts[0].id, new Buffer([ 0x00, 0x01 ])) // distance: 0000000000000010 | ||
test.deepEqual(contacts[1].id, new Buffer([ 0x01, 0x03 ])) // distance: 0000000100000000 | ||
test.deepEqual(contacts[2].id, new Buffer([ 0x01, 0x02 ])) // distance: 0000000100000010 | ||
test.deepEqual(contacts[3].id, new Buffer([ 0x01, 0x01 ])) | ||
test.deepEqual(contacts[4].id, new Buffer([ 0x01, 0x00 ])) | ||
test.deepEqual(contacts[5].id, new Buffer([ 0x01, 0x07 ])) | ||
test.deepEqual(contacts[6].id, new Buffer([ 0x01, 0x06 ])) | ||
test.deepEqual(contacts[7].id, new Buffer([ 0x01, 0x05 ])) | ||
test.deepEqual(contacts[8].id, new Buffer([ 0x01, 0x04 ])) | ||
test.deepEqual(contacts[9].id, new Buffer([ 0x01, 0x0b ])) | ||
test.deepEqual(contacts[10].id, new Buffer([ 0x01, 0x0a ])) | ||
test.deepEqual(contacts[11].id, new Buffer([ 0x01, 0x09 ])) | ||
test.deepEqual(contacts[12].id, new Buffer([ 0x01, 0x08 ])) | ||
test.deepEqual(contacts[13].id, new Buffer([ 0x01, 0x0f ])) | ||
test.deepEqual(contacts[14].id, new Buffer([ 0x01, 0x0e ])) | ||
test.deepEqual(contacts[15].id, new Buffer([ 0x01, 0x0d ])) | ||
test.deepEqual(contacts[16].id, new Buffer([ 0x01, 0x0c ])) | ||
test.deepEqual(contacts[17].id, new Buffer([ 0x01, 0x13 ])) | ||
test.deepEqual(contacts[18].id, new Buffer([ 0x01, 0x12 ])) | ||
test.deepEqual(contacts[19].id, new Buffer([ 0x01, 0x11 ])) | ||
test.deepEqual(contacts[20].id, new Buffer([ 0x01, 0x10 ])) | ||
test.deepEqual(contacts[21].id, new Buffer([ 0x80, 0x03 ])) // distance: 1000000000000000 | ||
// console.log(require('util').inspect(kBucket, false, null)) | ||
test.done() | ||
} |
@@ -1,48 +0,47 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['count returns 0 when no contacts in bucket'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket.count(), 0); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket.count(), 0) | ||
test.done() | ||
} | ||
test['count returns 1 when 1 contact in bucket'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.add(contact); | ||
test.equal(kBucket.count(), 1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.add(contact) | ||
test.equal(kBucket.count(), 1) | ||
test.done() | ||
} | ||
test['count returns 1 when same contact added to bucket twice'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.add(contact); | ||
kBucket.add(contact); | ||
test.equal(kBucket.count(), 1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.add(contact) | ||
kBucket.add(contact) | ||
test.equal(kBucket.count(), 1) | ||
test.done() | ||
} | ||
test['count returns number of added unique contacts'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
kBucket.add({id: new Buffer("a")}); | ||
kBucket.add({id: new Buffer("a")}); | ||
kBucket.add({id: new Buffer("b")}); | ||
kBucket.add({id: new Buffer("b")}); | ||
kBucket.add({id: new Buffer("c")}); | ||
kBucket.add({id: new Buffer("d")}); | ||
kBucket.add({id: new Buffer("c")}); | ||
kBucket.add({id: new Buffer("d")}); | ||
kBucket.add({id: new Buffer("e")}); | ||
kBucket.add({id: new Buffer("f")}); | ||
test.equal(kBucket.count(), 6); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
kBucket.add({ id: new Buffer('a') }) | ||
kBucket.add({ id: new Buffer('a') }) | ||
kBucket.add({ id: new Buffer('b') }) | ||
kBucket.add({ id: new Buffer('b') }) | ||
kBucket.add({ id: new Buffer('c') }) | ||
kBucket.add({ id: new Buffer('d') }) | ||
kBucket.add({ id: new Buffer('c') }) | ||
kBucket.add({ id: new Buffer('d') }) | ||
kBucket.add({ id: new Buffer('e') }) | ||
kBucket.add({ id: new Buffer('f') }) | ||
test.equal(kBucket.count(), 6) | ||
test.done() | ||
} |
@@ -1,48 +0,45 @@ | ||
"use strict"; | ||
'use strict' | ||
var bufferEqual = require('buffer-equal') | ||
var EventEmitter = require('events').EventEmitter | ||
var KBucket = require('../index') | ||
var bufferEqual = require('buffer-equal'); | ||
var EventEmitter = require('events').EventEmitter; | ||
var test = module.exports = {} | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {}; | ||
test['localNodeId should be a random SHA-1 if not provided'] = function (test) { | ||
test.expect(2); | ||
var kBucket = new KBucket(); | ||
test.ok(kBucket.localNodeId instanceof Buffer); | ||
test.equal(kBucket.localNodeId.length, 20); // SHA-1 is 160 bits (20 bytes) | ||
test.done(); | ||
}; | ||
test.expect(2) | ||
var kBucket = new KBucket() | ||
test.ok(kBucket.localNodeId instanceof Buffer) | ||
test.equal(kBucket.localNodeId.length, 20) // SHA-1 is 160 bits (20 bytes) | ||
test.done() | ||
} | ||
test['localNodeId is a Buffer populated from options if options.localNodeId Buffer is provided'] = function (test) { | ||
var localNodeId = new Buffer("some length"); | ||
test.expect(2); | ||
var kBucket = new KBucket({localNodeId: localNodeId}); | ||
test.ok(kBucket.localNodeId instanceof Buffer); | ||
test.ok(bufferEqual(kBucket.localNodeId, localNodeId)); | ||
test.done(); | ||
}; | ||
var localNodeId = new Buffer('some length') | ||
test.expect(2) | ||
var kBucket = new KBucket({localNodeId: localNodeId}) | ||
test.ok(kBucket.localNodeId instanceof Buffer) | ||
test.ok(bufferEqual(kBucket.localNodeId, localNodeId)) | ||
test.done() | ||
} | ||
test['throws exception if options.localNodeId is a String'] = function (test) { | ||
var localNodeId = "some identifier"; | ||
test.expect(1); | ||
test.throws(function() { | ||
new KBucket({localNodeId: "some identifier"}); | ||
}, Error); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
test.throws(function () { | ||
return new KBucket({ localNodeId: 'some identifier' }) | ||
}, Error) | ||
test.done() | ||
} | ||
test['root is \'self\' if not provided'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.strictEqual(kBucket.root, kBucket); | ||
test.done(); | ||
}; | ||
test["root is 'self' if not provided"] = function (test) { | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.strictEqual(kBucket.root, kBucket) | ||
test.done() | ||
} | ||
test['inherits from EventEmitter'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.ok(kBucket instanceof EventEmitter); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.ok(kBucket instanceof EventEmitter) | ||
test.done() | ||
} |
@@ -1,61 +0,60 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['id 00000000, bitIndex 0, should be low'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("00", "hex"), 0), -1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x00 ]), 0), -1) | ||
test.done() | ||
} | ||
test['id 01000000, bitIndex 0, should be low'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("40", "hex"), 0), -1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x40 ]), 0), -1) | ||
test.done() | ||
} | ||
test['id 01000000, bitIndex 1, should be high'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("40", "hex"), 1), 1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x40 ]), 1), 1) | ||
test.done() | ||
} | ||
test['id 01000000, bitIndex 2, should be low'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("40", "hex"), 2), -1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x40 ]), 2), -1) | ||
test.done() | ||
} | ||
test['id 01000000, bitIndex 9, should be low'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("40", "hex"), 9), -1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x40 ]), 9), -1) | ||
test.done() | ||
} | ||
test['id 01000001, bitIndex 7, should be high'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("41", "hex"), 7), 1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x41 ]), 7), 1) | ||
test.done() | ||
} | ||
test['id 0100000100000000, bitIndex 7, should be high'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("4100", "hex"), 7), 1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x41, 0x00 ]), 7), 1) | ||
test.done() | ||
} | ||
test['id 000000000100000100000000, bitIndex 15, should be high'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket._determineBucket(new Buffer("004100", "hex"), 15), 1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket._determineBucket(new Buffer([ 0x00, 0x41, 0x00 ]), 15), 1) | ||
test.done() | ||
} |
@@ -1,45 +0,34 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['distance between 00000000 and 00000000 is 00000000'] = function (test) { | ||
test.expect(1); | ||
test.equal( | ||
KBucket.distance(new Buffer('00', 'hex'), new Buffer('00', 'hex')), | ||
0); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
test.equal(KBucket.distance(new Buffer([ 0x00 ]), new Buffer([ 0x00 ])), 0) | ||
test.done() | ||
} | ||
test['distance between 00000000 and 00000001 is 00000001'] = function (test) { | ||
test.expect(1); | ||
test.equal( | ||
KBucket.distance(new Buffer('00', 'hex'), new Buffer('01', 'hex')), | ||
1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
test.equal(KBucket.distance(new Buffer([ 0x00 ]), new Buffer([ 0x01 ])), 1) | ||
test.done() | ||
} | ||
test['distance between 00000010 and 00000001 is 00000011'] = function (test) { | ||
test.expect(1); | ||
test.equal( | ||
KBucket.distance(new Buffer('02', 'hex'), new Buffer('01', 'hex')), | ||
3); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
test.equal(KBucket.distance(new Buffer([ 0x02 ]), new Buffer([ 0x01 ])), 3) | ||
test.done() | ||
} | ||
test['distance between 00000000 and 0000000000000000 is 0000000011111111'] = function (test) { | ||
test.expect(1); | ||
test.equal( | ||
KBucket.distance(new Buffer('00', 'hex'), new Buffer('0000', 'hex')), | ||
255); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
test.equal(KBucket.distance(new Buffer([ 0x00 ]), new Buffer([ 0x00, 0x00 ])), 255) | ||
test.done() | ||
} | ||
test['distance between 0000000100100100 and 0100000000100100 is 0100000100000000'] = function (test) { | ||
test.expect(1); | ||
test.equal( | ||
KBucket.distance(new Buffer('0124', 'hex'), new Buffer('4024', 'hex')), | ||
16640); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
test.equal(KBucket.distance(new Buffer([ 0x01, 0x24 ]), new Buffer([ 0x40, 0x24 ])), 16640) | ||
test.done() | ||
} |
@@ -1,62 +0,55 @@ | ||
"use strict"; | ||
'use strict' | ||
var bufferEqual = require('buffer-equal') | ||
var KBucket = require('../index') | ||
var bufferEqual = require('buffer-equal'); | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['throws TypeError if id is not a Buffer'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.throws(function () { | ||
kBucket.get("foo"); | ||
}, TypeError); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.throws(function () { | ||
kBucket.get('foo') | ||
}, TypeError) | ||
test.done() | ||
} | ||
test['get retrieves null if no contacts'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.strictEqual(null, kBucket.get(new Buffer('foo'))); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.strictEqual(null, kBucket.get(new Buffer('foo'))) | ||
test.done() | ||
} | ||
test['get retrieves a contact that was added'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.add(contact); | ||
test.ok(bufferEqual(kBucket.get(new Buffer("a")).id, new Buffer("a"))); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.add(contact) | ||
test.ok(bufferEqual(kBucket.get(new Buffer('a')).id, new Buffer('a'))) | ||
test.done() | ||
} | ||
test['get retrieves most recently added contact if same id'] = function (test) { | ||
test.expect(3); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a"), foo: 'foo', bar: ':p', vectorClock: 0}; | ||
var contact2 = {id: new Buffer("a"), foo: 'bar', vectorClock: 1}; | ||
kBucket.add(contact); | ||
kBucket.add(contact2); | ||
test.ok(bufferEqual(kBucket.get(new Buffer("a")).id, new Buffer("a"))); | ||
test.equal(kBucket.get(new Buffer("a")).foo, 'bar'); | ||
test.strictEqual(kBucket.get(new Buffer("a")).bar, undefined); | ||
test.done(); | ||
}; | ||
test.expect(3) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a'), foo: 'foo', bar: ':p', vectorClock: 0 } | ||
var contact2 = { id: new Buffer('a'), foo: 'bar', vectorClock: 1 } | ||
kBucket.add(contact) | ||
kBucket.add(contact2) | ||
test.ok(bufferEqual(kBucket.get(new Buffer('a')).id, new Buffer('a'))) | ||
test.equal(kBucket.get(new Buffer('a')).foo, 'bar') | ||
test.strictEqual(kBucket.get(new Buffer('a')).bar, undefined) | ||
test.done() | ||
} | ||
test['get retrieves contact from nested leaf node'] = function (test) { | ||
test.expect(1); | ||
var iString; | ||
var kBucket = new KBucket({localNodeId: new Buffer('0000', 'hex')}); | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; i++) { | ||
iString = i.toString('16'); | ||
if (iString.length < 2) { | ||
iString = '0' + iString; | ||
} | ||
iString = '80' + iString; // make sure all go into "far away" bucket | ||
kBucket.add({id: new Buffer(iString, 'hex')}); | ||
} | ||
// cause a split to happen | ||
kBucket.add({id: new Buffer('00' + iString, 'hex'), find: 'me'}); | ||
test.equal(kBucket.get(new Buffer('00' + iString, 'hex')).find, 'me'); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket({localNodeId: new Buffer([ 0x00, 0x00 ])}) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; ++i) { | ||
kBucket.add({ id: new Buffer([ 0x80, i ]) }) // make sure all go into "far away" bucket | ||
} | ||
// cause a split to happen | ||
kBucket.add({ id: new Buffer([ 0x00, i ]), find: 'me' }) | ||
test.equal(kBucket.get(new Buffer([ 0x00, i ])).find, 'me') | ||
test.done() | ||
} |
@@ -1,22 +0,20 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['indexOf returns a contact with id that contains the same byte sequence as the test contact'] = function (test) { | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
kBucket.add({ id: new Buffer('a') }) | ||
test.equal(kBucket._indexOf(new Buffer('a')), 0) | ||
test.done() | ||
} | ||
test['indexOf returns a contact with id that contains the same byte sequence' + | ||
' as the test contact'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
kBucket.add({id: new Buffer("a")}); | ||
test.equal(kBucket._indexOf(new Buffer("a")), 0); | ||
test.done(); | ||
}; | ||
test['indexOf returns -1 if contact is not found'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
kBucket.add({id: new Buffer("a")}); | ||
test.equal(kBucket._indexOf(new Buffer("b")), -1); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
kBucket.add({ id: new Buffer('a') }) | ||
test.equal(kBucket._indexOf(new Buffer('b')), -1) | ||
test.done() | ||
} |
@@ -1,48 +0,58 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['throws TypeError if contact.id is not a Buffer'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: "foo"}; | ||
test.throws(function () { | ||
kBucket.remove(contact.id); | ||
}, TypeError); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: 'foo' } | ||
test.throws(function () { | ||
kBucket.remove(contact.id) | ||
}, TypeError) | ||
test.done() | ||
} | ||
test['removing a contact should remove contact from nested buckets'] = function (test) { | ||
test.expect(2); | ||
var kBucket = new KBucket({localNodeId: new Buffer('0000', 'hex')}); | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; i++) { | ||
var iString = i.toString('16'); | ||
if (iString.length < 2) { | ||
iString = '0' + iString; | ||
} | ||
iString = '80' + iString; // make sure all go into "far away" bucket | ||
kBucket.add({id: new Buffer(iString, 'hex')}); | ||
} | ||
// cause a split to happen | ||
kBucket.add({id: new Buffer('00' + iString, 'hex')}); | ||
// console.log(require('util').inspect(kBucket, false, null)); | ||
var contactToDelete = {id: new Buffer('8000', 'hex')}; | ||
test.equal(kBucket.high._indexOf(contactToDelete.id), 0); | ||
kBucket.remove(new Buffer('8000', 'hex')); | ||
test.equal(kBucket.high._indexOf(contactToDelete.id), -1); | ||
test.done(); | ||
}; | ||
test.expect(2) | ||
var kBucket = new KBucket({ localNodeId: new Buffer([ 0x00, 0x00 ]) }) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; ++i) { | ||
kBucket.add({ id: new Buffer([ 0x80, i ]) }) // make sure all go into "far away" bucket | ||
} | ||
// cause a split to happen | ||
kBucket.add({ id: new Buffer([ 0x00, i ]) }) | ||
// console.log(require('util').inspect(kBucket, false, null)) | ||
var contactToDelete = { id: new Buffer([ 0x80, 0x00 ]) } | ||
test.equal(kBucket.high._indexOf(contactToDelete.id), 0) | ||
kBucket.remove(new Buffer([ 0x80, 0x00 ])) | ||
test.equal(kBucket.high._indexOf(contactToDelete.id), -1) | ||
test.done() | ||
} | ||
test['should generate "removed"'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.on('removed', function (removedContact) { | ||
test.deepEqual(removedContact, contact); | ||
}); | ||
kBucket.add(contact); | ||
kBucket.remove(contact.id); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.on('removed', function (removedContact) { test.deepEqual(removedContact, contact) }) | ||
kBucket.add(contact) | ||
kBucket.remove(contact.id) | ||
test.done() | ||
} | ||
test['should generate event "removed" when removing from a split bucket'] = function (test) { | ||
test.expect(2) | ||
var kBucket = new KBucket({ | ||
localNodeId: new Buffer('') // need non-random localNodeId for deterministic splits | ||
}) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; ++i) { | ||
kBucket.add({ id: new Buffer('' + i) }) | ||
} | ||
test.ok(!kBucket.bucket) | ||
var contact = { id: new Buffer('a') } | ||
kBucket.on('removed', function (removedContact) { | ||
test.deepEqual(removedContact, contact) | ||
}) | ||
kBucket.add(contact) | ||
kBucket.remove(contact.id) | ||
test.done() | ||
} |
@@ -1,122 +0,102 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['adding a contact does not split K-bucket'] = function (test) { | ||
test.expect(3); | ||
var kBucket = new KBucket(); | ||
kBucket.add({id: new Buffer("a")}); | ||
test.ok(!kBucket.low); | ||
test.ok(!kBucket.high); | ||
test.ok(kBucket.bucket); | ||
test.done(); | ||
}; | ||
test.expect(3) | ||
var kBucket = new KBucket() | ||
kBucket.add({ id: new Buffer('a') }) | ||
test.ok(!kBucket.low) | ||
test.ok(!kBucket.high) | ||
test.ok(kBucket.bucket) | ||
test.done() | ||
} | ||
test['adding maximum number of contacts (per K-bucket) [20]' + | ||
' into K-bucket does not split K-bucket'] = function (test) { | ||
test.expect(3); | ||
var kBucket = new KBucket(); | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; i++) { | ||
kBucket.add({id: new Buffer("" + i)}); | ||
} | ||
test.ok(!kBucket.low); | ||
test.ok(!kBucket.high); | ||
test.ok(kBucket.bucket); | ||
test.done(); | ||
}; | ||
test['adding maximum number of contacts (per K-bucket) [20] into K-bucket does not split K-bucket'] = function (test) { | ||
test.expect(3) | ||
var kBucket = new KBucket() | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; ++i) { | ||
kBucket.add({ id: new Buffer('' + i) }) | ||
} | ||
test.ok(!kBucket.low) | ||
test.ok(!kBucket.high) | ||
test.ok(kBucket.bucket) | ||
test.done() | ||
} | ||
test['adding maximum number of contacts (per K-bucket) + 1 [21]' + | ||
' into K-bucket splits the K-bucket'] = function (test) { | ||
test.expect(3); | ||
var kBucket = new KBucket(); | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; i++) { | ||
kBucket.add({id: new Buffer("" + i)}); | ||
} | ||
test.ok(kBucket.low instanceof KBucket); | ||
test.ok(kBucket.high instanceof KBucket); | ||
test.ok(!kBucket.bucket); | ||
test.done(); | ||
}; | ||
test['adding maximum number of contacts (per K-bucket) + 1 [21] into K-bucket splits the K-bucket'] = function (test) { | ||
test.expect(3) | ||
var kBucket = new KBucket() | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; ++i) { | ||
kBucket.add({ id: new Buffer('' + i) }) | ||
} | ||
test.ok(kBucket.low instanceof KBucket) | ||
test.ok(kBucket.high instanceof KBucket) | ||
test.ok(!kBucket.bucket) | ||
test.done() | ||
} | ||
test['split buckets inherit options from parent bucket'] = function (test) { | ||
var OPTIONS = ['arbiter', 'localNodeId', 'root', 'numberOfNodesPerKBucket', | ||
'numberOfNodesToPing']; | ||
test.expect(2 * OPTIONS.length); | ||
var kBucket = new KBucket(); | ||
var _options = {}; | ||
OPTIONS.forEach(function(option){ | ||
_options[option] = kBucket[option]; | ||
}); | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; i++) { | ||
kBucket.add({id: new Buffer("" + i)}); | ||
} | ||
OPTIONS.forEach(function(option){ | ||
test.strictEqual(kBucket.low[option], _options[option]); | ||
test.strictEqual(kBucket.high[option], _options[option]); | ||
}); | ||
test.done(); | ||
}; | ||
var OPTIONS = ['arbiter', 'localNodeId', 'root', 'numberOfNodesPerKBucket', 'numberOfNodesToPing'] | ||
test.expect(2 * OPTIONS.length) | ||
var kBucket = new KBucket() | ||
var _options = {} | ||
OPTIONS.forEach(function (option) { _options[option] = kBucket[option] }) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; ++i) { | ||
kBucket.add({ id: new Buffer('' + i) }) | ||
} | ||
OPTIONS.forEach(function (option) { | ||
test.strictEqual(kBucket.low[option], _options[option]) | ||
test.strictEqual(kBucket.high[option], _options[option]) | ||
}) | ||
test.done() | ||
} | ||
test['split buckets contain all added contacts'] = function (test) { | ||
test.expect(20 /*numberOfNodesPerKBucket*/ + 2); | ||
var kBucket = new KBucket({localNodeId: new Buffer('00', 'hex')}); | ||
var foundContact = {}; | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; i++) { | ||
var iString = i.toString('16'); | ||
if (iString.length < 2) { | ||
iString = '0' + iString; | ||
} | ||
kBucket.add({id: new Buffer(iString, 'hex')}); | ||
foundContact[iString] = false; | ||
test.expect(20 /* numberOfNodesPerKBucket */ + 2) | ||
var kBucket = new KBucket({localNodeId: new Buffer([ 0x00 ])}) | ||
var foundContact = {} | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; ++i) { | ||
kBucket.add({ id: new Buffer([ i ]) }) | ||
foundContact[i] = false | ||
} | ||
var traverse = function (node) { | ||
if (!node.bucket) { | ||
traverse(node.low) | ||
traverse(node.high) | ||
} else { | ||
node.bucket.forEach(function (contact) { | ||
foundContact[parseInt(contact.id.toString('hex'), 16)] = true | ||
}) | ||
} | ||
var traverse = function (node) { | ||
if (!node.bucket) { | ||
traverse(node.low); | ||
traverse(node.high); | ||
} else { | ||
node.bucket.forEach(function (contact) { | ||
foundContact[contact.id.toString('hex')] = true; | ||
}); | ||
} | ||
}; | ||
traverse(kBucket); | ||
Object.keys(foundContact).forEach(function (key) { | ||
test.ok(foundContact[key], key); | ||
}); | ||
test.ok(!kBucket.bucket); | ||
test.done(); | ||
}; | ||
} | ||
traverse(kBucket) | ||
Object.keys(foundContact).forEach(function (key) { test.ok(foundContact[key], key) }) | ||
test.ok(!kBucket.bucket) | ||
test.done() | ||
} | ||
test['when splitting buckets the "far away" bucket should be marked' + | ||
' to prevent splitting "far away" bucket'] = function (test) { | ||
test.expect(5); | ||
var kBucket = new KBucket({localNodeId: new Buffer('00', 'hex')}); | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; i++) { | ||
var iString = i.toString('16'); | ||
if (iString.length < 2) { | ||
iString = '0' + iString; | ||
} | ||
kBucket.add({id: new Buffer(iString, 'hex')}); | ||
test['when splitting buckets the "far away" bucket should be marked to prevent splitting "far away" bucket'] = function (test) { | ||
test.expect(5) | ||
var kBucket = new KBucket({ localNodeId: new Buffer([ 0x00 ]) }) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; ++i) { | ||
kBucket.add({ id: new Buffer([ i ]) }) | ||
} | ||
// above algorithm will split low bucket 4 times and put 0x00 through 0x0f | ||
// in the low bucket, and put 0x10 through 0x14 in high bucket | ||
// since localNodeId is 0x00, we expect every high bucket to be "far" and | ||
// therefore marked as "dontSplit = true" | ||
// there will be one "low" bucket and four "high" buckets (test.expect(5)) | ||
var traverse = function (node, dontSplit) { | ||
if (!node.bucket) { | ||
traverse(node.low, false) | ||
traverse(node.high, true) | ||
} else { | ||
if (dontSplit) test.ok(node.dontSplit) | ||
else test.ok(!node.dontSplit) | ||
} | ||
// above algorithm will split low bucket 4 times and put 0x00 through 0x0f | ||
// in the low bucket, and put 0x10 through 0x14 in high bucket | ||
// since localNodeId is 0x00, we expect every high bucket to be "far" and | ||
// therefore marked as "dontSplit = true" | ||
// there will be one "low" bucket and four "high" buckets (test.expect(5)) | ||
var traverse = function (node, dontSplit) { | ||
if (!node.bucket) { | ||
traverse(node.low, false); | ||
traverse(node.high, true); | ||
} else { | ||
if (dontSplit) { | ||
test.ok(node.dontSplit); | ||
} else { | ||
test.ok(!node.dontSplit); | ||
} | ||
} | ||
}; | ||
traverse(kBucket); | ||
test.done(); | ||
}; | ||
} | ||
traverse(kBucket) | ||
test.done() | ||
} |
@@ -1,40 +0,33 @@ | ||
"use strict"; | ||
'use strict' | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['toArray should return empty array if no contacts'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
test.equal(kBucket.toArray().length, 0); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
test.equal(kBucket.toArray().length, 0) | ||
test.done() | ||
} | ||
test['toArray should return all contacts in an array arranged from low to high buckets'] = function (test) { | ||
test.expect(22); | ||
var iString; | ||
var expectedIds = []; | ||
var kBucket = new KBucket({localNodeId: new Buffer('0000', 'hex')}); | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; i++) { | ||
iString = i.toString('16'); | ||
if (iString.length < 2) { | ||
iString = '0' + iString; | ||
} | ||
iString = '80' + iString; // make sure all go into "far away" bucket | ||
expectedIds.push(iString); | ||
kBucket.add({id: new Buffer(iString, 'hex')}); | ||
} | ||
// cause a split to happen | ||
kBucket.add({id: new Buffer('00' + iString, 'hex')}); | ||
// console.log(require('util').inspect(kBucket, {depth: null})); | ||
var contacts = kBucket.toArray(); | ||
// console.log(require('util').inspect(contacts, {depth: null})); | ||
test.equal(contacts.length, kBucket.numberOfNodesPerKBucket + 1); | ||
test.equal(contacts[0].id.toString('hex'), '00' + iString); | ||
contacts.shift(); // get rid of low bucket contact | ||
for (i = 0; i < kBucket.numberOfNodesPerKBucket; i++) { | ||
test.equal(contacts[i].id.toString('hex'), expectedIds[i]); | ||
} | ||
test.done(); | ||
}; | ||
test.expect(22) | ||
var kBucket = new KBucket({ localNodeId: new Buffer([ 0x00, 0x00 ]) }) | ||
var expectedIds = [] | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket; ++i) { | ||
kBucket.add({ id: new Buffer([ 0x80, i ]) }) // make sure all go into "far away" bucket | ||
expectedIds.push(0x80 * 256 + i) | ||
} | ||
// cause a split to happen | ||
kBucket.add({ id: new Buffer([ 0x00, 0x80, i - 1 ]) }) | ||
// console.log(require('util').inspect(kBucket, {depth: null})) | ||
var contacts = kBucket.toArray() | ||
// console.log(require('util').inspect(contacts, {depth: null})) | ||
test.equal(contacts.length, kBucket.numberOfNodesPerKBucket + 1) | ||
test.equal(parseInt(contacts[0].id.toString('hex'), 16), 0x80 * 256 + i - 1) | ||
contacts.shift() // get rid of low bucket contact | ||
for (i = 0; i < kBucket.numberOfNodesPerKBucket; ++i) { | ||
test.equal(parseInt(contacts[i].id.toString('hex'), 16), expectedIds[i]) | ||
} | ||
test.done() | ||
} |
@@ -1,69 +0,87 @@ | ||
"use strict"; | ||
'use strict' | ||
var bufferEqual = require('buffer-equal') | ||
var KBucket = require('../index') | ||
var KBucket = require('../index.js'), | ||
bufferEqual = require('buffer-equal'); | ||
var test = module.exports = {} | ||
var test = module.exports = {}; | ||
test['invalid index results in exception'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a")}; | ||
kBucket.add(contact); | ||
try { | ||
kBucket._update(contact, 1); | ||
} catch (exception) { | ||
test.ok(true); | ||
} | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a') } | ||
kBucket.add(contact) | ||
try { | ||
kBucket._update(contact, 1) | ||
} catch (exception) { | ||
test.ok(true) | ||
} | ||
test.done() | ||
} | ||
test['deprecated vectorClock results in contact drop'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a"), vectorClock: 3}; | ||
kBucket.add(contact); | ||
kBucket._update({id: new Buffer("a"), vectorClock: 2}, 0); | ||
test.equal(kBucket.bucket[0].vectorClock, 3); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a'), vectorClock: 3 } | ||
kBucket.add(contact) | ||
kBucket._update({ id: new Buffer('a'), vectorClock: 2 }, 0) | ||
test.equal(kBucket.bucket[0].vectorClock, 3) | ||
test.done() | ||
} | ||
test['equal vectorClock results in contact marked as most recent'] = function (test) { | ||
test.expect(1); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a"), vectorClock: 3}; | ||
kBucket.add(contact); | ||
kBucket.add({id: new Buffer("b")}); | ||
kBucket._update(contact, 0); | ||
test.equal(kBucket.bucket[1], contact); | ||
test.done(); | ||
}; | ||
test.expect(1) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a'), vectorClock: 3 } | ||
kBucket.add(contact) | ||
kBucket.add({ id: new Buffer('b') }) | ||
kBucket._update(contact, 0) | ||
test.equal(kBucket.bucket[1], contact) | ||
test.done() | ||
} | ||
test['more recent vectorClock results in contact update and contact being' + | ||
' marked as most recent'] = function (test) { | ||
test.expect(4); | ||
var kBucket = new KBucket(); | ||
var contact = {id: new Buffer("a"), old: 'property', vectorClock: 3}; | ||
kBucket.add(contact); | ||
kBucket.add({id: new Buffer("b")}); | ||
kBucket._update({id: new Buffer("a"), newer: 'property', vectorClock: 4}, 0); | ||
test.ok(bufferEqual(kBucket.bucket[1].id, contact.id)); | ||
test.equal(kBucket.bucket[1].vectorClock, 4); | ||
test.strictEqual(kBucket.bucket[1].old, undefined); | ||
test.strictEqual(kBucket.bucket[1].newer, 'property'); | ||
test.done(); | ||
}; | ||
test['more recent vectorClock results in contact update and contact being marked as most recent'] = function (test) { | ||
test.expect(4) | ||
var kBucket = new KBucket() | ||
var contact = { id: new Buffer('a'), old: 'property', vectorClock: 3 } | ||
kBucket.add(contact) | ||
kBucket.add({ id: new Buffer('b') }) | ||
kBucket._update({ id: new Buffer('a'), newer: 'property', vectorClock: 4 }, 0) | ||
test.ok(bufferEqual(kBucket.bucket[1].id, contact.id)) | ||
test.equal(kBucket.bucket[1].vectorClock, 4) | ||
test.strictEqual(kBucket.bucket[1].old, undefined) | ||
test.strictEqual(kBucket.bucket[1].newer, 'property') | ||
test.done() | ||
} | ||
test['should generate "updated"'] = function (test) { | ||
test.expect(2); | ||
var kBucket = new KBucket(); | ||
var contact1 = {id: new Buffer("a"), vectorClock: 1}; | ||
var contact2 = {id: new Buffer("a"), vectorClock: 2}; | ||
kBucket.on('updated', function (oldContact, newContact) { | ||
test.deepEqual(oldContact, contact1); | ||
test.deepEqual(newContact, contact2); | ||
}) | ||
kBucket.add(contact1); | ||
kBucket.add(contact2); | ||
test.done(); | ||
}; | ||
test.expect(2) | ||
var kBucket = new KBucket() | ||
var contact1 = { id: new Buffer('a'), vectorClock: 1 } | ||
var contact2 = { id: new Buffer('a'), vectorClock: 2 } | ||
kBucket.on('updated', function (oldContact, newContact) { | ||
test.deepEqual(oldContact, contact1) | ||
test.deepEqual(newContact, contact2) | ||
}) | ||
kBucket.add(contact1) | ||
kBucket.add(contact2) | ||
test.done() | ||
} | ||
test['should generate event "updated" when updating a split bucket'] = function (test) { | ||
test.expect(3) | ||
var kBucket = new KBucket({ | ||
localNodeId: new Buffer('') // need non-random localNodeId for deterministic splits | ||
}) | ||
for (var i = 0; i < kBucket.numberOfNodesPerKBucket + 1; ++i) { | ||
kBucket.add({ id: new Buffer('' + i) }) | ||
} | ||
test.ok(!kBucket.bucket) | ||
var contact1 = { id: new Buffer('a'), vectorClock: 1 } | ||
var contact2 = { id: new Buffer('a'), vectorClock: 2 } | ||
kBucket.on('updated', function (oldContact, newContact) { | ||
test.deepEqual(oldContact, contact1) | ||
test.deepEqual(newContact, contact2) | ||
}) | ||
kBucket.add(contact1) | ||
kBucket.add(contact2) | ||
test.done() | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
0
54642
2
981
271
1