【发布时间】:2019-03-21 10:13:23
【问题描述】:
我有一个 Java 程序,它通过 JNA 调用 Rust,为 Rust 端提供一个指针,该指针指向一个可能很大(堆分配)的缓冲区,该缓冲区由连续布局的 \0 终止的 UTF-8 字符串组成。内存归 Java 端所有,并在垃圾收集器完成关联对象时释放。
我的目标是通过将缓冲区解释为字符串向量来处理该缓冲区,做我需要做的事情,并删除 Rust 在缓冲区顶部分配的所有结构,例如Vec's、Strings 等。由于缓冲区的潜在大小,我想尽可能避免复制数据。
考虑以下代码:
use std::ffi::CString;
use std::os::raw::c_char;
pub extern "C" fn process_data(data: *const c_char, num_elements: i64) {
let mut vec: Vec<String> = Vec::with_capacity(num_elements as usize);
let mut offset = 0;
unsafe {
for _ in 0..num_elements {
let ptr = { data.offset(offset as isize) };
// Main goal here is to have no memory copy involved
let s = String::from_utf8_unchecked(CString::from_raw(ptr as *mut c_char).into_bytes());
offset += s.len() + 1; // Include string termination
vec.push(s);
}
}
// do stuff with the vector
// ...
// Now that we're done, vec would be dropped, freeing the strings, thus freeing their underlying memory.
}
我的理解是我现在有一个Vec,它在内部指向包含Strings 的缓冲区,而Strings 又在内部指向Vecs,然后以某种方式指向我传入的缓冲区。
如果我让代码像这样运行而不显式忘记向量,我会获得双重释放,因为 Java 试图释放缓冲区,但 Rust 已经通过删除向量来做到这一点。说得通。但是,忘记向量会泄漏缓冲区顶部的所有“管理”结构。
我想过如何在不泄漏任何内存的情况下释放 Rust 分配的所有内容。我考虑过明确泄漏框并删除它们给我的指针(因为Java仍然有一个指针),如下所示:
fn forget_vec(vec: Vec<String>) {
vec.into_iter().map(|s| {
Box::into_raw(s.into_bytes().into_boxed_slice());
}
}
但是,由于切片也是一个包含长度和指针的结构,并且通过执行上述操作,我认为我会泄漏此结构。我正在寻找消耗切片的东西,并且只返回一个像*const u8 这样的指针。
我有一种感觉,我通常会朝着正确的方向前进,但我缺少一些重要的东西,或者对 Rust 的了解太少,无法让它完全发挥作用。
【问题讨论】: