Compare commits
3 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d63a60d1ef | |
|
|
2badf9214c | |
|
|
509605f7aa |
|
|
@ -0,0 +1,25 @@
|
|||
<!doctype html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Show me the answer</title>
|
||||
</head>
|
||||
<div>
|
||||
待编码数据: <input type="text" id="inputText" placeholder="输入待编码数据" value="">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
编码结果:<input type="text" id="outputText" placeholder="编码结果" value="">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
解码结果:<input type="text" id="decodeText" placeholder="解码结果" value="">
|
||||
</div>
|
||||
<body>
|
||||
<script type="text/javascript" src="./wa.js">
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.Handle("/", http.FileServer(http.Dir("./")))
|
||||
http.ListenAndServe(":2333", nil)
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
(() => {
|
||||
class WaApp {
|
||||
constructor() {
|
||||
this._inst = null;
|
||||
this._wa_print_buf = "";
|
||||
}
|
||||
|
||||
init(url) {
|
||||
let app = this;
|
||||
let importsObject = {
|
||||
wasi_snapshot_preview1: new function () {
|
||||
this.args_get = () => { return 0; }
|
||||
this.args_sizes_get = () => { return 0; }
|
||||
|
||||
this.clock_res_get = () => { return 0; }
|
||||
this.clock_time_get = () => { return 0; }
|
||||
this.environ_get = () => { return 0; }
|
||||
this.environ_sizes_get = () => { return 0; }
|
||||
|
||||
this.fd_advise = () => { return 0; }
|
||||
this.fd_allocate = () => { return 0; }
|
||||
this.fd_close = () => { return 0; }
|
||||
this.fd_datasync = () => { return 0; }
|
||||
this.fd_fdstat_get = () => { return 0; }
|
||||
this.fd_fdstat_set_flags = () => { return 0; }
|
||||
this.fd_fdstat_set_rights = () => { return 0; }
|
||||
this.fd_filestat_get = () => { return 0; }
|
||||
this.fd_filestat_set_size = () => { return 0; }
|
||||
this.fd_filestat_set_times = () => { return 0; }
|
||||
this.fd_pread = () => { return 0; }
|
||||
this.fd_prestat_get = () => { return 0; }
|
||||
this.fd_prestat_dir_name = () => { return 0; }
|
||||
this.fd_pwrite = () => { return 0; }
|
||||
this.fd_read = () => { return 0; }
|
||||
this.fd_readdir = () => { return 0; }
|
||||
this.fd_renumber = () => { return 0; }
|
||||
this.fd_seek = () => { return 0; }
|
||||
this.fd_sync = () => { return 0; }
|
||||
this.fd_tell = () => { return 0; }
|
||||
this.fd_write = () => { return 0; }
|
||||
this.path_create_directory = () => { return 0; }
|
||||
this.path_filestat_get = () => { return 0; }
|
||||
this.path_filestat_set_times = () => { return 0; }
|
||||
this.path_link = () => { return 0; }
|
||||
this.path_open = () => { return 0; }
|
||||
this.path_readlink = () => { return 0; }
|
||||
this.path_remove_directory = () => { return 0; }
|
||||
this.path_rename = () => { return 0; }
|
||||
this.path_symlink = () => { return 0; }
|
||||
this.path_unlink_file = () => { return 0; }
|
||||
|
||||
this.poll_oneoff = () => { return 0; }
|
||||
this.proc_exit = () => { return 0; }
|
||||
this.random_get = () => { return 0; }
|
||||
this.sched_yield = () => { return 0; }
|
||||
this.sock_accept = () => { return 0; }
|
||||
this.sock_recv = () => { return 0; }
|
||||
this.sock_send = () => { return 0; }
|
||||
this.sock_shutdown = () => { return 0; }
|
||||
},
|
||||
wa_js_env: new function () {
|
||||
this.waPrintI32 = (i) => {
|
||||
app._wa_print_buf += i
|
||||
}
|
||||
|
||||
this.waPrintRune = (c) => {
|
||||
let ch = String.fromCodePoint(c);
|
||||
if (ch == '\n') {
|
||||
console.log(app._wa_print_buf);
|
||||
app._wa_print_buf = "";
|
||||
}
|
||||
else {
|
||||
app._wa_print_buf += ch
|
||||
}
|
||||
}
|
||||
|
||||
this.waPuts = (prt, len) => {
|
||||
let s = app.getString(prt, len);
|
||||
app._wa_print_buf += s
|
||||
}
|
||||
|
||||
this.rand = (m) => {
|
||||
return parseInt(Math.random() * m)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
WebAssembly.instantiateStreaming(fetch(url), importsObject).then(res => {
|
||||
this._inst = res.instance;
|
||||
this._inst.exports._start();
|
||||
|
||||
var that = this;
|
||||
|
||||
document.getElementById("inputText").addEventListener("input",function (event) {
|
||||
console.log("----------------------call encode----input data is:", event.target.value);
|
||||
const f_encode = that._inst.exports["base60.Base60Encode"];
|
||||
const f_decode = that._inst.exports["base60.Base60Decode"];
|
||||
|
||||
const encoder = new TextEncoder('utf-8');
|
||||
var byteArray = encoder.encode(event.target.value);
|
||||
|
||||
var address = that.malloc(byteArray.length);
|
||||
var length = byteArray.length;
|
||||
console.log("malloc address:", address, " byte length:", length);
|
||||
|
||||
that.setString(address, length, event.target.value);
|
||||
|
||||
console.log("byte values:", that.memUint8Array(address, length));
|
||||
|
||||
var res = f_encode(0, address, length, length);
|
||||
|
||||
console.log("encode byte result:", that.memUint8Array(res[1], res[2]));
|
||||
|
||||
console.log("encoded result: ", that.getString(res[1], res[2]), res);
|
||||
|
||||
var decode_result = f_decode(0, res[1], res[2]);
|
||||
console.log("decode result: ", that.getString(decode_result[1], decode_result[2]));
|
||||
document.getElementById("outputText").value = that.getString(res[1], res[2]);
|
||||
document.getElementById("decodeText").value = that.getString(decode_result[1], decode_result[2]);
|
||||
|
||||
that.release(res[0]);
|
||||
that.release(decode_result[0]);
|
||||
that.free(address);
|
||||
console.log("----------------------call encode end");
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
mem() {
|
||||
return this._inst.exports.memory;
|
||||
}
|
||||
|
||||
release(addr) {
|
||||
return this._inst.exports["$runtime.Release"](addr);
|
||||
}
|
||||
|
||||
malloc(n){
|
||||
return this._inst.exports["$runtime.waHeapAlloc"](n);
|
||||
}
|
||||
|
||||
free(addr){
|
||||
return this._inst.exports["$runtime.waHeapFree"](addr);
|
||||
}
|
||||
|
||||
memView(addr, len) {
|
||||
return new DataView(this._inst.exports.memory.buffer, addr, len);
|
||||
}
|
||||
|
||||
memUint8Array(addr, len) {
|
||||
return new Uint8Array(this.mem().buffer, addr, len)
|
||||
}
|
||||
|
||||
getString(addr, len) {
|
||||
return new TextDecoder("utf-8").decode(this.memView(addr, len));
|
||||
}
|
||||
|
||||
setString(addr, len, s) {
|
||||
const bytes = new TextEncoder("utf-8").encode(s);
|
||||
if (len > bytes.length) { len = bytes.length; }
|
||||
this.memUint8Array(addr, bytes.length).set(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
window['waApp'] = new WaApp();
|
||||
window['waApp'].init("./base60.wasm")
|
||||
|
||||
})()
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
|
||||
## 关于现有js调用webassembly的相关记录
|
||||
- 一个webassembly对象好像只有持有一个mem,然后wa语言生成的base60.wasm本身内部是有mem对象的,所以js和webassmbly的数据共享是基于这个生成的mem对象
|
||||
- 基于以上的认知,关于这部分包括以下几个步骤
|
||||
1. wa build获取base60.wat, 移动到example目录
|
||||
2. go run main.go 启动server, 访问本地的127.0.0.1:2333 界面
|
||||
3. 本地的第一次编解码是ok的,但是第二次调用之后都会出现问题
|
||||
|
||||
## 问题分析
|
||||
1. 关于如何在js调用ws的内存,查看base60.wat, 包括了两个和内存相关的导出方法
|
||||
- runtime.malloc
|
||||
- runtime.free
|
||||
现有的js的交互是基于这两个导出的方法的,但是应该使用上有问题,第二次调用编解码的逻辑,就会这里出现内存访问错误。
|
||||
|
||||
|
||||
2. 如何构造传入的参数
|
||||
|
||||
```
|
||||
-- Main中调用
|
||||
;;Base60Encode(t2)
|
||||
local.get $$t0.b
|
||||
call $$Retain
|
||||
local.get $$t0.d
|
||||
local.get $$t0.l
|
||||
local.get $$t0.c
|
||||
call $base60.Base60Encode
|
||||
local.set $$t1.l
|
||||
local.set $$t1.d
|
||||
local.get $$t1.b
|
||||
call $$Release
|
||||
local.set $$t1.b
|
||||
|
||||
|
||||
-- Encode签名
|
||||
(func $base60.Base60Encode (export "base60.Base60Encode") (param $buf.b i32) (param $buf.d i32) (param $buf.l i32) (param $buf.c i32) (result i32 i32 i32))
|
||||
|
||||
|
||||
-- Decode签名
|
||||
func $base60.Base60Decode (export "base60.Base60Decode") (param $s.b i32) (param $s.d i32) (param $s.l i32) (result i32 i32 i32 i32)
|
||||
```
|
||||
- 调Encode,需要传入四个变量,现在的处理,将l和c认为是长度和容量,d认为是数据部分首地址
|
||||
- b在构造的过程中认为是一个结构体的头部,包括4个i32的属性(第一个应该是和引用计数相关,第三个是数据部分长度,其余部分不太好参考)
|
||||
|
||||
|
||||
---
|
||||
总结问题
|
||||
1. 对于malloc和free的理解可能存在问题,导致出现内存访问越界
|
||||
2. 然后是关于slice在ws层面的b这个字段表示的内容,应该如何在js侧进行构造
|
||||
|
|
@ -1,22 +1,108 @@
|
|||
import "base60/big"
|
||||
|
||||
var (
|
||||
天干 = []string{"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"}
|
||||
地支 = []string{"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"}
|
||||
)
|
||||
var base60Table = [64]string{}
|
||||
var base60Table = [60]string{}
|
||||
|
||||
// var base60IndexTable = map[string]int{}
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 64; i++ {
|
||||
for i := 0; i < 60; i++ {
|
||||
base60Table[i] = 天干[i%10] + 地支[i%12]
|
||||
// base60IndexTable[base60Table[i]] = i
|
||||
}
|
||||
}
|
||||
|
||||
func base60Encode(buf: []byte) => string {
|
||||
return ""
|
||||
func Base60Encode(buf: []byte) => string {
|
||||
if len(buf) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
x := new(big.Int).SetBytes(buf)
|
||||
x.Abs(x)
|
||||
ri := len(buf) * 138 / 100 //
|
||||
ra := make([]byte, ri+1)
|
||||
|
||||
y := big.NewIntWithuint(60)
|
||||
m := big.NewIntWithuint(0)
|
||||
|
||||
for x.Sign() > 0 {
|
||||
x, m = x.DivMod(x, y, m)
|
||||
ra[ri] = byte(m.Bytes()[0])
|
||||
ri--
|
||||
}
|
||||
|
||||
// Leading zeroes encoded as base60Table zeros
|
||||
for i := 0; i < len(buf); i++ {
|
||||
if buf[i] != 0 {
|
||||
break
|
||||
}
|
||||
ra[ri] = 0
|
||||
ri--
|
||||
}
|
||||
ra = ra[ri+1:]
|
||||
|
||||
res: string
|
||||
// 数据输入是大端模式
|
||||
for i := 0; i < len(ra); i++ {
|
||||
res = res + base60Table[ra[i]]
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func base60Decode(s: string) => []byte {
|
||||
func Base60Decode(s: string) => []byte {
|
||||
|
||||
buf := []byte(s)
|
||||
if len(buf)%2 != 0 || len(buf) < 6 {
|
||||
return nil
|
||||
}
|
||||
x := new(big.Int)
|
||||
y := big.NewIntWithuint(60)
|
||||
|
||||
for i := 0; i < len(buf); i += 6 {
|
||||
val := base64GetIndex(buf[i : i+6])
|
||||
|
||||
z := big.NewIntWithuint(uint(val))
|
||||
x.Mul(x, y)
|
||||
x.Add(x, z)
|
||||
}
|
||||
xa := x.Bytes()
|
||||
|
||||
// Restore leading zeros
|
||||
i := 0
|
||||
// 字符 "甲子"
|
||||
for i < len(buf) && base64GetIndex(buf[i:i+6]) == 0 {
|
||||
i += 6
|
||||
}
|
||||
|
||||
ra := make([]byte, (i/6)+len(xa))
|
||||
copy(ra[(i/6):], xa)
|
||||
return ra
|
||||
return nil
|
||||
}
|
||||
|
||||
// 暂时无法基于rune进行迭代, 这些字符在utf8下的字节长度都是3
|
||||
// 单个byte -- 编码 --> 两个汉字(对应6bytes)
|
||||
func base64GetIndex(a: []byte) => int {
|
||||
if len(a) != 6 {
|
||||
panic("error encoded input")
|
||||
}
|
||||
|
||||
for i := 0; i < 60; i++ {
|
||||
target := []byte(天干[i%10] + 地支[i%12])
|
||||
flag := true
|
||||
for j := 0; j < 6; j++ {
|
||||
if target[j] != a[j] {
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
return i
|
||||
}
|
||||
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,3 +90,29 @@ func Test_Int_Quo {
|
|||
// Output:
|
||||
// start test int quo
|
||||
}
|
||||
|
||||
func Test_base60Encode {
|
||||
res := Base60Encode([]byte("你好"))
|
||||
println(res)
|
||||
res = Base60Encode([]byte{0, 0, 0, 0})
|
||||
println(res)
|
||||
// Output:
|
||||
// 乙丑癸巳甲寅己亥丁卯甲申丁未甲午己巳
|
||||
// 甲子甲子甲子甲子
|
||||
}
|
||||
|
||||
func Test_base60Decode {
|
||||
res := Base60Encode([]byte("你好"))
|
||||
t := Base60Decode(res)
|
||||
println(string(t))
|
||||
t = Base60Decode("甲子甲子甲子甲子")
|
||||
for i := 0; i < len(t); i++ {
|
||||
println(t[i])
|
||||
}
|
||||
// Output:
|
||||
// 你好
|
||||
// 0
|
||||
// 0
|
||||
// 0
|
||||
// 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@ func Buf2Uint64(b: []byte) => uint64 {
|
|||
|
||||
func Buf2Uint32(b: []byte) => uint32 {
|
||||
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ func NewInt(x: string) => *Int {
|
|||
return t
|
||||
}
|
||||
|
||||
func NewIntWithuint(x: uint) => *Int {
|
||||
t := new(Int)
|
||||
t.abs = nat{[]uint{x}}
|
||||
t.neg = false
|
||||
return t
|
||||
}
|
||||
|
||||
// Sign returns:
|
||||
//
|
||||
// -1 if x < 0
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ func bigEndianWord(buf: []byte) => uint {
|
|||
if _W == 64 {
|
||||
return uint(Buf2Uint64(buf))
|
||||
}
|
||||
return uint(Buf2Uint64(buf))
|
||||
return uint(Buf2Uint32(buf))
|
||||
}
|
||||
|
||||
// setBytes interprets buf as the bytes of a big-endian unsigned
|
||||
|
|
|
|||
Loading…
Reference in New Issue