Skip to content
This repository has been archived by the owner on Nov 10, 2020. It is now read-only.

Commit

Permalink
One file per class
Browse files Browse the repository at this point in the history
  • Loading branch information
michielbdejong committed Nov 11, 2016
1 parent c05ef67 commit 3832439
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 209 deletions.
210 changes: 1 addition & 209 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,211 +1,3 @@
var randomstring = require('randomstring');

function generateNewPathToken() {
return randomstring.generate(40);
}

function Neighbor(msgCallback) {
this._msgCallback = msgCallback;
this._theirLastMsg = {
timestamp: 0,
};
this._ourLastMsg = {
timestamp: 0,
};
}

Neighbor.prototype.sendStatusMessage = function(value, timestamp) {
if (this._ourLastMsg.value === value) {
return 0;
}
if (this._ourLastMsg.timestamp > timestamp) {
return 0;
}
this._ourLastMsg = {
msgType: 'status',
value,
timestamp,
};
this._msgCallback(this._ourLastMsg);
return 1;
};

Neighbor.prototype.sendProbeMessage = function(msgObj) {
msgObj.msgType = 'probe';
this._msgCallback(msgObj);
};

Neighbor.prototype.saveStatusMessage = function(msgObj) {
this._theirLastMsg = msgObj;
};

function Route(inNeighbor, treeToken) {
this._inNeighbor = inNeighbor;
this._treeToken = treeToken;
this._outNeighbors = {
};
}

Route.prototype.getNextSiblingToTry = function(outNeighborIds) {
for (var i=0; i<outNeighborIds.length; i++) {
if (typeof this._outNeighbors[outNeighborIds[i]] === 'undefined') {
this._outNeighbors[outNeighborIds[i]] = generateNewPathToken();
return outNeighborIds[i];
}
}
};

Route.prototype.getOutNeighborNick = function(pathToken) {
for (var outNeighborNick in this._outNeighbors) {
if (this._outNeighbors[outNeighborNick] === pathToken) {
return outNeighborNick;
}
}
};

var OPPOSITE = {
'in': 'out',
out: 'in',
};

function Node() {
this._neighbors = {
'in': {},
out: {},
};
this._routes = {};
this._lastTimestampGenerated = 0;
}

Node.prototype._getTimestamp = function() {
var candidate = new Date().getTime();
if (candidate <= this._lastTimestampGenerated) {
candidate++;
}
this._lastTimestampGenerated = candidate;
return candidate;
};

Node.prototype._noActiveNeighborsLeft = function(direction) {
for (var neighborId in this._neighbors[direction]) {
if (this._neighbors[direction][neighborId]._theirLastMsg.value === true) {
return false;
}
}
return true;
};

Node.prototype._startWave = function(direction, value, timestamp) {
var numTried = 0;
var numSent = 0;
for (var neighborId in this._neighbors[direction]) {
numTried++;
numSent += this._neighbors[direction][neighborId].sendStatusMessage(value, timestamp);
}
if (value === false) {
return; // no back wave
}
if (numTried === 0) { // bounce against edge of network
return {
value: false,
timestamp,
};
}
if (numSent === 0) { // special case, reply true
return {
value: true,
timestamp,
};
}
};

Node.prototype._activate = function(direction, timestamp) {
var msgBack = this._startWave(direction, true, timestamp);
if (typeof msgBack === 'object') {
this._startWave(OPPOSITE[direction], msgBack.value, msgBack.timestamp);
}
};

Node.prototype.addNeighbor = function(neighborId, direction, msgCallback) {
if (typeof this._neighbors[direction][neighborId] === 'undefined') {
this._neighbors[direction][neighborId] = new Neighbor(msgCallback);
this._activate(OPPOSITE[direction], this._getTimestamp());
}
};

Node.prototype.removeNeighbor = function(neighborId, direction) {
delete this._neighbors[direction][neighborId];
if (this._noNeighborsLeft(direction)) {
this._startWave(OPPOSITE[direction], false, this._getTimestamp());
}
};

