|
/* |
|
This file is part of the Andromeda codebase. Licensed under GPL v.2. |
|
|
|
https://github.com/andromeda/andromeda/blob/master/src/utils/utils.js |
|
https://github.com/andromeda/andromeda/blob/master/src/utils/serialization.js |
|
*/ |
|
"use strict"; |
|
|
|
|
|
var url = require('url'); |
|
var crypto = require('crypto'); |
|
|
|
var regex = { |
|
IPv4: /^(?!.*\.$)((1?\d?\d|25[0-5]|2[0-4]\d)(\.|$)){4}$/ |
|
}; |
|
|
|
var Color = { |
|
BOLD: [1, 22] |
|
, ITALIC: [3, 23] |
|
, UNDERLINE: [4, 24] |
|
, INVERSE: [7, 27] |
|
, WHITE: [37, 39] |
|
, GREY: [90, 39] |
|
, BLACK: [30, 39] |
|
, BLUE: [34, 39] |
|
, CYAN: [36, 39] |
|
, GREEN: [32, 39] |
|
, MAGENTA: [35, 39] |
|
, RED: [31, 39] |
|
, YELLOW: [33, 39] |
|
}; |
|
|
|
Color.ize = function (color, s) { |
|
return "\u001b[" + color[0] + "m" + s + "\u001b[" + color[1] + "m"; |
|
}; |
|
|
|
var C256 = { |
|
SGREY: [60, 39] // #708090, slategray |
|
, BEIGE: [231, 39] // #f8f8f2, beige |
|
, PINK: [204, 39] // #f92672, pink |
|
, MOEUVE: [147, 39] // #ae81ff, moeuve |
|
, LIMGRN: [149, 39] // #a6e22e, limegreen |
|
, MSTRD: [222, 39] // #e6db74, mustard |
|
, CYAN: [117, 39] // #66d9ef, cyan |
|
, ORANGE: [215, 39] // #fd971f, orange |
|
, BOLD: [1, 22] |
|
, UNDERLINE: [4, 24] |
|
}; |
|
|
|
C256.ize = function (color, s) { |
|
if (color[0] < 10) { |
|
return "\u001b[" + color[0] + "m" + s + "\u001b[" + color[1] + "m"; |
|
} |
|
return '\u001b[38;5;' + color[0] + 'm' + s + '\u001b['+ color[1] + 'm'; |
|
}; |
|
|
|
C256.l8r = function (color) { |
|
return function (s) { |
|
if (color[0] < 10) { |
|
return "\u001b[" + color[0] + "m" + s + "\u001b[" + color[1] + "m"; |
|
} |
|
return '\u001b[38;5;' + color[0] + 'm' + s + '\u001b['+ color[1] + 'm'; |
|
}; |
|
}; |
|
|
|
Color.printSpectrum = function () { |
|
for (var c in Color) { |
|
if (require('util').isArray(Color[c])) { |
|
console.log(Color.ize(Color[c], ('this is ' + c))); |
|
} |
|
} |
|
}; |
|
|
|
function coerce(val) { |
|
if (val instanceof Error) return val.stack || val.message; |
|
return val; |
|
} |
|
|
|
// taken from lodash |
|
function isNative(value) { |
|
// Used to resolve the internal `[[Class]]` of values |
|
var toString = Object.prototype.toString; |
|
|
|
// Used to resolve the decompiled source of functions |
|
var fnToString = Function.prototype.toString; |
|
|
|
// Used to detect host constructors (Safari > 4; really typed array specific) |
|
var reHostCtor = /^\[object .+?Constructor\]$/; |
|
|
|
// Compile a regexp using a common native method as a template. |
|
// We chose `Object#toString` because there's a good chance it is not being mucked with. |
|
var reNative = RegExp('^' + |
|
// Coerce `Object#toString` to a string |
|
String(toString) |
|
// Escape any special regexp characters |
|
.replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&') |
|
// Replace mentions of `toString` with `.*?` to keep the template generic. |
|
// Replace thing like `for ...` to support environments like Rhino which add extra info |
|
// such as method arity. |
|
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' |
|
); |
|
var type = typeof value; |
|
if (type === 'function') { |
|
// Use `Function#toString` to bypass the value's own `toString` method |
|
// and avoid being faked out. |
|
return reNative.test(fnToString.call(value)); |
|
} else { |
|
// Fallback to a host object check because some environments will represent |
|
// things like typed arrays as DOM methods which may not conform to the |
|
// normal native pattern. |
|
return (value && type === 'object' && reHostCtor.test(toString.call(value))); |
|
} |
|
} |
|
|
|
function addForward(uri1, uri2) { |
|
var destructured = url.parse(uri1, true); |
|
destructured.query.forward = uri2; |
|
delete destructured.search; |
|
return url.format(destructured); |
|
} |
|
|
|
function splitIntoForward(uri) { |
|
var u = url.parse(uri, true); |
|
if (u.query && u.query.forward) { |
|
var s = u.query.forward; |
|
delete u.query.forward; |
|
delete u.search; |
|
return {uri: url.format(u), forward: s}; |
|
} |
|
return uri; |
|
} |
|
|
|
function multiForward(listUris) { |
|
if (!(listUris instanceof Array) || (listUris.length < 1)) { |
|
throw new Error('multiForward argument is not an array (or is empty)'); |
|
} |
|
var slice = listUris.slice(1); |
|
var suffix = (slice.length === 1)? slice : multiForward(slice); |
|
return addForward(listUris[0], suffix); |
|
} |
|
|
|
var timestamp = function timestamp() { |
|
var d = new Date(); |
|
var time = [d.getHours(), d.getMinutes(), d.getSeconds(), ("000" + d.getMilliseconds()).slice(-3)].join(':'); |
|
return '[' + time + ']'; |
|
}; |
|
|
|
var generateError = function (where) { |
|
var backtrace = new Error(); |
|
return function(err) { |
|
if (err) { |
|
backtrace.stack = err.name + ': ' + err.message + |
|
backtrace.stack.substr(backtrace.name.length); |
|
throw backtrace; |
|
} |
|
}; |
|
}; |
|
|
|
function ensureCallback(cb) { |
|
return typeof cb === 'function' ? cb : function () {}; |
|
} |
|
|
|
var typeOf = function(v) { |
|
return ({}).toString.call(v).match(/\s([a-zA-Z]+)/)[1].toLowerCase(); |
|
}; |
|
|
|
var toHumanReadable = function(bytes) { |
|
if (bytes < 1024) { |
|
return bytes + " B"; |
|
} |
|
var exp = Math.floor(Math.log(bytes) / Math.log(1024)); |
|
var pre = "KMGTPE".charAt(exp-1); |
|
return require('util').format("%d%sB", (bytes / Math.pow(1024, exp)).toFixed(1), pre); |
|
}; |
|
|
|
// (placeholder) -- modified from https://gist.github.com/jed/982883 |
|
var uuid4 = function (a) { |
|
return a? (a^crypto.randomBytes(1)[0] % 16 >> a/4).toString(16) : |
|
([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid4); |
|
}; |
|
|
|
var uuid5 = function (str) { |
|
var digest = crypto.createHash('sha256').update(str).digest("hex"); |
|
digest = digest.slice(0, 8) + '-' + digest.slice(9, 13) + '-5' +digest.slice(14, 17) + '-' + digest.slice(18, 22) + '-' + digest.slice(22, 34); |
|
return digest; |
|
}; |
|
|
|
var isNode = function (obj) { |
|
return (obj && obj.host && obj.port && typeof obj.host === 'string' && typeof obj.port === 'number'); |
|
}; |
|
|
|
var toNodeId = function (obj) { |
|
if (!isNode(obj)) { |
|
return null; |
|
} |
|
return uuid5(obj.host + ':' + obj.port); |
|
}; |
|
|
|
var toNode = function (obj) { |
|
if (!isNode(obj)) { |
|
return null; |
|
} |
|
return {host: obj.host, port: obj.port}; |
|
}; |
|
|
|
var toArgs = function (varargs) { |
|
return arguments; |
|
}; |
|
|
|
var toCall = function (obj) { |
|
if (typeof obj !== 'object') { |
|
return false; |
|
} |
|
var result = []; |
|
for (var k in obj) { |
|
var num = Number.parseInt(k, 10); |
|
if (num.toString() === k) { |
|
result[num] = obj[k]; |
|
} else { |
|
return false; |
|
} |
|
} |
|
if (result.length === Object.keys(obj).length) { |
|
return result; |
|
} else { |
|
return false; |
|
} |
|
}; |
|
|
|
var getIP4 = function () { |
|
//based on http://stackoverflow.com/a/8440736 |
|
var ifaces = require('os').networkInterfaces(); |
|
var res = []; |
|
|
|
Object.keys(ifaces).forEach(function (ifname) { |
|
var alias = 0; |
|
|
|
ifaces[ifname].forEach(function (iface) { |
|
if ('IPv4' !== iface.family || iface.internal !== false) { |
|
// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses |
|
return; |
|
} |
|
|
|
if (alias >= 1) { |
|
// this single interface has multiple ipv4 addresses |
|
res.push({alias: alias, address: iface.address}); |
|
} else { |
|
// this interface has only one ipv4 adress |
|
res.push({address: iface.address}); |
|
} |
|
++alias; |
|
}); |
|
}); |
|
return res; |
|
}; |
|
|
|
var defaultOpts = function (obj, prop, df) { |
|
// if not object (i.e., a string or regex or..) then assign to p the value |
|
// if null and there is a default value, use this! |
|
var p = (typeOf(obj) !== 'object' && typeOf(obj) !== 'undefined' && |
|
typeOf(obj) !== 'null') ? obj : ((obj && obj[prop]) ? obj[prop] : df); |
|
obj = (typeOf(obj) === 'object')? obj : {}; |
|
obj[prop] = p; |
|
return obj; |
|
}; |
|
|
|
var defaultCallback = function (cb, df) { |
|
return cb || df || function () {}; |
|
}; |
|
|
|
// colors are pluggable |
|
// fixme -- simply add a beautify options that expands newline |
|
var stringify = function(code, options) { |
|
var defaultOptions = { |
|
name: '', |
|
depth: 0, |
|
expandBoxedPrimitives: false, |
|
traverseHierarchy: false, |
|
overwritePrompt: false, |
|
colors: false |
|
}; |
|
|
|
defaultOptions.palette = [ |
|
'str', 'symbol', 'fun', 'num', 'null', 'undefined', 'boxed', 'regexp', 'key' |
|
].reduce(function (o, i) { |
|
// each type gets a function that simply returns the contents |
|
o[i] = function (s) {return s;}; |
|
return o; |
|
}, {}); |
|
|
|
options = merge(defaultOptions, options); |
|
var overwrite = options.overwritePrompt; |
|
options.overwritePrompt = false; |
|
var s; |
|
if (typeof code === 'function') { |
|
// fixme for built-ins, we can't get a name; could have a lookup map |
|
s = code.toString(); |
|
if (options.colors) { |
|
s = options.palette.fun(s); |
|
} |
|
} else if (typeof code === 'string') { |
|
s = '"' + code.toString() + '"'; |
|
if (options.colors) { |
|
s = options.palette.str(s); |
|
} |
|
} else if (typeof code === 'number') { |
|
s = code.toString(); |
|
if (options.colors) { |
|
s = options.palette.num(s); |
|
} |
|
} else if (typeof code === 'object') { |
|
var sty = typeOf(code); |
|
if (sty === 'array') { |
|
s = '['; |
|
var cm = (code.length > 3)? ',\n' : ', '; |
|
for (var i = 0; i < code.length; i++) { |
|
s += stringify(code[i], options) + cm; |
|
} |
|
s = (code.length > 0)? s.slice(0, s.length - 2) + ']' : '[]'; |
|
} else if (sty === 'object' || sty === 'arguments') { |
|
s = '{'; |
|
var num = 0; |
|
var v; |
|
// checking if there is budget for a single line using a heuristic: |
|
// lineSize - sizeOfKeys - (sizeOfFirstValue * numberOfValues) |
|
var budget = 100 - Object.keys(code).join(' ').length - |
|
(stringify(code[Object.keys(code)[0]], options).length * |
|
Object.keys(code).length); |
|
|
|
|
|
for (var key in code) { |
|
if (!options.traverseHierarchy && !code.hasOwnProperty(key)) { |
|
continue; |
|
} |
|
v = stringify(code[key], options); |
|
v = (v.split('\n').length > 1)? ('\n' + v.replace(/^/gm,' ')) : v; |
|
var sk = options.colors? options.palette.key('"'+key+'"') : key; |
|
var comma = (budget < 0 || Object.keys(code).length > 2)? ',\n' : ', '; |
|
s += (num === 0? '' : ' ') + sk + ': ' + v + comma; |
|
num++; |
|
} |
|
var l = (s.length - 2 <= 2)? 1 : s.length - 2; |
|
s = s.slice(0, l) + ((l == 1)? '}' : '}'); |
|
} else if (typeOf(code) === 'null') { |
|
s = 'null'; |
|
if (options.colors) { |
|
s = options.palette.null(s); |
|
} |
|
} else if (typeOf(code) === 'regexp') { |
|
s = code.toString(); |
|
if (options.colors) { |
|
s = options.palette.regexp(s); |
|
} |
|
// arguments object, date, other built-in objects |
|
} else { |
|
if (options.expandBoxedPrimitives) { |
|
s = 'new ' + typeOf(code).charAt(0).toUpperCase() + |
|
typeOf(code).slice(1) + '("' + code.toString() + '");'; |
|
if (options.colors) { |
|
s = options.palette.boxed(s); |
|
} |
|
|
|
} else { |
|
s = '"' + code.toString() + '"'; |
|
} |
|
} |
|
} else if (typeof code === 'symbol') { |
|
s = code.toString(); |
|
if (options.colors) { |
|
s = options.palette.symbol(s); |
|
} |
|
} else if (typeof code === 'boolean') { |
|
s = code.toString(); |
|
if (options.colors) { |
|
s = options.palette.boolean(s); |
|
} |
|
} else { |
|
if (code === undefined) { |
|
s = "undefined"; |
|
if (options.colors) { |
|
s = options.palette.undefined(s); |
|
} |
|
} else if (code === null) { |
|
s = "null"; |
|
if (options.colors) { |
|
s = options.palette.null(s); |
|
} |
|
} else { |
|
if (code.toString) { |
|
s = code.toString(); |
|
} else { |
|
s = 'EXCEPTION'; |
|
// what to do if it does not have toString? |
|
} |
|
|
|
} |
|
} |
|
return padString(s, overwrite); |
|
}; |
|
|
|
|
|
var typeAsString = function (o, x) { |
|
if (o === undefined) { |
|
return ''; |
|
} |
|
|
|
var MAX_LINES = 1; |
|
var MAX_LENGTH = 72; |
|
|
|
var ty = typeOf(o); |
|
x = x || ' :: '; |
|
o = stringify(o); |
|
if (o.length > MAX_LENGTH) { |
|
o = o.slice(0, 20) + '[..truncated..]' + o.slice(-20); |
|
o = o.replace(/\s/, ' '); |
|
} |
|
var lines = o.split('\n'); |
|
// if longer than 8 lines |
|
|
|
var res = []; |
|
if (lines.length > MAX_LINES) { |
|
for (var i = 0; i < Math.floor(MAX_LINES / 2); i++) { |
|
res.push(lines[i]); |
|
} |
|
res.push('[..multiple lines..]'); |
|
for (i = lines.length - Math.floor(MAX_LINES / 2); i < lines.length; i++) { |
|
res.push(lines[i]); |
|
} |
|
} else { |
|
res = lines; |
|
} |
|
|
|
o = '@base ' + res.join('\n') + x + ty; |
|
return Color.ize(Color.GREY, o); |
|
}; |
|
|
|
var isCyclic = function (obj) { |
|
try { |
|
JSON.stringify(obj); |
|
return false; |
|
} catch (e) { |
|
return true; |
|
} |
|
}; |
|
|
|
var detectCycle = function (obj) { |
|
var seenObjects = []; |
|
function detect (obj) { |
|
if (!isCyclic(obj)) { |
|
return {isCyclic: false}; |
|
} |
|
if (seenObjects.indexOf(obj) !== -1) { |
|
return true; |
|
} |
|
seenObjects.push(obj); |
|
for (var key in obj) { |
|
if (obj.hasOwnProperty(key) && detect(obj[key])) { |
|
return {isCyclic: true, obj: obj, key: key}; |
|
} |
|
} |
|
} |
|
return detect(obj); |
|
}; |
|
|
|
var clone = function(src) { |
|
return JSON.parse(JSON.stringify(src)); |
|
}; |
|
|
|
var _checkCircular = function(e, o1, o2) { |
|
if (e.name === 'RangeError') { |
|
var inspect = require('util').inspect; |
|
return (inspect(o1) === inspect(o2)); |
|
} |
|
return false; |
|
}; |
|
|
|
var equal = function (o1, o2) { |
|
try { |
|
require('assert').deepEqual(o1, o2); |
|
return true; |
|
} catch (e) { |
|
return _checkCircular(e, o1, o2); |
|
} |
|
}; |
|
|
|
var strictEqual = function (o1, o2) { |
|
try { |
|
require('assert').deepStrictEqual(o1, o2); |
|
return true; |
|
} catch (e) { |
|
return _checkCircular(e, o1, o2); |
|
} |
|
}; |
|
|
|
// on type mismatch, use approriate resolution or, by default, return o1 -- so |
|
// that defaults are never overwritten! |
|
// but if type is the same (and not object -- i.e., no recursion) pick o2! |
|
// options: |
|
// * onTypeMismatch: what do do if the two objects are not of the same type |
|
// * onType[x] = function (o1, o2): what to do on type mismatches |
|
// * onCycle: what to do if any of the object has a cycle |
|
// * clone: [false] clone object before anything |
|
var merge = function (o1, o2, options) { |
|
options = options || {}; |
|
|
|
// merge(1, [1,2,3]) -> [1,2,3] |
|
if (typeOf(o1) !== typeOf(o2)) { |
|
if (options.onTypeMismatch) { |
|
return options.onTypeMismatch(o1, o2); |
|
} |
|
return o1; |
|
} |
|
|
|
if (isCyclic(o1) || isCyclic(o2)) { |
|
if (options.onCycle) { |
|
return options.onCycle(o1, o2); |
|
} |
|
return o1; |
|
} |
|
|
|
if (options.clone) { |
|
o1 = clone(o1); |
|
o2 = clone(o2); |
|
} |
|
|
|
// merge(1, 2) -> 2 |
|
if (typeOf(o1) !== 'object') { |
|
// at this point o1 and o2 have the same type |
|
if (options.onType && options.onType[typeOf(o1)]) { |
|
return options.onType[typeOf(o1)](o1, o2); |
|
} |
|
return o2; |
|
} |
|
|
|
var result = options.clone? clone(o1) : o1; |
|
|
|
if (options.onType && options.onType['object']) { |
|
// user can decide whether to use pre-populated 'result' |
|
return options.onType['object'](o1, o2, options, result); |
|
} |
|
|
|
options.clone = false; |
|
for (var k in o2) { |
|
// we won't keep cloning recursively! |
|
result[k] = result[k]? merge(result[k], o2[k], options) : o2[k]; |
|
} |
|
return result; |
|
}; |
|
|
|
// Accept a string of the forms: |
|
// * '[.]one.two.three[.]' |
|
// * '[/]one/two/three[/]' |
|
// ..and creates a similar property on the value |
|
var createProperty = function (obj, propertyString, value) { |
|
var props; |
|
if (propertyString.indexOf('.') >= 0) { |
|
props = propertyString.replace(/^\.|\.$/g, '').split('.'); |
|
} else if (propertyString.indexOf('/') >= 0) { |
|
props = propertyString.replace(/^\/|\/$/g, '').split('/'); |
|
} else { |
|
props = [propertyString]; |
|
} |
|
|
|
var tmp = obj; |
|
for (var i =0; i < props.length; i++) { |
|
if (!tmp.hasOwnProperty(props[i]) || (typeof tmp[props[i]] !== 'object')) { |
|
tmp[props[i]] = {}; |
|
if ((i === props.length - 1) && (arguments.length === 3)) { |
|
tmp[props[i]] = value; |
|
} |
|
} |
|
tmp = tmp[props[i]]; |
|
} |
|
|
|
return obj; |
|
}; |
|
|
|
var unix = { |
|
// fixme: need to add asynchronous |
|
exec: function (cmd) { |
|
if (/^win/.test(require('os').platform)) { |
|
return new Error('Not on Unix'); |
|
} |
|
var exec = require('child_process').execSync; |
|
var s; |
|
try { |
|
s = exec(cmd, {stdio: ['pipe', 'pipe', 'stdio']}).toString(); |
|
return s; |
|
} catch (e) { |
|
e.stdio = s; |
|
return e; |
|
} |
|
} |
|
}; |
|
|
|
var percentOf = function (part, total) { |
|
return ((part / total) * 100).toFixed(2) + "%" |
|
}; |
|
|
|
var padString = function (str, len, c) { |
|
c = c || ' '; |
|
return len? ((str.length < len) ? padString(str + c, len) : str) : str; |
|
}; |
|
|
|
var escape = function (key) { |
|
// we are encoding spaces, slashes etc. |
|
return encodeURIComponent(key); |
|
}; |
|
|
|
var unescape = function (key) { |
|
// we are encoding spaces, slashes etc. |
|
return encodeURIComponent(key); |
|
}; |
|
|
|
var getStats = function (arr) { |
|
// trivial stats -- to be extended soon |
|
var sum = 0; |
|
for (var i = 0; i < arr.length; i++) { |
|
sum += arr[i]; |
|
} |
|
return {total: arr.length, avg: sum / arr.length} |
|
}; |
|
|
|
var toMicros = function (t) { |
|
return (t[0] * 1000000 + t[1]/1000); |
|
} |
|
|
|
var toMillis = function (t) { |
|
return (t[0] * 1000 + t[1]/1000000); |
|
} |
|
|
|
var defOpts = { |
|
name: '', |
|
expandBoxedPrimitives: false, |
|
colors: false |
|
}; |
|
|
|
var readFrom = function readFrom(code, options) { |
|
// in any other case return the value |
|
if (typeof code !== 'string') { |
|
return code; |
|
} |
|
options = merge(defOpts, options); |
|
var Module = module.constructor; |
|
var path = require('path'); |
|
var paths = Module._nodeModulePaths(path.dirname(options.name)); |
|
|
|
var m = new Module(options.name, module.parent); |
|
//TODO, this might actually require a resolution protocol |
|
m.filename = options.name; |
|
m.paths = [].concat(paths); |
|
m._compile(code, options.name); |
|
|
|
return m.exports; |
|
}; |
|
|
|
// offer a minification option |
|
var storeOn = function storeOn (code, options) { |
|
options = merge(defOpts, options); |
|
var s = stringify(code, options); |
|
|
|
if (options.wrap) { |
|
s = 'function (andromeda) {\n return ' + s + '}'; |
|
} |
|
|
|
if (options.context && typeof options.context === 'object') { |
|
var ctx = 'function (andromeda) {\n var context = ' + |
|
stringify(options.context).replace(/\n/g, '\n ') + ';'; |
|
s = s.replace(/^.*/, ctx); |
|
} |
|
|
|
if (options.name && typeof options.name === 'string' && options.name !== '') { |
|
return 'module.exports.' + options.name + ' = ' + s + ';'; |
|
} else { |
|
return 'module.exports = ' + s + ';'; |
|
} |
|
}; |
|
|
|
// Impl. 1 |
|
var Transform = require('stream').Transform; |
|
require('util').inherits(Identity, Transform); |
|
var instances = 0; |
|
var ids = []; |
|
function Identity(options, tOptions) { |
|
if (!(this instanceof Identity)) |
|
return new Identity(tOptions); |
|
Transform.call(this, tOptions); |
|
// stream statistics |
|
// pattern match stream for end to output stats to stderr |
|
this._options = options; |
|
this._counter = 0; |
|
this._extras = ''; |
|
this._reported = false; |
|
this.totalLatency = -99; |
|
this.id = 0; |
|
}; |
|
|
|
Identity.prototype._transform = function(chunk, encoding, done) { |
|
//console.error(chunk.toString().length) |
|
// pattern match stream for end to output stats to stderr |
|
//log('\n pushing from options.port' + this._options.port); |
|
if (this._options.once) { |
|
if (this.totalLatency === -99) { |
|
console.log(this._options.once) |
|
this.totalLatency = process.hrtime(); |
|
this.id = instances; |
|
ids[instances] = this.totalLatency; |
|
console.log('=', ids); |
|
console.log('start time', this.totalLatency); |
|
instances++; |
|
} |
|
} else { |
|
if (!this._reported) { |
|
this._reported = true; |
|
var t = process.hrtime(); |
|
var s = "\n" + this._options.port.toString() + ' start:' + this._options.tag + " [" + t.toString() + "]\n"; |
|
logAsync(s); |
|
} |
|
} |
|
this._counter++; |
|
this.push(chunk); |
|
done(); |
|
}; |
|
|
|
Identity.prototype._flush = function(done) { |
|
if (this._options.once) { |
|
// it seems it's flushing twice? |
|
if (instances > 1) { |
|
instances--; |
|
} else { |
|
console.log(instances, ids[0]); |
|
console.log(instances, 'instance diff', process.hrtime(this.totalLatency)); |
|
console.log(instances, 'maxx diff', process.hrtime(ids[0])); |
|
} |
|
} else { |
|
var t = process.hrtime(); |
|
console.error(this._counter, t, this._options); |
|
// TODO add "generator" for generator stage, so that we parse it later |
|
var s = "\n" + this._options.port.toString() + ' end:' + this._options.tag + " [" + t.toString() + "]\n" |
|
logAsync(s); |
|
} |
|
//this.push(null); |
|
done(); |
|
}; |
|
|
|
//// Impl. 2 |
|
//var id = { |
|
// transform: function(chunk, encoding, next) { |
|
// //console.error(chunk.toString().length) |
|
// //this.push(chunk) |
|
// // sets this._transform under the hood |
|
// // generate output as many times as needed |
|
// // this.push(chunk); |
|
// // call when the current chunk is consumed |
|
// next(); |
|
// }, |
|
// flush: function(done) { |
|
// // sets this._flush under the hood |
|
// // generate output as many times as needed |
|
// //console.error('done') |
|
// this.push('done'); |
|
// done(); |
|
// } |
|
//} |
|
////var T = stream.Transform; |
|
////var transform = new T(id); |
|
|
|
var toEnum = function (arr) { |
|
return arr.reduce(function (obj, e) { |
|
obj[e.toString().toUpperCase()] = e; |
|
return obj; |
|
}, {}) |
|
} |
|
|
|
var logFile = 'results.txt'; |
|
var log = function (data, f) { |
|
f = f || logFile |
|
try { |
|
require('fs').appendFileSync(f, stringify(data)); |
|
} catch (e) { |
|
require('fs').appendFile('SOMETHING-WENT-TERRIBLY-WRONG', (e.toString() + e.stack.toString())); |
|
} |
|
|
|
}; |
|
|
|
var logAsync = function (data, f) { |
|
f = f || logFile |
|
require('fs').appendFile(f, stringify(data)); |
|
}; |
|
|
|
// from: github.com/firefoxes/diff-hrtimear |
|
var diffHrtime = function(b, a){ |
|
var as = a[0], ans = a[1], |
|
bs = b[0], bns = b[1], |
|
ns = ans - bns, |
|
s = as - bs; |
|
if (ns < 0) { |
|
s -= 1 |
|
ns += 1e9 |
|
} |
|
return [s, ns] |
|
}; |
|
|
|
module.exports.regex = regex; |
|
// FIXME: utils should be able to choose, given a param |
|
module.exports.Color = Color; |
|
module.exports.C256 = C256; |
|
module.exports.coerce = coerce; |
|
module.exports.isNative = isNative; |
|
module.exports.addForward = addForward; |
|
module.exports.multiForward = multiForward; |
|
module.exports.splitIntoForward = splitIntoForward; |
|
module.exports.timestamp = timestamp; |
|
module.exports.generateError = generateError; |
|
module.exports.ensureCallback = ensureCallback; |
|
module.exports.typeOf = typeOf; |
|
module.exports.toHumanReadable = toHumanReadable; |
|
module.exports.uuid4 = uuid4; |
|
module.exports.uuid5 = uuid5; |
|
module.exports.isNode = isNode; |
|
module.exports.toNodeId = toNodeId; |
|
module.exports.toNode = toNode; |
|
module.exports.toArgs = toArgs; |
|
module.exports.toCall = toCall; |
|
module.exports.getIP4 = getIP4; |
|
module.exports.defaultCallback = defaultCallback; |
|
module.exports.defaultOpts = defaultOpts; |
|
module.exports.stringify = stringify; |
|
module.exports.typeAsString = typeAsString; |
|
module.exports.detectCycle = detectCycle; |
|
module.exports.equal = equal; |
|
module.exports.clone = clone; |
|
module.exports.createProperty = createProperty; |
|
module.exports.unix = unix; |
|
module.exports.percentOf = percentOf; |
|
module.exports.padString = padString; |
|
module.exports.escape = escape; |
|
module.exports.unescape = unescape; |
|
module.exports.getStats = getStats; |
|
module.exports.toMicros = toMicros; |
|
module.exports.toMillis = toMillis; |
|
module.exports.storeOn = storeOn; |
|
module.exports.readFrom = readFrom; |
|
module.exports.Identity = Identity; |
|
module.exports.toEnum = toEnum; |
|
module.exports.merge = merge; |
|
module.exports.log = log; |
|
module.exports.logAsync = logAsync; |
|
module.exports.diffHrtime = diffHrtime; |