【问题标题】:How can I free all structures that Rust allocated on top of an FFI buffer without freeing the buffer itself?如何在不释放缓冲区本身的情况下释放 Rust 在 FFI 缓冲区顶部分配的所有结构?
【发布时间】: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 的了解太少,无法让它完全发挥作用。

【问题讨论】:

    标签: rust ffi


    【解决方案1】:

    重新阅读CString 的文档,强调我的:

    表示 拥有、C 兼容、以 nul 结尾的字符串的类型,中间没有 nul 字节。

    这种类型的目的是能够安全地从 Rust 字节切片或向量生成与 C 兼容的字符串

    您确实拥有这些字符串,Java 拥有。请改用&amp;strCStr

    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 本身之外什么都不会被释放。

    【讨论】:

    • 非常感谢,这完全有道理。我知道我错过了一些重要的事情:)
    猜你喜欢
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-04
    • 2015-12-11
    相关资源
    最近更新 更多