forked from nearform/node-cephes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcephes-wrapper.js
142 lines (122 loc) · 4.08 KB
/
cephes-wrapper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
const TOTAL_STACK = 1024 * 1024; // 1MB
const TOTAL_MEMORY = 2 * 1024 * 1024; // 1MB
const WASM_PAGE_SIZE = 64 * 1024; // Defined in WebAssembly specs
if (typeof self !== 'undefined' && !self.Buffer) {
self.Buffer = require('buffer').Buffer;
}
const WASM_CODE = Buffer.from(require('./cephes.wasm.base64.json'), 'base64');
class CephesWrapper {
constructor(sync) {
// Initialize the runtime's memory
this._wasmMemory = new WebAssembly.Memory({
'initial': TOTAL_MEMORY / WASM_PAGE_SIZE,
'maximum': TOTAL_MEMORY / WASM_PAGE_SIZE
});
this._HEAP8 = new Int8Array(this._wasmMemory.buffer);
this._HEAP16 = new Int16Array(this._wasmMemory.buffer);
this._HEAP32 = new Int32Array(this._wasmMemory.buffer);
this._HEAPF32 = new Float32Array(this._wasmMemory.buffer);
this._HEAPF64 = new Float64Array(this._wasmMemory.buffer);
// Compile and export program
if (sync) {
// compile synchronously
const program = this._compileSync();
this._exportProgram(program);
// create a dummy compile promise
this.compiled = Promise.resolve();
} else {
// create a singleton compile promise
this.compiled = this._compileAsync()
.then((program) => this._exportProgram(program));
}
}
_AsciiToString(ptr) {
let str = '';
while (1) {
const ch = this._HEAP8[((ptr++)>>0)];
if (ch === 0) return str;
str += String.fromCharCode(ch);
}
}
_mtherr(name /* char* */, code /* int */) {
// from mtherr.c
let codemsg = '';
switch (code) {
case 1: codemsg = 'argument domain error'; break;
case 2: codemsg = 'function singularity'; break;
case 3: codemsg = 'overflow range error'; break;
case 4: codemsg = 'underflow range error'; break;
case 5: codemsg = 'total loss of precision'; break;
case 6: codemsg = 'partial loss of precision'; break;
case 33: codemsg = 'Unix domain error code'; break;
case 34: codemsg = 'Unix range error code'; break;
default: codemsg = 'unknown error';
}
const fnname = this._AsciiToString(name);
const message = 'cephes reports "' + codemsg + '" in ' + fnname;
// Restore stack to the STACKTOP before throwing. This only works because
// all the exported cephes functions are plain functions.
this.stackRestore(0);
if (code == 1) {
throw new RangeError(message);
} else {
throw new Error(message);
}
}
_wasmImports() {
return {
'env': {
// cephes error handler
"_mtherr": this._mtherr.bind(this),
// memory
"memory": this._wasmMemory,
"STACKTOP": 0,
"STACK_MAX": TOTAL_STACK
}
};
}
_compileSync() {
return new WebAssembly.Instance(
new WebAssembly.Module(WASM_CODE),
this._wasmImports()
);
}
_compileAsync() {
return WebAssembly.instantiate(
WASM_CODE,
this._wasmImports()
).then((results) => results.instance);
}
_exportProgram(program) {
// export cephes functions
for (const key of Object.keys(program.exports)) {
if (key.startsWith('_cephes_')) {
this[key] = program.exports[key];
}
}
// export special stack functions
this.stackAlloc = program.exports.stackAlloc;
this.stackRestore = program.exports.stackRestore;
this.stackSave = program.exports.stackSave;
}
// export helper functions
getValue(ptr, type) {
type = type || 'i8';
if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit
switch(type) {
case 'i1': return this._HEAP8[((ptr)>>0)];
case 'i8': return this._HEAP8[((ptr)>>0)];
case 'i16': return this._HEAP16[((ptr)>>1)];
case 'i32': return this._HEAP32[((ptr)>>2)];
case 'i64': return this._HEAP32[((ptr)>>2)];
case 'float': return this._HEAPF32[((ptr)>>2)];
case 'double': return this._HEAPF64[((ptr)>>3)];
default: throw new Error('invalid type for getValue: ' + type);
}
return null;
}
writeArrayToMemory(array, buffer) {
this._HEAP8.set(array, buffer);
}
}
module.exports = CephesWrapper;