У меня есть Java-программа, которая вызывает Rust через JNA, предоставляя стороне Rust указатель на потенциально большой (выделенный в куче) буфер непрерывно размеченных \ 0 завершенных строк UTF-8.Память принадлежит стороне Java и освобождается, когда сборщик мусора завершает связанный объект.
Моя цель - обработать этот буфер, интерпретируя его как строковый вектор, делая то, что мне нужно, и отбрасывая всеструктуры, которые Rust размещает поверх буфера, например Vec
, 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 уже сделал это,сбросив вектор.Имеет смысл.Однако, если забыть, что вектор пропускает все структуры «управления» поверх буфера.
Я думал о том, как можно освободить все, что 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, чтобы заставить его работать напрямую.