Js 使用 wasm-bindgen 绑定大型 rust 对象
Js binding for large rust object using wasm-bindgen
我想写一个 vscode 扩展,显示一个大的二进制文件的内容,写成 bincode
:
#[macro_use]
extern crate serde_derive;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, BufWriter};
#[derive(Serialize, Deserialize)]
pub struct MyValue {
pub name: String,
}
#[derive(Serialize, Deserialize)]
pub struct MyStruct {
pub data: HashMap<String, MyValue>,
}
impl MyStruct {
pub fn dump(&self, filename: &str) -> Result<(), String> {
let file = File::create(filename).map_err(|msg| msg.to_string())?;
let writer = BufWriter::new(file);
bincode::serialize_into(writer, self).map_err(|msg| msg.to_string())
}
pub fn load(filename: &str) -> Result<Self, String> {
let file = File::open(filename).map_err(|msg| msg.to_string())?;
let reader = BufReader::new(file);
bincode::deserialize_from::<BufReader<_>, Self>(reader).map_err(|msg| msg.to_string())
}
}
因此有一个 wasm 绑定:
#[wasm_bindgen]
#[derive(Clone)]
pub struct PyMyStruct {
inner: Arc<MyStruct>,
}
#[wasm_bindgen]
impl PyMyStruct {
pub fn new(filename: &str) -> Self {
Self {
inner: Arc::new(MyStruct::load(filename).unwrap()),
}
}
pub fn header(self) -> Array {
let keys = Array::new();
for key in self.inner.data.keys() {
keys.push(&JsValue::from_str(key));
}
keys
}
pub fn value(&self, name: &str) -> JsValue {
if let Some(value) = self.inner.data.get(name) {
JsValue::from_serde(value).unwrap_or(JsValue::NULL)
} else {
JsValue::NULL
}
}
}
它为 JavaScript 世界提供了一个简单的界面,以便访问该文件的内容。
使用 Arc
以防止在 JavaScript 端处理时发生昂贵的意外内存复制。
(keys
没有标记为可变的,但 Rust 编译器这样推荐,这看起来可能很奇怪)
当运行测试代码:
const {PyMyStruct} = require("./corejs.js");
let obj = new PyMyStruct("../../dump.spb")
console.log(obj.header())
您收到错误消息:
Error: null pointer passed to rust
有人知道如何处理这个用例吗?
谢谢!
这里的问题是您使用的是 new PyMyStruct()
而不是 PyMyStruct.new()
。在 wasm-bindgen 的调试模式下,您将在运行时收到有关此的错误。使用 .new()
将解决您的问题:
let obj = PyMyStruct.new("../../dump.spb")
如果您将 #[wasm_bindgen(constructor)]
注释添加到 new
方法,那么 new PyMyStruct()
也将起作用:
#[wasm_bindgen]
impl PyMyStruct {
#[wasm_bindgen(constructor)]
pub fn new(filename: &str) -> Self {
Self {
inner: 1,
}
}
}
现在可以了:
let obj = new PyMyStruct("../../dump.spb")
我通过使用 https://neon-bindings.com 而不是将 API 编译为 web-assembly 解决了这个问题。
此处的绑定如下所示:
use core;
use std::rc::Rc;
use neon::prelude::*;
#[derive(Clone)]
pub struct MyStruct {
inner: Rc<core::MyStruct>,
}
declare_types! {
pub class JsMyStruct for MyStruct {
init(mut cx) {
let filename = cx.argument::<JsString>(0)?.value();
match core::MyStruct::load(&filename) {
Ok(inner) => return Ok(MyStruct{
inner: Rc::new(inner)
}),
Err(msg) => {
panic!("{}", msg)
}
}
}
method header(mut cx) {
let this = cx.this();
let container = {
let guard = cx.lock();
let this = this.borrow(&guard);
(*this).clone()
};
let keys = container.inner.data.keys().collect::<Vec<_>>();
let js_array = JsArray::new(&mut cx, keys.len() as u32);
for (i, obj) in keys.into_iter().enumerate() {
let js_string = cx.string(obj);
js_array.set(&mut cx, i as u32, js_string).unwrap();
}
Ok(js_array.upcast())
}
method value(mut cx) {
let key = cx.argument::<JsString>(0)?.value();
let this = cx.this();
let container = {
let guard = cx.lock();
let this = this.borrow(&guard);
(*this).clone()
};
if let Some(data) = container.inner.data.get(&key) {
return Ok(neon_serde::to_value(&mut cx, data)?);
} else {
panic!("No value for key \"{}\" available", key);
}
}
}
}
register_module!(mut m, {
m.export_class::<JsMyStruct>("MyStruct")?;
Ok(())
});
我想写一个 vscode 扩展,显示一个大的二进制文件的内容,写成 bincode
:
#[macro_use]
extern crate serde_derive;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, BufWriter};
#[derive(Serialize, Deserialize)]
pub struct MyValue {
pub name: String,
}
#[derive(Serialize, Deserialize)]
pub struct MyStruct {
pub data: HashMap<String, MyValue>,
}
impl MyStruct {
pub fn dump(&self, filename: &str) -> Result<(), String> {
let file = File::create(filename).map_err(|msg| msg.to_string())?;
let writer = BufWriter::new(file);
bincode::serialize_into(writer, self).map_err(|msg| msg.to_string())
}
pub fn load(filename: &str) -> Result<Self, String> {
let file = File::open(filename).map_err(|msg| msg.to_string())?;
let reader = BufReader::new(file);
bincode::deserialize_from::<BufReader<_>, Self>(reader).map_err(|msg| msg.to_string())
}
}
因此有一个 wasm 绑定:
#[wasm_bindgen]
#[derive(Clone)]
pub struct PyMyStruct {
inner: Arc<MyStruct>,
}
#[wasm_bindgen]
impl PyMyStruct {
pub fn new(filename: &str) -> Self {
Self {
inner: Arc::new(MyStruct::load(filename).unwrap()),
}
}
pub fn header(self) -> Array {
let keys = Array::new();
for key in self.inner.data.keys() {
keys.push(&JsValue::from_str(key));
}
keys
}
pub fn value(&self, name: &str) -> JsValue {
if let Some(value) = self.inner.data.get(name) {
JsValue::from_serde(value).unwrap_or(JsValue::NULL)
} else {
JsValue::NULL
}
}
}
它为 JavaScript 世界提供了一个简单的界面,以便访问该文件的内容。
使用 Arc
以防止在 JavaScript 端处理时发生昂贵的意外内存复制。
(keys
没有标记为可变的,但 Rust 编译器这样推荐,这看起来可能很奇怪)
当运行测试代码:
const {PyMyStruct} = require("./corejs.js");
let obj = new PyMyStruct("../../dump.spb")
console.log(obj.header())
您收到错误消息:
Error: null pointer passed to rust
有人知道如何处理这个用例吗?
谢谢!
这里的问题是您使用的是 new PyMyStruct()
而不是 PyMyStruct.new()
。在 wasm-bindgen 的调试模式下,您将在运行时收到有关此的错误。使用 .new()
将解决您的问题:
let obj = PyMyStruct.new("../../dump.spb")
如果您将 #[wasm_bindgen(constructor)]
注释添加到 new
方法,那么 new PyMyStruct()
也将起作用:
#[wasm_bindgen]
impl PyMyStruct {
#[wasm_bindgen(constructor)]
pub fn new(filename: &str) -> Self {
Self {
inner: 1,
}
}
}
现在可以了:
let obj = new PyMyStruct("../../dump.spb")
我通过使用 https://neon-bindings.com 而不是将 API 编译为 web-assembly 解决了这个问题。
此处的绑定如下所示:
use core;
use std::rc::Rc;
use neon::prelude::*;
#[derive(Clone)]
pub struct MyStruct {
inner: Rc<core::MyStruct>,
}
declare_types! {
pub class JsMyStruct for MyStruct {
init(mut cx) {
let filename = cx.argument::<JsString>(0)?.value();
match core::MyStruct::load(&filename) {
Ok(inner) => return Ok(MyStruct{
inner: Rc::new(inner)
}),
Err(msg) => {
panic!("{}", msg)
}
}
}
method header(mut cx) {
let this = cx.this();
let container = {
let guard = cx.lock();
let this = this.borrow(&guard);
(*this).clone()
};
let keys = container.inner.data.keys().collect::<Vec<_>>();
let js_array = JsArray::new(&mut cx, keys.len() as u32);
for (i, obj) in keys.into_iter().enumerate() {
let js_string = cx.string(obj);
js_array.set(&mut cx, i as u32, js_string).unwrap();
}
Ok(js_array.upcast())
}
method value(mut cx) {
let key = cx.argument::<JsString>(0)?.value();
let this = cx.this();
let container = {
let guard = cx.lock();
let this = this.borrow(&guard);
(*this).clone()
};
if let Some(data) = container.inner.data.get(&key) {
return Ok(neon_serde::to_value(&mut cx, data)?);
} else {
panic!("No value for key \"{}\" available", key);
}
}
}
}
register_module!(mut m, {
m.export_class::<JsMyStruct>("MyStruct")?;
Ok(())
});