Node.prototype._probeIsKnown = function(treeToken) {
return (typeof this._routes[treeToken] !== 'undefined');
};

Node.prototype.handleProbeMessage = function(neighborId, direction, msgObj) {
if (direction === 'in') {
if (this._probeIsKnown(msgObj.treeToken)) {
this._cycleFound = true;
return;
}
this._routes[msgObj.treeToken] = new Route(neighborId, msgObj.treeToken, msgObj.pathToken);
var firstOutNeighborId = this._routes[msgObj.treeToken].getNextSiblingToTry(this.getActiveNeighbors().out);
if (firstOutNeighborId) {
this._neighbors.out[firstOutNeighborId].sendProbeMessage(msgObj);
} else { // backtrack
this._neighbors['in'][neighborId].sendProbeMessage(msgObj);
}
} else {
var nextOutNeighborId = this._routes[msgObj.treeToken].getNextSiblingToTry(this.getActiveNeighbors().out);
if (nextOutNeighborId) {
msgObj.pathToken = this._routes[msgObj.treeToken].getPathToken();
this._neighbors.out[nextOutNeighborId].sendProbeMessage(msgObj);
} else { // backtrack
this._neighbors['in'][neighborId].sendProbeMessage(msgObj);
}
}
};

Node.prototype.handleStatusMessage = function(neighborId, direction, msgObj) {
var now = new Date().getTime();
if (msgObj.timestamp > now) {
msgObj.timestamp = now;
}

this._neighbors[direction][neighborId].saveStatusMessage(msgObj);

if (msgObj.value === true) {
this._activate(OPPOSITE[direction], msgObj.timestamp);
}

if (msgObj.value === false && this._noActiveNeighborsLeft(direction)) {
this._startWave(OPPOSITE[direction], false, msgObj.timestamp);
}
};

Node.prototype.getActiveNeighbors = function() {
var ret = {
'in': [],
out: [],
};
['in', 'out'].map(direction => {
for (var neighborId in this._neighbors[direction]) {
if (this._neighbors[direction][neighborId]._theirLastMsg.value === true) {
ret[direction].push(neighborId);
}
}
});
return ret;
};

Node.prototype.getPeerPair = function(treeToken, pathToken, inNeighborNick) {
var route = this._routes[treeToken];
return {
inNeighborNick: inNeighborNick,
outNeighborNick: this._routes[treeToken].getOutNeighborNick(pathToken),
};
};
var Node = require('./node');

module.exports = Node;
36 changes: 36 additions & 0 deletions neighbor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
function Neighbor(msgCallback) {
this._msgCallback = msgCallback;
this._theirLastMsg = {
timestamp: 0,
};
this._ourLastMsg = {
timestamp: 0,
};
}

Neighbor.prototype.sendStatusMessage = function(value, timestamp) {
if (this._ourLastMsg.value === value) {
return 0;
}
if (this._ourLastMsg.timestamp > timestamp) {
return 0;
}
this._ourLastMsg = {
msgType: 'status',
value,
timestamp,
};
this._msgCallback(this._ourLastMsg);
return 1;
};

Neighbor.prototype.sendProbeMessage = function(msgObj) {
msgObj.msgType = 'probe';
this._msgCallback(msgObj);
};

Neighbor.prototype.saveStatusMessage = function(msgObj) {
this._theirLastMsg = msgObj;
};

module.exports = Neighbor;
149 changes: 149 additions & 0 deletions node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
var Neighbor = require('./neighbor');
var Route = require('./route');

var OPPOSITE = {
'in': 'out',
out: 'in',
};

function Node() {
this._neighbors = {
'in': {},
out: {},
};
this._routes = {};
this._lastTimestampGenerated = 0;
}

Node.prototype._getTimestamp = function() {
var candidate = new Date().getTime();
if (candidate <= this._lastTimestampGenerated) {
candidate++;
}
this._lastTimestampGenerated = candidate;
return candidate;
};

