157 lines
4.1 KiB
JavaScript
157 lines
4.1 KiB
JavaScript
'use strict';
|
|
|
|
|
|
var assign = require('object-assign');
|
|
var base64decode = require('./lib/base64decode');
|
|
var hasWebAssembly = require('./lib/wa_detect');
|
|
|
|
|
|
var DEFAULT_OPTIONS = {
|
|
js: true,
|
|
wasm: true
|
|
};
|
|
|
|
|
|
function MultiMath(options) {
|
|
if (!(this instanceof MultiMath)) return new MultiMath(options);
|
|
|
|
var opts = assign({}, DEFAULT_OPTIONS, options || {});
|
|
|
|
this.options = opts;
|
|
|
|
this.__cache = {};
|
|
|
|
this.__init_promise = null;
|
|
this.__modules = opts.modules || {};
|
|
this.__memory = null;
|
|
this.__wasm = {};
|
|
|
|
this.__isLE = ((new Uint32Array((new Uint8Array([ 1, 0, 0, 0 ])).buffer))[0] === 1);
|
|
|
|
if (!this.options.js && !this.options.wasm) {
|
|
throw new Error('mathlib: at least "js" or "wasm" should be enabled');
|
|
}
|
|
}
|
|
|
|
|
|
MultiMath.prototype.has_wasm = hasWebAssembly;
|
|
|
|
|
|
MultiMath.prototype.use = function (module) {
|
|
this.__modules[module.name] = module;
|
|
|
|
// Pin the best possible implementation
|
|
if (this.options.wasm && this.has_wasm() && module.wasm_fn) {
|
|
this[module.name] = module.wasm_fn;
|
|
} else {
|
|
this[module.name] = module.fn;
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
MultiMath.prototype.init = function () {
|
|
if (this.__init_promise) return this.__init_promise;
|
|
|
|
if (!this.options.js && this.options.wasm && !this.has_wasm()) {
|
|
return Promise.reject(new Error('mathlib: only "wasm" was enabled, but it\'s not supported'));
|
|
}
|
|
|
|
var self = this;
|
|
|
|
this.__init_promise = Promise.all(Object.keys(self.__modules).map(function (name) {
|
|
var module = self.__modules[name];
|
|
|
|
if (!self.options.wasm || !self.has_wasm() || !module.wasm_fn) return null;
|
|
|
|
// If already compiled - exit
|
|
if (self.__wasm[name]) return null;
|
|
|
|
// Compile wasm source
|
|
return WebAssembly.compile(self.__base64decode(module.wasm_src))
|
|
.then(function (m) { self.__wasm[name] = m; });
|
|
}))
|
|
.then(function () { return self; });
|
|
|
|
return this.__init_promise;
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Methods below are for internal use from plugins
|
|
|
|
|
|
// Simple decode base64 to typed array. Useful to load embedded webassembly
|
|
// code. You probably don't need to call this method directly.
|
|
//
|
|
MultiMath.prototype.__base64decode = base64decode;
|
|
|
|
|
|
// Increase current memory to include specified number of bytes. Do nothing if
|
|
// size is already ok. You probably don't need to call this method directly,
|
|
// because it will be invoked from `.__instance()`.
|
|
//
|
|
MultiMath.prototype.__reallocate = function mem_grow_to(bytes) {
|
|
if (!this.__memory) {
|
|
this.__memory = new WebAssembly.Memory({
|
|
initial: Math.ceil(bytes / (64 * 1024))
|
|
});
|
|
return this.__memory;
|
|
}
|
|
|
|
var mem_size = this.__memory.buffer.byteLength;
|
|
|
|
if (mem_size < bytes) {
|
|
this.__memory.grow(Math.ceil((bytes - mem_size) / (64 * 1024)));
|
|
}
|
|
|
|
return this.__memory;
|
|
};
|
|
|
|
|
|
// Returns instantinated webassembly item by name, with specified memory size
|
|
// and environment.
|
|
// - use cache if available
|
|
// - do sync module init, if async init was not called earlier
|
|
// - allocate memory if not enougth
|
|
// - can export functions to webassembly via "env_extra",
|
|
// for example, { exp: Math.exp }
|
|
//
|
|
MultiMath.prototype.__instance = function instance(name, memsize, env_extra) {
|
|
if (memsize) this.__reallocate(memsize);
|
|
|
|
// If .init() was not called, do sync compile
|
|
if (!this.__wasm[name]) {
|
|
var module = this.__modules[name];
|
|
this.__wasm[name] = new WebAssembly.Module(this.__base64decode(module.wasm_src));
|
|
}
|
|
|
|
if (!this.__cache[name]) {
|
|
var env_base = {
|
|
memoryBase: 0,
|
|
memory: this.__memory,
|
|
tableBase: 0,
|
|
table: new WebAssembly.Table({ initial: 0, element: 'anyfunc' })
|
|
};
|
|
|
|
this.__cache[name] = new WebAssembly.Instance(this.__wasm[name], {
|
|
env: assign(env_base, env_extra || {})
|
|
});
|
|
}
|
|
|
|
return this.__cache[name];
|
|
};
|
|
|
|
|
|
// Helper to calculate memory aligh for pointers. Webassembly does not require
|
|
// this, but you may wish to experiment. Default base = 8;
|
|
//
|
|
MultiMath.prototype.__align = function align(number, base) {
|
|
base = base || 8;
|
|
var reminder = number % base;
|
|
return number + (reminder ? base - reminder : 0);
|
|
};
|
|
|
|
|
|
module.exports = MultiMath;
|