Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is proper Rust way to allocate opaque buffer for external C library?

Tags:

c

rust

ffi

I have an external library (e.g. libcisland.so) with interface like this:

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);

The user of this library is expected to do following:

// 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);

I can't figure out how to properly wrap this interface to Rust. This is what I ended up:

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;
}

Is it Ok to use Vec(u8) for this purpose? Is this Drop trait implemented properly?

like image 338
Vasily Olekhov Avatar asked Oct 15 '22 10:10

Vasily Olekhov


1 Answers

Is it Ok to use Vec(u8) for this purpose?

I think Vec<u8> is ok, but you should initialize it rather than using a zero-length vector, pointing at uninitialized memory. It would also be more robust to use Box<[u8]> because that will enforce that it can't be reallocated accidentally.

Is this Drop trait implemented properly?

It should not be necessary to implement Drop at all. The fields of Island each will drop correctly anyway.

Rather than store the handle, I would get it each time using a method. Then your struct is much simpler.

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
    }
}

You don't need a Drop implementation because the Box will drop automatically when it goes out of scope (as would a Vec).

like image 104
Peter Hall Avatar answered Oct 19 '22 00:10

Peter Hall