Это не так просто, как вам может понравиться.
Способы распределения раскрываются в 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 и выделить немного лишнего места для хранения размера. Затем вы можете сохранить размер и вернуть указатель чуть выше этого.
полностью непроверенной пример:
#![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);
}
Не [... using Vec
as an allocator ...] более высокого уровня решения?
Это, безусловно, возможно, но я не совсем уверен, как это будет работать с перераспределением. Вам также необходимо будет отслеживать размер и емкость Vec
, чтобы восстановить его, чтобы перераспределить его.
https://www.reddit.com/r/rust/comments/2eqdg2/allocate_a_vec_on_cs_heap/ Есть несколько мыслей по подобной теме, хотя управление жизненным циклом распределения может быть сложным. – snuk182
Как вы отмечаете, эта тема касается того, что Rust использует тот же распределитель, что и C. Это возможно с помощью [настраиваемых распределителей] (https://doc.rust-lang.org/stable/book/custom-allocators.html). – Shepmaster