¿Cómo uso el asignador de memoria Rust para una biblioteca C que se puede proporcionar un asignador?

Estoy escribiendo enlaces de Rust a una biblioteca de C que tiene la opción de usar un asignador de memoria de terceros. Su interfaz se ve así:

struct allocator { void*(*alloc)(void *old, uint); void(*free)(void*); }; 

La estructura de rust correspondiente es, supongo, la siguiente:

 #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq)] pub struct Allocator { alloc: Option *mut c_void>, free: Option, } 

¿Cómo puedo implementar estas dos funciones externas que deberían imitar al asignador? No encontré nada que se pareciera realmente a la API del asignador en Rust (sin embargo, entiendo por qué), por lo que siento curiosidad por si es posible.

No es tan fácil como te gustaría.

Los métodos de asignación se exponen en el módulo de alloc heap de la caja de alloc .

Crear algunos métodos de envoltura y rellenar la estructura es sencillo, pero rápidamente nos encontramos con un problema:

 #![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 *mut c_void>, free: Option, } 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), }; } 

El asignador de Rust espera que se le informe el tamaño de cualquier asignación previa, así como la alineación deseada. La API que está comparando no tiene ninguna forma de pasar eso.

La alineación debe (no soy un experto) estar bien para codificar en algún valor, por ejemplo, 16 bytes. El tamaño es más complicado. Es probable que necesite robar algunos trucos de C antiguos y asignar un poco más de espacio para almacenar el tamaño. Luego, puede almacenar el tamaño y devolver un puntero justo después de eso.

Un ejemplo completamente no probado :

 #![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 *mut c_void>, free: Option, } 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::(); 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::(); 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); } 

¿No es [… usar Vec como asignador …] la solución más de alto nivel?

Eso es ciertamente posible, pero no estoy completamente seguro de cómo funcionaría con la reasignación. También deberá realizar un seguimiento del tamaño y la capacidad del Vec para poder reconstituirlo y reasignarlo.