为外部 C 库分配不透明缓冲区的正确 Rust 方法是什么?
What is proper Rust way to allocate opaque buffer for external C library?
我有一个外部库(例如 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
特性是否正确实施?
Is it Ok to use Vec(u8)
for this purpose?
矢量不应该那样使用,即使您的代码应该可以工作,这也不是一个好方法。
要正确执行此操作,您需要一个实验性功能(这个功能非常稳定),您需要使用 System
structure and Alloc
特性。不幸的是,您的库没有对其句柄给出任何对齐要求,因此我们必须使用 1
.
pub type Handle = *mut u8;
根据您的 typedef void* handle;
是不正确的(顺便说一下,隐藏指针是错误的)。应该是 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()
函数稍微更改为 return 结果。我敢打赌你的 C 函数也会这样做。
Is it Ok to use Vec(u8) for this purpose?
我认为 Vec<u8>
没问题,但你应该初始化它而不是使用零长度向量,指向未初始化的内存。使用 Box<[u8]>
也会更健壮,因为这将强制它不能被意外重新分配。
Is this Drop trait implemented properly?
根本没有必要实施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
也是如此)。
我有一个外部库(例如 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
特性是否正确实施?
Is it Ok to use
Vec(u8)
for this purpose?
矢量不应该那样使用,即使您的代码应该可以工作,这也不是一个好方法。
要正确执行此操作,您需要一个实验性功能(这个功能非常稳定),您需要使用 System
structure and Alloc
特性。不幸的是,您的库没有对其句柄给出任何对齐要求,因此我们必须使用 1
.
pub type Handle = *mut u8;
根据您的 typedef void* handle;
是不正确的(顺便说一下,隐藏指针是错误的)。应该是 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()
函数稍微更改为 return 结果。我敢打赌你的 C 函数也会这样做。
Is it Ok to use Vec(u8) for this purpose?
我认为 Vec<u8>
没问题,但你应该初始化它而不是使用零长度向量,指向未初始化的内存。使用 Box<[u8]>
也会更健壮,因为这将强制它不能被意外重新分配。
Is this Drop trait implemented properly?
根本没有必要实施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
也是如此)。