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

Dom*_*nic 3 ffi rust

我有一个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.
}
Run Code Online (Sandbox Code Playgroud)

我的理解是,我现在有一个Vec内部指向包含Strings 的缓冲区,而s又在内部指向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());
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,由于切片也是包含长度和指针的结构,并且通过执行上述操作,我认为我会泄漏此结构.我一直在寻找消耗切片的东西,只给我一个指针*const u8.

我有一种感觉,我通常会朝着正确的方向前进,但是我缺少一些重要的东西,或者对Rust的理解太少,无法让它完全正常工作.

She*_*ter 6

重读文档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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当你Vec被删除时,它只会丢弃引用,除了它Vec自己之外什么也没有被释放.