我的情况
我一直在尝试使用 Rust 中的部分客户端制作 IO 游戏,使用 wasm-pack 编译为 WebAssembly,我通过 Javascript 访问它。 Rust 在这里用作库,并在需要处理包和游戏逻辑时被调用。为了能够处理逻辑,它需要游戏中所有实体和地图的上下文,这可能是大量数据。现在,需要游戏上下文的函数每秒被调用数百次,我不想实现任何批处理逻辑,只想完全避免该问题。这就是为什么我决定使用 WebAssembly 中的内存系统(https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Memory)来存储游戏状态,以便我可以轻松地从 Rust 检索它。
TLDR:我想避免将游戏上下文(我正在制作 IO 游戏,使用 JS 和 Rust 作为 WASM,使用 wasm-bindgen 和 wasm-pack)从 Javascript 传递到 Rust,而是希望将其存储在内存中。
实际问题
我现在如何分配和释放、读取和写入该内存?我当前的实现(如下)
lib.rs
pub mod memory_operations {
[...] Includes
///Write
unsafe fn write_mem<T>(ptr: *mut u8, x: T) {
let ptr = ptr as *mut T;
unsafe {
*ptr = x;
}
}
/// READ
unsafe fn read_mem<'a, T>(ptr: *mut u8) -> &'a mut T {
let ptr = ptr as *mut T;
unsafe {
&mut *ptr
}
}
/// Allocate
unsafe fn allocate_memory<T>() -> MemorySpace {
let size = std::mem::size_of::<T>();
let mut memory = Vec::with_capacity(size);
let ptr: *mut u8 = memory.as_mut_ptr();
std::mem::forget(memory);
MemorySpace::new(ptr as *mut T)
}
/// Deallocate without getting T back and without calling the deconstructor
unsafe fn deallocate_memory_raw(memory: MemorySpace) {
let buffer = unsafe {
std::slice::from_raw_parts_mut(memory.ptr(), memory.size())
};
drop(buffer);
}
/// Deallocate a Layout in memory as a type T and get that type T back
unsafe fn deallocate_memory_type<'a, T>(memory: MemorySpace) -> &'a mut T {
let ptr = memory.ptr() as *mut T;
println!("Dealloc Type");
unsafe { &mut *ptr }
}
#[derive(Debug, Clone, Copy)]
/// Simple Wrapper for Layout and pointer to pass it to JS
pub struct MemorySpace {
offset: *mut u8,
size: usize,
align: usize,
}
impl MemorySpace {
pub fn new<T: Sized>(offset: *mut T) -> MemorySpace {
let layout =
unsafe { alloc::Layout::for_value_raw(offset) };
let offset = offset as *mut u8;
MemorySpace {offset, size: layout.size(), align: layout.align()}
}
pub fn ptr(&self) -> *mut u8 {self.offset}
pub fn size(&self) -> usize {self.size}
pub fn align(&self) -> usize {self.align}
pub fn layout(&self) -> Result<Layout, LayoutError> {
Layout::from_size_align(self.size, self.align)
}
}
}
当前写入时失败,似乎只是不分配或分配和释放内存。我目前只是将其作为二进制文件而不是 WASM 文件进行测试,以便我可以提前调试问题。
问题
我想知道这是否是编译为 wasm 时访问内存的正确方法(如果您认为这个内存问题很愚蠢,请告诉我)。如果是这种情况,我该如何修复此代码以作为二进制文件和 wasm 文件运行?
您提到您正在使用
wasm-bindgen
和 wasm-pack
构建 JS/Rust 游戏。为什么不简单地使用 wasm-bindgen
来进行内存共享呢?如here所述,它允许您注释struct
和方法,以便它们可以传递给JS并具有类似对象的接口,其中方法调用Rust方法。对象本身仍然分配在 WASM 内存中。
如果您出于某种原因想自己执行此操作,那么至少看看
wasm-bindgen
在 JS 粘合代码中生成了什么。它创建了许多由 Uint8Array
支持的类型化数组,例如 wasm.memory.buffer
。然后通过写入或读取此类型化数组来完成对 WASM 内存的原始访问。这也是粘合方法的作用。
使用你的代码,我能够创建一个
MemorySpace
,将其传递给JS函数,JS函数写入一些数据,然后Rust可以将其读回。我在 #[wasm_bindgen]
及其 MemorySpace
块上使用了 impl
。
cafce25指出了您的释放方法中的一个问题:删除
std::slice::from_raw_parts_mut
的结果不会执行任何操作,因为切片不拥有。您想使用 Vec
恢复完整的
Vec::from_raw_parts
,然后将其删除。
我还要注意,
MemorySpace
应该在其Drop
处理程序中解除分配,而不是使用您可能忘记调用的单独的解除分配方法。同样,您应该确保保留对传递给 JS 的 MemorySpace
的引用,只要它可用即可。