Compare commits

...

3 Commits

Author SHA1 Message Date
trueabc d63a60d1ef 使用heapAlloc和heapFree管理内存 2023-08-25 10:53:11 +08:00
trueabc 2badf9214c js调用ws问题总结 2023-08-23 09:48:05 +08:00
trueabc 509605f7aa base60编码 2023-08-12 08:46:54 +08:00
9 changed files with 377 additions and 7 deletions

25
example/index.html Normal file
View File

@ -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>

10
example/main.go Normal file
View File

@ -0,0 +1,10 @@
package main
import (
"net/http"
)
func main() {
http.Handle("/", http.FileServer(http.Dir("./")))
http.ListenAndServe(":2333", nil)
}

167
example/wa.js Normal file
View File

@ -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")
})()

49
log/0822.md Normal file
View File

@ -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侧进行构造

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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