Java与wasm(rust)间字符串传参

前言

wasmer-java导入函数中提到wasm操作限制较大。我们需要通过导入外部函数去解决一些问题。

截至写文章时,已用wasmer-java中他人维护的分支解决导入函数问题

问题

但是导入的外部函数与wasm(实际上为rust程序)之间参数传递只能使用intlongfloatdouble 四种,这是远远不够的。

解决方法

对于一些复杂对象的传递,我们可以考虑使用字符串。但是字符串类型也是不能作为参数直接在Java与wasm中直接传递的,我们需要借助wasmer-java中的Memory类,将字符串直接写入虚拟机内存中,并传递一个 int 类型的指针。

传入字符串

Java
参数初始化
1
2
3
4
5
6
7
8
9
10
11
//获取memory对象
Memory memory = instance.exports.getMemory("memory");
String str = "要传递的参数";
//获取字符串对应的字节数组
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
//调用rust中写的申请内存区域的方法,记录这块内存地址的指针
Integer strPtr = (Integer) instance.exports.getFunction("allocate").apply(bytes.length)[0];
//写入内存
ByteBuffer mbf = memory.buffer();
mbf.position(strPtr);
mbf.put(bytes);
参数传递
1
Object[] results = instance.exports.getFunction("函数名").apply(strPtr);
Rust
直接解析

通过CStrCString两个库操作

1
2
3
4
5
6
7
8
9
10
#[no_mangle]
pub extern fn func(param_ptr: *mut c_char){
let result;
unsafe {
result = CStr::from_ptr(str_ptr).to_str().unwrap();
}

let string = CString::new(format!("wallet:{}", result)).unwrap();
//其他操作
}

传出字符串

Rust

返回 *mut c_char 类型即可

1
2
3
4
5
6
7
8
#[no_mangle]
pub extern fn func() -> *mut c_char{
let result = ...;

let string = CString::new(format!("wallet:{}", result)).unwrap();

string.into_raw()
}
Java
1
2
3
4
5
6
7
//获取指针
Integer resultPtr = (Integer) instance.exports.getFunction("func").apply()[0];
//获取内存
Memory memory = instance.exports.getMemory("memory");
ByteBuffer mbf = memory.buffer();
//解析
String res = getString(resultPtr,mbf);
1
2
3
4
5
6
7
8
9
10
11
12
13
public static String getString(Integer ptr, ByteBuffer mbf) {
StringBuilder sb = new StringBuilder();
for(int i = ptr, max = mbf.limit(); i < max; i++) {
mbf.position(i);
byte b = mbf.get();
if (b == 0) {
break;
}
sb.appendCodePoint(b);
}
String result = sb.toString();
return result;
}