multimath
Version:
Core to create fast image math in WebAssembly and JS.
158 lines (111 loc) • 4.24 kB
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;