trek-router
Advanced tools
Comparing version 0.0.13 to 0.1.0
@@ -12,9 +12,22 @@ 'use strict'; | ||
const min = Math.min; | ||
const METHODS = ['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']; | ||
const SNODE = 0; // Static node | ||
const PNODE = 1; // Param node | ||
const ANODE = 2; // Catch-all node | ||
const SNODE = 1; // Static node | ||
const PNODE = 2; // Param node | ||
const CNODE = 3; // Catch-all node | ||
let Node = (function () { | ||
/** | ||
* Node | ||
* | ||
* @class Node | ||
* @constructor | ||
* @param {String} path | ||
* @param {Number} has | ||
* @param {Function|GeneratorFunction} handler | ||
* @param {Array} [edges] | ||
*/ | ||
var Node = (function () { | ||
function Node(prefix, has, handler, edges) { | ||
@@ -27,17 +40,23 @@ _classCallCheck(this, Node); | ||
this.handler = handler; | ||
this.edges = edges; | ||
if (!edges) this.edges = []; | ||
this.edges = edges || []; | ||
} | ||
/** | ||
* Find edge by charCode | ||
* | ||
* @param {Number} char code | ||
* @return {Node|undefined} node | ||
*/ | ||
Node.prototype.findEdge = function findEdge(c) { | ||
let i = 0; | ||
let l = this.edges.length; | ||
let e = void 0; | ||
var i = 0; | ||
var l = this.edges.length; | ||
var e = undefined; | ||
for (; i < l; i++) { | ||
for (; i < l; ++i) { | ||
e = this.edges[i]; | ||
// compare charCode | ||
// Compare charCode | ||
if (e.label === c) return e; | ||
} | ||
return null; | ||
return undefined; | ||
}; | ||
@@ -48,3 +67,10 @@ | ||
let Router = (function () { | ||
/** | ||
* Router | ||
* | ||
* @class Router | ||
* @constructor | ||
*/ | ||
var Router = (function () { | ||
function Router() { | ||
@@ -61,31 +87,78 @@ var _this = this; | ||
/** | ||
* Add new route | ||
* | ||
* @method add | ||
* @param {String} method | ||
* @param {String} path | ||
* @param {Function|GeneratorFunction} handler | ||
*/ | ||
Router.prototype.add = function add(method, path, handler) { | ||
for (let i = 0, l = path.length; i < l; i++) { | ||
// `:` | ||
if (path.charCodeAt(i) === 58) { | ||
// Count params | ||
var count = -1; | ||
// Store param keys | ||
var keys = []; | ||
if (handler) handler.keys = keys; | ||
var i = 0; | ||
var l = path.length; | ||
var ch = undefined; | ||
for (; i < l; ++i) { | ||
ch = path.charCodeAt(i); | ||
if (ch === 58 /*':'*/) { | ||
// Param start index | ||
var j = i + 1; | ||
count++; | ||
this.insert(method, path.substring(0, i), null, PNODE); | ||
// `/` | ||
for (; i < l && path.charCodeAt(i) !== 47; i++) {} | ||
// for (; i < l && (path.charCodeAt(i) ^ 47); i++) {} | ||
for (; i < l && path.charCodeAt(i) !== 47 /*'/'*/; ++i) {} | ||
// Create param key `$n` | ||
var param = '$' + count; | ||
var prefix = path.substring(0, j) + param; | ||
// Store original param key | ||
keys.push(path.substring(j, i)); | ||
// Override path | ||
path = prefix + path.substring(i); | ||
// Override i, l | ||
i = prefix.length; | ||
l = path.length; | ||
if (i === l) { | ||
this.insert(method, path.substring(0, i), handler, SNODE); | ||
this.insert(method, path.substring(0, i), handler, 0); | ||
return; | ||
} | ||
this.insert(method, path.substring(0, i), null, SNODE); | ||
// `*` | ||
} else if (path.charCodeAt(i) === 42) { | ||
this.insert(method, path.substring(0, i), handler, ANODE); | ||
this.insert(method, path.substring(0, i), null, 0); | ||
} else if (ch === 42 /*'*'*/) { | ||
this.insert(method, path.substring(0, i), null, CNODE); | ||
this.insert(method, path.substring(0, l), handler, 0); | ||
} | ||
} | ||
this.insert(method, path, handler, SNODE); | ||
this.insert(method, path, handler, SNODE, true); | ||
}; | ||
/** | ||
* Insert new route | ||
* | ||
* @method insert | ||
* @private | ||
* @param {String} method | ||
* @param {String} path | ||
* @param {Function|GeneratorFunction} handler | ||
* @param {Number} has | ||
* @param {Boolean} [bool=false] if bool is true, unshift it to edges | ||
*/ | ||
Router.prototype.insert = function insert(method, path, handler, has) { | ||
let cn = this.trees[method]; // Current node as root | ||
let search = path; | ||
var bool = arguments[4] === undefined ? false : arguments[4]; | ||
var cn = this.trees[method]; // Current node as root | ||
var search = path; | ||
while (true) { | ||
let sl = search.length; | ||
let pl = cn.prefix.length; | ||
let l = lcp(search, cn.prefix); | ||
var sl = search.length; | ||
var pl = cn.prefix.length; | ||
var l = lcp(search, cn.prefix); | ||
@@ -100,6 +173,5 @@ if (l === 0) { | ||
} | ||
return; | ||
} else if (l < pl) { | ||
// Split the node | ||
let n = new Node(cn.prefix.substring(l), cn.has, cn.handler, cn.edges); | ||
// Split node | ||
var n = new Node(cn.prefix.substring(l), cn.has, cn.handler, cn.edges); | ||
cn.edges = [n]; // Add to parent | ||
@@ -110,3 +182,3 @@ | ||
cn.prefix = cn.prefix.substring(0, l); | ||
cn.has = SNODE; | ||
cn.has = 0; | ||
cn.handler = null; | ||
@@ -119,16 +191,18 @@ | ||
// Need to fork a node | ||
let n = new Node(search.substring(l), has, handler, null); | ||
cn.edges.push(n); | ||
var _n = new Node(search.substring(l), has, handler); | ||
// cn.edges.push(n); | ||
cn.edges[bool ? 'unshift' : 'push'](_n); | ||
} | ||
break; | ||
} else if (l < sl) { | ||
search = search.substring(l); | ||
let e = cn.findEdge(search.charCodeAt(0)); | ||
var e = cn.findEdge(search.charCodeAt(0)); | ||
if (e) { | ||
// Go deeper | ||
cn = e; | ||
} else { | ||
let n = new Node(search, has, handler, []); | ||
cn.edges.push(n); | ||
break; | ||
continue; | ||
} | ||
// Create child node | ||
var n = new Node(search, has, handler); | ||
// cn.edges.push(n); | ||
cn.edges[bool ? 'unshift' : 'push'](n); | ||
} else { | ||
@@ -139,13 +213,24 @@ // Node already exists | ||
} | ||
break; | ||
} | ||
return; | ||
} | ||
}; | ||
/** | ||
* Find route by method and path | ||
* | ||
* @method find | ||
* @param {String} method | ||
* @param {String} path | ||
* @return {Array} result | ||
* @property {NULL|Function|GeneratorFunction} result[0] | ||
* @property {Array} result[1] | ||
*/ | ||
Router.prototype.find = function find(method, path) { | ||
let cn = this.trees[method]; // Current node as root | ||
let search = path; | ||
let n = 0; // Param count | ||
let result = [null, []]; | ||
let params = result[1]; | ||
var cn = this.trees[method]; // Current node as root | ||
var search = path; | ||
var n = 0; // Param count | ||
var result = Array(2); | ||
var params = result[1] = []; | ||
@@ -158,48 +243,59 @@ while (true) { | ||
result[1] = params; | ||
if (cn.handler) { | ||
var keys = cn.handler.keys; | ||
for (var _i = 0, _l = keys.length; _i < _l; ++_i) { | ||
params[_i].name = keys[_i]; | ||
} | ||
} | ||
return result; | ||
} | ||
let pl = cn.prefix.length; | ||
let l = lcp(search, cn.prefix); | ||
var pl = cn.prefix.length; | ||
var l = lcp(search, cn.prefix); | ||
var e = undefined; | ||
if (l === pl) { | ||
search = search.substring(l); | ||
if (cn.has === PNODE) { | ||
// Param node | ||
cn = cn.edges[0]; | ||
l = search.length; | ||
// `/` | ||
for (var i = 0; i < l && search.charCodeAt(i) !== 47; i++) {} | ||
// for (var i = 0; i < l && (search.charCodeAt(i) ^ 47); i++) {} | ||
} | ||
params[n] = { | ||
name: cn.prefix.substring(1), | ||
value: search.substring(0, i) | ||
}; | ||
n++; | ||
// Search SNODE | ||
e = cn.findEdge(search.charCodeAt(0)); | ||
if (e) { | ||
cn = e; | ||
continue; | ||
} | ||
search = search.substring(i); | ||
} else if (cn.has === ANODE) { | ||
// Catch-all node | ||
params[n] = { | ||
name: cn.prefix.substring(1), | ||
value: search | ||
}; | ||
search = ''; // End search | ||
} | ||
// Search PNODE | ||
e = cn.findEdge(58 /*':'*/); | ||
if (e) { | ||
cn = e; | ||
l = search.length; | ||
for (var i = 0; i < l && search.charCodeAt(i) !== 47 /*'/'*/; i++) {} | ||
// search === '' | ||
if (search.length === 0) { | ||
continue; | ||
} | ||
params[n] = { | ||
name: e.prefix.substring(1), | ||
value: search.substring(0, i) | ||
}; | ||
n++; | ||
// Dig more | ||
let e = cn.findEdge(search.charCodeAt(0)); | ||
if (!e) { | ||
// Not found | ||
return result; | ||
} | ||
search = search.substring(i); | ||
continue; | ||
} | ||
// Search CNODE | ||
e = cn.findEdge(42 /*'*'*/); | ||
if (e) { | ||
cn = e; | ||
// Catch-all node | ||
params[n] = { | ||
name: '_name', | ||
value: search | ||
}; | ||
search = ''; // End search | ||
} | ||
if (search.length === 0) { | ||
continue; | ||
} | ||
return result; | ||
@@ -214,5 +310,5 @@ } | ||
function lcp(a, b) { | ||
let i = 0; | ||
let max = Math.min(a.length, b.length); | ||
for (; i < max && a.charCodeAt(i) === b.charCodeAt(i); i++) {} | ||
var i = 0; | ||
var max = min(a.length, b.length); | ||
for (; i < max && a.charCodeAt(i) === b.charCodeAt(i); ++i) {} | ||
return i; | ||
@@ -219,0 +315,0 @@ } |
{ | ||
"name": "trek-router", | ||
"version": "0.0.13", | ||
"description": "A fast HTTP router router", | ||
"version": "0.1.0", | ||
"description": "A fast HTTP router", | ||
"repository": "trekjs/router", | ||
@@ -24,7 +24,8 @@ "main": "lib/router.js", | ||
"devDependencies": { | ||
"babel": "^5.x", | ||
"babel": "^5.0.12", | ||
"benchmark": "^1.0.0", | ||
"isparta": "^3.0.0", | ||
"isparta": "^3.x", | ||
"istanbul": "^0.3.13", | ||
"mocha": "^2.2.1", | ||
"lodash": "^3.6.0", | ||
"mocha": "^2.2.4", | ||
"path-to-regexp": "^1.0.3", | ||
@@ -31,0 +32,0 @@ "route-recognizer": "^0.1.5", |
@@ -16,3 +16,3 @@ # trek-router | ||
**trek-router** VS | ||
**VS** | ||
@@ -27,12 +27,12 @@ * [path-to-regexp][] | ||
trek-router x 2,953 ops/sec ±0.82% (86 runs sampled) | ||
memoryUsage: { rss: 41099264, heapTotal: 27330560, heapUsed: 12131640 } | ||
path-to-regexp x 398 ops/sec ±0.91% (84 runs sampled) | ||
memoryUsage: { rss: 44023808, heapTotal: 31446272, heapUsed: 13515744 } | ||
route-recognizer x 280 ops/sec ±1.81% (79 runs sampled) | ||
memoryUsage: { rss: 63115264, heapTotal: 51041024, heapUsed: 18087664 } | ||
route-trie x 1,098 ops/sec ±1.16% (86 runs sampled) | ||
memoryUsage: { rss: 64200704, heapTotal: 51041024, heapUsed: 20905648 } | ||
routington x 1,156 ops/sec ±1.56% (87 runs sampled) | ||
memoryUsage: { rss: 64397312, heapTotal: 51041024, heapUsed: 12043280 } | ||
trek-router x 5,236 ops/sec ±0.37% (102 runs sampled) | ||
memoryUsage: { rss: 39653376, heapTotal: 31446272, heapUsed: 12252760 } | ||
path-to-regexp x 434 ops/sec ±3.19% (90 runs sampled) | ||
memoryUsage: { rss: 59383808, heapTotal: 50021120, heapUsed: 19588840 } | ||
route-recognizer x 336 ops/sec ±2.33% (92 runs sampled) | ||
memoryUsage: { rss: 62365696, heapTotal: 53104896, heapUsed: 20683920 } | ||
route-trie x 1,105 ops/sec ±4.55% (86 runs sampled) | ||
memoryUsage: { rss: 64360448, heapTotal: 54136832, heapUsed: 28538568 } | ||
routington x 1,237 ops/sec ±0.54% (100 runs sampled) | ||
memoryUsage: { rss: 67698688, heapTotal: 58252544, heapUsed: 18660040 } | ||
Fastest is trek-router | ||
@@ -58,2 +58,3 @@ ``` | ||
// Not Found | ||
let result = r.find('GET', '/photos/233') | ||
@@ -60,0 +61,0 @@ // => [handler, params] |
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
12089
263
81
10
1