Node.prototype._noActiveNeighborsLeft = function(direction) {
for (var neighborId in this._neighbors[direction]) {
if (this._neighbors[direction][neighborId]._theirLastMsg.value === true) {
return false;
}
}
return true;
};

Node.prototype._startWave = function(direction, value, timestamp) {
var numTried = 0;
var numSent = 0;
for (var neighborId in this._neighbors[direction]) {
numTried++;
numSent += this._neighbors[direction][neighborId].sendStatusMessage(value, timestamp);
}
if (value === false) {
return; // no back wave
}
if (numTried === 0) { // bounce against edge of network
return {
value: false,
timestamp,
};
}
if (numSent === 0) { // special case, reply true
return {
value: true,
timestamp,
};
}
};

Node.prototype._activate = function(direction, timestamp) {
var msgBack = this._startWave(direction, true, timestamp);
if (typeof msgBack === 'object') {
this._startWave(OPPOSITE[direction], msgBack.value, msgBack.timestamp);
}
};

Node.prototype.addNeighbor = function(neighborId, direction, msgCallback) {
if (typeof this._neighbors[direction][neighborId] === 'undefined') {
this._neighbors[direction][neighborId] = new Neighbor(msgCallback);
this._activate(OPPOSITE[direction], this._getTimestamp());
}
};

Node.prototype.removeNeighbor = function(neighborId, direction) {
delete this._neighbors[direction][neighborId];
if (this._noNeighborsLeft(direction)) {
this._startWave(OPPOSITE[direction], false, this._getTimestamp());
}
};

Node.prototype._probeIsKnown = function(treeToken) {
return (typeof this._routes[treeToken] !== 'undefined');
};

Node.prototype.handleProbeMessage = function(neighborId, direction, msgObj) {
if (direction === 'in') {
if (this._probeIsKnown(msgObj.treeToken)) {
this._cycleFound = true;
return;
}
this._routes[msgObj.treeToken] = new Route(neighborId, msgObj.treeToken, msgObj.pathToken);
var firstOutNeighborId = this._routes[msgObj.treeToken].getNextSiblingToTry(this.getActiveNeighbors().out);
if (firstOutNeighborId) {
this._neighbors.out[firstOutNeighborId].sendProbeMessage(msgObj);
} else { // backtrack
this._neighbors['in'][neighborId].sendProbeMessage(msgObj);
}
} else {
var nextOutNeighborId = this._routes[msgObj.treeToken].getNextSiblingToTry(this.getActiveNeighbors().out);
if (nextOutNeighborId) {
msgObj.pathToken = this._routes[msgObj.treeToken].getPathToken();
this._neighbors.out[nextOutNeighborId].sendProbeMessage(msgObj);
} else { // backtrack
this._neighbors['in'][neighborId].sendProbeMessage(msgObj);
}
}
};

Node.prototype.handleStatusMessage = function(neighborId, direction, msgObj) {
var now = new Date().getTime();
if (msgObj.timestamp > now) {
msgObj.timestamp = now;
}

this._neighbors[direction][neighborId].saveStatusMessage(msgObj);

if (msgObj.value === true) {
this._activate(OPPOSITE[direction], msgObj.timestamp);
}

if (msgObj.value === false && this._noActiveNeighborsLeft(direction)) {
this._startWave(OPPOSITE[direction], false, msgObj.timestamp);
}
};

Node.prototype.getActiveNeighbors = function() {
var ret = {
'in': [],
out: [],
};
['in', 'out'].map(direction => {
for (var neighborId in this._neighbors[direction]) {
if (this._neighbors[direction][neighborId]._theirLastMsg.value === true) {
ret[direction].push(neighborId);
}
}
});
return ret;
};

Node.prototype.getPeerPair = function(treeToken, pathToken, inNeighborNick) {
var route = this._routes[treeToken];
return {
inNeighborNick: inNeighborNick,
outNeighborNick: this._routes[treeToken].getOutNeighborNick(pathToken),
};
};

module.exports = Node;
Loading

0 comments on commit 3832439

Please sign in to comment.