如何在不释放缓冲区本身的情况下释放 Rust 在 FFI 缓冲区顶部分配的所有结构?
How can I free all structures that Rust allocated on top of an FFI buffer without freeing the buffer itself?
我有一个 Java 程序,它通过 JNA 调用 Rust,为 Rust 端提供一个指针,指向一个潜在的大(堆分配的)连续布局的缓冲区,\0 终止 UTF-8字符串。内存由 Java 方拥有,并在垃圾收集器完成关联对象时释放。
我的目标是通过将缓冲区解释为字符串向量来处理该缓冲区,执行我需要做的事情,并删除 Rust 在缓冲区顶部分配的所有结构,例如Vec
's、String
s 等。由于缓冲区的潜在大小,我想尽可能避免复制数据。
考虑以下代码:
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
内部指向包含 String
s 的缓冲区,后者又在内部指向 Vec
s,然后以某种方式指向进入我传入的缓冲区。
如果我让代码 运行 像这样而不显式忘记向量,我会得到双重释放,因为 Java 试图释放缓冲区,但 Rust 已经通过删除向量来这样做了。说得通。但是,忘记向量会泄漏缓冲区顶部的所有 "management" 结构。
我考虑了如何在不泄漏任何内存的情况下释放 Rust 分配的所有内容。我考虑过明确泄漏框并删除它们给我的指针(因为 Java 仍然有一个指针)沿着:
fn forget_vec(vec: Vec<String>) {
vec.into_iter().map(|s| {
Box::into_raw(s.into_bytes().into_boxed_slice());
}
}
但是,由于切片也是一个包含长度和指针的结构,通过执行上述操作,我想我会泄露这个结构。我一直在寻找消耗切片的东西,只有 returns 我是一个像 *const u8
.
这样的指针
我感觉我大体上是在朝着正确的方向前进,但是我遗漏了一些重要的东西或者对 Rust 的理解太少而无法让它完全发挥作用。
重新阅读 CString
的文档,强调我的:
A type representing an owned, C-compatible, nul-terminated string with no nul bytes in the middle.
This type serves the purpose of being able to safely generate a C-compatible string from a Rust byte slice or vector.
您不拥有这些字符串,Java拥有。使用 &str
和 CStr
代替:
use std::ffi::CStr;
use std::os::raw::c_char;
pub extern "C" fn process_data(data: *const c_char, num_elements: i64) {
let mut vec: Vec<&str> = Vec::with_capacity(num_elements as usize);
unsafe {
let mut ptr = data;
for _ in 0..num_elements {
let s = CStr::from_ptr(ptr);
ptr = ptr.add(s.to_bytes().len() + 1); // Include string termination
if let Ok(s) = s.to_str() {
vec.push(s);
}
}
}
}
当你的 Vec
被删除时,它只是删除引用,除了 Vec
本身之外没有任何东西被释放。
我有一个 Java 程序,它通过 JNA 调用 Rust,为 Rust 端提供一个指针,指向一个潜在的大(堆分配的)连续布局的缓冲区,\0 终止 UTF-8字符串。内存由 Java 方拥有,并在垃圾收集器完成关联对象时释放。
我的目标是通过将缓冲区解释为字符串向量来处理该缓冲区,执行我需要做的事情,并删除 Rust 在缓冲区顶部分配的所有结构,例如Vec
's、String
s 等。由于缓冲区的潜在大小,我想尽可能避免复制数据。
考虑以下代码:
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
内部指向包含 String
s 的缓冲区,后者又在内部指向 Vec
s,然后以某种方式指向进入我传入的缓冲区。
如果我让代码 运行 像这样而不显式忘记向量,我会得到双重释放,因为 Java 试图释放缓冲区,但 Rust 已经通过删除向量来这样做了。说得通。但是,忘记向量会泄漏缓冲区顶部的所有 "management" 结构。
我考虑了如何在不泄漏任何内存的情况下释放 Rust 分配的所有内容。我考虑过明确泄漏框并删除它们给我的指针(因为 Java 仍然有一个指针)沿着:
fn forget_vec(vec: Vec<String>) {
vec.into_iter().map(|s| {
Box::into_raw(s.into_bytes().into_boxed_slice());
}
}
但是,由于切片也是一个包含长度和指针的结构,通过执行上述操作,我想我会泄露这个结构。我一直在寻找消耗切片的东西,只有 returns 我是一个像 *const u8
.
我感觉我大体上是在朝着正确的方向前进,但是我遗漏了一些重要的东西或者对 Rust 的理解太少而无法让它完全发挥作用。
重新阅读 CString
的文档,强调我的:
A type representing an owned, C-compatible, nul-terminated string with no nul bytes in the middle.
This type serves the purpose of being able to safely generate a C-compatible string from a Rust byte slice or vector.
您不拥有这些字符串,Java拥有。使用 &str
和 CStr
代替:
use std::ffi::CStr;
use std::os::raw::c_char;
pub extern "C" fn process_data(data: *const c_char, num_elements: i64) {
let mut vec: Vec<&str> = Vec::with_capacity(num_elements as usize);
unsafe {
let mut ptr = data;
for _ in 0..num_elements {
let s = CStr::from_ptr(ptr);
ptr = ptr.add(s.to_bytes().len() + 1); // Include string termination
if let Ok(s) = s.to_str() {
vec.push(s);
}
}
}
}
当你的 Vec
被删除时,它只是删除引用,除了 Vec
本身之外没有任何东西被释放。