WebAssembly入门之利用Memory传递字节数据

利用灵活的“导入”和“导出”机制,WebAssembly与承载的JavaScript应用之间可以很便利地“互通有无”。《与JavaScript的交互》着重演示了如何利用函数的导入和导出实现功能的共享,接下来我们主要关注数据的传递或者共享。总的来说,WebAssembly与宿主程序之间的数据传递主要有如下三种手段,本篇文章主要关注Memory。源代码下载:app3 app4

  • Memory:以二进制(字节)的形式传递数据;

  • Table:传递类型化数据(目前只支持函数);

  • Global:共享全局变量;

一、导入Memory

顾名思义,一个Memory映射一块连续的内存,应用以二进制(字节)的形式对它进行读写。Memory可以利用导入功能从宿主程序传递给WebAssembly,下面的实例演示了这样的场景:作为宿主的JavaScript应用创建一个Memory对象并写入相应的内容,然后将其导入到加载的WebAssembly模块,后者将其中的内容读出来。

如下所示的代码片段是承载WebAssembly程序的app.wat文件的内容,我们利用(memory)定义了一个导入的memory,导入路径为“imports.memory”,后面指定的参数1代表初始大小,单位为Page(64K)。接下来我们定义了四个导出函数,它们会从指定的位置(参数$index表示偏移量)读取相应长度的字节内容,并将其转换成对应的类型。具体来说,这四个函数的返回类型分别为i32、i64、f32和f64,也就是WebAssembly支持的四种数据类型。具体的读取通过执行{i32|i64|f32|f64}.load指令完成,该指令将读取位置作为唯一参数,所以我们在执行该指令之前需要执行local.get 指令将代表读取位置的$index参数压入栈中。

(module
    (memory (import "imports" "memory") 1)

    (func (export "readAsInt32") (param $index i32) (result i32)
         local.get $index
         i32.load
    )

    (func (export "readAsInt64") (param $index i32) (result i64)
         local.get $index
         i64.load
    )

    (func (export "readAsSingle") (param $index i32) (result f32)
         local.get $index
         f32.load
    )

    (func (export "readAsDouble") (param $index i32) (result f64)
         local.get $index
         f64.load
    )
)

有人可能有这样的疑问,我们在执行load指令的时候为什么没有指定具体读取的Memory对象呢?这是因为目前一个WebAssembly模块只能拥有一个Memory对象,这一限制可能会在后续版本中解除,针对多Memory的提案在两年前已经提出。

接下来我们依然需要执行“wat2wasm app.wat –o app.wasm”命令对app.wat文件进行编译,最终生成二进制的模块文件app.wasm。该文件在index.html页面的JavaScript脚本中被加载,index.html页面的内容如下所示。如下面的代码片段所示,我们调用构造函数WebAssembly.Memory创建了一个Memory对象,并将初始大小设置为1(Page)。我们将这个Memory对象的缓冲区(对应buffer属性)映射为一个Uint32Array数组。通过设置这个数组的前两个元素的值(123),我们相应的字节写入前8个字节。

<html>
    <head></head>
    <body>
        <div id="container"></div>
        <script>
            var memory= new WebAssembly.Memory({ initial: 1 });
            const array = new Uint32Array(memory.buffer);
            array[0] = 123;
            array[1] = 123;
            WebAssembly
                .instantiateStreaming(fetch("app.wasm"), {"imports":{"memory":memory}})
                .then(results => {
                    var exports = results.instance.exports;
                        document.getElementById("container").innerHTML =
                        `<p>Int32: ${exports.readAsInt32(0)}</p>` +
                        `<p>Int64: ${exports.readAsInt64(0)}</p>` +
                        `<p>Single: ${exports.readAsSingle(0)}</p>` +
                        `<p>Double: ${exports.readAsDouble(0)}</p>` ;
                });
        </script>
    </body>
</html>

我们从WebAssembly模块实例中导出前面定义的4个函数,并将针对4种不同类型读取出来的值格式化成HTML呈现出来,下图所示的就是最终的输出结果。

