(module
(func $add (param $lhs i32) (param $rhs i32) (result i32)
local.get $lhs
local.get $rhs
i32.add)
(export "add" (func $add))
)
┌────────┬──────────────────────────────────────────────────┬────────────────┐
│00000000│ 00 61 73 6d 01 00 00 00 01 07 01 60 02 7f 7f 01 │0asm•000•••`••••│
│00000010│ 7f 03 02 01 00 07 07 01 03 61 64 64 00 00 0a 09 │••••0••••add00__│
│00000020│ 01 07 00 20 00 20 01 6a 0b │••0 0 •j• │
└────────┴──────────────────────────────────────────────────┴────────────────┘
June 2018: 85.57% of installed browsers (87.21% of desktop browsers)
Compile to asm.js by a JavaScript polyfill
Yes
https://developer.mozilla.org/en-US/docs/WebAssembly
https://caniuse.com/#feat=wasm
Rust + Webassembly
> cargo generate --git https://github.com/rustwasm/wasm-pack-template
🤷 Project Name: wasm-game-of-life
🔧 Creating project called `wasm-game-of-life`...
✨ Done! New project created /Users/karuna/job/wasm-game-of-life
> cd wasm-game-of-life
> wasm-pack build
...
> ls pkg
README.md package.json wasm_game_of_life.d.ts wasm_game_of_life.js wasm_game_of_life_bg.d.ts wasm_game_of_life_bg.wasm
> npm init wasm-app www
> cd www
> npm install
{
// ...
"dependencies": {
"wasm-game-of-life": "file:../pkg", // Add this line!
// ...
}
}
www/package.json
import * as wasm from "wasm-game-of-life";
wasm.greet();
www/index.js
> npm install
> npm run start
open http://localhost:8080/
Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
Any live cell with two or three live neighbours lives on to the next generation.
Any live cell with more than three live neighbours dies, as if by overpopulation.
Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
[dependencies]
...
js-sys = "0.3"
fixedbitset = "0.1.9"
Cargo.toml
mod utils;
extern crate fixedbitset;
use fixedbitset::FixedBitSet;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Cell {
Dead = 0,
Alive = 1,
}
#[wasm_bindgen]
#[derive(Default)]
pub struct Universe {
width: u32,
height: u32,
cells: FixedBitSet,
}
impl Universe {
fn get_index(&self, row: u32, column: u32) -> usize {
(row * self.width + column) as usize
}
fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
let mut count = 0;
for delta_row in [self.height - 1, 0, 1].iter().cloned() {
for delta_col in [self.width - 1, 0, 1].iter().cloned() {
if delta_row == 0 && delta_col == 0 {
continue;
}
let neighbor_row = (row + delta_row) % self.height;
let neighbor_col = (column + delta_col) % self.width;
let idx = self.get_index(neighbor_row, neighbor_col);
count += self.cells[idx] as u8;
}
}
count
}
}
#[wasm_bindgen]
impl Universe {
pub fn new() -> Universe {
let width = 64;
let height = 64;
let size = (width * height) as usize;
let mut cells = FixedBitSet::with_capacity(size);
for i in 0..size {
cells.set(i, js_sys::Math::random() < 0.5);
}
Universe {
width,
height,
cells,
}
}
pub fn tick(&mut self) {
let mut next = self.cells.clone();
for row in 0..self.height {
for col in 0..self.width {
let idx = self.get_index(row, col);
let cell = self.cells[idx];
let live_neighbors = self.live_neighbor_count(row, col);
next.set(
idx,
match (cell, live_neighbors) {
(true, x) if x < 2 => false,
(true, 2) | (true, 3) => true,
(true, x) if x > 3 => false,
(false, 3) => true,
(otherwise, _) => otherwise,
},
);
}
}
self.cells = next;
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn cells(&self) -> *const u32 {
self.cells.as_slice().as_ptr()
}
}
src/lib.rs
import { Universe, Cell } from "wasm-game-of-life";
import { memory } from "wasm-game-of-life/wasm_game_of_life_bg";
const CELL_SIZE = 5; // px
const GRID_COLOR = "#CCCCCC";
const DEAD_COLOR = "#FFFFFF";
const ALIVE_COLOR = "#000000";
const universe = Universe.new();
const width = universe.width();
const height = universe.height();
const canvas = document.getElementById("game-of-life-canvas");
canvas.height = (CELL_SIZE + 1) * height + 1;
canvas.width = (CELL_SIZE + 1) * width + 1;
const ctx = canvas.getContext('2d');
const renderLoop = () => {
universe.tick();
drawGrid();
drawCells();
requestAnimationFrame(renderLoop);
};
const drawGrid = () => {
ctx.beginPath();
ctx.strokeStyle = GRID_COLOR;
// Vertical lines.
for (let i = 0; i <= width; i++) {
ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0);
ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1);
}
// Horizontal lines.
for (let j = 0; j <= height; j++) {
ctx.moveTo(0, j * (CELL_SIZE + 1) + 1);
ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1);
}
ctx.stroke();
};
const getIndex = (row, column) => {
return row * width + column;
};
const bitIsSet = (n, arr) => {
const byte = Math.floor(n / 8);
const mask = 1 << (n % 8);
return (arr[byte] & mask) === mask;
};
const drawCells = () => {
const cellsPtr = universe.cells();
const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8);
ctx.beginPath();
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = getIndex(row, col);
ctx.fillStyle = bitIsSet(idx, cells)
? ALIVE_COLOR
: DEAD_COLOR;
ctx.fillRect(
col * (CELL_SIZE + 1) + 1,
row * (CELL_SIZE + 1) + 1,
CELL_SIZE,
CELL_SIZE
);
}
}
ctx.stroke();
};
drawGrid();
drawCells();
requestAnimationFrame(renderLoop);
www/index.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello wasm-pack!</title>
</head>
<body>
<canvas id="game-of-life-canvas"></canvas>
<noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
<script src="./bootstrap.js"></script>
</body>
</html>
www/index.html
$> wasm-pack build
$> cd www
$> npm install
$> npm run start
open http://localhost:8080
other execution runtime: wasmer, wasmtime, lucet
> wapm install php
> cat phpinfo.php
> wapm run php phpinfo.php
#[no_mangle]
pub extern fn sum(x: i32, y: i32) -> i32 {
x + y
}
simple.rs
simple.rs
package main
import (
"fmt"
wasm "github.com/wasmerio/go-ext-wasm/wasmer"
)
func main() {
bytes, _ := wasm.ReadBytes("simple.wasm")
instance, _ := wasm.NewInstance(bytes)
defer instance.Close()
sum := instance.Exports["sum"]
result, _ := sum(5, 37)
fmt.Println(result) // 42!
}
The End
終
Tamat