WebAssembly 和 JavaScript 的 String 类型数据交换

前言

我们知道 WASM 和 HOST 环境(Browser)是通过线型内存共享空间的,所以本质上交换数据就是在这段共享内存中存和取数据,以及对数据如何编码。

这里又涉及到对应的 WASM 产物是如何在内存中存放数据的,例如下面的案例是在内存空间直接寻址提取和存放数据的

不同的数据结构(Array,Object,Struct)有自己的内存空间定义,所以我只针对 String 做 Case

字符串类型

wasm 到 js

index.c: 其中定义 get_str_from_wasm 方法,返回字符串指针

// wasm string pointer
char * get_str_from_wasm(void) {
return (char *)"Hello, From WASM";
}

Makefile: 编译

main:
emcc \
index.c \
-s ERROR_ON_UNDEFINED_SYMBOLS=0 \
-s EXPORTED_FUNCTIONS="[_malloc,_free,_get_str_from_js]" \
--no-entry \
-o \
./index.wasm
clean:
rm -rfv *.wasm

index.html: 加载 wasm,通过1byte(8Bit)去取内存数据

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shared</title>
</head>
<script type="text/javascript">
(async function () {
function AsciiToString(ptr, heapu8) {
let str = '';
while (1) {
let ch = heapu8[ptr++];
if (!ch) return str;
str += String.fromCharCode(ch);
}
}
const resp = await fetch(`./index.wasm?t=${Date.now()}`);
const bytes = await resp.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {},
});
// 结构化数据
const HEAP8 = new Int8Array(instance.exports.memory.buffer);
// get string from wasm
const wasm_str_ptr = instance.exports.get_str_from_wasm();
const wasm_str = AsciiToString(wasm_str_ptr, HEAP8);
console.log(wasm_str); //Hello, From WASM
})().catch(err => console.error(err));
</script>
</body>
</html>

我只用 ascii 码的形式去编码数据和解码数据,其实如果涉及到 UTF-8 类型数据,需要用 Int32Array 去取数据解码。

js 到 wasm

index.c: 定义 set_str_from_js 来设置字符串,get_str_from_js 来去字符串指针

char * js_str;
// set string pointer
void set_str_from_js(char * str) {
js_str = str;
}
// return string pointer
char * get_str_from_js() {
return js_str;
}

Makefile

main:
emcc \
index.c \
-s ERROR_ON_UNDEFINED_SYMBOLS=0 \
-s EXPORTED_FUNCTIONS="[_malloc,_free,_get_str_from_wasm,_set_str_from_js]" \
--no-entry \
-o \
./index.wasm
clean:
rm -rfv *.wasm

index.html 因为

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shared</title>
</head>
<script type="text/javascript">
(async function () {
function AsciiToString(ptr, heapu8) {
let str = '';
while (1) {
let ch = heapu8[ptr++];
if (!ch) return str;
str += String.fromCharCode(ch);
}
}
const resp = await fetch(`./index.wasm?t=${Date.now()}`);
const bytes = await resp.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {},
});
const str = "Hello, From JS";
const str_ptr = instance.exports.malloc(str.length);
const HEAP8 = new Int8Array(instance.exports.memory.buffer);
// 1 byte 1 byte 的形式去内存中设置值,直接修改 wasm 内存数据
str.split('').forEach((char, index) => {
HEAP8[(str_ptr + index) >> 0] = char.charCodeAt(0);
});
// 传递指针
instance.exports.set_str_from_js(str_ptr);
// 获取值
const str_ptr_from_wasm = instance.exports.get_str_from_js();
const str_from_wasm = AsciiToString(str_ptr_from_wasm, HEAP8);
console.log(str_from_wasm); // "Hello, From JS"
})().catch(err => console.error(err));
</script>
</body>
</html>

可以看出,string 是不能直接通过 API 形式传递,需要开辟内存空间,往对应的内存空间写数据,再把 pointer 传入到 wasm 方法去。

其他类型数据

其他高级类型数据和对应的存储结构有关,可以通过高阶的 wrap 形式来做传递,本质上和 String 的传递类似。

posted @   buzzjan  阅读(844)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示