4a5b5ff317f6359970974e077585a1eb.png

二、导出Memory

上面演示了如何将Memory对象从宿主应用中导入到WebAssembly模块,现在我们反其道而行,将WebAssembly模块中创建的Memory导出到宿主程序,为此我们将app.wat文件修改为如下的内容。如代码片段所示,我们利用(memory (export "memory") 1)节点定义了一个初始大小为1Page的Memory,并以名称“memory”进行导出。随后定义的导出函数initialize会将作为参数指定的两个整数写入前8个字节。针对Memory的写入通过{i32|i64|f32|f64}.store指令完成,该指令接受两个参数,第一个代表写入的位置,第二个代表写入的值。由于我们具体调用的是i32.store指令,所以在第二次调用的时候指定的写入位置是4,而不是2。

(module
    (memory (export "memory") 1)
    (func (export "initialize") (param $first i32) (param $second i32)
         i32.const 0
         local.get $first
         i32.store

         i32.const 4
         local.get $second
         i32.store
    )
)

在如下所示的index.html中,我们在加载WebAssembly模块“app.wasm”并得到模块实例后,调用导出的initialize函数在Memory中写入两个整数123。然后我们导出Memory对象,并将它的缓冲区映射为四种类型的数组(Uint32Array、BigUint64Array、Float32Array和Float64Array),并将第一个元素的值读取出来,这一操作与上面定义针对四个类型定义的读取函数可谓“异曲同工”。

<html>
    <head></head>
    <body>
        <div id="container"></div>
        <script>
            WebAssembly
                .instantiateStreaming(fetch("app.wasm"))
                .then(results => {
                    var exports = results.instance.exports;
                    exports.initialize(123,123);
                    var buffer = exports.memory.buffer;
                    document.getElementById("container").innerHTML =
                        `<p>Int32: ${new Uint32Array(buffer)[0]}</p>`+
                        `<p>Int32: ${new BigUint64Array(buffer)[0]}</p>` +
                        `<p>Int32: ${new Float32Array(buffer)[0]}</p>`+
                        `<p>Int32: ${new Float64Array(buffer)[0]}</p>`;
                });
        </script>
    </body>
</html>

我们按照相同的方式将读取出来的四个值转换成HTML进行输出,所以我们在浏览器上看到相同的结果。

2cf724d6358dc86fe6183299d317e0aa.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebAssembly 是一个二进制格式,它运行在浏览器中。要监听 WebAssembly 数据的变化,您可以使用 JavaScript 和 WebAssembly 接口之间的交互。 具体来说,您可以使用 WebAssembly 的内存映射功能将 WebAssembly 中的数据映射到 JavaScript 中的 ArrayBuffer 中。然后,您可以使用 JavaScript 的 TypedArray 来访问和修改 ArrayBuffer 中的数据。 以下是一个简单的示例,演示如何监听 WebAssembly 中的数据变化: 1. 首先,您需要在 WebAssembly 模块中声明一个内存导入: ``` (import "js" "mem" (memory 1)) ``` 2. 然后,在 JavaScript 中,您可以使用以下代码将 WebAssembly 内存映射到 ArrayBuffer 中: ``` const mem = new Uint8Array(instance.exports.mem.buffer); ``` 注意,这里的 `instance` 是您从 WebAssembly.instantiate() 函数返回的实例对象。 3. 您可以使用以下代码来监听 ArrayBuffer 中的变化: ``` mem.addEventListener('change', () => { console.log('Memory changed!'); }); ``` 注意,这里的 `change` 事件不是标准事件,而是自定义事件。您需要在 WebAssembly 模块中导出一个函数来触发这个事件: ``` (import "js" "trigger_change_event" (func $trigger_change_event)) ``` 并在 JavaScript 中添加以下代码来监听这个事件: ``` WebAssembly.addEventListner('change', () => { console.log('Memory changed!'); }); ``` 当您在 WebAssembly 中修改内存时,请确保调用 `trigger_change_event` 函数来触发 `change` 事件,以便 JavaScript 可以接收到通知。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值