如何为可以提供分配器的 C 库使用 Rust 内存分配器?
How do I use the Rust memory allocator for a C library that can be provided an allocator?
我正在将 Rust 绑定写入 C 库,该库可以选择使用第三方内存分配器。它的界面是这样的:
struct allocator {
void*(*alloc)(void *old, uint);
void(*free)(void*);
};
我猜相应的 Rust 结构如下:
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Allocator {
alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>,
free: Option<extern "C" fn(*mut c_void)>,
}
如何实现这两个模拟分配器的外部函数?我没有在 Rust 中发现任何真正看起来像分配器 API 的东西(但是我明白为什么),所以我很好奇它是否可能。
这并不像您想象的那么简单。
分配方法公开在heap
module of the alloc
crate。
创建一些包装器方法并填充结构很简单,但我们很快 运行 遇到一个问题:
#![feature(heap_api)]
extern crate libc;
extern crate alloc;
use libc::{c_void, c_uint};
use alloc::heap;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Allocator {
alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>,
free: Option<extern "C" fn(*mut c_void)>,
}
extern "C" fn alloc_ext(old: *mut c_void, size: c_uint) -> *mut c_void {
if old.is_null() {
heap::allocate(size as usize, align) as *mut c_void
} else {
heap::reallocate(old as *mut u8, old_size, size as usize, align) as *mut c_void
}
}
extern "C" fn free_ext(old: *mut c_void) {
heap::deallocate(old as *mut u8, old_size, align);
}
fn main() {
Allocator {
alloc: Some(alloc_ext),
free: Some(free_ext),
};
}
Rust 分配器期望被告知任何先前分配的大小以及所需的对齐方式。您正在匹配的 API 没有任何方式传递它。
对齐应该(我不是专家)可以硬编码某个值,比如 16 个字节。尺寸比较棘手。您可能需要窃取一些旧的 C 技巧并分配一些额外的 space 来存储大小。然后您可以存储大小和 return 一个刚刚超过它的指针。
一个完全未经测试的例子:
#![feature(alloc, heap_api)]
extern crate libc;
extern crate alloc;
use libc::{c_void, c_uint};
use alloc::heap;
use std::{mem, ptr};
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Allocator {
alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>,
free: Option<extern "C" fn(*mut c_void)>,
}
const ALIGNMENT: usize = 16;
extern "C" fn alloc_ext(old: *mut c_void, size: c_uint) -> *mut c_void {
unsafe {
// Should check for integer overflow
let size_size = mem::size_of::<usize>();
let size = size as usize + size_size;
let memory = if old.is_null() {
heap::allocate(size, ALIGNMENT)
} else {
let old = old as *mut u8;
let old = old.offset(-(size_size as isize));
let old_size = *(old as *const usize);
heap::reallocate(old, old_size, size, ALIGNMENT)
};
*(memory as *mut usize) = size;
memory.offset(size_size as isize) as *mut c_void
}
}
extern "C" fn free_ext(old: *mut c_void) {
if old.is_null() { return }
unsafe {
let size_size = mem::size_of::<usize>();
let old = old as *mut u8;
let old = old.offset(-(size_size as isize));
let old_size = *(old as *const usize);
heap::deallocate(old as *mut u8, old_size, ALIGNMENT);
}
}
fn main() {
Allocator {
alloc: Some(alloc_ext),
free: Some(free_ext),
};
let pointer = alloc_ext(ptr::null_mut(), 54);
let pointer = alloc_ext(pointer, 105);
free_ext(pointer);
}
Isn't [... using Vec
as an allocator ...] the more high-level solution?
这当然是可能的,但我不完全确定它如何与重新分配一起工作。您还必须跟踪 Vec
的大小和容量,以便重新构造它以重新分配/删除它。
我正在将 Rust 绑定写入 C 库,该库可以选择使用第三方内存分配器。它的界面是这样的:
struct allocator {
void*(*alloc)(void *old, uint);
void(*free)(void*);
};
我猜相应的 Rust 结构如下:
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Allocator {
alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>,
free: Option<extern "C" fn(*mut c_void)>,
}
如何实现这两个模拟分配器的外部函数?我没有在 Rust 中发现任何真正看起来像分配器 API 的东西(但是我明白为什么),所以我很好奇它是否可能。
这并不像您想象的那么简单。
分配方法公开在heap
module of the alloc
crate。
创建一些包装器方法并填充结构很简单,但我们很快 运行 遇到一个问题:
#![feature(heap_api)]
extern crate libc;
extern crate alloc;
use libc::{c_void, c_uint};
use alloc::heap;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Allocator {
alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>,
free: Option<extern "C" fn(*mut c_void)>,
}
extern "C" fn alloc_ext(old: *mut c_void, size: c_uint) -> *mut c_void {
if old.is_null() {
heap::allocate(size as usize, align) as *mut c_void
} else {
heap::reallocate(old as *mut u8, old_size, size as usize, align) as *mut c_void
}
}
extern "C" fn free_ext(old: *mut c_void) {
heap::deallocate(old as *mut u8, old_size, align);
}
fn main() {
Allocator {
alloc: Some(alloc_ext),
free: Some(free_ext),
};
}
Rust 分配器期望被告知任何先前分配的大小以及所需的对齐方式。您正在匹配的 API 没有任何方式传递它。
对齐应该(我不是专家)可以硬编码某个值,比如 16 个字节。尺寸比较棘手。您可能需要窃取一些旧的 C 技巧并分配一些额外的 space 来存储大小。然后您可以存储大小和 return 一个刚刚超过它的指针。
一个完全未经测试的例子:
#![feature(alloc, heap_api)]
extern crate libc;
extern crate alloc;
use libc::{c_void, c_uint};
use alloc::heap;
use std::{mem, ptr};
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Allocator {
alloc: Option<extern "C" fn(*mut c_void, c_uint) -> *mut c_void>,
free: Option<extern "C" fn(*mut c_void)>,
}
const ALIGNMENT: usize = 16;
extern "C" fn alloc_ext(old: *mut c_void, size: c_uint) -> *mut c_void {
unsafe {
// Should check for integer overflow
let size_size = mem::size_of::<usize>();
let size = size as usize + size_size;
let memory = if old.is_null() {
heap::allocate(size, ALIGNMENT)
} else {
let old = old as *mut u8;
let old = old.offset(-(size_size as isize));
let old_size = *(old as *const usize);
heap::reallocate(old, old_size, size, ALIGNMENT)
};
*(memory as *mut usize) = size;
memory.offset(size_size as isize) as *mut c_void
}
}
extern "C" fn free_ext(old: *mut c_void) {
if old.is_null() { return }
unsafe {
let size_size = mem::size_of::<usize>();
let old = old as *mut u8;
let old = old.offset(-(size_size as isize));
let old_size = *(old as *const usize);
heap::deallocate(old as *mut u8, old_size, ALIGNMENT);
}
}
fn main() {
Allocator {
alloc: Some(alloc_ext),
free: Some(free_ext),
};
let pointer = alloc_ext(ptr::null_mut(), 54);
let pointer = alloc_ext(pointer, 105);
free_ext(pointer);
}
Isn't [... using
Vec
as an allocator ...] the more high-level solution?
这当然是可能的,但我不完全确定它如何与重新分配一起工作。您还必须跟踪 Vec
的大小和容量,以便重新构造它以重新分配/删除它。