Какой правильный способ Rust выделить непрозрачный буфер для внешней библиотеки C? - PullRequest
1 голос
/ 04 октября 2019

У меня есть внешняя библиотека (например, libcisland.so) с таким интерфейсом:

size_t lib_handle_size();
typedef void* handle;
int lib_init(handle h);
int lib_store(handle h, int value);
int lib_restore(handle h, int *pvalue);

Ожидается, что пользователь этой библиотеки сделает следующее:

// allocate some buffer in client address space
handle h = malloc(lib_handle_size());
// pass this buffer to library for initialization
if (lib_init(h)) { /* handle errors */ }
// library initializes this handle by some opaque fashion
// then user uses it
lib_store(h,42);
int r;
lib_restore(h,&r);
// after all work is done, user frees this handle
free(h);

Я могуне могу понять, как правильно обернуть этот интерфейс в Rust. Вот что я закончил:

pub struct Island {
    h: Handle,
    v: Vec<u8>,
}

impl Island {
    pub fn new() -> Island {
        let len = unsafe { lib_handle_size() };
        let mut v: Vec<u8> = Vec::with_capacity(len);
        let h: Handle = v.as_mut_ptr();
        Island { v:v, h:h, }
    }

    pub fn store(&mut self, v: i32) {
        unsafe { lib_store(self.h, v); }
    }

    pub fn restore(&mut self) -> i32 {
        let mut v = 0;
        unsafe { lib_restore(self.h, &mut v); }
        v
    }
}

impl Drop for Island {
    fn drop(&mut self) {
        drop(&mut self.v);
    }
}

/// unsafe part
use libc::size_t;
pub type Handle = *mut u8;
#[link(name="cisland")]
extern {
    pub fn lib_handle_size() -> size_t;
    pub fn lib_init(h: Handle) -> i32;
    pub fn lib_store(h: Handle, value: i32) -> i32;
    pub fn lib_restore(h: Handle, pvalue: &mut i32) -> i32;
}

Можно ли использовать Vec(u8) для этой цели? Эта черта Drop реализована правильно?

Ответы [ 2 ]

1 голос
/ 04 октября 2019

Можно ли использовать Vec (u8) для этой цели?

Я думаю, что Vec<u8> в порядке, но вы должны инициализировать его, а не использовать вектор нулевой длины,указывая на неинициализированную память. Также было бы более надежно использовать Box<[u8]>, потому что это обеспечит невозможность его перераспределения случайно.

Правильно ли реализована эта черта отбрасывания?

Не нужно вообще реализовывать Drop. Поля Island каждое в любом случае будет корректно пропадать.

Вместо того, чтобы хранить дескриптор, я получал бы его каждый раз, используя метод. Тогда ваша структура будет намного проще.

use libc::c_void;

pub struct Island {
    buf: Box<[u8]>,
}

impl Island {
    pub fn new() -> Island {
        let len = unsafe { lib_handle_size() };
        let v: Vec<u8> = vec![0; len];
        Island { buf: v.into_boxed_slice() }
    }

    pub fn store(&mut self, v: i32) {
        unsafe { lib_store(self.handle_mut(), v); }
    }

    pub fn restore(&mut self) -> i32 {
        let mut v = 0;
        unsafe { lib_restore(self.handle_mut(), &mut v); }
        v
    }

    fn handle_mut(&mut self) -> *mut c_void {
        self.buf.as_mut_ptr() as *mut c_void
    }
}

Вам не нужна реализация Drop, потому что Box автоматически выпадет, когда выйдет из области видимости (как и Vec).

1 голос
/ 04 октября 2019

Можно ли использовать Vec(u8) для этой цели?

Вектор не должен использоваться таким образом, даже если ваш код должен работать, это не хороший метод.

Чтобы сделать это правильно, вам нужна экспериментальная функция (эта довольно стабильная), вам нужно использовать структуру System и черту Alloc. К сожалению, ваша библиотека не предъявляет никаких требований к выравниванию для своего дескриптора, поэтому мы должны использовать 1.

pub type Handle = *mut u8; неверно в соответствии с вашим typedef void* handle; (кстати, указатель hide неверен). Это должно быть pub type Handle = *mut libc::c_void;.

#![feature(allocator_api)]
use std::alloc::{Alloc, Layout, System};

use std::ptr::NonNull;

pub struct Island {
    handle: NonNull<u8>,
    layout: Layout,
}

impl Island {
    pub fn new() -> Island {
        let size = unsafe { lib_handle_size() };
        let layout = Layout::from_size_align(size, 1).unwrap();
        let handle = unsafe { System.alloc(layout).unwrap() };
        unsafe {
            // can have error I guess ?
            lib_init(handle.as_ptr() as Handle);
        }
        Self { handle, layout }
    }

    pub fn store(&mut self, v: i32) -> Result<(), ()> {
        unsafe {
            lib_store(self.handle.as_ptr() as Handle, v);
        }
        Ok(())
    }

    pub fn restore(&mut self, v: &mut i32) -> Result<(), ()> {
        unsafe {
            lib_restore(self.handle.as_ptr() as Handle, v);
        }
        Ok(())
    }
}

impl Drop for Island {
    fn drop(&mut self) {
        unsafe { System.dealloc(self.handle, self.layout) }
    }
}

/// unsafe part
use libc::size_t;
pub type Handle = *mut libc::c_void;
#[link(name = "cisland")]
extern "C" {
    pub fn lib_handle_size() -> size_t;
    pub fn lib_init(h: Handle) -> i32;
    pub fn lib_store(h: Handle, value: i32) -> i32;
    pub fn lib_restore(h: Handle, pvalue: &mut i32) -> i32;
}

Я немного изменил вашу функцию store() и restore(), чтобы вернуть Result. Могу поспорить, что ваша функция C делает то же самое.

...