Skip to content

Commit

Permalink
Let module load asynchronously
Browse files Browse the repository at this point in the history
  • Loading branch information
yishn committed May 13, 2018
1 parent 5218dc0 commit 8029057
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 41 deletions.
4 changes: 1 addition & 3 deletions .gitignore
@@ -1,6 +1,4 @@
.nyc_output/
target/
node_modules/
deadstones.js
deadstones_bg.js
deadstones_bg.wasm
wasm/
2 changes: 2 additions & 0 deletions .npmignore
Expand Up @@ -2,3 +2,5 @@
target/
node_modules/
*.rs
wasm/deadstones.js
wasm/deadstones_bg.js
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -10,10 +10,10 @@ Use npm to install:
$ npm install @sabaki/deadstones
~~~

Then require it as follows:
This module loads asynchronously, please require it as follows:

~~~js
const deadstones = require('@sabaki/deadstones');
const deadstones = await require('@sabaki/deadstones');
~~~

## Building
Expand Down
22 changes: 9 additions & 13 deletions src/main.js → js/main.js
Expand Up @@ -14,36 +14,32 @@ const parseGrid = (values, width) => {
.map((_, y) => values.slice(y * width, (y + 1) * width))
}

module.exports = function(lib) {
let result = {}

result.guess = function(data, {finished = false, iterations = 100} = {}) {
module.exports = require('./wasm').then(lib => ({
guess: function(data, {finished = false, iterations = 100} = {}) {
let [newData, width] = parseBoard(data)
let indices = lib.guess(newData, width, finished, iterations, Date.now())

return parseVertices(indices, width)
}
},

result.playTillEnd = function(data, sign) {
playTillEnd: function(data, sign) {
let [newData, width] = parseBoard(data)
let values = lib.play_till_end(newData, width, sign, Date.now())

return parseGrid(values, width)
}
},

result.getProbabilityMap = function(data, iterations) {
getProbabilityMap: function(data, iterations) {
let [newData, width] = parseBoard(data)
let values = lib.get_probability_map(newData, width, iterations, Date.now())

return parseGrid(values, width)
}
},

result.getFloatingStones = function(data) {
getFloatingStones: function(data) {
let [newData, width] = parseBoard(data)
let indices = lib.get_floating_stones(newData, width)

return parseVertices(indices, width)
}

return result
}
}))
138 changes: 138 additions & 0 deletions js/wasm.js
@@ -0,0 +1,138 @@
const make = wasm => {
let result = {}

let cachegetUint8Memory = null;
function getUint8Memory() {
if (cachegetUint8Memory === null ||
cachegetUint8Memory.buffer !== wasm.memory.buffer)
cachegetUint8Memory = new Uint8Array(wasm.memory.buffer);
return cachegetUint8Memory;
}

let cachegetUint64Memory = null;
function getUint64Memory() {
if (cachegetUint64Memory === null ||
cachegetUint64Memory.buffer !== wasm.memory.buffer)
cachegetUint64Memory = new BigUint64Array(wasm.memory.buffer);
return cachegetUint64Memory;
}

function passArray8ToWasm(arg) {
const ptr = wasm.__wbindgen_malloc(arg.length * 1);
getUint8Memory().set(arg, ptr / 1);
return [ptr, arg.length];
}

let cachegetUint32Memory = null;
function getUint32Memory() {
if (cachegetUint32Memory === null ||
cachegetUint32Memory.buffer !== wasm.memory.buffer)
cachegetUint32Memory = new Uint32Array(wasm.memory.buffer);
return cachegetUint32Memory;
}

function getArrayU32FromWasm(ptr, len) {
return getUint32Memory().subarray(ptr / 4, ptr / 4 + len);
}

let cachedGlobalArgumentPtr = null;
function globalArgumentPtr() {
if (cachedGlobalArgumentPtr === null)
cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr();
return cachedGlobalArgumentPtr;
}

result.guess = function(arg0, arg1, arg2, arg3, arg4) {
const [ptr0, len0] = passArray8ToWasm(arg0);
const retptr = globalArgumentPtr();
wasm.guess(retptr, ptr0, len0, arg1, arg2 ? 1 : 0, arg3, arg4);
const mem = getUint32Memory();
const ptr = mem[retptr / 4];
const len = mem[retptr / 4 + 1];
const realRet = getArrayU32FromWasm(ptr, len);
wasm.__wbindgen_free(ptr, len * 4);
return realRet;
};

let cachegetFloat32Memory = null;
function getFloat32Memory() {
if (cachegetFloat32Memory === null ||
cachegetFloat32Memory.buffer !== wasm.memory.buffer)
cachegetFloat32Memory = new Float32Array(wasm.memory.buffer);
return cachegetFloat32Memory;
}

function getArrayF32FromWasm(ptr, len) {
return getFloat32Memory().subarray(ptr / 4, ptr / 4 + len);
}

result.get_probability_map = function(arg0, arg1, arg2, arg3) {
const [ptr0, len0] = passArray8ToWasm(arg0);
const retptr = globalArgumentPtr();
wasm.get_probability_map(retptr, ptr0, len0, arg1, arg2, arg3);
const mem = getUint32Memory();
const ptr = mem[retptr / 4];
const len = mem[retptr / 4 + 1];
const realRet = getArrayF32FromWasm(ptr, len);
wasm.__wbindgen_free(ptr, len * 4);
return realRet;
};

let cachegetInt8Memory = null;
function getInt8Memory() {
if (cachegetInt8Memory === null ||
cachegetInt8Memory.buffer !== wasm.memory.buffer)
cachegetInt8Memory = new Int8Array(wasm.memory.buffer);
return cachegetInt8Memory;
}

function getArrayI8FromWasm(ptr, len) {
return getInt8Memory().subarray(ptr / 1, ptr / 1 + len);
}

result.play_till_end = function(arg0, arg1, arg2, arg3) {
const [ptr0, len0] = passArray8ToWasm(arg0);
const retptr = globalArgumentPtr();
wasm.play_till_end(retptr, ptr0, len0, arg1, arg2, arg3);
const mem = getUint32Memory();
const ptr = mem[retptr / 4];
const len = mem[retptr / 4 + 1];
const realRet = getArrayI8FromWasm(ptr, len);
wasm.__wbindgen_free(ptr, len * 1);
return realRet;
};

result.get_floating_stones = function(arg0, arg1) {
const [ptr0, len0] = passArray8ToWasm(arg0);
const retptr = globalArgumentPtr();
wasm.get_floating_stones(retptr, ptr0, len0, arg1);
const mem = getUint32Memory();
const ptr = mem[retptr / 4];
const len = mem[retptr / 4 + 1];
const realRet = getArrayU32FromWasm(ptr, len);
wasm.__wbindgen_free(ptr, len * 4);
return realRet;
};

const TextDecoder2 = typeof TextDecoder === 'undefined' ? require('util').TextDecoder : TextDecoder;

let cachedDecoder = new TextDecoder2('utf-8');

function getStringFromWasm(ptr, len) {
return cachedDecoder.decode(getUint8Memory().subarray(ptr, ptr + len));
}

result.__wbindgen_throw = function(ptr, len) {
throw new Error(getStringFromWasm(ptr, len));
};

return result
}

module.exports = Promise.resolve().then(() => {
const {join} = require('path');
const {readFileSync} = require('fs');
const buffer = readFileSync(join(__dirname, '..', 'wasm', 'deadstones_bg.wasm'));

return WebAssembly.instantiate(buffer, {'./deadstones': make()});
}).then(module => make(module.instance.exports))
1 change: 0 additions & 1 deletion nodejs/index.js

This file was deleted.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions package.json
@@ -1,8 +1,8 @@
{
"name": "@sabaki/deadstones",
"version": "2.0.0-beta2",
"version": "2.0.0-beta3",
"description": "Simple Monte Carlo functions to determine dead stones on a Go board.",
"main": "./nodejs/index.js",
"main": "./js/main.js",
"repository": {
"type": "git",
"url": "git+https://github.com/SabakiHQ/deadstones.git"
Expand All @@ -24,15 +24,14 @@
"homepage": "https://github.com/SabakiHQ/deadstones",
"scripts": {
"compile": "cargo build --target wasm32-unknown-unknown --release",
"pack:nodejs": "wasm-bindgen ./target/wasm32-unknown-unknown/release/deadstones.wasm --out-dir ./nodejs --no-typescript --nodejs",
"pack:webpack": "wasm-bindgen ./target/wasm32-unknown-unknown/release/deadstones.wasm --out-dir ./webpack --no-typescript",
"pack": "npm run pack:nodejs && npm run pack:webpack",
"pack": "mkdirp ./wasm && wasm-bindgen ./target/wasm32-unknown-unknown/release/deadstones.wasm --out-dir ./wasm --no-typescript --nodejs",
"build": "npm run compile && npm run pack",
"snip": "wasm-snip --snip-rust-fmt-code --snip-rust-panicking-code ./nodejs/deadstones_bg.wasm -o ./nodejs/deadstones_bg.wasm && wasm-snip --snip-rust-fmt-code --snip-rust-panicking-code ./webpack/deadstones_bg.wasm -o ./webpack/deadstones_bg.wasm ",
"snip": "wasm-snip --snip-rust-fmt-code --snip-rust-panicking-code ./wasm/deadstones_bg.wasm -o ./wasm/deadstones_bg.wasm",
"prepublishOnly": "npm run build && npm run snip",
"test": "tap tests/*.test.js"
},
"devDependencies": {
"mkdirp": "^0.5.1",
"tap": "^11.1.5"
}
}
13 changes: 9 additions & 4 deletions tests/getFloatingStones.test.js
@@ -1,16 +1,19 @@
const t = require('tap')
const deadstones = require('..')
const data = require('./data')

t.test('should not mutate board data', t => {
t.test('should not mutate board data', async t => {
let deadstones = await require('..')
let boardJSON = JSON.stringify(data.finished)

deadstones.getFloatingStones(data.finished)

t.assert(JSON.stringify(data.finished), boardJSON)
t.end()
})

t.test('finished game', t => {
t.test('finished game', async t => {
let deadstones = await require('..')

t.deepEqual(deadstones.getFloatingStones(data.finished).sort(), [
[10, 5], [13, 13], [13, 14], [14, 7], [18, 13],
[2, 13], [2, 14], [5, 13], [6, 13], [9, 3], [9, 5]
Expand All @@ -19,7 +22,9 @@ t.test('finished game', t => {
t.end()
})

t.test('unfinished game', t => {
t.test('unfinished game', async t => {
let deadstones = await require('..')

t.deepEqual(deadstones.getFloatingStones(data.unfinished), [[0, 1]])
t.end()
})
7 changes: 4 additions & 3 deletions tests/getProbabilityMap.test.js
@@ -1,16 +1,17 @@
const t = require('tap')
const deadstones = require('..')
const data = require('./data')

t.test('should not mutate board data', t => {
t.test('should not mutate board data', async t => {
let deadstones = await require('..')
let boardJSON = JSON.stringify(data.finished)
deadstones.getProbabilityMap(data.finished, 50)

t.assert(JSON.stringify(data.finished), boardJSON)
t.end()
})

t.test('should contain values between -1 and 1', t => {
t.test('should contain values between -1 and 1', async t => {
let deadstones = await require('..')
let map = deadstones.getProbabilityMap(data.unfinished, 50)

t.assert(map.every(row => row.every(x => -1 <= x && x <= 1)))
Expand Down
11 changes: 7 additions & 4 deletions tests/guess.test.js
@@ -1,23 +1,26 @@
const t = require('tap')
const deadstones = require('..')
const data = require('./data')

t.test('should not mutate board data', t => {
t.test('should not mutate board data', async t => {
let deadstones = await require('..')
let boardJSON = JSON.stringify(data.finished)

deadstones.guess(data.finished, {finished: true})

t.assert(JSON.stringify(data.finished), boardJSON)
t.end()
})

t.test('should detect some dead stones from unfinished games', t => {
t.test('should detect some dead stones from unfinished games', async t => {
let deadstones = await require('..')
let dead = deadstones.guess(data.unfinished)

t.assert(dead.length > 0)
t.end()
})

t.test('should detect floating stones from finished games', t => {
t.test('should detect floating stones from finished games', async t => {
let deadstones = await require('..')
let dead = deadstones.guess(data.finished, {finished: true})
let floating = deadstones.getFloatingStones(data.finished)

Expand Down
8 changes: 5 additions & 3 deletions tests/playTillEnd.test.js
@@ -1,16 +1,18 @@
const t = require('tap')
const deadstones = require('..')
const data = require('./data')

t.test('should not mutate board data', t => {
t.test('should not mutate board data', async t => {
let boardJSON = JSON.stringify(data.finished)
let deadstones = await require('..')

deadstones.playTillEnd(data.finished, -1)

t.assert(JSON.stringify(data.finished), boardJSON)
t.end()
})

t.test('should not have empty vertices', t => {
t.test('should not have empty vertices', async t => {
let deadstones = await require('..')
let finished = deadstones.playTillEnd(data.unfinished, 1)

t.assert(finished.every(row => row.every(x => x === 1 || x === -1)))
Expand Down
1 change: 0 additions & 1 deletion webpack/index.js

This file was deleted.

0 comments on commit 8029057

Please sign in to comment.