From f92e96f8c8307b89c64a06cb5eb1da0122d13732 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 23:21:44 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/ui/cordova/www/js/jamlib.browser.debug.js | 30434 +++++++++++++++++ 1 file changed, 30434 insertions(+) create mode 100644 js/ui/cordova/www/js/jamlib.browser.debug.js diff --git a/js/ui/cordova/www/js/jamlib.browser.debug.js b/js/ui/cordova/www/js/jamlib.browser.debug.js new file mode 100644 index 0000000..907fcfa --- /dev/null +++ b/js/ui/cordova/www/js/jamlib.browser.debug.js @@ -0,0 +1,30434 @@ +var CoreModule = {}; +CoreModule['com/io']='com/io.browser'; +CoreModule['crypto']='os/crypto'; +CoreModule['util']='os/util'; +CoreModule['http']='os/http.browser'; +CoreModule['url']='os/url'; +CoreModule['path']='os/path'; +CoreModule['string_decoder']='os/string_decoder'; +CoreModule['fs']=''; +CoreModule['stream']=''; +CoreModule['zlib']=''; +CoreModule['dgram']=''; +CoreModule['net']=''; +CoreModule['child_process']=''; + +var BundleModuleCode=[]; +var BundleObjectCode=[]; +var BundleModules = []; +PATH=[".","/home/sbosse/proj/jam/js"]; +if (typeof global == "undefined") global=(typeof window != "undefined"?window:{}) +if (typeof process == "undefined") var process={}; +Require=function(modupath) { + if (CoreModule[modupath]!=undefined) modupath=CoreModule[modupath]; + if (modupath=='') return undefined; + if (BundleModules[modupath]) return BundleModules[modupath]; + var exports={}, module={exports:exports}; + if (BundleModuleCode[modupath]) BundleModuleCode[modupath](module,exports,window,process); + else if (BundleObjectCode[modupath]) BundleObjectCode[modupath](module,exports,window,process); + else return undefined; + BundleModules[modupath]=module.exports||module; + return module.exports||module;}; +var FilesEmbedded = {}; +var FileEmbedd = function (path,format) {}; +var FileEmbedded = function (path,format) {return FilesEmbedded[path](format);}; +global.TARGET='browser'; + +BundleModuleCode['com/io.browser']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: sbosse on 28-3-15. + ** $VERSION: 1.10.1 + ** + ** $INFO: + * + * This module encapsulates all IO operations (except networking) supporting + * browser applications. + * + ** $ENDOFINFO + */ +/* +************ +** Browser +************ +*/ +var tracing = true; +var stderr_fun = function (str) { console.log(str); }; +var stdout_fun = function (str) { console.log(str); }; +var args=[]; +var inspect = Require('os/inspect'); + +Require('os/polyfill') + +global.checkOptions = function(options,defaultOptions) { + return Object.assign({}, defaultOptions||{}, options) }; +global.checkOption = function (option,defaultOption) { + return option==undefined? defaultOption:option }; + +var io = { + /* + ************ + ** Browser + ************ + */ + /* + ** FILE IO + * TODO WebStorage + */ + close: function (fd) { + return; + }, + exists: function (path) { + return false; + }, + open: function (path, mode) { + var fd = Fs.openSync(path, mode); + return fd; + }, + + read: function (fd, len, foff) { + // TODO + }, + read_file: function (path) { + return ''; + }, + + read_line: function (fd) { + // TODO + }, + /** + * + * @param fd + * @param buf + * @param boff + * @param len + * @param [foff] + * @returns {*} + */ + read_buf: function (fd, buf, boff, len, foff) { + return -1; + }, + sync: function (fd) { + return; + }, + /** + * + * @param fd + * @param data + * @param [foff] + * @returns {*} + */ + write: function (fd, data, foff) { + return -1; + }, + /** + * + * @param fd + * @param buf + * @param bpos + * @param blen + * @param [foff] + * @returns {*} + */ + write_buf: function (fd, buf, bpos, blen, foff) { + return -1; + }, + + /* + ** CONSOLE IO + */ + debug: function (msg) { + stderr_fun('Debug: ' + msg); + }, + err: function (msg) { + stderr_fun('Error: ' + msg); + throw Error(msg); + }, + fail: function (msg) { + stderr_fun('Fatal Error: ' + msg); + }, + inspect: function (obj) { + return inspect(obj); + }, + stacktrace: function () { + var e = new Error('dummy'); + var stack = e.stack.replace(/^[^\(]+?[\n$]/gm, '') + .replace(/^\s+at\s+/gm, '') + .replace(/^Object.\s*\(/gm, '{anonymous}()@') + .split('\n'); + stderr_fun('Stack Trace'); + stderr_fun('--------------------------------'); + for(var i in stack) { + if (i>0) { + var line = stack[i]; + if(line.indexOf('Module.',0)>=0) break; + stderr_fun(line); + } + } + stderr_fun('--------------------------------'); + }, + /** + * + * @param e + * @param where + */ + printstack: function (e,where) { + if (where==undefined) stderr_fun(e); + else stderr_fun(where+': '+e); + }, + sprintstack: function (e) { + return e?e.toString():'' + }, + /** + * + * @param {boolean|string} condmsg conditional message var log=X; log((log lt. N)||(msg)) + */ + log: function (condmsg) { + if (condmsg != true) console.warn(condmsg); + }, + out: function (msg) { + stdout_fun(msg) + }, + warn: function (msg) { + stderr_fun('Warning: ' + msg); + }, + + + set_stderr: function(fun) { + stderr_fun=fun; + }, + set_stdout: function(fun) { + stdout_fun=fun; + }, + + stderr: function (msg) { + stderr_fun(msg); + }, + stdout: function (msg) { + stdout_fun(msg); + }, + + /** Write a message with a time stamp written to the trace file. + * + * @param {boolean|string} condmsg conditional message var trace=Io.tracing; trace(trace||(msg)) + */ + trace: function (condmsg) { + if (condmsg != true && tracefile != undefined) { + var date = new Date(); + var time = date.getTime(); + this.log('[' + time + '] ' + condmsg + '\n'); + } + }, + tracing: tracing, + /** + * + * @param {string} path + */ + trace_open: function (path) { + return undefined; + }, + + exit: function (n) { + return; + }, + getenv: function (name, def) { + return def; + }, + workdir: function () { + return ''; + }, + /** + * @return {string []} + */ + getargs: function () { + return args; + }, + set_args: function (argv) { + args=argv; + }, + + sleep: function(delay) { + var start = new Date().getTime(); + while (new Date().getTime() < start + delay); + }, + + /** Return system time in milliseconds + */ + time: function () { + var date = new Date(); + return date.getTime(); + }, + + /** + ** Return current time in hour:minute:second format + */ + Time: function () + { + var now = new Date(); + var hour = "0" + now.getHours(); + hour = hour.substring(hour.length-2); + var minute = "0" + now.getMinutes(); + minute = minute.substring(minute.length-2); + var second = "0" + now.getSeconds(); + second = second.substring(second.length-2); + return hour + ":" + minute + ":" + second; + }, + /** + ** Return current date in year-month-day format + */ + Date: function () + { + var now = new Date(); + var year = "" + now.getFullYear(); + var month = "0" + (now.getMonth()+1); + month = month.substring(month.length-2); + var date = "0" + now.getDate(); + date = date.substring(date.length-2); + return year + "-" + month + "-" + date; + }, + +}; + +module.exports = io; +}; +BundleModuleCode['os/inspect']=function (module,exports,global,process){ + +/** + * Module dependencies. + */ + +var map = function(array, callback) { + var length = array.length, + i = -1, + il = length - 1, + results = new Array(length); + while (i++ < il) { + results[i] = callback(array[i], i, array); + } + return results; +} +var indexOf = function(arr, obj){ + if (arr.indexOf) return arr.indexOf(obj); + for (var i = 0; i < arr.length; ++i) { + if (arr[i] === obj) return i; + } + return -1; +}; +var str = Object.prototype.toString; +var isArray = isArray || function (val) { + return !! val && '[object Array]' == str.call(val); +}; +var forEach = function (ary, callback, thisArg) { + if (ary.forEach) { + ary.forEach(callback, thisArg); + return; + } + for (var i = 0; i < ary.length; i+=1) { + callback.call(thisArg, ary[i], i, ary); + } +}; +var _hasOwn = Object.prototype.hasOwnProperty; + +var reduce = function (xs, f, acc) { + var hasAcc = arguments.length >= 3; + if (hasAcc && xs.reduce) return xs.reduce(f, acc); + if (xs.reduce) return xs.reduce(f); + for (var i = 0; i < xs.length; i++) { + if (!_hasOwn.call(xs, i)) continue; + if (!hasAcc) { + acc = xs[i]; + hasAcc = true; + continue; + } + acc = f(acc, xs[i], i); + } + return acc; +}; +var getObjectKeys = Require('os/object-keys'); +var JSON = Require('os/json3'); + +/** + * Make sure `Object.keys` work for `undefined` + * values that are still there, like `document.all`. + * http://lists.w3.org/Archives/Public/public-html/2009Jun/0546.html + * + * @api private + */ + +function objectKeys(val){ + if (Object.keys) return Object.keys(val); + return getObjectKeys(val); +} + +/** + * Module exports. + */ + +module.exports = inspect; + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + * @license MIT (© Joyent) + */ +/* legacy: obj, showHidden, depth, colors*/ + +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + _extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + '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] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + +function stylizeNoColor(str, styleType) { + return str; +} + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} + +function isUndefined(arg) { + return arg === void 0; +} + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isString(arg) { + return typeof arg === 'string'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isNull(arg) { + return arg === null; +} + +function hasOwn(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +function arrayToHash(array) { + var hash = {}; + + forEach(array, function(val, idx) { + hash[val] = true; + }); + + return hash; +} + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwn(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + forEach(keys, function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = objectKeys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden && Object.getOwnPropertyNames) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (indexOf(keys, 'message') >= 0 || indexOf(keys, 'description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = map(keys, function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = { value: value[key] }; + if (Object.getOwnPropertyDescriptor) { + desc = Object.getOwnPropertyDescriptor(value, key) || desc; + } + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwn(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (indexOf(ctx.seen, desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = map(str.split('\n'), function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + map(str.split('\n'), function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = reduce(output, function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + +function _extend(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = objectKeys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +} +}; +BundleModuleCode['os/object-keys']=function (module,exports,global,process){ +'use strict'; + +// modified from https://github.com/es-shims/es5-shim +var has = Object.prototype.hasOwnProperty; +var toStr = Object.prototype.toString; +var slice = Array.prototype.slice; +var toStr = Object.prototype.toString; +var isArgs = function (value) { + var str = toStr.call(value); + var isArgs = str === '[object Arguments]'; + if (!isArgs) { + isArgs = str !== '[object Array]' && + value !== null && + typeof value === 'object' && + typeof value.length === 'number' && + value.length >= 0 && + toStr.call(value.callee) === '[object Function]'; + } + return isArgs; +}; +var isEnumerable = Object.prototype.propertyIsEnumerable; +var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString'); +var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype'); +var dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' +]; +var equalsConstructorPrototype = function (o) { + var ctor = o.constructor; + return ctor && ctor.prototype === o; +}; +var excludedKeys = { + $console: true, + $external: true, + $frame: true, + $frameElement: true, + $frames: true, + $innerHeight: true, + $innerWidth: true, + $outerHeight: true, + $outerWidth: true, + $pageXOffset: true, + $pageYOffset: true, + $parent: true, + $scrollLeft: true, + $scrollTop: true, + $scrollX: true, + $scrollY: true, + $self: true, + $webkitIndexedDB: true, + $webkitStorageInfo: true, + $window: true +}; +var hasAutomationEqualityBug = (function () { + /* global window */ + if (typeof window === 'undefined') { return false; } + for (var k in window) { + try { + if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') { + try { + equalsConstructorPrototype(window[k]); + } catch (e) { + return true; + } + } + } catch (e) { + return true; + } + } + return false; +}()); +var equalsConstructorPrototypeIfNotBuggy = function (o) { + /* global window */ + if (typeof window === 'undefined' || !hasAutomationEqualityBug) { + return equalsConstructorPrototype(o); + } + try { + return equalsConstructorPrototype(o); + } catch (e) { + return false; + } +}; + +var keysShim = function keys(object) { + var isObject = object !== null && typeof object === 'object'; + var isFunction = toStr.call(object) === '[object Function]'; + var isArguments = isArgs(object); + var isString = isObject && toStr.call(object) === '[object String]'; + var theKeys = []; + + if (!isObject && !isFunction && !isArguments) { + throw new TypeError('Object.keys called on a non-object'); + } + + var skipProto = hasProtoEnumBug && isFunction; + if (isString && object.length > 0 && !has.call(object, 0)) { + for (var i = 0; i < object.length; ++i) { + theKeys.push(String(i)); + } + } + + if (isArguments && object.length > 0) { + for (var j = 0; j < object.length; ++j) { + theKeys.push(String(j)); + } + } else { + for (var name in object) { + if (!(skipProto && name === 'prototype') && has.call(object, name)) { + theKeys.push(String(name)); + } + } + } + + if (hasDontEnumBug) { + var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object); + + for (var k = 0; k < dontEnums.length; ++k) { + if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) { + theKeys.push(dontEnums[k]); + } + } + } + return theKeys; +}; + +keysShim.shim = function shimObjectKeys() { + if (Object.keys) { + var keysWorksWithArguments = (function () { + // Safari 5.0 bug + return (Object.keys(arguments) || '').length === 2; + }(1, 2)); + if (!keysWorksWithArguments) { + var originalKeys = Object.keys; + Object.keys = function keys(object) { // eslint-disable-line func-name-matching + if (isArgs(object)) { + return originalKeys(slice.call(object)); + } else { + return originalKeys(object); + } + }; + } + } else { + Object.keys = keysShim; + } + return Object.keys || keysShim; +}; + +module.exports = keysShim; +}; +BundleModuleCode['os/json3']=function (module,exports,global,process){ +/*! JSON v3.3.2 | https://bestiejs.github.io/json3 | Copyright 2012-2015, Kit Cambridge, Benjamin Tan | http://kit.mit-license.org */ +;(function () { + // Detect the `define` function exposed by asynchronous module loaders. The + // strict `define` check is necessary for compatibility with `r.js`. + var isLoader = typeof define === "function" && define.amd; + + // A set of types used to distinguish objects from primitives. + var objectTypes = { + "function": true, + "object": true + }; + + // Detect the `exports` object exposed by CommonJS implementations. + var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; + + // Use the `global` object exposed by Node (including Browserify via + // `insert-module-globals`), Narwhal, and Ringo as the default context, + // and the `window` object in browsers. Rhino exports a `global` function + // instead. + var root = objectTypes[typeof window] && window || this, + freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == "object" && global; + + if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) { + root = freeGlobal; + } + + // Public: Initializes JSON 3 using the given `context` object, attaching the + // `stringify` and `parse` functions to the specified `exports` object. + function runInContext(context, exports) { + context || (context = root.Object()); + exports || (exports = root.Object()); + + // Native constructor aliases. + var Number = context.Number || root.Number, + String = context.String || root.String, + Object = context.Object || root.Object, + Date = context.Date || root.Date, + SyntaxError = context.SyntaxError || root.SyntaxError, + TypeError = context.TypeError || root.TypeError, + Math = context.Math || root.Math, + nativeJSON = context.JSON || root.JSON; + + // Delegate to the native `stringify` and `parse` implementations. + if (typeof nativeJSON == "object" && nativeJSON) { + exports.stringify = nativeJSON.stringify; + exports.parse = nativeJSON.parse; + } + + // Convenience aliases. + var objectProto = Object.prototype, + getClass = objectProto.toString, + isProperty = objectProto.hasOwnProperty, + undefined; + + // Internal: Contains `try...catch` logic used by other functions. + // This prevents other functions from being deoptimized. + function attempt(func, errorFunc) { + try { + func(); + } catch (exception) { + if (errorFunc) { + errorFunc(); + } + } + } + + // Test the `Date#getUTC*` methods. Based on work by @Yaffle. + var isExtended = new Date(-3509827334573292); + attempt(function () { + // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical + // results for certain dates in Opera >= 10.53. + isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && + isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; + }); + + // Internal: Determines whether the native `JSON.stringify` and `parse` + // implementations are spec-compliant. Based on work by Ken Snyder. + function has(name) { + if (has[name] != null) { + // Return cached feature test result. + return has[name]; + } + var isSupported; + if (name == "bug-string-char-index") { + // IE <= 7 doesn't support accessing string characters using square + // bracket notation. IE 8 only supports this for primitives. + isSupported = "a"[0] != "a"; + } else if (name == "json") { + // Indicates whether both `JSON.stringify` and `JSON.parse` are + // supported. + isSupported = has("json-stringify") && has("date-serialization") && has("json-parse"); + } else if (name == "date-serialization") { + // Indicates whether `Date`s can be serialized accurately by `JSON.stringify`. + isSupported = has("json-stringify") && isExtended; + if (isSupported) { + var stringify = exports.stringify; + attempt(function () { + isSupported = + // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly + // serialize extended years. + stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && + // The milliseconds are optional in ES 5, but required in 5.1. + stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && + // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative + // four-digit years instead of six-digit years. Credits: @Yaffle. + stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && + // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond + // values less than 1000. Credits: @Yaffle. + stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; + }); + } + } else { + var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}'; + // Test `JSON.stringify`. + if (name == "json-stringify") { + var stringify = exports.stringify, stringifySupported = typeof stringify == "function"; + if (stringifySupported) { + // A test function object with a custom `toJSON` method. + (value = function () { + return 1; + }).toJSON = value; + attempt(function () { + stringifySupported = + // Firefox 3.1b1 and b2 serialize string, number, and boolean + // primitives as object literals. + stringify(0) === "0" && + // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object + // literals. + stringify(new Number()) === "0" && + stringify(new String()) == '""' && + // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or + // does not define a canonical JSON representation (this applies to + // objects with `toJSON` properties as well, *unless* they are nested + // within an object or array). + stringify(getClass) === undefined && + // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and + // FF 3.1b3 pass this test. + stringify(undefined) === undefined && + // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, + // respectively, if the value is omitted entirely. + stringify() === undefined && + // FF 3.1b1, 2 throw an error if the given value is not a number, + // string, array, object, Boolean, or `null` literal. This applies to + // objects with custom `toJSON` methods as well, unless they are nested + // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` + // methods entirely. + stringify(value) === "1" && + stringify([value]) == "[1]" && + // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of + // `"[null]"`. + stringify([undefined]) == "[null]" && + // YUI 3.0.0b1 fails to serialize `null` literals. + stringify(null) == "null" && + // FF 3.1b1, 2 halts serialization if an array contains a function: + // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3 + // elides non-JSON values from objects and arrays, unless they + // define custom `toJSON` methods. + stringify([undefined, getClass, null]) == "[null,null,null]" && + // Simple serialization test. FF 3.1b1 uses Unicode escape sequences + // where character escape codes are expected (e.g., `\b` => `\u0008`). + stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized && + // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. + stringify(null, value) === "1" && + stringify([1, 2], null, 1) == "[\n 1,\n 2\n]"; + }, function () { + stringifySupported = false; + }); + } + isSupported = stringifySupported; + } + // Test `JSON.parse`. + if (name == "json-parse") { + var parse = exports.parse, parseSupported; + if (typeof parse == "function") { + attempt(function () { + // FF 3.1b1, b2 will throw an exception if a bare literal is provided. + // Conforming implementations should also coerce the initial argument to + // a string prior to parsing. + if (parse("0") === 0 && !parse(false)) { + // Simple parsing test. + value = parse(serialized); + parseSupported = value["a"].length == 5 && value["a"][0] === 1; + if (parseSupported) { + attempt(function () { + // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. + parseSupported = !parse('"\t"'); + }); + if (parseSupported) { + attempt(function () { + // FF 4.0 and 4.0.1 allow leading `+` signs and leading + // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow + // certain octal literals. + parseSupported = parse("01") !== 1; + }); + } + if (parseSupported) { + attempt(function () { + // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal + // points. These environments, along with FF 3.1b1 and 2, + // also allow trailing commas in JSON objects and arrays. + parseSupported = parse("1.") !== 1; + }); + } + } + } + }, function () { + parseSupported = false; + }); + } + isSupported = parseSupported; + } + } + return has[name] = !!isSupported; + } + has["bug-string-char-index"] = has["date-serialization"] = has["json"] = has["json-stringify"] = has["json-parse"] = null; + + if (!has("json")) { + // Common `[[Class]]` name aliases. + var functionClass = "[object Function]", + dateClass = "[object Date]", + numberClass = "[object Number]", + stringClass = "[object String]", + arrayClass = "[object Array]", + booleanClass = "[object Boolean]"; + + // Detect incomplete support for accessing string characters by index. + var charIndexBuggy = has("bug-string-char-index"); + + // Internal: Normalizes the `for...in` iteration algorithm across + // environments. Each enumerated key is yielded to a `callback` function. + var forOwn = function (object, callback) { + var size = 0, Properties, members, property; + + // Tests for bugs in the current environment's `for...in` algorithm. The + // `valueOf` property inherits the non-enumerable flag from + // `Object.prototype` in older versions of IE, Netscape, and Mozilla. + (Properties = function () { + this.valueOf = 0; + }).prototype.valueOf = 0; + + // Iterate over a new instance of the `Properties` class. + members = new Properties(); + for (property in members) { + // Ignore all properties inherited from `Object.prototype`. + if (isProperty.call(members, property)) { + size++; + } + } + Properties = members = null; + + // Normalize the iteration algorithm. + if (!size) { + // A list of non-enumerable properties inherited from `Object.prototype`. + members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"]; + // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable + // properties. + forOwn = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, length; + var hasProperty = !isFunction && typeof object.constructor != "function" && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty; + for (property in object) { + // Gecko <= 1.0 enumerates the `prototype` property of functions under + // certain conditions; IE does not. + if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) { + callback(property); + } + } + // Manually invoke the callback for each non-enumerable property. + for (length = dontEnums.length; property = dontEnums[--length];) { + if (hasProperty.call(object, property)) { + callback(property); + } + } + }; + } else { + // No bugs detected; use the standard `for...in` algorithm. + forOwn = function (object, callback) { + var isFunction = getClass.call(object) == functionClass, property, isConstructor; + for (property in object) { + if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) { + callback(property); + } + } + // Manually invoke the callback for the `constructor` property due to + // cross-environment inconsistencies. + if (isConstructor || isProperty.call(object, (property = "constructor"))) { + callback(property); + } + }; + } + return forOwn(object, callback); + }; + + // Public: Serializes a JavaScript `value` as a JSON string. The optional + // `filter` argument may specify either a function that alters how object and + // array members are serialized, or an array of strings and numbers that + // indicates which properties should be serialized. The optional `width` + // argument may be either a string or number that specifies the indentation + // level of the output. + if (!has("json-stringify") && !has("date-serialization")) { + // Internal: A map of control characters and their escaped equivalents. + var Escapes = { + 92: "\\\\", + 34: '\\"', + 8: "\\b", + 12: "\\f", + 10: "\\n", + 13: "\\r", + 9: "\\t" + }; + + // Internal: Converts `value` into a zero-padded string such that its + // length is at least equal to `width`. The `width` must be <= 6. + var leadingZeroes = "000000"; + var toPaddedString = function (width, value) { + // The `|| 0` expression is necessary to work around a bug in + // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`. + return (leadingZeroes + (value || 0)).slice(-width); + }; + + // Internal: Serializes a date object. + var serializeDate = function (value) { + var getData, year, month, date, time, hours, minutes, seconds, milliseconds; + // Define additional utility methods if the `Date` methods are buggy. + if (!isExtended) { + var floor = Math.floor; + // A mapping between the months of the year and the number of days between + // January 1st and the first of the respective month. + var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; + // Internal: Calculates the number of days between the Unix epoch and the + // first day of the given month. + var getDay = function (year, month) { + return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); + }; + getData = function (value) { + // Manually compute the year, month, date, hours, minutes, + // seconds, and milliseconds if the `getUTC*` methods are + // buggy. Adapted from @Yaffle's `date-shim` project. + date = floor(value / 864e5); + for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++); + for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++); + date = 1 + date - getDay(year, month); + // The `time` value specifies the time within the day (see ES + // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used + // to compute `A modulo B`, as the `%` operator does not + // correspond to the `modulo` operation for negative numbers. + time = (value % 864e5 + 864e5) % 864e5; + // The hours, minutes, seconds, and milliseconds are obtained by + // decomposing the time within the day. See section 15.9.1.10. + hours = floor(time / 36e5) % 24; + minutes = floor(time / 6e4) % 60; + seconds = floor(time / 1e3) % 60; + milliseconds = time % 1e3; + }; + } else { + getData = function (value) { + year = value.getUTCFullYear(); + month = value.getUTCMonth(); + date = value.getUTCDate(); + hours = value.getUTCHours(); + minutes = value.getUTCMinutes(); + seconds = value.getUTCSeconds(); + milliseconds = value.getUTCMilliseconds(); + }; + } + serializeDate = function (value) { + if (value > -1 / 0 && value < 1 / 0) { + // Dates are serialized according to the `Date#toJSON` method + // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15 + // for the ISO 8601 date time string format. + getData(value); + // Serialize extended years correctly. + value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + + "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) + + // Months, dates, hours, minutes, and seconds should have two + // digits; milliseconds should have three. + "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) + + // Milliseconds are optional in ES 5.0, but required in 5.1. + "." + toPaddedString(3, milliseconds) + "Z"; + year = month = date = hours = minutes = seconds = milliseconds = null; + } else { + value = null; + } + return value; + }; + return serializeDate(value); + }; + + // For environments with `JSON.stringify` but buggy date serialization, + // we override the native `Date#toJSON` implementation with a + // spec-compliant one. + if (has("json-stringify") && !has("date-serialization")) { + // Internal: the `Date#toJSON` implementation used to override the native one. + function dateToJSON (key) { + return serializeDate(this); + } + + // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. + var nativeStringify = exports.stringify; + exports.stringify = function (source, filter, width) { + var nativeToJSON = Date.prototype.toJSON; + Date.prototype.toJSON = dateToJSON; + var result = nativeStringify(source, filter, width); + Date.prototype.toJSON = nativeToJSON; + return result; + } + } else { + // Internal: Double-quotes a string `value`, replacing all ASCII control + // characters (characters with code unit values between 0 and 31) with + // their escaped equivalents. This is an implementation of the + // `Quote(value)` operation defined in ES 5.1 section 15.12.3. + var unicodePrefix = "\\u00"; + var escapeChar = function (character) { + var charCode = character.charCodeAt(0), escaped = Escapes[charCode]; + if (escaped) { + return escaped; + } + return unicodePrefix + toPaddedString(2, charCode.toString(16)); + }; + var reEscape = /[\x00-\x1f\x22\x5c]/g; + var quote = function (value) { + reEscape.lastIndex = 0; + return '"' + + ( + reEscape.test(value) + ? value.replace(reEscape, escapeChar) + : value + ) + + '"'; + }; + + // Internal: Recursively serializes an object. Implements the + // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations. + var serialize = function (property, object, callback, properties, whitespace, indentation, stack) { + var value, type, className, results, element, index, length, prefix, result; + attempt(function () { + // Necessary for host object support. + value = object[property]; + }); + if (typeof value == "object" && value) { + if (value.getUTCFullYear && getClass.call(value) == dateClass && value.toJSON === Date.prototype.toJSON) { + value = serializeDate(value); + } else if (typeof value.toJSON == "function") { + value = value.toJSON(property); + } + } + if (callback) { + // If a replacement function was provided, call it to obtain the value + // for serialization. + value = callback.call(object, property, value); + } + // Exit early if value is `undefined` or `null`. + if (value == undefined) { + return value === undefined ? value : "null"; + } + type = typeof value; + // Only call `getClass` if the value is an object. + if (type == "object") { + className = getClass.call(value); + } + switch (className || type) { + case "boolean": + case booleanClass: + // Booleans are represented literally. + return "" + value; + case "number": + case numberClass: + // JSON numbers must be finite. `Infinity` and `NaN` are serialized as + // `"null"`. + return value > -1 / 0 && value < 1 / 0 ? "" + value : "null"; + case "string": + case stringClass: + // Strings are double-quoted and escaped. + return quote("" + value); + } + // Recursively serialize objects and arrays. + if (typeof value == "object") { + // Check for cyclic structures. This is a linear search; performance + // is inversely proportional to the number of unique nested objects. + for (length = stack.length; length--;) { + if (stack[length] === value) { + // Cyclic structures cannot be serialized by `JSON.stringify`. + throw TypeError(); + } + } + // Add the object to the stack of traversed objects. + stack.push(value); + results = []; + // Save the current indentation level and indent one additional level. + prefix = indentation; + indentation += whitespace; + if (className == arrayClass) { + // Recursively serialize array elements. + for (index = 0, length = value.length; index < length; index++) { + element = serialize(index, value, callback, properties, whitespace, indentation, stack); + results.push(element === undefined ? "null" : element); + } + result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]"; + } else { + // Recursively serialize object members. Members are selected from + // either a user-specified list of property names, or the object + // itself. + forOwn(properties || value, function (property) { + var element = serialize(property, value, callback, properties, whitespace, indentation, stack); + if (element !== undefined) { + // According to ES 5.1 section 15.12.3: "If `gap` {whitespace} + // is not the empty string, let `member` {quote(property) + ":"} + // be the concatenation of `member` and the `space` character." + // The "`space` character" refers to the literal space + // character, not the `space` {width} argument provided to + // `JSON.stringify`. + results.push(quote(property) + ":" + (whitespace ? " " : "") + element); + } + }); + result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}"; + } + // Remove the object from the traversed object stack. + stack.pop(); + return result; + } + }; + + // Public: `JSON.stringify`. See ES 5.1 section 15.12.3. + exports.stringify = function (source, filter, width) { + var whitespace, callback, properties, className; + if (objectTypes[typeof filter] && filter) { + className = getClass.call(filter); + if (className == functionClass) { + callback = filter; + } else if (className == arrayClass) { + // Convert the property names array into a makeshift set. + properties = {}; + for (var index = 0, length = filter.length, value; index < length;) { + value = filter[index++]; + className = getClass.call(value); + if (className == "[object String]" || className == "[object Number]") { + properties[value] = 1; + } + } + } + } + if (width) { + className = getClass.call(width); + if (className == numberClass) { + // Convert the `width` to an integer and create a string containing + // `width` number of space characters. + if ((width -= width % 1) > 0) { + if (width > 10) { + width = 10; + } + for (whitespace = ""; whitespace.length < width;) { + whitespace += " "; + } + } + } else if (className == stringClass) { + whitespace = width.length <= 10 ? width : width.slice(0, 10); + } + } + // Opera <= 7.54u2 discards the values associated with empty string keys + // (`""`) only if they are used directly within an object member list + // (e.g., `!("" in { "": 1})`). + return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []); + }; + } + } + + // Public: Parses a JSON source string. + if (!has("json-parse")) { + var fromCharCode = String.fromCharCode; + + // Internal: A map of escaped control characters and their unescaped + // equivalents. + var Unescapes = { + 92: "\\", + 34: '"', + 47: "/", + 98: "\b", + 116: "\t", + 110: "\n", + 102: "\f", + 114: "\r" + }; + + // Internal: Stores the parser state. + var Index, Source; + + // Internal: Resets the parser state and throws a `SyntaxError`. + var abort = function () { + Index = Source = null; + throw SyntaxError(); + }; + + // Internal: Returns the next token, or `"$"` if the parser has reached + // the end of the source string. A token may be a string, number, `null` + // literal, or Boolean literal. + var lex = function () { + var source = Source, length = source.length, value, begin, position, isSigned, charCode; + while (Index < length) { + charCode = source.charCodeAt(Index); + switch (charCode) { + case 9: case 10: case 13: case 32: + // Skip whitespace tokens, including tabs, carriage returns, line + // feeds, and space characters. + Index++; + break; + case 123: case 125: case 91: case 93: case 58: case 44: + // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at + // the current position. + value = charIndexBuggy ? source.charAt(Index) : source[Index]; + Index++; + return value; + case 34: + // `"` delimits a JSON string; advance to the next character and + // begin parsing the string. String tokens are prefixed with the + // sentinel `@` character to distinguish them from punctuators and + // end-of-string tokens. + for (value = "@", Index++; Index < length;) { + charCode = source.charCodeAt(Index); + if (charCode < 32) { + // Unescaped ASCII control characters (those with a code unit + // less than the space character) are not permitted. + abort(); + } else if (charCode == 92) { + // A reverse solidus (`\`) marks the beginning of an escaped + // control character (including `"`, `\`, and `/`) or Unicode + // escape sequence. + charCode = source.charCodeAt(++Index); + switch (charCode) { + case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114: + // Revive escaped control characters. + value += Unescapes[charCode]; + Index++; + break; + case 117: + // `\u` marks the beginning of a Unicode escape sequence. + // Advance to the first character and validate the + // four-digit code point. + begin = ++Index; + for (position = Index + 4; Index < position; Index++) { + charCode = source.charCodeAt(Index); + // A valid sequence comprises four hexdigits (case- + // insensitive) that form a single hexadecimal value. + if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) { + // Invalid Unicode escape sequence. + abort(); + } + } + // Revive the escaped character. + value += fromCharCode("0x" + source.slice(begin, Index)); + break; + default: + // Invalid escape sequence. + abort(); + } + } else { + if (charCode == 34) { + // An unescaped double-quote character marks the end of the + // string. + break; + } + charCode = source.charCodeAt(Index); + begin = Index; + // Optimize for the common case where a string is valid. + while (charCode >= 32 && charCode != 92 && charCode != 34) { + charCode = source.charCodeAt(++Index); + } + // Append the string as-is. + value += source.slice(begin, Index); + } + } + if (source.charCodeAt(Index) == 34) { + // Advance to the next character and return the revived string. + Index++; + return value; + } + // Unterminated string. + abort(); + default: + // Parse numbers and literals. + begin = Index; + // Advance past the negative sign, if one is specified. + if (charCode == 45) { + isSigned = true; + charCode = source.charCodeAt(++Index); + } + // Parse an integer or floating-point value. + if (charCode >= 48 && charCode <= 57) { + // Leading zeroes are interpreted as octal literals. + if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) { + // Illegal octal literal. + abort(); + } + isSigned = false; + // Parse the integer component. + for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++); + // Floats cannot contain a leading decimal point; however, this + // case is already accounted for by the parser. + if (source.charCodeAt(Index) == 46) { + position = ++Index; + // Parse the decimal component. + for (; position < length; position++) { + charCode = source.charCodeAt(position); + if (charCode < 48 || charCode > 57) { + break; + } + } + if (position == Index) { + // Illegal trailing decimal. + abort(); + } + Index = position; + } + // Parse exponents. The `e` denoting the exponent is + // case-insensitive. + charCode = source.charCodeAt(Index); + if (charCode == 101 || charCode == 69) { + charCode = source.charCodeAt(++Index); + // Skip past the sign following the exponent, if one is + // specified. + if (charCode == 43 || charCode == 45) { + Index++; + } + // Parse the exponential component. + for (position = Index; position < length; position++) { + charCode = source.charCodeAt(position); + if (charCode < 48 || charCode > 57) { + break; + } + } + if (position == Index) { + // Illegal empty exponent. + abort(); + } + Index = position; + } + // Coerce the parsed value to a JavaScript number. + return +source.slice(begin, Index); + } + // A negative sign may only precede numbers. + if (isSigned) { + abort(); + } + // `true`, `false`, and `null` literals. + var temp = source.slice(Index, Index + 4); + if (temp == "true") { + Index += 4; + return true; + } else if (temp == "fals" && source.charCodeAt(Index + 4 ) == 101) { + Index += 5; + return false; + } else if (temp == "null") { + Index += 4; + return null; + } + // Unrecognized token. + abort(); + } + } + // Return the sentinel `$` character if the parser has reached the end + // of the source string. + return "$"; + }; + + // Internal: Parses a JSON `value` token. + var get = function (value) { + var results, hasMembers; + if (value == "$") { + // Unexpected end of input. + abort(); + } + if (typeof value == "string") { + if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") { + // Remove the sentinel `@` character. + return value.slice(1); + } + // Parse object and array literals. + if (value == "[") { + // Parses a JSON array, returning a new JavaScript array. + results = []; + for (;;) { + value = lex(); + // A closing square bracket marks the end of the array literal. + if (value == "]") { + break; + } + // If the array literal contains elements, the current token + // should be a comma separating the previous element from the + // next. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "]") { + // Unexpected trailing `,` in array literal. + abort(); + } + } else { + // A `,` must separate each array element. + abort(); + } + } else { + hasMembers = true; + } + // Elisions and leading commas are not permitted. + if (value == ",") { + abort(); + } + results.push(get(value)); + } + return results; + } else if (value == "{") { + // Parses a JSON object, returning a new JavaScript object. + results = {}; + for (;;) { + value = lex(); + // A closing curly brace marks the end of the object literal. + if (value == "}") { + break; + } + // If the object literal contains members, the current token + // should be a comma separator. + if (hasMembers) { + if (value == ",") { + value = lex(); + if (value == "}") { + // Unexpected trailing `,` in object literal. + abort(); + } + } else { + // A `,` must separate each object member. + abort(); + } + } else { + hasMembers = true; + } + // Leading commas are not permitted, object property names must be + // double-quoted strings, and a `:` must separate each property + // name and value. + if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") { + abort(); + } + results[value.slice(1)] = get(lex()); + } + return results; + } + // Unexpected token encountered. + abort(); + } + return value; + }; + + // Internal: Updates a traversed object member. + var update = function (source, property, callback) { + var element = walk(source, property, callback); + if (element === undefined) { + delete source[property]; + } else { + source[property] = element; + } + }; + + // Internal: Recursively traverses a parsed JSON object, invoking the + // `callback` function for each value. This is an implementation of the + // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2. + var walk = function (source, property, callback) { + var value = source[property], length; + if (typeof value == "object" && value) { + // `forOwn` can't be used to traverse an array in Opera <= 8.54 + // because its `Object#hasOwnProperty` implementation returns `false` + // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`). + if (getClass.call(value) == arrayClass) { + for (length = value.length; length--;) { + update(getClass, forOwn, value, length, callback); + } + } else { + forOwn(value, function (property) { + update(value, property, callback); + }); + } + } + return callback.call(source, property, value); + }; + + // Public: `JSON.parse`. See ES 5.1 section 15.12.2. + exports.parse = function (source, callback) { + var result, value; + Index = 0; + Source = "" + source; + result = get(lex()); + // If a JSON string contains multiple tokens, it is invalid. + if (lex() != "$") { + abort(); + } + // Reset the parser state. + Index = Source = null; + return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result; + }; + } + } + + exports.runInContext = runInContext; + return exports; + } + + if (freeExports && !isLoader) { + // Export for CommonJS environments. + runInContext(root, freeExports); + } else { + // Export for web browsers and JavaScript engines. + var nativeJSON = root.JSON, + previousJSON = root.JSON3, + isRestored = false; + + var JSON3 = runInContext(root, (root.JSON3 = { + // Public: Restores the original value of the global `JSON` object and + // returns a reference to the `JSON3` object. + "noConflict": function () { + if (!isRestored) { + isRestored = true; + root.JSON = nativeJSON; + root.JSON3 = previousJSON; + nativeJSON = previousJSON = null; + } + return JSON3; + } + })); + + root.JSON = { + "parse": JSON3.parse, + "stringify": JSON3.stringify + }; + } + + // Export for asynchronous module loaders. + if (isLoader) { + define(function () { + return JSON3; + }); + } +}).call(this); +}; +BundleModuleCode['os/polyfill']=function (module,exports,global,process){ +/************** ARRAY ********************/ + +if (!Array.prototype.find) { + Object.defineProperty(Array.prototype, 'find', { + value: function(predicate) { + // 1. Let O be ? ToObject(this value). + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let len be ? ToLength(? Get(O, "length")). + var len = o.length >>> 0; + + // 3. If IsCallable(predicate) is false, throw a TypeError exception. + if (typeof predicate !== 'function') { + throw new TypeError('predicate must be a function'); + } + + // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. + var thisArg = arguments[1]; + + // 5. Let k be 0. + var k = 0; + + // 6. Repeat, while k < len + while (k < len) { + // a. Let Pk be ! ToString(k). + // b. Let kValue be ? Get(O, Pk). + // c. Let testResult be ToBoolean(? Call(predicate, T, ( kValue, k, O ))). + // d. If testResult is true, return kValue. + var kValue = o[k]; + if (predicate.call(thisArg, kValue, k, o)) { + return kValue; + } + // e. Increase k by 1. + k++; + } + + // 7. Return undefined. + return undefined; + } + }); +} + +if (typeof Object.assign != 'function') { + // Must be writable: true, enumerable: false, configurable: true + Object.defineProperty(Object, "assign", { + value: function assign(target, varArgs) { // .length of function is 2 + 'use strict'; + if (target == null) { // TypeError if undefined or null + throw new TypeError('Cannot convert undefined or null to object'); + } + + var to = Object(target); + + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + + if (nextSource != null) { // Skip over if undefined or null + for (var nextKey in nextSource) { + // Avoid bugs when hasOwnProperty is shadowed + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }, + writable: true, + configurable: true + }); +} +}; +BundleModuleCode['com/path']=function (module,exports,global,process){ +var Fs = Require('fs'); + +var _process = process || {}; +(function () { + "use strict"; + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + + +var isWindows = _process.platform === 'win32'; +var util = Require('util'); +if (!util.deprecate) util.deprecate=function(f,w) {return f;}; + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + + +if (isWindows) { + // Regex to split a windows path into three parts: [*, device, slash, + // tail] windows-only + var splitDeviceRe = + /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + + // Regex to split the tail part of the above into [*, dir, basename, ext] + var splitTailRe = + /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; + + // Function to split a filename into [root, dir, basename, ext] + // windows version + var splitPath = function(filename) { + // Separate device+slash from tail + var result = splitDeviceRe.exec(filename), + device = (result[1] || '') + (result[2] || ''), + tail = result[3] || ''; + // Split the tail into dir, basename and extension + var result2 = splitTailRe.exec(tail), + dir = result2[1], + basename = result2[2], + ext = result2[3]; + return [device, dir, basename, ext]; + }; + + var normalizeUNCRoot = function(device) { + return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\'); + }; + + // path.resolve([from ...], to) + // windows version + exports.resolve = function() { + var resolvedDevice = '', + resolvedTail = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1; i--) { + var path; + if (i >= 0) { + path = arguments[i]; + } else if (!resolvedDevice) { + path = _process.cwd(); + } else { + // Windows has the concept of drive-specific current working + // directories. If we've resolved a drive letter but not yet an + // absolute path, get cwd for that drive. We're sure the device is not + // an unc path at this points, because unc paths are always absolute. + path = _process.env['=' + resolvedDevice]; + // Verify that a drive-local cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + if (!path || path.substr(0, 3).toLowerCase() !== + resolvedDevice.toLowerCase() + '\\') { + path = resolvedDevice + '\\'; + } + } + + // Skip empty and invalid entries + if (!util.isString(path)) { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = exports.isAbsolute(path), + tail = result[3]; + + if (device && + resolvedDevice && + device.toLowerCase() !== resolvedDevice.toLowerCase()) { + // This path points to another device so it is not applicable + continue; + } + + if (!resolvedDevice) { + resolvedDevice = device; + } + if (!resolvedAbsolute) { + resolvedTail = tail + '\\' + resolvedTail; + resolvedAbsolute = isAbsolute; + } + + if (resolvedDevice && resolvedAbsolute) { + break; + } + } + + // Convert slashes to backslashes when `resolvedDevice` points to an UNC + // root. Also squash multiple slashes into a single one where appropriate. + if (isUnc) { + resolvedDevice = normalizeUNCRoot(resolvedDevice); + } + + // At this point the path should be resolved to a full absolute path, + // but handle relative paths to be safe (might happen when process.cwd() + // fails) + + // Normalize the tail path + + function f(p) { + return !!p; + } + + resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f), + !resolvedAbsolute).join('\\'); + + return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || + '.'; + }; + + // windows version + exports.normalize = function(path) { + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = exports.isAbsolute(path), + tail = result[3], + trailingSlash = /[\\\/]$/.test(tail); + + // If device is a drive letter, we'll normalize to lower case. + if (device && device.charAt(1) === ':') { + device = device[0].toLowerCase() + device.substr(1); + } + + // Normalize the tail path + tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) { + return !!p; + }), !isAbsolute).join('\\'); + + if (!tail && !isAbsolute) { + tail = '.'; + } + if (tail && trailingSlash) { + tail += '\\'; + } + + // Convert slashes to backslashes when `device` points to an UNC root. + // Also squash multiple slashes into a single one where appropriate. + if (isUnc) { + device = normalizeUNCRoot(device); + } + + return device + (isAbsolute ? '\\' : '') + tail; + }; + + // windows version + exports.isAbsolute = function(path) { + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = !!device && device.charAt(1) !== ':'; + // UNC paths are always absolute + return !!result[2] || isUnc; + }; + + // windows version + exports.join = function() { + function f(p) { + if (!util.isString(p)) { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + } + + var paths = Array.prototype.filter.call(arguments, f); + var joined = paths.join('\\'); + + // Make sure that the joined path doesn't start with two slashes, because + // normalize() will mistake it for an UNC path then. + // + // This step is skipped when it is very clear that the user actually + // intended to point at an UNC path. This is assumed when the first + // non-empty string arguments starts with exactly two slashes followed by + // at least one more non-slash character. + // + // Note that for normalize() to treat a path as an UNC path it needs to + // have at least 2 components, so we don't filter for that here. + // This means that the user can use join to construct UNC paths from + // a server name and a share name; for example: + // path.join('//server', 'share') -> '\\\\server\\share\') + if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) { + joined = joined.replace(/^[\\\/]{2,}/, '\\'); + } + + return exports.normalize(joined); + }; + + // path.relative(from, to) + // it will solve the relative path from 'from' to 'to', for instance: + // from = 'C:\\orandea\\test\\aaa' + // to = 'C:\\orandea\\impl\\bbb' + // The output of the function should be: '..\\..\\impl\\bbb' + // windows version + exports.relative = function(from, to) { + from = exports.resolve(from); + to = exports.resolve(to); + + // windows is not case sensitive + var lowerFrom = from.toLowerCase(); + var lowerTo = to.toLowerCase(); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end + 1); + } + + var toParts = trim(to.split('\\')); + + var lowerFromParts = trim(lowerFrom.split('\\')); + var lowerToParts = trim(lowerTo.split('\\')); + + var length = Math.min(lowerFromParts.length, lowerToParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (lowerFromParts[i] !== lowerToParts[i]) { + samePartsLength = i; + break; + } + } + + if (samePartsLength == 0) { + return to; + } + + var outputParts = []; + for (var i = samePartsLength; i < lowerFromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('\\'); + }; + + exports.sep = '\\'; + exports.delimiter = ';'; + +} else /* posix */ { + + // Split a filename into [root, dir, basename, ext], unix version + // 'root' is just a slash, or nothing. + var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); + }; + + // path.resolve([from ...], to) + // posix version + exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : _process.cwd(); + + // Skip empty and invalid entries + if (!util.isString(path)) { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + }; + + // path.normalize(path) + // posix version + exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = path[path.length - 1] === '/', + segments = path.split('/'), + nonEmptySegments = []; + + // Normalize the path + for (var i = 0; i < segments.length; i++) { + if (segments[i]) { + nonEmptySegments.push(segments[i]); + } + } + path = normalizeArray(nonEmptySegments, !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; + }; + + // posix version + exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; + }; + + // posix version + exports.join = function() { + var path = ''; + for (var i = 0; i < arguments.length; i++) { + var segment = arguments[i]; + if (!util.isString(segment)) { + throw new TypeError('Arguments to path.join must be strings'); + } + if (segment) { + if (!path) { + path += segment; + } else { + path += '/' + segment; + } + } + } + return exports.normalize(path); + }; + + + // path.relative(from, to) + // posix version + exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); + }; + + exports.sep = '/'; + exports.delimiter = ':'; +} + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + + +exports.exists = util.deprecate(function(path, callback) { + if (Fs) Fs.exists(path, callback); + else callback(false); +}, 'path.exists is now called `fs.exists`.'); + + +exports.existsSync = util.deprecate(function(path) { + if (Fs) return Fs.existsSync(path); + else return false; +}, 'path.existsSync is now called `fs.existsSync`.'); + + +if (isWindows) { + exports._makeLong = function(path) { + // Note: this will *probably* throw somewhere. + if (!util.isString(path)) + return path; + + if (!path) { + return ''; + } + + var resolvedPath = exports.resolve(path); + + if (/^[a-zA-Z]\:\\/.test(resolvedPath)) { + // path is local filesystem path, which needs to be converted + // to long UNC path. + return '\\\\?\\' + resolvedPath; + } else if (/^\\\\[^?.]/.test(resolvedPath)) { + // path is network UNC path, which needs to be converted + // to long UNC path. + return '\\\\?\\UNC\\' + resolvedPath.substring(2); + } + + return path; + }; +} else { + exports._makeLong = function(path) { + return path; + }; +} +}()); +}; +BundleModuleCode['os/util']=function (module,exports,global,process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + '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] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +}; + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ + +exports.inherits = Require('os/inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} +}; +BundleModuleCode['os/inherits']=function (module,exports,global,process){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} +}; +BundleModuleCode['com/sprintf']=function (module,exports,global,process){ +(function(window) { + var re = { + not_string: /[^s]/, + number: /[diefg]/, + json: /[j]/, + not_json: /[^j]/, + text: /^[^\x25]+/, + modulo: /^\x25{2}/, + placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/, + key: /^([a-z_][a-z_\d]*)/i, + key_access: /^\.([a-z_][a-z_\d]*)/i, + index_access: /^\[(\d+)\]/, + sign: /^[\+\-]/ + } + + function sprintf() { + var key = arguments[0], cache = sprintf.cache + if (!(cache[key] && cache.hasOwnProperty(key))) { + cache[key] = sprintf.parse(key) + } + return sprintf.format.call(null, cache[key], arguments) + } + + sprintf.format = function(parse_tree, argv) { + var cursor = 1, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = "" + for (i = 0; i < tree_length; i++) { + node_type = get_type(parse_tree[i]) + if (node_type === "string") { + output[output.length] = parse_tree[i] + } + else if (node_type === "array") { + match = parse_tree[i] // convenience purposes only + if (match[2]) { // keyword argument + arg = argv[cursor] + for (k = 0; k < match[2].length; k++) { + if (!arg.hasOwnProperty(match[2][k])) { + throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k])) + } + arg = arg[match[2][k]] + } + } + else if (match[1]) { // positional argument (explicit) + arg = argv[match[1]] + } + else { // positional argument (implicit) + arg = argv[cursor++] + } + + if (get_type(arg) == "function") { + arg = arg() + } + + if (re.not_string.test(match[8]) && re.not_json.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) { + throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg))) + } + + if (re.number.test(match[8])) { + is_positive = arg >= 0 + } + + switch (match[8]) { + case "b": + arg = arg.toString(2) + break + case "c": + arg = String.fromCharCode(arg) + break + case "d": + case "i": + arg = parseInt(arg, 10) + break + case "j": + arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0) + break + case "e": + arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential() + break + case "f": + arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg) + break + case "g": + arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg) + break + case "o": + arg = arg.toString(8) + break + case "s": + arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg) + break + case "u": + arg = arg >>> 0 + break + case "x": + arg = arg.toString(16) + break + case "X": + arg = arg.toString(16).toUpperCase() + break + } + if (re.json.test(match[8])) { + output[output.length] = arg + } + else { + if (re.number.test(match[8]) && (!is_positive || match[3])) { + sign = is_positive ? "+" : "-" + arg = arg.toString().replace(re.sign, "") + } + else { + sign = "" + } + pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " " + pad_length = match[6] - (sign + arg).length + pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : "") : "" + output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg) + } + } + } + return output.join("") + } + + sprintf.cache = {} + + sprintf.parse = function(fmt) { + var _fmt = fmt, match = [], parse_tree = [], arg_names = 0 + while (_fmt) { + if ((match = re.text.exec(_fmt)) !== null) { + parse_tree[parse_tree.length] = match[0] + } + else if ((match = re.modulo.exec(_fmt)) !== null) { + parse_tree[parse_tree.length] = "%" + } + else if ((match = re.placeholder.exec(_fmt)) !== null) { + if (match[2]) { + arg_names |= 1 + var field_list = [], replacement_field = match[2], field_match = [] + if ((field_match = re.key.exec(replacement_field)) !== null) { + field_list[field_list.length] = field_match[1] + while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") { + if ((field_match = re.key_access.exec(replacement_field)) !== null) { + field_list[field_list.length] = field_match[1] + } + else if ((field_match = re.index_access.exec(replacement_field)) !== null) { + field_list[field_list.length] = field_match[1] + } + else { + throw new SyntaxError("[sprintf] failed to parse named argument key") + } + } + } + else { + throw new SyntaxError("[sprintf] failed to parse named argument key") + } + match[2] = field_list + } + else { + arg_names |= 2 + } + if (arg_names === 3) { + throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported") + } + parse_tree[parse_tree.length] = match + } + else { + throw new SyntaxError("[sprintf] unexpected placeholder") + } + try {_fmt = _fmt.substring(match[0].length)} catch (e) {throw new SyntaxError("[sprintf] unexpected fromat")} + } + return parse_tree + } + + var vsprintf = function(fmt, argv, _argv) { + _argv = (argv || []).slice(0) + _argv.splice(0, 0, fmt) + return sprintf.apply(null, _argv) + } + + /** + * helpers + */ + function get_type(variable) { + return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase() + } + + function str_repeat(input, multiplier) { + return Array(multiplier + 1).join(input) + } + + /** + * export to either browser or node.js + */ + if (typeof exports !== "undefined") { + exports.sprintf = sprintf + exports.vsprintf = vsprintf + } + else { + window.sprintf = sprintf + window.vsprintf = vsprintf + + if (typeof define === "function" && define.amd) { + define(function() { + return { + sprintf: sprintf, + vsprintf: vsprintf + } + }) + } + } +})(typeof window === "undefined" ? this : window); +}; +BundleModuleCode['os/base64']=function (module,exports,global,process){ +var keyStr = "ABCDEFGHIJKLMNOP" + + "QRSTUVWXYZabcdef" + + "ghijklmnopqrstuv" + + "wxyz0123456789+/" + + "="; + +var Base64 = { + encode: function (input) { + input = escape(input); + var output = ""; + var chr1, chr2, chr3 = ""; + var enc1, enc2, enc3, enc4 = ""; + var i = 0; + + do { + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + keyStr.charAt(enc1) + + keyStr.charAt(enc2) + + keyStr.charAt(enc3) + + keyStr.charAt(enc4); + chr1 = chr2 = chr3 = ""; + enc1 = enc2 = enc3 = enc4 = ""; + } while (i < input.length); + + return output; + }, + + encodeBuf: function (input) { + var output = ""; + var NaN = output.charCodeAt(2); + var chr1, chr2, chr3 = ""; + var enc1, enc2, enc3, enc4 = ""; + var i = 0; + var len = input.length; + do { + chr1 = input.readUInt8(i++); + chr2 = (i> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + keyStr.charAt(enc1) + + keyStr.charAt(enc2) + + keyStr.charAt(enc3) + + keyStr.charAt(enc4); + chr1 = chr2 = chr3 = ""; + enc1 = enc2 = enc3 = enc4 = ""; + } while (i < len); + + return output; + }, + + decode: function (input) { + var output = ""; + var chr1, chr2, chr3 = ""; + var enc1, enc2, enc3, enc4 = ""; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + do { + enc1 = keyStr.indexOf(input.charAt(i++)); + enc2 = keyStr.indexOf(input.charAt(i++)); + enc3 = keyStr.indexOf(input.charAt(i++)); + enc4 = keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + chr1 = chr2 = chr3 = ""; + enc1 = enc2 = enc3 = enc4 = ""; + + } while (i < input.length); + + return unescape(output); + }, + decodeBuf: function (input) { + var len = input.length; + var buf = new Buffer(len); + var chr1, chr2, chr3 = ""; + var enc1, enc2, enc3, enc4 = ""; + var i = 0; + var buflen = 0; + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + buf.fill(0); + do { + enc1 = keyStr.indexOf(input.charAt(i++)); + enc2 = keyStr.indexOf(input.charAt(i++)); + enc3 = keyStr.indexOf(input.charAt(i++)); + enc4 = keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + buf.writeUInt8(chr1,buflen); + buflen++; + if (enc3 != 64) { + buf.writeUInt8(chr2,buflen); + buflen++; + } + if (enc4 != 64) { + buf.writeUInt8(chr3,buflen); + buflen++; + } + + chr1 = chr2 = chr3 = ""; + enc1 = enc2 = enc3 = enc4 = ""; + + } while (i < input.length); + + return buf.slice(0,buflen); + } + +}; + + +module.exports = Base64; +}; +BundleModuleCode['os/buffer']=function (module,exports,global,process){ +var Ieee754 = Require('os/buffer_ieee754'); + +/* ------- base64-js -------- */ +var lookup = [] +var revLookup = [] +var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array + +function init () { + var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i] + revLookup[code.charCodeAt(i)] = i + } + + revLookup['-'.charCodeAt(0)] = 62 + revLookup['_'.charCodeAt(0)] = 63 +} + +init() + +function toByteArray (b64) { + var i, j, l, tmp, placeHolders, arr + var len = b64.length + + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + placeHolders = b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(len * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? len - 4 : len + + var L = 0 + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] + arr[L++] = (tmp >> 16) & 0xFF + arr[L++] = (tmp >> 8) & 0xFF + arr[L++] = tmp & 0xFF + } + + if (placeHolders === 2) { + tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[L++] = tmp & 0xFF + } else if (placeHolders === 1) { + tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[L++] = (tmp >> 8) & 0xFF + arr[L++] = tmp & 0xFF + } + + return arr +} + +function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] +} + +function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output.push(tripletToBase64(tmp)) + } + return output.join('') +} + +function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var output = '' + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + output += lookup[tmp >> 2] + output += lookup[(tmp << 4) & 0x3F] + output += '==' + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) + output += lookup[tmp >> 10] + output += lookup[(tmp >> 4) & 0x3F] + output += lookup[(tmp << 2) & 0x3F] + output += '=' + } + + parts.push(output) + + return parts.join('') +} +/* ------- base64-js -------- */ + +var assert; + +exports.Buffer = Buffer; +exports.SlowBuffer = Buffer; +Buffer.poolSize = 8192; +exports.INSPECT_MAX_BYTES = 50; + +function stringtrim(str) { + if (str.trim) return str.trim(); + return str.replace(/^\s+|\s+$/g, ''); +} + +function Buffer(subject, encoding, offset) { + if(!assert) assert= { + ok : function(cond,msg) { + if (cond != true) { + console.log('** Assertion failed: '+msg+' **'); + throw Error(msg); + } + } + }; + if (!(this instanceof Buffer)) { + return new Buffer(subject, encoding, offset); + } + this.parent = this; + this.offset = 0; + + // Work-around: node's base64 implementation + // allows for non-padded strings while base64-js + // does not.. + if (encoding == "base64" && typeof subject == "string") { + subject = stringtrim(subject); + while (subject.length % 4 != 0) { + subject = subject + "="; + } + } + + var type; + + // Are we slicing? + if (typeof offset === 'number') { + this.length = coerce(encoding); + // slicing works, with limitations (no parent tracking/update) + // check https://github.com/toots/buffer-browserify/issues/19 + for (var i = 0; i < this.length; i++) { + this[i] = subject.get(i+offset); + } + } else { + // Find the length + switch (type = typeof subject) { + case 'number': + this.length = coerce(subject); + break; + + case 'string': + this.length = Buffer.byteLength(subject, encoding); + break; + + case 'object': // Assume object is an array + this.length = coerce(subject.length); + break; + + default: + throw new TypeError('First argument needs to be a number, ' + + 'array or string.'); + } + + // Treat array-ish objects as a byte array. + if (isArrayIsh(subject)) { + for (var i = 0; i < this.length; i++) { + if (subject instanceof Buffer) { + this[i] = subject.readUInt8(i); + } + else { + // Round-up subject[i] to a UInt8. + // e.g.: ((-432 % 256) + 256) % 256 = (-176 + 256) % 256 + // = 80 + this[i] = ((subject[i] % 256) + 256) % 256; + } + } + } else if (type == 'string') { + // We are a string + this.length = this.write(subject, 0, encoding); + } else if (type === 'number') { + for (var i = 0; i < this.length; i++) { + this[i] = 0; + } + } + } +} + +Buffer.prototype.get = function get(i) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this[i]; +}; + +Buffer.prototype.set = function set(i, v) { + if (i < 0 || i >= this.length) throw new Error('oob'); + return this[i] = v; +}; + +Buffer.byteLength = function (str, encoding) { + switch (encoding || "utf8") { + case 'hex': + return str.length / 2; + + case 'utf8': + case 'utf-8': + return utf8ToBytes(str).length; + + case 'ascii': + case 'binary': + return str.length; + + case 'base64': + return base64ToBytes(str).length; + + default: + throw new Error('Unknown encoding'); + } +}; + +Buffer.prototype.utf8Write = function (string, offset, length) { + var bytes, pos; + return Buffer._charsWritten = blitBuffer(utf8ToBytes(string), this, offset, length); +}; + +Buffer.prototype.asciiWrite = function (string, offset, length) { + var bytes, pos; + return Buffer._charsWritten = blitBuffer(asciiToBytes(string), this, offset, length); +}; + +Buffer.prototype.binaryWrite = Buffer.prototype.asciiWrite; + +Buffer.prototype.base64Write = function (string, offset, length) { + var bytes, pos; + return Buffer._charsWritten = blitBuffer(base64ToBytes(string), this, offset, length); +}; + +Buffer.prototype.base64Slice = function (start, end) { + var bytes = Array.prototype.slice.apply(this, arguments) + return fromByteArray(bytes); +}; + +Buffer.prototype.utf8Slice = function () { + var bytes = Array.prototype.slice.apply(this, arguments); + var res = ""; + var tmp = ""; + var i = 0; + while (i < bytes.length) { + if (bytes[i] <= 0x7F) { + res += decodeUtf8Char(tmp) + String.fromCharCode(bytes[i]); + tmp = ""; + } else + tmp += "%" + bytes[i].toString(16); + + i++; + } + + return res + decodeUtf8Char(tmp); +} + +Buffer.prototype.asciiSlice = function () { + var bytes = Array.prototype.slice.apply(this, arguments); + var ret = ""; + for (var i = 0; i < bytes.length; i++) + ret += String.fromCharCode(bytes[i]); + return ret; +} + +Buffer.prototype.binarySlice = Buffer.prototype.asciiSlice; + +Buffer.prototype.inspect = function() { + var out = [], + len = this.length; + for (var i = 0; i < len; i++) { + out[i] = toHex(this[i]); + if (i == exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...'; + break; + } + } + return ''; +}; + + +Buffer.prototype.hexSlice = function(start, end) { + var len = this.length; + + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; + + var out = ''; + for (var i = start; i < end; i++) { + out += toHex(this[i]); + } + return out; +}; + + +Buffer.prototype.toString = function(encoding, start, end) { + encoding = String(encoding || 'utf8').toLowerCase(); + start = +start || 0; + if (typeof end == 'undefined') end = this.length; + + // Fastpath empty strings + if (+end == start) { + return ''; + } + + switch (encoding) { + case 'hex': + return this.hexSlice(start, end); + + case 'utf8': + case 'utf-8': + return this.utf8Slice(start, end); + + case 'ascii': + return this.asciiSlice(start, end); + + case 'binary': + return this.binarySlice(start, end); + + case 'base64': + return this.base64Slice(start, end); + + case 'ucs2': + case 'ucs-2': + return this.ucs2Slice(start, end); + + default: + throw new Error('Unknown encoding'); + } +}; + + +Buffer.prototype.hexWrite = function(string, offset, length) { + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + + // must be an even number of digits + var strLen = string.length; + if (strLen % 2) { + throw new Error('Invalid hex string'); + } + if (length > strLen / 2) { + length = strLen / 2; + } + for (var i = 0; i < length; i++) { + var b = parseInt(string.substr(i * 2, 2), 16); + if (isNaN(b)) throw new Error('Invalid hex string'); + this[offset + i] = b; + } + Buffer._charsWritten = i * 2; + return i; +}; + + +Buffer.prototype.write = function(string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length; + length = undefined; + } + } else { // legacy + var swap = encoding; + encoding = offset; + offset = length; + length = swap; + } + + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + encoding = String(encoding || 'utf8').toLowerCase(); + + switch (encoding) { + case 'hex': + return this.hexWrite(string, offset, length); + + case 'utf8': + case 'utf-8': + return this.utf8Write(string, offset, length); + + case 'ascii': + return this.asciiWrite(string, offset, length); + + case 'binary': + return this.binaryWrite(string, offset, length); + + case 'base64': + return this.base64Write(string, offset, length); + + case 'ucs2': + case 'ucs-2': + return this.ucs2Write(string, offset, length); + + default: + throw new Error('Unknown encoding'); + } +}; + +// slice(start, end) +function clamp(index, len, defaultValue) { + if (typeof index !== 'number') return defaultValue; + index = ~~index; // Coerce to integer. + if (index >= len) return len; + if (index >= 0) return index; + index += len; + if (index >= 0) return index; + return 0; +} + +Buffer.prototype.slice = function(start, end) { + var len = this.length; + start = clamp(start, len, 0); + end = clamp(end, len, len); + return new Buffer(this, end - start, +start); +}; + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function(target, target_start, start, end) { + var source = this; + start || (start = 0); + if (end === undefined || isNaN(end)) { + end = this.length; + } + target_start || (target_start = 0); + + if (end < start) throw new Error('sourceEnd < sourceStart'); + + // Copy 0 bytes; we're done + if (end === start) return 0; + if (target.length == 0 || source.length == 0) return 0; + + if (target_start < 0 || target_start >= target.length) { + throw new Error('targetStart out of bounds'); + } + + if (start < 0 || start >= source.length) { + throw new Error('sourceStart out of bounds'); + } + + if (end < 0 || end > source.length) { + throw new Error('sourceEnd out of bounds'); + } + + // Are we oob? + if (end > this.length) { + end = this.length; + } + + if (target.length - target_start < end - start) { + end = target.length - target_start + start; + } + + var temp = []; + for (var i=start; i= this.length) { + throw new Error('start out of bounds'); + } + + if (end < 0 || end > this.length) { + throw new Error('end out of bounds'); + } + + for (var i = start; i < end; i++) { + this[i] = value; + } +} + +// Static methods +Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer; +}; + +Buffer.concat = function (list, totalLength) { + if (!isArray(list)) { + throw new Error("Usage: Buffer.concat(list, [totalLength])\n \ + list should be an Array."); + } + + if (list.length === 0) { + return new Buffer(0); + } else if (list.length === 1) { + return list[0]; + } + + if (typeof totalLength !== 'number') { + totalLength = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + totalLength += buf.length; + } + } + + var buffer = new Buffer(totalLength); + var pos = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + buf.copy(buffer, pos); + pos += buf.length; + } + return buffer; +}; + +Buffer.isEncoding = function(encoding) { + switch ((encoding + '').toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + case 'raw': + return true; + + default: + return false; + } +}; + +// helpers + +function coerce(length) { + // Coerce length to a number (possibly NaN), round up + // in case it's fractional (e.g. 123.456) then do a + // double negate to coerce a NaN to 0. Easy, right? + length = ~~Math.ceil(+length); + return length < 0 ? 0 : length; +} + +function isArray(subject) { + return (Array.isArray || + function(subject){ + return {}.toString.apply(subject) == '[object Array]' + }) + (subject) +} + +function isArrayIsh(subject) { + return isArray(subject) || Buffer.isBuffer(subject) || + subject && typeof subject === 'object' && + typeof subject.length === 'number'; +} + +function toHex(n) { + if (n < 16) return '0' + n.toString(16); + return n.toString(16); +} + +function utf8ToBytes(str) { + var byteArray = []; + for (var i = 0; i < str.length; i++) + if (str.charCodeAt(i) <= 0x7F) + byteArray.push(str.charCodeAt(i)); + else { + var h = encodeURIComponent(str.charAt(i)).substr(1).split('%'); + for (var j = 0; j < h.length; j++) + byteArray.push(parseInt(h[j], 16)); + } + + return byteArray; +} + +function asciiToBytes(str) { + var byteArray = [] + for (var i = 0; i < str.length; i++ ) + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push( str.charCodeAt(i) & 0xFF ); + + return byteArray; +} + +function base64ToBytes(str) { + return toByteArray(str); +} + +function blitBuffer(src, dst, offset, length) { + var pos, i = 0; + while (i < length) { + if ((i+offset >= dst.length) || (i >= src.length)) + break; + + dst[i + offset] = src[i]; + i++; + } + return i; +} + +function decodeUtf8Char(str) { + try { + return decodeURIComponent(str); + } catch (err) { + return String.fromCharCode(0xFFFD); // UTF 8 invalid char + } +} + +// read/write bit-twiddling + +Buffer.prototype.readUInt8 = function(offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return; + + return buffer[offset]; +}; + +function readUInt16(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return 0; + + if (isBigEndian) { + val = buffer[offset] << 8; + if (offset + 1 < buffer.length) { + val |= buffer[offset + 1]; + } + } else { + val = buffer[offset]; + if (offset + 1 < buffer.length) { + val |= buffer[offset + 1] << 8; + } + } + + return val; +} + +Buffer.prototype.readUInt16LE = function(offset, noAssert) { + return readUInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt16BE = function(offset, noAssert) { + return readUInt16(this, offset, true, noAssert); +}; + +function readUInt32(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return 0; + + if (isBigEndian) { + if (offset + 1 < buffer.length) + val = buffer[offset + 1] << 16; + if (offset + 2 < buffer.length) + val |= buffer[offset + 2] << 8; + if (offset + 3 < buffer.length) + val |= buffer[offset + 3]; + val = val + (buffer[offset] << 24 >>> 0); + } else { + if (offset + 2 < buffer.length) + val = buffer[offset + 2] << 16; + if (offset + 1 < buffer.length) + val |= buffer[offset + 1] << 8; + val |= buffer[offset]; + if (offset + 3 < buffer.length) + val = val + (buffer[offset + 3] << 24 >>> 0); + } + + return val; +} + +Buffer.prototype.readUInt32LE = function(offset, noAssert) { + return readUInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readUInt32BE = function(offset, noAssert) { + return readUInt32(this, offset, true, noAssert); +}; + + +/* + * Signed integer types, yay team! A reminder on how two's complement actually + * works. The first bit is the signed bit, i.e. tells us whether or not the + * number should be positive or negative. If the two's complement value is + * positive, then we're done, as it's equivalent to the unsigned representation. + * + * Now if the number is positive, you're pretty much done, you can just leverage + * the unsigned translations and return those. Unfortunately, negative numbers + * aren't quite that straightforward. + * + * At first glance, one might be inclined to use the traditional formula to + * translate binary numbers between the positive and negative values in two's + * complement. (Though it doesn't quite work for the most negative value) + * Mainly: + * - invert all the bits + * - add one to the result + * + * Of course, this doesn't quite work in Javascript. Take for example the value + * of -128. This could be represented in 16 bits (big-endian) as 0xff80. But of + * course, Javascript will do the following: + * + * > ~0xff80 + * -65409 + * + * Whoh there, Javascript, that's not quite right. But wait, according to + * Javascript that's perfectly correct. When Javascript ends up seeing the + * constant 0xff80, it has no notion that it is actually a signed number. It + * assumes that we've input the unsigned value 0xff80. Thus, when it does the + * binary negation, it casts it into a signed value, (positive 0xff80). Then + * when you perform binary negation on that, it turns it into a negative number. + * + * Instead, we're going to have to use the following general formula, that works + * in a rather Javascript friendly way. I'm glad we don't support this kind of + * weird numbering scheme in the kernel. + * + * (BIT-MAX - (unsigned)val + 1) * -1 + * + * The astute observer, may think that this doesn't make sense for 8-bit numbers + * (really it isn't necessary for them). However, when you get 16-bit numbers, + * you do. Let's go back to our prior example and see how this will look: + * + * (0xffff - 0xff80 + 1) * -1 + * (0x007f + 1) * -1 + * (0x0080) * -1 + */ +Buffer.prototype.readInt8 = function(offset, noAssert) { + var buffer = this; + var neg; + + if (!noAssert) { + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (offset >= buffer.length) return; + + neg = buffer[offset] & 0x80; + if (!neg) { + return (buffer[offset]); + } + + return ((0xff - buffer[offset] + 1) * -1); +}; + +function readInt16(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt16(buffer, offset, isBigEndian, noAssert); + neg = val & 0x8000; + if (!neg) { + return val; + } + + return (0xffff - val + 1) * -1; +} + +Buffer.prototype.readInt16LE = function(offset, noAssert) { + return readInt16(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt16BE = function(offset, noAssert) { + return readInt16(this, offset, true, noAssert); +}; + +function readInt32(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt32(buffer, offset, isBigEndian, noAssert); + neg = val & 0x80000000; + if (!neg) { + return (val); + } + + return (0xffffffff - val + 1) * -1; +} + +Buffer.prototype.readInt32LE = function(offset, noAssert) { + return readInt32(this, offset, false, noAssert); +}; + +Buffer.prototype.readInt32BE = function(offset, noAssert) { + return readInt32(this, offset, true, noAssert); +}; + +function readFloat(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + // TODO + return Ieee754.readIEEE754(buffer, offset, isBigEndian, + 23, 4); +} + +Buffer.prototype.readFloatLE = function(offset, noAssert) { + return readFloat(this, offset, false, noAssert); +}; + +Buffer.prototype.readFloatBE = function(offset, noAssert) { + return readFloat(this, offset, true, noAssert); +}; + +function readDouble(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return Ieee754.readIEEE754(buffer, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.readDoubleLE = function(offset, noAssert) { + return readDouble(this, offset, false, noAssert); +}; + +Buffer.prototype.readDoubleBE = function(offset, noAssert) { + return readDouble(this, offset, true, noAssert); +}; + + +/* + * We have to make sure that the value is a valid integer. This means that it is + * non-negative. It has no fractional component and that it does not exceed the + * maximum allowed value. + * + * value The number to check for validity + * + * max The maximum value + */ +function verifuint(value, max) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value >= 0, + 'specified a negative value for writing an unsigned value'); + + assert.ok(value <= max, 'value is larger than maximum value for type'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +Buffer.prototype.writeUInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xff); + } + + if (offset < buffer.length) { + buffer[offset] = value; + } +}; + +function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffff); + } + + for (var i = 0; i < Math.min(buffer.length - offset, 2); i++) { + buffer[offset + i] = + (value & (0xff << (8 * (isBigEndian ? 1 - i : i)))) >>> + (isBigEndian ? 1 - i : i) * 8; + } + +} + +Buffer.prototype.writeUInt16LE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt16BE = function(value, offset, noAssert) { + writeUInt16(this, value, offset, true, noAssert); +}; + +function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffffffff); + } + + for (var i = 0; i < Math.min(buffer.length - offset, 4); i++) { + buffer[offset + i] = + (value >>> (isBigEndian ? 3 - i : i) * 8) & 0xff; + } +} + +Buffer.prototype.writeUInt32LE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeUInt32BE = function(value, offset, noAssert) { + writeUInt32(this, value, offset, true, noAssert); +}; + + +/* + * We now move onto our friends in the signed number category. Unlike unsigned + * numbers, we're going to have to worry a bit more about how we put values into + * arrays. Since we are only worrying about signed 32-bit values, we're in + * slightly better shape. Unfortunately, we really can't do our favorite binary + * & in this system. It really seems to do the wrong thing. For example: + * + * > -32 & 0xff + * 224 + * + * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of + * this aren't treated as a signed number. Ultimately a bad thing. + * + * What we're going to want to do is basically create the unsigned equivalent of + * our representation and pass that off to the wuint* functions. To do that + * we're going to do the following: + * + * - if the value is positive + * we can pass it directly off to the equivalent wuint + * - if the value is negative + * we do the following computation: + * mb + val + 1, where + * mb is the maximum unsigned value in that byte size + * val is the Javascript negative integer + * + * + * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If + * you do out the computations: + * + * 0xffff - 128 + 1 + * 0xffff - 127 + * 0xff80 + * + * You can then encode this value as the signed version. This is really rather + * hacky, but it should work and get the job done which is our goal here. + */ + +/* + * A series of checks to make sure we actually have a signed 32-bit number + */ +function verifsint(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); + + assert.ok(Math.floor(value) === value, 'value has a fractional component'); +} + +function verifIEEE754(value, max, min) { + assert.ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + assert.ok(value <= max, 'value larger than maximum allowed value'); + + assert.ok(value >= min, 'value smaller than minimum allowed value'); +} + +Buffer.prototype.writeInt8 = function(value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7f, -0x80); + } + + if (value >= 0) { + buffer.writeUInt8(value, offset, noAssert); + } else { + buffer.writeUInt8(0xff + value + 1, offset, noAssert); + } +}; + +function writeInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 1 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fff, -0x8000); + } + + if (value >= 0) { + writeUInt16(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt16LE = function(value, offset, noAssert) { + writeInt16(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt16BE = function(value, offset, noAssert) { + writeInt16(this, value, offset, true, noAssert); +}; + +function writeInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fffffff, -0x80000000); + } + + if (value >= 0) { + writeUInt32(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); + } +} + +Buffer.prototype.writeInt32LE = function(value, offset, noAssert) { + writeInt32(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeInt32BE = function(value, offset, noAssert) { + writeInt32(this, value, offset, true, noAssert); +}; + +function writeFloat(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); + } + + Ieee754.writeIEEE754(buffer, value, offset, isBigEndian, 23, 4); +} + +Buffer.prototype.writeFloatLE = function(value, offset, noAssert) { + writeFloat(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeFloatBE = function(value, offset, noAssert) { + writeFloat(this, value, offset, true, noAssert); +}; + +function writeDouble(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + assert.ok(value !== undefined && value !== null, + 'missing value'); + + assert.ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + assert.ok(offset !== undefined && offset !== null, + 'missing offset'); + + assert.ok(offset + 7 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); + } + + Ieee754.writeIEEE754(buffer, value, offset, isBigEndian, + 52, 8); +} + +Buffer.prototype.writeDoubleLE = function(value, offset, noAssert) { + writeDouble(this, value, offset, false, noAssert); +}; + +Buffer.prototype.writeDoubleBE = function(value, offset, noAssert) { + writeDouble(this, value, offset, true, noAssert); +}; +}; +BundleModuleCode['os/buffer_ieee754']=function (module,exports,global,process){ +exports.readIEEE754 = function(buffer, offset, isBE, mLen, nBytes) { + var e, m, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = isBE ? 0 : (nBytes - 1), + d = isBE ? 1 : -1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; + +exports.writeIEEE754 = function(buffer, value, offset, isBE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isBE ? (nBytes - 1) : 0, + d = isBE ? -1 : 1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; +}; +}; +BundleModuleCode['os/process']=function (module,exports,global,process){ +// shim for using process in browser +if (typeof process != 'undefined' && process.env) { + module.exports = process; + return; +} + +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +(function () { + try { + cachedSetTimeout = setTimeout; + } catch (e) { + cachedSetTimeout = function () { + throw new Error('setTimeout is not defined'); + } + } + try { + cachedClearTimeout = clearTimeout; + } catch (e) { + cachedClearTimeout = function () { + throw new Error('clearTimeout is not defined'); + } + } +} ()) +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = cachedSetTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + cachedClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + cachedSetTimeout(drainQueue, 0); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; +process.pid=0; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; +}; +BundleModuleCode['/home/sbosse/proj/jam/js/top/jamlib.js']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: 25-12-16 by sbosse. + ** $RCS: $Id: jamlib.js,v 1.4 2017/06/19 17:18:39 sbosse Exp sbosse $ + ** $VERSION: 1.28.2 + ** + ** $INFO: + ** + ** JAM library API that can be embedded in any host application. + ** + ** + ** New: Embedded auto setup (e.g., for clusters) using command line arguments + ** + ** jamlib autosetup:"{options}" + ** + ** + ** $ENDOFINFO + */ +var onexit=false; +var start=false; +var options = { + geo:undefined, + verbose:0, + version:'1.28.2' // public version +}; + +global.config={simulation:false,nonetwork:false}; + +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var Aios = Require('jam/aios'); +var Esprima = Require('parser/esprima'); +var Json = Require('jam/jsonfn'); +var fs = Require('fs'); +var Sat = Require('dos/ext/satelize'); +var CBL = Require('com/cbl'); + +var DIR = Aios.DIR; + +// Parse command line arguments; extract a:v attributes +var environment = process.env; process.argv.slice(2).forEach(function (arg) { + var tokens=arg.match(/([a-zA-Z]+):(['"0-9a-zA-Z_:\->\.\{\},;]+)/); + if (tokens && tokens.length==3) environment[tokens[1]]=tokens[2]; +}); + +if (typeof setImmediate == 'undefined') { + function setImmediate(callback) {return setTimeout(callback,0)}; +} + +// Extend DIR with IP capabilities of NORTH, .. +DIR.North= function (ip) { return {tag:DIR.NORTH,ip:ip}} +DIR.South= function (ip) { return {tag:DIR.SOUTH,ip:ip}} +DIR.West = function (ip) { return {tag:DIR.WEST ,ip:ip}} +DIR.East = function (ip) { return {tag:DIR.EAST ,ip:ip}} +DIR.Up = function (ip) { return {tag:DIR.UP ,ip:ip}} +DIR.Down = function (ip) { return {tag:DIR.DOWN ,ip:ip}} + +/** + * typeof options = { + * connections?, + * print? is agent and control message output function, + * printAgent? is agent message only output function, + * fork?, + * provider?, consumer?, + * classes?, + * id?:string is JAM and JAM root node id, + * world?:string is JAM world id, + * position?:{x,y}, + * cluster?:boolean|[] is an attached cluster node, + * nowatch:boolean is a disable flag for agent watchdog checking, + * checkpoint:boolean is a flag forcing code checkpointing (even if watchdog is available), + * nolimits:boolean is a disable flag for agent resource monitoring, + * log?:{class?:boolean,node?,agent?,parent?,host?,time?,Time?,pid?}, + * logJam?:{host?,time?,pid?,node?,world?}, + * scheduler?:scheduler is an external scheduler, singlestep?, + * network?:{cluster?,rows,columns,connect?:function}, + * verbose?, TMO? } + * with typeof connections = { + * @kind : {from:string,to?:string,proto:string='udp'|'tcp'|'http'|'stream',num?:number,on?,range?:number[]}, + * @kind : {send:function, status:function, register?:function(@link)} , + * @kind : .. } + * with @kind = {north,south,west,east,ip, ..} + * + * Connecting JAM nodes (IP) + * ------------------------- + * + * .. Jam({ + * connections: { + * // Generic, P2PN + * ip?: {from:string,to?:string,proto:string='udp'|'tcp'|'http',num?:number} // AMP link (UDP) or set of AMP links (num>1) + * // Assigned to a logical direction, P2P + * north?: { + * from:string,to?:string,proto?='udp'|'tcp'|'http'|'device',device?:string // device is a hardware P2P stream device + * }, .. + * + * Integration of host program streams + * ------------------------------------ + * + * var chan = Some Stream Channel Object; + * + * .. Jam({ + * connections: { + * north?: { + * register: function (link) { + * // register channel data handler with link handler + * chan.on('data',function (data) { + * // process raw data, extract msg={agent:string,to?,from?,..} or {signal:string,to?,from?,..} + * if (msg.agent) link.emit('agent',msg.agent); + * if (msg.signal) link.emit('signal',msg.signal); + * }); + * } + * send: function (msg) { + * chan.send(msg); + * }, + * status: function (to) { + * return true; + * } + * } + * }, .. + * } + * + * Cluster + * -------- + * + * A forked cluster consists of a master node (0) and up to 8 child ndoes connected around the root node + * by streams in directions {E,S,SE,W,SW,N,NW,NE}. Each node is executed physically in a different JAM process. + * Ex. network: {cluster:true, rows:2, columns:2}, + * + */ + +var jam = function (options) { + var self=this, + p,conn,node; + this.options = options||{}; + this.environment=environment; + if (this.setup) this.setup(); // overwrite options + if (this.options.world && !this.options.id) this.options.id=this.options.world; + if (!this.options.id) this.options.id=Aios.aidgen(); + if (!this.options.log) this.options.log={}; + if (!this.options.logJam) this.options.logJam={pid:false,host:false,time:false}; + this.verbose = this.options.verbose || 0; + this.Aios = Aios; + this.DIR = Aios.aios.DIR; + + Aios.options.verbose=this.verbose; + if (options.scheduler) Aios.current.scheduler=scheduler; + if (options.nolimits||options.nowatch||options.checkpoint) + Aios.config({nolimits:options.nolimits,nowatch:options.nowatch,checkpoint:options.checkpoint}); + + // out=function (msg) { Io.print('[JAM '+self.options.id+'] '+msg)}; + if (this.options.print) Aios.print=Aios.printAgent=this.options.print; + if (this.options.print2) Aios.printAgent=this.options.print2; + if (this.options.printAgent) Aios.printAgent=this.options.printAgent; + + // JAM messages + this.log=function (msg) { + var s='[JAM',sep=' '; + if (self.options.logJam.pid && process) s += (' '+process.pid),sep=':'; + if (self.options.logJam.world && Aios.current.world) s += (sep+Aios.current.world.id),sep=':'; + if (self.options.logJam.node && Aios.current.node) s += (sep+Aios.current.node.id),sep=':'; + if (self.options.logJam.time) s += (sep+Aios.time()); + Aios.print(s+'] '+msg); + }; + + + this.err=function (msg,err) { + self.log('Error: '+msg); + throw (err||'JAMLIB'); + } + this.warn=function (msg) { + self.log('Warning: '+msg); + } + + this.error=undefined; + + // Create a world + this.world = Aios.World.World([],{ + id:this.options.world||this.options.id.toUpperCase(), + classes:options.classes||[], + scheduler:options.scheduler, + verbose:options.verbose + }); + if (this.verbose) this.log('Created world '+this.world.id+'.'); + + this.node=none; + this.run=false; + + + // Service loop executing the AIOS scheduler + // NOT USED if there is an external scheduler supplied (world will create JAM scheduling loop) + + + this.ticks=0; // schedule loop execution counter! + this.steps=0; // Number of schedule loop execution steps + this.loop=none; // Schedule loop function + this.looping=none; // Current schedule loop run (or none); can be waiting for a timeout + + Aios.config({fastcopy:this.options.fastcopy, + verbose:this.options.verbose}); + + if (this.options.log) + for(p in this.options.log) Aios.config(this.options.log[p]?{"log+":p}:{"log-":p}); + + this.process = Aios.Proc.Proc(); + this.process.agent={id:'jamlib'}; + + this.events={}; +} + +// Import analyzer class... +var JamAnal = Require('jam/analyzer'); +JamAnal.current(Aios); +jam.prototype.analyzeSyntax=JamAnal.jamc.prototype.analyze; +jam.prototype.syntax=JamAnal.jamc.prototype.syntax; + + + +/** Add agent class to the JAM world and create sandboxed constructors. + * type constructor = function|string + */ +jam.prototype.addClass = function (name,constructor,env) { + this.world.addClass(name,constructor,env); + if (this.verbose) this.log('Agent class '+name+' added to world library.'); +}; + +/** Add a new node to the world. + * Assumption: 2d meshgrid network with (x,y) coordinates. + * The root node has position {x=0,y=0}. + * type of nodeDesc = {x:number,y:number,id?} + * + */ +jam.prototype.addNode = function (nodeDesc) { + var node,x,y; + x=nodeDesc.x; + y=nodeDesc.y; + if (Comp.array.find(this.world.nodes,function (node) { + return node.position.x==x && node.position.y==y; + })) { + this.err('addNodes: Node at positition ('+x+','+y+') exists already.'); + return; + } + node=Aios.Node.Node({id:nodeDesc.id||Aios.aidgen(),position:{x:x,y:y}},true); + if (this.verbose) this.log('Created node '+node.id+' ('+x+','+y+').'); + // Add node to world + this.world.addNode(node); + return node.id; +} + +/** Add logical nodes. + * The root node has position {x=0,y=0}. + * type of nodes = [{x:number,y:number,id?},..] + */ +jam.prototype.addNodes = function (nodes) { + var n,node,x,y,nodeids=[]; + for(n in nodes) { + nodeids.push(this.addNode(nodes[n])); + } + return nodeids; +} + +/** Analyze agent class template in text or object form + ** typeof @options = {..,classname?:string} + * Returns {report:string,interface} + */ +jam.prototype.analyze = function (ac,options) { + var source,name,syntax,content,report,interface; + if (Comp.obj.isString(ac)) { + // TODO + } else if (Comp.obj.isObject(ac)) { + // TODO + } else if (Comp.obj.isFunction(ac)) { + source = ac.toString(); + if (!options.classname) { + name=source.match(/^ *function *([^\s\(]*)\(/); + if (name && name[1]!='') options.classname=name[1]; + } + content = 'var ac ='+source; + syntax = Esprima.parse(content, { tolerant: true, loc:true }); + try { + interface=this.analyzeSyntax(syntax,{ + classname:options.classname||'anonymous', + level:options.level==undefined?2:options.level, + verbose:options.verbose, + err:function (msg){throw msg}, + out:function (msg){if (!report) report=msg; else report=report+'\n'+msg;}, + warn:function (msg){if (!report) report=msg; else report=report+'\n'+msg;} + }); + return {report:report||'OK',interface:interface}; + } catch (e) { + return {report:e,interface:interface}; + } + } +} +jam.prototype.clock = Aios.clock; + +/** Compile (analyze) an agent class constructor function and add it to the world class library. + ** Can be used after an open statement. + ** Usage: compileClass(name,constructor,options?) + ** compileClass(constructor,options?) + ** + ** typeof @name=string|undefined + ** typeof @constructor=function|string + ** typeof @options={verbose:number|boolean)|number|undefined +*/ +jam.prototype.compileClass = function (name,constructor,options) { + var ac,p,verbose,content,syntax,report,text,env={ac:undefined},self=this,ac; + + if (typeof name == 'function') constructor=name,name=undefined,options=constructor; + if (typeof options == 'object') verbose=options.verbose; + else if (options!=undefined) verbose=options; else verbose=this.verbose; + // if (typeof constructor != 'function') throw 'compileClass: second constructor argument not a function'; + + if (typeof constructor == 'function') text = constructor.toString(); + else text = constructor; + + if (!name) { + // try to find name in function definition + name=text.match(/[\s]*function[\s]*([A-Za-z0-9_]+)[\s]*\(/); + if (!name) throw ('compileClass: No class name provided and no name found in constructor '+ + text.substring(0,80)); + name=name[1]; + + } + content = 'var ac = '+text; + try { syntax = Esprima.parse(content, { tolerant: true, loc:true }) } + catch (e) { throw 'compileClass('+name+'): Parsing failed with '+e } + report = this.analyzeSyntax(syntax, + { + classname:name, + level:2, + verbose:verbose||0, + err: function (msg){self.log(msg)}, + out: function (msg){self.log(msg)}, + warn: function (msg){self.log(msg)} + }); + if (report.errors.length) { throw 'compileClass('+name+'): failed with '+report.errors.join('; ')}; + for (p in report.activities) env[p]=p; + with (env) { eval(content) }; + ac=env.ac; env.ac=undefined; + this.addClass(name,ac,env); + return name; +} + +/** Connect logical nodes (virtual link). + * The root node has position {x=0,y=0}. + * type of links = [{x1:number,y1:number,x2:number,x2:number},..]|[{x,y},{x,y}] + */ +jam.prototype.connectNodes = function (connections) { + var c,node1,node2,x1,y1,x2,y2,dir; + if (connections[0].x != undefined && connections[0].y != undefined) { + if (connections.length!=2) throw 'INVALID'; // invalid + // simple style + connections=[{x1:connections[0].x,x2:connections[1].x, + y1:connections[0].y,y2:connections[0].y}]; + } + for(c in connections) { + x1=connections[c].x1; + y1=connections[c].y1; + x2=connections[c].x2; + y2=connections[c].y2; + if (this.verbose) this.log('Connecting ('+x1+','+y1+') -> ('+x2+','+y2+')'); + node1=Comp.array.find(this.world.nodes,function (node) { + return node.position.x==x1 && node.position.y==y1; + }); + node2=Comp.array.find(this.world.nodes,function (node) { + return node.position.x==x2 && node.position.y==y2; + }); + if (!node1) this.err('connectNodes: Node at positition ('+x1+','+y1+') does not exist.'); + if (!node2) this.err('connectNodes: Node at positition ('+x2+','+y2+') does not exist.'); + if ((x2-x1)==0) { + if ((y2-y1) > 0) dir=Aios.DIR.SOUTH; + else dir=Aios.DIR.NORTH; + } else if ((x2-x1)>0) dir=Aios.DIR.EAST; + else dir=Aios.DIR.WEST; + this.world.connect(dir,node1,node2); + this.world.connect(Aios.DIR.opposite(dir),node2,node1); + } +} + +/** Dynamically connect remote endpoint at run-time + * typeof @to = string url>| + */ +jam.prototype.connectTo = function (to,nodeid) { + var node=this.getNode(nodeid), + tokens=(typeof to=='string')?to.split('->'):null, + dir; + // console.log(tokens) + if (!node) return; + if (to.tag) dir=to; + else if (tokens.length==2) { + dir=Aios.DIR.from(tokens[0]); + if (dir) dir.ip=tokens[1]; + } else dir={tag:'DIR.IP',ip:to}; + if (dir) this.world.connectTo(dir,node); +} + +/** Check connection status of a link + * + */ +jam.prototype.connected = function (dir,nodeid) { + var node=this.getNode(nodeid); + if (!node) return; + return this.world.connected(dir,node); +} + +/** Create and start an agent from class ac with arguments. + * Ac is either already loaded (i.e., ac specifies the class name) or + * AC is supplied as a constructor function (ac), a class name, or a sandboxed constructor + * {fun:function,mask:{}} object for a specific level. + * + * type of ac = string|object|function + * type of args = * [] + * level = {0,1,2,3} + * + */ +jam.prototype.createAgent = function (ac,args,level,className,parent) { + var node=this.world.nodes[this.node], + process=none,sac; + if (level==undefined) level=1; + if (!className && typeof ac == 'string') className=ac; + if (!className && typeof ac == 'function') className=Aios.Code.className(ac); + if (Comp.obj.isFunction(ac) || Comp.obj.isObject(ac)) { + // Create an agent process from a constructor function or sandboxed constructor object + process = Aios.Code.createOn(node,ac,args,level,className); + if (process && !process.agent.parent) process.agent.parent=parent; + if (process) return process.agent.id; + } else { + // It is a class name. Find an already sandboxed constructor from world classes pool + if (this.world.classes[ac]) + process = Aios.Code.createOn(node,this.world.classes[ac][level],args,level,className); + else { + this.error='createAgent: Cannot find agent class '+ac; + this.log(this.error); + return; + } + if (process) { + if (!process.agent.parent) process.agent.parent=parent; + process.agent.ac=ac; + return process.agent.id; + } else return none; + } +} + +/** Create agent on specified (logical or physical) node. + * typeof node = number|string|{x,y} + */ +jam.prototype.createAgentOn = function (node,ac,args,level,className,parent) { + var res,_currentNode=this.node,found=this.getNode(node); + + if (found) { + this.setCurrentNode(); + res=this.createAgent(ac,args,level,className,parent); + this.setCurrentNode(_currentNode); + } + return res; +} + +/** Create a physical communication port + * + */ +jam.prototype.createPort = function (dir,options,nodeid) { + if (!options) options={}; + var multicast=options.multicast; + switch (dir.tag) { + case Aios.DIR.NORTH: + case Aios.DIR.SOUTH: + case Aios.DIR.WEST: + case Aios.DIR.EAST: + case Aios.DIR.UP: + case Aios.DIR.DOWN: + multicast=false; + } + if (options.from==undefined && dir.ip) options.from=dir.ip.toString(); + var chan=this.world.connectPhy( + dir, + this.getNode(nodeid), + { + broker:options.broker, + multicast:multicast, + name:options.name, + on:options.on, + oneway:options.oneway, + proto:options.proto||'udp', + rcv:options.from, + snd:options.to, + verbose:(options.verbose!=undefined?options.verbose:this.verbose) + }); + chan.init(); + chan.start(); + return chan; +} +/** Dynamically disconnect remote endpoint at run-time + * + */ +jam.prototype.disconnect = function (to,nodeid) { + var node=this.getNode(nodeid); + if (node) { + this.world.disconnect(to,node); + } +} + +/** Emit an event + ** function emit(@event,@arg1,..) + */ +jam.prototype.emit = function () { + Aios.emit.apply(this,arguments); +} + + +/** Execute an agent snapshot on current node delivered in JSON+ text format or read from a file. + */ +jam.prototype.execute = function (data,file) { + if (!data && file && fs) + try { + data=fs.readFileSync(file,'utf8'); + } catch (e) { + this.log('Error: Reading file '+file+' failed: '+e); + return undefined; + } + if (data) return this.world.nodes[this.node].receive(data,true); +} + +/** Execute an agent snapshot on node @node delivered in JSON+ text format or read from a file. + */ +jam.prototype.executeOn = function (data,node,file) { + node=this.getNode(node); + if (!node) return; + if (!data && file && fs) + try { + data=fs.readFileSync(file,'utf8'); + } catch (e) { + this.log('Error: Reading file '+file+' failed: '+e); + return undefined; + } + if (data) return node.receive(data,true); +} + +/** Extend AIOS of specific privilege level. The added functions can be accessed by agents. + * + * function extend(level:number [],name:string,func:function,argn?:number|number []); + */ +jam.prototype.extend = function (level,name,func,argn) { + var self=this; + if (Comp.obj.isArray(level)) { + Comp.array.iter(level,function (l) {self.extend(l,name,func,argn)}); + return; + } + switch (level) { + case 0: + if (Aios.aios0[name]) throw Error('JAM: Cannot extend AIOS(0) with '+name+', existst already!'); + Aios.aios0[name]=func; break; + case 1: + if (Aios.aios1[name]) throw Error('JAM: Cannot extend AIOS(1) with '+name+', existst already!'); + Aios.aios1[name]=func; break; + case 2: + if (Aios.aios2[name]) throw Error('JAM: Cannot extend AIOS(2) with '+name+', existst already!'); + Aios.aios2[name]=func; break; + case 3: + if (Aios.aios3[name]) throw Error('JAM: Cannot extend AIOS(3) with '+name+', existst already!'); + Aios.aios3[name]=func; break; + default: + throw Error('JAM: Extend: Invalid privilige level argument ([0,1,2,3])'); + } + if (!JamAnal.corefuncs[name]) JamAnal.corefuncs[name]={argn:argn||func.length}; +} + +/** Return node object referenced by logical node number, position, or name + * If @id is undefined return current node object. + */ +jam.prototype.getNode = function (id) { + var node; + if (id==undefined) return this.world.nodes[this.node]; + if (typeof id == 'number') + node=this.world.nodes[id]; + else if (typeof id == 'string') { + // Search node identifier or position; + loop: for(var i in this.world.nodes) { + if (this.world.nodes[i] && this.world.nodes[i].id==id) { + node = this.world.nodes[i]; + break loop; + } + } + } else if (id.x != undefined && + id.y != undefined) { + // Search node position; + loop: for(var i in this.world.nodes) { + if (this.world.nodes[i] && Comp.obj.equal(this.world.nodes[i].position,id)) { + node = this.world.nodes[i]; + break loop; + } + } + } + + return node; +} + +/** Return node name from logical node number or position + * + */ +jam.prototype.getNodeName = function (nodeNumberorPosition) { + var node=this.getNode(nodeNumberorPosition); + if (node) return node.id; +} + +/** Get current agent process or search for agent process + * + */ +jam.prototype.getProcess = function (agent) { + if (!agent) + return Aios.current.process; + else { + // TODO + } +} + + +/** Return node name from logical node number or position + * + */ +jam.prototype.getWorldName = function () { + return this.world.id; +} + +jam.prototype.info = function (kind,id) { + switch (kind) { + case 'node': + var node=this.getNode(id); + if (!node) return; + return { + id:node.id, + position: node.position, + location:node.location, + type:node.type + } + break; + case 'version': return Aios.options.version; + case 'host': return { + type:global.TARGET, + watchdog:Aios.watchdog?true:false, + protect: Aios.watchdog&&Aios.watchdog.protect?true:false, + jsonify:Aios.options.json, + minify:!Aios.Code.options.compactit, + }; + } +} + +/** INITIALIZE + * 1. Create and initialize node(s)/world + * 2. Add optional TS provider/consumer + * 3. Create physical network conenctions + */ +jam.prototype.init = function (callback) { + var i=0,j=0, n, p, id, node, connect=[], chan, dir, dirs, pos, + self=this; + + // Current node == root node + this.node=0; + + ///////////// CREATE NODES ///////// + if (!this.options.network) { + if (this.options.position) i=this.options.position.x,j=this.options.position.y; + // Create one (root) node if not already existing + if (!this.getNode({x:i,y:j})) { + node = Aios.Node.Node({ + id:this.options.id, + position:{x:i,y:j}, + TMO:this.options.TMO, + type:this.options.type + },true); + // Add node to world + if (this.verbose) this.log('Created '+(i==0&&j==0?'root ':'')+'node '+node.id+' ('+i+','+j+').'); + this.world.addNode(node); + } + // Register jamlib event handler for the root node + this.register(node); + } else if (!this.options.network.cluster) { + // Create a virtual network of logical nodes. Default: grid + if (this.options.network.rows && this.options.network.columns) { + for(j=0;j world node# + function makeconn (p,conn) { + var link = { + _handler:[], + emit: function (event,msg) { + if (link._handler[event]) link._handler[event](msg); + }, + on: function (event,callback) { + link._handler[event]=callback; + }, + send: function (data,dest,context) { + var res; + self.world.nodes[self.node].connections[p]._count += data.length; + res=conn.send(data,dest); + if (!res) { + context.error='Migration to destination '+dest+' failed'; + // We're still in the agent process context! Throw an error for this agent .. + throw 'MOVE'; + }; + + // kill ghost agent + context.process.finalize(); + }, + status : conn.status?conn.status:(function () {return true}), + count: conn.count?conn.count:function () {return link._count}, + _count:0 + }; + if (conn.register) conn.register(link); + return link; + } + node.connections[p] = makeconn(p,conn); + // register agent receiver and signal handler + node.connections[p].on('agent',node.receive.bind(node)); + node.connections[p].on('signal',node.handle.bind(node)); + } else if (p=='stream') { + // 3. Physical process stream interface (cluster); child->parent proecss connection + chan=this.world.connectPhy( + conn.dir, + this.getNode(), + { + proto:'stream', + sock:process, + mode:'object', + verbose:this.verbose + }); + chan.init(); + } + } + } + if (callback) callback(); + +} + + +/** Tuple space input operation - non blocking, i.e., equiv. to inp(pat,_,0) + */ +jam.prototype.inp = function (pat,all) { + return this.world.nodes[this.node].ts.extern.inp(pat,all); +} + + +/** Kill agent with specified id ('*': kill all agents on node or current node) + */ +jam.prototype.kill = function (id,node) { + if (id=='*') { + this.world.nodes[this.node].processes.table.forEach(function (p) { + if (p) Aios.kill(p.agent.id); + }); + } else + return Aios.kill(id); +} + +/** Try to locate this node (based on network connectivity) + * Any geospatial information is attached to current (node=undefined) or specific node + */ + +jam.prototype.locate = function (nodeid,cb) { + var node=this.getNode(nodeid); + if (!node) return; + Sat.satelize({},function (err,info) { + if (err) { + return cb?cb(undefined,err):console.log(err.toString()); + } else { + var location = { + ip:info.query, + gps:{lat:info.lat,lon:info.lon}, + geo:{city:info.city,country:info.country,countryCode:info.countryCode,region:info.region,zip:info.zip} + } + node.location=location; + if (cb) cb(location); + } + }) +} +/** Lookup nodes and get connection info (more general as connected and broker support) + * + */ +jam.prototype.lookup = function (dir,callback,nodeid) { + var node=this.getNode(nodeid); + if (!node) return; + return this.world.lookup(dir,callback,node); +} + +/** Execute an agent snapshot in JSON+ text form after migration provided from host application + */ +jam.prototype.migrate = function (data) { + return this.world.nodes[this.node].receive(data,false); +} + +/** Install event handler +* +* typeof @event = {'agent','agent+','agent-','signal+','signal','link+','link-',..} +* agent+/agent-: Agent creation and destruction event +* agent: Agent receive event +* signal+: Signal raise event +* signal: Signal receive (handle) event +* route+: A new link was established +* route-: A link is broken +*/ + +jam.prototype.on = function (event,handler) { + Aios.on(event,handler); +} + +/** Remove event handler + */ +jam.prototype.off = function (ev) { + Aios.off(event); +} + + + +/** Read and parse one agent class from file. Can contain nested open statements. + * Browser (no fs module): @file parameter contains source text. + * File/source text format: function [ac] (p1,p2,..) { this.x; .. ; this.act = {..}; ..} + * open(file:string,options?:{verbose?:number|boolean,classname?:string}) -> function | object + * + * Output can be processed by method compileClass + */ +jam.prototype.open = function (file,options) { + var self=this, + res, + text, + name, + ast=null; + if (!options) options={}; + name=options.classname||''; + if (options.verbose>0) this.log('Reading agent class template '+name+' from '+file); + + function parseModel (text) { + var modu={},more,module={exports:{}},name=text.match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/); + if (name) name=name[1]; + function open(filename) { + var text; + try { + text=fs?fs.readFileSync(filename,'utf8'):null; + return parseModel(text); + } catch (e) { + self.log('Error: Opening of '+(fs?file:'text')+' failed: '+e); + } + } + try { + with (module) {eval('res = '+text)}; + if (name) { modu[name]=res; return modu} + else if (module.exports) return module.exports; + else return res; + } catch (e) { + try { + ast = Esprima.parse(text, { tolerant: true, loc:true }); + if (ast.errors && ast.errors.length>0) more = ', '+ast.errors[0]; + } catch (e) { + if (e.lineNumber) more = ', in line '+e.lineNumber; + } + self.log(e.name+(e.message?': '+e.message:'')+(more?more:'')); + } + } + try { + text=fs?fs.readFileSync(file,'utf8'):file; // Browser: file parameter contains already source text + return parseModel(text); + } catch (e) { + this.log('Error: Opening of '+(fs?file:'text')+' failed: '+e); + } +}; + +/** Tuple space output operation + */ +jam.prototype.out = function (tuple) { + return this.world.nodes[this.node].ts.extern.out(tuple); +} + +/** Tuple space read operation - non blocking, i.e., equiv. to rd(pat,_,0) + */ +jam.prototype.rd = function (pat,all) { + return this.world.nodes[this.node].ts.extern.rd(pat,all); +} + +/** 1. Read agent template classes from file and compile (analyze) agent constructor functions. + * Expected file format: module.exports = { ac1: function (p1,p2,..) {}, ac2:.. } + * 2. Read single agent constructor function from file + * + * typeof @options={verbose,error:function} + */ +// TODO: clean up, split fs interface, no require caching .. +if (fs) jam.prototype.readClass = function (file,options) { + var self=this, + ac, + name, + env, + interface, + text, + modu, + path, + p,m, + regex1, + ast=null, + fileText=null, + off=null; + this.error=_; + function errLoc(ast) { + var err; + if (ast && ast.errors && ast.errors.length) { + err=ast.errors[0]; + if (err.lineNumber != undefined) return 'line '+err.lineNumber; + } + return 'unknown' + } + try { + if (!options) options={}; + if (options.verbose>0) this.log('Looking up agent class template(s) from '+file); + //modu=Require(file); + if (Comp.obj.isEmpty(modu)) { + if (options.verbose>0) this.log('Reading agent class template(s) from file '+file); + if (Comp.string.get(file,0)!='/') + path = (process.cwd?process.cwd()+'/':'./')+file; + else + path = file; + fileText=fs.readFileSync(path,'utf8'); + ast=Esprima.parse(fileText, { tolerant: true, loc:true }); + if (require.cache) delete require.cache[file]; // force reload of file by require + modu=require(path); + if(Comp.obj.isEmpty(modu)) { + modu={}; + // Try evaluation of fileText containing one single function definition + if (!fileText) throw 'No such file!'; + name=fileText.match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/); + if (!name) throw ('Export interface of module is empty and file contains no valid function definition!'); + name=name[1]; + eval('(function () {'+fileText+' modu["'+name+'"]='+name+'})()'); + } + } + if (!modu || Comp.obj.isEmpty(modu)) throw 'Empty module.'; + + for (m in modu) { + ac=modu[m]; + env={}; + + if (fileText) off=this.syntax.find(fileText,'VariableDeclarator',m); + if (off && off.loc) this.syntax.offset=off.loc.start.line-1; + + content = 'var ac = '+ac; + syntax = Esprima.parse(content, { tolerant: true, loc:true }); + interface = this.analyzeSyntax(syntax, + { + classname:m, + level:2, + verbose: options.verbose||0, + err: options.error||function (msg){throw(msg)}, + out: function (msg){self.log(msg)}, + warn: function (msg){self.log(msg)} + }); + // text=Json.stringify(ac); + for (var p in interface.activities) env[p]=p; + with (env) { eval(content) }; + + if (options.verbose>0) this.log('Adding agent class constructor '+m+' ('+(typeof ac)+').'); + this.addClass(m,ac,env); + this.syntax.offset=0; + } + this.error=undefined; + return true; + } catch (e) { + this.error='Compiling agent class file "'+file+'" failed: '+e+ + (ast && ast.errors.length?', in '+errLoc(ast):''); + if (options.error) + options.error(e+(ast && ast.errors.length?', in '+errLoc(ast):'')); + else { + this.log(this.error); + } + return false; + } +}; + +/** Register jamlib event handler for the (root) node +*/ +jam.prototype.register = function (node) { + this.on('agent', function (msg) { node.receive(msg) }); + this.on('signal', function (msg) { node.handle(msg) }); +} + +/** Disconnect and remove a virtual node from the world + * + */ +jam.prototype.removeNode = function (nodeid) { + this.world.removeNode(nodeid); +} + +/** Tuple space remove operation + */ +jam.prototype.rm = function (pat,all) { + return this.world.nodes[this.node].ts.extern.rm(pat,all); +} + + +/** Take an agent process snapshot executed currently on given node @node:number|string|undefined. + * If @file:string is not specified, a string containing the snapshot is + * returned, otehrwise it is saved to the file (text format. JSON+). + * If @node is undefined, the current node is used. + * If @kill is set, the agent is killed after taken the snapshot. + */ +jam.prototype.saveSnapshotOn = function (aid,node,file,kill) { + var snapshot,pro; + node=this.getNode(node); + if (!node) return; + // Look-up agent process .. + pro=node.getAgentProcess(aid); + if (!pro) return; + // Take snapshot od the process .. + snapshot=Aios.Code.ofCode(pro,false); + if (kill) Aios.killOn(aid,node); + // Save it .. + if (!file) return snapshot; + else if (fs) return fs.writeFileSync(file, snapshot, 'utf8'); +} + +jam.prototype.saveSnapshot = function (aid,file,kill) { + return this.saveSnapshotOn(aid,_,file,kill); +} + +/** Force a scheduler run immediately normally executed by the + * jam service loop. Required if there were externeal agent + * management, e.g., by sending signals. + */ +jam.prototype.schedule = function () { + if (this.loop) clearTimeout(this.loop); + this.loop=setTimeout(this.looping,1); +} + + +/** Access to JAM security module + * + */ +jam.prototype.security = Aios.Sec; + +/** Set current node + * + */ +jam.prototype.setCurrentNode=function (n) { + if (n>=0 && n < this.world.nodes.length) this.node=n; +} + +/** Send a signal to a specific agent 'to'. + * + */ +jam.prototype.signal=function (to,sig,arg,broadcast) { + var node=this.getNode(), + _process=Aios.current.process; + Aios.current.process=this.process; + if (!broadcast) + Aios.aios.send(to,sig,arg); + else + Aios.aios.broadcast(to,sig,arg); + + Aios.current.process=_process; + this.schedule(); +} + + +/** Start the JAM scheduler + * + */ +jam.prototype.start=function (callback) { + var self=this,cbl=CBL(callback); + // Start all connections if not already done + + this.world.nodes.forEach(function (node) { + node.connections.forEach(function (chan,kind) { + if (!chan) return; + if (chan.start) cbl.push(function (next) {chan.start(next)}); + }); + }); + cbl.start(); + + Aios.on('schedule',function () { + self.schedule(); + }); + + function loop() { + var loop = function () { + var nexttime,curtime; + if (self.verbose>2) self.log('loop: Entering scheduler #'+self.ticks); + self.ticks++; + + nexttime=Aios.scheduler(); + curtime=Aios.time(); + if (self.verbose>2) self.log('loop: Scheduler returned nexttime='+nexttime+ + ' ('+(nexttime>0?nexttime-curtime:0)+')'); + if (!self.run) return; + if (nexttime>0) + self.loop=setTimeout(loop,nexttime-curtime); + else if (nexttime==0) + self.loop=setTimeout(loop,1000); + else setImmediate(loop); + }; + self.loop = setTimeout(loop,1); + }; + this.looping=loop; + + Aios.config({iterations:100}); + + this.run=true; + this.world.start(); + this.log('Starting JAM loop .. '); + if (!this.options.scheduler) this.loop = setTimeout(loop,1); // Start internal scheduling loop +} + +/** Get agent process table info and other statistics + * + * type kind = 'process'|'agent'|'node'|'vm'|'conn' + */ + + +jam.prototype.stats = function (kind,id) { + var p,n,sys,conn,pro,agent,state,stats,allstats={},signals,node; + switch (kind) { + case 'process': + case 'agent': + for(n in this.world.nodes) { + stats={}; + node=this.world.nodes[n]; + for (p in node.processes.table) { + if (node.processes.table[p]) { + pro=node.processes.table[p]; + if (pro.signals.length == 0) signals=[]; + else signals = pro.signals.map(function (sig) {return sig[0] }); + agent=pro.agent; + if (pro.suspended) state='SUSPENDED'; + else if (pro.blocked) state='BLOCKED'; + else if (pro.dead) state='DEAD'; + else if (pro.kill) state='KILL'; + else if (pro.move) state='MOVE'; + else state='READY'; + stats[agent.id]={ + pid:pro.pid, + gid:pro.gid, + state:state, + parent:pro.agent.parent, + class:pro.agent.ac, + next:agent.next, + resources:Comp.obj.copy(pro.resources) + }; + if (signals.length) stats[agent.id].signals=signals; + } + } + allstats[node.id]=stats; + } + break; + case 'node': + return Comp.obj.copy(this.getNode(id).stats); + break; + case 'conn': + for(n in this.world.nodes) { + stats={}; + node=this.world.nodes[n]; + for (p in node.connections) { + conn=node.connections[p]; + if (conn) { + stats[p]={count:conn.count(),conn:conn.status('%')}; + } + } + allstats[node.id]=stats; + } + break; + case 'vm': + // Return VM memory usage in kB units and VM system information + if (process && process.memoryUsage) { + sys=process.memoryUsage(); + for ( p in sys) sys[p] = (sys[p]/1024)|0; + sys.v8 = process.versions && process.versions.v8; + sys.node = process.versions && process.versions.node; + sys.arch = process.arch; + sys.platform = process.platform; + sys.watchdog = Aios.watchdog?(Aios.watchdog.checkPoint?'semi':'full'):'none'; + return sys; + } + break; + } + if (this.world.nodes.length==1) return stats; + else return allstats; +} + +/** Stepping the scheduler loop + */ +jam.prototype.step = function (steps,callback) { + // TODO: accurate timing + var self=this, + milliTime=function () {return Math.ceil(Date.now())}, + curtime=Aios.time(),// Aios.time(); + lasttime=curtime; + + + function loop () { + var loop = function () { + var nexttime,curtime; + if (self.verbose>1) self.log('loop: Entering scheduler #'+self.ticks); + self.ticks++,self.steps--; + self.time=curtime=Aios.time(); + + // Execute scheduler loop + nexttime=Aios.scheduler(); + + curtime=Aios.time(); + if (self.verbose>1) self.log('loop: Scheduler returned nexttime='+nexttime+ + ' ('+(nexttime>0?nexttime-curtime:0)+')'); + if (self.steps==0 || !self.run) { + self.loop=none; + self.run=false; + self.time=curtime; + if (callback) callback(); + return; + } + if (nexttime>0) + self.loop=setTimeout(loop,nexttime-curtime); + else if (nexttime < 0) self.loop=setImmediate(loop); + else { + self.loop=none; + self.run=false; + self.time=curtime; + if (callback) callback(); + } + }; + self.loop = setTimeout(loop,1); + }; + this.looping=loop; + + Aios.config({iterations:1}); + this.steps=steps; + this.run=true; + if (this.time>0) current.world.lag=current.world.lag+(curtime-this.time); + this.time=curtime; + if (!this.options.scheduler) this.loop = setTimeout(loop,0); // Start internal scheduling loop +} + + +/** Stop the JAM scheduler and all network connections + * + */ +jam.prototype.stop=function (callback) { + this.run=false,cbl=CBL(callback); + this.log('Stopping JAM ..'); + Aios.off('schedule'); + if (this.loop) + clearTimeout(this.loop); + this.world.nodes.forEach(function (node) { + node.connections.forEach(function (chan,kind) { + if (!chan) return; + if (chan.stop) cbl.push(function (next) {chan.stop(next)}); + }); + }); + cbl.start(); +} +/** Tuple space test operation - non blocking + */ +jam.prototype.test = function (pat) { + return this.world.nodes[this.node].ts.extern.exists(pat); +} + +/** Tuple space testandset operation + */ +jam.prototype.ts = function (pat,callback) { + return this.world.nodes[this.node].ts.extern.ts(pat,callback); +} + +/** Get JAM time + */ +jam.prototype.time=function () { + return Aios.time(); +} + +/** Get JAMLIB version + */ +jam.prototype.version=function () { + return options.version; +} + + + +var Jam = function(options) { + var obj = new jam(options); + return obj; +}; + +/** Embedded cluster setup and start; + * Provided by process arguments + */ +if (environment.autosetup) { + try { + var _options=JSON.parse(environment.autosetup); + // console.log('['+process.pid+'] JAM cluster setup with options:',process.argv[_index+1]); + jam.prototype.setup=function () { + for(var p in _options) this.options[p]=_options[p]; + } + } catch (e) { + console.log('['+process.pid+'] JAM auto setup failed: '+e); + } +} + + +module.exports = { + Aios:Aios, + Comp:Comp, + Esprima:Esprima, + Io:Io, + Jam:Jam, + Json:Json, + environment:environment, + options:options +} +}; +BundleModuleCode['com/compat']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 30-3-15 by sbosse. + ** $VERSION: 1.23.1 + ** + ** $INFO: + ** + ** JavaScript-OCaML Compatibility Module + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Path = Require('com/path'); +var Sprintf = Require('com/sprintf'); + +/******************************* +** Some global special "values" +********************************/ + +/** A matching template pattern matching any value + * + * @type {undefined} + */ +global.any = undefined; +/** A matching template pattern matching any value + * + * @type {undefined} + */ +global._ = undefined; + +/** + * + * @type {null} + */ +global.none = null; +/** + * + * @type {null} + */ +global.empty = null; + +global.NL = '\n'; + +global.int = function (v) {return v|0}; +global.div = function (a,b) {return a/b|0}; + +if (!Object.prototype.forEach) { + Object.defineProperties(Object.prototype, { + 'forEach': { + value: function (callback) { + if (this == null) { + throw new TypeError('Not an object'); + } + var obj = this; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + callback.call(obj, obj[key], key, obj); + } + } + }, + writable: true + } + }); +} +/** Just transfer parent prototypes to child + * + */ +function inherit(child,parent) { + for(var p in parent.prototype) { + if (p == '__proto__') continue; + child.prototype[p]=parent.prototype[p]; + } +} + +/** Portable class inheritance and instanceOf polyfill + * + */ +// SomeObject.prototype.__proto__=SomeObject2.prototype; +// Child class inherits prototype from parent using __proto__ +function inheritPrototype(child,parent) { + var __proto__=child.__proto__; + child.prototype.__proto__=parent.prototype; + if (!__proto__) for(var p in parent.prototype) { + if (p == '__proto__') continue; + child.prototype[p]=parent.prototype[p]; + } +} +// Polyfill fir o instanceof c with inheritance check (checking __proto__) +function instanceOf(obj,cla) { + var p=obj.__proto__; + if (obj instanceof cla) return true; + while (p) { + if (p === cla.prototype) return true; + p=p.__proto__ + } + return false; +} +// Polyfill for __defineGetter__ / __defineSetter__ +function defineGetter(cla,prop,fun) { + Object.defineProperty(cla.prototype,prop,{ + configurable:true, + get:fun + }); +} +function defineSetter(cla,prop,fun) { + Object.defineProperty(cla.prototype,prop,{ + configurable:true, + set:fun + }); + +} + +global.inherit = inherit; +global.inheritPrototype = inheritPrototype; +global.instanceOf = instanceOf; +global.defineGetter = defineGetter; +global.defineSetter = defineSetter; + +/** + * + */ +var assert = function(condmsg) { + if (condmsg != true) { + Io.out('** Assertion failed: '+condmsg+' **'); + Io.stacktrace(); + throw Error(condmsg); + } +}; +global.assert=assert; + +function forof(obj,f) { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = obj[Symbol.iterator](), _step; + !(_iteratorNormalCompletion = (_step = _iterator.next()).done); + _iteratorNormalCompletion = true) { + element = _step.value; + + f(element); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } +} + + +global.forof=forof; + +/** OBJ + * + */ +var obj = { + /** Compact an object: + * [{a:b},[c:d},..] -> {a:b,c:d,..} + * {a:[b]} -> {a:b} + * + */ + compact: function (o) { + var a; + if (obj.isArray(o)) { + if (o.length==1 && obj.isObject(o[0])) return obj.compact(o[0]); + else return o; + } else if (obj.isObject(o)) for (a in o) { + var elem=o[a]; + o[a]=obj.compact(elem); + } + return o; + }, + copy: function (o) { + if (o === null || typeof o !== 'object') { + return o; + } + + var temp = (o instanceof Array) ? [] : {}; + for (var key in o) { + temp[key] = obj.copy(o[key]); + } + + return temp; + }, + equal: function (o1,o2) { + if (!o1 || !o2) return false; + for(var i in o1) if (o1[i]!=o2[i]) return false; + for(var i in o2) if (o1[i]!=o2[i]) return false; + return true; + }, + extend: function (o1,o2) { + for(var i in o2) o1[i]=o2[i]; + return o1; + }, + find: function(obj,fun) { + var p; + for(p in obj) { + if (fun(obj[p],p)) return obj[p]; + } + }, + + hasProperty: function (o,p) { + return o[p]!=undefined || (p in o); + }, + head:function(o) { + for (var p in o) return p; + return undefined; + }, + // transfer src attributes to dst recusively (no object overwrite) + inherit: function (dst,src) { + for(var i in src) { + if (typeof dst[i] == 'object' && typeof src[i] == 'object') + inherit(dst[i],src[i]); + else if (typeof dst[i] == 'undefined') + dst[i]=src[i]; + } + return dst; + }, + isArray:function (o) { + if (o==_ || o ==null) return false; + else return typeof o == "array" || (typeof o == "object" && o.constructor === Array); + }, + isMatrix:function (o) { + if (o==_ || o ==null) return false; + else return obj.isArray(o) && + obj.isArray(o[0]); + }, + isEmpty: function (o) { + for(var prop in o) { + if (o[prop]!=undefined) return false; + } + return true; + }, + isFunction: function (o) { + return typeof o == "function"; + }, + isObj:function (o) { + return typeof o == "object"; + }, + isObject:function (o) { + return typeof o == "object"; + }, + isRegex: function (o) { + return o instanceof RegExp; + }, + isString: function (o) { + return typeof o == "string" || (typeof o == "object" && o.constructor === String); + }, + isNumber: function (o) { + return typeof o == "number" || (typeof o == "object" && o.constructor === Number); + }, + + + iter: function(obj,fun) { + var p; + for(p in obj) { + fun(obj[p],p) + } + } +}; + +/** ARRAY + * + */ +var array = { + /** Evaluate a function returning a boolean value for each member of the array and + * compute the boolean conjunction. + * + * @param {* []} array + * @param {function(*,number)} fun + */ + and: function(array,fun) { + var res=true; + var i=0; + var len=array.length; + for(i=0;i|string|Blob|ArrayBuffer} + */ + copy: function(src,dst) { + var i; + if (dst) { + for(i in src) dst[i]=src[i]; + } else return src.slice(); + }, + /** Create a new array with initial element values. + * + * @param length + * @param init + * @returns {Array} + */ + create : function(length,init) { + var arr = [], i = length; + while (i--) { + arr[i] = init; + } + return arr; + }, + /** Create a matrix (array of array) with initial element values. + * + */ + create_matrix : function(rows,cols,init) { + var m = []; + var r = []; + var i,j; + for (i = 0; i < rows; i++) { + r=[]; + for(j=0;j=0;i--) { + fun(array[i],i) + } + }, + /** Return last element of array. + * + */ + last : function(array) { + var len=array.length; + if (len==0) return none; + else return array[len-1]; + }, + + length : function(array) { + return array.length; + }, + /** + * + * @param {* []} array1 + * @param {* []} array2 + * @param {function(*,*,number)} fun + * @returns {* []} + */ + map2: function(array1,array2,fun) { + var i=0; + assert((array1.length == array2.length)||('Array.map2: arrays of different lengths')); + var len=array1.length; + var res=[]; + for(i=0;i1) { + var hd = this.head(array); + var tl = this.tail(array); + fun_hdtl(hd,tl); + } else fun_hdtl(this.head(array),[]); + }, + /** + * + * @param {* []} array + * @param {Function} fun_hd1hd2 - function(hd1,hd2) + * @param {Function} [fun_hdtl] - function(hd,tl) + * @param {Function} [fun_empty] - function() + */ + match2: function(array,fun_hd1hd2,fun_hdtl,fun_empty) { + if (array.length == 0 && fun_empty) + fun_empty(); + else if (array.length == 2) { + var hd1 = this.head(array); + var hd2 = this.second(array); + fun_hd1hd2(hd1,hd2); + } + else if (array.length>1 && fun_hdtl) { + var hd = this.head(array); + var tl = this.tail(array); + fun_hdtl(hd,tl); + } else if (fun_hdtl) fun_hdtl(this.head(array),[]); + }, + /** Return the maximum number of an array applying + * an optional mapping function. + * + * @param {* []} array + * @param [fun] + * @returns {number|undefined} + */ + max : function (array,fun) { + var res=undefined; + for(var i in array) { + var num; + if (fun) num=fun(array[i]); else num=array[i]; + if (!obj.isNumber(num)) return undefined; + if (res==undefined) res=num; else res=pervasives.max(res,num); + } + return res; + }, + /** Return the minimum number of an array applying + * an optional mapping function. + * + * @param {* []} array + * @param [fun] + * @returns {number|undefined} + */ + min : function (array,fun) { + var res=undefined; + for(var i in array) { + var num; + if (fun) num=fun(array[i]); else num=array[i]; + if (!obj.isNumber(num)) return undefined; + if (res==undefined) res=num; else res=pervasives.min(res,num); + } + return res; + }, + /** Check for an element in the array. + * + * @param {(number|string|boolean) []} array + * @param {number|string|boolean} element + * @returns {boolean} + */ + member: function(array,element) { + var i,exist; + var len=array.length; + exist=false; + loop: for(i=0;i ['barney', 'fred'] + */ + pluck: function(collection, key) { + return collection.map(function(object) { + return object == null ? undefined : object[key]; + }); + }, + /* + ** Push/pop head elements (Stack behaviour) + */ + /** Remove and return top element of array. + * + * @param array + * @returns {*} + */ + pop : function(array) { + var element=array[0]; + array.shift(); + return element; + }, + print: function(array) { + var i; + var len=array.length; + var str='['; + for(i=0;i [1,2,6] + * @param {* []} array + * @returns {* []} + */ + remove: function(array,begin,end) { + var i,a; + if (end==undefined) end=begin+1; + if (begin<0 || end >= array.length) return []; + a=array.slice(0,begin); + for(i=end;i use remove!!! split should return two arrays!! + * + * @param array + * @param pos + * @param [len] + * @param element + */ + split: function(array,pos,len) { + if (pos==0) return array.slice((len||1)); + else { + var a1=array.slice(0,pos); + var a2=array.slice(pos+(len||1)); + return a1.concat(a2); + } + }, + /** Return the sum number of an array applying + * an optional mapping function. + * + * @param {* []} array + * @param [fun] + * @returns {number|undefined} + */ + sum : function (array,fun) { + var res=0; + for(var i in array) { + var num=0; + if (fun) num=fun(array[i]); else num=array[i]; + if (!obj.isNumber(num)) return undefined; + res += num; + } + return res; + }, + /** Return a new array w/o the head element (or optional + * w/o the first top elements). + * + */ + tail : function(array,top) { + var array2=array.slice(); + array2.shift(); + if (top) for(;top>1;top--) array2.shift(); + return array2; + }, + /** Return union of two sets (== conjunction set) + * + * @param {* []} set1 + * @param {* []} set2 + * @param {function} [fun] Equality test + * @returns {* []} + */ + union : function(set1,set2,fun) { + var i,j,res = []; + for (i in set1) { + for (j in set2) { + if (fun != undefined && fun(set1[i],set2[j])) res.push(set1[i]); + else if (fun == undefined && set1[i]==set2[j]) res.push(set1[i]); + } + } + return res; + }, + + /** + * Creates a duplicate-free version of an array + */ + unique: function(array) { + var length = array ? array.length : 0; + function baseUniq(array) { + var index = -1, + length = array.length, + seen, + result = []; + + seen = result; + outer: + while (++index < length) { + var value = array[index]; + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === value) { + continue outer; + } + } + result.push(value); + } + return result; + } + if (!length) { + return []; + } + return baseUniq(array); + }, + + /** + * Creates an array excluding all provided values + * without([1, 2, 1, 3], 1, 2); + * // => [3] + */ + without: function () { + var array, + values=[]; + for(var i in arguments) { + if (i==0) array=arguments[0]; + else values.push(arguments[i]); + } + return array.filter(function (e) { + return values.indexOf(e) == -1; + }); + }, + /** Test for zero elements {0, '', false, undefined, ..} + */ + zero: function (array) { + for(var i in array) if (!!array[i]) return false; + return true; + }, +}; + +/** STRING + * + */ +var string = { + /** Is pattern conatined in template? + * + */ + contains: function (template,pattern) { + return template.indexOf(pattern)>-1; + }, + copy: function(src) { + var i; + var dst=''; + for(i=0;i>4) & 0xf).toString(16))+ + ((n&0xf).toString(16)); + case 4: return (((n>>12) & 0xf).toString(16)+ + ((n>>8) & 0xf).toString(16)+ + ((n>>4) & 0xf).toString(16)+ + (n&0xf).toString(16)); + case 6: return (((n>>20) & 0xf).toString(16)+ + ((n>>16) & 0xf).toString(16)+ + ((n>>12) & 0xf).toString(16)+ + ((n>>8) & 0xf).toString(16)+ + ((n>>4) & 0xf).toString(16)+ + (n&0xf).toString(16)); + case 8: return (((n>>28) & 0xf).toString(16)+ + ((n>>24) & 0xf).toString(16)+ + ((n>>20) & 0xf).toString(16)+ + ((n>>16) & 0xf).toString(16)+ + ((n>>12) & 0xf).toString(16)+ + ((n>>8) & 0xf).toString(16)+ + ((n>>4) & 0xf).toString(16)+ + (n&0xf).toString(16)); + default: return 'format_hex??'; + } + }, + /** + * + * @param {string} str + * @param {number} index + * @returns {string} + */ + get: function (str,index) { + assert((str != undefined && index < str.length && index >= 0)||('string.get ('+str.length+')')); + return str.charAt(index); + }, + isBoolean: function (str) { + return (str=='true' || str=='false') + }, + isNumeric: function (str) { + return !isNaN(parseFloat(str)) && isFinite(str); + }, + isText: function (s) { + var is_text=true; + string.iter(s,function (ch,i) { + string.match(ch,[ + ['a','z',function () {}], + ['A','Z',function () {}], + ['0','9',function () {if (i==0) is_text=false;}], + function () {is_text=false;} + ]); + }); + return is_text; + }, + /** + * + * @param {string} str + * @param {function(string,number)} fun + */ + iter: function(str,fun) { + var i; + var len=str.length; + for (i = 0; i < len; i++) { + var c = str.charAt(i); + fun(c,i); + } + }, + /** + * + * @param str + * @returns {*} + */ + length: function(str) { + if (str!=undefined) return str.length; + else return 0; + }, + /** + * + * @param str + * @returns {string} + */ + lowercase : function (str) { + return str.toLowerCase(); + }, + /** + * + * @param {number} size + * @param {string} init + * @returns {string} + */ + make: function(size,init) + { + var i; + var s=''; + for(i=0;i,,..],fun] | [:string,:string,fun] | fun) [] + */ + match: function(str,cases) { + var i,j; + var cas,cex,cv; + for(i in cases) { + cas=cases[i]; + if (obj.isArray(cas)) { + switch (cas.length) { + case 2: + // Multi-value-case + cex=cas[0]; + if (!obj.isArray(cex)) { + if (this.equal(str,cex)) { + cas[1](); + return; + } + } else { + for(j in cex) { + cv=cex[j]; + if (this.equal(str,cv)) { + cas[1](); + return; + } + } + } + break; + case 3: + // Character range check + try { + j=pervasives.int_of_char(str); + if (j>= pervasives.int_of_char(cas[0]) && j<=pervasives.int_of_char(cas[1])) { + cas[2](str); + return; + } + } catch(e) { + return + }; + break; + case 1: + cas[0](str); // Default case - obsolete + return; + default: + throw 'String.match #args'; + } + } else if (obj.isFunction(cas)) { + // Default case + cas(str); + return; + } + } + }, + /** Pad a string on the left (pre-str.length) if pre>0, + * right (post-str.length) if post>0, or centered (pre>0&post>0). + * + */ + + pad: function (str,pre,post,char) { + var len = str.length; + if (pre>0 && post==0) return string.make(len-pre,char||' ')+str; + else if (post>0 && pre==0) return str+string.make(post-len,char||' '); + else return string.make(len-pre/2,char||' ')+str+string.make(len-post/2,char||' '); + }, + /** + * + * @param str + * @param pos + * @param len + * @returns {Number} + */ + parse_hex: function (str,pos,len) { + // parse a hexadecimal number in string 'str' starting at position 'pos' with 'len' figures. + return parseInt(this.sub(str,pos,len),16); + }, + /** Return the sub-string after a point in the source string ('.' or optional point string). + * If there is no splitting point, the original string is returned. + * + * @param str + * @param [point] + * @returns {string} + */ + postfix: function (str,point) { + var n = str.indexOf(point||'.'); + if (n <= 0) return str; + else return str.substr(n+1); + }, + /** Return the sub-string before a point in the source string ('.' or optional point string) + * If there is no splitting point, the original string is returned. + * + * @param str + * @param [point] + * @returns {string} + */ + prefix: function (str,point) { + var n = str.indexOf(point||'.'); + if (n <= 0) return str; + else return str.substr(0,n); + }, + replace_first: function (pat,repl,str) { + return str.replace(pat,repl); + }, + replace_all: function (pat,repl,str) { + return str.replace('/'+pat+'/g',repl); + }, + /** + * + * @param str + * @param index + * @param char + * @returns {string} + */ + set: function (str,index,char) { + assert((str != undefined && index < str.length && index >= 0)||'string.get'); + return str.substr(0, index) + char + str.substr(index+1) + }, + /** + * + * @param delim + * @param str + * @returns {*|Array} + */ + split: function (delim,str) { + return str.split(delim); + }, + startsWith : function (str,head) { + return !str.indexOf(head); + }, + /** Return a sub-string. + * + * @param str + * @param off + * @param [len] If not give, return a sub-string from off to end + * @returns {string} + */ + sub: function (str,off,len) { + if (len) + return str.substr(off,len); + else + return str.substr(off); + }, + /** Remove leading and trailing characters from string + * + * @param str + * @param {number} pref number of head characters to remove + * @param {number} post number of tail characters to remove + * @returns {*} + */ + trim: function (str,pref,post) { + if (str.length==0 || + pref>str.length || + post>str.length || + pref < 0 || post < 0 || + (pref==0 && post==0) + ) return str; + return str.substr(pref,str.length-pref-post); + }, + /** Return a string with all characters converted to uppercase letters. + * + * @param str + * @returns {string} + */ + uppercase : function (str) { + return str.toUpperCase(); + }, + /** Return a string with first character converted to uppercase letter. + * + * @param str + * @returns {string} + */ + Uppercase : function (str) { + var len = str.length; + if (len > 1) { + var head = str.substr(0,1); + var tail = str.substr(1,len-1); + return head.toUpperCase()+tail.toLowerCase() + } if (len==1) return str.toUpperCase(); + else return ''; + } +}; + +/** RANDOM + * + */ +var rnd = Math.random; +/* Antti Syk�ri's algorithm adapted from Wikipedia MWC +** Returns a random generator function [0.0,1.0| with seed initialization +*/ +var seeder = function(s) { + var m_w = s; + var m_z = 987654321; + var mask = 0xffffffff; + + return function() { + m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; + m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; + + var result = ((m_z << 16) + m_w) & mask; + result /= 4294967296; + + return result + 0.5; + } +} + +var random = { + float: function(max) { + return rnd()*max + }, + int: function(max) { + return Math.floor(rnd()*max+0) + }, + // integer + interval: function(min,max) { + return Math.round(min+rnd()*(max-min)) + }, + // float + range: function(min,max) { + return min+rnd()*(max-min) + }, + seed: function (s) { + // Create a new initialized random generator + rnd=seeder(s); + } +}; + +/** PRINTF + * + */ +var printf = { + /** Trim string(s). + * + * @param str + * @param indent + * @param [width] + * @param {string} [tab] + * @returns {string} + */ + align: function (str,indent,width,tab) { + var lines = string.split('\n',str); + var form = ''; + var sp = printf.spaces(indent); + var spbreak = sp; + + array.iter(lines,function(line){ + var rest; + function breakit(spbreak,str) { + if (width < (str.length + spbreak.length)) { + return spbreak+string.sub(str,0,width-spbreak.length)+'\n'+ + breakit(spbreak,string.sub(str,width-spbreak.length,str.length-width+spbreak.length)); + } else return spbreak+str+'\n'; + } + if (width && width < (line.length + indent)) { + if (tab) { + var pos = string.find(tab,line); + if (pos > 0 && pos < width) spbreak=printf.spaces(pos+indent+1); + else spbreak=sp; + } + form=form+sp+string.sub(line,0,width-indent)+'\n'; + rest=string.sub(line,width-indent,line.length-width+indent); + form=form+breakit(spbreak,rest); + } + else + form=form+sp+line+'\n'; + }); + return form; + }, + /** Format a list of array elements using the (optional) mapping + * function and the separator (optional, too, default is ','). + * + */ + list: function (array,fun,sep) { + var i, str=''; + if (sep==undefined) sep=','; + if (fun==undefined) fun=function (s) {return s;}; + if (!obj.isArray(array)) array=[array]; + for (i in array) { + if (str==='') str=fun(array[i]); + else str=str+sep+fun(array[i]); + } + return str; + }, + /** + * + * @param n + * @returns {string} + */ + spaces: function (n){ + return string.make(n,' '); + }, + /** Formatted printer (simplified) + * + * @param {* []} args (['%format',arg]|string) [] format=%s,%d,%f,%c,%x,%#d,%#s,.. + * @returns {string} + */ + sprintf2: function(args) { + var str=''; + array.iter(args,function(fmtarg) { + var len, n,fs; + if (obj.isArray(fmtarg)) { + if (fmtarg.length==2) { + var fmt=fmtarg[0]; + var arg=fmtarg[1]; + var fc=''; + var fn=0; + string.iter(fmt,function(c) { + if (c=='s' || c=='d' || c=='f' || c=='x') { + fc=c; + } else if (c!='%') { + fn=fn*10; + n=parseInt(c); + if (!isNaN(n)) fn=fn+n; + } + }); + if (fc=='s' && obj.isString(arg)) { + str=str+arg; + if (fn!=0) { + len=arg.length; + if (len 0 && path[0] == '/'); + }, + /** + * + * @param pathl + * @param absolute + * @returns {string} + */ + join: function (pathl,absolute) { + var path=(absolute?'/':''); + array.iter(pathl,function (name,index) { + if (index>0) { + path=path+'/'+name; + } + else { + path=path+name; + } + }); + return path; + }, + /** + * + * @param path + * @returns {string} + */ + normalize : function (path) { + return Path.normalize(path) + }, + /** + * + * @param path + * @returns {*} + */ + path_absolute: function (path) { + if (this.is_relative(path)) { + var workdir = Io.workdir(); + return this.path_normalize(workdir + '/' + path); + } else return this.path_normalize(path); + }, + /** Duplicate of Path.normalize!? + * + * @param path + * @returns {string} + */ + path_normalize: function (path) { + var i; + if (string.equal(path, '')) path = '/'; + var relpath = !(string.get(path, 0) == '/'); + var pathlist = path.split('/'); + var pathlist2 = pathlist.filter(function (s) { + return (!string.equal(s, '') && !string.equal(s, '.')) + }); + var pathlist3 = []; + array.iter(pathlist2, function (pe) { + if (!string.equal(pe, '..')) { + array.push(pathlist3, pe) + } else { + if (pathlist3.length == 0) return ''; + else + pathlist3 = array.tail(pathlist3); + } + }); + var path2 = ''; + i = 0; + array.iter(pathlist3, function (pe) { + var sep; + if (i == 0) sep = ''; else sep = '/'; + path2 = pe + sep + path2; + i++; + }); + if (relpath) return path2; else return '/' + path2; + }, + removeext: function (path) { + return path.substr(0, path.lastIndexOf('.')); + } +}; + +/** PERVASIVES + * + * + */ +var pervasives = { + assert:assert, + char_of_int: function (i) {return String.fromCharCode(i)}, + div: function(a,b) {return a/b|0;}, + failwith: function(msg) {Io.err(msg);}, + float_of_string: function(s) {var num=parseFloat(s); if (isNaN(num)) throw 'NaN'; else return num;}, + int_of_char: function(c) {return c.charCodeAt()}, + int_of_float: function(f) {return f|0;}, + int_of_string: function(s) { + var num=parseInt(s); if (isNaN(num)) throw 'NaN'; else return num; + }, + + /** Try to find a value in a search list and return a mapping value. + * + * @param {*} value + * @param {* []} mapping [testval,mapval] [] + * @returns {*} + */ + map: function(value,mapping) { + function eq(v1,v2) { + if (v1==v2) return true; + if (obj.isString(v1) && obj.isString(v2)) return string.equal(v1,v2); + return false; + } + if (!array.empty(mapping)) { + var hd=array.head(mapping); + var tl=array.tail(mapping); + if (eq(hd[0],value)) return hd[1]; + else return pervasives.map(value,tl); + } else return undefined; + }, + /** Apply a matcher function to a list of cases with case handler functions. + * A case is matched if the matcher function returns a value/object. + * + * The result of the matcher function is passed as an argument ot the case handler function. + * The return value of the case handler fucntion is finally returned by this match function + * or undefined if there was no matching case. + * + * @param {function(*,*):*} matcher function(expr,pat) + * @param {*} expr + * @param {*[]} cases (pattern,handler function | handler function) [] + * @returns {*|undefined} + */ + match: function (matcher,expr,cases) { + var ret = undefined; + array.iter_break(cases, function (match) { + var quit, succ, pat, fun; + + if (match.length == 2) { + /* + ** Pattern, Function + */ + pat = match[0]; + fun = match[1]; + succ = matcher(expr, pat); + if (succ) ret = fun(succ); + quit = succ!=undefined; + } else if (match.length == 1) { + /* + ** Default case, Function + */ + fun = match[0]; + ret = fun(); + quit= true; + } + return quit; + }); + return ret; + }, + mtime: function () {var time = new Date(); return time.getTime();}, + min: function(a,b) { return (ab)?a:b}, + string_of_float: function(f) {return f.toString()}, + string_of_int: function(i) {return i.toString()}, + string_of_int64: function(i) {return i.toString()}, + time: function () {var time = new Date(); return (time.getTime()/1000)|0;} +}; + +/** BIT + * + */ +var bit = { + get: function (v,b) {return (v >> b) && 1;}, + isSet: function (v,b) {return ((v >> b) && 1)==1;}, + set: function (v,b) {return v & (1 << b);} +}; + +/** ARGS + * + */ +var args = { + /** Parse process or command line arguments (array argv). The first offset [1] arguments are + ** ignored. The numarg pattern '*' consumes all remaining arguments. + * + * @param {string []} argv + * @param {*[]} map [,,]|[] [] + * @param {number} [offset] + */ + parse: function(argv,map,offset) { + var shift=undefined, + in_shift=0, + shift_args=[], + names, + mapfun, + numarg, + len=argv.length; + + if (offset==undefined) offset=1; + + argv.forEach(function (val, index) { + var last=index==(len-1); + if(index>=offset) { + if (in_shift==0) { + array.check(map,function (onemap) { + assert(onemap!=undefined||'map'); + if (onemap.length==3) { + names = onemap[0]; + numarg = onemap[1]; + mapfun = onemap[2]; + if (!obj.isArray(names)) names=[names]; + var found = array.find(names,function (name) { + if (string.equal(val, name)) return name; else _; + }); + if (found) { + if (numarg==0) mapfun(found); + else { + in_shift=numarg; + shift_args=[]; + shift=mapfun; + } + return true; + } + } else if (obj.isFunction(onemap)) { + onemap(val); + return true; + } else if (onemap.length==1) { + mapfun = onemap[0]; + mapfun(val); + return true; + } + return false; + }); + } else { + shift_args.push(val); + if (in_shift!='*') in_shift--; + if (in_shift==0 && shift!=undefined) { + numarg=shift_args.length; + switch (numarg) { + case 0: shift(val);break; + case 1: shift(shift_args[0],val); break; + case 2: shift(shift_args[0],shift_args[1],val); break; + case 3: shift(shift_args[0],shift_args[1],shift_args[2],val); break; + default: break; + } + shift=undefined; + } else if (in_shift=='*' && last) shift(shift_args); + } + } + }); + } + +}; + +/** HASHTBL + * + */ +var hashtbl = { + add: function(hash,key,data) { + hash[key]=data; + }, + create: function(initial) { + return []; + }, + empty: function(hash) { + for (var key in hash) return false; + return true; + }, + find: function(hash,key) { + return hash[key]; + }, + invalidate: function(hash,key) { + hash[key]=undefined; + }, + iter: function(hash,fun) { + for (var key in hash) { + if (hash[key]!=undefined) fun(key,hash[key]); + } + }, + mem: function(hash,key) { + return hash[key] != undefined; + }, + remove: function(hash,key) { + // TODO: check, its wrong! + if (!hash.hasOwnProperty(key)) + return; + if (isNaN(parseInt(key)) || !(hash instanceof Array)) + delete hash[key]; + else + hash.splice(key, 1) + } +}; + +var types = []; +/** + * + * @param name + * @returns {number} + */ +function register_type(name) { + var typoff = 1000+types.length*1000; + if (array.member(types,name)) throw('[COMP] register_type: type '+name+' exists already.'); + types.push(name); + return typoff; +} + +/** + * + * @typedef {{v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tuple + */ +/** + * + * @typedef {{t:number, v1:*, v2:*, v3:*, v4:*, v5:*, v6:*, v7:*, v8:*, v9:* }} tagged_tuple + */ + +module.exports = { + args:args, + assert: assert, + array:array, + bit:bit, + div:pervasives.div, + filename:filename, + hashtbl:hashtbl, + isNodeJS: function () { + return (typeof global !== "undefined" && + {}.toString.call(global) == '[object global]'); + }, + obj:obj, + pervasives:pervasives, + printf:printf, + random:random, + string:string, + isArray: obj.isArray, + isString: obj.isString, + isNumber: obj.isNumber, + + register_type:register_type, + /** + * + * @param tag + * @param [val1] + * @param [val2] + * @param [val3] + * @returns {(tagged_tuple)} + */ + Tuple: function (tag,val1,val2,val3) { + if(val3) return {t:tag,v1:val1,v2:val2,v3:val3}; + else if (val2) return {t:tag,v1:val1,v2:val2}; + else if (val1) return {t:tag,v1:val1}; + else return {t:tag}; + } +}; +}; +BundleModuleCode['jam/aios']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $VERSION: 1.37.4 + ** $RCS: $Id: aios.js,v 1.5 2017/06/19 17:18:39 sbosse Exp sbosse $ + ** $INFO: + ** + ** JavaScript AIOS: Agent Execution & IO System with Sandbox environment. + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var Name = Require('com/pwgen'); +var Conf = Require('jam/conf'); +var Code = Require('jam/code'); +var Sig = Require('jam/sig'); +var Node = Require('jam/node'); +var Proc = Require('jam/proc'); +var Sec = Require('jam/security'); +var Ts = Require('jam/ts'); +var World = Require('jam/world'); +var Chan = Require('jam/chan'); +var Mobi = Require('jam/mobi'); +var Ml = Require('ml/ml'); +var Nn = Require('nn/nn'); +var Simu = global.config.simulation?Require(global.config.simulation):none; +var Json = Require('jam/jsonfn'); +var Net = Require('dos/network'); +var watchdog = Require('jam/watchdog'); +var util = Require('util'); + +var aiosExceptions = ['CREATE','MOVE','SIGNAL','SCHEDULE','WATCHDOG','EOL','KILL']; +var aiosEvents = ['agent','agent+','agent-','signal','signal+','node+','node-']; + +// AIOS OPTIONS // +var options = { + version: "1.37.4", + // Fast dirty process forking and migration between logical nodes (virtual) + // w/o using of/toCode? + fastcopy:false, + // Using JSON+ (json compliant) or JSOB (raw object) in to/ofCode? + json:false, + // logging parameters + log : { + node:false, + agent:true, + parent:false, + pid:false, // agent process id! + host:false, // host id (os pid) + time:false, // time in milliseconds + Time:true, // time in hour:minute:sec format + class:false + }, + // agent ID generator name options + nameopts : {length:8, memorable:true, lowercase:true}, + // Disable agent checkpointing and resource control + nolimits:false, + // No statistics + nostats:false, + // Use process memory for resource control? (slows down JAM execution) + useproc: false, + // Verbosity level + verbose:0, + + // Default maximal run-time of an agent process activity + TIMESCHED:200, + // Default maximal run-time of an agent process + TIMEPOOL:5000, + // Default maximal memory of an agent (code+data) + MEMPOOL:50000, + // Maximal number of tuple generations on current node + TSPOOL:1000, + // Maximal number of tuple generations on current node + AGENTPOOL:20, + // Default minimal run-time costs below 1ms resolution (very short activity executions) + MINCOST:0.1, + // Default maximal scheduler run-time + RUNTIME:1000 +}; + +var timer, + ticks=0, // scheduler execution counter! + iterations=0, + events={}; + +// Current execution environment (scheduler: global scheduler) +var current = {process:none,world:none,node:none,network:none,error:none,scheduler:none}; + +// System clock in ms or hh:mm:ss format +function clock (ms) { + if (!ms) return Io.Time(); + else return Io.time(); +} + +// AIOS smart logging function for Agents +var logAgent = function(){ + var msg=''; + arguments.forEach(function (arg,i) { + if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg); + else msg += (i>0?' '+Io.inspect(arg):Io.inspect(arg)); + }); + (Aios.printAgent||Aios.print)('['+(options.log.host?(process.pid+'.'):'')+ + (options.log.world&¤t.world?(current.world.id+'.'):'')+ + (options.log.node&¤t.node?(current.node.id+'.'):'')+ + (options.log.class&¤t.process?(current.process.agent.ac+'.'):'')+ + (options.log.agent&¤t.process?(current.process.agent.id):'')+ + (options.log.parent&¤t.process?('<'+current.process.agent.parent):'')+ + (options.log.pid&¤t.process?('('+current.process.pid+')'):'')+ + (options.log.time?(':'+time()):'')+ + (options.log.Time?(':'+Io.Time()):'')+ + '] '+msg) +} + +// AIOS smart logging function for AIOS internals (w/o agent messages) +var logAIOS = function(){ + var msg=''; + arguments.forEach(function (arg,i) { + if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg); + else msg += (i>0?' '+Io.inspect(arg):Io.inspect(arg)); + }); + Aios.print('['+(options.log.host?(process.pid+'.'):'')+ + (options.log.world&¤t.world?(current.world.id+'.'):'')+ + (options.log.node&¤t.node?(current.node.id+'.'):'')+ + (options.log.pid&¤t.process?('('+current.process.pid+')'):'')+ + (options.log.time?(':'+time()):'')+ + (options.log.Time?(':'+Io.Time()):'')+ + '] '+msg) +} + +// Generic AIOS messages +var log = function () { + var msg=''; + arguments.forEach(function (arg,i) { + if (typeof arg == 'string' || typeof arg == Number) msg += (i>0?', '+arg:arg); + else msg += (i>0?', '+Io.inspect(arg):Io.inspect(arg)); + }); + Aios.print('[AIOS] '+msg); +} + +/** Sandbox module environment for agents (level 0): Untrusted, + * minimal set of operations (no move, fork, kill(others),..) + */ +var aios0 = { + abs:Math.abs, + add: function (a,b) { + var res,i; + if (Comp.obj.isNumber(a) && Comp.obj.isNumber(b)) return a+b; + if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) { + if (a.length!=b.length) return none; + res=Comp.array.copy(a); + for (i in a) { + res[i]=aios0.add(a[i],b[i]); + } + return res; + } + if (Comp.obj.isArray(a) && Comp.obj.isFunction(b)) { + res=Comp.array.copy(a); + for (i in a) { + res[i]=aios0.add(a[i],b.call(current.process.agent,a[i])); + } + return res; + } + if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) { + res={}; + for (i in a) { + res[i]=aios0.add(a[i],b[i]); + } + return res; + } + return none; + }, + Capability: Sec.Capability, + clock: clock, + concat: function (a,b) { + var res,i; + if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) + return Comp.array.concat(a,b); + else if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) { + res={}; + for (i in a) { + res[i]=a[i]; + } + for (i in b) { + res[i]=b[i]; + } + return res; + } else if (Comp.obj.isString(a) && Comp.obj.isString(b)) { + return a+b; + } else + return undefined; + }, + contains : function (o,e) { + // e can be a scalar or array of values + if (Comp.obj.isArray(o)) + return Comp.array.contains(o,e); + else if (Comp.obj.isObj(o) && (Comp.obj.isString(e) || Comp.obj.isNumber(e))) + return o[e] != undefined; + else if (Comp.obj.isString(o) && Comp.obj.isString(e)) + return o.indexOf(e)!=-1 + }, + copy : function (o) { + var _o,p; + if (Comp.obj.isArray(o)) return o.slice(); + else if (Comp.obj.isObject(o)) { + _o={}; + for(p in o) _o[p]=aios0.copy(o[p]); + return _o; + } + else if (Comp.obj.isString(o)) + return o.slice(); + else return o; + }, + div: div, + dump: function (x) { + if (x=='res') x=Comp.obj.copy(current.process.resources); + if (x=='?') x=this; + logAgent(util.inspect(x)); }, + empty: function (o) { + if (Comp.obj.isArray(o) || Comp.obj.isString(o)) return o.length==0; + else if (Comp.obj.isObj(o)) return Comp.obj.isEmpty(o); + else return false; + }, + equal: function (a,b) { + var i; + if (Comp.obj.isNumber(a) && Comp.obj.isNumber(b)) return a==b; + else if (Comp.obj.isArray(a) && Comp.obj.isArray(b)) { + if (a.length!=b.length) return false; + for (i in a) { + if (!aios0.equal(a[i],b[i])) return false; + } + return true; + } + else if (Comp.obj.isObj(a) && Comp.obj.isObj(b)) { + for (i in a) { + if (!aios0.equal(a[i],b[i])) return false; + } + return true; + } + else if (Comp.obj.isString(a) && Comp.obj.isString(b)) + return (a.length==b.length && a==b) + return false; + }, + filter:function (a,f) { + var element,res=[],len,len2,i,j,found; + if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) { + res=[]; + len=a.length; + for(i=0;iv) {v=a0; vi=i}; + }); + if (vi!=undefined) return a[vi]; + } else return Math.max(a,b); + }, + me: function () { + return current.process.agent.id; + }, + min: function (a,b) { + if (Comp.obj.isArray(a)) { + var f=function (x) {return x},v,vi; + if (Comp.obj.isFunction(b)) f=b; + Comp.array.iter(a,function (a0,i) { + a0=f(a0); + if (v==undefined || a00) + return a[Comp.random.int(n)]; + else + return none; + } else if (Comp.obj.isObj(a)) { + n=0; + for(p in a) if (a[p]!=undefined) n++; + i=Math.min(Comp.random.interval(0,n),n-1); + n=0; + for(p in a) if (a[p]!=undefined) {if (n==i) return a[p]; n++}; + } else if (b==undefined) {b=a;a=0}; + if (!frac ||frac==1) + return Comp.random.interval(a,b); + else { + r=Comp.random.range(a,b); + return ((r/frac)|0)*frac; + } + }, + Port: Sec.Port, + Private: Sec.Private, + reverse: function (a) { + if (Comp.obj.isArray(a)) + return a.slice().reverse(); + else if (Comp.obj.isString(a)) + return a.split("").reverse().join("") + }, + sleep:Sig.agent.sleep, + sort: function (a,f) { + if (Comp.obj.isArray(a) && Comp.obj.isFunction(f)) { + return Comp.array.sort(a,function (x,y) { + return f.call(current.process.agent,x,y); + }); + } else return undefined; + }, + sum: function (o,f) { + if (Comp.obj.isArray(o)) return Comp.array.sum(o,f); + else if (Comp.obj.isObject(o)) { + var s=0,p; + if (!f) f=function(x){return x}; + for(p in o) s+=f(o[p]); + return s; + } + }, + string:function (o) {if (Comp.obj.isString(o)) return o; else return o.toString()}, + tail:function (a) { + if (Comp.obj.isArray(a)) + return Comp.array.tail(a); + else return undefined; + }, + time:function () { return time()-current.world.lag}, + zero: function (a) { + var i; + if (Comp.obj.isNumber(a)) return a==0; + if (Comp.obj.isArray(a)) { + for (i in a) { + if (!aios0.zero(a[i])) return false; + } + return true; + } + if (Comp.obj.isObj(a)) { + for (i in a) { + if (!aios0.zero(a[i])) return false; + } + return true; + } + return false; + }, + + Vector: function (x,y,z) {var o={}; if (x!=_) o['x']=x; if (y!=_) o['y']=y; if (z!=_) o['z']=z; return o}, + + // Scheduling and checkpointing + B:B, + CP:CP, + I:I, + L:L, + RT:RT, + + Math:Math +} + +// Sandbox module environment for agents (level 1): Trusted, standard operational set +var aios1 = { + abs:aios0.abs, + add:aios0.add, + act:Conf.agent.act, + alt:Ts.agent.alt, + broadcast:Sig.agent.broadcast, + Capability: Sec.Capability, + clock: clock, + collect:Ts.agent.collect, + concat:aios0.concat, + contains:aios0.contains, + copy:aios0.copy, + copyto:Ts.agent.copyto, + // type create = function(ac:string,args:object|[]) -> agentid:string + create: function(ac,args,level) { + if (level==undefined || level>1) level=1; + var process=none; + if (!Comp.obj.isArray(args) && !Comp.obj.isObject(args)) { + current.error='Invalid argument: Agent argument is neither array nor object'; + throw 'CREATE'; + }; + if (current.world.classes[ac] && current.world.classes[ac][level]) + process = Code.createOn(current.node,current.world.classes[ac][level],args,level,ac); + else if (current.process.agent.subclass && current.process.agent.subclass[ac]) { + process = Code.createOn(current.node,current.process.agent.subclass[ac],args,level,ac); + } else { + current.error='Invalid argument: Unknown agent class '+ac; + throw 'CREATE'; + } + if (process) { + if (current.process!=none && process.gid==none) { + process.gid=current.process.pid; + if (!process.agent.parent) + process.agent.parent=current.process.agent.id; + } + return process.agent.id; + } else return none; + }, + div: aios0.div, + dump: aios0.dump, + empty:aios0.empty, + evaluate:Ts.agent.evaluate, + equal:aios0.equal, + exists:Ts.agent.exists, + Export:function (name,code) { current.node.export(name,code) }, + filter:aios0.filter, + fork:function (parameter) {var process = current.process.fork(parameter,undefined,options.fastcopy); return process.agent.id}, + head:aios0.head, + id:aidgen, + Import:function (name) { return current.node.import(name) }, + info:aios0.info, + inp:Ts.agent.inp, + int: aios0.int, + isin: aios0.isin, + iter:aios0.iter, + kill:function (aid) {if (aid==undefined) kill(current.process.agent.id); else kill(aid)}, + length: aios0.length, + link:function (dir) {return current.world.connected(dir,current.node)}, + listen:Ts.agent.listen, + log:aios0.log, + me:aios0.me, + ml:Ml.agent, + nn:Nn.agent, + mark:Ts.agent.mark, + map:aios0.map, + max:aios0.max, + matrix:aios0.matrix, + moveto:Mobi.agent.move, + min:aios0.min, + myClass:aios0.myClass, + myNode:aios0.myNode, + myParent:aios0.myParent, + neg:aios0.neg, + negotiate:function (res,val,cap) { return negotiate(1,res,val,cap) }, + opposite:Mobi.agent.opposite, + out:Ts.agent.out, + random: aios0.random, + rd:Ts.agent.rd, + rm:Ts.agent.rm, + Port: Sec.Port, + position: function () {return current.node.position}, + Private: Sec.Private, + privilege: function () {return 1}, + reverse:aios0.reverse, + security: Sec, + send:Sig.agent.send, + sendto:Sig.agent.sendto, + sleep:Sig.agent.sleep, + sort:aios0.sort, + store:Ts.agent.store, + string:aios0.string, + sum:aios0.sum, + tail:aios0.tail, + test:Ts.agent.exists, + time:aios0.time, + timer:Sig.agent.timer, + trans:Conf.agent.trans, + try_alt:Ts.agent.try.alt, + try_inp:Ts.agent.try.inp, + try_rd:Ts.agent.try.rd, + wakeup:Sig.agent.wakeup, + zero:aios0.zero, + + B:B, + CP:CP, + I:I, + L:L, + RT:RT, + + Vector:aios0.Vector, + DIR:Mobi.agent.DIR, + Math:Math +}; + +// Sandbox module environment for agents (level 2): Trusted with extended privileges +var aios2 = { + abs:aios0.abs, + add:aios0.add, + act:Conf.agent.act, + alt:Ts.agent.alt, + broadcast:Sig.agent.broadcast, + Capability: Sec.Capability, + clock: clock, + collect:Ts.agent.collect, + concat:aios0.concat, + contains:aios0.contains, + copy:aios0.copy, + copyto:Ts.agent.copyto, + create: function(ac,args,level) { + var process=none; + if (level==undefined) level=2; + if (!Comp.obj.isArray(args) && !Comp.obj.isObject(args)) { + current.error='Invalid argument: Agent arguments is neither array nor object'; + throw 'CREATE'; + }; + if (current.world.classes[ac] && current.world.classes[ac][level]) + process = Code.createOn(current.node,current.world.classes[ac][level],args,level,ac); + else if (current.process.agent.subclass && current.process.agent.subclass[ac]) { + process = Code.createOn(current.node,current.process.agent.subclass[ac],args,level,ac); + } else { + current.error='Invalid argument: Unknown agent class '+ac; + throw 'CREATE'; + } + if (process) { + process.agent.ac=ac; + if (current.process!=none && process.gid==none) { + process.gid=current.process.pid; + if (process.agent.parent==_ || process.agent.parent==none) + process.agent.parent=current.process.agent.id; + } + return process.agent.id; + } else return none; + }, + div: aios0.div, + dump: aios0.dump, + empty:aios0.empty, + evaluate:Ts.agent.evaluate, + equal:aios0.equal, + exists:Ts.agent.exists, + Export:function (name,code) { current.node.export(name,code) }, + filter:aios0.filter, + fork:function (parameter) {var process = current.process.fork(parameter); return process.agent.id}, + head:aios0.head, + id:aidgen, + Import:function (name) { return current.node.import(name) }, + info:aios0.info, + inp:Ts.agent.inp, + int: aios0.int, + isin: aios0.isin, + iter:aios0.iter, + kill:function (aid) {if (aid==undefined) kill(current.process.agent.id); else kill(aid)}, + length: aios0.length, + link:function (dir) {return current.world.connected(dir,current.node)}, + listen:Ts.agent.listen, + log:aios0.log, + max:aios0.max, + me:aios0.me, + ml:Ml.agent, + min:aios0.min, + myClass:aios0.myClass, + myNode:aios0.myNode, + myParent:aios0.myParent, + mark:Ts.agent.mark, + map:aios0.map, + matrix:aios0.matrix, + moveto:Mobi.agent.move, + neg:aios0.neg, + negotiate:function (res,val,cap) { return negotiate(2,res,val,cap) }, + nn:Nn.agent, + opposite:Mobi.agent.opposite, + out:Ts.agent.out, + random: aios0.random, + rd:Ts.agent.rd, + reverse:aios0.reverse, + rm:Ts.agent.rm, + Port: Sec.Port, + position: function () {return current.node.position}, + Private: Sec.Private, + privilege: function () {return 2}, + security: Sec, + send:Sig.agent.send, + sendto:Sig.agent.sendto, + sleep:Sig.agent.sleep, + sort:aios0.sort, + store:Ts.agent.store, + string:aios0.string, + sum:aios0.sum, + tail:aios0.tail, + test:Ts.agent.exists, + time:aios0.time, + timer:Sig.agent.timer, + trans:Conf.agent.trans, + try_alt:Ts.agent.try.alt, + try_inp:Ts.agent.try.inp, + try_rd:Ts.agent.try.rd, + wakeup:Sig.agent.wakeup, + zero:aios0.zero, + + B:B, + CP:CP, + I:I, + L:L, + RT:RT, + + Vector:aios0.Vector, + DIR:Mobi.agent.DIR, + + Math:Math, +}; + +// Sandbox module environment for agents (level 3): Trusted with extended privileges, system level +// May not migrate!! +var aios3 = { + abs:aios0.abs, + add:aios0.add, + act:Conf.agent.act, + alt:Ts.agent.alt, + broadcast:Sig.agent.broadcast, + Capability: Sec.Capability, + clock: clock, + collect:Ts.agent.collect, + connectTo:function (dir,options) { + // Connect this node with another node using a virtual or physical channel link + var node=current.node, world=current.world; + if (!dir || !dir.tag) throw('CONNECT'); + world.connectTo(dir,node,options); + }, + concat:aios0.concat, + contains:aios0.contains, + copy:aios0.copy, + copyto:Ts.agent.copyto, + create: aios2.create, + div: aios0.div, + dump: aios0.dump, + empty:aios0.empty, + equal:aios0.equal, + evaluate:Ts.agent.evaluate, + exists:Ts.agent.exists, + Export:aios2.Export, + filter:aios0.filter, + fork:aios2.fork, + head:aios0.head, + id:aidgen, + Import:aios2.Import, + info:aios0.info, + inp:Ts.agent.inp, + int: aios0.int, + isin: aios0.isin, + iter:aios0.iter, + kill:aios2.kill, + length:aios0.length, + link:aios2.link, + listen:Ts.agent.listen, + log:aios0.log, + max:aios0.max, + me:aios0.me, + ml:Ml.agent, + min:aios0.min, + myClass:aios0.myClass, + myNode:aios0.myNode, + myParent:aios0.myParent, + mark:Ts.agent.mark, + map:aios0.map, + matrix:aios0.matrix, + moveto:function () {/* System level agents may not migrate ! */ current.error='ENOTSUPPORTED';throw 'MOVE';}, + neg:aios0.neg, + negotiate:function (res,val,cap) { return negotiate(3,res,val,cap) }, + nn:Nn.agent, + opposite:Mobi.agent.opposite, + out:Ts.agent.out, + random: aios0.random, + rd:Ts.agent.rd, + rm:Ts.agent.rm, + Port: Sec.Port, + position: function () {return current.node.position}, + Private: Sec.Private, + privilege: function () {return 3}, + reverse:aios0.reverse, + send:Sig.agent.send, + sendto:Sig.agent.sendto, + sleep:aios0.sleep, + sort:aios0.sort, + store:Ts.agent.store, + string:aios0.string, + sum:aios0.sum, + tail:aios0.tail, + test:Ts.agent.exists, + time:aios0.time, + timer:Sig.agent.timer, + trans:Conf.agent.trans, + try_alt:Ts.agent.try.alt, + try_inp:Ts.agent.try.inp, + try_rd:Ts.agent.try.rd, + wakeup:Sig.agent.wakeup, + zero:aios0.zero, + + B:B, + CP:CP, + I:I, + L:L, + RT:RT, + + Vector:aios0.Vector, + DIR:Mobi.agent.DIR, + + Math:Math, + + // Exucute an IO block sequence in an agent process context + IOB: function (block) { + var proc=current.process; + setImmediate(function () { + var index=0; + function next (to) { + var _proc=current.process,_node=current.node; + if (to==none) { + // done or failiure + proc.mask.next=undefined; + proc.wakeup(); + return; + } + index=index+to; + try { + current.process=proc; current.node=proc.node; + block[index].call(proc.agent); + } catch (e) { + logAgent('Caught IOB error: '+e); + } + current.process=_proc; current.node=_node; + } + proc.mask.next=next; + next(0); + }); + proc.suspend(); + } +}; + +var aios = aios1; + +/* +** Agent code scheduling blocks can migrate +** - must be handled different from internal scheduling blocks! +*/ +// Schedule linear sequence of functions that may block (suspending execution of current agent process). +function B(block) { + if (current.process.schedule.length==0) + current.process.schedule = block; + else + current.process.schedule = Comp.array.concat(block,current.process.schedule); +} + +/** Add pending callback call to process scheduling block + * + */ +function CB(process,cb,args) { + if (args) + Comp.array.push(process.schedule,function () { cb.apply(this,args) }); + else + Comp.array.push(process.schedule,cb); +} + +/** Agent process activity check pointing (injected in loops/functions) + * + */ +function CP() { + if (current.process.runtime && (current.process.runtime+current.world.lag-Date.now())<0) throw "SCHEDULE"; + return true; +} + +/** Agent exception checker; agents may not consume scheduler/watchdog exceptions!! +*/ + +function RT(e) { + if (['WATCHDOG','SCHEDULE'].indexOf(e.toString())!=-1) throw(e); +} + +/** Schedule an object iteration sequence that may block (suspending execution of current agent process). + * + */ +function I(obj,next,block,finalize) { + /* + ** Iterate and schedule a block + * obj: [] + * next: function(next) {} + */ + var index=0; + var length=obj.length; + + var iterator = [ + function() { + next(obj[index]); + if (index\s*\(/gm, '{anonymous}()@') + .split('\n'); + log(e); + log('Stack Trace'); + log('--------------------------------'); + for(var i in stack) { + if (i>0) { + var line = stack[i]; + if(line.indexOf('Module.',0)>=0) break; + log(line); + } + } + log('--------------------------------'); +}; +/** Emit event + * function emit(@event,@arg1,..) + */ +function emit() { + if (events[arguments[0]]) + events[arguments[0]](arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]); +} +/** Try to get the source position of an error raised in an agent activity + * + */ +function errorLocation(process,err) { + try { + var stack = err.stack.split('\n'); + for (var i in stack) { + var line=stack[i]; + if (line.indexOf('at act.')>=0||line.indexOf('at F.act.')>=0) { + return line.replace(/\([^\)]+\)/,'').replace(/\)/,''); + } + else if (line.indexOf('at trans.')>=0 || line.indexOf('at F.trans.')>=0) { + return line.replace(/\([^\)]+\)/,'').replace(/\)/,''); + } + } + return ''; + } catch (e) { + return ''; + } +} + +// Execute a block scheduling function +function exec_block_fun(next) { + var fun = next[0]||next, + argn = next.length-1; + switch (argn) { + case 0: + case -1: + fun(); break; + case 1: fun(next[1]); break; + case 2: fun(next[1],next[2]); break; + case 3: fun(next[1],next[2],next[3]); break; + case 4: fun(next[1],next[2],next[3],next[4]); break; + case 5: fun(next[1],next[2],next[3],next[4],next[5]); break; + case 6: fun(next[1],next[2],next[3],next[4],next[5],next[6]); break; + case 7: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7]); break; + case 8: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7],next[8]); break; + case 9: fun(next[1],next[2],next[3],next[4],next[5],next[6],next[7],next[8],next[9]); break; + default: + // TODO: fun.apply(undefined,next.slice(1)) + Io.err('Aios.exec_block_fun: more than 9 function arguments'); + } +} + + +/** Fork the current agent with an optional new set of parameters. + * + */ +function fork(parameters) { + return current.process.fork(parameters); +} + +/** Kill an agent (if agent identifier is undefined the current agent will be killed). + * + */ +function kill(agent) { + var process; + if (!agent) { + process=current.process; + } else { + process=current.node.processes.process(agent); + } + if (process) { + process.kill=true; + current.node.unregister(process); + return true; + } else return false; +} + +function killOn(agent,node) { + var process; + process=node.processes.process(agent); + if (process) { + process.kill=true; + node.unregister(process); + }; +} + +/** Lock the global namespace. Disable inter-agent communication + * by using the global namespace => Sandbox (level 2) + * + */ +function lock() { + Object.preventExtensions(global); +} + +/** Execute agent processes until there are no more schedulable agents. + * Loop returns if there are no more runnable agents. If there are waiting + * agent processes, the loop will be rescheduled on the earliest time event. + * + */ + +function loop(services) { + var nexttime = scheduler(services); + if (nexttime>0) { + // Nothing to do. + // Sleep until next event and re-enter the scheduling loop. + if (options.verbose>2) log('[LOOP '+current.node.id+'] next schedule on '+ nexttime); + timer=setTimeout(function () {loop (services)},nexttime-time()); + } +} + +function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) }; + +/** Call agent exception handler. If exception was handled by agent return true, otherwise false. + * + */ +function handleException(process,exc,arg1,arg2,arg3,arg4) { + var agent=process.agent; + if (Aios.watchdog && Aios.watchdog.protect) { + try { Aios.watchdog.protect(function () {agent.on[exc].call(agent,arg1,arg2,arg3,arg4)})} catch(e) { + // If there is no handler managing the error (e.g. SCHEDULE), the agent must be terminated! + if (options.verbose) logAIOS ('Agent '+agent.id+' ['+agent.ac+'] failed handling '+exc+'('+arg1+')'); + process.kill=true + return false; + }; + } else + try {agent.on[exc].call(agent,arg1,arg2,arg3,arg4)} catch(e) { + // If there is no handler managing the error (e.g. SCHEDULE), the agent must be terminated! + if (options.verbose) logAIOS ('Agent '+agent.id+' ['+agent.ac+'] failed handling '+exc+'('+arg1+')'); + process.kill=true + return false; + } + return true; +} + +/** Agent resource constraint negotiation + * + */ +function negotiate (level,resource,value,cap) { + var obj; + // Check capability rights + function checkRights(r) { + return (level > 1) || + (cap && Net.prv_rights_check(cap.cap_priv,Aios.current.node.random[cap.cap_port],r)) + + } + switch (resource) { + case 'CPU': + if (!checkRights(Net.Rights.NEG_CPU)) return false; + if (value>options.TIMEPOOL) current.process.resources.CPU=value; break; + case 'SCHED': + case 'SCHEDULE': + if (!checkRights(Net.Rights.NEG_SCHED)) return false; + if (value>options.TIMESCHED) current.process.resources.SCHED=value; break; + case 'MEM': + case 'MEMORY': + if (!checkRights(Net.Rights.NEG_RES)) return false; + if (value>options.MEMPOOL) current.process.resources.MEM=value; break; + case 'TS': + if (!checkRights(Net.Rights.NEG_RES)) return false; + if (value>options.TSPOOL) current.process.resources.TS=value; break; + case 'AGENT': + if (!checkRights(Net.Rights.NEG_RES)) return false; + if (value>options.AGENTPOOL) current.process.resources.AGENT=value; break; + case 'LEVEL': + if (!checkRights(Net.Rights.NEG_LEVEL)) return false; + // Extend process mask + switch (value) { + case 1: + break; + case 2: + break; + } + break; + case '?': + obj=Comp.obj.copy(current.process.resources); + Comp.obj.extend(obj,{ + SCHED: current.process.resources.SCHED||options.TIMESCHED, + CPU: current.process.resources.CPU||options.TIMEPOOL, + MEM: current.process.resources.MEM||options.MEMPOOL, + TS: current.process.resources.TS||options.TSPOOL, + AGENT: current.process.resources.AGENT||options.AGENTPOOL, + }); + return obj; + break; + default: return false; + } + return true; +} + + + +/** Event callback management + * + */ +function off(event) { + // TODO: care of function chains?? + events[event]=undefined; +} +function on(event,fun) { + if (events[event]) { + // Implement callback function chain + var funorig=events[event]; + events[event]=function () { + funorig.apply(this,arguments); + fun.apply(this,arguments); + }; + } else + events[event]=fun; +} + +function out(str) {log(str)}; + +/** Get current resource allocation of process memory + * + */ +function resource(r0) { + var r; + if (!options.useproc) return 0; + // Time expensive operation: requires system call and a lot of internal computation + r=process.memoryUsage(); + // console.log(r) + if (r0==undefined) + return {r:r.rss-r.heapTotal,h:r.heapUsed}; + else return int((Math.max(0,r.rss-r.heapTotal-r0.r)+Math.max(0,r.heapUsed-r0.h))/1024); +} + +/** Scheduling function for one agent process. + * + * Scheduling order: + * 1. Process Blocks (process.block, passed to global DOS scheduler) + * 2. Signals (process.signals, handled by AIOS scheduler) + * 3. Transition (process.transition==true, handled by AIOS scheduler) + * 4. Agent Blocks (process.schedule, handled by AIOS scheduler) + * 5. Activity (handled by AIOS scheduler) + * + */ +var SA = { + NOOP:0, + BLOCK:1, + NORES:2, + SIG:3, + TRANS:4, + SCHED:5, + ACT:6, + print: function (op) { + switch (op) { + case SA.NOOP: return 'NOOP'; + case SA.BLOCK: return 'BLOCK'; + case SA.NORES: return 'NORES'; + case SA.SIG: return 'SIG'; + case SA.TRANS: return 'TRANS'; + case SA.SCHED: return 'SCHED'; + case SA.ACT: return 'ACT'; + } + } +} + +// One scheduler run +function schedule(process) { + var exec,sig,start,delta,next, + _current, + node=current.node, + agent=process.agent, + action='', + op=SA.NOOP, + handled, + exception, + r0; + + ticks++; // move to scheduler ??? + // console.log(process); + assert((process.agent!=undefined && process.id=='agent')||('Aios.schedule: not an agent process: '+process.id)); + + /* Order of operation selection: + ** 0. Process (internal) block scheduling [block] + ** 1. Resource exception handling + ** 2. Signal handling [signals] + ** - Signals only handled if process priority < HIGH + ** - Signal handling increase proecss priority to enable act scheduling! + ** 3. Transition execution + ** 4. Agent schedule block execution [schedule] + ** 5. Next activity execution + */ + + if (process.blocked || + (process.suspended==true && process.block.length==0 && process.signals.length==0) || + process.dead==true || + (agent.next==none && process.signals.length==0 && process.schedule.length == 0)) op=SA.NOOP; + // if (process.suspended==true && process.schedule.length==0 && process.signals.length==0) op=SA.NOOP; + else if (!process.blocked && process.block.length > 0) op=SA.BLOCK; + else if (!options.nolimits && + (process.resources.consumed>(process.resources.CPU||options.TIMEPOOL) || + process.resources.memory>(process.resources.MEM||options.MEMPOOL))) op=SA.NORES; + else if (process.priority0) op=SA.SIG; + else if (!process.suspended && process.transition) op=SA.TRANS; + else if (!process.suspended && process.schedule.length > 0) op=SA.SCHED; + else if (!process.suspended) op=SA.ACT; + + if (options.verbose>2) print('[SCH] '+time()+' '+process.agent.id+' : '+ + SA.print(op)+' [susp='+process.suspended+ + ',trans='+process.transition+',tmo='+process.timeout+']'); + + if (op==SA.NOOP) return 0; + + start=time(); + + if (Aios.watchdog) Aios.watchdog.start(process.resources.SCHED||options.TIMESCHED); + else if (!options.nolimits) + process.runtime=start-current.world.lag+(process.resources.SCHED||options.TIMESCHED); + if (!options.nolimits) + r0=resource(); // Start resource monitor + + current.process=process; + current.error=none; + if (current.scheduler) _current=current.scheduler.SetCurrent(process); + try { + switch (op) { + case SA.BLOCK: + // An internal schedule block [Linear/Loop] + // Pass to global scheduler + // console.log(process.block) + schedule_block(process); + break; + case SA.NORES: + throw 'EOL'; + break; + case SA.SIG: + /* Execute a signal handler + ** 1. A signal handler can wakeup a suspended agent process by calling wakeup() + ** 2. A signal handler can wakeup a suspended agent process by modifying variables and satisfying the current + ** transition condition resulting in an activity transition! + */ + if (!process.suspended && !process.transition) process.priority++; + // Pending activity execution -> block signal handling temporarily + action='signal'; + sig=Comp.array.pop(process.signals); + try { + // sig=[signal,argument?,from?] + agent.on[sig[0]].call(agent,sig[1],sig[2]); + if (process.suspended && process.transition) process.suspended=false; // ==> 2.) + } catch(e) { + if (!agent.on[sig[0]]) + logAIOS ('Signal handler '+sig[0]+' in agent '+agent.id+' ['+agent.ac+'] not defined, ignoring signal.'); + else + logAIOS ('Signal handler '+sig[0]+' in agent '+agent.id+' ['+agent.ac+'] failed: '+e+ + (current.error?' / '+current.error:'')+', in: \n'+Code.print(agent.on[sig[0]])+ + +errorLocation(process,e)) + current.error=none; + process.kill=true; // Always? + }; + Aios.emit('signal+',process,node,sig[0],sig[1],sig[2]); + break; + case SA.TRANS: + // Pending next computation: Compute next transition after wakeup or after a signal was handled. + // If still not successfull, suspend agent process. + try { + action='transition'; + next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):agent.trans[agent.next]; + // TODO: check blocking state - transitions may not block! + if (next) { + agent.next=next; + process.suspended=false; + process.transition=false; + } else { + process.suspended=true; + } + } catch (e) { + if (agent.trans[agent.next]==undefined) + logAIOS ('Transition table entry '+agent.next+' not defined in agent '+agent.id+' ['+agent.ac+'].'); + else + logAIOS ('Agent '+agent.id+' ['+agent.ac+'] in transition '+agent.next+ + ' failed:\n'+e+(current.error?' / '+current.error:'')+ + +errorLocation(process,e)); + process.kill=true; + current.error=none; + } + break; + case SA.SCHED: + // An agent schedule block function [Linear/Loop] executed in agent context + action='block'; + exec = Comp.array.pop(process.schedule); + Aios.watchdog&&Aios.watchdog.protect?Aios.watchdog.protect(exec.bind(agent)):exec.call(agent); + if (!process.kill && !process.suspended && process.schedule.length == 0) { + // next=agent.trans[agent.next].call(agent); + next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):agent.trans[agent.next]; + if (!next) process.suspend(0,true); // no current transition enabled; suspend process + else agent.next=next; + } + break; + case SA.ACT: + // Normal activity execution + // console.log('[SCH] next:'+agent.next) + if (process.priority==Proc.PRIO.HIGH) process.priority--; + action='activity'; + if (agent.next==none) throw 'KILL'; + Aios.watchdog&&Aios.watchdog.protect?Aios.watchdog.protect(agent.act[agent.next].bind(agent)):agent.act[agent.next].call(agent); + if (!process.kill && !process.suspended && process.schedule.length == 0) { + action='transition'; + // next=agent.trans[agent.next].call(agent); + next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent):agent.trans[agent.next]; + // TODO: check blocking state - transitions may not block! + if (!next) process.suspend(0,true); // no current transition enabled; suspend process + else agent.next=next; + } + break; + } + } catch (e) { + if (Aios.watchdog) Aios.watchdog.stop(); + exception=true; + switch (e) { + case 'SCHEDULE': + case 'WATCHDOG': + e='SCHEDULE'; + if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10); + else process.runtime=time()-current.world.lag+options.TIMESCHED/10; + handleException(process,'error',e,options.TIMESCHED,agent.next); + break; + case 'EOL': + if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10); else + process.runtime=time()-current.world.lag+options.TIMESCHED/10; + // New time or memory contingent must be negotiated based on policy! + if (process.resources.consumed>(process.resources.CPU||options.TIMEPOOL)) { + handleException(process,'error',e,process.resources.consumed,agent.next); + if (process.resources.consumed>(process.resources.CPU||options.TIMEPOOL)) + process.kill=true; + } else if (process.resources.memory>(process.resources.MEM||options.MEMPOOL)) { + handleException(process,'error','EOM',process.resources.memory,agent.next); + if (process.resources.memory>(process.resources.MEM||options.MEMPOOL)) + process.kill=true; + } else { + // TODO generic resource overflow? + handleException(process,'error','EOR',0,agent.next); + process.kill=true; + } + break; + case 'KILL': + if (Aios.watchdog) Aios.watchdog.start(options.TIMESCHED/10); + else process.runtime=time()-current.world.lag+options.TIMESCHED/10; + handleException(process,'exit'); + process.kill=true; + break; + default: + if (agent.act[agent.next]==undefined) + logAIOS('Activity '+agent.next+' not defined in agent '+ + agent.id+' ['+agent.ac+'].'); + else if (agent.trans[agent.next]==undefined) + logAIOS('Transition table entry '+agent.next+' not defined in agent '+agent.id+' ['+agent.ac+'].'); + else { + handled=handleException(process,aiosExceptions.indexOf(e.toString())!=-1?e:'error',e,current.error,agent.next); + if (!handled && options.verbose) + logAIOS ('Agent '+agent.id+' ['+agent.ac+'] in '+(action=='block'?'block in':action)+' '+ + (action=='signal'?sig[0]:agent.next)+ + ' failed: Error '+e+(current.error?('; '+current.error):'')+ + ', in code: \n'+( + action=='activity'?Code.print(agent.act[agent.next]): + (action=='transition'?Code.print(agent.trans[agent.next]): + (agent.on && sig && agent.on[sig[0]])?Code.print(agent.on[sig[0]]):'none') + )+ + errorLocation(process,e)); + if (!handled && options.verbose>1 && ['CREATE','MOVE','SIGNAL'].indexOf(e) == -1) Io.printstack(e); + } + if (!handled) process.kill=true; + current.error=none; + } + } + if (Aios.watchdog) Aios.watchdog.stop(); + else process.runtime=0; + + if (!options.nostats) { + delta=(time()-start)||options.MINCOST; + process.resources.consumed += delta; + process.resources.memory += resource(r0); + current.node.stats.cpu += delta; + } + + if (options.verbose && exception && process.kill) logAIOS('Killed agent '+agent.id); + + if (current.scheduler) current.scheduler.SetCurrent(_current); + + current.process=none; + + if (options.verbose>2) print(time()+' <- '+process.print()); + + return 1; +} + +/** + * Internal block scheduling + */ + + +function schedule_block(process) { + var next; + /* + ** Process current function block sequence first! + ** Format: [[fun,arg1,arg2,...],[block2], [block3], ..] + ** Simplified: [fun,fun,...] + */ + if (!process.blocked) { + next = process.block[0]; + process.block.splice(0,1); + /* + ** Do no execute handler blocks maybe at the end of a subsection + ** of the block list. + */ + while (!Comp.array.empty(process.block) && next.handler!=undefined) { + next = process.block[0]; + process.block.splice(0,1); + } + if (next.handler==undefined) { + try {exec_block_fun(next)} catch(e) { + /* + ** Iterate through the block list and try to find a handler entry. + */ + while (next.handler==undefined && !Comp.array.empty(process.block)) { + next = process.block[0]; + process.block.splice(0,1); + } + if (next.handler!=undefined) { + /* + ** Call handler ... + */ + // console.log(next.handler.toString()) + try {exec_block_fun([next.handler,e])} + catch (e) { + Io.out('Aios.schedule_block [Internal B], in agent context '+ + process.agent.id+', got exception in exception handler: '+e); + // Io.printstack(e); + Io.out(Json.stringify(next).replace(/\\n/g,'\n')); + }; + } else { + logAIOS ('Agent '+process.agent.id+' ['+process.agent.ac+'] in activity '+ + process.agent.next+ + ' failed:\n'+e+(current.error?' / '+current.error:', in: \n'+ + Code.print(process.agent.act[process.agent.next]))+ + '');// '\nat:\n'+Io.sprintstack(e))); + process.kill=true; + current.error=none; + } + } + } + } +} + + +/** Main scheduler entry. + * Returns the next event time, negative number of scheduled agent processes, or zero. + * If result is negative, the scheduler should be executed immediately again because there + * can be pending agent signals created in the current run. + */ +function scheduler(services) { + var scheduled=0,run=1,nexttime=0,n=0,curtime,process,env,node,pro, + timeout=time()+options.RUNTIME; + + while (run && (iterations==0 || n0) { + remove=false; + // 1.1. Check timers and execute runnable signaled agents + Comp.array.iter(node.timers, function(timer,i) { + if (timer && timer[1]<=curtime) { + var process=timer[0], + agent=process.agent, + // Save original process state + suspended=process.suspended, + timeout=process.timeout; + + // process.suspeneded=false; ?? Signal handler can be executed even with blocked process + process.signals.push([timer[2],timer[3],agent.id]); + // TODO: A wakeup call in the signal handler re-enters schedule() !!! + run += schedule(process); + curtime=time()-current.world.lag; + remove=true; + node.timers[i]=undefined; + + // Restore original process state + //process.suspended=suspended; ?? + process.timeout=timeout; + } else if (timer) nexttime=min0(nexttime,timer[1]); + }); + // 1.2. Timer destruction + if (remove) + node.timers= + Comp.array.filter(node.timers,function (timer) { + return timer!=undefined; + }); + } + + curtime=time()-current.world.lag; + // Node service management (caches, TS) + node.service(curtime); + + // 3. Agent process management + for (pro in node.processes.table) { + if (node.processes.table[pro]) { + // 2.1 Agent execution + curtime=time()-current.world.lag; + process=node.processes.table[pro]; + // Io.out('scheduler: checking '+process.agent.id+': '+process.suspended+' '+process.timeout); + if (process.suspended && process.timeout && process.timeout<=curtime) { + // Io.out('scheduler: waking up '+process.agent.id); + process.wakeup(); + } + run += schedule(process); + // 2.2 Agent destruction + if (node.processes.table[pro] && node.processes.table[pro].kill) + node.unregister(node.processes.table[pro]); + if (node.processes.table[pro] && process.suspended && process.timeout>0) + nexttime=min0(nexttime,process.timeout); + } + } + } + scheduled += run; + } + + if (scheduled>0) return -scheduled; + else if (nexttime>0) return nexttime; + else return 0; +} + +/* +** The time function can be changed, e.g., by simulators handling simulation +** steps instead of real time. Can be changed with Aios.config({time:fun}), +** updating all Aios/aiosX references and CP as well. +*/ +var time = function () {return Math.ceil(Date.now())}; + +var Aios = { + aidgen:aidgen, + aios:aios1, + aios0:aios0, + aios1:aios1, + aios2:aios2, + aios3:aios3, + aiosEvents:aiosEvents, + callback:undefined, + clock:clock, + collect:Ts.agent.collect, + // External API: Change AIOS settings only using config! + config:config, + current:current, + emit:emit, // Emit event + err: function (msg) {if (options.verbose) log('Error: '+msg)}, + fork:fork, + kill:kill, + killOn:killOn, + lock:lock, + loop:loop, + log:log, // Generic AIOS logging function + logAgent:logAgent, // Agent message logging (with details about current) + logAIOS:logAIOS, // AIOS logging function related with agent proecssing (with details about current) + ml:Ml.agent, + off:off, // Remove event handler + on:on, // Add event handler + options:options, + print:Io.out, // Print function for agent messages via Aios.aiosX.log and internal Aios.log; + // OR if printAgent is set only AIOS internal messages; can be modified by host app + printAgent:undefined, // Print function for agent messages only via Aios.aiosX.log; can be modified by host app + schedule:schedule, + scheduler:scheduler, + ticks:function (v) { if (v!=undefined) ticks=v; else return ticks}, + time:time, + timeout:function (tmo) { return tmo>0?Aios.time()-current.world.lag+tmo:0 }, // Compute absolute time from relative timeout + Chan:Chan, + Code:Code, + Mobi:Mobi, + Name:Name, + Node:Node, + Proc:Proc, + Sec:Sec, + Sig:Sig, + Simu:Simu, + Ts:Ts, + World:World, + CB:CB, + CP:CP, + RT:RT, + B:B, + DIR:Mobi.DIR, + I:I, + L:L, + warn: function (msg) {if (options.verbose>1) log('Warning: '+msg)}, + watchdog: undefined +} + +// Builtin watchdog support by JS VM platform? +if (watchdog && watchdog.start) Aios.watchdog=watchdog; +if (watchdog && watchdog.init) watchdog.init('WATCHDOG'); +if (watchdog && watchdog.checkPoint) { + // only partial watchdog support by platform + aios0.CP=watchdog.checkPoint; + aios1.CP=watchdog.checkPoint; + aios2.CP=watchdog.checkPoint; + aios3.CP=watchdog.checkPoint; + Aios.CP=watchdog.checkPoint; +} + +Conf.current(Aios); +Code.current(Aios); +Sig.current(Aios); +Sec.current(Aios); +Ts.current(Aios); +Proc.current(Aios); +Node.current(Aios); +World.current(Aios); +Mobi.current(Aios); +if (Simu) Simu.current(Aios); +Chan.current(Aios); +Json.current(Aios); +Ml.current(Aios); +Nn.current(Aios); + +module.exports = Aios; +}; +BundleModuleCode['com/pwgen']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Bermi Ferrer, Stefan Bosse + ** $INITIAL: (C) 2011-2015 Bermi Ferrer , 2017-2018 Stefan Bosse + ** $REVESIO: 1.3.1 + ** + ** $INFO: + * + * password-generator using crypto random number generation (slow,HQ) + * !using built-in crypto random generators using either native crypto module or polyfill! + * + * options = {length,memorable,lowercase,uppercase,pattern,number?:boolean,range?:[]} + * + * Using always twister random byte generator (not random byte array) + * + * $ENDINFO + */ + +var Crypto = Require('os/crypto.rand'); // Require('crypto'); + +module.exports.generate = function (options) { + + function numgen (options) { + // assuming byte number range 0-255 + var arr = new Uint8Array(options.length||8); + getRandomValues(arr); + return arr; + } + + function pwgen (options) { + var localName, consonant, letter, vowel, pattern = options.pattern, + char = "", n, i, validChars = [], prefix=options.prefix; + letter = /[a-zA-Z]$/; + vowel = /[aeiouAEIOU]$/; + consonant = /[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]$/; + if (options.length == null) { + options.length = 10; + } + if (pattern == null) { + pattern = /\w/; + } + if (prefix == null) { + prefix = ''; + } + + // Non memorable passwords will pick characters from a pre-generated + // list of characters + if (!options.memorable) { + for (i = 33; 126 > i; i += 1) { + char = String.fromCharCode(i); + if (char.match(pattern)) { + validChars.push(char); + } + } + + if (!validChars.length) { + throw new Error("Could not find characters that match the " + + "password pattern " + pattern + ". Patterns must match individual " + + "characters, not the password as a whole."); + } + } + + + while (prefix.length < options.length) { + if (options.memorable) { + if (prefix.match(consonant)) { + pattern = vowel; + } else { + pattern = consonant; + } + n = Crypto.randomByte(33,126); // rand(33, 126); + char = String.fromCharCode(n); + } else { + char = validChars[rand(0, validChars.length)]; + } + + if (options.lowercase) char = char.toLowerCase(); + else if (options.uppercase) char = char.toUpperCase(); + + if (char.match(pattern)) { + prefix = "" + prefix + char; + } + } + return prefix; + }; + + + function rand(min, max) { + var key, value, arr = new Uint8Array(max); + getRandomValues(arr); + for (key in arr) { + if (arr.hasOwnProperty(key)) { + value = arr[key]; + if (value > min && value < max) { + return value; + } + } + } + return rand(min, max); + } + + + function getRandomValues(buf) { + var bytes = Crypto.randomBytes(buf.length); + buf.set(bytes); + } + if (options.number) + return numgen(options) + else + return pwgen(options); +}; +}; +BundleModuleCode['os/crypto.rand']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $VERSION: 1.2.4 + ** + ** $INFO: + ** + ** Crypto module with HQ random number generators (replacing not available crypto.getRandomValues + ** if there is no global crypto module). + ** + ** $ENDOFINFO + */ +var crypto = global.crypto || global.msCrypto; + +if (!crypto && typeof require != 'undefined') try { crypto=global.crypto=require('require') } catch (e) {}; + +var twister; + +var MersenneTwister = function(seed) { + if (seed == undefined) { + /** + ** It is not sure that Math.random is seeded randomly + ** Thus, a combination of current system time and Math.random + ** is used to seed and initialize this random generator + */ + seed = new Date().getTime(); + seed *= Math.random()*91713; + seed |= 0; + } + + /* Period parameters */ + this.N = 624; + this.M = 397; + this.MATRIX_A = 0x9908b0df; /* constant vector a */ + this.UPPER_MASK = 0x80000000; /* most significant w-r bits */ + this.LOWER_MASK = 0x7fffffff; /* least significant r bits */ + + this.mt = new Array(this.N); /* the array for the state vector */ + this.mti=this.N+1; /* mti==N+1 means mt[N] is not initialized */ + + if (seed.constructor == Array) { + this.init_by_array(seed, seed.length); + } + else { + this.init_seed(seed); + } +} + +/* initializes mt[N] with a seed */ +/* origin name init_genrand */ +MersenneTwister.prototype.init_seed = function(s) { + this.mt[0] = s >>> 0; + for (this.mti=1; this.mti>> 30); + this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + + this.mti; + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + this.mt[this.mti] >>>= 0; + /* for >32 bit machines */ + } +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +/* slight change for C++, 2004/2/26 */ +MersenneTwister.prototype.init_by_array = function(init_key, key_length) { + var i, j, k; + this.init_seed(19650218); + i=1; j=0; + k = (this.N>key_length ? this.N : key_length); + for (; k; k--) { + var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30) + this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + + init_key[j] + j; /* non linear */ + this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ + i++; j++; + if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } + if (j>=key_length) j=0; + } + for (k=this.N-1; k; k--) { + var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30); + this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) + - i; /* non linear */ + this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ + i++; + if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } + } + + this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ +} + +/* generates a random number on [0,0xffffffff]-interval */ +/* origin name genrand_int32 */ +MersenneTwister.prototype.random_int = function() { + var y; + var mag01 = new Array(0x0, this.MATRIX_A); + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (this.mti >= this.N) { /* generate N words at one time */ + var kk; + + if (this.mti == this.N+1) /* if init_seed() has not been called, */ + this.init_seed(5489); /* a default initial seed is used */ + + for (kk=0;kk>> 1) ^ mag01[y & 0x1]; + } + for (;kk>> 1) ^ mag01[y & 0x1]; + } + y = (this.mt[this.N-1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK); + this.mt[this.N-1] = this.mt[this.M-1] ^ (y >>> 1) ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + + /* Tempering */ + y ^= (y >>> 11); + y ^= (y << 7) & 0x9d2c5680; + y ^= (y << 15) & 0xefc60000; + y ^= (y >>> 18); + + return y >>> 0; +} + +/* generates a random number on [0,0x7fffffff]-interval */ +/* origin name genrand_int31 */ +MersenneTwister.prototype.random_int31 = function() { + return (this.random_int()>>>1); +} + +/* generates a random number on [0,1]-real-interval */ +/* origin name genrand_real1 */ +MersenneTwister.prototype.random_incl = function() { + return this.random_int()*(1.0/4294967295.0); + /* divided by 2^32-1 */ +} + +/* generates a random number on [0,1)-real-interval */ +MersenneTwister.prototype.random = function() { + return this.random_int()*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on (0,1)-real-interval */ +/* origin name genrand_real3 */ +MersenneTwister.prototype.random_excl = function() { + return (this.random_int() + 0.5)*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on [0,1) with 53-bit resolution*/ +/* origin name genrand_res53 */ +MersenneTwister.prototype.random_long = function() { + var a=this.random_int()>>>5, b=this.random_int()>>>6; + return(a*67108864.0+b)*(1.0/9007199254740992.0); +} + +function polyfill () { + twister = new MersenneTwister(); // (Math.random()*Number.MAX_SAFE_INTEGER)|0) + if (!crypto) crypto=global.crypto={}; + crypto.getRandomValues = function getRandomValues (abv) { + var l = abv.length + while (l--) { + abv[l] = Math.floor(twister.random() * 256) + } + return abv + } + if (!global.Uint8Array && !Uint8Array) throw new Error('crypto.rand: No Uint8Array found!'); + if (!global.Uint8Array) global.Uint8Array=Uint8Array; +} + + +function randomByte (min,max) { + if (!twister) twister = new MersenneTwister(); + return Math.floor(twister.random() * (max-min))+min; +} + +function randomBytes (size, cb) { + // phantomjs needs to throw + if (size > 65536) throw new Error('requested too many random bytes') + if (!crypto || !crypto.getRandomValues) polyfill(); + + // in case browserify isn't using the Uint8Array version + var rawBytes = new global.Uint8Array(size); + // This will not work in older browsers. + // See https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues + if (size > 0) { // getRandomValues fails on IE if size == 0 + crypto.getRandomValues(rawBytes); + } + // phantomjs doesn't like a buffer being passed here + var bytes = new Buffer(rawBytes); + if (typeof cb === 'function') { + cb(null, bytes) + } + + return bytes +} + +module.exports = { + randomByte:randomByte, + randomBytes:randomBytes +} +}; +BundleModuleCode['jam/conf']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2017 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $RCS: $Id: conf.js,v 1.2 2017/05/23 07:00:43 sbosse Exp $ + ** $VERSION: 1.1.5 + ** + ** $INFO: + ** + ** JavaScript AIOS Agent Reconfiguration Sub-System + ** + ** $ENDOFINFO + */ + +var Json = Require('jam/jsonfn'); +var Comp = Require('com/compat'); +var current=none; +var Aios = none; + +var act = { + add: function (act,code) { + current.process.agent.act[act]=code; + // Add the new activity to the mask environment of the agent for further referencing. + current.process.mask[act]=act; + }, + delete: function (act) { + if(Comp.obj.isArray(act)) Comp.array.iter(act,function (a) {current.process.agent.act[a]=undefined}); + else current.process.agent.act[act]=undefined + }, + update: function (act,code) { + current.process.agent.act[act]=code; + } +}; + +var trans = { + add: function (from,cond) { + if (current.process.agent.trans[from]) { + var regex1= /"function[\s]*\([\s]*\)[\s]*\{([^\}]+)\}"/; + var regex2= /\\n/g; + var old=Json.stringify(current.process.agent.trans[from]).replace(regex1,"$1").replace(regex2,""); + var next=Json.stringify(cond).replace(regex1,"$1").replace(regex2,""); + var merged='(function () {'+old+next+'})'; + //console.log(merged) + with(current.process.mask) { + current.process.agent.trans[from]=eval(merged); + } + } else current.process.agent.trans[from]=cond; + }, + delete: function (trans) { + if(Comp.obj.isArray(trans)) Comp.array.iter(trans,function (t) {current.process.agent.trans[t]=undefined}); + else current.process.agent.trans[trans]=undefined + }, + update: function (from,cond) { + current.process.agent.trans[from]=cond; + } +} + + +module.exports = { + agent:{ + act:act, + trans:trans + }, + current:function (module) { current=module.current; Aios=module; } +} +}; +BundleModuleCode['jam/jsonfn']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Vadim Kiryukhin, Stefan Bosse + ** $INITIAL: (C) 2006-2017 Vadim Kiryukhin + ** $MODIFIED: by sbosse. + ** $RCS: $Id: jsonfn.js,v 1.1 2017/05/20 15:56:53 sbosse Exp $ + ** $VERSION: 1.1.7 + ** + ** $INFO: + ** + ** JSONfn - javascript (both node.js and browser) plugin to stringify, + ** parse and clone objects with functions with masked context (mask). + ** + ** browser: + ** JSONfn.stringify(obj); + ** JSONfn.parse(str[, date2obj]); + ** JSONfn.clone(obj[, date2obj]); + ** + ** nodejs: + ** var JSONfn = require('path/to/json-fn'); + ** JSONfn.stringify(obj); + ** JSONfn.parse(str[, date2obj]); + ** JSONfn.clone(obj[, date2obj]); + ** + ** + ** @obj - Object; + ** @str - String, which is returned by JSONfn.stringify() function; + ** @date2obj - Boolean (optional); if true, date string in ISO8061 format + ** is converted into a Date object; otherwise, it is left as a String. + ** + ** $ENDOFINFO + */ + +var current=none; + +(function (exports) { + + exports.stringify = function (obj) { + + return JSON.stringify(obj, function (key, value) { + if (value instanceof Function || typeof value == 'function') { + return value.toString(true); // try minification (true) if supported + } + if (value instanceof RegExp) { + return '_PxEgEr_' + value; + } + return value; + }); + }; + + exports.parse = function (str, mask) { + var code; + try { + with (mask) { + code= JSON.parse(str, function (key, value) { + var prefix; + + try { + if (typeof value != 'string') { + return value; + } + if (value.length < 8) { + return value; + } + + prefix = value.substring(0, 8); + + if (prefix === 'function') { + return eval('(' + value + ')'); + } + if (prefix === '_PxEgEr_') { + return eval(value.slice(8)); + } + return value; + } catch (e) { + throw {error:e,value:value}; + } + }); + }; + } catch (e) { + // within mask there was no current reference + if (current) current.error=e.value||str; + throw e.error||e; + } + return code; + }; + + exports.clone = function (obj, date2obj) { + return exports.parse(exports.stringify(obj), date2obj); + }; + + exports.current =function (module) { current=module.current; }; + +}(typeof exports === 'undefined' ? (window.JSONfn = {}) : exports)); + + +}; +BundleModuleCode['jam/code']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $RCS: $Id: code.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ + ** $VERSION: 1.10.7 + ** + ** $INFO: + ** + ** JavaScript AIOS Agent Code Management Sub-System + ** + ** New: Check pointing (CP) can be replaced with JS VM watchdog timer. + ** New: Fast sandboxed constructor + ** New: Dual mode JSON+/JSOB (with auto detection in toCode) + ** New: Dirty fastcopy process copy (JS object copy) + ** New: Function toString with minification (if supported by platform) + ** + ** JSOB: Simplified and compact textual representation of JS object including function code + ** + ** $ENDOFINFO + */ + +var options = { + compactit:true, + version:'1.10.7' +} + +try { + // Use built-in JS code minimizer if available + var M = process.binding('minify'); + Function.prototype._toString=Function.prototype.toString; + Function.prototype.toString = function (compact) { + return compact?M.minify(this._toString()):this._toString(); + } +} catch (e) {}; + +var Io = Require('com/io'); +var Json = Require('jam/jsonfn'); +var Comp = Require('com/compat'); +var sandbox = Require('jam/sandbox')(); +var current=none; +var Aios = none; +var util = Require('util'); + +/* Test if Json.stringify returns compacted code, otherwise text must be compacted here */ +function _testac (p1,p2) { + /* comment */ + this.x = p1; + this.y = p2; + this.z = 0; + this.act = { + init: function () { + /* comment */ + this.z=this.x; + this.x++; + } + } +} +var _testobj = new _testac(1,2); +options.compactit=Json.stringify(_testobj).length>72; +var inject = {cp:undefined,rt:undefined}; + +/** Construct an agent object with given arguments (array) + * + */ + +function construct(constructor,argArray) { + var inst = Object.create(constructor.prototype); + constructor.apply(inst, argArray); + return inst; +} + +/** Fast dirty copy (fork): Return a fresh copy of the agent object (i.e., process.agent, instead using ofCode/toCode transf.) + * attached to a new process object. + * All function and AIOS references will be copied as is. The AIOS level cannot be changed. The mask of the + * parent process is now valid for the copied process, too. Any changes in the parent environment effects the child + * process, too, and vice versa. + * + */ +function copyProcess(process) { + var _process,_agent,agent=process.agent,mask=process.mask; + + process.node.stats.fastcopy++; + + agent.process={}; + + for (var p in process) { + switch (p) { + case 'schedule': + if (process.schedule.length > 0) + agent.process[p]=process.schedule; + // keep it only if <> [] + break; + case 'blocked': + if (agent.process.suspended==true) + agent.process[p]=true; + // keep it only if it is true + break; + case 'gid': + case 'pid': + break; // ????? + // case 'delta': + case 'back': + case 'dir': + // keep it + agent.process[p]=process[p]; + break; + } + } + if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined; + agent['self']=undefined; + + _agent=Comp.obj.copy(agent); + + if (!_agent.process) + _process=Aios.Proc.Proc({mask:mask,agent:_agent}); + else { + _process=Aios.Proc.Proc(agent.process); + _process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:_agent}); + _agent['process']=undefined; + } + agent['self']=agent; + return _process; +} + +/** Return name of a class constructor function + * + */ +function className (f) { + var name=f.toString().match(/[\s]*function[\s]*([a-z0-9]+)[\s]*\(/); + return name?name[1]:"unknown" +} + +/** Create a sandboxed agent object process on the current node + * using either a sandboxed agent constructor object {fun,mask} + * or a generic agent class constructor function that is sandboxed here. + * + * Returns process object. + * + * type create = + * function (node:node, + * constructor:function|{fun:function,mask:{}}, + * args:{}|[],level?:number,className?:string) -> process + */ +function create(constructor,args,level,className) { + return createOn(current.node,constructor,args,level,className); +} + +/** Create a sandboxed agent process on a specified node + * using either a sandboxed agent constructor object {fun,mask} + * or a generic agent class constructor function that is sandboxed here. + * + * + * Returns process object. + * + * type createOn = + * function (node:node, + * constructor:function|{fun:function,mask:{}}, + * args:{}|*[],level?:number,className?:string) -> process + */ +function createOn(node,constructor,args,level,className) { + if (!constructor.fun && !Comp.obj.isFunction(constructor)) { + Aios.err('Code.create: No valid constructor function specified.'); + return; + } + + var code, + agent0, + agent, + process, + _process; + + _process=current.process; + current.process={timeout:0}; + if (level==undefined) level=1; + + try { + if (!constructor.fun) + constructor=makeSandbox(constructor,level); + if (!(args instanceof Array)) args=[args]; + + agent0= construct(constructor.fun,args); + + if (!agent0) { + Aios.err('Code.createOn ('+className+'): Agent constructor failed.'); + current.process=_process; + return null; + } + + process=makeProcess(agent0,constructor.mask); + process.resources.memory=constructor.size||0; + current.process=_process; + + } catch (e) { + current.process=_process; + Aios.err('Code.createOn ('+className+'): '+e); + return; + } + + agent=process.agent; + if (!Comp.obj.isArray(args) && Comp.obj.isObject(args)) + for (var p in args) { + if (Comp.obj.hasProperty(agent,p)) agent[p]=args[p]; + } + // Test minimal structure requirements + if (!agent['next'] || !agent['trans'] || !agent['act']) { + Aios.err('Code.createOn: Missing next/trans/act attribute in agent constructor '+className); + return none; // must be defined and initialized + } + process.level=level; + agent['self']=agent; + if (className) agent['ac']=className; + node.register(process); + + node.stats.create++; + + return process; +} + +/** Create a compiled agent process in a sandbox environment from an + * agent class constructor function on the current node. + * Returns JSON+/JSOB representation of agent process snapshot and + * the newly created process. + * + */ +function createAndReturn(constructor,ac,args,level) { + if (!(args instanceof Array)) args=[args]; +/* + var code = ofCode({agent:new constructor(args[0],args[1],args[2],args[3], + args[4],args[5],args[6],args[7], + args[8],args[9])},true); +*/ + var process,agent, + code = ofCode({agent:construct(constructor,args)},true); + if (level==undefined) level=1; + process = toCode(code,level); + agent=process.agent; + agent.id=Aios.aidgen(); + agent.ac=ac; + return {code:ofCode(process,false),process:process}; +} + +/** Fork an agent object and return JSON+/JSOB text code. + * Note: Forking discards current scheduling blocks (in contrast to migration)!!! + * + * Returns cleaned code (w/o CP and internal AIOS properties). + * + */ +function forkCode(process) { + var code='',p; + var agent = process.agent; + var self = agent.self; + // Clean up current agent process + agent['process']=undefined; + agent['self']=undefined; + + code=Aios.options.json?Json.stringify(agent):toString(agent); + + // Restore current agent process + agent.process=process; + agent.self=self; + + // Cleanup required? + + // CP/RT removal + if (inject.cp || inject.rt) + code=removeInjection(code); + return code; +} + +/** Convert agent object code from a process to text JSON+/JSOB. + * Returns cleaned code (w/o CP and internal AIOS properties). + * @clean: Code is already clean, no further filtering + * + */ +function ofCode(process,clean) { + var code='',p; + var agent=process.agent; + agent.process={}; + + for (var p in process) { + switch (p) { + case 'schedule': + if (process.schedule.length > 0) + agent.process[p]=process.schedule; + // keep it only if <> [] + break; + case 'blocked': + if (agent.process.suspended==true) + agent.process[p]=true; + // keep it only if it is true + break; + case 'gid': + case 'pid': + break; // ????? + // case 'delta': + case 'back': + case 'dir': + // keep it + agent.process[p]=process[p]; + break; + } + } + if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined; + agent['self']=undefined; + + code=Aios.options.json?Json.stringify(agent):toString(agent); + + if (clean && !options.compactit) return code; + + + /* Newline and comment removal is critical. We need to convert '\\''n' to '\n', + ** replacing comments, finally removing '\n'. This should only be done one time + ** on agent creation with compact=true option. Have top deal with '\\''\\''n', too! + ** More complictaed, we must preserve newlines after blocks! + */ + + if (!clean && (inject.cp||inject.rt)) + // CP/RT removal; no or only partial watchdog support by platform + code=removeInjection(code); + + if (options.compactit) code=minimize(code); + + return code; +} + +/** Fast copy agent process creation (virtual, migrate). + * All function and AIOS references will remain unchanged. The AIOS level cannot be changed. The mask of the + * original (died) process is now valid for the new process, too. + */ +function ofObject(agent) { + var process; + + if (!agent.process) + process=Aios.Proc.Proc({mask:agent.mask,agent:agent}); + else { + process=Aios.Proc.Proc(agent.process); + process.init({timeout:0,schedule:[],blocked:false,mask:agent.mask,agent:agent}); + agent['process']=undefined; + } + agent['mask']=undefined; + + process.node.stats.fastcopy++; + + return process; +} + +/** Convert agent text sources to agent code in JSOB format + * + */ +function ofString(source,mask) { + var code; + try { + // execute script in private context + with (mask) { + eval('"use strict"; code = '+source); + } + } catch (e) { console.log(e) }; + return code; +} + + +/** Create an agent process from agent object code + * + */ +function makeProcess (agent,mask) { + var process; + // Add all activities to the masked environment: + if (mask) for(var p in agent.act) { + mask[p]=p; + } + if (!agent.process) + process=Aios.Proc.Proc({mask:mask,agent:agent}); + else { + process=Aios.Proc.Proc(agent.process); + process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:agent}); + agent['process']=undefined; + } + agent['self']=agent; + + return process; +} + +/** Create a sandboxed agent class constructor object {fun,mask} from + * an agent class template constructor function providing + * a sandboxed agent constructor function and the sandbox + * mask agent environment. + * The optional environment object 'env' can contain additional references, e.g., + * activitiy references. + * + * Note: All agents created using the constructor function share the same mask + * object! + * + * typeof constructor = function|string + * typeof sac = {fun:function, mask: {}, size:number } + */ +function makeSandbox (constructor,level,env) { + var _process,sac,aios; + switch (level) { + case 0: aios=Aios.aios0; break; + case 1: aios=Aios.aios1; break; + case 2: aios=Aios.aios2; break; + case 3: aios=Aios.aios3; break; + default: aios=Aios.aios0; break; + } + _process=current.process; + current.process={timeout:0}; + sac=sandbox(constructor,aios,inject,env); + current.process=_process; + return sac; +} + +/** Minimize code text + * + */ +function minimize (code) { + // Inline and multi-line comments + var regex4= /\/\*([\S\s]*?)\*\//g; + var regex5= /([^\\}])\\n/g; + var regex6= /\/\/[^\n]+/g; + // Newline after {},; + var regex7= /[ ]*([{},; ]|else)[ ]*\n[\n]*/g; + // Filter for string quotes + var regex8= /([^\'"]+)|([\'"](?:[^\'"\\]|\\.)+[\'"])/g; + // Multi-spaces reduction + var regex9= / [ ]+/g; + // relax } syntax errors after newline removal; exclude keywords! + var regex10= /}\s+(?!else|finally|catch)([a-zA-Z_]+)/g; + // relax ) syntax errors after newline removal + var regex11= /\)\s+([a-zA-Z_]+)/g; + + code=code.replace(regex4,"") + .replace(regex5,'$1\n') + .replace(regex5,'$1\n') + .replace(regex6,"") + .replace(regex7,"$1") + .replace(regex8, function($0, $1, $2) { + if ($1) { + return $1.replace(regex9,' ').replace(regex10,'};$1').replace(regex11,')\n$1'); + } else { + return $2; + } + }); + return code; +} + +/** Print agent code + */ + +function print(agent) { + var process = agent.process; + var self = agent.self; + agent['process']=undefined; + agent['self']=undefined; + + var text=Aios.options.json?Json.stringify(agent):toString(agent); + + agent.process=process; + agent.self=self; + + if (!text) return 'undefined'; + var regex4= /\\n/g; + if (inject.cp || inject.rt) + // CP/RT removal; no or only partial watchdog support by platform + text= removeInjection(text); + + return text.replace(regex4,'\n'); +} + +/** Remove CP/RT injections from code text + * + */ +function removeInjection(text) { + // CP removal + if (inject.cp) { + var regex1= /CP\(\);/g; + var regex2= /\(\(([^\)]+)\)\s&&\sCP\(\)\)/g; + var regex3= /,CP\(\)/g; + text=text.replace(regex1,"").replace(regex2,"($1)").replace(regex3,""); + } + // RT removal + if (inject.rt) { + var regex4= /RT\(\);/g; + text=text.replace(regex4,""); + } + return text; +} + +/** Returns size of cleaned code (w/o CP and internal AIOS properties). + * + */ +function size(agent) { + var text='',p; + var process = agent.process; + var self = agent.self; + agent['process']=undefined; + agent['self']=undefined; + + text=Aios.options.json?Json.stringify(agent):toString(agent); + + agent.process=process; + agent.self=self; + + if (inject.cp || inject.rt) { + text=removeInjection(text); + } + + return text.length; +} + +/** Convert JSON+/or JSOB text to an agent object process encapsulated in a sandbox (aios access only). + * Returns process container with CP injected agent code (process.agent). + * + * CP Injection (required on generic JS VM platform w/o watchdog, e.g., node.js, browser): + * 1. In all loop expressions (for/while) + * 2. In all function bodies (start) + * + * No watchdog: Aios.watchdog == undefined (nodes.js, browser) + * Full watchdog implementation: Aios.watchdog && Aios.watchdog.checkPoint==undefined (jvm) + * Partial watchdog implementation with checkPoint function: Aios.watchdog.checkPoint (jxcore) + * + * + */ +function toCode(text,level) { + var agent, + process, + p, + aios, + next; + switch (level) { + case undefined: + case 0: aios=Aios.aios0; break; + case 1: aios=Aios.aios1; break; + case 2: aios=Aios.aios2; break; + case 3: aios=Aios.aios3; break; + default: aios=Aios.aios0; break; + } + if (inject.cp) { + // CP injection; no or only partial watchdog support + var regex1= /while[\s]*\(([^\)]+)\)/g; + var regex2= /for[\s]*\(([^\)]+)\)/g; + var regex3= /function([^\{]+)\{/g; + + text=text.replace(regex1,"while (($1) && CP())") + .replace(regex2,"for ($1,CP())") + .replace(regex3,"function $1{CP();"); + } + if (inject.rt) { + // RT injection + var regex4 = /catch[\s]*\([\s]*([a-zA-Z0-9_]+)[\s]*\)[\s]*\{/g; + text=text.replace(regex4,'catch ($1) {'+inject.rt+'($1);'); + } + + /* Set up an object to serve as the local context for the code + ** being evaluated. The entire global scope must be masked out! + ** Additionally, Json defines a variable current, which must be + ** masked, too. + */ + var mask = {current:undefined}; + // mask local properties + for (p in this) + mask[p] = undefined; + // mask global properties + for (p in global) + mask[p] = undefined; + // add sandbox content + for (p in aios) { + mask[p]=aios[p]; + } + // Auto detect JSON+ / RAWOBJ format + var isjson=Comp.string.startsWith(text,'{"'); + try {agent=isjson?Json.parse(text,mask):ofString(text,mask);} + catch (e) { + if (Aios.options.verbose) Aios.log('Aios.code.toCode: '+e+(current.error?(',\nin: '+current.error):'')); + return null; + } + if (!agent) { + return Aios.log('Aios.code.toCode: Invalid agent code received (empty or invalid source text?)'); + } + // Add all activities to the masked environment: + for(var p in agent.act) { + mask[p]=p; + } + if (!agent.process) + process=Aios.Proc.Proc({mask:mask,agent:agent}); + else { + process=Aios.Proc.Proc(agent.process); + process.init({timeout:0,schedule:[],blocked:false,mask:mask,agent:agent}); + agent['process']=undefined; + } + process.level=level; + process.resources.memory=text.length; + agent['self']=agent; + + return process; +} + +/** Convert agent object (i.e., process.agent) to a snapshot object. + * + */ +function toObject(process) { + var _process,_agent,agent=process.agent,mask=process.mask; + + agent.process={}; + + for (var p in process) { + switch (p) { + case 'schedule': + if (process.schedule.length > 0) + agent.process[p]=process.schedule; + // keep it only if <> [] + break; + case 'blocked': + if (agent.process.suspended==true) + agent.process[p]=true; + // keep it only if it is true + break; + case 'gid': + case 'pid': + break; // ????? + // case 'delta': + case 'back': + case 'dir': + // keep it + agent.process[p]=process[p]; + break; + } + } + if (Comp.obj.isEmpty(agent.process)) agent['process']=undefined; + agent['self']=undefined; + + _agent=Comp.obj.copy(agent); + _agent.mask = mask; + + agent['self']=agent; + return _agent; +} + +/** Convert agent object to text source in JSOB format + * + */ +function toString(o) { + var p,i,s='',sep; + if (Comp.obj.isArray(o)) { + s='[';sep=''; + for(p in o) { + s=s+sep+toString(o[p]); + sep=','; + } + s+=']'; + } else if (o instanceof Buffer) { + s='[';sep=''; + for(i=0;i0) continue; + _mask = _mask + ',' + p; + } + for (p in modules) + mask[p]=modules[p]; + if (env) for (p in env) + mask[p]=env[p]; + + if (typeof f == 'function') source = f.toString(true); // try minification (true) if supported + else source=f; + + if (inject.cp) { + // CP injection + var regex1= /while[\s]*\(([^\)]+)\)/g; + var regex2= /for[\s]*\(([^\)]+)\)/g; + var regex3= /function([^\{]+)\{/g; + + source=source.replace(regex1,"while (($1) && "+inject.cp+"())") + .replace(regex2,"for ($1,"+inject.cp+"())") + .replace(regex3,"function $1{"+inject.cp+"();"); + } + if (inject.rt) { + var regex4 = /catch[\s]*\([\s]*([a-zA-Z0-9_]+)[\s]*\)[\s]*\{/g; + source=source.replace(regex4,'catch ($1) {'+inject.rt+'($1);'); + } + + mask.eval=undefined;_mask += ',eval' + + var F = new Function(_mask,'"use strict"; with(this) { f=('+source+').bind(this)} return f') + .bind(mask); + return {fun:F(),mask:mask,_mask:_mask}; +} + +module.exports = { + sandbox:sandbox, + Sandbox:Sandbox +} + +module.exports = function () {return sandbox} +}; +BundleModuleCode['jam/sig']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 15/1/16 by sbosse. + ** $RCS: $Id: sig.js,v 1.3 2017/06/19 17:18:39 sbosse Exp sbosse $ + ** $VERSION: 1.3.1 + ** + ** $INFO: + ** + ** JavaScript AIOS Agent Signal Sub-System + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var current=none; +var Aios = none; + +var options = { + version:'1.3.1' +} + +var sig = { + broadcast: function (ac,range,sig,arg) { + var delivered=0; + // Currently only range=0 is supported => local agents + if (!Comp.obj.isString(ac)) {current.error='broadcast, invalid class '+ac;throw 'SIGNAL'}; + if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='broadcast, invalid signal '+sig;throw 'SIGNAL'}; + if (range!=0) {current.error='broadcast, invalid range '+range;throw 'SIGNAL'}; + for (var p in current.node.processes.table) { + var proc=current.node.processes.table[p]; + if (proc && proc.agent.ac == ac && proc.agent.on[sig]) { + proc.signals.push([sig,arg,current.process.agent.id]); + delivered++; + } + } + return delivered; + }, + // 'to' is the destination agent id + // 'from' indicates source agent id and remote signal propagation (from node.handle) + send: function (to,sig,arg,from) { + // Local agent? + var pid=current.node.processes.lookup(to); + if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='send, invalid signal';throw 'SIGNAL'}; + current.node.stats.signal++; + if (pid!=none) { + // [sig,arg,from] + current.node.processes.table[pid].signals.push([sig,arg,from||current.process.agent.id]); + // ?? Aios.emit('schedule',current.node); + return true; + } else { + // console.log('send',current.node.id,to,sig,arg,current.node.processes.gone[to]) + // Agent migrated and still cached? + if (current.node.processes.gone[to]) { + var curtime=Aios.time()-current.world.lag; + current.node.processes.gone[to].timeout=curtime+current.node.TMO; + return route(current.node.processes.gone[to].dir, + to,sig,arg,from||current.process.agent.id); + } else if (current.node.signals[to]) { + var curtime=Aios.time()-current.world.lag; + current.node.signals[to].timeout=curtime+current.node.TMO; + return route(current.node.signals[to].dir, + to,sig,arg,from||current.process.agent.id); + + } + } + return false; + }, + // Send a signal to agents on a specific remote destination node, e.g., to=DIR.DELTA([-1,-2]) + sendto: function (to,sig,arg) { + var delivered=0,i; + if (!Comp.obj.isString(sig) && !Comp.obj.isNumber(sig)) {current.error='sendto, invalid signal '+sig;throw 'SIGNAL'}; + if ((to.tag||to).indexOf('DIR') != 0) {current.error='sendto, invalid destination '+to; throw 'SIGNAL'}; + if (to == Aios.DIR.ORIGIN || (to.delta && Comp.array.zero(to.delta))) { + if (sig=='TS.SIG') { + // copy/collect from remote TS + for(i in arg) { + Aios.Ts.agent.out(arg[i]); + } + } else for (var p in current.node.processes.table) { + var proc=current.node.processes.table[p]; + if (proc && proc.agent.on && proc.agent.on[sig]) { + proc.signals.push([sig,arg,current.process.agent.id]); + delivered++; + } + } + return delivered; + } else { + return route(to, + none,sig,arg,current.process.agent.id); + } + }, + sleep: function (tmo) { + current.process.suspend(tmo?Aios.time()-current.world.lag+tmo:0); + }, + // Returns signal name + timer: { + // Add a oneshot or repeating timer raising a signal 'sig' after timeout 'tmo'. + add : function (tmo,sig,arg,repeat) { + if (!Comp.obj.isNumber(tmo)) {current.error='timer, invalid timeout '+tmo; throw 'SIGNAL'}; + if (!Comp.obj.isString(sig)) {current.error='timer, invalid signal '+sig; throw 'SIGNAL'}; + current.node.timers.push([current.process,(Aios.time()-current.world.lag+tmo),sig,arg,repeat]); + return sig; + }, + delete: function (sig) { + current.node.timers=current.node.timers.filter(function (t) { + return t[2]!=sig + }); + } + }, + wakeup: function (process) { + if (!process) current.process.wakeup(); + else process.wakeup(); + } +} + +/** Route signal to next node + * + */ +function route(dir,to,sig,arg,from) { + var node1=current.node, + chan=none, + dest, + stat, + alive = function () {return 1}, + sigobj = {sig:sig,to:to||dir,from:from,arg:arg,back:Aios.DIR.opposite(dir,true)}, + msg; + // console.log('route',node1.id,dir,sigobj) + switch (dir.tag||dir) { + case Aios.DIR.NORTH: chan=node1.connections.north; break; + case Aios.DIR.SOUTH: chan=node1.connections.south; break; + case Aios.DIR.WEST: chan=node1.connections.west; break; + case Aios.DIR.EAST: chan=node1.connections.east; break; + case Aios.DIR.UP: chan=node1.connections.up; break; + case Aios.DIR.DOWN: chan=node1.connections.down; break; + case Aios.DIR.NW: chan=node1.connections.nw; break; + case Aios.DIR.NE: chan=node1.connections.ne; break; + case Aios.DIR.SE: chan=node1.connections.se; break; + case Aios.DIR.SW: chan=node1.connections.sw; break; + case 'DIR.IP': chan=node1.connections.ip; dest=dir.ip; break; + case 'DIR.DELTA': + // Simple Delta routing: Minimize [x,y,..] -> [0,0,..] with {x,y,..} + sigobj.to=Comp.obj.copy(sigobj.to); + if (dir.delta[0]>0 && node1.connections.east && node1.connections.east.status()) + sigobj.to.delta[0]--,chan=node1.connections.east; + else if (dir.delta[0]<0 && node1.connections.west && node1.connections.west.status()) + sigobj.to.delta[0]++,chan=node1.connections.west; + else if (dir.delta[1]>0 && node1.connections.south && node1.connections.south.status()) + sigobj.to.delta[1]--,chan=node1.connections.south; + else if (dir.delta[1]<0 && node1.connections.north && node1.connections.north.status()) + sigobj.to.delta[1]++,chan=node1.connections.north; + else if (dir.delta[2]>0 && node1.connections.up && node1.connections.up.status()) + sigobj.to.delta[2]--,chan=node1.connections.up; + else if (dir.delta[2]<0 && node1.connections.down && node1.connections.down.status()) + sigobj.to.delta[2]++,chan=node1.connections.down; + break; + case 'DIR.PATH': + chan=node1.connections.path; dest=dir.path; + break; + case 'DIR.CAP': + if (!current.network) {current.error='No connection to server '+dir.cap; return false;}; + chan=node1.connections.dos; dest=Net.Parse.capability(dir.cap).cap; + break; + default: return false; + } + switch (dir.tag||dir) { + // One hop to next neighbour only? + case Aios.DIR.NORTH: + case Aios.DIR.SOUTH: + case Aios.DIR.WEST: + case Aios.DIR.EAST: + case Aios.DIR.UP: + case Aios.DIR.DOWN: + case Aios.DIR.NW: + case Aios.DIR.NE: + case Aios.DIR.SE: + case Aios.DIR.SW: + sigobj.to=Aios.DIR.ORIGIN; // After messaging signal has arrived + break; + } + if (chan==none || !chan.status(dest) /* OLDCOMM || !chan.signal*/) { + current.error='No connection to direction '+dir; + return false; + }; + node1.stats.signal++; + + if (Aios.options.fastcopy && chan.virtual) msg=sigobj; + else msg=Aios.Code.toString(sigobj); + /** OLDCOMM + chan.signal(msg,dest); + */ + /* NEWCOMM */ + chan.send({signal:msg,to:dest}); + + return true; +} + +module.exports = { + agent:sig, + options:options, + route:route, + current:function (module) { current=module.current; Aios=module; } +} +}; +BundleModuleCode['jam/node']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $RCS: $Id: node.js,v 1.3 2017/06/06 14:53:57 sbosse Exp $ + ** $VERSION: 1.10.3 + ** + ** $INFO: + ** + ** JavaScript AIOS Agent Node Sub-System + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var Security = Require('jam/security'); +var current=none; +var Aios = none; + +var options = { + version:'1.10.3' +} + +function aid(process) { return process.agent.id+':'+process.pid } +function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) }; + +/** Create a node. + * typeof options = {id,maxpro,maxts,position:{x,y},defaultLevel?,TMO?} + * + */ +var node= function (options) { + var self=this; + options=checkOptions(options,{}); + this.options=options; + this.id = checkOption(this.options.id,Aios.aidgen()); + this.position = checkOption(this.options.position,Aios.DIR.ORIGIN); + this.type = checkOption(this.options.type,'generic'); + this.verbose = checkOption(this.options.verbose,0); + // Default AIOS privilege level for received agent snapshots + this.defaultLevel=checkOption(this.options.defaultLevel,1); + this.processes={ + free:none, + max:checkOption(this.options.maxpro,100), + // (proc|undefined) [] + table:[], + // number|undefined [] + hash:[], + top:0, + used:0, + // a cache of migrated agents [id]={dir,timeout} + gone:[] + }; + this.processes.lookup = function (aid) { + if(self.processes.hash[aid]!=undefined) + return self.processes.hash[aid]; + else + return none; + }; + this.processes.process = function (aid) { + if (self.processes.hash[aid]!=_) + return self.processes.table[self.processes.hash[aid]]; + else + return none; + }; + + // Signal propagation cache [from]={dir:timeout} + this.signals=[]; + // [agent,tmo,sig,arg] + this.timers=[]; + + /** Connections to other nodes using P2P/IP/DOS links + * type link = {recv: function (callback),send:function(data), + * status: function() -> bool,count:number} + */ + this.connections={north:none,south:none,west:none,east:none}; + // tuple spaces + this.ts = Aios.Ts.create({maxn:checkOption(this.options.maxts,8), + id:this.id,node:self}); + + // Random ports for negotiation and node security + this.random = {}; + this.port = Security.Port.unique(); + this.random[this.port]=Security.Port.unique(); + + // Code dictionary shared by agents + this.library = {}; + + // Location (geo) and position (virtual) information + // location : { ip:string, + // gps:{lat:number, lon:number}, + // geo:{city:string,country:string,countryCode:string,region:string,zip:string} } + + this.location = null; + + + this.position = options.position; + + this.stats = { + cpu:0, + create:0, + fastcopy:0, + fork:0, + handled:0, + migrate:0, + received:0, + signal:0, + error:0, + tsout:0, + tsin:0, + agents:0, + } + + // Agent migration (gone) cache timeout + this.TMO = checkOption(this.options.TMO,100000); + // Needs node's service? + this.timeout = 0; + + Aios.emit('node+',self); + +}; + +/** Clean-up and destroy this node. Terminate all agent processes. + */ +node.prototype.destroy = function () { + var p,pro,_node=current.node,self=this; + this.connections={}; + current.node=this; + for(p in this.processes.table) { + pro=this.processes.table[p]; + if (!pro) continue; + pro.kill=true; + this.unregister(pro); + } + this.processes.gone=[]; + this.ts = none; + current.node=_node; + Aios.emit('node-',self); +} + +/** Export of code library + * + */ +node.prototype.export = function (name,code) { + // copy and sandbox code + if (!this.library[name]) + this.library[name]=Aios.Code.toString(code); +} + +/** Find an agent of the node by it's id and/or class, or agents matching a regular expression. + * + */ +node.prototype.getAgentProcess = function (id,ac) { + var matches=Comp.obj.isRegex(id)?[]:undefined, + table=this.processes.table,p; + if (!matches && this.processes.hash[id]!=undefined) { + p=table[this.processes.hash[id]]; + if (!ac || p.agent.ac==ac) return p; + } + for(var p in table) { + if (!table[p]) continue; + if (!matches && table[p].agent,id==id && (!ac || table[p].agent,ac=ac)) return table[p]; + if (matches && id.test(table[p].agent.id)) matches.push(table[p]); + } + return matches; +}; + +node.prototype.getAgent = function (id,ac) { + var pros=this.getAgentProcess(id,ac); + if (pros && Comp.obj.isArray(pros)) return Comp.array.map(pros,function (pro) {return pro.agent}); + else if (pros) return pros.agent; +}; + + +/** Receive a signal to be passed to an agent located here or routed to another node. + * Message is in JSOB text format or a JS object (fastcopy mode). + * + * typeof sigobj = {to,sig,arg,from,back?} + * + */ +node.prototype.handle = function (msg) { + var delivered,tmo,curtime=Aios.time()-current.world.lag, + _node=current.node,self=this, + sigobj=(typeof msg == 'string')?Aios.Code.ofString(msg,{}):msg; + if (!sigobj) return; // Error + current.node=this; + // console.log('handler',this.id,sigobj); + delivered=(Aios.Mobi.DIR.isDir(sigobj.to)?Aios.Sig.agent.sendto:Aios.Sig.agent.send) + (sigobj.to,sigobj.sig,sigobj.arg,sigobj.from); + if (delivered && sigobj.back) { + // Update signal route cache + tmo=curtime+this.TMO; + this.signals[sigobj.from]={dir:sigobj.back,timeout:tmo}; + this.timeout=min0(this.timeout,tmo); + }; + this.stats.handled++; + current.node=_node; + Aios.emit('schedule',self); +} + +/** Import code from library. + * Returns a sandboxed code copy. + * + */ +node.prototype.import = function (name) { + var code; + if (this.library[name]) code=Aios.Code.ofString(this.library[name],current.process.mask); + return code; +} + +/** Get node statistics + * + */ +node.prototype.info = function () { + var self=this, + p, + obj = {}; + ovj.stats = this.stats; + obj.id = this.id; + obj.position = this.position; + obj.agents={}; + var update=function (obj) { + var p; + for (p in obj) { + if (p != '_update') delete obj[p]; + } + for (p in self.processes.hash) { + if (self.processes.hash[p]!=_) + obj[p]=self.processes.table[self.processes.hash[p]]; + }; + } + obj.agents._update=update; + update(obj.agents); + + obj.signals=this.signals; + obj.timers=this.timers; + obj.ts=this.ts; + obj.connections=this.connections; + return obj; +} + +/** Print node statistics + * + */ +node.prototype.print = function (summary) { + var i,blocked,pending,total,ghost=0; + var str='==== NODE '+this.id+' ===='+NL; + str += 'SYSTIME='+Aios.time()+NL; + str += 'PROCESS TABLE >>'+NL; + if (summary) { + blocked=0; pending=0; total=0; ghost=0; + for (i in this.processes.table) { + if (this.processes.table[i]!=_) { + total++; + if (this.processes.table[i].blocked) blocked++; + if (this.processes.table[i].signals.length>0) pending++; + if (this.processes.table[i].agent.next==undefined) ghost++; + }; + } + str += ' TOTAL='+total+' BLOCKED='+blocked+' DYING='+ghost+' SIGPEND='+pending+NL; + } else { + for (i in this.processes.table) { + if (this.processes.table[i]!=_) { + str += ' ['+aid(this.processes.table[i])+'] '+ + 'NEXT='+this.processes.table[i].agent.next+' '+ + this.processes.table[i].print(); + }; + } + } + if (this.timers.length>0) { + str += 'TIMER TABLE >>'+NL; + for (i in this.timers) { + str += ' ['+aid(this.timers[i][0])+'] TMO='+this.timers[i][1]+' SIG='+this.timers[i][2]+NL; + } + } + str += 'TUPLE SPACES >>'+NL; + if (summary) str += ' '+this.ts.print(summary); else str += this.ts.print(summary); + return str; +} + +/** Receive migrated agent text code and create a process container registered on this node. + * If start=false then the next activity is computed here. + * + */ +node.prototype.receive = function (msg,start,from) { + // Save context + var _process=current.process, + _node=current.node, + self=this, + process,agent; + if (this.verbose>1) Io.log ('Received (start='+start+'):\n'+msg); + if (typeof msg !== 'object') process=Aios.Code.toCode(msg,this.defaultLevel); + else process=Aios.Code.ofObject(msg); // Virtual migration, same physical JAM + + if (!process) return; // Error + + agent=process.agent; + agent['self']=agent; + this.register(process); + this.stats.received++; + + if (process.dir || process.delta) { + /* TODO migration if this node is not the destination */ + }; + if (!process.back && from && from.address && from.port) process.back=Aios.DIR.IP(from.address+':'+from.port); + if (process.back && process.agent.parent) { // register child-to-parent signal path + tmo=Aios.time()-current.world.lag+this.TMO; + this.signals[process.agent.parent]={dir:process.back,timeout:tmo}; + this.timeout=min0(this.timeout,tmo); + } + + // console.log('node.receive '+this.position.x+','+this.position.y); + if (process.schedule.length == 0) { + // Compute next activity on THIS node + current.node=this; + current.process=process; + try { + if (!start) + agent.next=(typeof agent.trans[agent.next] == 'function')?agent.trans[agent.next].call(agent): + agent.trans[agent.next]; + if (process.blocked) throw 'BLOCKING'; + //console.log(agent.next); + } catch (e) { + Aios.aios.log ('Node.receive: Agent '+agent.id+' ['+agent.ac+'] in transition '+agent.next+ + ' failed:\n'+e+(current.error?' / '+current.error:', in: \n'+Aios.Code.print(agent.trans[agent.next]))+ + '\nat:\n'+Io.sprintstack(e)); + this.unregister(process); + }; + // Restore context + current.node=_node; + current.process=_process; + } +} + +/** Register agent code and assign a process container. + * + */ +node.prototype.register = function (process) { + var i,p, + self=this, + agent=process.agent; + if (this.processes.free==none) { + loop: for (i in this.processes.table) { + if (this.processes.table[i]==_) { this.processes.free=i; break loop}; + } + } + if (this.processes.free!=none) { + this.processes.table[this.processes.free]=process; + process.pid=this.processes.free; + process.agent=agent; + this.processes.free=none; + } else { + this.processes.table[this.processes.top]=process; + process.agent=agent; + process.pid=this.processes.top; + this.processes.top++; + } + if (agent.id==undefined) agent.id=Aios.aidgen(); + this.processes.hash[agent.id]=process.pid; + this.processes.used++; + this.stats.agents++; + + if (this.processes.gone[process.agent.id]) + // Agent returned again! + this.processes.gone[process.agent.id]=undefined; + process.node=this; + Aios.emit('agent+',process,self); + Aios.emit('schedule',self); +} + +/** Node Garbage Collection and Timeout Service + * + */ +node.prototype.service = function (curtime) { + var nexttime=0,p,pro,sig; + + // TS cleanup management + this.ts.service(curtime); + + if (curtime 5) + key[i] = 0; + else { + if ((get_portbyte(port, (j >> 3)) & (1 << (j & 7))) != 0) + key[i] = 1; + else + key[i] = 0; + j++; + } + } + + Des48.des_OWsetkey(key); + /* + ** Now go encrypt constant 0 + */ + block=Des48.des_OWcrypt48(block); + + + /* + ** and put the bits in the destination port + */ + var pb = 0; + + for (i = 0; i < PORT_SIZE;i++) { + var pbyte = 0; + for (j = 0; j < 8; j++) { + pbyte = pbyte | (block[pb] << j); + pb++; + } + pubport=set_portbyte(pubport, i, pbyte); + } + return pubport; +} + +function pad(str,size) { + while (str.length < (size || 2)) {str = "0" + str;} + return str; +} + +function port_cmp(port1,port2) { + if (port1==undefined || port2==undefined) return (port1==port2); + else return String.equal(port1,port2); +} + +function port_copy(port) { + return String.copy(port); +} + +// Expected format: XX:XX:XX:XX:XX +function port_of_string(str,compact) { + var tokens=str.split(':'),i,port=''; + for (i=0;i 0) str = str + ':'; + str = str + pad(num.toString(16).toUpperCase(), 2); + } + } else str='undefined'; + return str; +} + + +function prv2pub (port) { + var putport; + if (priv2pub_cache[port] == undefined) { + putport=one_way(port); + priv2pub_cache[port] = putport; + } else putport = priv2pub_cache[port]; + return putport; +} + +function prv_cmp(prv1,prv2) { + return (prv1==undefined&&prv2==undefined) || + (prv1.prv_obj==prv2.prv_obj && + prv1.prv_rights==prv2.prv_rights && + port_cmp(prv1.prv_rand,prv2.prv_rand)) +} + +/** + ** Decode a private structure (check for a valid private field) + * + * typeof @prv = privat + * typeof @rand = port + * returns boolean + */ +function prv_decode (prv,rand) { + if (prv.prv_rights == PRV_ALL_RIGHTS) + return port_cmp(prv.prv_rand,rand); + else { + var tmp_port = port_copy(rand), + pt0 = get_portbyte(tmp_port, 0), + pr0 = prv.prv_rights; + tmp_port = set_portbyte(tmp_port, 0, (pt0 ^ pr0)); + tmp_port = one_way(tmp_port); + return port_cmp(prv.prv_rand, tmp_port) + } +} + +/* + ** Encode a private part from the object number, the rights field + ** and the random port. + ** Returns the created private structure. + */ +function prv_encode(obj,rights,rand) { + var tmp_port = port_copy(rand), + r1 = rights, + rmask = PRV_ALL_RIGHTS; + + if (rights == PRV_ALL_RIGHTS) + return Private(obj,r1 & rmask,tmp_port); + else { + var pt0 = get_portbyte(tmp_port,0); + tmp_port = set_portbyte(tmp_port,0,pt0 ^ r1); + tmp_port = one_way(tmp_port); + return Private(obj,r1 & rmask,tmp_port) + } +} + +function prv_of_string(str) { var pp=prv_parse(str,0); return pp?pp.priv:undefined } + +/* + ** Return the private object number form a private structure + */ +function prv_number(prv) { + return prv.prv_obj; +} + +// Expected format: obj(right)[port] +function prv_parse(str,offset) { + var priv=Private(); + var sv; + var len=str.length,pos=offset; + if (str[pos]=='(') pos++; + sv=''; + while(str[pos]!='(') { + sv=sv+str[pos]; + pos++; + } + priv.prv_obj=Perv.int_of_string(sv); + sv=''; + if (str[pos]=='(') pos++; + while(str[pos]!=')') { + sv=sv+str[pos]; + pos++; + } + priv.prv_rights=Perv.int_of_string('0x'+sv); + if (str[pos]==')') pos++; + var pp=port_parse(str,pos); + if (pp==undefined) return undefined; + priv.prv_rand=pp.port; + pos=pp.pos; + return {priv:priv,pos:pos}; +} + + +function prv_to_string(priv) { + var str=''; + if (priv==undefined) return 'undefined'; + str=priv.prv_obj; + str=str+'('+String.format_hex(priv.prv_rights,2).toUpperCase()+')['; + str=str+port_to_string(priv.prv_rand)+']'; + return str; +} + +/** Restrict a private field (rights&mask) of a capability. + * + * @param {privat} priv + * @param {number} mask rights restriction mask + * @param {port} random secret server random port + */ +function prv_restrict(priv,mask,random) { + var pr = prv_encode(priv.prv_obj, + priv.prv_rights & mask, + random); + return pr; +} +/* + ** Return the private rights field. + */ +function prv_rights(prv) { + return prv.prv_rights & Rights.PRV_ALL_RIGHTS; +} +/* + ** Check the private rights field: 1. Validation, 2: Required rights. + */ +function prv_rights_check(prv,rand,required) { + if (!Net.prv_decode(prv,rand)) return false; + return (prv.prv_rights & required)==required; +} + +/* + * Return a new random unique port. + * + * Warning: the quality of the random ports are strongly + * related to JSVMs underlying random generator. + * + * typeof return = port + */ +function uniqport() { + var port = String.create (PORT_SIZE); + var i,values; + + do { + values = Rnd.generate({number:true,length:PORT_SIZE}); + for (i = 0; i <= (PORT_SIZE - 1); i++) + port = String.set(port, i, (Perv.char_of_int(values[i]))); + if (uniquePorts[port]) uniquePorts[port]++; + else uniquePorts[port]=1; + } while (uniquePorts[port]>1); + return port; +} + +Port.equal = port_cmp +Port.toString = port_to_string +Port.ofString = port_of_string +Port.prv2pub = prv2pub +Port.unique = uniqport +Private.decode = prv_decode +Private.encode = prv_encode +Private.equal = prv_cmp +Private.number = prv_number +Private.ofString = prv_of_string +Private.restrict = prv_restrict +Private.rights = prv_rights +Private.rights_check = prv_rights_check +Private.toString = prv_to_string +Capability.toString = cap_to_string +Capability.ofString = cap_of_string + + +var Security = { + current:function (module) { current=module.current; Aios=module; }, + + PORT_SIZE:PORT_SIZE, + PRIV_SIZE:PRIV_SIZE, + Private:Private, + Capability: Capability, + Port: Port, + nilport: Port(), + nilpriv: Private(0,0,Port()), + nilcap: Capability(Port(),Private(0,0,Port())), + one_way : one_way, + prv2pub : prv2pub, +} + +module.exports = Security; + +}; +BundleModuleCode['dos/des48']=function (module,exports,global,process){ +/** + ** ================================== + ** OOOO OOOO OOOO O O OOOO + ** O O O O O O O O O + ** O O O O O O O O O + ** OOOO OOOO OOOO O OOO OOOO + ** O O O O O O O O O + ** O O O O O O O O O + ** OOOO OOOO OOOO OOOO O O OOOO + ** ================================== + ** BSSLAB, Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR. + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2016 BSSLAB + ** $CREATED: 3/30/15 by sbosse. + ** $VERSION: 1.1.4 + ** + ** $INFO: + ** + ** DOS: Encryption 48bit + ** + ** $ENDOFINFO + */ +var util = Require('util'); +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var Array = Comp.array; +var assert = Comp.assert; + + +const des_HBS = 24; +const des_BS = des_HBS * 2; + + +/* +** Initial permutation, +*/ + +var des_IP = [ + 23, 27, 34, 44, 37, 17, 12, 42, + 3, 32, 41, 29, 20, 2, 1, 10, + 0, 28, 40, 6, 7, 11, 16, 8, + 25, 30, 14, 26, 47, 38, 19, 43, + 18, 5, 35, 39, 36, 21, 4, 45, + 24, 22, 13, 33, 31, 9, 15, 46 ]; + +/* +** Final permutation, FP = IP^(-1) +*/ + +var des_FP = [ + 16, 14, 13, 8, 38, 33, 19, 20, + 23, 45, 15, 21, 6, 42, 26, 46, + 22, 5, 32, 30, 12, 37, 41, 0, + 40, 24, 27, 1, 17, 11, 25, 44, + 9, 43, 2, 34, 36, 4, 29, 35, + 18, 10, 7, 31, 3, 39, 47, 28 ]; + +/* +** Permuted-choice 1 from the key bits +** to yield C and D. +** Note that bits 8,16... are left out: + ** They are intended for a parity check. +*/ + +var des_PC1_C = [ + 57,49,41,33,25,17, 9, + 1,58,50,42,34,26,18, + 10, 2,59,51,43,35,27, + 19,11, 3,60,52,44,36 ]; + +var des_PC1_D = [ + 63,55,47,39,31,23,15, + 7,62,54,46,38,30,22, + 14, 6,61,53,45,37,29, + 21,13, 5,28,20,12, 4 ]; + + +/* +** Sequence of shifts used for the key schedule. +*/ + +var des_shifts = [ + 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 ]; + + + +/* +** Permuted-choice 2, to pick out the bits from +** the CD array that generate the key schedule. +*/ + +var des_PC2_C = [ + 14,17,11,24, 1, 5, + 3,28,15, 6,21,10, + 23,19,12, 4,26, 8, + 16, 7,27,20,13, 2 ]; + +var des_PC2_D = [ + 41,52,31,37,47,55, + 30,40,51,45,33,48, + 44,49,39,56,34,53, + 46,42,50,36,29,32 ]; + +/* +** The C and D arrays used to calculate the key schedule. +*/ + + +var des_C = Array.create(56,0); +// des_D = des_C[28] +var des_D_get = function (i) {return des_C[i+28]}; +var des_D_set = function (i,sval) { des_C[i+28] = sval }; + +/* +** The key schedule. +** Generated from the key. +*/ + +var des_KS= Array.create_matrix(16,48,0); + +var des_OWsetkey = function(key) { + var ks = []; + var t = 0; + var i,j,k; + /* + ** First, generate C and D by permuting + ** the key. The low order bit of each + ** 8-bit char is not used, so C and D are only 28 + ** bits apiece. + */ + + for(i = 0;i < 28;i++) { + + var index1 = des_PC1_C[i] - 1; + var index2 = des_PC1_D[i] - 1; + + des_C[i] = key[index1]; + des_D_set(i,key[index2]); + } + + /* + ** To generate Ki, rotate C and D according + ** to schedule and pick up a permutation + ** using PC2. + */ + + + for (i = 0 ;i< 16;i++) { + + ks = des_KS[i]; + + // rotate + for (k = 0; k < des_shifts[i]; k++) { + t = des_C[0]; + + for (j = 0; j < 27; j++) { + des_C[j] = -des_C[j + 1]; + } + + des_C[27] = t; + t = des_D_get(0); + + for (j = 0; j < 27; j++) { + des_D_set(j, des_D_get(j + 1)); + } + des_D_set(27, t); + } + + /* + ** get Ki. Note C and D are concatenated. + */ + + for (j = 0; j < 24; j++) { + ks[j] = des_C[des_PC2_C[j] - 1]; + ks[j + 24] = des_D_get(des_PC2_D[j] - 28 - 1); + } + + } +}; + + +/* +** The E bit-selection table. +*/ + +var des_E = [ + 22, 15, 12, 3, 8, 2, 23, 16, + 14, 13, 9, 10, 0, 1, 21, 19, + 18, 6, 11, 7, 17, 4, 20, 5, + 5, 17, 11, 13, 12, 14, 8, 7, + 19, 22, 18, 9, 3, 4, 1, 6, + 16, 2, 20, 15, 10, 23, 0, 21 ]; + + +/* +** The 8 selection functions. +** For some reason, they give a 0-origin +** index, unlike everything else. +*/ + +var des_S = [ + [ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, + 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, + 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, + 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13 ], + + [ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, + 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, + 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, + 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9 ], + + [ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, + 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, + 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12 ], + + [ 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, + 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, + 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, + 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14 ], + + [ 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, + 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, + 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, + 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3 ], + + [ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, + 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, + 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, + 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13 ], + + [ 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, + 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, + 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, + 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12 ], + + [ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, + 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, + 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, + 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11 ] + ]; + + +/* +** P is a permutation on the selected combination +** of the current L and key. +*/ + +var des_P = [ + 3, 13, 9, 12, 8, 20, 21, 7, + 5, 23, 16, 1, 14, 18, 4, 15, + 22, 10, 2, 0, 11, 19, 17, 6 ]; + +var des_L = Array.create(des_BS,0); +var des_R_get = function (i) { return des_L[(i+des_HBS)]}; +var des_R_set = function (i,sval) { des_L[i+des_HBS]= sval}; +var des_tempL = Array.create(des_HBS,0); +var des_f = Array.create (32,0); + +/* +** Warning!! +** +** f[] used to be HBS for some years. +** 21/6/1990 cbo and sater discovered that inside the loop where f is computed +** indices are used from 0 to 31. These overlapped the preS array which is +** declared hereafter on all compilers upto that point, but only those +** values that were not used anymore. But the values of f are only used +** upto HBS. Makes you wonder about the one-way property. +** Then came ACK, and reversed the order of the arrays in the image. +** +** As a short term solution f[] was increased to 32, but in the long run +** someone should have a good look at our "oneway" function +*/ + +/* +** The combination of the key and the input, before selection. +*/ +var des_preS = Array.create (48,0); + +/* +** The payoff: encrypt a block. (Now 48 bytes, 1 bit/byte) +*/ + +var des_OWcrypt48 = function(block) { + var ks = []; + var t1 = 0; + var t2 = 0; + var i, j, k; + /* + ** First, permute the bits in the input + */ + + for (j = 0; j <= (des_BS - 1); j++) { + des_L[j] = block[des_IP[j]]; + } + /* + ** Perform an encryption operation 16 times. + */ + + for (i = 0; i <= 15; i++) { + ks = des_KS[i]; + + /* + ** Save the R array, + ** which will be the new L. + */ + + for (j = 0; j < (des_HBS - 1); j++) { + des_tempL[j] = des_R_get(j); + } + /* + ** Expand R to 48 bits using the E selector; + ** exclusive-or with the current key bits. + */ + + for (j = 0; j <= 47; j++) { + des_preS[j] = (des_R_get(des_E[j])) ^ ks[j]; + } + + /* + ** The pre-select bits are now considered + ** in 8 groups of 6 bits each. + ** The 8 selection functions map these + ** 6-bit quantities into 4-bit quantities + ** and the results permuted + ** to make an f(R, K). + ** The indexing into the selection functions + ** is peculiar; it could be simplified by + ** rewriting the tables. + */ + + t1 = 0; + t2 = 0; + + for (j = 0; j <= 7; j++) { + var sind2 = + ((des_preS[t1 + 0] << 5) & 0xff) + + ((des_preS[t1 + 1] << 3) & 0xff) + + ((des_preS[t1 + 2] << 2) & 0xff) + + ((des_preS[t1 + 3] << 1) & 0xff) + + ((des_preS[t1 + 4] << 0) & 0xff) + + ((des_preS[t1 + 5] << 4) & 0xff); + + k = des_S[j][sind2]; + + des_f[t2 + 0] = (k >> 3) & 0x1; + des_f[t2 + 1] = (k >> 2) & 0x1; + des_f[t2 + 2] = (k >> 1) & 0x1; + des_f[t2 + 3] = (k >> 0) & 0x1; // 3 .. 31 !!! + + t1 = t1 + 6; + t2 = t2 + 4; + } + + /* + ** The new R is L ^ f(R, K). + ** The f here has to be permuted first, though. + */ + + for (j = 0; j < des_HBS; j++) { + des_R_set(j, (des_L[j] ^ des_f[des_P[j]])); + } + + /* + ** Finally, the new L (the original R) + ** is copied back. + */ + + for (j = 0; j < des_HBS; j++) { + des_L[j] = des_tempL[j]; + } + + } + + + /* + ** The output L and R are reversed. + */ + + for (j = 0; j < des_HBS; j++) { + t1 = des_L[j]; + des_L[j] = des_R_get(j); + des_R_set(j, t1); + } + + /* + ** The final output + ** gets the inverse permutation of the very original. + */ + + for (j = 0; j < des_BS; j++) { + block[j] = des_L[des_FP[j]]; + } + return block; +}; + +module.exports = { + des_OWsetkey:des_OWsetkey, + des_OWcrypt48:des_OWcrypt48 +}; +}; +BundleModuleCode['jam/proc']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $RCS: $Id: proc.js,v 1.1 2017/05/20 15:56:53 sbosse Exp $ + ** $VERSION: 1.5.1 + ** + ** $INFO: + ** + ** JavaScript AIOS Agent Process Module + ** + ** $ENDOFINFO + */ +var Comp = Require('com/compat'); +var current=none; +var Aios = none; + +var options = { + version:'1.4.5' +} + +var PRIO = { + LOW:0, + NORMAL:1, + HIGH:2 +} +/* +** Agent process - must be compatible with scheduler context process! +*/ + +var proc = function (properties) { + // Agent code + this.agent={}; + + // Internal scheudling blocks - can'tmigrate - if any + // Handled by global scheduler (DOS) + this.block=[]; + // Process execution suspended? + this.blocked=false; + + // Process blocking timeout + this.timeout=0; + + // For soft checkpointing + this.runtime=0; + + // Ressource control (node constraints) + this.resources = { + consumed:0, // total processing time consumed + memory:0, // total memory (code+data) consumed + tuples:0, // total tuple generation + agent:0, // total agents created + } + + this.level=undefined; + + // Dynamic process priority effecting scheduling order + this.priority = PRIO.NORMAL; + + // Agent scheduling blocks - can migrate! + // Handled by AIOS scheduler only! + // function [] + this.schedule=[]; + // Agent activity suspended, waiting for an event? + this.suspended=false; + this.suspendedIn=undefined; + + this.error=none; + + // process id + this.pid=none; + // process parent id + this.gid=none; + this.id='agent'; + this.mask={}; + // [sig,arg,from] [] + this.signals=[]; + + // Did we moved? + this.move=none; + // Killing state + this.kill=false; + // Dead state + this.dead=false; + // Pending next transition computatuion? + this.transition=false; + + for (var p in properties) { + if (properties[p]!=undefined) this[p]=properties[p]; + } + + // Used in simulators only: A geometric shape object + this.shape=undefined; + + if (current.world) this.parent = current.world.context; + this.node=current.node; +} + + +/** Execute a callback function in this agent process context immediately (should invoke scheduler and CB!) + * + */ +proc.prototype.callback = function (cb,args) { + var _process=current.process,_node=current.node, res; + current.node=this.node; + current.process=this; + try { + res=cb.apply(this.agent,args||[]); + } catch (e) { + Aios.aios.log('Caught callback error: '+e); + } + current.process=_process; + current.node=_node; + return res; +} + +/** Execute this process immediately + * + */ +proc.prototype.exec = function() { + var _process=current.process,_node=current.node, res; + current.node=this.node; + res = Aios.schedule(this); + current.process=_process; + current.node=_node; + return res; +} + +/** Finalize this process + * + */ +proc.prototype.finalize = function() { + this.kill=true; + this.suspended=false; + current.node.unregister(this); +} + + +/** Fork an agent process. + * Returns child process. + * If level is not specified, the parent process level is used. + */ +proc.prototype.fork = function(parameters,level,dirty) { + var code, + _process=current.process, + process_, + agent_, + p; + if (dirty && level!=undefined) dirty=false; // Dirty process copy with level change not possible! + if (level==undefined) level=current.process.mask.privilege(); + else level=Math.min(current.process.mask.privilege(),level); + if (!dirty) { + code = Aios.Code.forkCode(current.process); + process_ = Aios.Code.toCode(code,level); + } else { + process_ = Aios.Code.copyProcess(current.process); + } + agent_ = process_.agent + agent_.id=Aios.aidgen(); + agent_.parent=current.process.agent.id; + process_.init({gid:current.process.pid}); + current.process=process_; + current.node.register(process_); + // Update forked child agent parameters only if they already exist + for (p in parameters) { + if (Comp.obj.hasProperty(agent_,p)) agent_[p]=parameters[p]; + } + // Should next activity computed in scheduler by setting process.transition ??? + // compute next activity after fork if there is no scheduling block, + // no parameter next set, + // and forkCode should always discard all current schedule blocks! + if (!parameters.next) try { + agent_.next=(typeof agent_.trans[agent_.next] == 'function')?agent_.trans[agent_.next].call(agent_): + agent_.trans[agent_.next]; + } catch (e) { /*kill agent?*/ process_.kill=true; }; + this.node.stats.fork++; + current.process=_process; + return process_; +} + +proc.prototype.init = function (properties) { + for (var p in properties) { + if (this[p]!=undefined) this[p]=properties[p]; + } +} + +proc.prototype.print = function () { + var str='', + agent=this.agent; + str = 'PID='+this.pid+ + (this.gid?' GID='+this.gid:'')+ + (this.timeout?(' TMO='+this.timeout):'')+ + (this.blocked?' BLOCKED':'')+ + (this.suspended?' SUSP':'')+ + (this.kill?' KILL':'')+ + (this.dead?' DEAD':''); + if (this.schedule.length>0) str += ' SCHEDULE='+this.schedule.length; + if (this.block.length>0) str += ' BLOCK='+this.block.length; + if (this.signals.length>0) str += ' SIGNALS('+this.signals.length+'):'+ + Comp.printf.list(this.signals,function (s) {return s[0]}); + if (this.transition) str += ' TRANS'; + if (this.consumed|0) str += ' CONS='+(this.consumed|0); + if (agent) str += ' AGENT '+agent.id+' next='+agent.next; + return str; +} + + +/** + * Suspend agent activity processing, but not internal block scheduling! + */ +proc.prototype.suspend = function (timeout,transition,suspendedIn){ + if (!this.kill && !this.dead) { + this.suspended=true; + if (timeout!=undefined) this.timeout=timeout; + if (transition) this.transition=true; // pending next computation + this.suspendedIn = suspendedIn; + } +} + + +proc.prototype.update = function (properties) { + for (var p in properties) { + this[p]=properties[p]; + } +} + + +/** + * Wakeup agent process from a previous suspend call (sleep) + */ +proc.prototype.wakeup = function (immediate){ + this.suspended=false; + this.timeout=0; + if (!this.kill && !this.dead && (immediate || this.schedule.length == 0)) { + var _process=current.process,_node=current.node; + current.node=this.node; + if (this.suspendedIn=='ts') this.node.ts.cleanup(this,true); // Have to call callback handler to inform about timeout!? + this.suspendedIn=undefined; + this.transition=this.schedule.length == 0; + // Re-entering the scheduler is a bad idea!? + Aios.schedule(this); + current.process=_process; + current.node=_node; + } +} + +function Proc(properties) { + var obj = new proc(properties); + return obj; +} + + +module.exports = { + agent: { + fork:function fork(parameters) { + return current.process.fork(parameters); + } + }, + isProc: function (o) { return o instanceof Proc }, + Proc:Proc, + PRIO:PRIO, + current:function (module) { current=module.current; Aios=module; }, + options:options +} +}; +BundleModuleCode['jam/ts']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $RCS: $Id: ts.js,v 1.3 2017/06/19 17:18:39 sbosse Exp sbosse $ + ** $VERSION: 1.8.2 + ** + ** $INFO: + ** + ** JavaScript Agent Tuple-Space Sub-System + ** + ** New: xx.try function synonyms (inp.try, rd.try,..) and try functions now fire callback on timeout + ** New: testandset + ** New: eval/listen + ** New: Patterns can contain regular expression! (p_i instanceof RegExp) + ** New: A rd/inp operation can return all matching tuples + ** New: alt operation supporting listening on multiple patterns + ** New: Distributed TS with collect, copyto, store + ** + ** Exeception: 'TS' + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var current=none; +var Aios = none; +var verbose=false; + +var options = { + version:'1.8.2', +} + +function aid(process) { return process.agent.id+':'+process.pid } +function log(tsi,process,msg) { + if (verbose && process) Aios.aios.log('[TS'+(tsi.i)+':'+current.node.ts.id+'] Ag ' + + aid(process)+ ' '+msg); + else if (verbose) Io.log('[TS'+(tsi.i)+':'+current.node.ts.id+'] SYS'+msg); +} +function min0(a,b) { return a==0?b:(b==0?a:Comp.pervasives.min(a,b)) }; + +/******************************************* +** Waiter management +*******************************************/ + +/** Add a waiter to a tuple space data base + * + */ +var addwaiter = function (tsi,waiter) { + var index,key; + if (tsi.waiters.free.length==0) { + index=tsi.waiters.length; + tsi.waiters.push(waiter); + } else { + index=tsi.waiters.free[0]; + tsi.waiters[index]=waiter; + tsi.waiters.free.shift(); + } + if (typeof (key=waiter.pat[0]) == 'string') switch (waiter.op) { + case 'listen': + tsi.waiters.hash[key]=index; break; + } +} + +/** Check for waiting agent processes and try to match the provided tuple. + * Readers can read multiple copies of the tuple, whereby consumers can only read the tuple one time. + * Consumers (in-op) can be in a waiting list (next/prev). If one waiter in a list consumes + * a tuple, all waiters must be removed. The other waiters (the same process, but different patterns; alt-op) + * can be in different tuple data bases! + * + */ +var checkwaiter = function (tsi,tuple,callback) { + var res,consumed=false, + i,waiter, + _process=current.process; + // Create agent callback + function cb(waiter,res) { + Aios.CB(waiter.pro,function () {waiter.cb.call(waiter.pro.agent,res)}); + } + for(i=0;i0) { + consumed = Comp.array.findmap(current.node.ts.consumers,function (consumer) { + return consumer(tuple); + }); + } + return consumed; +} + +var findwaiter = function (tsi,waiter) { + var i; + for(i=0;icurrent.node.ts.n || nary==0) return; + tsi=current.node.ts.db[nary]; + if (!all && Comp.isString(pat[0]) && tsi.hash[pat[0]]!=undefined) { + // Speedup trial with hash key + res=match(tsi.data[tsi.hash[pat[0]]],pat); + } + if (res==none) { + res = (all?Comp.array.filtermap:Comp.array.findmap)(tsi.data,function (tuple) { + if (tuple==_) return none; + else return match(tuple,pat); + }); + if (res && res.length==0) res=none; + if (res == none && current.node.ts.providers.length>0) { + res = Comp.array.findmap(current.node.ts.providers,function (provider) { + return provider(pat); + }); + } + } + return res; +} + +/******************************************* +** Tuple management +*******************************************/ + +/** + * Compare two values, check equiality + */ +var equal = function(x,y) { + var i; + if(x==y) return true; + if (Comp.obj.isArray(x) && Comp.obj.isArray(y)) { + if (x.length!=y.length) return false; + for(i in x) { + if (x[i] != y[i]) return false; + } + return true; + } + return false; +} + +/** Match a tuple element with a template pattern element y. + * + */ +var match1 = function (x,y) { + if (y==any) return true; + if (x==y) return true; + if ((x instanceof Array) && (y instanceof Array)) return match(x,y)!=none; + if (y instanceof RegExp && typeof x == 'string' && y.test(x)) return true; + return false; +} + +/** Match a tuple with a template and return none or the original tuple (equivalence result?) + * + */ +var match = function (tuple,templ) { + var i; + if (tuple.length != templ.length) return none; + for(i in tuple) { + if (!match1(tuple[i],templ[i])) return none; + }; + return tuple; +} + + +/** Find and remove one/all matching tuple(s) from the database based on pattern matching + * + */ +var remove = function (pat,all) { + var tsi,nary=pat.length,res=none,removed=false,hashed=_; + if (nary>current.node.ts.n || nary==0) return; + tsi=current.node.ts.db[nary]; + if (!all && Comp.isString(pat[0])) hashed=tsi.hash[pat[0]]; + if (hashed != _) { + // Speedup trial with hash key + res=match(tsi.data[hashed],pat); + if (res) { + // invalidate matching tuple in data list + removed=true; + tsi.data[hashed]=_; + tsi.tmo[hashed]=0; + // remember the free slot in the data list + if (tsi.free==none) tsi.free=hashed; + // invalidate hash entry - tuple is consumed + delete tsi.hash[pat[0]]; + } + } + if (res==none || removed==false) { + res = (all?Comp.array.filtermap:Comp.array.findmap)(tsi.data,function (tuple,i) { + if (tuple==_) return none; + var res_=match(tuple,pat); + if (res_!=none) { + if (Comp.isString(pat[0]) && tsi.hash[pat[0]]==i) { + // Invalidate hash - tuple is consumed + delete tsi.hash[pat[0]]; + } + tsi.data[i]=_; + tsi.tmo[i]=0; + if (tsi.free==none) tsi.free=i; + return res_; + } else return none; + }); + if (res && res.length==0) res=none; + } + return res; +} + + +/******************************************* +** Tuple Space Agent/Client API +*******************************************/ + +var ts = { + // consuming - tmo <> 0 => try_alt + alt: function (pats,callback,all,tmo) { + var tsi,nary, + i,p,pat,waiters=none,last=none; + for(i in pats) { + pat=pats[i]; + nary=pat.length; + if (nary>current.node.ts.n || nary==0) return none; + res = remove(pat,all); + if (res && res.length) current.node.stats.tsin += (all?res.length:1); + if (res && callback) { + callback.call(current.process.agent,res); + return; + } else if (callback && (tmo==undefined||tmo>0)) { + if (waiters==none) + waiters={pat:pat, + pro:current.process, + cb:callback, + op:'in'+(all?'-all':''), + tmo:tmo>0?Aios.time()-current.world.lag+tmo:0 + },last=waiters; + else { + last.next={pat:pat, + pro:current.process, + cb:callback, + op:'in'+(all?'-all':''), + tmo:tmo>0?Aios.time()-current.world.lag+tmo:0, + prev:last + },last=last.next; + } + } + } + if (waiters!=none) { + p=waiters; + while(p) { + tsi=current.node.ts.db[p.pat.length]; + addwaiter(tsi,p); + p=p.next; + } + log(tsi,current.process,' +waiter'); + current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts'); + } + }, + + // The collect primitive moves tuples from this source TS that match template + // pattern into destination TS specified by path 'to' (a node destination). + collect: function (to,pat) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0) return none; + tsi=current.node.ts.db[nary]; + res = remove(pat,true); + if (res.length>0) { + current.node.stats.tsin += res.length; + Aios.Sig.agent.sendto(to,'TS.SIG',res); + } + return res.length; + }, + // Copy all matching tuples form this source TS to a remote destination TS + // specified by path 'to' (a node destination). + copyto: function (to,pat) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0) return 0; + tsi=current.node.ts.db[nary]; + res = lookup(pat,true); + if (res.length>0) { + Aios.Sig.agent.sendto(to,'TS.SIG',res); + } + return res.length; + }, + + // Access a tuple evaluator - non-blocking: no listener -> callback(null) + // TODO blocking/tmo + evaluate: function (pat,callback,tmo) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0) return none; + tsi=current.node.ts.db[nary]; + consumed=checkwaiter(tsi,pat,callback); + if (!consumed && callback) callback.call(current.process.agent,null); + }, + + // Test tuple existence + exists: function (pat) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0) return none; + tsi=current.node.ts.db[nary]; + res = lookup(pat); + return res!=none; + }, + + // consuming - tmo <> 0 => try_in + inp: function (pat,callback,all,tmo) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; + tsi=current.node.ts.db[nary]; + res = remove(pat,all); + log(tsi,current.process,' in? '+res+' []='+count(tsi)); + if (res && res.length) current.node.stats.tsin += (all?res.length:1); + if (res==none && callback && (tmo==undefined||tmo>0)) { + addwaiter(tsi,{pat:pat, + pro:current.process, + cb:callback, + op:'in'+(all?'-all':''), + tmo:tmo>0?Aios.time()-current.world.lag+tmo:0 + }); + log(tsi,current.process,' +waiter'); + current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts'); + return none; + } else if (callback) callback.call(current.process.agent,res); else return res; + }, + + // Provide a tuple evaluator + listen: function (pat,callback) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; + tsi=current.node.ts.db[nary]; + addwaiter(tsi,{pat:pat, + pro:current.process, + cb:callback, + op:'listen', + tmo:0 + }); + }, + + // Store time-limited tuples + mark: function (tuple,tmo) { + var p,tsi,nary=tuple.length,consumed=false; + if (nary>current.node.ts.n || nary==0 || typeof tuple != 'object') throw 'TS'; + tsi=current.node.ts.db[nary]; + current.node.stats.tsout++; + // Check waiters + consumed=checkwaiter(tsi,tuple); + if (!consumed) { + if (tsi.free==none) { + loop: for (var i in tsi.data) { + if (tsi.data[i]==_) {tsi.free=i; break loop} + } + } + if (tsi.free!=none) { + tsi.data[tsi.free]=tuple; + tsi.tmo[tsi.free]=Aios.time()-current.world.lag+tmo; + current.node.ts.timeout=min0(current.node.ts.timeout,tsi.tmo[tsi.free]); + if (Comp.obj.isString(tuple[0])) + tsi.hash[tuple[0]]=tsi.free; + tsi.free=none; + } else { + tsi.data.push(tuple); + tsi.tmo.push(Aios.time()-current.world.lag+tmo); + // hash is only a first guess to find a tuple + if (Comp.obj.isString(tuple[0])) + tsi.hash[tuple[0]]=tsi.data.length-1; + } + } else current.node.stats.tsin++; + }, + // Store a tuple in this TS + out: function (tuple) { + var tsi,nary=tuple.length,consumed=false,res; + if (nary>current.node.ts.n || nary==0 || typeof tuple != 'object') throw 'TS'; + tsi=current.node.ts.db[nary]; + current.node.stats.tsout++; + // Check waiters + consumed=checkwaiter(tsi,tuple); + if (!consumed) { + if (tsi.free==none) { + loop: for (var i in tsi.data) { + if (tsi.data[i]==_) {tsi.free=i; break loop} + } + } + if (tsi.free!=none) { + tsi.data[tsi.free]=tuple; + tsi.tmo[tsi.free]=0; + if (Comp.obj.isString(tuple[0])) + tsi.hash[tuple[0]]=tsi.free; + tsi.free=none; + } + else { + tsi.data.push(tuple); + tsi.tmo.push(0); + // hash is only a first guess to find a tuple + if (Comp.obj.isString(tuple[0])) + tsi.hash[tuple[0]]=tsi.data.length-1; + } + } else current.node.stats.tsin++; + log(tsi,current.process,' out '+tuple+' ['+nary+'] consumed='+consumed+' []='+count(tsi)); + }, + + // not consuming - tmo <> undefined => try_rd [0: immed.] + rd: function (pat,callback,all,tmo) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; + tsi=current.node.ts.db[nary]; + res = lookup(pat,all); + + if (res==none && callback && (tmo==_||tmo>0)) { + addwaiter(tsi,{pat:pat, + pro:current.process, + cb:callback, + op:'rd'+(all?'-all':''), + tmo:tmo>0?Aios.time()-current.world.lag+tmo:0 + }); + current.process.suspend(tmo>0?Aios.time()-current.world.lag+tmo:0,_,'ts'); + return none; + } else if (callback) callback.call(current.process.agent,res); else return res; + }, + + // consuming + rm: function (pat,all) { + var tsi,nary=pat.length,res; + if (nary>current.node.ts.n || nary==0 || typeof pat != 'object') throw 'TS'; + tsi=current.node.ts.db[nary]; + res = remove(pat,all); + if (res && res.length) current.node.stats.tsin += (all?res.length:1); + return (res!=none); + }, + + // Remote tuple storage + store: function (to,tuple) { + Aios.Sig.agent.sendto(to,'TS.SIG',[tuple]); + return 1; + }, + + // Test and Set: Atomic modification of a tuple - non blocking + // typeof @callback: function (tuple) -> tuple + ts: function (pat,callback) { + var tsi,nary=pat.length,res,ret; + if (nary>current.node.ts.n || nary==0 || !Comp.obj.isArray(pat)) throw 'TS'; + tsi=current.node.ts.db[nary]; + res = lookup(pat,false); + log(tsi,current.process,' test? '+res+' []='+count(tsi)); + if (res) current.node.stats.tsin += 1; + if (callback) { + if (current.process) + ret=callback.call(current.process.agent,res); + else + ret=callback(res); + // update the modified tuple + if (ret && ret.length==res.length) Comp.array.copy(ret,res); + res=ret; + } else if (res) { + // restore the originally consumed tuple + ts.out(res); + } + return res; + }, + try : { + alt : function (tmo,pats,callback,all) { + return ts.alt(pats,callback,all,tmo); + }, + evaluate : function (tmo,pat,callback) { + return ts.evaluate(pat,callback,tmo); + }, + inp : function (tmo,pat,callback,all) { + return ts.inp(pat,callback,all,tmo); + }, + rd : function (tmo,pat,callback,all) { + return ts.rd(pat,callback,all,tmo); + } + } +} + +// Synonyms +ts.alt.try = ts.try.alt +ts.evaluate.try = ts.try.evaluate +ts.inp.try = ts.try.inp +ts.rd.try = ts.try.rd + +/******************************************* +** Tuple Space Data Base +*******************************************/ + +var tsd = function (options) { + var self=this; + if (!options) options={}; + this.n=options.maxn||8; + this.id=options.id||'TS'; + this.timeout=0; + this.db=Comp.array.init(this.n+1,function (i) { + var tsi; + if (i==0) return none; + tsi = { + i:i, + hash:[], + // number|none + free:none, + // [*] [] + data:[], + // number [] + tmo:[], + // [pattern,agent,callback,kind] + waiters:[] + }; + tsi.waiters.free=[]; + tsi.waiters.hash={}; // Hash tuple key for consuming waiter + return tsi; + }); + /* + ** Additional external tuple providers implementing a match function. + */ + this.providers=[]; + /* + ** Additional external tuple consumers implementing a match function. + */ + this.consumers=[]; + this.node=options.node; + + // External API w/o blocking and callbacks (i.e., try_ versions with tmo=0) + // Can be called from any context + this.extern = { + inp: function (pat,all) { + var res,tsi,nary=pat.length,_node=current.node; + current.node=self.node||_node; + if (nary>current.node.ts.n || nary==0) return none; + tsi=current.node.ts.db[nary]; + res = remove(pat,all); + if (res && res.length) current.node.stats.tsin += (all?res.length:1); + current.node=_node; + return res; + }, + mark: function (pat,tmo) { + var res,_node=current.node; + current.node=self.node||_node; + res = ts.mark(pat,tmo); + current.node=_node; + return res; + }, + out: function (pat) { + var res,_node=current.node; + current.node=self.node||_node; + res = ts.out(pat) + current.node=_node; + return res; + }, + rd: function (pat,all) { + var res,tsi,nary=pat.length,_node=current.node; + current.node=self.node||_node; + if (nary>current.node.ts.n || nary==0) return none; + tsi=current.node.ts.db[nary]; + res = lookup(pat,all); + if (res && res.length) current.node.stats.tsin += (all?res.length:1); + current.node=_node; + return res; + }, + rm: function (pat,all) { + var res,_node=current.node; + current.node=self.node||_node; + res=ts.rm(pat,all); + current.node=_node; + return res; + }, + ts: function (pat,callback) { + var res,_node=current.node; + current.node=self.node||_node; + res=ts.ts(pat,callback); + current.node=_node; + return res; + }, + } + +} + +var create = function (options) { + return new tsd(options); +} + +tsd.prototype.checkwaiter = function (tuple) { + var tsi,nary=tuple.length; + if (nary>this.n || nary==0) return none; + tsi=current.node.ts.db[nary]; + return checkwaiter(tsi,tuple); +} + +/** Remove an agent process from waiter queues. + * If doCallback is set, a pending operation callback handler is executed here (e.g., on timeout or interruption). + * + */ +tsd.prototype.cleanup = function (process,doCallback) { + var i,j,tsi,p,waiter; + for (i in current.node.ts.db) { + if (i==0) continue; + tsi=current.node.ts.db[i]; + for(j=0;j 'tuple + * type consumer : function ('pat) -> boolean + */ +tsd.prototype.register = function (func,consumer) { + if (consumer) this.consumers.push(func) + else this.providers.push(func); +}; + + +tsd.prototype.print = function (summary) { + var i,tsi,num,str='',sep=''; + if (summary) { + str += '['; + for (i in current.node.ts.db) { + if (i==0) continue; + tsi=current.node.ts.db[i]; + num = count(tsi); + if (num>0) { + str += sep+'TS'+(int(i)+1)+'='+num; + sep=' '; + } + } + str += ']'+NL; + } + else for (i in current.node.ts.db) { + if (i==0) continue; + tsi=current.node.ts.db[i]; + str += '['+Comp.printf.sprintf('%2d',tsi.i)+ + ' free='+(tsi.free?Comp.printf.sprintf('%4d',tsi.free):'none')+ + ' data='+Comp.printf.sprintf('%4d(%4d)',count(tsi),tsi.data.length)+ + ' waiters='+Comp.printf.sprintf('%4d',tsi.waiters.length)+']'+NL; + } + return str; +} + + +/** Tuple Space Garbage Collection and Timeout Service + * + */ +tsd.prototype.service = function (curtime) { + var i,hashed,tsi,nexttime=0; + + // TODO: if (curtime1) main.out(' .. nexttime = '+thr.nexttime+ + ' ('+(thr.nexttime>0?thr.nexttime-thr.curtime:0)+')'); + }; + this.sleep = function () { + var delta; + thr.curtime=Aios.time(); + delta=thr.nexttime>0?thr.nexttime-thr.curtime:1000; + if (main.verbose>2) main.out(' .. sleeping for '+delta+' ms'); + main.scheduler.Delay(delta); + }; + + this.transitions = function () { + var trans; + trans = + [ + [undefined, this.init, function (thr) { + return true + }], + [this.init, this.run, function (thr) { + return true + }], + [this.run, this.run, function (thr) { + return thr.nexttime<0; + }], + [this.run, this.sleep, function (thr) { + return !dying; + }], + [this.run, this.terminate, function (thr) { + return dying + }], + [this.sleep, this.run, function (thr) { + return true; + }] + ]; + return trans; + }; + this.context = main.scheduler.TaskContext('JAM World'+main.id, thr); + + } + +}; + +// Add an agent class constructor (@env can contain resolved constructor function variables). +// typepf constructor = function|string + +world.prototype.addClass = function (name,constructor,env) { + this.classes[name]=[ + Aios.Code.makeSandbox(constructor,0,env), + Aios.Code.makeSandbox(constructor,1,env), + Aios.Code.makeSandbox(constructor,2,env), + Aios.Code.makeSandbox(constructor,3,env) + ]; +} + +/** Add a node to the world. + * + */ +world.prototype.addNode = function (node) { + this.nodes.push(node); + if (node.id) this.hash[node.id]=node; +}; + +/** Connect two nodes in directions dir:node1->node2 and dir':node2->node1 + * with two virtual channel links that are created here. + * + */ +world.prototype.connect = function (dir,node1,node2,options) { + if (!options) options={}; + var chan=Aios.Chan.Virtual(node1,node2,dir,options); + switch (dir) { + case Aios.DIR.NORTH: + node1.connections.north=chan.link1; + node2.connections.south=chan.link2; + break; + case Aios.DIR.SOUTH: + node1.connections.south=chan.link1; + node2.connections.north=chan.link2; + break; + case Aios.DIR.WEST: + node1.connections.west=chan.link1; + node2.connections.east=chan.link2; + break; + case Aios.DIR.EAST: + node1.connections.east=chan.link1; + node2.connections.west=chan.link2; + break; + case Aios.DIR.NE: + node1.connections.ne=chan.link1; + node2.connections.sw=chan.link2; + break; + case Aios.DIR.NW: + node1.connections.nw=chan.link1; + node2.connections.se=chan.link2; + break; + case Aios.DIR.SE: + node1.connections.se=chan.link1; + node2.connections.nw=chan.link2; + break; + case Aios.DIR.SW: + node1.connections.sw=chan.link1; + node2.connections.ne=chan.link2; + break; + case Aios.DIR.UP: + node1.connections.up=chan.link1; + node2.connections.down=chan.link2; + break; + case Aios.DIR.DOWN: + node1.connections.down=chan.link1; + node2.connections.up=chan.link2; + break; + default: + if (current) current.error='EINVALID'; + throw 'CONNECT'; + } + chan.link2.on('agent',node1.receive.bind(node2)); + chan.link1.on('agent',node2.receive.bind(node1)); + chan.link2.on('signal',node1.handle.bind(node2)); + chan.link1.on('signal',node2.handle.bind(node1)); + chan.link1.end=node2.id; + chan.link2.end=node1.id; + return chan; +}; + +/** Connect node via a port in direction dir:node->*. The endpoint node * will be + * connected if the @snd parameter is specified. Otherwise only an unconnected port is created. + * An endpoint can be later connected using the world.connectTo method (if provided by the interface). + * + * One uni- or bidirectional physical link is created and attached to the given node. + * + * typeof options={ + * compress?:boolean, + * oneway?:boolean, + * proto:'udp'|'tcp'|'http'|'device', + * device?:string, + * rcv:url is node endpoint, + * snd?:url is remote endpoint + * } + * with type url = ":" | ":" | "" + * and ipport = (1-65535) | "*" + */ +world.prototype.connectPhy = function (dir,node,options) { + var self=this,chan,name=Aios.DIR.to(dir); + if (!options) options={}; + chan=Aios.Chan.Physical(node,dir,options); + switch (dir.tag||dir) { + case 'DIR.IP': + // Update routing table of router! + if (!node.connections.ip) node.connections.ip=new Aios.Chan.iprouter(); + node.connections.ip.addLink(chan.link); + chan.router=node.connections.ip; + break; + default: + if (!name) { + if (current) current.error='ENOCHANNEL'; + throw 'CONNECT'; + } + node.connections[name]=chan.link; + } + chan.link.on('agent',node.receive.bind(node)); + chan.link.on('signal',node.handle.bind(node)); + chan.link.on('class',function (obj){ for(var p in obj) self.addClass(p,obj[p].fun,obj[p].env)}); + return chan; +}; + +/** Connect a physical link of node @node to a remote endpoint (if curerently not connected) specified by the @dir parameter. + * typeof @dir = {tag,ip?,device?} with tag='DIR.IP'|'DIR.NORTH',.. + * + */ +world.prototype.connectTo = function (dir,node,options) { + var chan,tokens,to=dir.ip,name=Aios.DIR.to(dir); + if (!node) node=current.node; + chan=node.connections[name]; + if (chan && (chan.status(to) || !chan.connect)) chan=undefined; + if (chan) chan.connect(to,options); +} + +/** Check connectivity to a specific node or a set of nodes + * + */ +world.prototype.connected = function (dir,node) { + var name=Aios.DIR.to(dir),list; + chan=node.connections[name]; + switch (dir.tag||dir) { + case Aios.DIR.tag.PATH: + return chan && chan.status(dir.path); + break; + case Aios.DIR.tag.IP: + // DIR.IP('*') returns all linked IP routes + // DIR.IP('%') returns all linked nodes (names) + return chan && chan.status(dir.ip); + break; + case Aios.DIR.tag.NODE: + // DIR.NODE('*') returns all linked nodes on all connections! + if (dir.node=='*') { + // Check all conenctions for remote node information + list=[]; + if (node.connections.ip) list=list.concat(node.connections.ip.status('%')); + return list; + } else if (typeof dir.node == 'string') { + // Return link (IP) + if (node.connections.ip && node.connections.ip.lookup) { + found=node.connections.ip.lookup(dir.node); + return found?Aios.DIR.IP(found):none; + } + } + break; + case Aios.DIR.tag.DELTA: + // a rough guess (no nw/sw/se/ne) + if (dir.delta[0]==1) chan=node.connections.east; + else if (dir.delta[0]==-1) chan=node.connections.west; + else if (dir.delta[1]==1) chan=node.connections.north; + else if (dir.delta[1]==-1) chan=node.connections.south; + else if (dir.delta[2]==1) chan=node.connections.up; + else if (dir.delta[2]==-1) chan=node.connections.down; + return chan && chan.status(); + break; + default: + return (chan && chan.status())||false; + } +} + +/** Disconnect a physical link of node @node to a remote endpoint (if curerently connected) specified by the @dir parameter. + * + */ +world.prototype.disconnect = function (dir,node) { + var chan; + switch (dir.tag||dir) { + case 'DIR.IP': + if (node.connections.ip && + node.connections.ip.status(dir.ip) && + node.connections.ip.disconnect) + node.connections.ip.disconnect(dir.ip); + break; + } +} + + +/** Find an agent in the world by it's id and class, or agents matching a regular expression. + * + */ +world.prototype.getAgent = function (id,ac) { + var res=Comp.obj.isRegex(id)?[]:undefined; + for(var n in this.nodes) { + var table=this.nodes[n].processes.table; + for(var p in table) { + if (!table[p]) continue; + if (!res && table[p].agent,id==id && table[p].agent,ac=ac) return table[p].agent; + if (res && id.test(table[p].agent.id)) res.push(table[p].agent); + } + } + return res; +}; + + + +/** Find a node in the world by it's id or nodes matching a regular expression. + * + */ +world.prototype.getNode = function (nodeid) { + if (Comp.obj.isRegex(nodeid)) { + var res=[]; + for(var n in this.nodes) { + if (nodeid.test(this.nodes[n].id)) res.push(this.nodes[n]); + } + return res; + } else return this.hash[nodeid]; +}; + +world.prototype.info = function () { + var obj={}; + obj.agents=0; + obj.transferred=0; + for(var n in this.nodes) { + obj.agents += this.nodes[n].processes.used; + for (var l in this.nodes[n].connections) { + if (this.nodes[n].connections[l]) + obj.transferred += this.nodes[n].connections[l].count(); + } + } + return obj; +} + + +world.prototype.init = function () { +} + +/** Lookup nodes (using patterns and providing broker support) + * + */ +world.prototype.lookup = function (dir,callback,node) { + switch (dir.tag||dir) { + case Aios.DIR.tag.PATH: + if (node.connections.ip && node.connections.ip.lookup) return node.connections.ip.lookup(dir.path,callback); + break; + default: + if (callback) callback(); + } +} + +world.prototype.print = function (summary) { + var str='**** WORLD '+this.id+' ****'+NL; + var res = Io.mem(); + str += 'DATA='+int(res.data/1024)+' MB HEAP='+int(res.heap/1024)+' MB'+NL; + for(var n in this.nodes) { + str += this.nodes[n].print(summary); + } + return str; +} + +/** Disconnect and remove a node from the world. + * The node must be destroyed explicitly. + * + */ +world.prototype.removeNode = function (nodeid) { + var c,c2,conn,thenode,chan,node2; + this.nodes=Comp.array.filter(this.nodes,function (node) { + if (node.id==nodeid) thenode=node; + return node.id!=nodeid; + }); + this.hash[nodeid]=undefined; + if (thenode) for(c in thenode.connections) { + conn=thenode.connections[c]; + if (conn && conn.end) { + node2=this.getNode(conn.end); + if (node2) for (c2 in node2.connections) { + // Unlink? + if (node2.connections[c2] && node2.connections[c2].end==nodeid) + node2.connections[c2]=undefined; + } + } + } +}; + +world.prototype.start = function () { + var self=this; + if (this.scheduler) { + proc = new this.thread(0); + this.context=proc.context; + this.scheduler.Add(proc.context); + } + this.gc = setInterval(function () { + var node,n,p; + if (self.verbose>2) self.out('GC'); + for(n in self.nodes) { + node=self.nodes[n]; + for(p in node.processes.gone) { + if (node.processes.gone[p]) { + node.processes.gone[p].tmo -= 500; + if (node.processes.gone[p].tmo<=0) node.processes.gone[p]=undefined; + } + } + } + },500); +} + +world.prototype.stop = function () { +} + +var World = function (nodes,options) { + var obj=new world(nodes,options); + current.world=obj; + return obj; +} + +module.exports = { + options:options, + World:World, + current:function (module) { current=module.current; Aios=module} +} +}; +BundleModuleCode['jam/chan']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: 09-02-16 by sbosse. + ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ + ** $VERSION: 1.13.2 + ** + ** $INFO: + ** + ** JavaScript AIOS Agent Node Communication Module offering P2P communication with another nodes + ** + ** 1. Virtual Link: Connecting virtual (logical) nodes using buffers + ** + ** 2. Physical Link: Connecting physical nodes (on the same physical host or remote hosts) + ** using AMP protocol and IP communication (including endpoint pairing across NAT routers + ** using a rendezvous broker service) + ** + ** 3. Physical Link: Connecting node processes (in a cluster on the same physical host) using process streams + ** + ** For IP-based communication ports an internal IP router is provided offering operation + ** of multiple ports and connections. + ** + ** Communciation link object provided by 1.-3.: + ** + ** type link = { + ** on: method (@event,@handler) with @event={'agent'|'signal'|'class'}, + ** send: method (@msg) with @msg:{agent:string|object,to:dir}|{signal:string|object},to:dir}, + ** status: method (dir) -> boolean, + ** count: method () -> number is returning number of received (phy only) and sent bytes, + ** connect?:method (@to), + ** disconnect?:method (@to), + ** start?:method, + ** stop?:method + ** } + ** + ** + ** Events, emitter: link+ link- error(err="link"|string,arg?) + ** + ** + ** TODO: + ** - Phy capability protected communication and operations + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Lz = Require('os/lz-string'); +var Comp = Require('com/compat'); +var Buf = Require('dos/buf'); +var Net = Require('dos/network'); +var Command = Net.Command; +var Status = Net.Status; +var current=none; +var Aios=none; +var CBL = Require('com/cbl'); +var Amp = Require('jam/amp'); + +var options = { + verbose:1, + version:'1.13.2' +} +module.exports.options=options; + +var SLINK = { + INIT:'INIT', + INITED:'INITED', + RUNNING:'RUNNING' +} + +/******************** + * Virtual Circuit + ******************** + */ + +var virtual= function (node1,node2,dir,options) { + var self=this; + this.node1=node1; + this.node2=node2; + this.dir=dir; // node1 -> node2 + this.buffer1=[]; + this.buffer2=[]; + this.count1={rcv:0,snd:0}; + this.count2={rcv:0,snd:0}; + this.compress=options.compress; + + /* NEWCOMM */ + this.handler1=[]; + this.handler2=[]; + + // External API + this.link1 = { + on: function (event,callback) { + var data; + self.handler1[event]=callback; + if (event=='agent' && self.buffer2.length>0) { + // Agent receiver + data=Comp.array.pop(self.buffer2); + if (self.compress) data=Lz.decompress(data); + callback(data); + } + }, + + send: function (msg) { + var data; + if (msg.agent) { + // Agent migration + data=msg.agent; + if (self.compress) data=Lz.compress(data); + if (self.handler2.agent) self.handler2.agent(self.compress?Lz.decompress(data):data); + else self.buffer1.push(data); + if (data.length) self.count1.snd += data.length; else self.count1.snd++; + } else if (msg.signal) { + // Signal propagation - signals are not queued + data=msg.signal; + if (data.length) self.count1.snd += data.length; else self.count1.snd++; + if (self.handler2.signal) self.handler2.signal(data); + } + }, + count: function () {return self.count1.snd}, + status: function () {return true}, // Linked? + virtual:true + + } + + this.link2 = { + on: function (event,callback) { + var data; + self.handler2[event]=callback; + if (event=='agent' && self.buffer1.length>0) { + // Agent receiver + data=Comp.array.pop(self.buffer1); + if (self.compress) data=Lz.decompress(data); + callback(data); + } + }, + + send: function (msg) { + var data; + if (msg.agent) { + // Agent migration + data=msg.agent; + if (self.compress) data=Lz.compress(data); + if (self.handler1.agent) self.handler1.agent(self.compress?Lz.decompress(data):data); + else self.buffer2.push(data); + if (data.length) self.count2.snd += data.length; else self.count2.snd++; + } else if (msg.signal) { + // Signal propagation - signals are not queued + data=msg.signal; + if (data.length) self.count2.snd += data.length; else self.count2.snd++; + if (self.handler1.signal) self.handler1.signal(data); + } + }, + count: function () {return self.count2.snd}, + status: function () {return true}, // Linked? + virtual:true + + } +}; + +virtual.prototype.init = function () {}; +virtual.prototype.start = function () {}; +virtual.prototype.stop = function () {}; + +var Virtual = function (node1,node2,dir,options) { + var obj=new virtual(node1,node2,dir,options); + return obj; +} + +module.exports.Virtual=Virtual; +module.exports.current=function (module) { current=module.current; Aios=module; Amp.current(module); }; + + + + +if (global.config.nonetwork) return; +/******************************* PHYSICAL *************************************/ + + +/********************* + ** Physical Circuit + ********************* + * + * Using UDP-AMP or process stream connections (TODO) + * typeof options={ + * broker?:url is UDP hole punching rendezvous broker + * compress?:boolean, + * device?:string, + * name?:string is optional name of the comm. port e.g. the JAM node name, + * on?: { } is event handler object, + * oneway?:boolean, + * out?:function, + * proto?:'udp'|'tcp'|'http'|'hardware', + * rcv:url is this endpoint address, + * snd?:url is remote endpoint address, + * stream?:boolean, + * verbose? + * } + * with type url = ":" | ":" | "" + * and type ipport = (1-65535) | "*" + */ +var physical= function (node,dir,options) { + var self=this; + options=checkOptions(options,{}); + this.options=options; + + this.ip=none; + if (options.rcv) this.ip=url2addr(options.rcv); + else this.ip={address:'localhost',port:undefined}; + + this.node=node; + this.dir=dir; // outgoing port (node -> dst), e.g., IP + this.count=0; + this.broker=options.broker; + + this.mode=this.options.compress?Amp.AMMode.AMO_COMPRESS:0; + + this.state = SLINK.INIT; + + this.events = []; + + this.out=Aios.print; + + this.amp= Amp.Amp({ + broker:options.broker?url2addr(options.broker,this.ip.address):undefined, + dir:this.dir, + mode:this.options.mode, + name:this.options.name, + node:node, + oneway:this.options.oneway, + multicast:this.options.multicast, + proto:this.options.proto, + rcv:this.ip, + snd:options.snd?url2addr(options.snd,'127.0.0.1'):undefined, + sock:options.sock, + verbose:options.verbose, + }); + + // External API + this.link = { + on: function (event,callback) { + self.events[event]=callback; + }, + send: function (msg,to) { + var buf,data,addr=to?url2addr(to,'127.0.0.1'):{}; + if (msg.agent) { + data=msg.agent; // string of JSON+ + buf=Buf.Buffer(); + if (self.mode & Amp.AMMode.AMO_COMPRESS) data=Lz.compress(data); + Buf.buf_put_string(buf,data); + // function request(cmd:integer,msg:Buffer,snd?:address) + self.amp.request(Command.PS_MIGRATE, + buf, + self.amp.mode & Amp.AMMode.AMO_MULTICAST? addr:undefined); + } else if (msg.signal) { + data=msg.signal; // string of JSON + // Signal propagation + buf=Buf.Buffer(); + if (self.mode & Amp.AMMode.AMO_COMPRESS) data=Lz.compress(data); + Buf.buf_put_string(buf,data); + // function request(cmd:integer,msg:Buffer,snd?:address) + self.amp.request(Command.PS_SIGNAL, + buf, + self.amp.mode & Amp.AMMode.AMO_MULTICAST? addr:undefined); + } + }, + count: function () {return self.amp.count.rcv+self.amp.count.snd}, + status : function (to) { + if (self.amp) { + if (to) to=url2addr(to); + return to?self.amp.status(to.address,to.port):self.amp.status(); + } + }, // Linked? + ip:this.ip, + mode:this.amp.mode + } + + /** Connect to remote endpoint with optional capability key protection + * typeof @to = "" | ":" | "" + * typeof @key = string "()[]" + */ + this.link.connect=function (to,key) { + var addr=url2addr(to,self.ip.address); + self.amp.link(addr,true,key); + }; + // Disconnect remote endpoint + this.link.disconnect=function (to) { + var tokens; + if (!to){ + if (self.amp.snd && self.amp.snd.address && self.amp.snd.port) + self.amp.unlink(self.amp.snd); + } else { + var addr=url2addr(to,self.ip.address); + self.amp.unlink(addr); + } + }; + this.link.init=function (cb) { + if (self.state!=SLINK.INIT) return cb?cb():null; + self.state=SLINK.INITED; + return self.amp.init(cb); + } + this.link.start=function (cb) { + if (self.state!=SLINK.INITED) return cb?cb():null; + self.state=SLINK.RUNNING; + return self.amp.start(cb); + } + this.link.stop=function (cb) { + if (self.state!=SLINK.RUNNING) return cb?cb():null; + self.state=SLINK.INITED; + return self.amp.stop(cb); + } + + if (this.broker) this.link.lookup = function (path,callback) { + if (self.amp.lookup) self.amp.lookup(path,callback); + else if (callback) callback([]); + } + // Install route notification propagation to router (if installed) + this.amp.on('route+',function (arg,arg2) { + if (self.router) self.router.add(arg,self.link,arg2); + self.emit('link+',arg,arg2); + Aios.emit('link+',arg,arg2); + }); + this.amp.on('route-',function (arg) { + if (self.router) self.router.delete(arg,self.link); + self.emit('link-',arg); + Aios.emit('link-',arg); + }); + this.amp.on('error',function (err,arg) { + self.emit('error',err,arg); + }); + if (options.on) { + for(var p in options.on) this.on(p,options.on[p]); + } + // Register message receiver handler + this.amp.receiver(function (handler) { + var code,name,env,agentid,stat,obj; + if (!handler) return; + if (self.options.verbose>2) { self.out('AMP: got request:'+ Io.inspect(handler)) }; + switch (handler.cmd) { + case Command.PS_MIGRATE: + code = Buf.buf_get_string(handler.buf); + // console.log(code); + // console.log(myJam.amp.url(handler.remote)) + if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); + if (self.events.agent) self.events.agent(code,false,handler.remote); + break; + case Command.PS_CREATE: + code = Buf.buf_get_string(handler.buf); + // console.log(code); + // console.log(myJam.amp.url(handler.remote)) + if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); + if (self.events.agent) self.events.agent(code,true); + break; + case Command.PS_WRITE: + name = Buf.buf_get_string(handler.buf); + code = Buf.buf_get_string(handler.buf); + env = Buf.buf_get_string(handler.buf); + // console.log(code); + // console.log(myJam.amp.url(handler.remote)) + if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); + obj={}; + try {eval("env = "+env)} catch (e) {}; + obj[name]={ + fun:code, + env:env + } + if (self.events['class']) self.events['class'](obj); + break; + case Command.PS_SIGNAL: + // TODO + code = Buf.buf_get_string(handler.buf); + // console.log(code); + if (self.mode & Amp.AMMode.AMO_COMPRESS) code=Lz.decompress(code); + if (self.events.signal) self.events.signal(code,handler.remote); + break; + } + + }); +}; + +physical.prototype.emit = function (event,arg,aux1,aux2) { if (this.events[event]) this.events[event](arg,aux1,aux2)}; +physical.prototype.on = function (event,handler) {this.events[event]=handler}; +physical.prototype.init = function (callback) { return this.link.init(callback)}; +physical.prototype.start = function (callback) {return this.link.start(callback)}; +physical.prototype.stop = function () {return this.link.stop()}; + +var Physical = function (node,dir,options) { + var obj=new physical(node,dir,options); + return obj; +} + +module.exports.Physical=Physical; + +/************************* +** IP UTILS +*************************/ + +/* url = ":" | ":" | "" + * and ipport = (1-65535) | "*" + */ +function url2addr(url,defaultIP) { + var addr={address:defaultIP||'localhost',proto:'UDP',port:undefined}, + parts = url.toString().split(':'); + if (parts.length==1) { + if (Comp.string.isNumeric(parts[0])) addr.port=Number(parts[0]); // port number + else if (parts[0].indexOf('-') != -1) addr.port=parts[0]; // port range p0-p1 + else if (parts[0]=='*') addr.port=undefined; // any port + else addr.address=parts[0]; // ip/url + } else return {address:parts[0],port:parts[1]=='*'?undefined:Number(parts[1])||parts[1]}; + return addr; +}; + +function addr2url(addr) { + return addr.address+':'+(addr.port?addr.port:'*') +}; + +function addrequal(addr1,addr2) { + return ipequal(addr1.address,addr2.address) && addr1.port==addr2.port; +} + +function resolve (url,defaultIP) { + var addr=url2addr(url,defaultIP); + if (defaultIP=='127.0.0.1' && addr.address=='localhost') addr.address=defaultIP; + return addr2url(addr) +} + +function ipequal(ip1,ip2) { + if (ip1==undefined || ip2==undefined) return false; + else if ((Comp.string.equal(ip1,'localhost') || Comp.string.equal(ip1,'127.0.0.1')) && + (Comp.string.equal(ip2,'localhost') || Comp.string.equal(ip2,'127.0.0.1'))) return true; + else return ip1==ip2; +} + + +/*********************************************** + * IP Router using AMP/UDP/HTTP links + * Entry point for move and send operations DIR.IP + *********************************************** + */ + +function iprouter() { + this.routingTable={}; + this.nodeTable={}; + this.links=[]; +} +// Add route and link to be used for the route (and optional remote node id) +iprouter.prototype.add = function (to,link,node) { + to=resolve(to,'127.0.0.1'); + if (options.verbose) Aios.print('[IP] iprouter: add route '+addr2url(link.ip)+' -> '+to+(node?'#'+node:'')); + this.routingTable[to]=link; + this.nodeTable[to]=node; +} + +// Add link device +iprouter.prototype.addLink = function (link) { + if (!link.ip) link.ip='*'; + if (options.verbose) Aios.print('[IP] iprouter: add link '+addr2url(link.ip)); + this.links.push(link); +} + +// Connect to a remote endpoint +iprouter.prototype.connect = function (to,key) { + var link,p; + to=resolve(to,'127.0.0.1'); + // Search for an unconnected port!? + for(p in this.links) { + if (this.links[p].status(to)) return; + if (!(this.links[p].mode&Amp.AMMode.AMO_MULTICAST) && this.links[p].status()) continue; + link=this.links[p]; break; + } + if (link && link.connect) { + link.connect(to,key); + } +} + +// +iprouter.prototype.count = function (dest) { + var res=0; + for(var i in this.links) { + res += this.links[i].count(); + } + return res; +} + +// Remove route +iprouter.prototype.delete = function (to) { + to=resolve(to,'127.0.0.1'); + if (this.routingTable[to]) { + if (options.verbose) Aios.print('[IP] iprouter: remove route '+addr2url(this.routingTable[to].ip)+ ' -> ' + to); + delete this.routingTable[to]; + delete this.nodeTable[to]; + } +} + +// Disconnect a remote endpoint +iprouter.prototype.disconnect = function (to) { + // Search for a connected port! + to=resolve(to,'127.0.0.1'); + if (this.routingTable[to] && this.routingTable[to].status(to)) { + this.routingTable[to].disconnect(to); + } +} + +/** Lookup a IP:PORT address pair of a nodeid OR contact a broker to get reachable + * nodeid-IP address pairs + * + */ +iprouter.prototype.lookup = function (nodeid,callback) { + var p,result=[],n=0; + // Broker lookup with a pattern like /domain/* (DIR.PATH) + if (nodeid.indexOf('*')!=-1) { + // TODO + for (p in this.links) { + if (this.links[p].lookup) { + n++; + this.links[p].lookup(nodeid,function (_result) { + if (_result && _result.length) result=result.concat(_result); + n--; + if (n==0) callback(result); + }); + } + } + } else for(p in this.nodeTable) { + if (this.nodeTable[p] == nodeid && this.routingTable[p]) return p; + } +} + + +/** Try to find our local IP address. + * + */ +iprouter.prototype.ip = function () { + for(var i in this.links) { + if (this.links[i].ip) return this.links[i].ip; + } +} + +/** Reverse lookup: Get the nodeid from an IP:PORT address +* typeof @ip = string +*/ +iprouter.prototype.reverse = function (ip) { + return this.nodeTable[ip]; +} + + + +/** Send a message +* +*/ + +iprouter.prototype.send = function (msg) { + msg.to=resolve(msg.to,'127.0.0.1'); + if (this.routingTable[msg.to]) { + this.routingTable[msg.to].send(msg,msg.to); + } else { + + } +} + +/** Start all attached devices +* +*/ +iprouter.prototype.start = function (callback) { + var cbl=CBL(callback||function(){}); + this.links.forEach(function (link) { + cbl.push(function (next) {link.start(next)}); + }); + cbl.start(); +} + +// Check status of link in given direction (or any direction dest==undefined) +// OR return all current registered routes string [] (dest=='*')! +// OR return all current connected nodes string [] (dest=='%')! +// OR return all current registered links (ip) string [] (dest=='$')! +iprouter.prototype.status = function (dest) { + var res,p; + if (dest==undefined) { + // Any registered routes? + for(p in this.routingTable) { if (this.routingTable[p]) return true } + } else if (dest=='*') { + res=[]; + for(p in this.routingTable) { if (this.routingTable[p]) res.push(p) } + return res; + } else if (dest=='%') { + res=[]; + for(p in this.nodeTable) { + if (this.nodeTable[p] && this.routingTable[p]) res.push(this.nodeTable[p]); + } + return res; + } else { + dest=resolve(dest,'127.0.0.1'); + if (this.routingTable[dest]) + return this.routingTable[dest].status(dest); + else + return false; + } + return false; +} + +// Stop all attached devices +iprouter.prototype.stop = function (callback) { + var cbl=CBL(callback||function(){}); + this.links.forEach(function (link) { + cbl.push(function (next) {link.stop(next)}); + }); + cbl.start(); +} + + +module.exports.iprouter=iprouter; + +module.exports.Command=Command +module.exports.Status=Status + +module.exports.url2addr=url2addr; +module.exports.addr2url=addr2url; +}; +BundleModuleCode['os/lz-string']=function (module,exports,global,process){ +// Copyright (c) 2013 Pieroxy +// This work is free. You can redistribute it and/or modify it +// under the terms of the WTFPL, Version 2 +// For more information see LICENSE.txt or http://www.wtfpl.net/ +// +// For more information, the home page: +// http://pieroxy.net/blog/pages/lz-string/testing.html +// +// LZ-based compression algorithm, version 1.4.4 +var LZString = (function() { + +// private property +var f = String.fromCharCode; +var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +var keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; +var baseReverseDic = {}; + +function getBaseValue(alphabet, character) { + if (!baseReverseDic[alphabet]) { + baseReverseDic[alphabet] = {}; + for (var i=0 ; i>> 8; + buf[i*2+1] = current_value % 256; + } + return buf; + }, + + //decompress from uint8array (UCS-2 big endian format) + decompressFromUint8Array:function (compressed) { + if (compressed===null || compressed===undefined){ + return LZString.decompress(compressed); + } else { + var buf=new Array(compressed.length/2); // 2 bytes per character + for (var i=0, TotalLen=buf.length; i> 1; + } + } else { + value = 1; + for (i=0 ; i> 1; + } + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + delete context_dictionaryToCreate[context_w]; + } else { + value = context_dictionary[context_w]; + for (i=0 ; i> 1; + } + + + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + // Add wc to the dictionary. + context_dictionary[context_wc] = context_dictSize++; + context_w = String(context_c); + } + } + + // Output the code for w. + if (context_w !== "") { + if (Object.prototype.hasOwnProperty.call(context_dictionaryToCreate,context_w)) { + if (context_w.charCodeAt(0)<256) { + for (i=0 ; i> 1; + } + } else { + value = 1; + for (i=0 ; i> 1; + } + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + delete context_dictionaryToCreate[context_w]; + } else { + value = context_dictionary[context_w]; + for (i=0 ; i> 1; + } + + + } + context_enlargeIn--; + if (context_enlargeIn == 0) { + context_enlargeIn = Math.pow(2, context_numBits); + context_numBits++; + } + } + + // Mark the end of the stream + value = 2; + for (i=0 ; i> 1; + } + + // Flush the last char + while (true) { + context_data_val = (context_data_val << 1); + if (context_data_position == bitsPerChar-1) { + context_data.push(getCharFromInt(context_data_val)); + break; + } + else context_data_position++; + } + return context_data.join(''); + }, + + decompress: function (compressed) { + if (compressed == null) return ""; + if (compressed == "") return null; + return LZString._decompress(compressed.length, 32768, function(index) { return compressed.charCodeAt(index); }); + }, + + _decompress: function (length, resetValue, getNextValue) { + var dictionary = [], + next, + enlargeIn = 4, + dictSize = 4, + numBits = 3, + entry = "", + result = [], + i, + w, + bits, resb, maxpower, power, + c, + data = {val:getNextValue(0), position:resetValue, index:1}; + + for (i = 0; i < 3; i += 1) { + dictionary[i] = i; + } + + bits = 0; + maxpower = Math.pow(2,2); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + switch (next = bits) { + case 0: + bits = 0; + maxpower = Math.pow(2,8); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + c = f(bits); + break; + case 1: + bits = 0; + maxpower = Math.pow(2,16); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + c = f(bits); + break; + case 2: + return ""; + } + dictionary[3] = c; + w = c; + result.push(c); + while (true) { + if (data.index > length) { + return ""; + } + + bits = 0; + maxpower = Math.pow(2,numBits); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + switch (c = bits) { + case 0: + bits = 0; + maxpower = Math.pow(2,8); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + + dictionary[dictSize++] = f(bits); + c = dictSize-1; + enlargeIn--; + break; + case 1: + bits = 0; + maxpower = Math.pow(2,16); + power=1; + while (power!=maxpower) { + resb = data.val & data.position; + data.position >>= 1; + if (data.position == 0) { + data.position = resetValue; + data.val = getNextValue(data.index++); + } + bits |= (resb>0 ? 1 : 0) * power; + power <<= 1; + } + dictionary[dictSize++] = f(bits); + c = dictSize-1; + enlargeIn--; + break; + case 2: + return result.join(''); + } + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + if (dictionary[c]) { + entry = dictionary[c]; + } else { + if (c === dictSize) { + entry = w + w.charAt(0); + } else { + return null; + } + } + result.push(entry); + + // Add w+entry[0] to the dictionary. + dictionary[dictSize++] = w + entry.charAt(0); + enlargeIn--; + + w = entry; + + if (enlargeIn == 0) { + enlargeIn = Math.pow(2, numBits); + numBits++; + } + + } + } +}; + return LZString; +})(); + +if (typeof define === 'function' && define.amd) { + define(function () { return LZString; }); +} else if( typeof module !== 'undefined' && module != null ) { + module.exports = LZString +} +}; +BundleModuleCode['dos/buf']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 29-4-15 by sbosse. + ** $RCS: $Id$ + ** $VERSION: 1.1.5 + ** + ** $INFO: + ** + ** DOS: Buffer Management + ** + ** $ENDOFINFO + */ +"use strict"; +var log = 0; + +var util = Require('util'); +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var String = Comp.string; +var Array = Comp.array; +var Perv = Comp.pervasives; +var des48 = Require('dos/des48'); +var Rand = Comp.random; +var Net = Require('dos/network'); +var Status = Net.Status; +var Fs = Require('fs'); + + +var SIZEOF_INT16 = 2; +var SIZEOF_INT32 = 4; +var PORT_SIZE = 6; +var PRIV_SIZE = PORT_SIZE+SIZEOF_INT32; +var CAP_SIZE = PORT_SIZE+PRIV_SIZE; + +/** Generic buffer, union with rpcio object + ** Argument: optional, hex-ascci string or number (size), passed to Buffer instantiation + * + * + * @param {number|string|Buffer} [data] + * @constructor + */ +var buffer = function (data) { + var size; + this.pos=0; + if (Comp.isNumber(data)) { + this.data=new Buffer(data); + } else if (Comp.isString(data)) { + this.data=new Buffer(''); + buf_of_hex(this,data) + } else if (Comp.isArray(data)) { + this.data=new Buffer(data); + } else if (typeof data == "object" && data.constructor === Buffer) { + this.data=data; + } else this.data=new Buffer(''); +}; + +/** Extend a buffer to new size (buf.pos+off). + * + * @param buf + * @param off + */ +function buf_extend(buf,off) { + if (buf.data.length<(buf.pos+off)) { + buf.data=Buffer.concat([buf.data,new Buffer(off-(buf.data.length-buf.pos))]); + } +} + +/** Expand buffer to new size. + * + * @param buf + * @param size + */ +function buf_expand(buf,size) { + if (buf.data.lengthsize) { + buf.data=Buffer.slice(buf.data,size); + } +} + +/* + ** BUFFER encoding and decoding of native data types + ** Supported objects: rpcio, buffer. + ** Supported native data types: int16, int32, string, float, port, private, capability, ... + ** ALL buffer data is stored in byte buffers that extends automatically (buf_put_XX). + */ +function buf_put_string (buf,str) { + buf_extend(buf,str.length+1); + for(var i=0;i> 8) & 0xff; + buf.pos=buf.pos+2; +} + +function buf_get_int16 (buf) { + var n=0; + var end=buf.data.length; + if (buf.pos+2 <= end) { + n = buf.data[buf.pos]; + n = n | (buf.data[buf.pos+1] << 8); + buf.pos = buf.pos + 2; + if (n&0x8000) return (n-0x10000); else return (n); + } else throw Status.BUF_OVERFLOW; +} + +function buf_put_int32 (buf,n) { + buf_extend(buf,4); + buf.data[buf.pos]=n & 0xff; + buf.data[buf.pos+1]=(n >> 8) & 0xff; + buf.data[buf.pos+2]=(n >> 16) & 0xff; + buf.data[buf.pos+3]=(n >> 24) & 0xff; + buf.pos=buf.pos+4; +} + +function buf_get_int32 (buf) { + var n=0; + var end=buf.data.length; + if (buf.pos+4 <= end) { + n = buf.data[buf.pos]; + n = n | (buf.data[buf.pos+1] << 8); + n = n | (buf.data[buf.pos+2] << 16); + n = n | (buf.data[buf.pos+3] << 24); + buf.pos = buf.pos + 4; + // TBD: Sign check??? + return (n); + } else throw Status.BUF_OVERFLOW; +} + +function buf_put_port (buf,port) { + buf_extend(buf,Net.PORT_SIZE); + for(var i=0;i> 8) & 0xff; + buf.data[buf.pos+2]=(priv.prv_obj >> 16) & 0xff; + buf.data[buf.pos+3]=priv.prv_rights & 0xff; + buf.pos=buf.pos+4; + buf_put_port(buf,priv.prv_rand); +} + +function buf_get_priv (buf,priv) { + var n; + var end=buf.data.length; + if (buf.pos+(Net.PRIV_SIZE) <= end) { + if (priv == undefined) priv = Net.Private(); + n = buf.data[buf.pos]; + n = n | (buf.data[buf.pos+1] << 8); + n = n | (buf.data[buf.pos+2] << 16); + priv.prv_obj=n; + priv.prv_rights=buf.data[buf.pos+3]; + buf.pos=buf.pos+4; + priv.prv_rand=buf_get_port(buf); + return priv; + } else throw Status.BUF_OVERFLOW; +} + +function buf_put_cap (buf,cap) { + buf_put_port(buf,cap.cap_port); + buf_put_priv(buf,cap.cap_priv); +} + +function buf_get_cap (buf,cap) { + var end=buf.data.length; + if (buf.pos+(Net.CAP_SIZE) <= end) { + if (cap == undefined) cap = Net.Capability(); + cap.cap_port=buf_get_port(buf); + buf_get_priv(buf,cap.cap_priv); + return cap; + } else throw Status.BUF_OVERFLOW; +} + +function buf_put_hdr (buf,hdr) { + buf_put_port(buf,hdr.h_port); + buf_put_priv(buf,hdr.h_priv); + buf_put_int32(buf,hdr.h_command); + buf_put_int32(buf,hdr.h_status); +} + +function buf_get_hdr (buf,hdr) { + if (hdr==undefined) hdr=Net.Header(); + hdr.h_port=buf_get_port(buf); + buf_get_priv(buf,hdr.h_priv); + hdr.h_command=buf_get_int32(buf); + hdr.h_status=buf_get_int32(buf); + return hdr; +} + +/** TODO: buf blit + * + * @param buf + * @param bufsrc + * @param [srcoff] + * @param [len] + */ +function buf_put_buf (buf,bufsrc,srcoff,len) { + if (srcoff==undefined) srcoff=0; + if (len==undefined) len=bufsrc.data.length; + buf_extend(buf,len); + for(var i=0;i= buf.data.length) buf_expand(buf,off+1); + buf.pos=off; +} +/** + * @param {file} fd + * @param {buffer} buf + * @param {number} [off] file offset + * @param {number} [len] + * @returns {number} n + */ +function buf_write (fd,buf,off,len) { + var n; + if (off==undefined) n=Io.write_buf(fd,buf.data,0,buf.data.length); + else { + if (len==undefined) len=buf.data.length; + n=Io.write_buf(fd,buf.data,0,len,off); + } + return n; +} +/** + * @param {file} fd + * @param {buffer} buf + * @param {number} off file offset + * @param {number} len + * @returns {number} n + */ +function buf_read (fd,buf,off,len) { + var n; + buf_expand(buf,len); + n=Io.read_buf(fd,buf.data,0,len,off); + buf.pos=0; + return n; +} + +function buf_print(buf) { + var str='['; + for(var i=0;i0) str=str+','+buf.data[i]; + else str=str+buf.data[i]; + } + return str+']'+buf.pos+':'+buf.data.length; +} + +function buf_set (buf,off,byte) { + if (off >= buf.data.length) buf_expand(buf,off+1); + buf.data[off]=byte; +} + +function buf_get (buf,off) { + return buf.data[off]; +} + +/** Reset buffer + * + * @param buf + */ +function buf_init (buf) { + buf.data=new Buffer(''); + buf.pos=0; +} + +function buf_copy (dst,src) { + dst.data=new Buffer(src.data); + dst.pos=0; +} + +function buf_blit (dst,dstoff,src,srcoff,len) { + buf_expand(dst,dstoff+len); + src.data.copy(dst.data,dstoff,srcoff,srcoff+len); + dst.pos=0; +} + + +/** + * + * @type {{SIZEOF_INT16: number, SIZEOF_INT32: number, PORT_SIZE: number, PRIV_SIZE: number, CAP_SIZE: number, Buffer: Function, buf_put_string: buf_put_string, buf_put_int16: buf_put_int16, buf_put_int32: buf_put_int32, buf_put_port: buf_put_port, buf_put_priv: buf_put_priv, buf_put_cap: buf_put_cap, buf_put_hdr: buf_put_hdr, buf_put_buf: buf_put_buf, buf_put_bytes: buf_put_bytes, buf_get_string: buf_get_string, buf_get_int16: buf_get_int16, buf_get_int32: buf_get_int32, buf_get_port: buf_get_port, buf_get_priv: buf_get_priv, buf_get_cap: buf_get_cap, buf_get_hdr: buf_get_hdr, buf_get_buf: buf_get_buf, buf_get_bytes: buf_get_bytes, buf_pad: buf_pad, buf_set: buf_set, buf_get: buf_get, buf_set_pos: buf_set_pos, buf_init: buf_init, buf_blit: buf_blit, buf_copy: buf_copy, buf_extend: buf_extend, buf_expand: buf_expand, buf_shrink: buf_shrink, buf_read: buf_read, buf_write: buf_write, buf_print: buf_print, buf_to_hex: buf_to_hex, buf_of_hex: buf_of_hex, buf_to_str: buf_to_str, buf_of_str: buf_of_str}} + */ +module.exports = { + SIZEOF_INT16: SIZEOF_INT16, + SIZEOF_INT32: SIZEOF_INT32, + PORT_SIZE: PORT_SIZE, + PRIV_SIZE: PRIV_SIZE, + CAP_SIZE: CAP_SIZE, + /** + * + * @param {number|string|Buffer} [data] + * @returns {buffer} + */ + Buffer: function Buffer(data) { + var obj = new buffer(data); + Object.preventExtensions(obj); + return obj; + }, + // Buffer data operations + buf_put_string:buf_put_string, + buf_put_int16:buf_put_int16, + buf_put_int32:buf_put_int32, + buf_put_port:buf_put_port, + buf_put_priv:buf_put_priv, + buf_put_cap:buf_put_cap, + buf_put_hdr:buf_put_hdr, + buf_put_buf:buf_put_buf, + buf_put_bytes:buf_put_bytes, + buf_get_string:buf_get_string, + buf_get_int16:buf_get_int16, + buf_get_int32:buf_get_int32, + buf_get_port:buf_get_port, + buf_get_priv:buf_get_priv, + buf_get_cap:buf_get_cap, + buf_get_hdr:buf_get_hdr, + buf_get_buf:buf_get_buf, + buf_get_bytes:buf_get_bytes, + buf_pad:buf_pad, + buf_set:buf_set, + buf_get:buf_get, + buf_set_pos:buf_set_pos, + buf_init:buf_init, + buf_blit:buf_blit, + buf_copy:buf_copy, + buf_extend:buf_extend, + buf_expand:buf_expand, + buf_shrink:buf_shrink, + // Buffer IO + buf_read:buf_read, + buf_write:buf_write, + buf_print:buf_print, + // Conversion + buf_to_hex:buf_to_hex, + buf_of_hex:buf_of_hex, + buf_to_str:buf_to_str, + buf_of_str:buf_of_str, + + length: function(buf) { + if (buf.data==undefined) return 0; + else return buf.data.length; + } +}; +}; +BundleModuleCode['dos/network']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR. + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 3-5-15 by sbosse. + ** $VERSION: 1.2.6 + ** + ** $INFO: + ** + ** DOS: Networking, Commands, Status/Error codes, .. + ** + ** $ENDOFINFO + */ +"use strict"; +var log = 0; + +var util = Require('util'); + +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var String = Comp.string; +var Array = Comp.array; +var Perv =Comp.pervasives; +var Des48 = Require('dos/des48'); +var Base64 = Require('os/base64'); +var Rand = Comp.random; +var Fs = Require('fs'); + +//var xmldoc = Require('xmldoc'); + + +function pad(str,size) { + while (str.length < (size || 2)) {str = "0" + str;} + return str; +} + + +/** Direction + * +var Direction = { + NORTH:1, + WEST:2, + EAST:3, + SOUTH:4, + ORIGIN:5, + tostring:function (i) { + switch (i) { + case 1: return 'NORTH'; + case 2: return 'WEST'; + case 3: return 'EAST'; + case 4: return 'SOUTH'; + case 5: return 'ORIGIN'; + default: return 'Direction?'; + } + } +}; +*/ + + +// Standard Object Service +var STD_FIRST_COM = 1000; +var STD_LAST_COM = 1999; +var STD_FIRST_ERR = (-STD_FIRST_COM); +var STD_LAST_ERR = (-STD_LAST_COM); + +// File Server +var AFS_FIRST_COM = 2000; +var AFS_LAST_COM = 2099; +var AFS_FIRST_ERR = (-AFS_FIRST_COM); +var AFS_LAST_ERR = (-AFS_LAST_COM); +var AFS_REQBUFSZ = 1024*32; + + +// Directory and Name Server +var DNS_FIRST_COM = 2100; +var DNS_LAST_COM = 2199; +var DNS_FIRST_ERR = (-DNS_FIRST_COM); +var DNS_LAST_ERR = (-DNS_LAST_COM); +var DNS_MAXCOLUMNS = 4; + +// System Process Server (incl. Agent Platform Manager) +var PS_FIRST_COM = 2200; +var PS_LAST_COM = 2299; +var PS_FIRST_ERR = (-PS_FIRST_COM); +var PS_LAST_ERR = (-PS_LAST_COM); + +// Broker Server +var BR_FIRST_COM = 2300; +var BR_LAST_COM = 2399; +var BR_FIRST_ERR = (-BR_FIRST_COM); +var BR_LAST_ERR = (-BR_LAST_COM); +/** RPC Status + * + * @enum {number} + */ +var Status = { + STD_OK:0, + STD_CAPBAD : STD_FIRST_ERR, + STD_COMBAD : (STD_FIRST_ERR-1), + STD_ARGBAD : (STD_FIRST_ERR-2), + STD_NOTNOW : (STD_FIRST_ERR-3), + STD_NOSPACE : (STD_FIRST_ERR-4), + STD_DENIED : (STD_FIRST_ERR-5), + STD_NOMEM : (STD_FIRST_ERR-6), + STD_EXISTS : (STD_FIRST_ERR-7), + STD_NOTFOUND : (STD_FIRST_ERR-8), + STD_SYSERR : (STD_FIRST_ERR-9), + STD_INTR : (STD_FIRST_ERR-10), + STD_OVERFLOW : (STD_FIRST_ERR-11), + STD_WRITEPROT : (STD_FIRST_ERR-12), + STD_NOMEDIUM : (STD_FIRST_ERR-13), + STD_IOERR : (STD_FIRST_ERR-14), + STD_WRONGSRV : (STD_FIRST_ERR-15), + STD_OBJBAD : (STD_FIRST_ERR-16), + STD_UNKNOWN : (STD_FIRST_ERR-17), + DNS_UNAVAIL : (DNS_FIRST_ERR -1), + DNS_NOTEMPTY : (DNS_FIRST_ERR -2), + DNS_UNREACH : (DNS_FIRST_ERR -3), + DNS_CLASH : (DNS_FIRST_ERR -4), + RPC_FAILURE : -1, + BUF_OVERFLOW : -2, + print: function(stat) { + switch(stat) { + case Status.STD_OK : return 'STD_OK'; + case Status.STD_CAPBAD : return 'STD_CAPBAD'; + case Status.STD_COMBAD : return 'STD_COMBAD'; + case Status.STD_ARGBAD : return 'STD_ARGBAD'; + case Status.STD_NOTNOW : return 'STD_NOTNOW'; + case Status.STD_NOSPACE : return 'STD_NOSPACE'; + case Status.STD_DENIED : return 'STD_DENIED'; + case Status.STD_NOMEM : return 'STD_NOMEM'; + case Status.STD_EXISTS : return 'STD_EXISTS'; + case Status.STD_NOTFOUND : return 'STD_NOTFOUND'; + case Status.STD_SYSERR : return 'STD_SYSERR'; + case Status.STD_INTR : return 'STD_INTR'; + case Status.STD_OVERFLOW : return 'STD_OVERFLOW'; + case Status.STD_WRITEPROT : return 'STD_WRITEPROT'; + case Status.STD_NOMEDIUM : return 'STD_NOMEDIUM'; + case Status.STD_IOERR : return 'STD_IOERR'; + case Status.STD_WRONGSRV : return 'STD_WRONGSRV'; + case Status.STD_OBJBAD : return 'STD_OBJBAD'; + case Status.STD_UNKNOWN : return 'STD_UNKNOWN'; + case Status.DNS_UNAVAIL : return 'DNS_UNAVAIL'; + case Status.DNS_NOTEMPTY : return 'DNS_NOTEMPTY'; + case Status.DNS_UNREACH : return 'DNS_UNREACH'; + case Status.DNS_CLASH : return 'DNS_CLASH'; + case Status.RPC_FAILURE : return 'RPC_FAILURE'; + case Status.BUF_OVERFLOW : return 'BUF_OVERFLOW'; + default : return '"'+stat+'"'; + } + } +}; + +/** RPC Command + * + * @enum {number} + */ + +var Command = { + /* + ** Standard Commands + */ + STD_MONITOR : STD_FIRST_COM, + STD_AGE : (STD_FIRST_COM+1), + STD_COPY : (STD_FIRST_COM + 2), + STD_DESTROY : (STD_FIRST_COM + 3), + STD_INFO : (STD_FIRST_COM + 4), + STD_RESTRICT : (STD_FIRST_COM + 5), + STD_STATUS : (STD_FIRST_COM + 6), + STD_TOUCH : (STD_FIRST_COM + 7), + STD_GETPARAMS : (STD_FIRST_COM + 8), + STD_SETPARAMS : (STD_FIRST_COM + 9), + STD_NTOUCH : (STD_FIRST_COM + 10), + STD_EXIT : (STD_FIRST_COM + 11), + STD_RIGHTS : (STD_FIRST_COM + 12), + STD_EXEC : (STD_FIRST_COM + 13), + STD_LOCATION : (STD_FIRST_COM + 20), + STD_LABEL : (STD_FIRST_COM + 21), + + /* + ** AFC Commands + */ + AFS_CREATE : (AFS_FIRST_COM + 1), + AFS_DELETE : (AFS_FIRST_COM + 2), + AFS_FSCK : (AFS_FIRST_COM + 3), + AFS_INSERT : (AFS_FIRST_COM + 4), + AFS_MODIFY : (AFS_FIRST_COM + 5), + AFS_READ : (AFS_FIRST_COM + 6), + AFS_SIZE : (AFS_FIRST_COM + 7), + AFS_DISK_COMPACT : (AFS_FIRST_COM + 8), + AFS_SYNC : (AFS_FIRST_COM + 9), + AFS_DESTROY : (AFS_FIRST_COM + 10), + + /* + ** DNS Commands + */ + + DNS_CREATE : (DNS_FIRST_COM), + DNS_DISCARD : (DNS_FIRST_COM + 1), + DNS_LIST : (DNS_FIRST_COM + 2), + DNS_APPEND : (DNS_FIRST_COM + 3), + DNS_CHMOD : (DNS_FIRST_COM + 4), + DNS_DELETE : (DNS_FIRST_COM + 5), + DNS_LOOKUP : (DNS_FIRST_COM + 6), + DNS_SETLOOKUP : (DNS_FIRST_COM + 7), + DNS_INSTALL : (DNS_FIRST_COM + 8), + DNS_REPLACE : (DNS_FIRST_COM + 10), + DNS_GETMASKS : (DNS_FIRST_COM + 11), + DNS_GETSEQNR : (DNS_FIRST_COM + 12), + DNS_RENAME : (DNS_FIRST_COM + 13), + DNS_GETROOT : (DNS_FIRST_COM + 14), + DNS_GETDEFAFS : (DNS_FIRST_COM + 15), + + PS_STUN : (PS_FIRST_COM), // Kill a process/ create a snapshot + PS_MIGRATE : (PS_FIRST_COM+1), // Execute a process from a snapshot after migration (->next+) + PS_EXEC : (PS_FIRST_COM+2), // Execute a process from a snapshot (->next) + PS_WRITE : (PS_FIRST_COM+4), // Store a process class template + PS_READ : (PS_FIRST_COM+5), // Get a process class template + PS_CREATE : (PS_FIRST_COM+6), // Create a process from a template and execute + PS_FORK : (PS_FIRST_COM+7), // Fork a process from a running process + PS_SIGNAL : (PS_FIRST_COM+8), // Send a signal to a process + + BR_CONNECT : (BR_FIRST_COM), + BR_DISCONN : (BR_FIRST_COM+1), + + print: function(cmd) { + switch(cmd) { + case Command.STD_MONITOR : return 'STD_MONITOR'; + case Command.STD_AGE : return 'STD_AGE'; + case Command.STD_COPY : return 'STD_COPY'; + case Command.STD_DESTROY : return 'STD_DESTROY'; + case Command.STD_INFO : return 'STD_INFO'; + case Command.STD_RESTRICT : return 'STD_RESTRICT'; + case Command.STD_STATUS : return 'STD_STATUS'; + case Command.STD_TOUCH : return 'STD_TOUCH'; + case Command.STD_GETPARAMS : return 'STD_GETPARAMS'; + case Command.STD_SETPARAMS : return 'STD_SETPARAMS'; + case Command.STD_NTOUCH : return 'STD_NTOUCH'; + case Command.STD_EXIT : return 'STD_EXIT'; + case Command.STD_RIGHTS : return 'STD_RIGHTS'; + case Command.STD_EXEC : return 'STD_EXEC'; + case Command.STD_LOCATION : return 'STD_LOCATION'; + case Command.STD_LABEL : return 'STD_LABEL'; + case Command.AFS_CREATE : return 'AFS_CREATE'; + case Command.AFS_DELETE : return 'AFS_DELETE'; + case Command.AFS_FSCK : return 'AFS_FSCK'; + case Command.AFS_INSERT : return 'AFS_INSERT'; + case Command.AFS_MODIFY : return 'AFS_MODIFY'; + case Command.AFS_READ : return 'AFS_READ'; + case Command.AFS_SIZE : return 'AFS_SIZE'; + case Command.AFS_DISK_COMPACT : return 'AFS_DISK_COMPACT'; + case Command.AFS_SYNC : return 'AFS_SYNC'; + case Command.AFS_DESTROY : return 'AFS_DESTROY'; + case Command.DNS_CREATE : return 'DNS_CREATE'; + case Command.DNS_DISCARD : return 'DNS_DISCARD'; + case Command.DNS_LIST : return 'DNS_LIST'; + case Command.DNS_APPEND : return 'DNS_APPEND'; + case Command.DNS_CHMOD : return 'DNS_CHMOD'; + case Command.DNS_DELETE : return 'DNS_DELETE'; + case Command.DNS_LOOKUP : return 'DNS_LOOKUP'; + case Command.DNS_SETLOOKUP : return 'DNS_SETLOOKUP'; + case Command.DNS_INSTALL : return 'DNS_INSTALL'; + case Command.DNS_REPLACE : return 'DNS_REPLACE'; + case Command.DNS_GETMASKS : return 'DNS_GETMASKS'; + case Command.DNS_GETSEQNR : return 'DNS_GETSEQNR'; + case Command.DNS_RENAME : return 'DNS_RENAME'; + case Command.DNS_GETROOT : return 'DNS_GETRROT'; + case Command.DNS_GETDEFAFS : return 'DNS_GETDEFAFS'; + case Command.PS_STUN : return 'PS_STUN'; + case Command.PS_EXEC : return 'PS_EXEC'; + case Command.PS_MIGRATE : return 'PS_MIGRATE'; + case Command.PS_READ : return 'PS_READ'; + case Command.PS_WRITE : return 'PS_WRITE'; + case Command.PS_CREATE : return 'PS_CREATE'; + case Command.PS_FORK : return 'PS_FORK'; + case Command.PS_SIGNAL : return 'PS_SIGNAL'; + case Command.BR_CONNECT : return 'BR_CONNECT'; + case Command.BR_DISCONN : return 'BR_DISCONN'; + default: return '"'+cmd+'"'; + } + + + } +}; + +var PORT_SIZE = 6; +var PRIV_SIZE = 4+PORT_SIZE; +var CAP_SIZE = 16; + +/** Object Rights + * + * @enum {number} + */ +var Rights = { + AFS_RGT_READ : 0x1, + AFS_RGT_CREATE : 0x2, + AFS_RGT_MODIFY : 0x4, + AFS_RGT_DESTROY : 0x8, + AFS_RGT_ADMIN : 0x80, + + DNS_COLMASK : ((1 << DNS_MAXCOLUMNS) - 1), // Rights to access specific columns of a directory row, one bit, one column. + DNS_RGT_COLALL : ((1 << DNS_MAXCOLUMNS) - 1), + DNS_RGT_COL1 : 0x01, + DNS_RGT_OWNER : 0x01, + DNS_RGT_COL2 : 0x02, + DNS_RGT_GROUP : 0x02, + DNS_RGT_COL3 : 0x04, + DNS_RGT_OTHERS : 0x04, + DNS_RGT_COL4 : 0x08, + DNS_RGT_READ : 0x10, + DNS_RGT_CREATE : 0x20, + DNS_RGT_MODIFY : 0x40, + DNS_RGT_DELETE : 0x80, + + HOST_INFO : 0x01, + HOST_READ : 0x02, + HOST_WRITE : 0x04, + HOST_EXEC : 0x08, + + PSR_READ : 0x01, + PSR_WRITE : 0x02, + PSR_CREATE : 0x04, + PSR_DELETE : 0x08, + PSR_EXEC : 0x10, + PSR_KILL : 0x20, + PSR_ALL : 0xff, + + NEG_SCHED : 0x08, + NEG_CPU : 0x10, + NEG_RES : 0x20, + NEG_LEVEL : 0x40, + + PRV_ALL_RIGHTS : 0xff + +}; + + + +var DEF_RPC_MAX_HOP = 4; + +var priv2pub_cache = []; + +/** + * + * @param {number []} [port_vals] + * @returns {string} + */ +var port = function (port_vals) { + if (port_vals==undefined) port_vals=[0,0,0,0,0,0]; + var port=''; + for(var i = 0; i< PORT_SIZE;i++) { + port=port+Perv.char_of_int(port_vals[i]); + } + return port; + +}; +/** + * + * @param {number} [obj] + * @param {number} [rights] + * @param {port} [rand] + * @constructor + */ +var privat = function (obj,rights,rand) { + if (obj==undefined) { + // Create empty private field + this.prv_obj=0; + this.prv_rights=0; + this.prv_rand=port(); + } else { + this.prv_obj = obj; // Integer + this.prv_rights = rights; // Integer + this.prv_rand = rand; // Port=string + } +}; + +/** + * + * @param {port} [cap_port] + * @param {privat} [cap_priv] + * @constructor + */ +var capability = function(cap_port, cap_priv) { + if (cap_port==undefined) { + // Create empty capability + this.cap_port = port(); + this.cap_priv = new privat(); + } else { + this.cap_port = cap_port; // Port=string + if (cap_priv==undefined) + this.cap_priv = new privat(); + else + this.cap_priv = cap_priv; // Private + } +}; + +/* + ** RPC communication is XML based using the HTTP interface. + ** RPC communication is synchronous, hence a callback + ** function is used to handle the reply (acknowledge). + */ +/** + * + * @param {port} [h_port] + * @param {privat} [h_priv] + * @param {Command} [h_command] + * @param {(Status.STD_OK|*)} [h_status] + * @constructor + */ +var header = function(h_port,h_priv,h_command,h_status) { + if (h_port==undefined) { + // Create empty header + this.h_port = port(); + this.h_priv = new privat(); + this.h_command = undefined; + this.h_status = undefined; + } else { + this.h_port = h_port; + this.h_priv = h_priv; + this.h_command = h_command; + this.h_status = h_status; + } +}; + +/** + * + * @param {number} [obj] + * @param {number} [rights] + * @param {port} [rand] + * @returns {privat} + */ +function Private(obj,rights,rand) { + var _obj = new privat(obj,rights,rand); + Object.preventExtensions(_obj); + return _obj; +} +/** + * + * @param {port} [cap_port] + * @param {privat} [cap_priv] + * @returns {capability} + */ +function Capability (cap_port, cap_priv) { + var obj = new capability(cap_port, cap_priv); + Object.preventExtensions(obj); + return obj; +} +/** + * + * @param {port} [h_port] + * @param {privat} [h_priv] + * @param {Command} [h_command] + * @param {(Status.STD_OK|*)} [h_status] + * @returns {header} + */ + +function Header(h_port,h_priv,h_command,h_status) { + var obj = new header(h_port,h_priv,h_command,h_status); + Object.preventExtensions(obj); + return obj; +} + +/* +** Hash table of all locally created unique ports. + */ +var uniqports=[]; + + +/** + * + */ +var Net = { + // Direction:Direction, + PORT_SIZE:PORT_SIZE, + PRIV_SIZE:PRIV_SIZE, + + AFS_REQBUFSZ:AFS_REQBUFSZ, + CAP_SIZE:CAP_SIZE, + DNS_MAXCOLUMNS:DNS_MAXCOLUMNS, + TIMEOUT:5000, + DEF_RPC_MAX_HOP:DEF_RPC_MAX_HOP, + + Status:Status, + Command:Command, + Rights:Rights, + + Private:Private, + Capability: Capability, + Header: Header, + Port: port, + + /** + * @type {port} + */ + nilport: port(), + nilpriv: Private(0,0,this.nilport), + nilcap: Capability(this.nilport,this.nilpriv), + + /* + ** Utils to get and set single bytes of a port + */ + get_portbyte: function(port,i) { + return Perv.int_of_char(String.get(port,i)) + }, + set_portbyte: function(port,i,byte) { + return String.set(port, i, (Perv.char_of_int(byte))); + }, + /* + * Return a unique key of a capability that can be used for hash tables + */ + key: function (cap) { + return cap.cap_port+ + cap.cap_priv.prv_obj+ + cap.cap_priv.prv_rights+ + cap.cap_priv.prv_rand; + }, + + /* + ** Encryption function + */ + one_way: function (port) { + var key = Array.create(64,0); + var block = Array.create(48,0); + var pubport = String.make (PORT_SIZE,'\0'); + var i, j, k; + + /* + ** We actually need 64 bit key. + ** Throw some zeroes in at bits 6 and 7 mod 8 + ** The bits at 7 mod 8 etc are not used by the algorithm + */ + j=0; + for (i = 0; i< 64; i++) { + if ((i & 7) > 5) + key[i] = 0; + else { + if ((this.get_portbyte(port, (j >> 3)) & (1 << (j & 7))) != 0) + key[i] = 1; + else + key[i] = 0; + j++; + } + } + + Des48.des_OWsetkey(key); + /* + ** Now go encrypt constant 0 + */ + block=Des48.des_OWcrypt48(block); + + + /* + ** and put the bits in the destination port + */ + var pb = 0; + + for (i = 0; i < PORT_SIZE;i++) { + var pbyte = 0; + for (j = 0; j < 8; j++) { + pbyte = pbyte | (block[pb] << j); + pb++; + } + pubport=this.set_portbyte(pubport, i, pbyte); + } + + return pubport; + }, + /* + ** Check whether the required rights [R1;R2;..] are + ** present in the rights field rg. Return a boolean value. + */ + rights_req : function(rights,required) { + var all=true; + Array.iter(required,function(rq) { + if (rq & rights == 0) all = false; + }); + return all; + }, + port_cmp: function(port1,port2) { + var i; + var eq=true; + for(i=0;i 0) str = str + ':'; + str = str + pad(num.toString(16).toUpperCase(), 2); + } + } else str='undefined'; + return str; + }, + port_of_str: function (str,compact) { + var tokens=str.split(':'),i,port=''; + for (i=0;i 0) str = str + ':'; + str = str + pad(num.toString(16).toUpperCase(), 2); + } + } else str='undefined'; + return str; + }, + /** + * + * @param priv + * @returns {string} + */ + private: function(priv) { + var str=''; + if (priv==undefined) return 'undefined'; + str=priv.prv_obj; + str=str+'('+String.format_hex(priv.prv_rights,2).toUpperCase()+')['; + str=str+this.port(priv.prv_rand)+']'; + return str; + }, + status: Status.print + } +}; + +module.exports = Net; +}; +BundleModuleCode['com/cbl']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2017 bLAB + ** $CREATED: 27-11-17 by sbosse. + ** $VERSION: 1.1.1 + ** + ** $INFO: + ** + ** JavaScript Callback List + ** + ** Assume there is a set of non-blocking IO operations with callbacks io1,io2,.., and there is a final + ** callback function that should be called after all io operations have finished. + ** + ** $ENDOFINFO + */ + +function CBL(callback) { + if (!(this instanceof CBL)) return new CBL(callback); + this.schedules=[]; + this.callback=callback; +} + +// Next schedule +CBL.prototype.next = function (status) { + var f=this.schedules.shift(); + // if (f) console.log('next '+f.toString()) + if (f) { + f(this.next.bind(this),status); + } else if (this.callback) this.callback(status); +} + +// Add next IO callback at the end of the list +CBL.prototype.push = function (f) { + this.schedules.push(f); +} + +// Execute CBL +CBL.prototype.start = function () { + this.next(); +} + +// Add next IO callback at the top of the list +CBL.prototype.top = function (f) { + this.schedules.unshift(f); +} + +module.exports=CBL; +}; +BundleModuleCode['jam/amp']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 09-02-16 by sbosse. + ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ + ** $VERSION: 1.11.1 + ** + ** $INFO: + ** + ** JAM Agent Management Port (AMP) over UDP/HTTP/devices/streams + ** + ** + ** New: Fully negotiated IP Multicast Ports (P2N) + ** + ** $ENDOFINFO + */ + +var Io = Require('com/io'); +var Lz = Require('os/lz-string'); +var Comp = Require('com/compat'); +var Buf = Require('dos/buf'); +var Net = Require('dos/network'); +var Command = Net.Command; +var Status = Net.Status; +var current=none; +var Aios=none; +var CBL = Require('com/cbl'); + +var COM = Require('jam/ampCOM'), + AMMode=COM.AMMode, + AMMessageType=COM.AMMessageType, + AMState=COM.AMState, + amp=COM.amp, + options=COM.options, + url2addr=COM.url2addr, + addr2url=COM.addr2url, + addrequal=COM.addrequal, + resolve=COM.resolve, + ipequal=COM.ipequal, + getNetworkIP=COM.getNetworkIP, + doUntilAck=COM.doUntilAck; + +options.localhost='localhost'; +options.version='1.10.1', +options.AMC_MAXLIVE=5, +options.TIMER=500, +options.TRIES=10; +options.REGTMO=1000; + +/*********************** +** AMP +************************/ + +var ampMAN = Require('jam/ampMAN'); + +if (global.TARGET!= 'browser') { + /******************************* AMP *************************************/ + + var ampUDP = Require('jam/ampUDP'); + var ampTCP = Require('jam/ampTCP'); + var ampStream = Require('jam/ampStream'); + +} + +var ampHTTP = Require('jam/ampHTTP'); + + +/** Main AMP constructor + * ==================== + * + */ +var Amp = function (options) { + var obj; + switch (options.proto) { + case 'stream': + obj=new amp.stream(options); + break; + case 'http': + obj=new amp.http(options); + break; + case 'tcp': + obj=new amp.tcp(options); + break; + case 'udp': + default: + obj=new amp.udp(options); + } + return obj; +} + +module.exports.current=function (module) { + current=module.current; Aios=module; + if (ampMAN) ampMAN.current(module); + if (ampUDP) ampUDP.current(module); + if (ampHTTP) ampHTTP.current(module); + if (ampTCP) ampTCP.current(module); + if (ampStream) ampStream.current(module); +}; + +module.exports.Amp=Amp; +module.exports.AMMessageType=AMMessageType; +module.exports.AMState=AMState; +module.exports.AMMode=AMMode; +module.exports.url2addr=url2addr; +module.exports.addr2url=addr2url; +module.exports.Command=Command +module.exports.Status=Status +module.exports.options=options; +}; +BundleModuleCode['jam/ampCOM']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 09-02-16 by sbosse. + ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ + ** $VERSION: 1.13.1 + ** + ** $INFO: + ** + ** JAM Agent Management Port (AMP) - Common Types and Utils + ** + ** + ** + ** $ENDOFINFO + */ + +var options = { + peekIP: '134.102.22.124', // used by getnetworkip, must be an HTTP server +} + +var Comp = Require('com/compat'); +// Channel mode flags +var AMMode = { + AMO_UNICAST: 1, // P2P + AMO_MULTICAST: 2, // P2N + AMO_STATIC: 4, // + AMO_BUFFER: 8, // Transfer buffer data + AMO_OBJECT: 16, // Transfer objects instead of buffer data + AMO_COMPRESS: 32, // Compress data + AMO_SERVER: 64, // This is HTTP server mode + AMO_CLIENT: 128, // This is HTTP client mode + AMO_ONEWAY:256, // Other side can be reache dw/o link negotiation + print: function (m) { + var s='',sep=''; + if (m & AMMode.AMO_UNICAST) s += (sep+'UNI'),sep='|'; + if (m & AMMode.AMO_MULTICAST) s += (sep+'MUL'),sep='|'; + if (m & AMMode.AMO_STATIC) s += (sep+'STA'),sep='|'; + if (m & AMMode.AMO_BUFFER) s += (sep+'BUF'),sep='|'; + if (m & AMMode.AMO_OBJECT) s += (sep+'OBJ'),sep='|'; + if (m & AMMode.AMO_COMPRESS) s += (sep+'ZIP'),sep='|'; + if (m & AMMode.AMO_CLIENT) s += (sep+'CLI'),sep='|'; + if (m & AMMode.AMO_SERVER) s += (sep+'SRV'),sep='|'; + if (m & AMMode.AMO_ONEWAY) s += (sep+'ONE'),sep='|'; + return s; + } +} + +// Message type +var AMMessageType = { + AMMACK:0, + AMMPING:1, + AMMPONG:2, + AMMLINK:3, + AMMUNLINK:4, + AMMRPCHEAD:6, // Header followed by multiple data requests + AMMRPCDATA:7, + // Broker Rendezvous support + AMMCONTROL:8, + AMMRPC:9, // Header + data in one message + AMMCOLLECT:10, // Collect messages + AMMRPCHEADDATA:11, // Header with embedded data + + print:function(op) { + switch (op) { + case AMMessageType.AMMACK: return "AMMACK"; + case AMMessageType.AMMPING: return "AMMPING"; + case AMMessageType.AMMPONG: return "AMMPONG"; + case AMMessageType.AMMLINK: return "AMMLINK"; + case AMMessageType.AMMUNLINK: return "AMMUNLINK"; + case AMMessageType.AMMRPCHEAD: return "AMMRPCHEAD"; + case AMMessageType.AMMRPCHEADDATA: return "AMMRPCHEADDATA"; + case AMMessageType.AMMRPCDATA: return "AMMRPCDATA"; + case AMMessageType.AMMRPC: return "AMMRPC"; + case AMMessageType.AMMCOLLECT: return "AMMCOLLECT"; + // Rendezvous Broker Message + case AMMessageType.AMMCONTROL: return "AMMCONTROL"; + default: return "Chan.AMMessageType?"; + } + } + +}; + +// Channel state +var AMState = { + AMS_NOTINIT:1, // AMP Not initialized conenction + AMS_INIT:2, // AMP Server started, but not confirmed + AMS_READY:3, // AMP Server initialized and confirmed (other end point not connected) + AMS_NEGOTIATE:4, // AMP Server intiialized, in negotiation state (other end point not connected) + AMS_CONNECTED:5, // AMP Other side connected + AMS_AWAIT:6, // AMP waits for event (pairing) + AMS_NOTCONNECTED:10, // AMP Other side not connected + // Optional IP broker service + AMS_RENDEZVOUS:7, // Broker IP P2P rendezvous; starting + AMS_REGISTERED:8, // Broker IP P2P rendezvous; registered; expecting pairing + AMS_PAIRING:9, // Broker IP P2P rendezvous; now pairing; send punches until paired + AMS_PAIRED:10, // Broker IP P2P rendezvous; acknowldeged and paired -> NOTCONNECTED + print:function(op) { + switch (op) { + case AMState.AMS_NOTINIT: return "AMS_NOTINIT"; + case AMState.AMS_INIT: return "AMS_INIT"; + case AMState.AMS_READY: return "AMS_READY"; + case AMState.AMS_NEGOTIATE: return "AMS_NEGOTIATE"; + case AMState.AMS_CONNECTED: return "AMS_CONNECTED"; + case AMState.AMS_AWAIT: return "AMS_AWAIT"; + case AMState.AMS_NOTCONNECTED: return "AMS_NOTCONNECTED"; + case AMState.AMS_RENDEZVOUS: return "AMS_RENDEZVOUS"; + case AMState.AMS_REGISTERED: return "AMS_REGISTERED"; + case AMState.AMS_PAIRING: return "AMS_PAIRING"; + case AMState.AMS_PAIRED: return "AMS_PAIRED"; + default: return "Chan.AMState?"; + } + } + }; + +var amp={ + AMMessageType:AMMessageType, + AMState:AMState +}; + + +/************************* +** IP UTILS +*************************/ +function isLocal(addr) { + return addr=='localhost'|| + addr=='127.0.0.1' +} +/* typeof @url = ":" | ":" | ":" | "" + * and @ipport = (1-65535) | "*" + * and @port = string + */ +function url2addr(url,defaultIP) { + var addr={address:defaultIP||options.localhost,proto:'UDP',port:undefined}, + parts = url.split(':'); + if (parts.length==1) { + if (Comp.string.isNumeric(parts[0])) addr.port=Number(parts[0]); + else if (parts[0]=='*') addr.port=undefined; + else addr.address=parts[0]; + } else return {address:parts[0],port:parts[1]=='*'?undefined:Number(parts[1])||parts[1]}; + return addr; +}; + +function addr2url(addr) { + return (isLocal(addr.address)?options.localhost:addr.address)+':'+(addr.port?addr.port:'*') +}; + +function obj2url(obj) { + if (!obj) return '*'; + if (obj.name && !obj.address) return obj.name+':*'; + if (!obj.address) return '*'; + return (isLocal(obj.address)?options.localhost:obj.address)+':'+(obj.port?obj.port:'*') +}; + +function addrequal(addr1,addr2) { + return ipequal(addr1.address,addr2.address) && addr1.port==addr2.port; +} + +function addrempty(addr) { + return !(addr && addr.address && addr.port); +} + +function resolve (url) {return addr2url(url2addr(url)) } + +function ipequal(ip1,ip2) { + if (ip1==undefined || ip2==undefined) return false; + else if ((Comp.string.equal(ip1,'localhost') || Comp.string.equal(ip1,'127.0.0.1')) && + (Comp.string.equal(ip2,'localhost') || Comp.string.equal(ip2,'127.0.0.1'))) return true; + else return ip1==ip2; +} + +// Use remote TCP connection to get this host IP (private address if behind NAT) +var ipnet = Require('net'); +var myip; +function getNetworkIP(server,callback) { + var socket; + if (!ipnet) return callback('Not supported','error'); + if (myip) return callback(undefined,myip); + if (!server) server={address:options.peekIP,port:80}; + socket = ipnet.createConnection(server.port, server.address); + socket.on('connect', function() { + myip=socket.address().address; + callback(undefined, socket.address().address); + socket.end(); + }); + socket.on('error', function(e) { + callback(e, 'error'); + }); +} + + +function doUntilAck(interval, fn, ack, arg) { + if (ack()) return; + fn(arg); + return setTimeout(function() { + doUntilAck(interval, fn, ack, arg); + }, interval); +} + + +module.exports = { + AMMode:AMMode, + AMMessageType:AMMessageType, + AMState:AMState, + amp:amp, + options:options, + addrempty:addrempty, + url2addr:url2addr, + addr2url:addr2url, + obj2url:obj2url, + addrequal:addrequal, + resolve:resolve, + ipequal:ipequal, + getNetworkIP:getNetworkIP, + doUntilAck:doUntilAck +} +}; +BundleModuleCode['jam/ampMAN']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: 30-01-18 by sbosse. + ** $RCS: $Id: chan.js,v 1.3 2017/05/27 18:20:36 sbosse Exp $ + ** $VERSION: 1.14.4 + ** + ** $INFO: + ** + ** JAM Agent Management Port (AMP) - General Management Operations + ** + ** + ** New: + ** - Single message transfers (HEADER+DATA) + ** + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Lz = Require('os/lz-string'); +var Comp = Require('com/compat'); +var Buf = Require('dos/buf'); +var Net = Require('dos/network'); +var Command = Net.Command; +var Status = Net.Status; +var current=none; +var Aios=none; +var CBL = Require('com/cbl'); + +var COM = Require('jam/ampCOM'), + AMMode=COM.AMMode, + AMMessageType=COM.AMMessageType, + AMState=COM.AMState, + amp=COM.amp, + options=COM.options, + url2addr=COM.url2addr, + addr2url=COM.addr2url, + addrequal=COM.addrequal, + resolve=COM.resolve, + ipequal=COM.ipequal, + addrempty=COM.addrempty, + getNetworkIP=COM.getNetworkIP; + +// Get data from message +function msgData(msg) { + // typeof msg.data = Array | Buffer | { type: 'Buffer', data: Array } + return msg.data && msg.data.data?msg.data.data:msg.data; +} + +module.exports.current=function (module) { current=module.current; Aios=module; }; + + +amp.man = function (options) { + +} + +// Message logger +amp.man.prototype.LOG = function (op,msg) { + if (!this.logging) return; + switch (op) { + case 'print': + for(var i in this.logs) { + Aios.log(this.logs[i].op,this.logs[i].time,this.logs[i].msg,AMState.print(this.logs[i].state)); + } + this.logs=[]; + break; + case 'enable': + this.logging=true; + break; + case 'disable': + this.logging=false; + break; + default: + var date = new Date(); + var time = Math.floor(date.getTime()); + this.logs.push({op:op,time:time,msg:msg,state:(this.url && this.links[this.url].state)}); + } +} + + + +/** Transation cache for receiving data fragments that can be out of order. + * typeof @data = [handler:{tid,remote,cmd,size,frags,buf},data:[],timeout:number] + * + */ +amp.man.prototype.addTransaction = function (remote,tid,data) { + if (this.mode & AMMode.AMO_MULTICAST) + this.transactions[remote.address+remote.port+tid]=data; + else + this.transactions[tid]=data; +} +amp.man.prototype.deleteTransaction = function (remote,tid) { + if (this.mode & AMMode.AMO_MULTICAST) + delete this.transactions[remote.address+remote.port+tid]; + else + delete this.transactions[tid]; +} +amp.man.prototype.findTransaction = function (remote,tid) { + if (this.mode & AMMode.AMO_MULTICAST) + return this.transactions[remote.address+remote.port+tid]; + else + return this.transactions[tid]; +} + +/** Check the state of a link + * + */ +amp.man.prototype.checkState = function (state,addr) { + switch (state) { + case AMState.AMS_CONNECTED: + if (this.mode & AMMode.AMO_ONEWAY) return true; + if (this.mode & AMMode.AMO_MULTICAST) return this.links[addr2url(addr)]; + if (this.url && this.links[this.url].state == AMState.AMS_CONNECTED) return true; + break; + } + return false; +} + +/** Update AMP object configuration + * + */ +amp.man.prototype.config = function(options) { + for(var p in options) this[p]=options[p]; +} +/** Handle events + * + */ +amp.man.prototype.emit = function(event,arg,aux,aux2) { + if (this.events[event]) this.events[event](arg,aux,aux2); +} + +/** Handler for incoming messages (proecssed by receiver) + * + */ +amp.man.prototype.handle = function (msg,remote,response) { + var handler,thisnum,ipport,cmsg,url,ack; + if (this.verbose > 1) this.out('handle '+AMMessageType.print(msg.type)+' from '+addr2url(remote)); + switch (msg.type) { + case AMMessageType.AMMRPCHEAD: + case AMMessageType.AMMRPCHEADDATA: + if (!this.checkState(AMState.AMS_CONNECTED,remote)) return; + + handler={}; + handler.tid=msg.tid; + // handler.remote=remote.address+':'+Buf.buf_get_int16(buf); + handler.remote=remote; + handler.cmd=msg.cmd; + handler.size=msg.size; + handler.frags=msg.frags; + // console.log(handler) + if (handler.size>0 && handler.frags>0) { + // AMMRPCDATA messages are following + handler.buf=Buf.Buffer(); + dlist = Comp.array.range(0, handler.frags - 1); + // Add transaction to cache for pending data + this.addTransaction(remote, handler.tid, [handler,dlist,1000]); + } else if (handler.size>0) { + // Single message transfer; message contains all data + handler.buf=msgData(msg); + this.callback(handler); + } else { + // No data; control message + handler.buf=Buf.Buffer(); + this.callback(handler); + } + break; + + case AMMessageType.AMMRPCDATA: + if (!this.checkState(AMState.AMS_CONNECTED,remote)) return; + thisnum = msg.off/this.dlimit; + transaction = this.findTransaction(remote,msg.tid); + if (transaction!=undefined) { + handler=transaction[0]; + if (this.verbose>1) + this.out('receiver: adding data num='+ + thisnum+' off='+msg.off+' size='+msg.size+' dlist='+transaction[1]); + + Buf.buf_get_buf(msgData(msg),handler.buf,msg.off,msg.size); + transaction[1]=Comp.array.filter(transaction[1],function(num) {return (num!=thisnum)}); + if (Comp.array.empty(transaction[1])) { + if (this.verbose>1) this.out('[AMP] receiver: finalize '+addr2url(remote)); + // Io.out(handler.data.toString()); + // Deliver + this.callback(handler); + this.deleteTransaction(remote,msg.tid); + } + } + break; + + case AMMessageType.AMMRPC: + if (!this.checkState(AMState.AMS_CONNECTED,remote)) return; + // Complete RPC message + handler={}; + handler.tid=msg.tid; + // handler.remote=remote.address+':'+Buf.buf_get_int16(buf); + handler.remote=remote; + handler.cmd=msg.cmd; + handler.size=msg.size; + handler.frags=msg.frags; + handler.buf=Buf.Buffer(msgData(msg)); + this.callback(handler); + if (this.ack && response) this.ack(response); + break; + + case AMMessageType.AMMPING: + url=addr2url(remote); + ipport=remote.port; + if (this.mode&AMMode.AMO_MULTICAST) { + if (!this.links[url] || this.links[url].state!=AMState.AMS_CONNECTED) return; + } else if (this.url) { + if (this.links[this.url].state!=AMState.AMS_CONNECTED) return; + } + // Send back a PONG message only if we're connected + this.pong({address:remote.address,port:ipport},response); + break; + + case AMMessageType.AMMPONG: + ipport=remote.port; + if (this.mode&AMMode.AMO_MULTICAST) { + url=addr2url(remote); + if (this.links[url] && this.links[url].state==AMState.AMS_CONNECTED) { + this.links[url].live = options.AMC_MAXLIVE; + } + } else if (this.url && this.links[this.url].state==AMState.AMS_CONNECTED) { + this.links[this.url].live = options.AMC_MAXLIVE; + } + if (this.ack && response) this.ack(response); + break; + + case AMMessageType.AMMACK: + if (msg.status=="ELINKED") { + if (this.mode&AMMode.AMO_MULTICAST) { + // Multicast mode + url=addr2url(remote); + if (!this.links[url] || this.links[url].state==AMState.AMS_NOTCONNECTED) { + // Ad-hoc remote connect + if (!this.links[url]) this.links[url]={}; + this.links[url].snd=remote; + this.links[url].live=options.AMC_MAXLIVE; + this.links[url].port=msg.port; + this.links[url].ipport=remote.port; + this.links[url].state=AMState.AMS_CONNECTED; + this.links[url].node=msg.node; + this.emit('route+',url,msg.node); + this.watchdog(true); + if (this.verbose) + this.out('Linked with ad-hoc '+this.proto+' '+url+', AMP '+ + Net.Print.port(msg.port)+', Node '+msg.node); + } + } + } + break; + + case AMMessageType.AMMLINK: + ipport=remote.port; + if (this.mode&AMMode.AMO_MULTICAST) { + // Multicast mode + url=addr2url(remote); + if (!this.links[url] || this.links[url].state==AMState.AMS_NOTCONNECTED) { + // Ad-hoc remote connect + if (!this.links[url]) this.links[url]={}; + this.links[url].snd=remote; + this.links[url].live=options.AMC_MAXLIVE; + this.links[url].port=msg.port; + this.links[url].ipport=remote.port; + // back link acknowledge + this.link(this.links[url].snd,false,none,response); + // no ack="EOK" -- ack send by link response!; + this.links[url].state=AMState.AMS_CONNECTED; + this.links[url].node=msg.node; + // if (this.mode&AMMode.AMO_UNICAST) this.snd=remote,this.url=url; + this.emit('route+',url,msg.node); + this.watchdog(true); + if (this.verbose) + this.out('Linked with ad-hoc '+this.proto+' '+url+', AMP '+ + Net.Print.port(msg.port)+', Node '+msg.node); + } else if (this.links[url].state==AMState.AMS_CONNECTED) { + // Already linked! Just acknowledge + ack="ELINKED"; + } + } else { + url=addr2url(remote); + + // Unicast mode; only one connection + if (this.links[url] && !addrempty(this.links[url].snd) && + this.links[url].state==AMState.AMS_NOTCONNECTED && + ipequal(this.links[url].snd.address,remote.address) && + this.links[url].snd.port==ipport) // ipport or remote.port?? + { + // Preferred / expected remote connect + this.links[url].snd=remote; + this.links[url].port=msg.port; + this.links[url].ipport=remote.port; + this.links[url].node=msg.node; + this.links[url].live=options.AMC_MAXLIVE; + + // back link acknowledge + this.link(this.links[url].snd); + + this.links[url].state=AMState.AMS_CONNECTED; + // Inform router + this.emit('route+',url,msg.node); + this.watchdog(true); + if (this.verbose) + this.out('Linked with preferred '+this.proto+' '+ url +', '+ + Net.Print.port(msg.port)); + } else if ((!this.links[url] && !this.url) || + (this.links[url] && this.links[url].state==AMState.AMS_NOTCONNECTED) || + (this.broker && this.url && this.links[this.url].state==AMState.AMS_NOTCONNECTED)) { + if (!this.links[url]) this.links[url]={}; + this.links[url].snd=remote; + this.links[url].live=options.AMC_MAXLIVE; + this.links[url].port=msg.port; + this.links[url].ipport=remote.port; + this.links[url].node=msg.node; + + // back link acknowledge + this.link(this.links[url].snd,false,none,response); + // no ack="EOK"; - ack was send with link message! + + this.links[url].state=AMState.AMS_CONNECTED; + this.url=url; // remember this link + + // Inform router + this.emit('route+',url,msg.node); + this.watchdog(true); + + if (this.verbose) + this.out('Linked with ad-hoc ' + this.proto +' '+ url +', '+ + Net.Print.port(msg.port)); + } + } + if (ack && this.ack && response) this.ack(response,ack); + break; + + case AMMessageType.AMMUNLINK: + ipport=remote.port; + if (this.mode&AMMode.AMO_MULTICAST) { + // Multicast mode + url=addr2url(remote); // ipport or remote.port?? + if (this.links[url] && !addrempty(this.links[url].snd) && ipequal(this.links[url].snd.address,remote.address) && + this.links[url].snd.port==ipport && this.links[url].state==AMState.AMS_CONNECTED) { + this.links[url].state=AMState.AMS_NOTCONNECTED; + // Not negotiated. Just close the link! + if (this.verbose) + this.out('Unlinked ' +url+', '+ + Net.Print.port(msg.port)); + // Inform router + this.emit('route-',url); + if (!this.links[url].snd.connect) this.links[url].snd={}; + if (this.cleanup) this.cleanup(url); + } + } else { + // Unicast mode + if (this.url && !addrempty(this.links[this.url].snd) && + ipequal(this.links[this.url].snd.address,remote.address) && + this.links[this.url].snd.port==ipport && + this.links[this.url].state==AMState.AMS_CONNECTED) + { + this.links[this.url].state=AMState.AMS_NOTCONNECTED; + addr=this.links[this.url].snd; + // Not negotiated. Just close the link! + if (this.verbose) + this.out('Unlinked ' + this.url +', '+ + Net.Print.port(msg.port)); + + // Inform router + this.emit('route-',addr2url(addr)); + if (!this.links[this.url].snd.connect) this.links[this.url].snd=null; + if (this.cleanup) this.cleanup(url); + } + } + if (this.ack && response) this.ack(response); + break; + + // optional rendezvous brokerage ; remote is broker IP!!! + case AMMessageType.AMMCONTROL: + cmsg = JSON.parse(msgData(msg)); + if (this.verbose>1) this.out('# got message '+msgData(msg)); + this.LOG('rcv',cmsg); + // All brokerage and pairing is handled by the root path '*'! + if (this.control && this.links['*']) + this.control(this.links['*'],cmsg,remote); + + break; + + default: + this.out('handle: Unknown message type '+msg.type); + } +} + +/** Install event handler + * + */ +amp.man.prototype.on = function(event,handler) { + this.events[event]=handler; +} + +// Status of link, optionally checking destination +amp.man.prototype.status = function (ip,ipport) { + var p,url; + if (this.mode&AMMode.AMO_MULTICAST) { + if (!ip) { + for(p in this.links) if (this.links[p] && this.links[p].state==AMState.AMS_CONNECTED) return true; + return false; + } else { + url=addr2url({address:ip,port:ipport}); + if (!this.links[url]) return false; + return this.links[url].state==AMState.AMS_CONNECTED; + } + } + if (!ip && this.url) return this.links[this.url].state==AMState.AMS_CONNECTED || + (this.mode&AMMode.AMO_ONEWAY)==AMMode.AMO_ONEWAY; + + return (this.url && this.links[this.url].snd.address==ip && this.links[this.url].snd.port==ipport); +} +}; +BundleModuleCode['jam/ampHTTP']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 09-02-16 by sbosse. + ** $RCS: $Id:$ + ** $VERSION: 1.12.6 + ** + ** $INFO: + ** + ** JAM Agent Management Port (AMP) over HTTP + ** Only Mulitcast IP(*) mode is supported! + ** + ** Events out: 'error','route-' + ** + ** TODO: Garbage collection + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Lz = Require('os/lz-string'); +var Comp = Require('com/compat'); +var Buf = Require('dos/buf'); +var Net = Require('dos/network'); +var Command = Net.Command; +var Status = Net.Status; +var current=none; +var Aios=none; +var CBL = Require('com/cbl'); +var Bas64 = Require('os/base64'); + +var COM = Require('jam/ampCOM'), + AMMode=COM.AMMode, + AMMessageType=COM.AMMessageType, + AMState=COM.AMState, + amp=COM.amp, + options=COM.options, + url2addr=COM.url2addr, + addr2url=COM.addr2url, + addrequal=COM.addrequal, + resolve=COM.resolve, + ipequal=COM.ipequal, + getNetworkIP=COM.getNetworkIP; + +var debug = false; + +module.exports.current=function (module) { current=module.current; Aios=module; }; + +/* +** Parse query string '?attr=val&attr=val... and return parameter record +*/ +function parseQueryString( url ) { + var queryString = url.substring( url.indexOf('?') + 1 ); + if (queryString == url) return []; + var params = {}, queries, temp, i, l; + + // Split into key/value pairs + queries = queryString.split("&"); + + // Convert the array of strings into an object + for ( i = 0, l = queries.length; i < l; i++ ) { + temp = queries[i].split('='); + if (temp[1]==undefined) temp[1]='true'; + params[temp[0]] = temp[1].replace('%20',' '); + } + + return params; +} +/* +** Format a query string from a parameter record +*/ +function formatQueryString (msg) { + var path= '/?'; + path += "type="+AMMessageType.print(msg.type); + if (msg.cmd) path += '&cmd='+msg.cmd; + if (msg.tid) path += '&tid='+msg.tid; + if (msg.port) path += '&port='+Net.port_to_str(msg.port); + if (msg.timeout) path += '&timeout='+msg.timeout; + if (msg.node) path += '&node='+msg.node.replace(' ','%20'); + if (msg.index) path += '&index='+msg.index; + return path; +} + +function msg2JSON(msg) { + if (msg.port) msg.port=Net.port_to_str(msg.port); + if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) { + if (msg.port) msg.port=Net.port_to_str(msg.port); + }); + return JSON.stringify(msg); +} +function JSON2msg(data) { + var msg=JSON.parse(data); + if (msg.port) msg.port=Net.port_of_str(msg.port); + if (msg.msg && msg.msg.length) Comp.array.iter(msg.msg,function (msg) { + if (msg.port) msg.port=Net.port_of_str(msg.port); + }); + return msg; +} + +/** Get XML data + * + */ +function getData(data) { + if (data==undefined) return undefined; + else if (data.val!='') return data.val; + else return data.children.toString(); +} + +function is_error(data,err) { + if (data==undefined) return true; + if (err==undefined) + return (data.length > 0 && Comp.string.get(data,0)=='E'); + else + return (Comp.string.equal(data,err)); +}; + +/** AMP port using HTTP + * =================== + * + * No negotiation is performed. Data transfer can be fragmented. + * Each time a remote endpoint sends a GET/PUT request, we stall the request until + * a timeout occurs or we have to send data to the remote endpoint. A link is established. + * The routing table is refreshed each time the same client send a + * GET/PUT request again. If the client do not send requests anymore after a timeout, it is considered to be + * unlinked and the route is removed. + * + * type amp.http = function (options:{rcv:address,snd?:address,verbose?,logging?,out?:function,log?}) + */ +var http = Require('http'); + +amp.http = function (options) { + var self=this; + this.proto = 'http'; + this.options=checkOptions(options,{}); + this.verbose=checkOption(this.options.verbose,0); + + this.dir = options.dir; // attached to JAM port + this.rcv = options.rcv; // Local HTTP Server Port; Server Mode + this.mode = AMMode.AMO_MULTICAST; // We can handle multiple links at once + this.node = options.node; // Attached to this node + + if (options.rcv && options.rcv.address!='*' && options.rcv.port) this.mode |= AMMode.AMO_SERVER; + else this.mode |= AMMode.AMO_CLIENT; + + this.options.keepalive=true; + + this.port = options.port||Net.uniqport(); // Connection Link Port (this side) + this.id = Net.Print.port(this.port); + // Stream socket; can be a process object! + this.out = function (msg) { + var time=Io.Time()+' '; + Aios.print(time+'[AMP '+Net.Print.port(self.port)+ + (self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] '+msg); + } + this.err = function (msg) { + var time=Io.Time()+' '; + Aios.print(time+'[AMP '+Net.Print.port(self.port)+ + (self.dir?(' '+Aios.DIR.print(self.dir)):'')+'] Error: '+msg); + throw 'AMP'; + } + + this.events = []; + // typeof linkentry = {snd:address,tries:number,state:amstate,collect?,collecting?,msgqueue?:{} []} + this.links = {}; + this.count={rcv:0,snd:0,lnk:0,png:0}; + if (options.snd) { + url=addr2url(options.snd,'127.0.0.1'); + this.links[url]={snd:options.snd,tries:0,state:AMState.AMS_NOTCONNECTED,live:options.AMC_MAXLIVE}; + //this.out(url) + } + // Collector thread collecting messages from server (AMO_CLIENT mode) + this.collector=undefined; + + this.logs=[]; + this.logging=options.logging||false; + if (this.logging) { + setInterval(function () { self.LOG('print') },5000); + } + this.index=0; +}; + +amp.http.prototype.LOG = amp.man.prototype.LOG; +amp.http.prototype.checkState = amp.man.prototype.checkState; +amp.http.prototype.config = amp.man.prototype.config; +amp.http.prototype.emit = amp.man.prototype.emit; +amp.http.prototype.on = amp.man.prototype.on; +amp.http.prototype.handle = amp.man.prototype.handle; +amp.http.prototype.status = amp.man.prototype.status; + +/** Acknowledge reply + * + */ +amp.http.prototype.ack=function(snd,status) { + this.reply(snd,{type:AMMessageType.AMMACK,status:status||"EOK", + port:this.port,node:this.node?this.node.id:'*'}); +} + +/** Callback from ampMAN handler to inform about remote unlink event + * + */ +amp.http.prototype.cleanup=function(url,keep) { + // Cleanup link + var obj=this.links[url]; + if (!obj) return; + obj.state=AMState.AMS_NOTCONNECTED + if (obj.collect) clearTimeout(obj.collect), obj.collect=undefined; + if (obj.collecting) this.reply(obj.collecting,{status:'ENOENTRY'}),obj.collecting=undefined; + // Link was initiated on remote side + // Remove link! + if (!keep) { + obj.snd={}; + this.links[url]=undefined; + } +} + +/** Collect request + * + */ +amp.http.prototype.collect=function(snd) { + var self=this, + url=addr2url(snd), + msg={type:AMMessageType.AMMCOLLECT,port:this.port,index:this.index++}; + if (this.links[url] && this.links[url].state==AMState.AMS_CONNECTED) + this.send(snd,msg,function (reply) { + var err=is_error(reply); + if (err) return; // self.cleanup(url,true); + if (reply.msg) Comp.array.iter(reply.msg,function (msg) { + self.handle(msg,snd); + }); + if (!self.links[url]) return; // unlinked? + self.links[url].collect=setTimeout(function () { + self.collect(snd); + },0); + }); +} +/** Service collect request + * + */ +amp.http.prototype.collecting=function(msg,remote,response) { + var url; + if (this.verbose>2) this.out('handle AMMCOLLECT from '+addr2url(remote)); + url=addr2url(remote); // ipport or remote.port?? + if (this.links[url] && this.links[url].msgqueue && this.links[url].msgqueue.length) { + this.reply(response,{msg:this.links[url].msgqueue}); + this.links[url].msgqueue=[]; + } + else if (this.links[url]) this.links[url].collecting=response; + else this.reply(response,{status:'ENOENTRY'}); +} + +/** HTTP GET request to send a messageto the server broker returning data on reply. + * + * @param path + * @param callback + */ + +amp.http.prototype.get = function (snd,path,callback) { + var body,req, + self=this; + + if (this.verbose>2) this.out('get '+addr2url(snd)+ path); + this.count.snd = this.count.snd + path.length; + if (!http.xhr) { + req = http.request({ + host: snd.address, + port: snd.port, + path: path, + method: 'GET', + keepAlive: this.options.keepalive, + headers: { + } + } , function(res) { + if (self.verbose>2) self.out('got '+addr2url(snd)+ path); + if (res.setEncoding != null) res.setEncoding('utf8'); + body = ''; + res.on('data', function (chunk) { + body = body + chunk; + }); + res.once('end', function () { + self.count.rcv += body.length; + if (callback) callback(body); + }); + }); + req.once('error', function(err) { + if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err); + self.emit('error',err); + if (callback) callback(); + }); + req.end(); + } else { + // XHR Browser + http.request({ + host: snd.address, + port: snd.port, + path:path, + proto:'http', + method: 'GET', + headers: { + } + } , function(err,xhr,body) { + if (err) { + if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' '+path+' failed: '+err); + self.emit('error',err); + if (callback) callback(); + } else { + self.count.rcv += body.length; + if (callback) callback(body); + } + }); + } +}; + +/** Initialize AMP + * + */ +amp.http.prototype.init = function(callback) { + if (callback) callback(); +}; + +/** Negotiate a virtual communication link (peer-to-peer). + * In oneway mode only a destination endpoint is set and it is assumed the endpoint can receive messages a-priori! + * + * typeof @snd = address + * typeof @callback = function + * typeof @connect = boolean is indicating an initial connect request and not an acknowledge + * typeof @key = private + * typeof @response = object + * + * +------------+ + * VCMessageType (int16) + * Connection Port (port) + * Node ID (string) + * // Receiver IP Port (int32) + * +------------+ + * + */ +amp.http.prototype.link=function(snd,connect,key,response) { + var self = this, + msg, + url; + if (this.verbose>1) this.out('amp.link: to '+addr2url(snd)); + + // MULTICAST mode + // Add new link to cache of links + if (!snd) this.err('link: no destinataion set in MULTICAST mode'); + url=addr2url(snd); + if (!this.links[url] || !this.links[url].snd.address) { + if (connect) snd.connect=true; + this.links[url]={ + snd:snd, + state:AMState.AMS_NOTCONNECTED, + tries:0, + connect:connect, + live:options.AMC_MAXLIVE}; + } + // Let watchdog handle connect request link messages + if (!this.inwatchdog && connect) + return this.watchdog(true); + // if (this.verbose>1) this.out('send link '+Io.inspect(snd)); + msg={type:AMMessageType.AMMLINK,port:this.port,node:this.node?this.node.id:'*',index:this.index++}; + + this.count.lnk++; + + if (response) + this.reply(response,msg); + else this.send(snd,msg,function (reply) { + if (is_error(reply)) return; // error + // start message collector thread after first link reply! + if ((self.mode & AMMode.AMO_CLIENT) && !self.links[url].collect) { + self.links[url].collect=setTimeout(function () { + self.collect(snd); + },0); + } + // handle reply + self.handle(reply,snd); + }); +}; + +amp.http.prototype.ping=function(snd,response) { + var self = this,msg={}; + + msg.type=AMMessageType.AMMPING; + msg.port=this.port; + msg.index=this.index++; + + // Buf.buf_put_int32(buf, self.rcv.port); + + if (this.verbose>1) this.out('amp.ping: to '+addr2url(snd)); + + this.count.png++; + + if (response) + this.reply(response,msg); + else this.send(snd,msg,function (reply) { + if (is_error(reply)) return; // error + // handle reply + self.handle(reply,snd); + }); +} +amp.http.prototype.pong=function(snd,response) { + var self = this,msg={}; + + msg.type=AMMessageType.AMMPONG; + msg.port=this.port; + msg.index=this.index++; + + // Buf.buf_put_int32(buf, self.rcv.port); + + if (this.verbose>1) this.out('amp.pong: to '+addr2url(snd)); + + this.count.png++; + + if (response) + this.reply(response,msg); + else this.send(snd,msg,function (reply) { + if (is_error(reply)) { + self.emit('error',reply); + } + }); +} + +/** HTTP PUT request to send a message and data to the AMP HTTP server. + * + * @param path + * @param data + */ +amp.http.prototype.put = function (snd,path,data) { + var self=this, + req,body; + this.count.snd = this.count.snd + path.length + data.length; + if (!http.xhr) { + req = http.request({ + host: snd.address, + port: snd.port, + path: path, + method: 'POST', + keepAlive: this.options.keepalive, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': data.length + } + } , function(res) { + if (res.setEncoding != null) res.setEncoding('utf8'); + // TODO body=+chunk, res.on('end') ..?? + res.once('data', function (chunk) { + // TODO + }); + }); + req.once('error', function(err) { + self.out('Warning: request to '+addr2url(snd)+' failed: '+err); + self.emit('error',err); + }); + + // write data to request body + req.write(data); + req.end(); + } else { + // XHR Browser + http.request({ + host: snd.address, + port: snd.port, + path: path, + method: 'POST', + body:data, + keepAlive: this.options.keepalive, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': data.length + } + } , function(err,xhr,body) { + if (err) { + if (self.verbose) self.out('Warning: request to '+addr2url(snd)+' failed: '+err); + self.emit('error',err); + } + // TODO + }) + } +}; + +amp.http.prototype.receiver = function (callback,rcv) { + var self = this; + + if (callback) this.callback=callback; + + if (this.mode & AMMode.AMO_SERVER) { + // Only if this is a public or locally visible network node this node + // should provide a server port! + if (rcv == undefined || rcv.address==undefined) rcv={},rcv.address=this.rcv.address; + if (rcv.port==undefined) rcv.port=this.rcv.port; + + this.server=http.createServer(function (request,response) { + if(parseQueryString(request.url).length==0) return response.end('EINVALID'); // accidental access by WEB browser + var i,body, + msg = parseQueryString(request.url), + remote = {address:request.connection.remoteAddress.replace(/^::ffff:/,''), + port:'['+msg.port.replace(/:/g,'')+']' /* unique remote identifier */}; + + if (self.verbose>2) + console.log(request.method,request.url,msg,addr2url(remote),url2addr(addr2url(remote))); + + self.count.rcv += msg.length; + msg.type=AMMessageType[msg.type]; + + if (debug) console.log(Io.Time(),msg) + + response.origin=request.headers.origin||request.headers.Origin; + Comp.string.match(request.method,[ + ['GET',function() { + if (msg.type==AMMessageType.AMMCOLLECT) + self.collecting(msg,remote,response); + else + self.handle(msg,remote,response); + }], + ['POST',function() { + body = ''; + request.on('data', function (chunk) { + body = body + chunk; + }); + request.on('end', function () { + msg.data=Buffer(body,'hex'); + self.count.rcv += msg.data.length; + if (msg.cmd) msg.cmd=Number(msg.cmd); + self.handle(msg,remote,response); + }); + }] + ]) + }); + + this.server.on("connection", function (socket) { + socket.setNoDelay(true); + }); + + this.server.on("error", function (err) { + self.out('Warning: receiver failed: '+err); + if (err) self.err(err); + }); + + this.server.listen(rcv.port,function (err) { + // Try to get network IP address of this host + if (!err) getNetworkIP(undefined,function (err,ip) { + if (!err) self.rcv.address=ip; + if (self.verbose) self.out('IP port '+addr2url(self.rcv)+ ' (proto '+self.options.proto+')'); + if (err) return self.out("! Unable to obtain network connection information: "+err); + }); + if (callback) callback(err); + }); + } + if (this.mode & AMMode.AMO_CLIENT) { + + // If this is a hidden node (e.g., inside a WEB browser), we have to connect to a remote public server + // by using stalled GET requests. + if (callback) this.callback=callback; + } +} + +/** Send a reply for a pending HTTP GET/PUT request (AMO_SERVER) + * + */ +amp.http.prototype.reply = function (response,msg) { + var data=msg2JSON(msg), header; + + if (response.origin!=undefined) + header={'Access-Control-Allow-Origin': response.origin, + 'Access-Control-Allow-Credentials': 'true', + 'Content-Type': 'text/plain'}; + else + header={'Content-Type': 'text/plain'}; + if (this.options.keepalive) header["Connection"]="keep-alive"; + + response.writeHead(200,header); + response.write(data); + if (debug) console.log(Io.Time(),msg) + response.end(); +} + +/** Send a request message to a remote node endpoint + * + * function (cmd:integer,msg:Buffer,snd:address) + */ + +amp.http.prototype.request = function (cmd,msg,snd) { + var self=this,req={}, + size = msg.data.length, + tid = Comp.random.int(65536/2); + + if (snd==undefined) this.err('request: snd=null'); + + req.type=AMMessageType.AMMRPC; + req.tid=tid; // Transaction Message ID + req.port=this.port; // This AMP id + // Buf.buf_put_int16(buf,self.rcv.port); // For reply + req.cmd=cmd; + req.size=size; + req.data=msg.data; + this.send(snd,req); + +} +/** Main entry for requests with JSON interface. Multiplexer for HTTP GET/PUT. + * + * msg: JSON + * callback : function (reply:object) + */ +amp.http.prototype.send = function (snd,msg,callback) { + var path, + url, + body, + self=this; + // Create query selector + path = formatQueryString(msg); + + if (typeof snd.port == 'string') { + url=addr2url(snd); + // If Pending get from client + + // Else queue message, client will collect them later (or never) + if (this.links[url]) { + if (!this.links[url].msgqueue) this.links[url].msgqueue=[]; + if (this.links[url].collecting) {// pending AMMCOLLECT request + if (this.verbose>1) this.out('REPLY msg '+AMMessageType.print(msg.type)+' to '+url); + this.reply(this.links[url].collecting,{msg:[msg]}); + this.links[url].collecting=undefined; + } else { + if (this.verbose>1) this.out('QUEUE msg '+AMMessageType.print(msg.type)+' for '+url); + this.links[url].msgqueue.push(msg); + } + } + } else if (msg.data!=undefined) { + // Convert buffer data to hex formatted string + body=msg.data.toString('hex'); + + this.put(snd,path,body,function (body) { + if (is_error(body)) self.emit('error',body); + else if (!is_status(body)) self.emit('error','EINVALID'); + // No reply expected! + }); + } else { + this.get(snd,path,function (body) { + var xml,i, + reply; + if (!body || is_error(body)) { + self.emit('error','EINVALID'); + } else { + reply=JSON2msg(body); + // { status:string,reply:*,msg?:{}[],..} + } + if (callback) callback(reply); + }); + } +} + + +// Start AMP watchdog and receiver +amp.http.prototype.start = function(callback) { + var self=this; + if (this.verbose>0 && this.mode & AMMode.AMO_SERVER) + this.out('Starting ' + addr2url(this.rcv)+' ['+AMMode.print(this.mode)+'] (proto '+this.proto+')'); + if (this.verbose>0 && this.mode & AMMode.AMO_CLIENT) + this.out('Starting ['+AMMode.print(this.mode)+'] (proto http)'); + this.watchdog(true); + if (!this.server && this.mode & AMMode.AMO_SERVER) { + // After stop? Restart receiver. + this.receiver(); + } + if (callback) callback(); +} + +// Stop AMP +amp.http.prototype.stop = function(callback) { + for(var p in this.links) { + if (this.links[p]) { + // Try to unlink remote endpoint + this.unlink(this.links[p].snd); + this.links[p].state=AMState.AMS_NOTCONNECTED; + if (this.links[p].collect) clearTimeout(this.links[p].collect),this.links[p].collect=undefined; + } + } + if (this.timer) clearTimeout(this.timer),this.timer=undefined; + if (this.server) this.server.close(),this.server=undefined; + + if (callback) callback(); +} + +// Unlink remote endpoint +amp.http.prototype.unlink=function(snd) { + var self = this,msg, + url = snd?addr2url(snd):null; + if (this.mode&AMMode.AMO_MULTICAST) { + if (!this.links[url] || this.links[url].state!=AMState.AMS_CONNECTED) return; + } else { + if (this.links.state!=AMState.AMS_CONNECTED) return; + } + msg={type:AMMessageType.AMMUNLINK,port:this.port,node:this.node?this.node.id:'*',index:this.index++}; + + this.send(snd,msg,function (reply) { + // handle reply + if (reply) {} + }); + this.emit('route-',addr2url(snd)); + if (this.mode&AMMode.AMO_MULTICAST) { + this.links[url].state=AMState.AMS_NOTCONNECTED; + if (!this.links[url].snd.connect) this.links[url].snd={}; + } else { + this.links.state=AMState.AMS_NOTCONNECTED; + if (!this.links.snd.connect) this.links.snd={}; + } + this.cleanup(url); +} + + +/** Install a watchdog timer. + * + * 1. If link state is AMS_NOTCONNECTED, retry link request if this.links[].snd is set. + * 2. If link state is AMS_CONNECTED, check link end point. + * 3, If link state is AMS_RENDEZVOUS, get remote endpoint connectivity via broker + * + * @param run + */ +amp.http.prototype.watchdog = function(run,immedOrDelay) { + var self=this; + if (this.timer) clearTimeout(self.timer),this.timer=undefined; + if (run) self.timer=setTimeout(function () { + if (!self.timer || self.inwatchdog) return; // stopped or busy? + self.timer = undefined; + self.inwatchdog=true; + + function handle(obj,url) { + if (self.verbose>1) self.out('Watchdog: handle link '+ + (obj.snd?addr2url(obj.snd):'')+' in state '+ + AMState.print(obj.state)+' live '+obj.live); + switch (obj.state) { + case AMState.AMS_CONNECTED: + if (obj.live == 0) { + // No PING received, disconnect... + if (self.verbose>0) + self.out('Endpoint ' + addr2url(obj.snd) + + ' not responding, propably dead. Unlinking...'); + obj.state = AMState.AMS_NOTCONNECTED; + self.emit('route-',addr2url(obj.snd)); + self.cleanup(url,obj.snd.connect); + if (obj.snd.connect) self.watchdog(true,2000); + } else { + obj.tries=0; + obj.live--; + self.watchdog(true); + if (self.mode&AMMode.AMO_MULTICAST) self.ping(obj.snd); + else self.ping(); + } + break; + case AMState.AMS_NOTCONNECTED: + case AMState.AMS_PAIRED: + if (obj.snd.port && typeof obj.snd.port == 'number') { + // Try link to specified remote endpoint obj.snd + if (self.verbose>0 && obj.tries==0) + self.out('Trying link to ' + addr2url(obj.snd)); + self.link(obj.snd); + obj.tries++; + if (obj.tries < options.TRIES) self.watchdog(true); + else { + self.out('Giving up to link '+addr2url(obj.snd)); + self.emit('error','link',addr2url(obj.snd)); + obj.snd={},obj.tries=0; + } + } + break; + // AMP P2P Control + case AMState.AMS_RENDEZVOUS: + obj.send( + {type:'register',name: self.rcv.name, linfo: self.rcv}, + self.broker, + function () {} + ); + self.watchdog(true); + break; + case AMState.AMS_REGISTERED: + if (obj.tries < options.TRIES && obj.snd.name) { + obj.tries++; + self.send( + {type:'pair', from:self.rcv.name, to: obj.snd.name}, + self.broker, + function () { + } + ); + } + if (obj.tries < options.TRIES) self.watchdog(true); + break; + } + } + for(var p in self.links) if (self.links[p]) handle(self.links[p],p); + self.inwatchdog=false; + },immedOrDelay==true?0:immedOrDelay||options.TIMER); +}; + +}; +BundleModuleCode['os/http.browser']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 bLAB + ** $CREATED: Stefan Bosse + ** $VERSION: 1.1.2 + ** + ** $INFO: + ** + * ================================ + * Browser HTTP Request (Simplified version) + * ================================ + * + ** + ** $ENDOFINFO + */ + +var XHR = XMLHttpRequest +if (!XHR) throw new Error('missing XMLHttpRequest') +else console.log('HTTP Browser Module Ver. 1.1.1 initialized.'); + +var DEFAULT_TIMEOUT = 2000; + +/** request + * typeof @options = { host: string, port:number, path:string, method:"GET"|"PUT", body?:string, headers:{} } + * typeof @callback = function (err, xhr, body) + */ + +function request(options, callback) { + var xhr = new XHR(), + err, + url = options.uri || ('http://'+options.host+':'+(options.port?options.port:80)+'/'+options.path), + is_cors = is_crossDomain(url), + supports_cors = ('withCredentials' in xhr) + + if(is_cors && !supports_cors) { + err = new Error('Browser does not support cross-origin request: ' + options.uri) + err.cors = 'unsupported' + return callback(err, xhr) + } + options.headers = options.headers || {}; + options.timeout = options.timeout || DEFAULT_TIMEOUT; + options.headers = options.headers || {}; + options.body = options.body || null; + + if(is_cors) xhr.withCredentials = !! options.withCredentials; + xhr.timeout = options.timeout; + + xhr.onopen = function () { + for (var key in options.headers) + xhr.setRequestHeader(key, options.headers[key]) + } + + xhr.onload = function () { + if(xhr.status === 0) { + err = new Error('EREQUEST') + callback(err, xhr) + } + else callback(null,xhr,xhr.responseText) + } + + xhr.ontimeout = function () { + // XMLHttpRequest timed out. Do something here. + err = new Error('ETIMEOUT') + err.duration = options.timeout + callback(err,xhr, null) + }; + + xhr.onrror = function () { + // XMLHttpRequest failed. Do something here. + err = new Error('ESERVER') + callback(err,xhr, null) + }; + + xhr.onreadystatechange = function () { + if (xhr.readyState === XHR.DONE) { + if(xhr.status === 0) { + err = new Error('ENETWORK') + callback(err, xhr) + } + } + }; + + switch (options.method) { + case 'GET': + case 'get': + xhr.open('GET', url, true /* async */); + xhr.send() + break; + case 'PUT': + case 'POST': + case 'put': + case 'post': + xhr.open('POST', url, true /* async */); + xhr.send(options.body) + break; + } +} + +function is_crossDomain(url) { + var rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/ + + + // jQuery #8138, IE may throw an exception when accessing + // a field from window.location if document.domain has been set + var ajaxLocation + try { ajaxLocation = location.href } + catch (e) { + // Use the href attribute of an A element since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; + } + + if (ajaxLocation.match('file:')) return true; + + var ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || [] + , parts = rurl.exec(url.toLowerCase() ) + + var result = !!( + parts && + ( parts[1] != ajaxLocParts[1] + || parts[2] != ajaxLocParts[2] + || (parts[3] || (parts[1] === "http:" ? 80 : 443)) != (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? 80 : 443)) + ) + ) + + //console.debug('is_crossDomain('+url+') -> ' + result) + return result +} + +module.exports = { + request:request, + xhr: true +}; +}; +BundleModuleCode['jam/mobi']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 15-1-16 by sbosse. + ** $RCS: $Id: mobi.js,v 1.2 2017/05/27 18:20:36 sbosse Exp $ + ** $VERSION: 1.9.3 + ** + ** $INFO: + ** + ** JavaScript AIOS Agent Mobilityn Module + ** + ** $ENDOFINFO + */ + +var Comp = Require('com/compat'); +var Net; + +var options = { + version:'1.9.2' +} + +if (global.config.dos) Net=Require('dos/network'); + +var current=none; +var Aios = none; + +/** Direction type; used with move and link? operations + * The link operation can eitehr return a boolean value or + * a list of reachable destiantions (PATH/IP only). + * NORTH, ..., are used for P2P connections only. + */ +var DIRS= ['NORTH','SOUTH','WEST','EAST','LEFT','RIGHT','UP','DOWN','ORIGIN','NW','NE','SW','SE', + 'DELTA','RANGE','NODE','IP','PATH','CAP']; + +/* + +enum DIR = { + NORTH , SOUTH , .. , + IP(ip:string) , .. + } : dir +tyoe dir = NORTH | SOUTH | .. | IP {tag,ip:string } | .. +*/ +var DIR = { + NORTH:'DIR.NORTH', + SOUTH:'DIR.SOUTH', + WEST:'DIR.WEST', + EAST:'DIR.EAST', + LEFT:'DIR.LEFT', + RIGHT:'DIR.RIGHT', + UP:'DIR.UP', + DOWN:'DIR.DOWN', + ORIGIN:'DIR.ORIGIN', + NW:'DIR.NW', + NE:'DIR.NE', + SW:'DIR.SW', + SE:'DIR.SE', + // Assuming: z-> x N + // | W+E U(+z)/D(-z) + // v y S + DELTA: function (addr) { return {tag:"DIR.DELTA",delta:addr} }, + // Only for link? operation + RANGE: function (r) { return {tag:"DIR.RANGE",radius:r} }, + // Address a node (identifier name) directly + NODE: function (node) { return {tag:"DIR.NODE",node:node} }, + IP:function (addr) { return {tag:"DIR.IP",ip:addr} }, + /* + ** + ** Path can contain filter, e.g. range /distance[0-5], /distance[5], .. + ** or sets of destinations, e.g., /node* + ** or a hopping array [dest1,dest2,..] + ** type of path = string | string array + */ + PATH:function (path) { return {tag:"DIR.PATH",path:path} }, + CAP:function (cap) { return {tag:"DIR.CAP",cap:cap} } +} + +DIR.tag = { + NORTH:'DIR.NORTH', + SOUTH:'DIR.SOUTH', + WEST:'DIR.WEST', + EAST:'DIR.EAST', + LEFT:'DIR.LEFT', + RIGHT:'DIR.RIGHT', + UP:'DIR.UP', + DOWN:'DIR.DOWN', + ORIGIN:'DIR.ORIGIN', + NW:'DIR.NW', + NE:'DIR.NE', + SW:'DIR.SW', + SE:'DIR.SE', + DELTA:'DIR.DELTA', + RANGE:'DIR.RANGE', + NODE:'DIR.NODE', + IP:'DIR.IP', + PATH:'DIR.PATH', + CAP:'DIR.CAP', +} +/** Back direction. In case of IP, the remote address on receiving agent code is used. + */ + +function opposite (dir,next) { + var chan; + switch (dir.tag||dir) { + case DIR.NORTH: return DIR.SOUTH; + case DIR.SOUTH: return DIR.NORTH; + case DIR.WEST: return DIR.EAST; + case DIR.EAST: return DIR.WEST; + case DIR.LEFT: return DIR.RIGHT; + case DIR.RIGHT: return DIR.LEFT; + case DIR.UP: return DIR.DOWN; + case DIR.DOWN: return DIR.UP; + case DIR.NW: return DIR.SE; + case DIR.NE: return DIR.SW; + case DIR.SE: return DIR.NW; + case DIR.SW: return DIR.NE; + case DIR.tag.DELTA: + if (!next) return DIR.DELTA(dir.delta.map(function (v) {return -v})); + else return; + case DIR.tag.IP: + // try to use current process back attribute containing remote IP address upon receiving + if (current.process && current.process.back && current.process.back.tag==DIR.tag.IP) return current.process.back; + else return none; + case DIR.tag.NODE: + // try to use current process back attribute containing remote IP address upon receiving + if (current.process && current.process.back) { + switch (current.process.back.tag) { + case DIR.tag.IP: + // Try to resolve node name + if (current.node && current.node.connections.ip && current.node.connections.ip.reverse) + return DIR.NODE(current.node.connections.ip.reverse(current.process.back.ip)); + else + return current.process.back; + case DIR.tag.NODE: + return current.process.back; + default: + return none; + } + } else return none; + + case 'DIR.PATH': + // TODO: this node name/path! + return none; + case 'DIR.CAP': + // TODO: this node capability! + return none; + default: + return none; + } +}; + +// Create a valid DIR compatible type from a lowercase name specifier (e.g., north -> DIR.NORTH +DIR.from = function (name) { + var Dir=name.toUpperCase(); + if (DIRS.indexOf(Dir) == -1) return; + return {tag:'DIR.'+Dir} +} +// Create a valid lowercase name specifier from DIR (e.g. DIR.NORTH -> north) +DIR.to = function (dir) { + if ((dir.tag||dir).substr(0,4)!='DIR.') return; + return (dir.tag||dir).substr(4).toLowerCase(); +} + +DIR.isDir = function (o) { + return (o.tag||o).indexOf('DIR')==0; +} +DIR.opposite=opposite; +DIR.print = function (dir) { + if (!dir) return 'undefined'; + var name=(dir.tag||dir).substring(4); + switch (dir.tag||dir) { + case 'DIR.DELTA': + return name+'('+Comp.printf.list(dir.delta)+')'; + case 'DIR.RANGE': + return name+'('+dir.radius+')'; + case 'DIR.NODE': + return name+'('+dir.node+')'; + case 'DIR.IP': + return name+'('+(dir.ip==undefined?'*':dir.ip)+')'; + case 'DIR.PATH': + return name+'('+dir.path+')'; + case 'DIR.CAP': + return name+'('+dir.cao+')'; + default: return name + + } +}; + +/** Search a channel that is connected to node 'destnode' + * + */ +function lookup(node,destnode) { + var chan,path; + if (node.connections.ip && node.connections.ip.lookup) { + path=node.connections.ip.lookup(destnode); + if (path) return {chan:node.connections.ip,dest:path}; + } +} + +/** Move current agent to new node + * + */ +function move(dir) { + var node1=current.node, + chan=none, + dest, + stat, + path, + alive = function () {return 1}, + nokill=false, + name=DIR.to(dir), + msg; + switch (dir.tag||dir) { + case 'DIR.IP': + chan=node1.connections.ip; + dest=dir.ip; + break; + case 'DIR.DELTA': + current.process.dir=Comp.obj.copy(dir); + if (dir.delta[0]>0 && node1.connections.east && node1.connections.east.status()) + current.process.dir.delta[0]--,chan=node1.connections.east; + else if (dir.delta[0]<0 && node1.connections.west && node1.connections.west.status()) + current.process.dir.delta[0]++,chan=node1.connections.west; + else if (dir.delta[1]>0 && node1.connections.south && node1.connections.south.status()) + current.process.dir.delta[1]--,chan=node1.connections.south; + else if (dir.delta[1]<0 && node1.connections.north && node1.connections.north.status()) + current.process.dir.delta[1]++,chan=node1.connections.north; + else if (dir.delta[2]>0 && node1.connections.up && node1.connections.up.status()) + current.process.dir.delta[2]--,chan=node1.connections.up; + else if (dir.delta[2]<0 && node1.connections.down && node1.connections.down.status()) + current.process.dir.delta[2]++,chan=node1.connections.down; + break; + case 'DIR.NODE': + if (node1.connections.range && + node1.connections.range[dir.node] && + node1.connections.range[dir.node].status()) + chan=node1.connections.range[dir.node],dest=dir.node; + else { + // Find node name -> channel mapping + dest=lookup(node1,dir.node); + if (dest) chan=dest.chan,dest=dest.dest; + } + break; + case 'DIR.PATH': + // TODO + // if (!current.network) {current.error='No connection to path '+dir.path; throw 'MOVE'}; + if (Comp.obj.isArray(dir.path)) { + path=Comp.array.pop(dir.path); + } else path = dir.path; + chan=node1.connections.path; dest=path; + nokill=true; + break; + case 'DIR.CAP': + // TODO + if (!current.network) {current.error='No connection to server '+dir.cap; throw 'MOVE'}; + chan=node1.connections.dos; dest=Net.Parse.capability(dir.cap).cap; + nokill=true; + break; + default: + if (!name) { + current.error='ENOCHANNEL'; + throw 'MOVE'; + } + chan=node1.connections[name]; + } + // print(node1.connections); + // print(chan) + if (chan==none || !chan.status(dest)) { + current.error='No connection to direction '+DIR.print(dir); throw 'MOVE' + }; + + if (!current.process.back) current.process.back=Aios.DIR.opposite(dir); + node1.stats.migrate++; + + if (Aios.options.fastcopy && chan.virtual) msg=Aios.Code.toObject(current.process); + else msg=Aios.Code.ofCode(current.process,false); + + current.process.move=dir; + + /* NEWCOMM | context is current process !!!! */ + chan.send({agent:msg,to:dest,context:current.process}); + // kill or supend ghost agent + if (!nokill) current.process.kill=true; // discard process now + else current.process.suspended=true; // discard process after send op finished + //print(current.process.print()); + +} + +module.exports = { + agent:{ + move:move, + opposite:opposite, + DIR:DIR + }, + current:function (module) { current=module.current; Aios=module; }, + DIR:DIR, + DIRS:DIRS, + options:options +} +}; +BundleModuleCode['ml/ml']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2019 BSSLAB + ** $CREATED: 8-2-16 by sbosse. + ** $VERSION: 1.8.1 + ** + ** $INFO: + ** + ** JavaScript AIOS Machine Learning API + ** + ** type algorithm = {'dti','dt','knn','mlp','svm'} + ** + ** + ** dti: interval decision tree algorithm + ** ------------------------------------- + ** + ** General feature variable set: + ** + ** typeof @options = { + ** algorithm='dti', + ** data:{x1:number,x2:number,..,y:*} [] + ** target:string is e.g. 'y' + ** features: string [] is e.g. ['x1','x2',..] + ** eps:number is e.g. '5', + ** maxdepth:number, + ** } + ** + ** Or vector feature variables (i.e., features=[0,1,2,...n-1], target=n): + ** + ** typeof @options = { + ** algorithm='dti', + ** x:* [] [], + ** y:* [], + ** eps:number is e.g. '5', + ** maxdepth:number, + ** } + ** + ** knn: k-Nearest-Neighbour Algorithm + ** ---------------------------------- + ** + ** typeof @options = { + ** algorithm='knn', + ** x: number [][], + ** y: * [] + ** } + ** + ** mlp: multi layer perceptron Algorithm + ** ---------------------------------- + ** + ** typeof @options = { + ** algorithm='mlp', + ** x: number [][], + ** y: number [] [] | * [], + ** hidden_layers?:number [], + ** lr?:number, + ** epochs?:number, + ** labels?:string [], + ** features?: string [], + ** normalize?, + ** verbose?:number + ** } + ** + ** + ** text: text analysis (similarity checking) + ** ----------------------------------------- + ** classify(model,string) -> {match:number [0..1],string:string } + ** learn({algorithm:ML.TXT, data:string []]) -> model + ** test({algorithm:ML.TXT,string:string}|model,string) -> number [0..1] + ** similarity(string,string) -> number [0..1] + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var DT = Require('ml/dt'); +var DTI = Require('ml/dti'); +var KNN = Require('ml/knn'); +var SVM = Require('ml/svm'); +var MLP = Require('ml/mlp'); +var C45 = Require('ml/C45'); +var TXT = Require('ml/text'); +var current=none; +var Aios=none; + +var options = { + version: '1.8.1' +} + +// Some definitions +var ML = { + // Algorithms + C45:'c45', + DT:'dt', + DTI:'dti', + KNN:'knn', + KNN2:'knn2', + MLP:'mlp', + SVM:'svm', + TXT:'txt', + // Some Functions + EUCL:'euclidean', + PEAR:'pearson' +}; + +/** + * Computes Log with base-2 + * @private + */ +function log2(n) { + return Math.log(n) / Math.log(2); +} + +// Agent AIOS API +var ml = { + /** Classification: Apply sample data to learned model. + * Returns prediction result. + * + */ + classify: function (model,samples) { + var x; + switch (model.algorithm) { + + case ML.TXT: + // typeof options = {data: string []} + if (Comp.obj.isArray(samples)) + return samples.map(function (sample) { return TXT.classify(model,sample) }); + else + return TXT.classify(model,samples); + break; + + case ML.KNN: + return model.predict(samples); + break; + + case ML.SVM: + return model.predict(samples); + break; + + case ML.MLP: + if (Comp.obj.isMatrix(samples)) { + x=samples; + if (model.scale) + x=x.map(function (row) { return row.map(function (col) { + return -1+(col-model.scale.off)*model.scale.k })}); + return model.labels?model.predict(x).map(function (r) { + var o={}; + r.forEach(function (v,i) { o[model.labels[i]]=v }); + return o; + }):model.predict(x); + } else if (Comp.obj.isArray(samples)) { + x=samples; + if (model.scale) + x=x.map(function (col) { return (col-model.scale.off)*model.scale.k }); + return model.labels?model.predict([x]).map(function (r) { + var o={}; + r.forEach(function (v,i) { o[model.labels[i]]=v }); + return o; + })[0]:model.predict([x])[0]; + } else if (Comp.obj.isObj(samples) && model.features) { + x=model.features.map(function (f) { return samples[f] }); + if (model.scale) + x=x.map(function (col,i) { return model.scale.shift+ + (col-model.scale.off[i])*model.scale.k[i] }); +//console.log(x) + return model.labels?model.predict([x]).map(function (r) { + var o={}; + r.forEach(function (v,i) { o[model.labels[i]]=v }); + return o; + })[0]:model.predict([x])[0]; + } + break; + + case ML.C45: + // Sample row format: [x1,x2,..,xn] + if (Comp.obj.isMatrix(samples)) { + return samples.map(function (sample) { + return C45.classify(model,sample); + }); + } else if (Comp.obj.isArray(samples)) { + return C45.classify(model,samples); + } else if (Comp.obj.isObj(samples) && model.features) { + } + break; + + case ML.DTI: + default: + if (Comp.obj.isMatrix(samples)) + return samples.map(function (sample) { + return DTI.predict(model,sample) + }); + else + return DTI.predict(model,samples); + } + }, + column: function (data,key) { + return data.map(function (row) { + return row[key] + }) + }, + compact: function (model) { + switch (model.algorithm) { + case ML.DTI: + default: + return DTI.compactTree(model); + } + }, + depth: function (model) { + switch (model.algorithm) { + case ML.DTI: + return DTI.depth(model); + } + }, + // Information entropy of a set of values + entropy: function (vals) { + return C45.entropy(none,vals) + }, + // Information entropy of a value distribution + entropyN: function (counts) { + var e=0,sum=counts.reduce(function (a,b) { return a+b},0); + counts.forEach(function (n) { + var p=n/sum; + if (p==0) return; + e=e-p*C45.log2(p); + }); + return e; + }, + // Dependent entropy of a data column of a table with rows grouped by + // unique target column values + entropyDep: function (data,datacol,targetcol) { + var target = ml.column(data,targetcol); + var target_values = ml.unique(target); + var column = ml.column(data,datacol); + var column_values = ml.unique(column); + var e = 0; + column_values.forEach(function (v) { + var countv=0; + column.forEach(function (cv,i) { + if (cv==v) countv++; + }); + var occurences = target_values.map(function (t) { + var countd=0; + column.forEach(function (cv,i) { + if (target[i]==t && cv==v) countd++; + }); + return countd; + }); + // console.log(v,countv,column.length,occurences) + e = e + (countv/column.length)*ml.entropyN(occurences); + }); + return e; + }, + evaluate: function (model,target,samples) { + switch (model.algorithm) { + case ML.DTI: + default: + return DTI.evaluate(model,target,samples); + } + }, + /** Learning: Create a classification model from training data + * + */ + learn: function (options) { + var model,data,features,featureTypes,target,cols,n_ins,n_outs,x,y,scale,offset,shift; + if (options==_) options={}; + switch (options.algorithm) { + + case ML.TXT: + // typeof options = {data: string []} + model = TXT.create(options.data,{ + }); + model.algorithm=options.algorithm + return model; + break; + + case ML.KNN: + // typeof options = {x: number [][], y: * [], distance?:function|string,k?:number} + model = KNN.create(options.x,options.y,{ + distance:options.distance, + k:options.k + }); + model.algorithm=options.algorithm + return model; + break; + + case ML.SVM: + // typeof options = {x: number [][], y: {-1,1} []} + model = SVM.create({ + x:options.x, + y:options.y, + }); + model.algorithm=options.algorithm + model.train({ + C:options.C, + tol:options.tol, + max_passes:options.max_passes, + alpha_tol:options.alpha_tol, + kernel:options.kernel + }); + return model; + break; + + case ML.MLP: + // typeof options = {x: number [][], + // y: number [][] | * [], + // hidden_layers?:[],epochs?:number, + // labels?:string [], features?: string [], + // normalize?, bipolar?, verbose?} + // + // y and MLP(learn) requires [[p1,p2,..],[p1,p2,..],..] with 0>=p>=1 + // p:label probability + x=options.x; + if (Comp.obj.isMatrix(options.y)) y=options.y; + else if (Comp.obj.isArray(options.y) && options.labels) { + y=options.y.map(function (l1) { + return options.labels.map(function (l2) { + return l1==l2?1:0; + }); + }); + } else throw 'ML.learn.MLP: invalid options'; + if (options.normalize) { + var max=x[0].map(function (col) { return col}), + min=x[0].map(function (col) { return col}); + x.forEach(function (row) { row.forEach(function (col,i) { + max[i]=Math.max(max[i],col); + min[i]=Math.min(min[i],col) }) }); + shift=options.bipolar?-1:0; + scale=max.map(function (x,i) { return (shift?2:1)/((x-min[i])==0?1:x-min[i])}); + offset=min; + x=x.map(function (row) { return row.map(function (col,i) { return shift+(col-offset[i])*scale[i] }) }); + } +//console.log(x) + model = new MLP({ + input:x, + label:y, + n_ins:x[0].length, + n_outs:y[0].length, + hidden_layer_sizes:options.hidden_layers + }); + model.algorithm=options.algorithm; + model.labels=options.labels; + model.features=options.features; + model.scale=options.normalize?{k:scale,off:offset,shift:shift}:undefined; + model.set('log level',options.verbose||0); // 0 : nothing, 1 : info, 2 : warning. + + model.train({ + epochs : options.epochs||20000 + }); + return model; + break; + + case ML.C45: + // typeof options = {data: {}[], target:string, features: string []} | + // {data: [][], target?:string, features?: string []} | + // {x: number [][], y:[]} + var model = C45.create(); + if (options.x && options.y) { + data=options.x.slice().map(function (row,i) { row.push(options.y[i])}); + features=options.x[0].map(function (col,i) { return 'x'+i }); + featureTypes=options.x[0].map(function (col,i) { return 'number' }); + target='y'; + } else if (options.data && Comp.obj.isMatrix(options.data)) { + data=options.data; + features=options.features||options.data[0].slice(0,-1).map(function (col,i) { return String(i) }); + featureTypes=options.data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' }); + target=options.target||'y'; + } else if (options.data && Comp.obj.isArray(options.data) && Comp.obj.isObj(options.data[0]) && + options.target && options.features) { + rowNames=options.features.concat(options.target); + data=options.data.map(function (row) { rowNames.map(function (attr) { return row[attr] })}); + features=options.features; + featureTypes=data[0].slice(0,-1).map(function (col,i) { return typeof col == 'number'?'number':'category' }); + target=options.target; + } else throw 'ML.learn.C45: Invalid options'; +// console.log(data,target,features,featureTypes); + C45.train(model,{ + data: data, + target: target, + features: features, + featureTypes: featureTypes + }); + model.algorithm=options.algorithm + return model; + break; + + case ML.DTI: + default: + // typeof options = {data: {}[], target:string, features: string [], eps;number, maxdepth} | + // {x: number [][], y:[], eps;number, maxdepth} + if (options.eps==_) options.eps=0; + if (options.maxdepth==_) options.maxdepth=20; + if (options.data && options.target && options.features) + model = DTI.create(options); + else if (options.x && options.y) { + if (options.x.length != options.y.length) throw 'ML.learn.DTI: X and Y vector have different length'; + data=options.x.map(function (row,i) {row.push(options.y[i]); return row}); + features=Comp.array.init(data[0].length-1,function (i) { return String(i)}); + target=String(data[0].length-1); + console.log(data,features,target) + model = DTI.create({ + data:data, + features:features, + target:target, + eps:options.eps, + maxdepth:options.maxdepth + }); + } else throw 'ML.learn.DTI: Invalid options'; + model.algorithm=options.algorithm; + return model; + } + }, + print: function (model,indent,compact) { + switch (model.algorithm) { + case ML.DTI: + return DTI.print(model,indent,compact); + case ML.C45: + return C45.print(model,indent); + } + }, + // Only text module + similarity : TXT.similarity, + + // Check model consistency + test: function (model,samples) { + var x,y,res,p=0.0; + switch (model.algorithm) { + + case ML.TXT: + var model = model.string?{ data : [model.string] }:model; + if (Comp.obj.isArray(samples)) + return samples.map(function (sample) { + return TXT.classify(model,sample).match + }); + else + return TXT.classify(model,samples).match; + break; + + case ML.C45: + // Sample row format: [x1,x2,..,y] + if (Comp.obj.isMatrix(samples)) { + samples.forEach(function (sample) { + x=sample.slice(0,sample.length-1); + y=sample[sample.length-1]; + res= C45.classify(model,x); + if (res==y) p += 1; + }); + return p/samples.length; + } else if (Comp.obj.isArray(samples)) { + x=samples.slice(0,samples.length-1); + y=samples[samples.length-1]; + res = C45.classify(model,x); + return res==y?1.0:0.0 + } else if (Comp.obj.isObj(samples) && model.features) { + } + break; + + } + }, + + /** Return unique values of a set + * + */ + unique: C45.unique, + /** Update a learned model + * + */ + update: function (model,options) { + switch (model.algorithm||options.algorithm) { + + case ML.DTI: + default: + var model; + // typeof @options = {data: number [][], target:string, features: string [], eps?:number, maxdepth?:number} | + // {x: number [][], y:[], eps?:number, maxdepth?:number} + if (options.eps==_) options.eps=0; + if (options.maxdepth==_) options.maxdepth=20; + if (options.data && options.target && options.features) + model = DTI.update(model,options); + else if (options.x && options.y) { + if (options.x.length != options.y.length) throw 'ML.update.DTI: X and Y vector have different length'; + data=options.x.slice(); + data=data.map(function (row,i) {row.push(options.y[i]); return row}); + features=Comp.array.init(data[0].length-1,function (i) { return String(i)}); + target=String(data[0].length-1); + console.log(data,features,target) + model = DTI.update(model,{ + data:data, + features:features, + target:target, + eps:options.eps, + maxdepth:options.maxdepth + }); + } else throw 'ML.update.DTI: Invalid options'; + + model.algorithm=options.algorithm; + return model; + } + }, + ML:ML, +}; + + +module.exports = { + agent:ml, + classify:ml.classify, + column:ml.column, + compact:ml.compact, + depth:ml.depth, + entropy:ml.entropy, + entropyN:ml.entropyN, + entropyDep:ml.entropyDep, + evaluate:ml.evaluate, + learn:ml.learn, + print:ml.print, + test:ml.test, + unique:ml.unique, + update:ml.update, + ML:ML, + current:function (module) { current=module.current; Aios=module; } +} +}; +BundleModuleCode['ml/dt']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Ankit Kuwadekar, Stefan Bosse + ** $INITIAL: (C) 2014, Ankit Kuwadekar + ** $MODIFIED: (C) 2006-2018 bLAB by sbosse + ** $VERSION: 1.2.1 + ** + ** $INFO: + ** + ** ID3 Decision Tree Algorithm + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var current=none; +var Aios=none; + +/** + * Map of valid tree node types + * @constant + * @static + */ +var NODE_TYPES = { + RESULT: 'result', + FEATURE: 'feature', + FEATURE_VALUE: 'feature_value' +}; + +function depth(model) { + switch (model.type) { + case NODE_TYPES.RESULT: return 1; + case NODE_TYPES.FEATURE: + return 1+Comp.array.max(model.vals,function (val) { + return depth(val); + }); + case NODE_TYPES.FEATURE_VALUE: + return 1+depth(model.child); + } + return 0; +} + +function print(model) { + var line='',sep; + switch (model.type) { + case NODE_TYPES.RESULT: + return ' -> '+model.name; + case NODE_TYPES.FEATURE: + line='('+model.name+'?'; + sep=''; + Comp.array.iter(model.vals,function (v) { + line += sep+print(v); + sep=','; + }); + return line+')'; + case NODE_TYPES.FEATURE_VALUE: + return ' '+model.name+':'+print(model.child); + } + return 0; +} + +/** + * Predicts class for sample + */ +function predict(model,sample) { + var root = model; + while (root.type !== NODE_TYPES.RESULT) { + var attr = root.name; + var sampleVal = sample[attr]; + var childNode = Comp.array.find(root.vals, function(node) { + return node.name == sampleVal + }); + if (childNode){ + root = childNode.child; + } else { + root = root.vals[0].child; + } + } + return root.val; +}; + +/** + * Evalutes prediction accuracy on samples + */ +function evaluate(model,target,samples) { + + var total = 0; + var correct = 0; + + Comp.array.iter(samples, function(s) { + total++; + var pred = predict(model,s); + var actual = s[target]; + if (pred == actual) { + correct++; + } + }); + + return correct / total; +}; + +/** + * Creates a new tree + */ +function createTree(data, target, features, eps) { + var targets = Comp.array.unique(Comp.array.pluck(data, target)); + + // Aios.aios.log('createTree:'+targets.length); + if (targets.length == 1) { + return { + type: NODE_TYPES.RESULT, + val: targets[0], + name: targets[0], + alias: targets[0] + randomUUID() + }; + } + + if (features.length == 0) { + var topTarget = mostCommon(targets); + return { + type: NODE_TYPES.RESULT, + val: topTarget, + name: topTarget, + alias: topTarget + randomUUID() + }; + } + + var bestFeature = maxGain(data, target, features, eps); + var remainingFeatures = Comp.array.without(features, bestFeature); + var possibleValues = Comp.array.unique(Comp.array.pluck(data, bestFeature)); + + var node = { + name: bestFeature, + alias: bestFeature + randomUUID() + }; + + node.type = NODE_TYPES.FEATURE; + node.vals = Comp.array.map(possibleValues, function(v) { + var _newS = data.filter(function(x) { + return x[bestFeature] == v + }); + + var child_node = { + name: v, + alias: v + randomUUID(), + type: NODE_TYPES.FEATURE_VALUE + }; + + child_node.child = createTree(_newS, target, remainingFeatures, eps); + return child_node; + }); + + return node; +} + +/** + * Computes Max gain across features to determine best split + * @private + */ +function maxGain(data, target, features,eps) { + var gains=[]; + var maxgain= Comp.array.max(features, function(element) { + var g = gain(data, target, element, eps); + gains.push(element+':'+g); + return g; + }); + //Aios.aios.log(gains); + // console.log(gains); + return maxgain; +} + +/** + * Computes entropy of a list + * @private + */ +function entropy(vals) { + var uniqueVals = Comp.array.unique(vals); + var probs = uniqueVals.map(function(x) { + return prob(x, vals) + }); + + var logVals = probs.map(function(p) { + return -p * log2(p) + }); + + return logVals.reduce(function(a, b) { + return a + b + }, 0); +} + +function entropyEps(vals,eps) { + var uniqueVals = Comp.array.unique(vals); + var probs = uniqueVals.map(function(x) { + return probEps(x, vals, eps) + }); + + var logVals = probs.map(function(p) { + return -p * log2(p) + }); + + return logVals.reduce(function(a, b) { + return a + b + }, 0); +} + +/** + * Computes gain + * @private + */ +function gain(data, target, feature) { + var attrVals = Comp.array.unique(Comp.array.pluck(data, feature)); + var setEntropy = entropy(Comp.array.pluck(data, target)); + var setSize = data.length; + + var entropies = attrVals.map(function(n) { + var subset = data.filter(function(x) { + return x[feature] === n + }); + + return (subset.length / setSize) * entropy(Comp.array.pluck(subset, target)); + }); + + // var entropyData = entropyV(Comp.array.pluck(data, feature),eps); + // console.log('Feat '+feature+':'+entropyData); + var sumOfEntropies = entropies.reduce(function(a, b) { + return a + b + }, 0); + return setEntropy - sumOfEntropies; +} + +function uniqueEps(data,eps) { + var result=[]; + data.forEach(function (x) { + var found; + if (!results.length) results.push(x); + else { + results.forEach(function (y) { + if (found) return; + found = Math.abs(x-y)= (value-eps)) && (element <= (value+eps)); + }); + + var numOccurrences = occurrences.length; + var numElements = list.length; + return numOccurrences / numElements; +} + +/** + * Computes Log with base-2 + * @private + */ +function log2(n) { + return Math.log(n) / Math.log(2); +} + +/** + * Finds element with highest occurrence in a list + * @private + */ +function mostCommon(list) { + var elementFrequencyMap = {}; + var largestFrequency = -1; + var mostCommonElement = null; + + list.forEach(function(element) { + var elementFrequency = (elementFrequencyMap[element] || 0) + 1; + elementFrequencyMap[element] = elementFrequency; + + if (largestFrequency < elementFrequency) { + mostCommonElement = element; + largestFrequency = elementFrequency; + } + }); + + return mostCommonElement; +} + +/** + * Generates random UUID + * @private + */ +function randomUUID() { + return "_r" + Math.random().toString(32).slice(2); +} + +module.exports = { + NODE_TYPES:NODE_TYPES, + createTree:createTree, + depth:depth, + entropy:entropy, + evaluate:evaluate, + predict:predict, + print:print, + current:function (module) { current=module.current; Aios=module;} +}; + +}; +BundleModuleCode['ml/dti']=function (module,exports,global,process){ +/** + ** ============================== + ** O O O OOOO + ** O O O O O O + ** O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2018 bLAB + ** $CREATED: 03-03-16 by sbosse. + ** $VERSION: 1.4.2 + ** + ** $INFO: + ** + ** Interval Decision Tree Learner + ** + ** Modified ID3-based Decision Tree Algorithm that wraps all data with 2-eps intervals and uses + ** interval instead single value arithmetic for entropy calculation and feature selection. + ** The classification bases on a nearest-neighbourhood look-up of best matching results. + ** + ** Two different algorithms are supported: + ** + ** 1. Static (using learn), the DTI learner using attribute selection based on entropy. + ** The training data must be available in advance. + ** 2. Dynamic (using update), the DTI learrner using attribute selection based on significance. + ** The training data is applied sequentielly (stream learning) updating the model. + ** + ** Though in principle the both algrotihms can be mixed (first static, then dynamic updating), + ** the resulting model will have poor classification quality. Either use static or only dynamic + ** (stream) learning. + ** + ** + ** $ENDOFINFO + */ +var Io = Require('com/io'); +var Comp = Require('com/compat'); +var current=none; +var Aios=none; +var min = Comp.pervasives.min; +var max = Comp.pervasives.max; + +/** + * Map of valid tree node types + * @constant + * @static + */ +var NODE_TYPES = { + RESULT: 'result', + FEATURE: 'feature', + FEATURE_VALUE: 'feature_value' +}; + + +function Result(key) { + return { + type:NODE_TYPES.RESULT, + name:key + } +} + +function Feature(name,vals) { + return { + type:NODE_TYPES.FEATURE, + name:name, + vals:vals + } +} + +// A value can be a scalar or a range {a,b} object +function Value(val,child) { + return { + type:NODE_TYPES.FEATURE_VALUE, + val:val, + child:child + } +} + +/** Add a new training set with optional data set merging and value interval expansion. + * + */ +function add_training_set(data,set,merge) { + if (merge) { + // Merge a data set with an existing for a specific key; create value ranges + } else + data.push(set); +} + + +/** + * Computes Log with base-2 + * @private + */ +function log2(n) { + return Math.log(n) / Math.log(2); +} + + + + +function results(model) { + var line='',sep; + if (!model) return ''; + switch (model.type) { + case NODE_TYPES.RESULT: + return model.name; + case NODE_TYPES.FEATURE: + sep=''; + line=''; + Comp.array.iter(model.vals,function (v) { + line += sep+results(v); + sep=','; + }); + return line; + case NODE_TYPES.FEATURE_VALUE: + return results(model.child); + } + return 'result?'; +} + + +/** + * Finds element with highest occurrence in a list + * @private + */ +function mostCommon(list) { + var elementFrequencyMap = {}; + var largestFrequency = -1; + var mostCommonElement = null; + + list.forEach(function(element) { + var elementFrequency = (elementFrequencyMap[element] || 0) + 1; + elementFrequencyMap[element] = elementFrequency; + + if (largestFrequency < elementFrequency) { + mostCommonElement = element; + largestFrequency = elementFrequency; + } + }); + + return mostCommonElement; +} + +function addVal(v1,v2) { + if (v1.a!=undefined) { + if (v2.a!=undefined) return {a:v1.a+v2.a,b:v1.b+v2.b}; + else return {a:v1.a+v2,b:v2.b+v2}; + } else if (v2.a!=undefined) return {a:v2.a+v1,b:v2.b+v1}; + else return v1+v2; +} + +function lowerBound(v) { + if (v.a==undefined) return v; else return v.a; +} + +function upperBound(v) { + if (v.b==undefined) return v; else return v.b; +} + +function equal(v1,v2) { + return (v1==v2 || + (upperBound(v1) == upperBound(v2) && + (lowerBound(v1) == lowerBound(v2)))) +} + +function overlap(v1,v2) { + return (upperBound(v1) >= lowerBound(v2) && upperBound(v1) <= upperBound(v2)) || + (upperBound(v2) >= lowerBound(v1) && upperBound(v2) <= upperBound(v1)) +} + +function containsVal(vl,v) { + for (var i in vl) { + var v2=vl[i]; + if (overlap(v,v2)) return true; + } + return false; +} + +function centerVal(v) { + if (v.a==undefined) return v; else return (v.a+v.b)/2; +} + +function distanceVal (v1,v2) { + return Math.abs(centerVal(v1)-centerVal(v2)); +} + +function Bounds(vl,v) { + if (vl.length==0) return {a:v,b:v}; + else if (v==undefined) return {a:Min(vl),b:Max(vl)}; + else return {a:Min([Min(vl),v]),b:Max([Max(vl),v])}; +} + +function Min(vals) { + var min=none; + Comp.array.iter(vals, function (val) { + if (min==none) min=(val.a==undefined?val:val.a); + else min=val.a==undefined?(valmax?val:max):(val.b>max?val.a:max)); + }); + return max; +} + +// Return interval of a value x with a<=x_center-eps, b>=x_center+eps +function epsVal(x,eps) { + if (x.a == undefined) return {a:x-eps,b:x+eps}; + else if ((x.b-x.a) < 2*eps) return {a:centerVal(x)-eps,b:centerVal(x)+eps}; + else return x; +} +/** Filter out unique values that are spaced at least by eps + * + */ +function uniqueEps(data,eps) { + var results=[]; + Comp.array.iter(data,function (x) { + var found; + if (!results.length) results.push(x); + else { + Comp.array.iter(results,function (y,i) { + if (found) return; + found = Math.abs(centerVal(x)-centerVal(y)) lowerBound(_vals[i+1].val)) { + if (_vals[i].val.b) _vals[i].val.b=lowerBound(_vals[i+1].val)-1; + else _vals[i+1].val.a=upperBound(_vals[i].val)+1; + } + } + } + } + + model.vals=_vals; + return model; + break; + case NODE_TYPES.FEATURE_VALUE: + return model; + break; + } +} + + + +/** Creates a new tree from training data (data) + * + * data is {x1:v1,x2:v2,..,y:vn} [] + * target is classification key name + * features is ['x1','x2,',..] w/o target variable + * eps is interval applied to all data values + * + */ +function createTree(data, target, features, options) { + var _newS,child_node,bounds; + + var targets = Comp.array.unique(Comp.array.pluck(data, target)); + // console.log(targets) + if (options.maxdepth==undefined) options.maxdepth=1; + if (options.maxdepth==0) return Result('-'); + // console.log(data); + // console.log(features); + + //Aios.aios.log('createTree:'+targets.length); + //try {Aios.aios.CP();} catch (e) {throw 'DTI.createTree: '+options.maxdepth }; + if (Aios) Aios.aios.CP(); + if (targets.length == 1) return Result(targets[0]); + + if (features.length == 0) { + var topTarget = mostCommon(targets); + return Result(topTarget) + } + var bestFeatures = getBestFeatures(data, target, features, options.eps); + var bestFeature = bestFeatures[0]; + + var remainingFeatures = Comp.array.filtermap(bestFeatures,function (feat) { + if (feat.name!=bestFeature.name) return feat.name; + else return none; + }); +/* + var possibleValues = Comp.array.sort(Comp.array.pluck(data, bestFeature.name), function (x,y) { + if (upperBound(x) < lowerBound(y)) return -1; else return 1; // increasing value order + }); +*/ + var possibleValues = getPossibleVals(data,bestFeature.name); + + var vals=[]; + + //console.log(bestFeatures); + //console.log(possibleValues); + var partitions=partitionVals(possibleValues,options.eps); + // Aios.aios.log(partitions); + //console.log(bestFeatures); + //console.log(possibleValues); + if (partitions.length==1) { + // no further 2*eps separation possible, find best feature by largest distance + // resort best feature list with respect to value deviation + bestFeatures.sort(function (ef1,ef2) { + if (ef1.d > ef2.d) return -1; else return 1; + }); + bestFeature = bestFeatures[0]; + possibleValues = getPossibleVals(data,bestFeature.name); + Comp.array.iter(mergeVals(possibleValues),function (val,i) { + + _newS = data.filter(function(x) { + // console.log(x[bestFeature.name],val,overlap(val,x[bestFeature.name])) + + return overlap(val,x[bestFeature.name]); + }); + child_node = Value(val); + options.maxdepth--; + child_node.child = createTree(_newS, target, remainingFeatures, options); + //console.log(_newS); + vals.push(child_node); + }) + + } else Comp.array.iter(partitions,function (partition,i) { + + _newS = data.filter(function(x) { + // console.log(x[bestFeature.name],v,overlap(x[bestFeature.name],v)) + return containsVal(partition,x[bestFeature.name]); + }); + bounds = Bounds(partition); + child_node = Value(options.eps==0?{a:bounds.a,b:bounds.b}:{a:bounds.a-options.eps,b:bounds.b+options.eps}); + options.maxdepth--; + child_node.child = createTree(_newS, target, remainingFeatures, options); + //console.log(_newS); + vals.push(child_node); + }); + + return Feature(bestFeature.name,vals); +} + +/** Return the depth of the tree + * + */ +function depth(model) { + switch (model.type) { + case NODE_TYPES.RESULT: return 0; + case NODE_TYPES.FEATURE: + return 1+Comp.array.max(model.vals,function (val) { + return depth(val); + }); + case NODE_TYPES.FEATURE_VALUE: + return depth(model.child); + } + return 0; +} + +/** Computes entropy of a list with 2-epsilon intervals + * + */ + +function entropyEps(vals,eps) { + // TODO: overlapping value intervals + var uniqueVals = Comp.array.unique(vals); + var probs = uniqueVals.map(function(x) { + return probEps(x, vals, eps) + }); + + var logVals = probs.map(function(p) { + return -p * log2(p) + }); + + return logVals.reduce(function(a, b) { + return a + b + }, 0); +} + +function entropyEps2(vals,eps) { + // TODO: overlapping value intervals + var uniqueVals = uniqueEps(vals,eps); + var probs = uniqueVals.map(function(x) { + return probEps2(x, vals, eps) + }); + + var logVals = probs.map(function(p) { + return -p * log2(p) + }); + + return logVals.reduce(function(a, b) { + return a + b + }, 0); +} + + +function getBestFeatures(data,target,features,eps) { + var bestfeatures=[]; + function deviation(vals) { + var n = vals.length; + var mu=Comp.array.sum(vals,function (val) { + return (lowerBound(val)+upperBound(val))/2; + })/n; + var dev=Comp.array.sum(vals,function (val) { + return Math.pow(((lowerBound(val)+upperBound(val))/2)-mu,2); + })/n; + return dev; + } + for (var feature in features) { + if (features[feature]==undefined) throw 'DTI.getBestFeatures: invalid feature vector'; + var vals=Comp.array.pluck(data, features[feature]).map(function (val) {return val==undefined?0:val}); + var e = entropyEps(vals,eps); + var d = deviation(vals); + var min = Min(vals); + var max = Max(vals); + bestfeatures.push({e:e,d:d,range:{a:min,b:max},name:features[feature]}); + } + bestfeatures.sort(function (ef1,ef2) { + if (ef1.e > ef2.e) return -1; else return 1; + }); + return bestfeatures; +} + +/** Find in one data set the most significant feature variable (i.e., with highest value) + */ +function getSignificantFeature(data,features) { + var f,sig; + for (f in features) { + if (sig==undefined || sig.val < data[features[f]]) sig={name:features[f],val:data[features[f]]}; + } + return sig; +} + +function getPossibleVals(data,feature) { + return Comp.array.sort(Comp.array.pluck(data, feature), function (x,y) { + if (upperBound(x) < lowerBound(y)) return -1; else return 1; // increasing value order + }); +} + +/** Merge values and intervals + */ +function mergeVals(vals) { + var _vals, + merged,i,j; + for (i in vals) { + var vi = vals[i]; + if (!_vals) _vals=[vi]; + else { + // Find overlapping values and merge + merged=false; + loopj: for (j in _vals) { + var vj = _vals[j]; + if (equal(vi,vj)) { + merged=true; + break loopj; + } + else if (overlap(vi,vj)) { + merged=true; + _vals[j]={a:Min([vi,vj]),b:Max([vi,vj])}; + break loopj; + } + } + if (!merged) _vals.push(vi); + } + } + //Aios.aios.log(_vals); + return _vals||[]; +} + +/** + * Predicts class for sample + */ +function nearestVal(vals,sample,fun) { + var best=none; + for (var v in vals) { + var d=fun?distanceVal(fun(vals[v]),sample):distanceVal(vals[v],sample); + if (best==none) + best={v:vals[v],d:d}; + else if (best.d > d) + best={v:vals[v],d:d}; + } + if (best) return best.v; + else return none; +} + + +/** Parttition an ordered set of values + * Each partition of values has at least 2*eps distance to the next partition. + * + */ +function partitionVals(vals,eps) { + var last=none; + var partitions=[]; + var partition=[]; + for(var i in vals) { + var val0=vals[i]; + var val1=vals[i-1]; + + if (val1==undefined) partition.push(val0); + else if ( upperBound(val0) < upperBound(addVal(val1,2*eps))) partition.push(val0); + else { + partitions.push(partition); + partition=[val0]; + } + } + if (partition.length>0) partitions.push(partition); + return partitions; +} + +/** Make a predicition with sample data + * + */ +function predict(model,sample) { + var root = model; + while (root && root.type !== NODE_TYPES.RESULT) { + var attr = root.name; + var sampleVal = sample[attr]; + var childNode = nearestVal(root.vals,sampleVal,function (node) { + return node.val; + }); + + if (childNode){ + root = childNode.child; + } else { + root = none; + } + } + if (root) return root.name||root.val; + else return none; +}; + +/** Print the tree + * + */ +function print(model,indent, compact) { + var line='',sep; + if (compact) return results(model); + if (indent==undefined) indent=0; + if (!model) return ''; + var sp = function () {return Comp.string.create(indent);}; + switch (model.type) { + case NODE_TYPES.RESULT: + return sp()+'-> '+model.name+NL; + case NODE_TYPES.FEATURE: + line=sp()+'$'+model.name+'?'+NL; + Comp.array.iter(model.vals,function (v) { + line += print(v,indent+2); + }); + return line; + case NODE_TYPES.FEATURE_VALUE: + line=sp()+'='+(model.val.a==undefined?model.val:'['+model.val.a+','+model.val.b+']')+NL; + return line+print(model.child,indent+2); + } + return 'model?'; +} + +/** + * Computes probability of of a given value existing in a given list + * with additional 2*epsilon interval, only applicable to numerical values. + */ +function probEps(value, list, eps) { + // TODO: ranges + var occurrences = Comp.array.filter(list, function(element) { + return (element >= (value-eps)) && (element <= (value+eps)); + }); + + var numOccurrences = occurrences.length; + var numElements = list.length; + return numOccurrences / numElements; +} + +function probEps2(value, list, eps) { + // TODO: ranges + var occurrences = Comp.array.filter(list, function(element) { + return overlap(epsVal(value), epsVal(element)); + }); + + var numOccurrences = occurrences.length; + var numElements = list.length; + return numOccurrences / numElements; +} + +/** Incremental update of the model with new training set(s). Can be executed with an empty model. + * The current tree can be week for a new training set (new target). + * This can result in a classification of the new target with insignificant variables. + * Therefore, the last tree node must be exapnded with an additional strong (most significant) + * variable of the new data set (but it is still a heuristic for future updates). + */ +function updateTree(model,data, target, features, options) { + var eps = options.eps, + maxdepth = options.maxdepth, + verbose = options.verbose; + var featuresINm={}, // All current tree feature variables and their value interval + results=[], // All current tree result leafs + set,i,v,feature,remainingFeatures,exists,sigFeature; + // 1. Analysis of existing model + + var analyze = function (model,feature) { + var feature2; + if (!model) return; + switch (model.type) { + case NODE_TYPES.RESULT: + if (!Comp.array.contains(results,model.name)) results.push(model.name); + break; + case NODE_TYPES.FEATURE: + feature2={name:model.name}; + if (!featuresINm[model.name]) featuresINm[model.name]=feature2; + Comp.array.iter(model.vals,function (v) { analyze(v,featuresINm[model.name]) }); + break; + case NODE_TYPES.FEATURE_VALUE: + if (!feature.val) feature.val={ + a:(model.val.a==undefined?model.val:model.val.a), + b:(model.val.a==undefined?model.val:model.val.b) + }; else { + feature.val.a=min(feature.val.a, + (model.val.a==undefined?model.val:model.val.a)); + feature.val.b=max(feature.val.b, + (model.val.a==undefined?model.val:model.val.b)); + } + analyze(model.child); + break; + } + } + + + analyze(model); + // console.log(featuresINm); + // console.log(results); + + exists=Comp.array.contains(results,data[target]); + + + // 2a. Empty model, add first training set with two significant feature variable nodes + function init(set) { + set=data[i]; + sigFeature1=getSignificantFeature(set,features); + remainingFeatures=Comp.array.filter(features,function (feat) { + return sigFeature1.name!=feat; + }); + sigFeature2=getSignificantFeature(set,remainingFeatures); + + featuresINm[sigFeature1.name]={name:sigFeature1.name, + val:{a:sigFeature1.val-eps,b:sigFeature1.val+eps}}; + featuresINm[sigFeature2.name]={name:sigFeature2.name, + val:{a:sigFeature2.val-eps,b:sigFeature2.val+eps}}; + results.push(set[target]); + model=Feature(sigFeature1.name,[ + Value({a:set[sigFeature1.name]-eps,b:set[sigFeature1.name]+eps}, + Feature(sigFeature2.name,[ + Value({a:sigFeature2.val-eps,b:sigFeature2.val+eps}, + Result(set[target])) + ]))]); + return model; + } + + remainingFeatures=Comp.array.filter(features,function (feat) { + return !featuresINm[feat]; + }); + + // 2b. Update the tree with the new training set + var update = function (model,set,feature) { + var feature2,p; + if (!model) return; + switch (model.type) { + + case NODE_TYPES.RESULT: + if (model.name != set[target] && verbose) + console.log('Cannot insert new training set '+set[target]+' in tree. No more separating variables!'); + break; + + case NODE_TYPES.FEATURE: + // console.log(set[target]+': '+ model.name+'='+set[model.name]); + if (set[model.name]<(featuresINm[model.name].val.a-eps) || + set[model.name]>(featuresINm[model.name].val.b+eps)) { + // add new training set; done + // the current decision tree can be week, thus add another strong variable node, too! + sigFeature=getSignificantFeature(set,remainingFeatures); + featuresINm[sigFeature.name]={name:sigFeature.name, + val:{a:sigFeature.val-eps,b:sigFeature.val+eps}}; + featuresINm[model.name].val.a=min(featuresINm[model.name].val.a,set[model.name]-eps); + featuresINm[model.name].val.b=max(featuresINm[model.name].val.b,set[model.name]+eps); + if (!Comp.array.contains(results,set[target])) results.push(set[target]); + + model.vals.push(Value({a:set[model.name]-eps,b:set[model.name]+eps}, + Feature(sigFeature.name,[ + Value({a:sigFeature.val-eps,b:sigFeature.val+eps}, + Result(set[target])) + ]))); + model.vals=Comp.array.sort(model.vals,function (v1,v2) {return (lowerBound(v1.val), 2012 + * @author Martin Kleppe , 2012 + * @author Ubilabs http://ubilabs.net, 2012 + * @license MIT License + */ + +function Node(obj, dimension, parent) { + this.obj = obj; + this.left = null; + this.right = null; + this.parent = parent; + this.dimension = dimension; +} + +/* KDTree + * + */ + +function KDTree(points, metric) { + if (!(this instanceof KDTree)) return new KDTree(points, metric); + // If points is not an array, assume we're loading a pre-built tree + if (!Array.isArray(points)) { + this.dimensions = points.dimensions; + this.root = points; + restoreParent(this.root); + } else { + this.dimensions = new Array(points[0].length); + for (var i = 0; i < this.dimensions.length; i++) { + this.dimensions[i] = i; + } + this.root = buildTree(points, 0, null, this.dimensions); + } + this.metric = metric; +} + +// Convert to a JSON serializable structure; this just requires removing +// the `parent` property +KDTree.prototype.toJSON = function() { + var result = toJSONImpl(this.root, true); + result.dimensions = this.dimensions; + return result; +} + +KDTree.prototype.nearest = function(point, maxNodes, maxDistance) { + var metric = this.metric; + var dimensions = this.dimensions; + var i; + + var bestNodes = new BinaryHeap( + function (e) { + return -e[1]; + } + ); + + function nearestSearch(node) { + var dimension = dimensions[node.dimension]; + var ownDistance = metric(point, node.obj); + var linearPoint = {}; + var bestChild, + linearDistance, + otherChild, + i; + + function saveNode(node, distance) { + bestNodes.push([node, distance]); + if (bestNodes.size() > maxNodes) { + bestNodes.pop(); + } + } + + for (i = 0; i < dimensions.length; i += 1) { + if (i === node.dimension) { + linearPoint[dimensions[i]] = point[dimensions[i]]; + } else { + linearPoint[dimensions[i]] = node.obj[dimensions[i]]; + } + } + + linearDistance = metric(linearPoint, node.obj); + + if (node.right === null && node.left === null) { + if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { + saveNode(node, ownDistance); + } + return; + } + + if (node.right === null) { + bestChild = node.left; + } else if (node.left === null) { + bestChild = node.right; + } else { + if (point[dimension] < node.obj[dimension]) { + bestChild = node.left; + } else { + bestChild = node.right; + } + } + + nearestSearch(bestChild); + + if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) { + saveNode(node, ownDistance); + } + + if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) { + if (bestChild === node.left) { + otherChild = node.right; + } else { + otherChild = node.left; + } + if (otherChild !== null) { + nearestSearch(otherChild); + } + } + } + + if (maxDistance) { + for (i = 0; i < maxNodes; i += 1) { + bestNodes.push([null, maxDistance]); + } + } + + if (this.root) { + nearestSearch(this.root); + } + + var result = []; + for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) { + if (bestNodes.content[i][0]) { + result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]); + } + } + return result; +} + +function toJSONImpl(src) { + var dest = new Node(src.obj, src.dimension, null); + if (src.left) dest.left = toJSONImpl(src.left); + if (src.right) dest.right = toJSONImpl(src.right); + return dest; +} + +function buildTree(points, depth, parent, dimensions) { + var dim = depth % dimensions.length; + + if (points.length === 0) { + return null; + } + if (points.length === 1) { + return new Node(points[0], dim, parent); + } + + points.sort(function (a, b) { a[dimensions[dim]] - b[dimensions[dim]]}); + + var median = Math.floor(points.length / 2); + var node = new Node(points[median], dim, parent); + node.left = buildTree(points.slice(0, median), depth + 1, node, dimensions); + node.right = buildTree(points.slice(median + 1), depth + 1, node, dimensions); + + return node; +} + +function restoreParent(root) { + if (root.left) { + root.left.parent = root; + restoreParent(root.left); + } + + if (root.right) { + root.right.parent = root; + restoreParent(root.right); + } +} +/** BinaryHeap + * + */ + +// Binary heap implementation from: +// http://eloquentjavascript.net/appendix2.html +function BinaryHeap (scoreFunction) { + if (!(this instanceof BinaryHeap)) return new BinaryHeap (scoreFunction); + this.content = []; + this.scoreFunction = scoreFunction; +} + +BinaryHeap.prototype.push = function(element) { + // Add the new element to the end of the array. + this.content.push(element); + // Allow it to bubble up. + this.bubbleUp(this.content.length - 1); +} + +BinaryHeap.prototype.pop = function() { + // Store the first element so we can return it later. + var result = this.content[0]; + // Get the element at the end of the array. + var end = this.content.pop(); + // If there are any elements left, put the end element at the + // start, and let it sink down. + if (this.content.length > 0) { + this.content[0] = end; + this.sinkDown(0); + } + return result; +} + +BinaryHeap.prototype.peek = function() { + return this.content[0]; +} + +BinaryHeap.prototype.size = function() { + return this.content.length; +} + +BinaryHeap.prototype.bubbleUp = function(n) { + // Fetch the element that has to be moved. + var element = this.content[n]; + // When at 0, an element can not go up any further. + while (n > 0) { + // Compute the parent element's index, and fetch it. + var parentN = Math.floor((n + 1) / 2) - 1; + var parent = this.content[parentN]; + // Swap the elements if the parent is greater. + if (this.scoreFunction(element) < this.scoreFunction(parent)) { + this.content[parentN] = element; + this.content[n] = parent; + // Update 'n' to continue at the new position. + n = parentN; + } else { // Found a parent that is less, no need to move it further. + break; + } + } +} + +BinaryHeap.prototype.sinkDown = function(n) { + // Look up the target element and its score. + var length = this.content.length; + var element = this.content[n]; + var elemScore = this.scoreFunction(element); + + while (true) { + // Compute the indices of the child elements. + var child2N = (n + 1) * 2; + var child1N = child2N - 1; + // This is used to store the new position of the element, + // if any. + var swap = null; + // If the first child exists (is inside the array)... + if (child1N < length) { + // Look it up and compute its score. + var child1 = this.content[child1N]; + var child1Score = this.scoreFunction(child1); + // If the score is less than our element's, we need to swap. + if (child1Score < elemScore) { + swap = child1N; + } + } + // Do the same checks for the other child. + if (child2N < length) { + var child2 = this.content[child2N]; + var child2Score = this.scoreFunction(child2); + if (child2Score < (swap === null ? elemScore : child1Score)) { + swap = child2N; + } + } + + // If the element needs to be moved, swap it, and continue. + if (swap !== null) { + this.content[n] = this.content[swap]; + this.content[swap] = element; + n = swap; + } else { + // Otherwise, we are done. + break; + } + } +} + +/** KNN + * + */ + +/** + * @param {Array} dataset + * @param {Array} labels + * @param {object} options + * @param {number} [options.k=numberOfClasses + 1] - Number of neighbors to classify. + * @param {function} [options.distance=euclideanDistance] - Distance function that takes two parameters. + */ +function KNN(dataset, labels, options) { + if (!options) options={}; + if (!(this instanceof KNN)) return new KNN(dataset, labels, options); + if (dataset === true) { + var model = labels; + this.kdTree = new KDTree(model.kdTree, options); + this.k = model.k; + this.classes = new Set(model.classes); + this.isEuclidean = model.isEuclidean; + return; + } + + var classes = new Set(labels); + + var distance = getDistanceFunction(options.distance), + k = options.k||classes.size + 1; + + var points = new Array(dataset.length); + for (var i = 0; i < points.length; ++i) { + points[i] = dataset[i].slice(); + } + + for (i = 0; i < labels.length; ++i) { + points[i].push(labels[i]); + } + + this.kdTree = new KDTree(points, distance); + this.k = k; + this.classes = classes; + this.isEuclidean = distance === euclideanDistance; +} + +/** + * Create a new KNN instance with the given model. + * @param {object} model + * @param {function} distance=euclideanDistance - distance function must be provided if the model wasn't trained with euclidean distance. + * @return {KNN} + */ +function load(model, distance) { + if (!distance) distance = euclideanDistance; + if (model.name !== 'KNN') { + throw new Error('invalid model: ' + model.name); + } + if (!model.isEuclidean && distance === euclideanDistance) { + throw new Error('a custom distance function was used to create the model. Please provide it again'); + } + if (model.isEuclidean && distance !== euclideanDistance) { + throw new Error('the model was created with the default distance function. Do not load it with another one'); + } + return new KNN(true, model, distance); +} + +/** + * Return a JSON containing the kd-tree model. + * @return {object} JSON KNN model. + */ +KNN.prototype.toJSON = function() { + return { + name: 'KNN', + kdTree: this.kdTree, + k: this.k, + classes: Array.from(this.classes), + isEuclidean: this.isEuclidean + }; +} + +/** + * Predicts the output given the matrix to predict. + * @param {Array} dataset + * @return {Array} predictions + */ +KNN.prototype.predict = function(dataset) { + if (Array.isArray(dataset)) { + if (typeof dataset[0] === 'number') { + return getSinglePrediction(this, dataset); + } else if (Array.isArray(dataset[0]) && typeof dataset[0][0] === 'number') { + var predictions = new Array(dataset.length); + for (var i = 0; i < dataset.length; i++) { + predictions[i] = getSinglePrediction(this, dataset[i]); + } + return predictions; + } + } + throw new TypeError('dataset to predict must be an array or a matrix'); +} + +function getSinglePrediction(knn, currentCase) { + var nearestPoints = knn.kdTree.nearest(currentCase, knn.k); + var pointsPerClass = {}; + var predictedClass = -1; + var maxPoints = -1; + var lastElement = nearestPoints[0][0].length - 1; + //for (var element of knn.classes) { + // pointsPerClass[element] = 0; + //} + forof(knn.classes,function (element) { + pointsPerClass[element] = 0; + }); + for (var i = 0; i < nearestPoints.length; ++i) { + var currentClass = nearestPoints[i][0][lastElement]; + var currentPoints = ++pointsPerClass[currentClass]; + if (currentPoints > maxPoints) { + predictedClass = currentClass; + maxPoints = currentPoints; + } + } + + return predictedClass; +} + + + +/** Create a KNN + * + * typeof @options = {data:number [] [],result: * []} + * + */ +var KNN2 = function (options) { + if (!(this instanceof KNN)) return new KNN(options); + this.data = options.data; + this.result = options.result; +} + +/** Make a prediction + * + * typeof @options = {x,k?,weightf?:function,distance?:function} + */ +KNN2.prototype.predict = function(options) { + var x = options.x; + var k = options.k || 3; + var weightf = getWeightedFunction(options.weightf); + var distance = getDistanceFunction(options.distance); + var distanceList = []; + var i; + for(i=0; i= 1 || s == 0); + + s = Math.sqrt( (-2 * Math.log(s)) / s ); + return v1 * s; +} + +m.shape = function(mat) { + var row = mat.length; + var col = mat[0].length; + return [row,col]; +}; + +m.addVec = function(vec1, vec2) { + if(vec1.length === vec2.length) { + var result = []; + var i; + for(i=0;i max) + max = vec[i]; + } + return max; +} + +m.minMat = function(mat) { + var min = mat[0][0]; + var i = mat.length; + while (i--) { + for(var j=0;j tol && self.alphas[i] >0)) { + + // Randomly selects j (i != j) + var j = math.randInt(0,self.x.length-1); + if(i==j) j = (j+1) % self.x.length; + + var E_j = self.f(self.x[j]) - self.y[j]; + var alpha_i_old = self.alphas[i], alpha_j_old = self.alphas[j]; + + // Compute L,H + var L,H; + if(self.y[i] !== self.y[j]) { + L = Math.max(0, self.alphas[j] - self.alphas[i]); + H = Math.min(C, C + self.alphas[j] - self.alphas[i]); + } else { + L = Math.max(0, self.alphas[j] + self.alphas[i] - C); + H = Math.min(C, self.alphas[j] + self.alphas[i]); + } + + if(L === H) + continue; + + // Compute ETA + var ETA = 2 * self.kernel(self.x[i],self.x[j]) - self.kernel(self.x[i],self.x[i]) - self.kernel(self.x[j],self.x[j]); + if(ETA >= 0) + continue; + + // Clip new value to alpha_j + self.alphas[j] -= 1.*self.y[j] * (E_i - E_j) / ETA; + if(self.alphas[j] > H) + self.alphas[j] = H; + else if(self.alphas[j] < L) + self.alphas[j] = L; + + if(Math.abs(self.alphas[j] - alpha_j_old) < alphatol) + continue; + + // Clip new value to alpha_i + self.alphas[i] += self.y[i] * self.y[j] * (alpha_j_old - self.alphas[j]); + + // update b + var b1 = self.b - E_i - self.y[i] * (self.alphas[i] - alpha_i_old) * self.kernel(self.x[i],self.x[i]) + - self.y[j] * (self.alphas[j] - alpha_j_old) * self.kernel(self.x[i],self.x[j]); + var b2 = self.b - E_j - self.y[i] * (self.alphas[i] - alpha_i_old) * self.kernel(self.x[i],self.x[j]) + - self.y[j] * (self.alphas[j] - alpha_j_old) * self.kernel(self.x[j],self.x[j]); + + if(0 < self.alphas[i] && self.alphas[i] < C) + self.b = b1; + else if(0 < self.alphas[j] && self.alphas[j] < C) + self.b = b2; + else + self.b = (b1+b2)/2.0; + + numChangedAlphas ++ ; + } // end-if + } // end-for + if(numChangedAlphas == 0) + passes++; + else + passes = 0; + } +} + +SVM.prototype.predict = function(x) { + var self = this; + if(self.f(x) >= 0) + return 1; + else + return -1; +} + +SVM.prototype.f = function(x) { + var self = this; + var f = 0, j; + for(j=0; j