cephes-wrapper.js 4.0 KB

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