首页 文章

如何在不释放缓冲区本身的情况下释放Rust在FFI缓冲区之上分配的所有结构?

提问于
浏览
2

我有一个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 的缓冲区,然后内部指向 Vec ,然后以某种方式指向我传入的缓冲区 .

如果我让代码像这样运行而不会明确地忘记向量,我得到一个双重释放,因为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的理解太少,无法让它完全正常工作 .

1 回答

  • 5

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

    一种类型,表示拥有的,C兼容的,以空字符结尾的字符串,中间没有空字节 . 此类型的目的是能够从Rust字节切片或向量安全地生成C兼容的字符串 .

    你确实拥有这些字符串,Java也是如此 . 请改用 &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 本身之外没有任何内容被释放 .

相关问题